diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..42cf923d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# CMake->MSVC artifacts +*.sln +*.vcxproj +ALL_BUILD +ZERO_CHECK + +# MSVC secondary artifacts +*.suo +*.vcxproj.filters +*.vcxproj.user +*.pdb +*.ilk +*.lastbuildstate +*.sdf +*.opensdf +Debug/ +Release/ + +CMakeCache.txt +CMakeFiles +Makefile +*.cmake +*.cbp + +libfc.a +libfc_debug.a + +*.sw* + +fc_automoc.cpp +git_revision.cpp +GitSHA3.cpp + +ntp_test +task_cancel_test +udt_client +udt_server diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c49f87bee --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "vendor/secp256k1-zkp"] + path = vendor/secp256k1-zkp + url = https://github.com/cryptonomex/secp256k1-zkp.git +[submodule "vendor/websocketpp"] + path = vendor/websocketpp + url = https://github.com/zaphoyd/websocketpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d56f86d1f..73e6b1bd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,121 +1,505 @@ +# +# Defines fc library target. + PROJECT( fc ) +CMAKE_MINIMUM_REQUIRED( VERSION 2.8.12 ) -CMAKE_MINIMUM_REQUIRED( VERSION 2.8.0 ) +MESSAGE(STATUS "Configuring project fc located in: ${CMAKE_CURRENT_SOURCE_DIR}") +SET( CMAKE_AUTOMOC OFF ) -SET( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}" ) +# Setup module path to make visible used CMake extensions +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/GitVersionGen") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +INCLUDE(GetPrerequisites) INCLUDE( VersionMacros ) INCLUDE( SetupTargetMacros ) +INCLUDE(GetGitRevisionDescription) + +get_git_head_revision(GIT_REFSPEC FC_GIT_REVISION_SHA) +get_git_unix_timestamp(FC_GIT_REVISION_UNIX_TIMESTAMP) SET( DEFAULT_HEADER_INSTALL_DIR include/\${target} ) SET( DEFAULT_LIBRARY_INSTALL_DIR lib/ ) SET( DEFAULT_EXECUTABLE_INSTALL_DIR bin/ ) SET( CMAKE_DEBUG_POSTFIX _debug ) -#SET( BUILD_SHARED_LIBS NO ) +SET( BUILD_SHARED_LIBS NO ) +SET( ECC_IMPL secp256k1 CACHE STRING "secp256k1 or openssl or mixed" ) + +set(platformBitness 32) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(platformBitness 64) +endif() + +SET (ORIGINAL_LIB_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) -SET(Boost_USE_STATIC_LIBS ON) -FIND_PACKAGE(Boost 1.51 COMPONENTS thread date_time system filesystem program_options signals serialization chrono unit_test_framework context ) +SET(BOOST_COMPONENTS) +LIST(APPEND BOOST_COMPONENTS thread date_time system filesystem program_options signals serialization chrono unit_test_framework context locale iostreams) +SET( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" ) +IF( ECC_IMPL STREQUAL openssl ) + SET( ECC_REST src/crypto/elliptic_impl_pub.cpp ) +ELSE( ECC_IMPL STREQUAL openssl ) + SET( ECC_LIB secp256k1 ) + IF( ECC_IMPL STREQUAL mixed ) + SET( ECC_REST src/crypto/elliptic_impl_priv.cpp src/crypto/elliptic_impl_pub.cpp ) + ELSE( ECC_IMPL STREQUAL mixed ) + SET( ECC_REST src/crypto/elliptic_impl_priv.cpp ) + ENDIF( ECC_IMPL STREQUAL mixed ) +ENDIF( ECC_IMPL STREQUAL openssl ) +# Configure secp256k1-zkp +if ( WIN32 ) + # autoconf won't work here, hard code the defines + set( SECP256K1_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp" ) -INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR} ) -LINK_DIRECTORIES( ${Boost_LIBRARY_DIRS} ) + file( GLOB SECP256K1_SOURCES "${SECP256K1_DIR}/src/secp256k1.c" ) + add_library( secp256k1 ${SECP256K1_SOURCES} ) + + target_include_directories( secp256k1 PRIVATE "${SECP256K1_DIR}" PUBLIC "${SECP256K1_DIR}/include" ) + + set( SECP256K1_BUILD_DEFINES + USE_FIELD_10X26 + USE_FIELD_INV_BUILTIN + USE_NUM_NONE + USE_SCALAR_8X32 + USE_SCALAR_INV_BUILTIN ) + set_target_properties( secp256k1 PROPERTIES COMPILE_DEFINITIONS "${SECP256K1_BUILD_DEFINES}" LINKER_LANGUAGE C ) +else ( WIN32 ) + include(ExternalProject) + ExternalProject_Add( project_secp256k1 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/vendor/secp256k1-zkp + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp/configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/vendor/secp256k1-zkp --with-bignum=no + BUILD_COMMAND make + INSTALL_COMMAND true + BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/vendor/secp256k1-zkp/src/project_secp256k1-build/.libs/libsecp256k1.a + ) + ExternalProject_Add_Step(project_secp256k1 autogen + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp/autogen.sh + DEPENDERS configure + ) + + ExternalProject_Get_Property(project_secp256k1 binary_dir) + + add_library(secp256k1 STATIC IMPORTED) + set_property(TARGET secp256k1 PROPERTY IMPORTED_LOCATION ${binary_dir}/.libs/libsecp256k1${CMAKE_STATIC_LIBRARY_SUFFIX}) + set_property(TARGET secp256k1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp/include) + add_dependencies(secp256k1 project_secp256k1) +endif ( WIN32 ) +# End configure secp256k1-zkp IF( WIN32 ) - ADD_DEFINITIONS( -DBOOST_CONTEXT_NO_LIB ) - ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS ) - ADD_DEFINITIONS( -D_WIN32_WINNT=0x0501 ) - ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) + MESSAGE(STATUS "Configuring fc to build on Win32") + + set( RPCRT4 Rpcrt4 ) + + #boost + SET(BOOST_ROOT $ENV{BOOST_ROOT}) +# set(Boost_USE_DEBUG_PYTHON ON) + set(Boost_USE_MULTITHREADED ON) + set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries + + FIND_PACKAGE(Boost 1.53 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) + # For Boost 1.53 on windows, coroutine was not in BOOST_LIBRARYDIR and do not need it to build, but if boost versin >= 1.54, find coroutine otherwise will cause link errors + IF(NOT "${Boost_VERSION}" MATCHES "1.53(.*)") + SET(BOOST_LIBRARIES_TEMP ${Boost_LIBRARIES}) + FIND_PACKAGE(Boost 1.54 REQUIRED COMPONENTS coroutine) + LIST(APPEND BOOST_COMPONENTS coroutine) + SET(Boost_LIBRARIES ${BOOST_LIBRARIES_TEMP} ${Boost_LIBRARIES}) + ENDIF() + + set( PLATFORM_SPECIFIC_LIBS WS2_32.lib Userenv.lib) + # iphlpapi.lib + ELSE(WIN32) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") + MESSAGE(STATUS "Configuring fc to build on Unix/Apple") + + LIST(APPEND BOOST_COMPONENTS coroutine) + + FIND_PACKAGE(Boost 1.53 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) + + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") + + IF(NOT APPLE) + # Linux or other unix + SET(rt_library rt ) + SET(pthread_library pthread) + ENDIF(NOT APPLE) ENDIF(WIN32) -if( UNIX ) - if( NOT APPLE ) - set(rt_library rt ) - set(pthread_library pthread) - endif() -endif() + + +IF(NOT "$ENV{OPENSSL_ROOT_DIR}" STREQUAL "") + set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT_DIR} ) + set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include) + message(STATUS "Setting up OpenSSL root and include vars to ${OPENSSL_ROOT_DIR}, ${OPENSSL_INCLUDE_DIR}") +ENDIF() + + +find_package(OpenSSL REQUIRED) + +set( CMAKE_FIND_LIBRARY_SUFFIXES ${ORIGINAL_LIB_SUFFIXES} ) + +# We are now building in support for deflate compression into our websockets layer by default, +# which requires zlib. Aside from that, all of fc compiles without zlib, so this could be +# made optional without much effort +# (important exception, apple: as of 10.10 yosemite, the OpenSSL static libraries shipped with +# os x have a dependency on zlib) +# On a side note, fc's fc::zlib_compress() function uses a separate implementation of zlib +# from the miniz library. If we're comfortable requiring an external zlib, we can +# reimplement fc::zlib_compress() to call the real zlib, and remove miniz.c from our +# repository. +find_package( ZLIB REQUIRED ) option( UNITY_BUILD OFF ) -FIND_PACKAGE( OpenSSL ) -FIND_PACKAGE( ZLIB ) - -include_directories( vendor/zlib-1.2.7/) -include_directories( vendor/libssh2-1.4.2/include ) -include_directories( ${Boost_INCLUDE_DIR} ) -include_directories( ${OPENSSL_INCLUDE_DIR} ) -include_directories( include ) - -set( sources - src/logger.cpp - src/console_appender.cpp - src/file_appender.cpp - src/appender.cpp - src/logger_config.cpp - src/ssh.cpp - src/url.cpp - src/process.cpp - src/http_connection.cpp - src/http_server.cpp - src/json_rpc_connection.cpp - src/json_rpc_stream_connection.cpp - src/json_rpc_tcp_connection.cpp - src/json_rpc_tcp_server.cpp - src/json_rpc_error_object.cpp - src/error_report.cpp - src/value.cpp - src/lexical_cast.cpp - src/spin_lock.cpp - src/spin_yield_lock.cpp - src/task.cpp - src/future.cpp - src/shared_ptr.cpp - src/string.cpp - src/json.cpp - src/log.cpp - src/time.cpp - src/iostream.cpp - src/fstream.cpp - src/sstream.cpp - src/exception.cpp - src/thread.cpp - src/hex.cpp - src/sha1.cpp - src/sha256.cpp - src/filesystem.cpp - src/ip.cpp - src/bigint.cpp - src/mutex.cpp - src/pke.cpp - src/base64.cpp - src/base58.cpp - src/blowfish.cpp - src/dh.cpp - src/udp_socket.cpp - src/tcp_socket.cpp - src/asio.cpp - src/super_fast_hash.cpp - src/file_mapping.cpp - src/reflect.cpp -# src/program_options.cpp +set( fc_sources + src/uint128.cpp + src/real128.cpp + src/variant.cpp + src/exception.cpp + src/variant_object.cpp + src/thread/thread.cpp + src/thread/thread_specific.cpp + src/thread/future.cpp + src/thread/task.cpp + src/thread/spin_lock.cpp + src/thread/spin_yield_lock.cpp + src/thread/mutex.cpp + src/thread/non_preemptable_scope_check.cpp + src/asio.cpp + src/string.cpp + src/shared_ptr.cpp + src/time.cpp + src/utf8.cpp + src/io/iostream.cpp + src/io/datastream.cpp + src/io/buffered_iostream.cpp + src/io/fstream.cpp + src/io/sstream.cpp + src/io/json.cpp + src/io/varint.cpp + src/io/console.cpp + src/filesystem.cpp + src/interprocess/process.cpp + src/interprocess/signals.cpp + src/interprocess/file_mapping.cpp + src/interprocess/mmap_struct.cpp + src/rpc/cli.cpp + src/rpc/http_api.cpp + src/rpc/json_connection.cpp + src/rpc/state.cpp + src/rpc/websocket_api.cpp + src/log/log_message.cpp + src/log/logger.cpp + src/log/appender.cpp + src/log/console_appender.cpp + src/log/file_appender.cpp + src/log/gelf_appender.cpp + src/log/logger_config.cpp + src/crypto/_digest_common.cpp + src/crypto/openssl.cpp + src/crypto/aes.cpp + src/crypto/crc.cpp + src/crypto/city.cpp + src/crypto/base32.cpp + src/crypto/base36.cpp + src/crypto/base58.cpp + src/crypto/base64.cpp + src/crypto/bigint.cpp + src/crypto/hex.cpp + src/crypto/sha1.cpp + src/crypto/ripemd160.cpp + src/crypto/sha256.cpp + src/crypto/sha224.cpp + src/crypto/sha512.cpp + src/crypto/dh.cpp + src/crypto/blowfish.cpp + src/crypto/elliptic_common.cpp + ${ECC_REST} + src/crypto/elliptic_${ECC_IMPL}.cpp + src/crypto/rand.cpp + src/network/tcp_socket.cpp + src/network/udp_socket.cpp + src/network/udt_socket.cpp + src/network/http/http_connection.cpp + src/network/http/http_server.cpp + src/network/http/websocket.cpp + src/network/ntp.cpp + src/network/ip.cpp + src/network/rate_limiting.cpp + src/network/resolve.cpp + src/network/url.cpp + src/network/gntp.cpp + src/compress/smaz.cpp + src/compress/zlib.cpp + vendor/cyoencode-1.0.2/src/CyoDecode.c + vendor/cyoencode-1.0.2/src/CyoEncode.c + ) + +file( GLOB_RECURSE fc_headers ${CMAKE_CURRENT_SOURCE_DIR} *.hpp *.h ) + +set( sources + ${fc_sources} ) -add_subdirectory(vendor) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/git_revision.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp" @ONLY) +list(APPEND sources "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp") +list(APPEND sources ${fc_headers}) + +add_subdirectory( vendor/websocketpp EXCLUDE_FROM_ALL ) +add_subdirectory( vendor/udt4 ) + +setup_library( fc SOURCES ${sources} LIBRARY_TYPE STATIC ) +install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include ) + +# begin readline stuff +find_package(Curses) +find_package(Readline) + +file(GLOB HEADERS "include/bts/cli/*.hpp") + +if (READLINE_FOUND) + target_compile_definitions (fc PRIVATE HAVE_READLINE) + set(readline_libraries ${Readline_LIBRARY}) + if (CURSES_FOUND) + list(APPEND readline_libraries ${CURSES_LIBRARY}) + endif() + set(readline_includes ${Readline_INCLUDE_DIR}) +endif() +if(WIN32) + target_compile_definitions( fc PRIVATE _CRT_NONSTDC_NO_DEPRECATE ) +endif(WIN32) +# end readline stuff + +IF(WIN32) + target_compile_definitions(fc PUBLIC WIN32 NOMINMAX _WIN32_WINNT=0x0501 _CRT_SECURE_NO_WARNINGS + _SCL_SERCURE_NO_WARNINGS + # Needed to disable MSVC autolinking feature (#pragma comment) + BOOST_ALL_NO_LIB + # The current version of websockets doesn't correctly guess what 'chrono' implementation boost::asio uses + # on the recommended build platform of VC++12/boost_1.58. Force it here until websocket gets their + # autodetecting code to do the right thing. + _WEBSOCKETPP_CPP11_CHRONO_ + ) + # Activate C++ exception handling, assume extern C calls don't throw + # Add /U options to be sure settings specific to dynamic boost link are ineffective + target_compile_options(fc PUBLIC /EHsc /UBOOST_ALL_DYN_LINK /UBOOST_LINKING_PYTHON /UBOOST_DEBUG_PYTHON) +ELSE() + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall") + + IF(APPLE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall") + ELSE() + if( NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) + target_compile_options(fc PUBLIC -std=c++11 -Wall -fnon-call-exceptions) + endif() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fnon-call-exceptions") + ENDIF() +ENDIF() + +# This will become unnecessary once we update to websocketpp which fixes upstream issue #395 +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWEBSOCKETPP_STRICT_MASKING") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ASIO_HAS_STD_CHRONO") + +target_include_directories(fc + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include + ${Boost_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIR} + "${readline_includes}" -#add_executable( date t.cpp ) -#target_link_libraries( date fc ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ${Boost_DATE_TIME_LIBRARY}) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/vendor/boost_1.51/include + ${CMAKE_CURRENT_SOURCE_DIR}/vendor/cyoencode-1.0.2/src + ${CMAKE_CURRENT_SOURCE_DIR}/vendor/udt4/src + ${CMAKE_CURRENT_SOURCE_DIR}/vendor/websocketpp + ${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp + ${ZLIB_INCLUDE_DIR} + ) -setup_library( fc SOURCES ${sources} ) +#target_link_libraries( fc PUBLIC udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${ECC_LIB} ) +IF(NOT WIN32) + set(LINK_USR_LOCAL_LIB -L/usr/local/lib) +ENDIF() +target_link_libraries( fc PUBLIC ${LINK_USR_LOCAL_LIB} udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries} ${ECC_LIB} ) -#setup_executable( json_rpc_test SOURCES tests/json_rpc_test.cpp LIBRARIES fc ${ZLIB_LIBRARY} ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ${Boost_DATE_TIME_LIBRARY}) -setup_executable( ssh_test SOURCES tests/ssh.cpp LIBRARIES fc ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ssh2 ${OPENSSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${ZLIB_LIBRARY} ${ALL_OPENSSL_LIBRARIES} ${Boost_DATE_TIME_LIBRARY}) +if(MSVC) + set_source_files_properties( src/network/http/websocket.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) -setup_executable( logger_test SOURCES tests/logger.cpp LIBRARIES fc ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ssh2 ${OPENSSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${ZLIB_LIBRARY} ${ALL_OPENSSL_LIBRARIES} ${Boost_DATE_TIME_LIBRARY}) -#add_executable( test_vec tests/vector_test.cpp ) -#target_link_libraries( test_vec fc ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ) +IF(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY MATCHES "\\.(a|lib)$") +IF(WIN32) +add_definitions(/DBOOST_TEST_DYN_LINK) +ELSE(WIN32) +add_definitions(-DBOOST_TEST_DYN_LINK) +ENDIF(WIN32) +ENDIF() + +add_executable( api tests/api.cpp ) +target_link_libraries( api fc ) + +if( ECC_IMPL STREQUAL secp256k1 ) + add_executable( blind tests/all_tests.cpp tests/crypto/blind.cpp ) + target_link_libraries( blind fc ) +endif() + +include_directories( vendor/websocketpp ) + +add_executable( ntp_test tests/all_tests.cpp tests/network/ntp_test.cpp ) +target_link_libraries( ntp_test fc ) + +add_executable( task_cancel_test tests/all_tests.cpp tests/thread/task_cancel.cpp ) +target_link_libraries( task_cancel_test fc ) + + +add_executable( bloom_test tests/all_tests.cpp tests/bloom_test.cpp ) +target_link_libraries( bloom_test fc ) + +add_executable( real128_test tests/all_tests.cpp tests/real128_test.cpp ) +target_link_libraries( real128_test fc ) + +add_executable( hmac_test tests/hmac_test.cpp ) +target_link_libraries( hmac_test fc ) + +add_executable( blinding_test tests/blinding_test.cpp ) +target_link_libraries( blinding_test fc ) + + +add_executable( udt_server tests/udts.cpp ) +target_link_libraries( udt_server fc udt ) + +add_executable( udt_client tests/udtc.cpp ) +target_link_libraries( udt_client fc udt ) + +add_executable( ecc_test tests/crypto/ecc_test.cpp ) +target_link_libraries( ecc_test fc ) + +#add_executable( test_aes tests/aes_test.cpp ) +#target_link_libraries( test_aes fc ${rt_library} ${pthread_library} ) +#add_executable( test_sleep tests/sleep.cpp ) +#target_link_libraries( test_sleep fc ) +#add_executable( test_rate_limiting tests/rate_limiting.cpp ) +#target_link_libraries( test_rate_limiting fc ) + +add_executable( all_tests tests/all_tests.cpp + tests/compress/compress.cpp + tests/crypto/aes_test.cpp + tests/crypto/base_n_tests.cpp + tests/crypto/bigint_test.cpp + tests/crypto/blind.cpp + tests/crypto/blowfish_test.cpp + tests/crypto/dh_test.cpp + tests/crypto/rand_test.cpp + tests/crypto/sha_tests.cpp + tests/network/ntp_test.cpp + tests/network/http/websocket_test.cpp + tests/thread/task_cancel.cpp + tests/bloom_test.cpp + tests/real128_test.cpp + tests/utf8_test.cpp + ) +target_link_libraries( all_tests fc ) + +if(WIN32) + # add addtional import library on windows platform + target_link_libraries( fc PUBLIC crypt32.lib) + + # now generate a list of the DLLs we're using to use during the install process + include (ParseLibraryList) + PARSE_LIBRARY_LIST(${Boost_LIBRARIES} + FOUND parseOk + DEBUG Boost_LIBRARIES_DEBUG + OPT Boost_LIBRARIES_RELEASE + GENERAL Boost_LIBRARIES_GENERAL) + + #Variable will hold list of .pdb files generated for libraries the 'fc' module is linked to + set(INTERFACE_LINK_PDB_RELEASE) + + set(SHARED_LIBRARIES_RELEASE) + foreach(boost_import_lib ${Boost_LIBRARIES_RELEASE}) + get_filename_component(import_lib_name_root ${boost_import_lib} NAME_WE) + get_filename_component(import_lib_path ${boost_import_lib} PATH) + set(boost_dll "${import_lib_path}/${import_lib_name_root}${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(boost_lib_pdb "${import_lib_name_root}.pdb") + + FILE(GLOB_RECURSE boost_pdb_paths "${import_lib_path}/../../bin.v2/*/${boost_lib_pdb}") + + foreach(p ${boost_pdb_paths}) + if(p MATCHES ".*/address-model-${platformBitness}/") + GP_APPEND_UNIQUE(INTERFACE_LINK_PDB_RELEASE ${p}) + endif() + endforeach() + + if(EXISTS "${boost_dll}") + set(SHARED_LIBRARIES_RELEASE ${SHARED_LIBRARIES_RELEASE} "${boost_dll}") + endif() + + endforeach() + + set(INTERFACE_LINK_PDB_DEBUG) + set(SHARED_LIBRARIES_DEBUG) + foreach(boost_import_lib ${Boost_LIBRARIES_DEBUG}) + get_filename_component(import_lib_name_root ${boost_import_lib} NAME_WE) + get_filename_component(import_lib_path ${boost_import_lib} PATH) + set(boost_dll "${import_lib_path}/${import_lib_name_root}${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(boost_lib_pdb "${import_lib_name_root}.pdb") + + FILE(GLOB_RECURSE boost_pdb_paths "${import_lib_path}/../../bin.v2/*/${boost_lib_pdb}") + + foreach(p ${boost_pdb_paths}) + if(p MATCHES ".*/address-model-${platformBitness}/") + GP_APPEND_UNIQUE(INTERFACE_LINK_PDB_DEBUG ${p}) + endif() + endforeach() + if(EXISTS "${boost_dll}") + set(SHARED_LIBRARIES_DEBUG ${SHARED_LIBRARIES_DEBUG} "${boost_dll}") + endif() + endforeach() + + # message(STATUS "openssl_libraries=${OPENSSL_LIBRARIES}") + foreach(lib ${OPENSSL_LIBRARIES}) + get_filename_component(lib_name ${lib} NAME_WE) + if (${lib_name} STREQUAL "libeay32") + get_filename_component(lib_dir ${lib} DIRECTORY) + get_filename_component(openssl_dir "${lib_dir}/.." REALPATH) + set( eaydll "${openssl_dir}/bin/${lib_name}${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(eay_pdb "${openssl_dir}/bin/${lib_name}.pdb") + message(STATUS "eay=${eaydll}") + if(EXISTS ${eay_pdb}) + GP_APPEND_UNIQUE(INTERFACE_LINK_PDB_RELEASE ${eay_pdb}) + GP_APPEND_UNIQUE(INTERFACE_LINK_PDB_DEBUG ${eay_pdb}) + endif() + + set(SHARED_LIBRARIES_DEBUG ${SHARED_LIBRARIES_DEBUG} "${eaydll}") + set(SHARED_LIBRARIES_RELEASE ${SHARED_LIBRARIES_RELEASE} "${eaydll}") + endif() + endforeach() + + set_property(TARGET fc PROPERTY INTERFACE_LINK_PDB_RELEASE ${INTERFACE_LINK_PDB_RELEASE}) + set_property(TARGET fc PROPERTY INTERFACE_LINK_PDB_DEBUG ${INTERFACE_LINK_PDB_DEBUG}) + set_property(TARGET fc PROPERTY SHARED_LIBRARIES_DEBUG ${SHARED_LIBRARIES_DEBUG}) + set_property(TARGET fc PROPERTY SHARED_LIBRARIES_RELEASE ${SHARED_LIBRARIES_RELEASE}) + +endif(WIN32) + +SET(OPENSSL_CONF_TARGET ) +IF(DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) + SET (OPENSSL_CONF_TARGET ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +ELSE() + SET (OPENSSL_CONF_TARGET ${CMAKE_CURRENT_BINARY_DIR}) +ENDIF() + + IF(WIN32) +SET(POST_BUILD_STEP_COMMANDS ${POST_BUILD_STEP_COMMANDS} + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${OPENSSL_ROOT_DIR}/ssl/openssl.cnf" "${OPENSSL_CONF_TARGET}/openssl.cnf") + ENDIF(WIN32) + +ADD_CUSTOM_COMMAND(TARGET fc POST_BUILD ${POST_BUILD_STEP_COMMANDS} + COMMENT "Copying OpenSSL/ssl/openssl.cnf into target directory." +) -#add_executable( unit_tests tests/unit.cpp ) -#target_link_libraries( unit_tests fc ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ) +MESSAGE(STATUS "Finished fc module configuration...") diff --git a/CMakeModules/FindBoost.cmake b/CMakeModules/FindBoost.cmake new file mode 100644 index 000000000..4c37f5fff --- /dev/null +++ b/CMakeModules/FindBoost.cmake @@ -0,0 +1,1180 @@ +# - Find Boost include dirs and libraries +# Use this module by invoking find_package with the form: +# find_package(Boost +# [version] [EXACT] # Minimum or EXACT version e.g. 1.36.0 +# [REQUIRED] # Fail with error if Boost is not found +# [COMPONENTS ...] # Boost libraries by their canonical name +# ) # e.g. "date_time" for "libboost_date_time" +# This module finds headers and requested component libraries OR a CMake +# package configuration file provided by a "Boost CMake" build. For the +# latter case skip to the "Boost CMake" section below. For the former +# case results are reported in variables: +# Boost_FOUND - True if headers and requested libraries were found +# Boost_INCLUDE_DIRS - Boost include directories +# Boost_LIBRARY_DIRS - Link directories for Boost libraries +# Boost_LIBRARIES - Boost component libraries to be linked +# Boost__FOUND - True if component was found ( is upper-case) +# Boost__LIBRARY - Libraries to link for component (may include +# target_link_libraries debug/optimized keywords) +# Boost_VERSION - BOOST_VERSION value from boost/version.hpp +# Boost_LIB_VERSION - Version string appended to library filenames +# Boost_MAJOR_VERSION - Boost major version number (X in X.y.z) +# Boost_MINOR_VERSION - Boost minor version number (Y in x.Y.z) +# Boost_SUBMINOR_VERSION - Boost subminor version number (Z in x.y.Z) +# Boost_LIB_DIAGNOSTIC_DEFINITIONS (Windows) +# - Pass to add_definitions() to have diagnostic +# information about Boost's automatic linking +# displayed during compilation +# +# This module reads hints about search locations from variables: +# BOOST_ROOT - Preferred installation prefix +# (or BOOSTROOT) +# BOOST_INCLUDEDIR - Preferred include directory e.g. /include +# BOOST_LIBRARYDIR - Preferred library directory e.g. /lib +# Boost_NO_SYSTEM_PATHS - Set to ON to disable searching in locations not +# specified by these hint variables. Default is OFF. +# Boost_ADDITIONAL_VERSIONS +# - List of Boost versions not known to this module +# (Boost install locations may contain the version) +# and saves search results persistently in CMake cache entries: +# Boost_INCLUDE_DIR - Directory containing Boost headers +# Boost_LIBRARY_DIR - Directory containing Boost libraries +# Boost__LIBRARY_DEBUG - Component library debug variant +# Boost__LIBRARY_RELEASE - Component library release variant +# Users may set the these hints or results as cache entries. Projects should +# not read these entries directly but instead use the above result variables. +# Note that some hint names start in upper-case "BOOST". One may specify +# these as environment variables if they are not specified as CMake variables +# or cache entries. +# +# This module first searches for the Boost header files using the above hint +# variables (excluding BOOST_LIBRARYDIR) and saves the result in +# Boost_INCLUDE_DIR. Then it searches for requested component libraries using +# the above hints (excluding BOOST_INCLUDEDIR and Boost_ADDITIONAL_VERSIONS), +# "lib" directories near Boost_INCLUDE_DIR, and the library name configuration +# settings below. It saves the library directory in Boost_LIBRARY_DIR and +# individual library locations in Boost__LIBRARY_DEBUG and +# Boost__LIBRARY_RELEASE. When one changes settings used by previous +# searches in the same build tree (excluding environment variables) this +# module discards previous search results affected by the changes and searches +# again. +# +# Boost libraries come in many variants encoded in their file name. Users or +# projects may tell this module which variant to find by setting variables: +# Boost_USE_MULTITHREADED - Set to OFF to use the non-multithreaded +# libraries ('mt' tag). Default is ON. +# Boost_USE_STATIC_LIBS - Set to ON to force the use of the static +# libraries. Default is OFF. +# Boost_USE_STATIC_RUNTIME - Set to ON or OFF to specify whether to use +# libraries linked statically to the C++ runtime +# ('s' tag). Default is platform dependent. +# Boost_USE_DEBUG_PYTHON - Set to ON to use libraries compiled with a +# debug Python build ('y' tag). Default is OFF. +# Boost_USE_STLPORT - Set to ON to use libraries compiled with +# STLPort ('p' tag). Default is OFF. +# Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS +# - Set to ON to use libraries compiled with +# STLPort deprecated "native iostreams" +# ('n' tag). Default is OFF. +# Boost_COMPILER - Set to the compiler-specific library suffix +# (e.g. "-gcc43"). Default is auto-computed +# for the C++ compiler in use. +# Boost_THREADAPI - Suffix for "thread" component library name, +# such as "pthread" or "win32". Names with +# and without this suffix will both be tried. +# Other variables one may set to control this module are: +# Boost_DEBUG - Set to ON to enable debug output from FindBoost. +# Please enable this before filing any bug report. +# Boost_DETAILED_FAILURE_MSG +# - Set to ON to add detailed information to the +# failure message even when the REQUIRED option +# is not given to the find_package call. +# Boost_REALPATH - Set to ON to resolve symlinks for discovered +# libraries to assist with packaging. For example, +# the "system" component library may be resolved to +# "/usr/lib/libboost_system.so.1.42.0" instead of +# "/usr/lib/libboost_system.so". This does not +# affect linking and should not be enabled unless +# the user needs this information. +# On Visual Studio and Borland compilers Boost headers request automatic +# linking to corresponding libraries. This requires matching libraries to be +# linked explicitly or available in the link library search path. In this +# case setting Boost_USE_STATIC_LIBS to OFF may not achieve dynamic linking. +# Boost automatic linking typically requests static libraries with a few +# exceptions (such as Boost.Python). Use +# add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS}) +# to ask Boost to report information about automatic linking requests. +# +# Example to find Boost headers only: +# find_package(Boost 1.36.0) +# if(Boost_FOUND) +# include_directories(${Boost_INCLUDE_DIRS}) +# add_executable(foo foo.cc) +# endif() +# Example to find Boost headers and some libraries: +# set(Boost_USE_STATIC_LIBS ON) +# set(Boost_USE_MULTITHREADED ON) +# set(Boost_USE_STATIC_RUNTIME OFF) +# find_package(Boost 1.36.0 COMPONENTS date_time filesystem system ...) +# if(Boost_FOUND) +# include_directories(${Boost_INCLUDE_DIRS}) +# add_executable(foo foo.cc) +# target_link_libraries(foo ${Boost_LIBRARIES}) +# endif() +# +# Boost CMake ---------------------------------------------------------- +# +# If Boost was built using the boost-cmake project it provides a package +# configuration file for use with find_package's Config mode. This module +# looks for the package configuration file called BoostConfig.cmake or +# boost-config.cmake and stores the result in cache entry "Boost_DIR". If +# found, the package configuration file is loaded and this module returns with +# no further action. See documentation of the Boost CMake package +# configuration for details on what it provides. +# +# Set Boost_NO_BOOST_CMAKE to ON to disable the search for boost-cmake. + +#============================================================================= +# Copyright 2006-2012 Kitware, Inc. +# Copyright 2006-2008 Andreas Schneider +# Copyright 2007 Wengo +# Copyright 2007 Mike Jackson +# Copyright 2008 Andreas Pakulat +# Copyright 2008-2012 Philip Lowman +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + +#------------------------------------------------------------------------------- +# Before we go searching, check whether boost-cmake is avaialble, unless the +# user specifically asked NOT to search for boost-cmake. +# +# If Boost_DIR is set, this behaves as any find_package call would. If not, +# it looks at BOOST_ROOT and BOOSTROOT to find Boost. +# + +message(STATUS "Using custom FindBoost.cmake") + +if (NOT Boost_NO_BOOST_CMAKE) + # If Boost_DIR is not set, look for BOOSTROOT and BOOST_ROOT as alternatives, + # since these are more conventional for Boost. + if ("$ENV{Boost_DIR}" STREQUAL "") + if (NOT "$ENV{BOOST_ROOT}" STREQUAL "") + set(ENV{Boost_DIR} $ENV{BOOST_ROOT}) + elseif (NOT "$ENV{BOOSTROOT}" STREQUAL "") + set(ENV{Boost_DIR} $ENV{BOOSTROOT}) + endif() + endif() + + # Do the same find_package call but look specifically for the CMake version. + # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no + # need to delegate them to this find_package call. + find_package(Boost QUIET NO_MODULE) + mark_as_advanced(Boost_DIR) + + # If we found boost-cmake, then we're done. Print out what we found. + # Otherwise let the rest of the module try to find it. + if (Boost_FOUND) + message("Boost ${Boost_FIND_VERSION} found.") + if (Boost_FIND_COMPONENTS) + message("Found Boost components:") + message(" ${Boost_FIND_COMPONENTS}") + endif() + return() + endif() +endif() + + +#------------------------------------------------------------------------------- +# FindBoost functions & macros +# + +############################################ +# +# Check the existence of the libraries. +# +############################################ +# This macro was taken directly from the FindQt4.cmake file that is included +# with the CMake distribution. This is NOT my work. All work was done by the +# original authors of the FindQt4.cmake file. Only minor modifications were +# made to remove references to Qt and make this file more generally applicable +# And ELSE/ENDIF pairs were removed for readability. +######################################################################### + +macro(_Boost_ADJUST_LIB_VARS basename) + if(Boost_INCLUDE_DIR ) + if(Boost_${basename}_LIBRARY_DEBUG AND Boost_${basename}_LIBRARY_RELEASE) + # if the generator supports configuration types then set + # optimized and debug libraries, or if the CMAKE_BUILD_TYPE has a value + if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) + set(Boost_${basename}_LIBRARY optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) + else() + # if there are no configuration types and CMAKE_BUILD_TYPE has no value + # then just use the release libraries + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) + endif() + # FIXME: This probably should be set for both cases + set(Boost_${basename}_LIBRARIES optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) + endif() + + # if only the release version was found, set the debug variable also to the release version + if(Boost_${basename}_LIBRARY_RELEASE AND NOT Boost_${basename}_LIBRARY_DEBUG) + set(Boost_${basename}_LIBRARY_DEBUG ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE}) + endif() + + # if only the debug version was found, set the release variable also to the debug version + if(Boost_${basename}_LIBRARY_DEBUG AND NOT Boost_${basename}_LIBRARY_RELEASE) + set(Boost_${basename}_LIBRARY_RELEASE ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_DEBUG}) + endif() + + # If the debug & release library ends up being the same, omit the keywords + if(${Boost_${basename}_LIBRARY_RELEASE} STREQUAL ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE} ) + endif() + + if(Boost_${basename}_LIBRARY) + set(Boost_${basename}_FOUND ON) + endif() + + endif() + # Make variables changeble to the advanced user + mark_as_advanced( + Boost_${basename}_LIBRARY_RELEASE + Boost_${basename}_LIBRARY_DEBUG + ) +endmacro() + +macro(_Boost_CHANGE_DETECT changed_var) + set(${changed_var} 0) + foreach(v ${ARGN}) + if(DEFINED _Boost_COMPONENTS_SEARCHED) + if(${v}) + if(_${v}_LAST) + string(COMPARE NOTEQUAL "${${v}}" "${_${v}_LAST}" _${v}_CHANGED) + else() + set(_${v}_CHANGED 1) + endif() + elseif(_${v}_LAST) + set(_${v}_CHANGED 1) + endif() + if(_${v}_CHANGED) + set(${changed_var} 1) + endif() + else() + set(_${v}_CHANGED 0) + endif() + endforeach() +endmacro() + +macro(_Boost_FIND_LIBRARY var) + find_library(${var} ${ARGN}) + + # If we found the first library save Boost_LIBRARY_DIR. + if(${var} AND NOT Boost_LIBRARY_DIR) + get_filename_component(_dir "${${var}}" PATH) + set(Boost_LIBRARY_DIR "${_dir}" CACHE PATH "Boost library directory" FORCE) + endif() + + # If Boost_LIBRARY_DIR is known then search only there. + if(Boost_LIBRARY_DIR) + set(_boost_LIBRARY_SEARCH_DIRS ${Boost_LIBRARY_DIR} NO_DEFAULT_PATH) + endif() +endmacro() + +#------------------------------------------------------------------------------- + +# +# Runs compiler with "-dumpversion" and parses major/minor +# version with a regex. +# +function(_Boost_COMPILER_DUMPVERSION _OUTPUT_VERSION) + + exec_program(${CMAKE_CXX_COMPILER} + ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion + OUTPUT_VARIABLE _boost_COMPILER_VERSION + ) + string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" + _boost_COMPILER_VERSION ${_boost_COMPILER_VERSION}) + + set(${_OUTPUT_VERSION} ${_boost_COMPILER_VERSION} PARENT_SCOPE) +endfunction() + +# +# Take a list of libraries with "thread" in it +# and prepend duplicates with "thread_${Boost_THREADAPI}" +# at the front of the list +# +function(_Boost_PREPEND_LIST_WITH_THREADAPI _output) + set(_orig_libnames ${ARGN}) + string(REPLACE "thread" "thread_${Boost_THREADAPI}" _threadapi_libnames "${_orig_libnames}") + set(${_output} ${_threadapi_libnames} ${_orig_libnames} PARENT_SCOPE) +endfunction() + +# +# If a library is found, replace its cache entry with its REALPATH +# +function(_Boost_SWAP_WITH_REALPATH _library _docstring) + if(${_library}) + get_filename_component(_boost_filepathreal ${${_library}} REALPATH) + unset(${_library} CACHE) + set(${_library} ${_boost_filepathreal} CACHE FILEPATH "${_docstring}") + endif() +endfunction() + +function(_Boost_CHECK_SPELLING _var) + if(${_var}) + string(TOUPPER ${_var} _var_UC) + message(FATAL_ERROR "ERROR: ${_var} is not the correct spelling. The proper spelling is ${_var_UC}.") + endif() +endfunction() + +# Guesses Boost's compiler prefix used in built library names +# Returns the guess by setting the variable pointed to by _ret +function(_Boost_GUESS_COMPILER_PREFIX _ret) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" + OR "${CMAKE_CXX_COMPILER}" MATCHES "icl" + OR "${CMAKE_CXX_COMPILER}" MATCHES "icpc") + if(WIN32) + set (_boost_COMPILER "-iw") + else() + set (_boost_COMPILER "-il") + endif() + elseif (MSVC12) + set(_boost_COMPILER "-vc120") + elseif (MSVC11) + set(_boost_COMPILER "-vc110") + elseif (MSVC10) + set(_boost_COMPILER "-vc100") + elseif (MSVC90) + set(_boost_COMPILER "-vc90") + elseif (MSVC80) + set(_boost_COMPILER "-vc80") + elseif (MSVC71) + set(_boost_COMPILER "-vc71") + elseif (MSVC70) # Good luck! + set(_boost_COMPILER "-vc7") # yes, this is correct + elseif (MSVC60) # Good luck! + set(_boost_COMPILER "-vc6") # yes, this is correct + elseif (BORLAND) + set(_boost_COMPILER "-bcb") + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "SunPro") + set(_boost_COMPILER "-sw") + elseif (MINGW) + if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) + set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34 + else() + _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) + set(_boost_COMPILER "-mgw${_boost_COMPILER_VERSION}") + endif() + elseif (UNIX) + if (CMAKE_COMPILER_IS_GNUCXX) + if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) + set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34 + else() + _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) + # Determine which version of GCC we have. + if(APPLE) + if(Boost_MINOR_VERSION) + if(${Boost_MINOR_VERSION} GREATER 35) + # In Boost 1.36.0 and newer, the mangled compiler name used + # on Mac OS X/Darwin is "xgcc". + set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") + else() + # In Boost <= 1.35.0, there is no mangled compiler name for + # the Mac OS X/Darwin version of GCC. + set(_boost_COMPILER "") + endif() + else() + # We don't know the Boost version, so assume it's + # pre-1.36.0. + set(_boost_COMPILER "") + endif() + else() + set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}") + endif() + endif() + endif () + else() + # TODO at least Boost_DEBUG here? + set(_boost_COMPILER "") + endif() + set(${_ret} ${_boost_COMPILER} PARENT_SCOPE) +endfunction() + +function(_Boost_consider_adding_pthreads _outvar) + # On Unix platforms (excluding cygwin) add pthreads to Boost_LIBRARIES + # if the user is searching for the boost-thread component. + if(UNIX AND NOT CYGWIN) + list(FIND Boost_FIND_COMPONENTS thread _using_boost_thread) + if(_using_boost_thread GREATER -1) + find_library(BOOST_THREAD_LIBRARY NAMES pthread + DOC "The threading library used by boost-thread" + ) + if(BOOST_THREAD_LIBRARY) + set(${_outvar} ${ARGN} ${BOOST_THREAD_LIBRARY} PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + +# +# End functions/macros +# +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +# main. +#------------------------------------------------------------------------------- + +if(NOT DEFINED Boost_USE_MULTITHREADED) + set(Boost_USE_MULTITHREADED TRUE) +endif() + +# Check the version of Boost against the requested version. +if(Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR) + message(SEND_ERROR "When requesting a specific version of Boost, you must provide at least the major and minor version numbers, e.g., 1.34") +endif() + +if(Boost_FIND_VERSION_EXACT) + # The version may appear in a directory with or without the patch + # level, even when the patch level is non-zero. + set(_boost_TEST_VERSIONS + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}.${Boost_FIND_VERSION_PATCH}" + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") +else() + # The user has not requested an exact version. Among known + # versions, find those that are acceptable to the user request. + set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} + "1.56.0" "1.56" "1.55.0" "1.55" "1.54.0" "1.54" + "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" + "1.50.0" "1.50" "1.49.0" "1.49" "1.48.0" "1.48" "1.47.0" "1.47" "1.46.1" + "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43" "1.42.0" "1.42" + "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" + "1.36.1" "1.36.0" "1.36" "1.35.1" "1.35.0" "1.35" "1.34.1" "1.34.0" + "1.34" "1.33.1" "1.33.0" "1.33") + set(_boost_TEST_VERSIONS) + if(Boost_FIND_VERSION) + set(_Boost_FIND_VERSION_SHORT "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") + # Select acceptable versions. + foreach(version ${_Boost_KNOWN_VERSIONS}) + if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}") + # This version is high enough. + list(APPEND _boost_TEST_VERSIONS "${version}") + elseif("${version}.99" VERSION_EQUAL "${_Boost_FIND_VERSION_SHORT}.99") + # This version is a short-form for the requested version with + # the patch level dropped. + list(APPEND _boost_TEST_VERSIONS "${version}") + endif() + endforeach() + else() + # Any version is acceptable. + set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}") + endif() +endif() + +# The reason that we failed to find Boost. This will be set to a +# user-friendly message when we fail to find some necessary piece of +# Boost. +set(Boost_ERROR_REASON) + +if(Boost_DEBUG) + # Output some of their choices + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_USE_MULTITHREADED = ${Boost_USE_MULTITHREADED}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_USE_STATIC_LIBS = ${Boost_USE_STATIC_LIBS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_USE_STATIC_RUNTIME = ${Boost_USE_STATIC_RUNTIME}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_ADDITIONAL_VERSIONS = ${Boost_ADDITIONAL_VERSIONS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_NO_SYSTEM_PATHS = ${Boost_NO_SYSTEM_PATHS}") +endif() + +if(WIN32) + # In windows, automatic linking is performed, so you do not have + # to specify the libraries. If you are linking to a dynamic + # runtime, then you can choose to link to either a static or a + # dynamic Boost library, the default is to do a static link. You + # can alter this for a specific library "whatever" by defining + # BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to be + # linked dynamically. Alternatively you can force all Boost + # libraries to dynamic link by defining BOOST_ALL_DYN_LINK. + + # This feature can be disabled for Boost library "whatever" by + # defining BOOST_WHATEVER_NO_LIB, or for all of Boost by defining + # BOOST_ALL_NO_LIB. + + # If you want to observe which libraries are being linked against + # then defining BOOST_LIB_DIAGNOSTIC will cause the auto-linking + # code to emit a #pragma message each time a library is selected + # for linking. + set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC") +endif() + +_Boost_CHECK_SPELLING(Boost_ROOT) +_Boost_CHECK_SPELLING(Boost_LIBRARYDIR) +_Boost_CHECK_SPELLING(Boost_INCLUDEDIR) + +# Collect environment variable inputs as hints. Do not consider changes. +foreach(v BOOSTROOT BOOST_ROOT BOOST_INCLUDEDIR BOOST_LIBRARYDIR) + set(_env $ENV{${v}}) + if(_env) + file(TO_CMAKE_PATH "${_env}" _ENV_${v}) + else() + set(_ENV_${v} "") + endif() +endforeach() +if(NOT _ENV_BOOST_ROOT AND _ENV_BOOSTROOT) + set(_ENV_BOOST_ROOT "${_ENV_BOOSTROOT}") +endif() + +# Collect inputs and cached results. Detect changes since the last run. +if(NOT BOOST_ROOT AND BOOSTROOT) + set(BOOST_ROOT "${BOOSTROOT}") +endif() +set(_Boost_VARS_DIR + BOOST_ROOT + Boost_NO_SYSTEM_PATHS + ) + +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Declared as CMake or Environmental Variables:") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " BOOST_ROOT = ${BOOST_ROOT}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " BOOST_INCLUDEDIR = ${BOOST_INCLUDEDIR}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " BOOST_LIBRARYDIR = ${BOOST_LIBRARYDIR}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") +endif() + +# ------------------------------------------------------------------------ +# Search for Boost include DIR +# ------------------------------------------------------------------------ + +set(_Boost_VARS_INC BOOST_INCLUDEDIR Boost_INCLUDE_DIR Boost_ADDITIONAL_VERSIONS) +_Boost_CHANGE_DETECT(_Boost_CHANGE_INCDIR ${_Boost_VARS_DIR} ${_Boost_VARS_INC}) +# Clear Boost_INCLUDE_DIR if it did not change but other input affecting the +# location did. We will find a new one based on the new inputs. +if(_Boost_CHANGE_INCDIR AND NOT _Boost_INCLUDE_DIR_CHANGED) + unset(Boost_INCLUDE_DIR CACHE) +endif() + +if(NOT Boost_INCLUDE_DIR) + set(_boost_INCLUDE_SEARCH_DIRS "") + if(BOOST_INCLUDEDIR) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_INCLUDEDIR}) + elseif(_ENV_BOOST_INCLUDEDIR) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_INCLUDEDIR}) + endif() + + if( BOOST_ROOT ) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_ROOT}/include ${BOOST_ROOT}) + elseif( _ENV_BOOST_ROOT ) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_ROOT}/include ${_ENV_BOOST_ROOT}) + endif() + + if( Boost_NO_SYSTEM_PATHS) + list(APPEND _boost_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH) + else() + list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS + C:/boost/include + C:/boost + /sw/local/include + ) + endif() + + # Try to find Boost by stepping backwards through the Boost versions + # we know about. + # Build a list of path suffixes for each version. + set(_boost_PATH_SUFFIXES) + foreach(_boost_VER ${_boost_TEST_VERSIONS}) + # Add in a path suffix, based on the required version, ideally + # we could read this from version.hpp, but for that to work we'd + # need to know the include dir already + set(_boost_BOOSTIFIED_VERSION) + + # Transform 1.35 => 1_35 and 1.36.0 => 1_36_0 + if(_boost_VER MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+") + string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1_\\2_\\3" + _boost_BOOSTIFIED_VERSION ${_boost_VER}) + elseif(_boost_VER MATCHES "[0-9]+\\.[0-9]+") + string(REGEX REPLACE "([0-9]+)\\.([0-9]+)" "\\1_\\2" + _boost_BOOSTIFIED_VERSION ${_boost_VER}) + endif() + + list(APPEND _boost_PATH_SUFFIXES + "boost-${_boost_BOOSTIFIED_VERSION}" + "boost_${_boost_BOOSTIFIED_VERSION}" + "boost/boost-${_boost_BOOSTIFIED_VERSION}" + "boost/boost_${_boost_BOOSTIFIED_VERSION}" + ) + + endforeach() + + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Include debugging info:") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _boost_INCLUDE_SEARCH_DIRS = ${_boost_INCLUDE_SEARCH_DIRS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _boost_PATH_SUFFIXES = ${_boost_PATH_SUFFIXES}") + endif() + + # Look for a standard boost header file. + find_path(Boost_INCLUDE_DIR + NAMES boost/config.hpp + HINTS ${_boost_INCLUDE_SEARCH_DIRS} + PATH_SUFFIXES ${_boost_PATH_SUFFIXES} + ) +endif() + +# ------------------------------------------------------------------------ +# Extract version information from version.hpp +# ------------------------------------------------------------------------ + +# Set Boost_FOUND based only on header location and version. +# It will be updated below for component libraries. +if(Boost_INCLUDE_DIR) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp") + endif() + + # Extract Boost_VERSION and Boost_LIB_VERSION from version.hpp + set(Boost_VERSION 0) + set(Boost_LIB_VERSION "") + file(STRINGS "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS REGEX "#define BOOST_(LIB_)?VERSION ") + set(_Boost_VERSION_REGEX "([0-9]+)") + set(_Boost_LIB_VERSION_REGEX "\"([0-9_]+)\"") + foreach(v VERSION LIB_VERSION) + if("${_boost_VERSION_HPP_CONTENTS}" MATCHES ".*#define BOOST_${v} ${_Boost_${v}_REGEX}.*") + set(Boost_${v} "${CMAKE_MATCH_1}") + endif() + endforeach() + unset(_boost_VERSION_HPP_CONTENTS) + + math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") + math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000") + math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100") + + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\nBoost include path: ${Boost_INCLUDE_DIR}") + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "version.hpp reveals boost " + "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + endif() + + if(Boost_FIND_VERSION) + # Set Boost_FOUND based on requested version. + set(_Boost_VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + if("${_Boost_VERSION}" VERSION_LESS "${Boost_FIND_VERSION}") + set(Boost_FOUND 0) + set(_Boost_VERSION_AGE "old") + elseif(Boost_FIND_VERSION_EXACT AND + NOT "${_Boost_VERSION}" VERSION_EQUAL "${Boost_FIND_VERSION}") + set(Boost_FOUND 0) + set(_Boost_VERSION_AGE "new") + else() + set(Boost_FOUND 1) + endif() + if(NOT Boost_FOUND) + # State that we found a version of Boost that is too new or too old. + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}\nDetected version of Boost is too ${_Boost_VERSION_AGE}. Requested version was ${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") + if (Boost_FIND_VERSION_PATCH) + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}.${Boost_FIND_VERSION_PATCH}") + endif () + if (NOT Boost_FIND_VERSION_EXACT) + set(Boost_ERROR_REASON "${Boost_ERROR_REASON} (or newer)") + endif () + set(Boost_ERROR_REASON "${Boost_ERROR_REASON}.") + endif () + else() + # Caller will accept any Boost version. + set(Boost_FOUND 1) + endif() +else() + set(Boost_FOUND 0) + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}Unable to find the Boost header files. Please set BOOST_ROOT to the root directory containing Boost or BOOST_INCLUDEDIR to the directory containing Boost's headers.") +endif() + +# ------------------------------------------------------------------------ +# Suffix initialization and compiler suffix detection. +# ------------------------------------------------------------------------ + +set(_Boost_VARS_NAME + Boost_COMPILER + Boost_THREADAPI + Boost_USE_DEBUG_PYTHON + Boost_USE_MULTITHREADED + Boost_USE_STATIC_LIBS + Boost_USE_STATIC_RUNTIME + Boost_USE_STLPORT + Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS + ) +_Boost_CHANGE_DETECT(_Boost_CHANGE_LIBNAME ${_Boost_VARS_NAME}) + +# Setting some more suffixes for the library +set(Boost_LIB_PREFIX "") +if ( WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN) + set(Boost_LIB_PREFIX "lib") +endif() + +if (Boost_COMPILER) + set(_boost_COMPILER ${Boost_COMPILER}) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "using user-specified Boost_COMPILER = ${_boost_COMPILER}") + endif() +else() + # Attempt to guess the compiler suffix + # NOTE: this is not perfect yet, if you experience any issues + # please report them and use the Boost_COMPILER variable + # to work around the problems. + _Boost_GUESS_COMPILER_PREFIX(_boost_COMPILER) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "guessed _boost_COMPILER = ${_boost_COMPILER}") + endif() +endif() + +set (_boost_MULTITHREADED "-mt") +if( NOT Boost_USE_MULTITHREADED ) + set (_boost_MULTITHREADED "") +endif() +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_MULTITHREADED = ${_boost_MULTITHREADED}") +endif() + +#====================== +# Systematically build up the Boost ABI tag +# http://boost.org/doc/libs/1_41_0/more/getting_started/windows.html#library-naming +set( _boost_RELEASE_ABI_TAG "-") +set( _boost_DEBUG_ABI_TAG "-") +# Key Use this library when: +# s linking statically to the C++ standard library and +# compiler runtime support libraries. +if(Boost_USE_STATIC_RUNTIME) + set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}s") + set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}s") +endif() +# g using debug versions of the standard and runtime +# support libraries +if(WIN32) + if(MSVC OR "${CMAKE_CXX_COMPILER}" MATCHES "icl" + OR "${CMAKE_CXX_COMPILER}" MATCHES "icpc") + set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}g") + endif() +endif() +# y using special debug build of python +if(Boost_USE_DEBUG_PYTHON) + set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}y") +endif() +# d using a debug version of your code +set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}d") +# p using the STLport standard library rather than the +# default one supplied with your compiler +if(Boost_USE_STLPORT) + set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}p") + set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}p") +endif() +# n using the STLport deprecated "native iostreams" feature +if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) + set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}n") + set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}n") +endif() + +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_RELEASE_ABI_TAG = ${_boost_RELEASE_ABI_TAG}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_DEBUG_ABI_TAG = ${_boost_DEBUG_ABI_TAG}") +endif() + +# ------------------------------------------------------------------------ +# Begin finding boost libraries +# ------------------------------------------------------------------------ +set(_Boost_VARS_LIB BOOST_LIBRARYDIR Boost_LIBRARY_DIR) +_Boost_CHANGE_DETECT(_Boost_CHANGE_LIBDIR ${_Boost_VARS_DIR} ${_Boost_VARS_LIB} Boost_INCLUDE_DIR) +# Clear Boost_LIBRARY_DIR if it did not change but other input affecting the +# location did. We will find a new one based on the new inputs. +if(_Boost_CHANGE_LIBDIR AND NOT _Boost_LIBRARY_DIR_CHANGED) + unset(Boost_LIBRARY_DIR CACHE) +endif() + +if(Boost_LIBRARY_DIR) + set(_boost_LIBRARY_SEARCH_DIRS ${Boost_LIBRARY_DIR} NO_DEFAULT_PATH) +else() + set(_boost_LIBRARY_SEARCH_DIRS "") + if(BOOST_LIBRARYDIR) + list(APPEND _boost_LIBRARY_SEARCH_DIRS ${BOOST_LIBRARYDIR}) + elseif(_ENV_BOOST_LIBRARYDIR) + list(APPEND _boost_LIBRARY_SEARCH_DIRS ${_ENV_BOOST_LIBRARYDIR}) + endif() + + if(BOOST_ROOT) + list(APPEND _boost_LIBRARY_SEARCH_DIRS ${BOOST_ROOT}/lib ${BOOST_ROOT}/stage/lib) + elseif(_ENV_BOOST_ROOT) + list(APPEND _boost_LIBRARY_SEARCH_DIRS ${_ENV_BOOST_ROOT}/lib ${_ENV_BOOST_ROOT}/stage/lib) + endif() + + list(APPEND _boost_LIBRARY_SEARCH_DIRS + ${Boost_INCLUDE_DIR}/lib + ${Boost_INCLUDE_DIR}/../lib + ${Boost_INCLUDE_DIR}/../lib/${CMAKE_LIBRARY_ARCHITECTURE} + ${Boost_INCLUDE_DIR}/stage/lib + ) + if( Boost_NO_SYSTEM_PATHS ) + list(APPEND _boost_LIBRARY_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH) + else() + list(APPEND _boost_LIBRARY_SEARCH_DIRS PATHS + C:/boost/lib + C:/boost + /sw/local/lib + ) + endif() +endif() + +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_LIBRARY_SEARCH_DIRS = ${_boost_LIBRARY_SEARCH_DIRS}") +endif() + +# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES +if( Boost_USE_STATIC_LIBS ) + set( _boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) + endif() +endif() + +# We want to use the tag inline below without risking double dashes +if(_boost_RELEASE_ABI_TAG) + if(${_boost_RELEASE_ABI_TAG} STREQUAL "-") + set(_boost_RELEASE_ABI_TAG "") + endif() +endif() +if(_boost_DEBUG_ABI_TAG) + if(${_boost_DEBUG_ABI_TAG} STREQUAL "-") + set(_boost_DEBUG_ABI_TAG "") + endif() +endif() + +# The previous behavior of FindBoost when Boost_USE_STATIC_LIBS was enabled +# on WIN32 was to: +# 1. Search for static libs compiled against a SHARED C++ standard runtime library (use if found) +# 2. Search for static libs compiled against a STATIC C++ standard runtime library (use if found) +# We maintain this behavior since changing it could break people's builds. +# To disable the ambiguous behavior, the user need only +# set Boost_USE_STATIC_RUNTIME either ON or OFF. +set(_boost_STATIC_RUNTIME_WORKAROUND false) +if(WIN32 AND Boost_USE_STATIC_LIBS) + if(NOT DEFINED Boost_USE_STATIC_RUNTIME) + set(_boost_STATIC_RUNTIME_WORKAROUND true) + endif() +endif() + +# On versions < 1.35, remove the System library from the considered list +# since it wasn't added until 1.35. +if(Boost_VERSION AND Boost_FIND_COMPONENTS) + if(Boost_VERSION LESS 103500) + list(REMOVE_ITEM Boost_FIND_COMPONENTS system) + endif() +endif() + +# If the user changed any of our control inputs flush previous results. +if(_Boost_CHANGE_LIBDIR OR _Boost_CHANGE_LIBNAME) + foreach(COMPONENT ${_Boost_COMPONENTS_SEARCHED}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + foreach(c DEBUG RELEASE) + set(_var Boost_${UPPERCOMPONENT}_LIBRARY_${c}) + unset(${_var} CACHE) + set(${_var} "${_var}-NOTFOUND") + endforeach() + endforeach() + set(_Boost_COMPONENTS_SEARCHED "") +endif() + +foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + + set( _boost_docstring_release "Boost ${COMPONENT} library (release)") + set( _boost_docstring_debug "Boost ${COMPONENT} library (debug)") + + # + # Find RELEASE libraries + # + set(_boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} + ${Boost_LIB_PREFIX}boost_${COMPONENT} ) + if(_boost_STATIC_RUNTIME_WORKAROUND) + set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}") + list(APPEND _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) + endif() + if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") + _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES}) + endif() + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: ${_boost_RELEASE_NAMES}") + endif() + + # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. + string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS}") + + _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE + NAMES ${_boost_RELEASE_NAMES} + HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp} + NAMES_PER_DIR + DOC "${_boost_docstring_release}" + ) + + # + # Find DEBUG libraries + # + set(_boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED} + ${Boost_LIB_PREFIX}boost_${COMPONENT} ) + if(_boost_STATIC_RUNTIME_WORKAROUND) + set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}") + list(APPEND _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) + endif() + if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") + _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_DEBUG_NAMES ${_boost_DEBUG_NAMES}) + endif() + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: ${_boost_DEBUG_NAMES}") + endif() + + # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. + string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS}") + + _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG + NAMES ${_boost_DEBUG_NAMES} + HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp} + NAMES_PER_DIR + DOC "${_boost_docstring_debug}" + ) + + if(Boost_REALPATH) + _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "${_boost_docstring_release}") + _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG "${_boost_docstring_debug}" ) + endif() + + _Boost_ADJUST_LIB_VARS(${UPPERCOMPONENT}) + +endforeach() + +# Restore the original find library ordering +if( Boost_USE_STATIC_LIBS ) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +endif() + +# ------------------------------------------------------------------------ +# End finding boost libraries +# ------------------------------------------------------------------------ + +set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) +set(Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR}) + +# The above setting of Boost_FOUND was based only on the header files. +# Update it for the requested component libraries. +if(Boost_FOUND) + # The headers were found. Check for requested component libs. + set(_boost_CHECKED_COMPONENT FALSE) + set(_Boost_MISSING_COMPONENTS "") + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} COMPONENT) + set(_boost_CHECKED_COMPONENT TRUE) + if(NOT Boost_${COMPONENT}_FOUND) + string(TOLOWER ${COMPONENT} COMPONENT) + list(APPEND _Boost_MISSING_COMPONENTS ${COMPONENT}) + endif() + endforeach() + + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] Boost_FOUND = ${Boost_FOUND}") + endif() + + if (_Boost_MISSING_COMPONENTS) + set(Boost_FOUND 0) + # We were unable to find some libraries, so generate a sensible + # error message that lists the libraries we were unable to find. + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}\nThe following Boost libraries could not be found:\n") + foreach(COMPONENT ${_Boost_MISSING_COMPONENTS}) + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON} boost_${COMPONENT}\n") + endforeach() + + list(LENGTH Boost_FIND_COMPONENTS Boost_NUM_COMPONENTS_WANTED) + list(LENGTH _Boost_MISSING_COMPONENTS Boost_NUM_MISSING_COMPONENTS) + if (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") + else () + set(Boost_ERROR_REASON + "${Boost_ERROR_REASON}Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") + endif () + endif () + + if( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT ) + # Compatibility Code for backwards compatibility with CMake + # 2.4's FindBoost module. + + # Look for the boost library path. + # Note that the user may not have installed any libraries + # so it is quite possible the Boost_LIBRARY_DIRS may not exist. + set(_boost_LIB_DIR ${Boost_INCLUDE_DIR}) + + if("${_boost_LIB_DIR}" MATCHES "boost-[0-9]+") + get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) + endif() + + if("${_boost_LIB_DIR}" MATCHES "/include$") + # Strip off the trailing "/include" in the path. + get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) + endif() + + if(EXISTS "${_boost_LIB_DIR}/lib") + set(_boost_LIB_DIR ${_boost_LIB_DIR}/lib) + else() + if(EXISTS "${_boost_LIB_DIR}/stage/lib") + set(_boost_LIB_DIR ${_boost_LIB_DIR}/stage/lib) + else() + set(_boost_LIB_DIR "") + endif() + endif() + + if(_boost_LIB_DIR AND EXISTS "${_boost_LIB_DIR}") + set(Boost_LIBRARY_DIRS ${_boost_LIB_DIR}) + endif() + + endif() +else() + # Boost headers were not found so no components were found. + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + set(Boost_${UPPERCOMPONENT}_FOUND 0) + endforeach() +endif() + +# ------------------------------------------------------------------------ +# Notification to end user about what was found +# ------------------------------------------------------------------------ + +set(Boost_LIBRARIES "") +if(Boost_FOUND) + if(NOT Boost_FIND_QUIETLY) + message(STATUS "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + if(Boost_FIND_COMPONENTS) + message(STATUS "Found the following Boost libraries:") + endif() + endif() + foreach( COMPONENT ${Boost_FIND_COMPONENTS} ) + string( TOUPPER ${COMPONENT} UPPERCOMPONENT ) + if( Boost_${UPPERCOMPONENT}_FOUND ) + if(NOT Boost_FIND_QUIETLY) + message (STATUS " ${COMPONENT}") + endif() + list(APPEND Boost_LIBRARIES ${Boost_${UPPERCOMPONENT}_LIBRARY}) + endif() + endforeach() + + # Add pthread library on UNIX if thread component was found + _Boost_consider_adding_pthreads(Boost_LIBRARIES ${Boost_LIBRARIES}) +else() + if(Boost_FIND_REQUIRED) + message(SEND_ERROR "Unable to find the requested Boost libraries.\n${Boost_ERROR_REASON}") + else() + if(NOT Boost_FIND_QUIETLY) + # we opt not to automatically output Boost_ERROR_REASON here as + # it could be quite lengthy and somewhat imposing in its requests + # Since Boost is not always a required dependency we'll leave this + # up to the end-user. + if(Boost_DEBUG OR Boost_DETAILED_FAILURE_MSG) + message(STATUS "Could NOT find Boost\n${Boost_ERROR_REASON}") + else() + message(STATUS "Could NOT find Boost") + endif() + endif() + endif() +endif() + +# Configure display of cache entries in GUI. +foreach(v BOOSTROOT BOOST_ROOT ${_Boost_VARS_INC} ${_Boost_VARS_LIB}) + get_property(_type CACHE ${v} PROPERTY TYPE) + if(_type) + set_property(CACHE ${v} PROPERTY ADVANCED 1) + if("x${_type}" STREQUAL "xUNINITIALIZED") + if("x${v}" STREQUAL "xBoost_ADDITIONAL_VERSIONS") + set_property(CACHE ${v} PROPERTY TYPE STRING) + else() + set_property(CACHE ${v} PROPERTY TYPE PATH) + endif() + endif() + endif() +endforeach() + +# Record last used values of input variables so we can +# detect on the next run if the user changed them. +foreach(v + ${_Boost_VARS_INC} ${_Boost_VARS_LIB} + ${_Boost_VARS_DIR} ${_Boost_VARS_NAME} + ) + if(DEFINED ${v}) + set(_${v}_LAST "${${v}}" CACHE INTERNAL "Last used ${v} value.") + else() + unset(_${v}_LAST CACHE) + endif() +endforeach() + +# Maintain a persistent list of components requested anywhere since +# the last flush. +set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}") +list(APPEND _Boost_COMPONENTS_SEARCHED ${Boost_FIND_COMPONENTS}) +list(REMOVE_DUPLICATES _Boost_COMPONENTS_SEARCHED) +list(SORT _Boost_COMPONENTS_SEARCHED) +set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}" + CACHE INTERNAL "Components requested for this build tree.") diff --git a/CMakeModules/FindReadline.cmake b/CMakeModules/FindReadline.cmake new file mode 100644 index 000000000..745cfe583 --- /dev/null +++ b/CMakeModules/FindReadline.cmake @@ -0,0 +1,47 @@ +# - Try to find readline include dirs and libraries +# +# Usage of this module as follows: +# +# find_package(Readline) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# Readline_ROOT_DIR Set this variable to the root installation of +# readline if the module has problems finding the +# proper installation path. +# +# Variables defined by this module: +# +# READLINE_FOUND System has readline, include and lib dirs found +# Readline_INCLUDE_DIR The readline include directories. +# Readline_LIBRARY The readline library. + +find_path(Readline_ROOT_DIR + NAMES include/readline/readline.h +) + +find_path(Readline_INCLUDE_DIR + NAMES readline/readline.h + HINTS ${Readline_ROOT_DIR}/include +) + +find_library(Readline_LIBRARY + NAMES readline + HINTS ${Readline_ROOT_DIR}/lib +) + +if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + set(READLINE_FOUND TRUE) +else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + FIND_LIBRARY(Readline_LIBRARY NAMES readline) + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) + MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) +endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + +mark_as_advanced( + Readline_ROOT_DIR + Readline_INCLUDE_DIR + Readline_LIBRARY +) diff --git a/CMakeModules/FindVLD.cmake b/CMakeModules/FindVLD.cmake new file mode 100644 index 000000000..716625c94 --- /dev/null +++ b/CMakeModules/FindVLD.cmake @@ -0,0 +1,123 @@ +# Module for locating Visual Leak Detector. +# +# Customizable variables: +# VLD_ROOT_DIR +# This variable points to the Visual Leak Detector root directory. By +# default, the module looks for the installation directory by examining the +# Program Files/Program Files (x86) folders and the VLDROOT environment +# variable. +# +# Read-only variables: +# VLD_FOUND +# Indicates that the library has been found. +# +# VLD_INCLUDE_DIRS +# Points to the Visual Leak Detector include directory. +# +# VLD_LIBRARY_DIRS +# Points to the Visual Leak Detector directory that contains the libraries. +# The content of this variable can be passed to link_directories. +# +# VLD_LIBRARIES +# Points to the Visual Leak Detector libraries that can be passed to +# target_link_libararies. +# +# +# Copyright (c) 2012 Sergiu Dotenco +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +INCLUDE (FindPackageHandleStandardArgs) + +SET (_VLD_POSSIBLE_LIB_SUFFIXES lib) + +# Version 2.0 uses vld_x86 and vld_x64 instead of simply vld as library names +IF (CMAKE_SIZEOF_VOID_P EQUAL 4) + LIST (APPEND _VLD_POSSIBLE_LIB_SUFFIXES lib/Win32) +ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 8) + LIST (APPEND _VLD_POSSIBLE_LIB_SUFFIXES lib/Win64) +ENDIF (CMAKE_SIZEOF_VOID_P EQUAL 4) + +FIND_PATH (VLD_ROOT_DIR + NAMES include/vld.h + PATHS ENV VLDROOT + "$ENV{PROGRAMFILES}/Visual Leak Detector" + "$ENV{PROGRAMFILES(X86)}/Visual Leak Detector" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Visual Leak Detector;InstallLocation]" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Visual Leak Detector;InstallLocation]" + DOC "VLD root directory") + +FIND_PATH (VLD_INCLUDE_DIR + NAMES vld.h + HINTS ${VLD_ROOT_DIR} + PATH_SUFFIXES include + DOC "VLD include directory") + +FIND_LIBRARY (VLD_LIBRARY_DEBUG + NAMES vld + HINTS ${VLD_ROOT_DIR} + PATH_SUFFIXES ${_VLD_POSSIBLE_LIB_SUFFIXES} + DOC "VLD debug library") + +IF (VLD_ROOT_DIR) + SET (_VLD_VERSION_FILE ${VLD_ROOT_DIR}/CHANGES.txt) + + IF (EXISTS ${_VLD_VERSION_FILE}) + SET (_VLD_VERSION_REGEX + "Visual Leak Detector \\(VLD\\) Version (([0-9]+)\\.([0-9]+)([a-z]|(.([0-9]+)))?)") + FILE (STRINGS ${_VLD_VERSION_FILE} _VLD_VERSION_TMP REGEX + ${_VLD_VERSION_REGEX}) + + STRING (REGEX REPLACE ${_VLD_VERSION_REGEX} "\\1" _VLD_VERSION_TMP + "${_VLD_VERSION_TMP}") + + STRING (REGEX REPLACE "([0-9]+).([0-9]+).*" "\\1" VLD_VERSION_MAJOR + "${_VLD_VERSION_TMP}") + STRING (REGEX REPLACE "([0-9]+).([0-9]+).*" "\\2" VLD_VERSION_MINOR + "${_VLD_VERSION_TMP}") + + SET (VLD_VERSION ${VLD_VERSION_MAJOR}.${VLD_VERSION_MINOR}) + + IF ("${_VLD_VERSION_TMP}" MATCHES "^([0-9]+).([0-9]+).([0-9]+)$") + # major.minor.patch version numbering scheme + STRING (REGEX REPLACE "([0-9]+).([0-9]+).([0-9]+)" "\\3" + VLD_VERSION_PATCH "${_VLD_VERSION_TMP}") + SET (VLD_VERSION "${VLD_VERSION}.${VLD_VERSION_PATCH}") + SET (VLD_VERSION_COUNT 3) + ELSE ("${_VLD_VERSION_TMP}" MATCHES "^([0-9]+).([0-9]+).([0-9]+)$") + # major.minor version numbering scheme. The trailing letter is ignored. + SET (VLD_VERSION_COUNT 2) + ENDIF ("${_VLD_VERSION_TMP}" MATCHES "^([0-9]+).([0-9]+).([0-9]+)$") + ENDIF (EXISTS ${_VLD_VERSION_FILE}) +ENDIF (VLD_ROOT_DIR) + +IF (VLD_LIBRARY_DEBUG) + SET (VLD_LIBRARY debug ${VLD_LIBRARY_DEBUG} CACHE DOC "VLD library") + GET_FILENAME_COMPONENT (_VLD_LIBRARY_DIR ${VLD_LIBRARY_DEBUG} PATH) + SET (VLD_LIBRARY_DIR ${_VLD_LIBRARY_DIR} CACHE PATH "VLD library directory") +ENDIF (VLD_LIBRARY_DEBUG) + +SET (VLD_INCLUDE_DIRS ${VLD_INCLUDE_DIR}) +SET (VLD_LIBRARY_DIRS ${VLD_LIBRARY_DIR}) +SET (VLD_LIBRARIES ${VLD_LIBRARY}) + +MARK_AS_ADVANCED (VLD_INCLUDE_DIR VLD_LIBRARY_DIR VLD_LIBRARY_DEBUG VLD_LIBRARY) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS (VLD REQUIRED_VARS VLD_ROOT_DIR + VLD_INCLUDE_DIR VLD_LIBRARY VERSION_VAR VLD_VERSION) diff --git a/CMakeModules/ParseLibraryList.cmake b/CMakeModules/ParseLibraryList.cmake new file mode 100644 index 000000000..e559b9d53 --- /dev/null +++ b/CMakeModules/ParseLibraryList.cmake @@ -0,0 +1,79 @@ +# -*- mode: cmake -*- + +# +# Shamelessly stolen from MSTK who shamelessly stole from Amanzi open source code https://software.lanl.gov/ascem/trac) +# +# PARSE_LIBRARY_LIST( +# DEBUG +# OPT +# GENERAL ) + +# CMake module +include(CMakeParseArguments) + +function(PARSE_LIBRARY_LIST) + + # Macro: _print_usage + macro(_print_usage) + message("PARSE_LIBRARY_LIST \n" + " FOUND \n" + " DEBUG \n" + " OPT \n" + " GENERAL \n" + "lib_list string to parse\n" + "FOUND flag to indicate if keywords were found\n" + "DEBUG variable containing debug libraries\n" + "OPT variable containing optimized libraries\n" + "GENERAL variable containing debug libraries\n") + + endmacro() + + # Read in args + cmake_parse_arguments(PARSE_ARGS "" "FOUND;DEBUG;OPT;GENERAL" "" ${ARGN}) + set(_parse_list "${PARSE_ARGS_UNPARSED_ARGUMENTS}") + if ( (NOT PARSE_ARGS_FOUND) OR + (NOT PARSE_ARGS_DEBUG) OR + (NOT PARSE_ARGS_OPT) OR + (NOT PARSE_ARGS_GENERAL) OR + (NOT _parse_list ) + ) + _print_usage() + message(FATAL_ERROR "Invalid arguments") + endif() + + # Now split the list + set(_debug_libs "") + set(_opt_libs "") + set(_gen_libs "") + foreach( item ${_parse_list} ) + if( ${item} MATCHES debug OR + ${item} MATCHES optimized OR + ${item} MATCHES general ) + + if( ${item} STREQUAL "debug" ) + set( mylist "_debug_libs" ) + elseif( ${item} STREQUAL "optimized" ) + set( mylist "_opt_libs" ) + elseif( ${item} STREQUAL "general" ) + set( mylist "_gen_libs" ) + endif() + else() + list( APPEND ${mylist} ${item} ) + endif() + endforeach() + + + # Now set output vairables + set(${PARSE_ARGS_DEBUG} "${_debug_libs}" PARENT_SCOPE) + set(${PARSE_ARGS_OPT} "${_opt_libs}" PARENT_SCOPE) + set(${PARSE_ARGS_GENERAL} "${_gen_libs}" PARENT_SCOPE) + + # If any of the lib lists are defined set flag to TRUE + if ( (_debug_libs) OR (_opt_libs) OR (_gen_libs) ) + set(${PARSE_ARGS_FOUND} TRUE PARENT_SCOPE) + else() + set(${PARSE_ARGS_FOUND} FALSE PARENT_SCOPE) + endif() + +endfunction(PARSE_LIBRARY_LIST) + diff --git a/GitVersionGen/GetGitRevisionDescription.cmake b/GitVersionGen/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..00f872715 --- /dev/null +++ b/GitVersionGen/GetGitRevisionDescription.cmake @@ -0,0 +1,176 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(get_git_unix_timestamp _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + # message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + "show" + "-s" + "--format=%ct" + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() diff --git a/GitVersionGen/GetGitRevisionDescription.cmake.in b/GitVersionGen/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..888ce13aa --- /dev/null +++ b/GitVersionGen/GetGitRevisionDescription.cmake.in @@ -0,0 +1,38 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") + configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + set(HEAD_HASH "${HEAD_REF}") + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/README-ecc.md b/README-ecc.md new file mode 100644 index 000000000..32942d2b1 --- /dev/null +++ b/README-ecc.md @@ -0,0 +1,43 @@ +ECC Support +=========== + +include/fc/crypto/elliptic.hpp defines an interface for some cryptographic +wrapper classes handling elliptic curve cryptography. + +Three implementations of this interface exist. One is based on OpenSSL, the +others are based on libsecp256k1 (see https://github.com/bitcoin/secp256k1 ). +The implementation to be used is selected at compile time using the +cmake variable "ECC_IMPL". It can take one of three values, openssl or +secp256k1 or mixed . +The default is "openssl". The alternatives can be configured when invoking +cmake, for example + +cmake -D ECC_IMPL=secp256k1 . + +If secp256k1 or mixed is chosen, the secp256k1 library and its include file +must already be installed in the appropriate library / include directories on +your system. + + +Testing +------- + +Type "make ecc_test" to build the ecc_test executable from +tests/crypto/ecc_test.cpp with the currently configured ECC implementation. + +ecc_test expects two arguments: + +ecc_test + + is a somewhat arbitrary password used for testing. + + is a data file containing intermediate test results. +If the file does not exist, it will be created and intermediate results from +the current ECC backend are written to it. +If the file does exist, intermediate results from the current ECC backend +are compared with the file contents. + +For a full round of interoperability testing, you can use the script +tests/crypto/ecc-interop.sh . +None of the test runs should produce any output. + diff --git a/README.md b/README.md index cae31d1da..31b0e44ea 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,14 @@ -# Fast Compiliing C++ Library ------------------------------------------ - -In my prior attempts at developing MACE what I discovered is that compile times -would explode to unreasonable levels that hinder the rate of development more -than what can be saved by reduced typing. So I began a quest to get C++ to compile -as quickly as Java or C# and the result is this library. - -One of the major drawbacks to templates is that they place everything in header and -must be compiled with every run and generate a lot of object code. With Link Time Optimization, -the benefit of inline methods mostly disapears, leaving only static vs dynamic polymorphism. - -For the vast majority of applications, a virtual method call is not the bottleneck and the -increased compile times costs more than is otherwise justified; therefore, the Fast Compiling C++ -library opts for virtual interfaces to handle reflection instead of template interfaces. One could -argue that both types of reflection could be useful. - -Another source of slowness was the standard template library itself. Most standard template library -classes cannot be forward declared and import thousands of lines of code into every compilation unit. - -Another source of slowness is the need to include headers simply because the 'size' of the object must -be known. A new utility class allows you to 'forward declare' the size required for certain types which -allows you to remove their inclusion from the header file. - - +fc +== + +FC stands for fast-compiling c++ library and provides a set of utility libraries useful +for the development of asynchronous libraries. Some of the highlights include: + + - Cooperative Multi-Tasking Library with support for Futures, mutexes, signals. + - Wrapper on Boost ASIO for handling async opperations cooperatively with easy to code synchronous style. + - Reflection for C++ allowing automatic serialization in Json & Binary of C++ Structs + - Automatic generation of client / server stubs for reflected interfaces with support for JSON-RPC + - Cryptographic Primitives for a variaty of hashes and encryption algorithms + - Logging Infrastructure + - Wraps many Boost APIs, such as boost::filesystem, boost::thread, and boost::exception to acceleate compiles + - Support for unofficial Boost.Process library. diff --git a/fc.natvis b/fc.natvis new file mode 100644 index 000000000..7188bfa86 --- /dev/null +++ b/fc.natvis @@ -0,0 +1,57 @@ + + + + + + *(const int64_t*)(&_data) + *(const uint64_t*)(&_data) + *(const double*)(&_data) + *(const bool*)(&_data) + *(const const_string_ptr*)(&_data) + *(const const_variants_ptr*)(&_data) + *(const const_variant_object_ptr*)(&_data) + (fc::variant::type_id)(*(((char*)(&_data)) + sizeof(fc::variant) - 1)) + + + + + {*($T1*)(((_store)._store)._data)} + + ($T1*)(((_store)._store)._data) + + + + + {my} + + my + + + + + invalid + valid, value = {*(($T1*)_value)} + + ($T1*)_value + _valid + + + + + {_p} + + _p + + + + \ No newline at end of file diff --git a/include/boost/atomic.hpp b/include/boost/atomic.hpp deleted file mode 100644 index a8b9c5527..000000000 --- a/include/boost/atomic.hpp +++ /dev/null @@ -1,217 +0,0 @@ -#ifndef BOOST_ATOMIC_HPP -#define BOOST_ATOMIC_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include - -#include -#include -#include -#include -#include -//#include - -namespace boost { - -template -class atomic : public detail::atomic::internal_atomic { -public: - typedef detail::atomic::internal_atomic super; - - atomic() {} - explicit atomic(T v) : super(v) {} -private: - atomic(const atomic &); - void operator=(const atomic &); -}; - - -template<> -class atomic : private detail::atomic::internal_atomic { -public: - typedef detail::atomic::internal_atomic super; - - atomic() {} - explicit atomic(bool v) : super(v) {} - - using super::load; - using super::store; - using super::compare_exchange_strong; - using super::compare_exchange_weak; - using super::exchange; - using super::is_lock_free; - - operator bool(void) const volatile {return load();} - bool operator=(bool v) volatile {store(v); return v;} -private: - atomic(const atomic &); - void operator=(const atomic &); -}; - -template<> -class atomic : private detail::atomic::internal_atomic { -public: - typedef detail::atomic::internal_atomic super; - - atomic() {} - explicit atomic(void * p) : super(p) {} - using super::load; - using super::store; - using super::compare_exchange_strong; - using super::compare_exchange_weak; - using super::exchange; - using super::is_lock_free; - - operator void *(void) const volatile {return load();} - void * operator=(void * v) volatile {store(v); return v;} - -private: - atomic(const atomic &); - void * operator=(const atomic &); -}; - -/* FIXME: pointer arithmetic still missing */ - -template -class atomic : private detail::atomic::internal_atomic { -public: - typedef detail::atomic::internal_atomic super; - - atomic() {} - explicit atomic(T * p) : super((intptr_t)p) {} - - T *load(memory_order order=memory_order_seq_cst) const volatile - { - return (T*)super::load(order); - } - void store(T *v, memory_order order=memory_order_seq_cst) volatile - { - super::store((intptr_t)v, order); - } - bool compare_exchange_strong( - T * &expected, - T * desired, - memory_order order=memory_order_seq_cst) volatile - { - return compare_exchange_strong(expected, desired, order, detail::atomic::calculate_failure_order(order)); - } - bool compare_exchange_weak( - T * &expected, - T *desired, - memory_order order=memory_order_seq_cst) volatile - { - return compare_exchange_weak(expected, desired, order, detail::atomic::calculate_failure_order(order)); - } - bool compare_exchange_weak( - T * &expected, - T *desired, - memory_order success_order, - memory_order failure_order) volatile - { - intptr_t expected_=(intptr_t)expected; - intptr_t desired_=(intptr_t)desired; - bool success=super::compare_exchange_weak(expected_, desired_, success_order, failure_order); - expected=(T*)expected_; - return success; - } - bool compare_exchange_strong( - T * &expected, - T *desired, - memory_order success_order, - memory_order failure_order) volatile - { - intptr_t expected_=(intptr_t)expected, desired_=(intptr_t)desired; - bool success=super::compare_exchange_strong(expected_, desired_, success_order, failure_order); - expected=(T*)expected_; - return success; - } - T *exchange(T * replacement, memory_order order=memory_order_seq_cst) volatile - { - return (T*)super::exchange((intptr_t)replacement, order); - } - using super::is_lock_free; - - operator T *(void) const volatile {return load();} - T * operator=(T * v) volatile {store(v); return v;} - - T * fetch_add(ptrdiff_t diff, memory_order order=memory_order_seq_cst) volatile - { - return (T*)super::fetch_add(diff*sizeof(T), order); - } - T * fetch_sub(ptrdiff_t diff, memory_order order=memory_order_seq_cst) volatile - { - return (T*)super::fetch_sub(diff*sizeof(T), order); - } - - T *operator++(void) volatile {return fetch_add(1)+1;} - T *operator++(int) volatile {return fetch_add(1);} - T *operator--(void) volatile {return fetch_sub(1)-1;} - T *operator--(int) volatile {return fetch_sub(1);} -private: - atomic(const atomic &); - T * operator=(const atomic &); -}; - -class atomic_flag : private atomic { -public: - typedef atomic super; - using super::is_lock_free; - - atomic_flag(bool initial_state) : super(initial_state?1:0) {} - atomic_flag() {} - - bool test_and_set(memory_order order=memory_order_seq_cst) - { - return super::exchange(1, order) ? true : false; - } - void clear(memory_order order=memory_order_seq_cst) - { - super::store(0, order); - } -}; - -typedef atomic atomic_char; -typedef atomic atomic_uchar; -typedef atomic atomic_schar; -typedef atomic atomic_uint8_t; -typedef atomic atomic_int8_t; -typedef atomic atomic_ushort; -typedef atomic atomic_short; -typedef atomic atomic_uint16_t; -typedef atomic atomic_int16_t; -typedef atomic atomic_uint; -typedef atomic atomic_int; -typedef atomic atomic_uint32_t; -typedef atomic atomic_int32_t; -typedef atomic atomic_ulong; -typedef atomic atomic_long; -typedef atomic atomic_uint64_t; -typedef atomic atomic_int64_t; -#ifdef BOOST_HAS_LONG_LONG -typedef atomic atomic_ullong; -typedef atomic atomic_llong; -#endif -#ifdef BOOST_ATOMIC_HAVE_GNU_128BIT_INTEGERS -typedef atomic<__uint128_t> atomic_uint128_t; -typedef atomic<__int128_t> atomic_int128_t; -#endif -#if BOOST_MSVC >= 1500 && (defined(_M_IA64) || defined(_M_AMD64)) && defined(BOOST_ATOMIC_HAVE_SSE2) -typedef atomic<__m128i> atomic_uint128_t; -typedef atomic<__m128i> atomic_int128_t; -#endif -typedef atomic atomic_address; -typedef atomic atomic_bool; - -static inline void atomic_thread_fence(memory_order order) -{ - detail::atomic::platform_atomic_thread_fence(order); -} - -} - -#endif diff --git a/include/boost/atomic/detail/base.hpp b/include/boost/atomic/detail/base.hpp deleted file mode 100644 index daa0d94d2..000000000 --- a/include/boost/atomic/detail/base.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_BASE_HPP -#define BOOST_DETAIL_ATOMIC_BASE_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - -static inline memory_order calculate_failure_order(memory_order order) -{ - switch(order) { - case memory_order_acq_rel: return memory_order_acquire; - case memory_order_release: return memory_order_relaxed; - default: return order; - } -} - -template -class platform_atomic : public fallback_atomic { -public: - typedef fallback_atomic super; - - explicit platform_atomic(T v) : super(v) {} - platform_atomic() {} -protected: - typedef typename super::integral_type integral_type; -}; - -template -class platform_atomic_integral : public build_atomic_from_exchange > { -public: - typedef build_atomic_from_exchange > super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral() {} -protected: - typedef typename super::integral_type integral_type; -}; - -template -static inline void platform_atomic_thread_fence(T order) -{ - /* FIXME: this does not provide - sequential consistency, need one global - variable for that... */ - platform_atomic a; - a.exchange(0, order); -} - -template::test> -class internal_atomic; - -template -class internal_atomic : private detail::atomic::platform_atomic { -public: - typedef detail::atomic::platform_atomic super; - - internal_atomic() {} - explicit internal_atomic(T v) : super(v) {} - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; - using super::load; - using super::store; - using super::exchange; - - bool compare_exchange_strong( - T &expected, - T desired, - memory_order order=memory_order_seq_cst) volatile - { - return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order order=memory_order_seq_cst) volatile - { - return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return super::compare_exchange_strong(expected, desired, success_order, failure_order); - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return super::compare_exchange_strong(expected, desired, success_order, failure_order); - } -private: - internal_atomic(const internal_atomic &); - void operator=(const internal_atomic &); -}; - -template -class internal_atomic : private detail::atomic::platform_atomic_integral { -public: - typedef detail::atomic::platform_atomic_integral super; - typedef typename super::integral_type integral_type; - - internal_atomic() {} - explicit internal_atomic(T v) : super(v) {} - - using super::is_lock_free; - using super::load; - using super::store; - using super::exchange; - using super::fetch_add; - using super::fetch_sub; - using super::fetch_and; - using super::fetch_or; - using super::fetch_xor; - - operator integral_type(void) const volatile {return load();} - integral_type operator=(integral_type v) volatile {store(v); return v;} - - integral_type operator&=(integral_type c) volatile {return fetch_and(c)&c;} - integral_type operator|=(integral_type c) volatile {return fetch_or(c)|c;} - integral_type operator^=(integral_type c) volatile {return fetch_xor(c)^c;} - - integral_type operator+=(integral_type c) volatile {return fetch_add(c)+c;} - integral_type operator-=(integral_type c) volatile {return fetch_sub(c)-c;} - - integral_type operator++(void) volatile {return fetch_add(1)+1;} - integral_type operator++(int) volatile {return fetch_add(1);} - integral_type operator--(void) volatile {return fetch_sub(1)-1;} - integral_type operator--(int) volatile {return fetch_sub(1);} - - bool compare_exchange_strong( - integral_type &expected, - integral_type desired, - memory_order order=memory_order_seq_cst) volatile - { - return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); - } - bool compare_exchange_weak( - integral_type &expected, - integral_type desired, - memory_order order=memory_order_seq_cst) volatile - { - return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); - } - bool compare_exchange_strong( - integral_type &expected, - integral_type desired, - memory_order success_order, - memory_order failure_order) volatile - { - return super::compare_exchange_strong(expected, desired, success_order, failure_order); - } - bool compare_exchange_weak( - integral_type &expected, - integral_type desired, - memory_order success_order, - memory_order failure_order) volatile - { - return super::compare_exchange_strong(expected, desired, success_order, failure_order); - } -private: - internal_atomic(const internal_atomic &); - void operator=(const internal_atomic &); -}; - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/builder.hpp b/include/boost/atomic/detail/builder.hpp deleted file mode 100644 index 19f8b3935..000000000 --- a/include/boost/atomic/detail/builder.hpp +++ /dev/null @@ -1,412 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_BUILDER_HPP -#define BOOST_DETAIL_ATOMIC_BUILDER_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - -/* -given a Base that implements: - -- load(memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) - -generates exchange and compare_exchange_strong -*/ -template -class build_exchange : public Base { -public: - typedef typename Base::integral_type integral_type; - - using Base::load; - using Base::compare_exchange_weak; - - bool compare_exchange_strong( - integral_type &expected, - integral_type desired, - memory_order success_order, - memory_order failure_order) volatile - { - integral_type expected_save=expected; - while(true) { - if (compare_exchange_weak(expected, desired, success_order, failure_order)) return true; - if (expected_save!=expected) return false; - expected=expected_save; - } - } - - integral_type exchange(integral_type replacement, memory_order order=memory_order_seq_cst) volatile - { - integral_type o=load(memory_order_relaxed); - do {} while(!compare_exchange_weak(o, replacement, order, memory_order_relaxed)); - return o; - } - - build_exchange() {} - explicit build_exchange(integral_type i) : Base(i) {} -}; - -/* -given a Base that implements: - -- fetch_add_var(integral_type c, memory_order order) -- fetch_inc(memory_order order) -- fetch_dec(memory_order order) - -creates a fetch_add method that delegates to fetch_inc/fetch_dec if operand -is constant +1/-1, and uses fetch_add_var otherwise - -the intention is to allow optimizing the incredibly common case of +1/-1 -*/ -template -class build_const_fetch_add : public Base { -public: - typedef typename Base::integral_type integral_type; - - integral_type fetch_add( - integral_type c, - memory_order order=memory_order_seq_cst) volatile - { - if (__builtin_constant_p(c)) { - switch(c) { - case -1: return fetch_dec(order); - case 1: return fetch_inc(order); - } - } - return fetch_add_var(c, order); - } - - build_const_fetch_add() {} - explicit build_const_fetch_add(integral_type i) : Base(i) {} -protected: - using Base::fetch_add_var; - using Base::fetch_inc; - using Base::fetch_dec; -}; - -/* -given a Base that implements: - -- load(memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) - -generates a -- not very efficient, but correct -- fetch_add operation -*/ -template -class build_fetch_add : public Base { -public: - typedef typename Base::integral_type integral_type; - - using Base::compare_exchange_weak; - - integral_type fetch_add( - integral_type c, memory_order order=memory_order_seq_cst) volatile - { - integral_type o=Base::load(memory_order_relaxed), n; - do {n=o+c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); - return o; - } - - build_fetch_add() {} - explicit build_fetch_add(integral_type i) : Base(i) {} -}; - -/* -given a Base that implements: - -- fetch_add(integral_type c, memory_order order) - -generates fetch_sub and post/pre- increment/decrement operators -*/ -template -class build_arithmeticops : public Base { -public: - typedef typename Base::integral_type integral_type; - - using Base::fetch_add; - - integral_type fetch_sub( - integral_type c, - memory_order order=memory_order_seq_cst) volatile - { -#if defined(BOOST_MSVC) -#pragma warning(push) -#pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned -#endif - return fetch_add(-c, order); -#if defined(BOOST_MSVC) -#pragma warning(pop) -#endif - } - - build_arithmeticops() {} - explicit build_arithmeticops(integral_type i) : Base(i) {} -}; - -/* -given a Base that implements: - -- load(memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) - -generates -- not very efficient, but correct -- fetch_and, fetch_or and fetch_xor operators -*/ -template -class build_logicops : public Base { -public: - typedef typename Base::integral_type integral_type; - - using Base::compare_exchange_weak; - using Base::load; - - integral_type fetch_and(integral_type c, memory_order order=memory_order_seq_cst) volatile - { - integral_type o=load(memory_order_relaxed), n; - do {n=o&c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); - return o; - } - integral_type fetch_or(integral_type c, memory_order order=memory_order_seq_cst) volatile - { - integral_type o=load(memory_order_relaxed), n; - do {n=o|c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); - return o; - } - integral_type fetch_xor(integral_type c, memory_order order=memory_order_seq_cst) volatile - { - integral_type o=load(memory_order_relaxed), n; - do {n=o^c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); - return o; - } - - build_logicops() {} - build_logicops(integral_type i) : Base(i) {} -}; - -/* -given a Base that implements: - -- load(memory_order order) -- store(integral_type i, memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) - -generates the full set of atomic operations for integral types -*/ -template -class build_atomic_from_minimal : public build_logicops< build_arithmeticops< build_fetch_add< build_exchange > > > { -public: - typedef build_logicops< build_arithmeticops< build_fetch_add< build_exchange > > > super; - typedef typename super::integral_type integral_type; - - build_atomic_from_minimal(void) {} - build_atomic_from_minimal(typename super::integral_type i) : super(i) {} -}; - -/* -given a Base that implements: - -- load(memory_order order) -- store(integral_type i, memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) -- compare_exchange_strong(integral_type &expected, integral_type desired, memory_order order) -- exchange(integral_type replacement, memory_order order) -- fetch_add_var(integral_type c, memory_order order) -- fetch_inc(memory_order order) -- fetch_dec(memory_order order) - -generates the full set of atomic operations for integral types -*/ -template -class build_atomic_from_typical : public build_logicops< build_arithmeticops< build_const_fetch_add > > { -public: - typedef build_logicops< build_arithmeticops< build_const_fetch_add > > super; - typedef typename super::integral_type integral_type; - - build_atomic_from_typical(void) {} - build_atomic_from_typical(typename super::integral_type i) : super(i) {} -}; - -/* -given a Base that implements: - -- load(memory_order order) -- store(integral_type i, memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) -- compare_exchange_strong(integral_type &expected, integral_type desired, memory_order order) -- exchange(integral_type replacement, memory_order order) -- fetch_add(integral_type c, memory_order order) - -generates the full set of atomic operations for integral types -*/ -template -class build_atomic_from_add : public build_logicops< build_arithmeticops > { -public: - typedef build_logicops< build_arithmeticops > super; - typedef typename super::integral_type integral_type; - - build_atomic_from_add(void) {} - build_atomic_from_add(typename super::integral_type i) : super(i) {} -}; - -/* -given a Base that implements: - -- load(memory_order order) -- store(integral_type i, memory_order order) -- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) -- compare_exchange_strong(integral_type &expected, integral_type desired, memory_order order) -- exchange(integral_type replacement, memory_order order) - -generates the full set of atomic operations for integral types -*/ -template -class build_atomic_from_exchange : public build_logicops< build_arithmeticops< build_fetch_add > > { -public: - typedef build_logicops< build_arithmeticops< build_fetch_add > > super; - typedef typename super::integral_type integral_type; - - build_atomic_from_exchange(void) {} - build_atomic_from_exchange(typename super::integral_type i) : super(i) {} -}; - - -/* -given a Base that implements: - -- compare_exchange_weak() - -generates load, store and compare_exchange_weak for a smaller -data type (e.g. an atomic "byte" embedded into a temporary -and properly aligned atomic "int"). -*/ -template -class build_base_from_larger_type { -public: - typedef Type integral_type; - - build_base_from_larger_type() {} - build_base_from_larger_type(integral_type t) {store(t, memory_order_relaxed);} - - integral_type load(memory_order order=memory_order_seq_cst) const volatile - { - larger_integral_type v=get_base().load(order); - return extract(v); - } - bool compare_exchange_weak(integral_type &expected, - integral_type desired, - memory_order success_order, - memory_order failure_order) volatile - { - larger_integral_type expected_; - larger_integral_type desired_; - - expected_=get_base().load(memory_order_relaxed); - expected_=insert(expected_, expected); - desired_=insert(expected_, desired); - bool success=get_base().compare_exchange_weak(expected_, desired_, success_order, failure_order); - expected=extract(expected_); - return success; - } - void store(integral_type v, - memory_order order=memory_order_seq_cst) volatile - { - larger_integral_type expected, desired; - expected=get_base().load(memory_order_relaxed); - do { - desired=insert(expected, v); - } while(!get_base().compare_exchange_weak(expected, desired, order, memory_order_relaxed)); - } - - bool is_lock_free(void) - { - return get_base().is_lock_free(); - } -private: - typedef typename Base::integral_type larger_integral_type; - - const Base &get_base(void) const volatile - { - intptr_t address=(intptr_t)this; - address&=~(sizeof(larger_integral_type)-1); - return *reinterpret_cast(address); - } - Base &get_base(void) volatile - { - intptr_t address=(intptr_t)this; - address&=~(sizeof(larger_integral_type)-1); - return *reinterpret_cast(address); - } - unsigned int get_offset(void) const volatile - { - intptr_t address=(intptr_t)this; - address&=(sizeof(larger_integral_type)-1); - return address; - } - - unsigned int get_shift(void) const volatile - { -#if defined(BOOST_LITTLE_ENDIAN) - return get_offset()*8; -#elif defined(BOOST_BIG_ENDIAN) - return (sizeof(larger_integral_type)-sizeof(integral_type)-get_offset())*8; -#else - #error "Unknown endian" -#endif - } - - integral_type extract(larger_integral_type v) const volatile - { - return v>>get_shift(); - } - - larger_integral_type insert(larger_integral_type target, integral_type source) const volatile - { - larger_integral_type tmp=source; - larger_integral_type mask=(larger_integral_type)-1; - - mask=~(mask<<(8*sizeof(integral_type))); - - mask=mask< -class build_atomic_from_larger_type : public build_atomic_from_minimal< build_base_from_larger_type > { -public: - typedef build_atomic_from_minimal< build_base_from_larger_type > super; - //typedef typename super::integral_type integral_type; - typedef Type integral_type; - - build_atomic_from_larger_type() {} - build_atomic_from_larger_type(integral_type v) : super(v) {} -}; - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/fallback.hpp b/include/boost/atomic/detail/fallback.hpp deleted file mode 100644 index e539099b6..000000000 --- a/include/boost/atomic/detail/fallback.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_FALLBACK_HPP -#define BOOST_DETAIL_ATOMIC_FALLBACK_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - -template -class fallback_atomic { -public: - fallback_atomic(void) {} - explicit fallback_atomic(const T &t) {memcpy(&i, &t, sizeof(T));} - - void store(const T &t, memory_order order=memory_order_seq_cst) volatile - { - detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); - memcpy((void*)&i, &t, sizeof(T)); - } - T load(memory_order /*order*/=memory_order_seq_cst) volatile const - { - detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); - T tmp; - memcpy(&tmp, (T*)&i, sizeof(T)); - return tmp; - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order /*success_order*/, - memory_order /*failure_order*/) volatile - { - detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); - if (memcmp((void*)&i, &expected, sizeof(T))==0) { - memcpy((void*)&i, &desired, sizeof(T)); - return true; - } else { - memcpy(&expected, (void*)&i, sizeof(T)); - return false; - } - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T replacement, memory_order /*order*/=memory_order_seq_cst) volatile - { - detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); - T tmp; - memcpy(&tmp, (void*)&i, sizeof(T)); - memcpy((void*)&i, &replacement, sizeof(T)); - return tmp; - } - bool is_lock_free(void) const volatile {return false;} -protected: - T i; - typedef T integral_type; -}; - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/gcc-alpha.hpp b/include/boost/atomic/detail/gcc-alpha.hpp deleted file mode 100644 index 8e55f44f5..000000000 --- a/include/boost/atomic/detail/gcc-alpha.hpp +++ /dev/null @@ -1,354 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_GCC_ALPHA_HPP -#define BOOST_DETAIL_ATOMIC_GCC_ALPHA_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -/* - Refer to http://h71000.www7.hp.com/doc/82final/5601/5601pro_004.html - (HP OpenVMS systems documentation) and the alpha reference manual. - */ - -/* - NB: The most natural thing would be to write the increment/decrement - operators along the following lines: - - __asm__ __volatile__( - "1: ldl_l %0,%1 \n" - "addl %0,1,%0 \n" - "stl_c %0,%1 \n" - "beq %0,1b\n" - : "=&b" (tmp) - : "m" (value) - : "cc" - ); - - However according to the comments on the HP website and matching - comments in the Linux kernel sources this defies branch prediction, - as the cpu assumes that backward branches are always taken; so - instead copy the trick from the Linux kernel, introduce a forward - branch and back again. - - I have, however, had a hard time measuring the difference between - the two versions in microbenchmarks -- I am leaving it in nevertheless - as it apparently does not hurt either. -*/ - -namespace boost { -namespace detail { -namespace atomic { - -static inline void fence_before(memory_order order) -{ - switch(order) { - case memory_order_consume: - case memory_order_release: - case memory_order_acq_rel: - case memory_order_seq_cst: - __asm__ __volatile__ ("mb" ::: "memory"); - default:; - } -} - -static inline void fence_after(memory_order order) -{ - switch(order) { - case memory_order_acquire: - case memory_order_acq_rel: - case memory_order_seq_cst: - __asm__ __volatile__ ("mb" ::: "memory"); - default:; - } -} - -template<> -inline void platform_atomic_thread_fence(memory_order order) -{ - switch(order) { - case memory_order_acquire: - case memory_order_consume: - case memory_order_release: - case memory_order_acq_rel: - case memory_order_seq_cst: - __asm__ __volatile__ ("mb" ::: "memory"); - default:; - } -} - -template -class atomic_alpha_32 { -public: - typedef T integral_type; - explicit atomic_alpha_32(T v) : i(v) {} - atomic_alpha_32() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - *reinterpret_cast(&i)=(int)v; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - int current, success; - __asm__ __volatile__( - "1: ldl_l %2, %4\n" - "cmpeq %2, %0, %3\n" - "mov %2, %0\n" - "beq %3, 3f\n" - "stl_c %1, %4\n" - "2:\n" - - ".subsection 2\n" - "3: mov %3, %1\n" - "br 2b\n" - ".previous\n" - - : "+&r" (expected), "+&r" (desired), "=&r"(current), "=&r"(success) - : "m" (i) - : - ); - if (desired) fence_after(success_order); - else fence_after(failure_order); - return desired; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - inline T fetch_add_var(T c, memory_order order) volatile - { - fence_before(order); - T original, modified; - __asm__ __volatile__( - "1: ldl_l %0, %2\n" - "addl %0, %3, %1\n" - "stl_c %1, %2\n" - "beq %1, 2f\n" - - ".subsection 2\n" - "2: br 1b\n" - ".previous\n" - - : "=&r" (original), "=&r" (modified) - : "m" (i), "r" (c) - : - ); - fence_after(order); - return original; - } - inline T fetch_inc(memory_order order) volatile - { - fence_before(order); - int original, modified; - __asm__ __volatile__( - "1: ldl_l %0, %2\n" - "addl %0, 1, %1\n" - "stl_c %1, %2\n" - "beq %1, 2f\n" - - ".subsection 2\n" - "2: br 1b\n" - ".previous\n" - - : "=&r" (original), "=&r" (modified) - : "m" (i) - : - ); - fence_after(order); - return original; - } - inline T fetch_dec(memory_order order) volatile - { - fence_before(order); - int original, modified; - __asm__ __volatile__( - "1: ldl_l %0, %2\n" - "subl %0, 1, %1\n" - "stl_c %1, %2\n" - "beq %1, 2f\n" - - ".subsection 2\n" - "2: br 1b\n" - ".previous\n" - - : "=&r" (original), "=&r" (modified) - : "m" (i) - : - ); - fence_after(order); - return original; - } -private: - T i; -}; - -template -class atomic_alpha_64 { -public: - typedef T integral_type; - explicit atomic_alpha_64(T v) : i(v) {} - atomic_alpha_64() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - *reinterpret_cast(&i)=v; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - int current, success; - __asm__ __volatile__( - "1: ldq_l %2, %4\n" - "cmpeq %2, %0, %3\n" - "mov %2, %0\n" - "beq %3, 3f\n" - "stq_c %1, %4\n" - "2:\n" - - ".subsection 2\n" - "3: mov %3, %1\n" - "br 2b\n" - ".previous\n" - - : "+&r" (expected), "+&r" (desired), "=&r"(current), "=&r"(success) - : "m" (i) - : - ); - if (desired) fence_after(success_order); - else fence_after(failure_order); - return desired; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - inline T fetch_add_var(T c, memory_order order) volatile - { - fence_before(order); - T original, modified; - __asm__ __volatile__( - "1: ldq_l %0, %2\n" - "addq %0, %3, %1\n" - "stq_c %1, %2\n" - "beq %1, 2f\n" - - ".subsection 2\n" - "2: br 1b\n" - ".previous\n" - - : "=&r" (original), "=&r" (modified) - : "m" (i), "r" (c) - : - ); - fence_after(order); - return original; - } - inline T fetch_inc(memory_order order) volatile - { - fence_before(order); - T original, modified; - __asm__ __volatile__( - "1: ldq_l %0, %2\n" - "addq %0, 1, %1\n" - "stq_c %1, %2\n" - "beq %1, 2f\n" - - ".subsection 2\n" - "2: br 1b\n" - ".previous\n" - - : "=&r" (original), "=&r" (modified) - : "m" (i) - : - ); - fence_after(order); - return original; - } - inline T fetch_dec(memory_order order) volatile - { - fence_before(order); - T original, modified; - __asm__ __volatile__( - "1: ldq_l %0, %2\n" - "subq %0, 1, %1\n" - "stq_c %1, %2\n" - "beq %1, 2f\n" - - ".subsection 2\n" - "2: br 1b\n" - ".previous\n" - - : "=&r" (original), "=&r" (modified) - : "m" (i) - : - ); - fence_after(order); - return original; - } -private: - T i; -}; - -template -class platform_atomic_integral : public build_atomic_from_typical > > { -public: - typedef build_atomic_from_typical > > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral : public build_atomic_from_typical > > { -public: - typedef build_atomic_from_typical > > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/gcc-armv6+.hpp b/include/boost/atomic/detail/gcc-armv6+.hpp deleted file mode 100644 index 04a5bda9c..000000000 --- a/include/boost/atomic/detail/gcc-armv6+.hpp +++ /dev/null @@ -1,299 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_GCC_ARMV6P_HPP -#define BOOST_DETAIL_ATOMIC_GCC_ARMV6P_HPP - -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// Copyright (c) 2009 Helge Bahmann -// Copyright (c) 2009 Phil Endecott -// ARM Code by Phil Endecott, based on other architectures. - - -#include -#include -#include - -// From the ARM Architecture Reference Manual for architecture v6: -// -// LDREX{} , [] -// Specifies the destination register for the memory word addressed by -// Specifies the register containing the address. -// -// STREX{} , , [] -// Specifies the destination register for the returned status value. -// 0 if the operation updates memory -// 1 if the operation fails to update memory -// Specifies the register containing the word to be stored to memory. -// Specifies the register containing the address. -// Rd must not be the same register is Rm or Rn. -// -// ARM v7 is like ARM v6 plus: -// There are half-word and byte versions of the LDREX and STREX instructions, -// LDREXH, LDREXB, STREXH and STREXB. -// There are also double-word versions, LDREXD and STREXD. -// (Actually it looks like these are available from version 6k onwards.) -// FIXME these are not yet used; should be mostly a matter of copy-and-paste. -// I think you can supply an immediate offset to the address. -// -// A memory barrier is effected using a "co-processor 15" instruction, -// though a separate assembler mnemonic is available for it in v7. - -namespace boost { -namespace detail { -namespace atomic { - - -// "Thumb 1" is a subset of the ARM instruction set that uses a 16-bit encoding. It -// doesn't include all instructions and in particular it doesn't include the co-processor -// instruction used for the memory barrier or the load-locked/store-conditional -// instructions. So, if we're compiling in "Thumb 1" mode, we need to wrap all of our -// asm blocks with code to temporarily change to ARM mode. -// -// You can only change between ARM and Thumb modes when branching using the bx instruction. -// bx takes an address specified in a register. The least significant bit of the address -// indicates the mode, so 1 is added to indicate that the destination code is Thumb. -// A temporary register is needed for the address and is passed as an argument to these -// macros. It must be one of the "low" registers accessible to Thumb code, specified -// usng the "l" attribute in the asm statement. -// -// Architecture v7 introduces "Thumb 2", which does include (almost?) all of the ARM -// instruction set. So in v7 we don't need to change to ARM mode; we can write "universal -// assembler" which will assemble to Thumb 2 or ARM code as appropriate. The only thing -// we need to do to make this "universal" assembler mode work is to insert "IT" instructions -// to annotate the conditional instructions. These are ignored in other modes (e.g. v6), -// so they can always be present. - -#if defined(__thumb__) && !defined(__ARM_ARCH_7A__) -// FIXME also other v7 variants. -#define BOOST_ATOMIC_ARM_ASM_START(TMPREG) "adr " #TMPREG ", 1f\n" "bx " #TMPREG "\n" ".arm\n" ".align 4\n" "1: " -#define BOOST_ATOMIC_ARM_ASM_END(TMPREG) "adr " #TMPREG ", 1f + 1\n" "bx " #TMPREG "\n" ".thumb\n" ".align 2\n" "1: " - -#else -// The tmpreg is wasted in this case, which is non-optimal. -#define BOOST_ATOMIC_ARM_ASM_START(TMPREG) -#define BOOST_ATOMIC_ARM_ASM_END(TMPREG) -#endif - - -#if defined(__ARM_ARCH_7A__) -// FIXME ditto. -#define BOOST_ATOMIC_ARM_DMB "dmb\n" -#else -#define BOOST_ATOMIC_ARM_DMB "mcr\tp15, 0, r0, c7, c10, 5\n" -#endif - -// There is also a "Data Synchronisation Barrier" DSB; this exists in v6 as another co-processor -// instruction like the above. - - -static inline void fence_before(memory_order order) -{ - // FIXME I don't understand enough about barriers to know what this should do. - switch(order) { - case memory_order_release: - case memory_order_acq_rel: - case memory_order_seq_cst: - int brtmp; - __asm__ __volatile__ ( - BOOST_ATOMIC_ARM_ASM_START(%0) - BOOST_ATOMIC_ARM_DMB - BOOST_ATOMIC_ARM_ASM_END(%0) - : "=&l" (brtmp) :: "memory" - ); - default:; - } -} - -static inline void fence_after(memory_order order) -{ - // FIXME I don't understand enough about barriers to know what this should do. - switch(order) { - case memory_order_acquire: - case memory_order_acq_rel: - case memory_order_seq_cst: - int brtmp; - __asm__ __volatile__ ( - BOOST_ATOMIC_ARM_ASM_START(%0) - BOOST_ATOMIC_ARM_DMB - BOOST_ATOMIC_ARM_ASM_END(%0) - : "=&l" (brtmp) :: "memory" - ); - case memory_order_consume: - __asm__ __volatile__ ("" ::: "memory"); - default:; - } -} - -#undef BOOST_ATOMIC_ARM_DMB - - -template -class atomic_arm_4 { -public: - typedef T integral_type; - explicit atomic_arm_4(T v) : i(v) {} - atomic_arm_4() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=const_cast(i); - fence_after(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - const_cast(i)=v; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - int success; - int tmp; - __asm__ __volatile__( - BOOST_ATOMIC_ARM_ASM_START(%2) - "mov %1, #0\n" // success = 0 - "ldrex %0, [%3]\n" // expected' = *(&i) - "teq %0, %4\n" // flags = expected'==expected - "ittt eq\n" - "strexeq %2, %5, [%3]\n" // if (flags.equal) *(&i) = desired, tmp = !OK - "teqeq %2, #0\n" // if (flags.equal) flags = tmp==0 - "moveq %1, #1\n" // if (flags.equal) success = 1 - BOOST_ATOMIC_ARM_ASM_END(%2) - : "=&r" (expected), // %0 - "=&r" (success), // %1 - "=&l" (tmp) // %2 - : "r" (&i), // %3 - "r" (expected), // %4 - "r" ((int)desired) // %5 - : "cc" - ); - if (success) fence_after(success_order); - else fence_after(failure_order); - return success; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - inline T fetch_add_var(T c, memory_order order) volatile - { - fence_before(order); - T original, tmp; - int tmp2; - __asm__ __volatile__( - BOOST_ATOMIC_ARM_ASM_START(%2) - "1: ldrex %0, [%3]\n" // original = *(&i) - "add %1, %0, %4\n" // tmp = original + c - "strex %2, %1, [%3]\n" // *(&i) = tmp; tmp2 = !OK - "teq %2, #0\n" // flags = tmp2==0 - "it ne\n" - "bne 1b\n" // if (!flags.equal) goto 1 - BOOST_ATOMIC_ARM_ASM_END(%2) - : "=&r" (original), // %0 - "=&r" (tmp), // %1 - "=&l" (tmp2) // %2 - : "r" (&i), // %3 - "r" (c) // %4 - : "cc" - ); - fence_after(order); - return original; - } - inline T fetch_inc(memory_order order) volatile - { - fence_before(order); - T original, tmp; - int tmp2; - __asm__ __volatile__( - BOOST_ATOMIC_ARM_ASM_START(%2) - "1: ldrex %0, [%3]\n" // original = *(&i) - "add %1, %0, #1\n" // tmp = original + 1 - "strex %2, %1, [%3]\n" // *(&i) = tmp; tmp2 = !OK - "teq %2, #0\n" // flags = tmp2==0 - "it ne\n" - "bne 1b\n" // if (!flags.equal) goto 1 - BOOST_ATOMIC_ARM_ASM_END(%2) - : "=&r" (original), // %0 - "=&r" (tmp), // %1 - "=&l" (tmp2) // %2 - : "r" (&i) // %3 - : "cc" - ); - fence_after(order); - return original; - } - inline T fetch_dec(memory_order order) volatile - { - fence_before(order); - T original, tmp; - int tmp2; - __asm__ __volatile__( - BOOST_ATOMIC_ARM_ASM_START(%2) - "1: ldrex %0, [%3]\n" // original = *(&i) - "sub %1, %0, #1\n" // tmp = original - 1 - "strex %2, %1, [%3]\n" // *(&i) = tmp; tmp2 = !OK - "teq %2, #0\n" // flags = tmp2==0 - "it ne\n" - "bne 1b\n" // if (!flags.equal) goto 1 - BOOST_ATOMIC_ARM_ASM_END(%2) - : "=&r" (original), // %0 - "=&r" (tmp), // %1 - "=&l" (tmp2) // %2 - : "r" (&i) // %3 - : "cc" - ); - fence_after(order); - return original; - } -private: - T i; -}; - - -// #ifdef _ARM_ARCH_7 -// FIXME TODO can add native byte and halfword version here - - -template -class platform_atomic_integral : public build_atomic_from_typical > > { -public: - typedef build_atomic_from_typical > > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - - - -typedef build_exchange > platform_atomic_address; - -} -} -} - -#undef BOOST_ATOMIC_ARM_ASM_START -#undef BOOST_ATOMIC_ARM_ASM_END - - -#endif diff --git a/include/boost/atomic/detail/gcc-ppc.hpp b/include/boost/atomic/detail/gcc-ppc.hpp deleted file mode 100644 index 1041e735b..000000000 --- a/include/boost/atomic/detail/gcc-ppc.hpp +++ /dev/null @@ -1,351 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_GCC_PPC_HPP -#define BOOST_DETAIL_ATOMIC_GCC_PPC_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -/* - Refer to: Motorola: "Programming Environments Manual for 32-Bit - Implementations of the PowerPC Architecture", Appendix E: - "Synchronization Programming Examples" for an explanation of what is - going on here (can be found on the web at various places by the - name "MPCFPE32B.pdf", Google is your friend...) - */ - -namespace boost { -namespace detail { -namespace atomic { - -static inline void fence_before(memory_order order) -{ - switch(order) { - case memory_order_release: - case memory_order_acq_rel: -#if defined(__powerpc64__) - __asm__ __volatile__ ("lwsync" ::: "memory"); - break; -#endif - case memory_order_seq_cst: - __asm__ __volatile__ ("sync" ::: "memory"); - default:; - } -} - -/* Note on the barrier instructions used by fence_after and -atomic_thread_fence: the "isync" instruction normally does -not wait for memory-accessing operations to complete, the -"trick" is to introduce a conditional branch that formally -depends on the memory-accessing instruction -- isync waits -until the branch can be resolved and thus implicitly until -the memory access completes. - -This means that the load(memory_order_relaxed) instruction -includes this branch, even though no barrier would be required -here, but as a consequence atomic_thread_fence(memory_order_acquire) -would have to be implemented using "sync" instead of "isync". -The following simple cost-analysis provides the rationale -for this decision: - -- isync: about ~12 cycles -- sync: about ~50 cycles -- "spurious" branch after load: 1-2 cycles -- making the right decision: priceless - -*/ - -static inline void fence_after(memory_order order) -{ - switch(order) { - case memory_order_acquire: - case memory_order_acq_rel: - case memory_order_seq_cst: - __asm__ __volatile__ ("isync"); - case memory_order_consume: - __asm__ __volatile__ ("" ::: "memory"); - default:; - } -} - -template<> -inline void platform_atomic_thread_fence(memory_order order) -{ - switch(order) { - case memory_order_acquire: - __asm__ __volatile__ ("isync" ::: "memory"); - break; - case memory_order_release: - case memory_order_acq_rel: -#if defined(__powerpc64__) - __asm__ __volatile__ ("lwsync" ::: "memory"); - break; -#endif - case memory_order_seq_cst: - __asm__ __volatile__ ("sync" ::: "memory"); - default:; - } -} - - -/* note: the __asm__ constraint "b" instructs gcc to use any register -except r0; this is required because r0 is not allowed in -some places. Since I am sometimes unsure if it is allowed -or not just play it safe and avoid r0 entirely -- ppc isn't -exactly register-starved, so this really should not matter :) */ - -template -class atomic_ppc_32 { -public: - typedef T integral_type; - explicit atomic_ppc_32(T v) : i(v) {} - atomic_ppc_32() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - __asm__ __volatile__ ( - "cmpw %0, %0\n" - "bne- 1f\n" - "1f:\n" - : "+b"(v)); - fence_after(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - *reinterpret_cast(&i)=v; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - int success; - __asm__ __volatile__( - "lwarx %0,0,%2\n" - "cmpw %0, %3\n" - "bne- 2f\n" - "stwcx. %4,0,%2\n" - "bne- 2f\n" - "addi %1,0,1\n" - "1:" - - ".subsection 2\n" - "2: addi %1,0,0\n" - "b 1b\n" - ".previous\n" - : "=&b" (expected), "=&b" (success) - : "b" (&i), "b" (expected), "b" ((int)desired) - ); - if (success) fence_after(success_order); - else fence_after(failure_order); - return success; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - inline T fetch_add_var(T c, memory_order order) volatile - { - fence_before(order); - T original, tmp; - __asm__ __volatile__( - "1: lwarx %0,0,%2\n" - "add %1,%0,%3\n" - "stwcx. %1,0,%2\n" - "bne- 1b\n" - : "=&b" (original), "=&b" (tmp) - : "b" (&i), "b" (c) - : "cc"); - fence_after(order); - return original; - } - inline T fetch_inc(memory_order order) volatile - { - fence_before(order); - T original, tmp; - __asm__ __volatile__( - "1: lwarx %0,0,%2\n" - "addi %1,%0,1\n" - "stwcx. %1,0,%2\n" - "bne- 1b\n" - : "=&b" (original), "=&b" (tmp) - : "b" (&i) - : "cc"); - fence_after(order); - return original; - } - inline T fetch_dec(memory_order order) volatile - { - fence_before(order); - T original, tmp; - __asm__ __volatile__( - "1: lwarx %0,0,%2\n" - "addi %1,%0,-1\n" - "stwcx. %1,0,%2\n" - "bne- 1b\n" - : "=&b" (original), "=&b" (tmp) - : "b" (&i) - : "cc"); - fence_after(order); - return original; - } -private: - T i; -}; - -#if defined(__powerpc64__) - -#warning Untested code -- please inform me if it works - -template -class atomic_ppc_64 { -public: - typedef T integral_type; - explicit atomic_ppc_64(T v) : i(v) {} - atomic_ppc_64() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - __asm__ __volatile__ ( - "cmpw %0, %0\n" - "bne- 1f\n" - "1f:\n" - : "+b"(v)); - fence_after(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - *reinterpret_cast(&i)=v; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - int success; - __asm__ __volatile__( - "ldarx %0,0,%2\n" - "cmpw %0, %3\n" - "bne- 2f\n" - "stdcx. %4,0,%2\n" - "bne- 2f\n" - "addi %1,0,1\n" - "1:" - - ".subsection 2\n" - "2: addi %1,0,0\n" - "b 1b\n" - ".previous\n" - : "=&b" (expected), "=&b" (success) - : "b" (&i), "b" (expected), "b" ((int)desired) - ); - if (success) fence_after(success_order); - else fence_after(failure_order); - fence_after(order); - return success; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - inline T fetch_add_var(T c, memory_order order) volatile - { - fence_before(order); - T original, tmp; - __asm__ __volatile__( - "1: ldarx %0,0,%2\n" - "add %1,%0,%3\n" - "stdcx. %1,0,%2\n" - "bne- 1b\n" - : "=&b" (original), "=&b" (tmp) - : "b" (&i), "b" (c) - : "cc"); - fence_after(order); - return original; - } - inline T fetch_inc(memory_order order) volatile - { - fence_before(order); - T original, tmp; - __asm__ __volatile__( - "1: ldarx %0,0,%2\n" - "addi %1,%0,1\n" - "stdcx. %1,0,%2\n" - "bne- 1b\n" - : "=&b" (original), "=&b" (tmp) - : "b" (&i) - : "cc"); - fence_after(order); - return original; - } - inline T fetch_dec(memory_order order) volatile - { - fence_before(order); - T original, tmp; - __asm__ __volatile__( - "1: ldarx %0,0,%2\n" - "addi %1,%0,-1\n" - "stdcx. %1,0,%2\n" - "bne- 1b\n" - : "=&b" (original), "=&b" (tmp) - : "b" (&i) - : "cc"); - fence_after(order); - return original; - } -private: - T i; -}; -#endif - -template -class platform_atomic_integral : public build_atomic_from_typical > > { -public: - typedef build_atomic_from_typical > > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -#if defined(__powerpc64__) -template -class platform_atomic_integral : public build_atomic_from_typical > > { -public: - typedef build_atomic_from_typical > > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; -#endif - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/gcc-x86.hpp b/include/boost/atomic/detail/gcc-x86.hpp deleted file mode 100644 index 6f1e83a95..000000000 --- a/include/boost/atomic/detail/gcc-x86.hpp +++ /dev/null @@ -1,546 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_GCC_X86_HPP -#define BOOST_DETAIL_ATOMIC_GCC_X86_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - -static inline void fence_before(memory_order order) -{ - switch(order) { - case memory_order_consume: - case memory_order_release: - case memory_order_acq_rel: - case memory_order_seq_cst: - __asm__ __volatile__ ("" ::: "memory"); - default:; - } -} - -static inline void fence_after(memory_order order) -{ - switch(order) { - case memory_order_acquire: - case memory_order_acq_rel: - case memory_order_seq_cst: - __asm__ __volatile__ ("" ::: "memory"); - default:; - } -} - -static inline void full_fence(void) -{ -#if (defined(__amd64__) || defined(__x86_64__)) - __asm__ __volatile__("mfence" ::: "memory"); -#else - /* could use mfence iff i686, but it does not appear to matter much */ - __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory"); -#endif -} - -static inline void fence_after_load(memory_order order) -{ - switch(order) { - case memory_order_seq_cst: - full_fence(); - case memory_order_acquire: - case memory_order_acq_rel: - __asm__ __volatile__ ("" ::: "memory"); - default:; - } -} - -template<> -inline void platform_atomic_thread_fence(memory_order order) -{ - switch(order) { - case memory_order_seq_cst: - full_fence(); - case memory_order_acquire: - case memory_order_consume: - case memory_order_acq_rel: - case memory_order_release: - __asm__ __volatile__ ("" ::: "memory"); - default:; - } -} - -template -class atomic_x86_8 { -public: - explicit atomic_x86_8(T v) : i(v) {} - atomic_x86_8() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - fence_before(order); - *reinterpret_cast(&i)=v; - } else { - exchange(v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - T prev=expected; - __asm__ __volatile__("lock; cmpxchgb %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); - bool success=(prev==expected); - if (success) fence_after(success_order); - else fence_after(failure_order); - expected=prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("xchgb %0, %1\n" : "=q" (r) : "m"(i), "0" (r) : "memory"); - return r; - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("lock; xaddb %0, %1" : "+q" (c), "+m" (i) :: "memory"); - return c; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - typedef T integral_type; -private: - T i; -}; - -template -class platform_atomic_integral : public build_atomic_from_add > { -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class atomic_x86_16 { -public: - explicit atomic_x86_16(T v) : i(v) {} - atomic_x86_16() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - fence_before(order); - *reinterpret_cast(&i)=v; - } else { - exchange(v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - T prev=expected; - __asm__ __volatile__("lock; cmpxchgw %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); - bool success=(prev==expected); - if (success) fence_after(success_order); - else fence_after(failure_order); - expected=prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("xchgw %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); - return r; - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("lock; xaddw %0, %1" : "+r" (c), "+m" (i) :: "memory"); - return c; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - typedef T integral_type; -private: - T i; -}; - -template -class platform_atomic_integral : public build_atomic_from_add > { -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class atomic_x86_32 { -public: - explicit atomic_x86_32(T v) : i(v) {} - atomic_x86_32() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - fence_before(order); - *reinterpret_cast(&i)=v; - } else { - exchange(v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - T prev=expected; - __asm__ __volatile__("lock; cmpxchgl %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); - bool success=(prev==expected); - if (success) fence_after(success_order); - else fence_after(failure_order); - expected=prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("xchgl %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); - return r; - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("lock; xaddl %0, %1" : "+r" (c), "+m" (i) :: "memory"); - return c; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - typedef T integral_type; -private: - T i; -}; - -template -class platform_atomic_integral : public build_atomic_from_add > { -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -#if (defined(__amd64__) || defined(__x86_64__)) -template -class atomic_x86_64 { -public: - explicit atomic_x86_64(T v) : i(v) {} - atomic_x86_64() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - fence_before(order); - *reinterpret_cast(&i)=v; - } else { - exchange(v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - fence_before(success_order); - T prev=expected; - __asm__ __volatile__("lock; cmpxchgq %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); - bool success=(prev==expected); - if (success) fence_after(success_order); - else fence_after(failure_order); - expected=prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("xchgq %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); - return r; - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - __asm__ __volatile__("lock; xaddq %0, %1" : "+r" (c), "+m" (i) :: "memory"); - return c; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - typedef T integral_type; -private: - T i; -} __attribute__((aligned(8))); - -#elif defined(__i686__) - -template -class atomic_x86_64 { -private: - typedef atomic_x86_64 this_type; -public: - explicit atomic_x86_64(T v) : i(v) {} - atomic_x86_64() {} - - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - long scratch; - fence_before(success_order); - T prev=expected; - /* Make sure ebx is saved and restored properly in case - this object is compiled as "position independent". Since - programmers on x86 tend to forget specifying -DPIC or - similar, always assume PIC. - - To make this work uniformly even in the non-PIC case, - setup register constraints such that ebx can not be - used by accident e.g. as base address for the variable - to be modified. Accessing "scratch" should always be okay, - as it can only be placed on the stack (and therefore - accessed through ebp or esp only). - - In theory, could push/pop ebx onto/off the stack, but movs - to a prepared stack slot turn out to be faster. */ - __asm__ __volatile__( - "movl %%ebx, %1\n" - "movl %2, %%ebx\n" - "lock; cmpxchg8b 0(%4)\n" - "movl %1, %%ebx\n" - : "=A" (prev), "=m" (scratch) - : "D" ((long)desired), "c" ((long)(desired>>32)), "S" (&i), "0" (prev) - : "memory"); - bool success=(prev==expected); - if (success) fence_after(success_order); - else fence_after(failure_order); - expected=prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - T prev=i; - do {} while(!compare_exchange_strong(prev, r, order, memory_order_relaxed)); - return prev; - } - - T load(memory_order order=memory_order_seq_cst) const volatile - { - /* this is a bit problematic -- there is no other - way to atomically load a 64 bit value, but of course - compare_exchange requires write access to the memory - area */ - T expected=i; - do { } while(!const_cast(this)->compare_exchange_strong(expected, expected, order, memory_order_relaxed)); - return expected; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - exchange(v, order); - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - T expected=i, desired;; - do { - desired=expected+c; - } while(!compare_exchange_strong(expected, desired, order, memory_order_relaxed)); - return expected; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - typedef T integral_type; -private: - T i; -} __attribute__((aligned(8))) ; - -#endif - -#if (defined(__amd64__) || defined(__x86_64__)) || defined(__i686__) -template -class platform_atomic_integral : public build_atomic_from_add >{ -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; -#endif - -// TODO: only use the sync intrinsics as a fallback, prefer inline asm as it -// allows us to do relaxed memory ordering. -#if (defined(__amd64__) || defined(__x86_64__)) && \ - defined(BOOST_ATOMIC_HAVE_SSE2) && \ - defined(BOOST_ATOMIC_HAVE_GNU_SYNC_16) && \ - defined(BOOST_ATOMIC_HAVE_GNU_ALIGNED_16) -template -class atomic_x86_128 { -public: - explicit atomic_x86_128(T v) : i(v) {} - atomic_x86_128() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v; - __asm__ __volatile__ ( - "movdqa %1, %%xmm0 ;\n" - "movdqa %%xmm0, %0 ;\n" - : "=m" (v) - : "m" (i) - : "xmm0", "memory" - ); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - // Atomically stores 128bit value by SSE instruction movdqa - __asm__ __volatile__ ( - "movdqa %1, %%xmm0 ;\n" - "movdqa %%xmm0, %0 ;\n" - : "=m" (i) - : "m" (v) - : "xmm0", "memory" - ); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - T prev = __sync_val_compare_and_swap_16 - (reinterpret_cast(&i), expected, desired); - bool success=(prev==expected); - if (success) fence_after(success_order); - else fence_after(failure_order); - expected=prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - while (!__sync_bool_compare_and_swap_16 - (reinterpret_cast(&i), i, r)) - {}; - - return r; - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - T expected=i, desired; - do { - desired=expected+c; - } while(!compare_exchange_strong(expected, desired, order, memory_order_relaxed)); - return expected; - } - - bool is_lock_free(void) const volatile {return true;} -protected: - typedef T integral_type; -private: - T i; -} __attribute__((aligned(16))); - -template -class platform_atomic_integral : public build_atomic_from_add >{ -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -#endif - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/generic-cas.hpp b/include/boost/atomic/detail/generic-cas.hpp deleted file mode 100644 index dc14a8bd0..000000000 --- a/include/boost/atomic/detail/generic-cas.hpp +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_GENERIC_CAS_HPP -#define BOOST_DETAIL_ATOMIC_GENERIC_CAS_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include - -#include -#include -#include - -/* fallback implementation for various compilation targets; -this is *not* efficient, particularly because all operations -are fully fenced (full memory barriers before and after -each operation) */ - -#if defined(__GNUC__) - namespace boost { namespace detail { namespace atomic { - static inline int32_t - fenced_compare_exchange_strong_32(volatile int32_t *ptr, int32_t expected, int32_t desired) - { - return __sync_val_compare_and_swap_4(ptr, expected, desired); - } - #define BOOST_ATOMIC_HAVE_CAS32 1 - - #if (defined(__amd64__) || defined(__x86_64__)) || defined(__i686__) - static inline int64_t - fenced_compare_exchange_strong_64(int64_t *ptr, int64_t expected, int64_t desired) - { - return __sync_val_compare_and_swap_8(ptr, expected, desired); - } - #define BOOST_ATOMIC_HAVE_CAS64 1 - #endif - }}} - -#elif defined(__ICL) || defined(_MSC_VER) - - #if defined(_MSC_VER) - #include - #include - #endif - - namespace boost { namespace detail { namespace atomic { - static inline int32_t - fenced_compare_exchange_strong(int32_t *ptr, int32_t expected, int32_t desired) - { - return _InterlockedCompareExchange(reinterpret_cast(ptr), desired, expected); - } - #define BOOST_ATOMIC_HAVE_CAS32 1 - #if defined(_WIN64) - static inline int64_t - fenced_compare_exchange_strong(int64_t *ptr, int64_t expected, int64_t desired) - { - return _InterlockedCompareExchange64(ptr, desired, expected); - } - #define BOOST_ATOMIC_HAVE_CAS64 1 - #endif - }}} - -#elif (defined(__ICC) || defined(__ECC)) - namespace boost { namespace detail { namespace atomic { - static inline int32_t - fenced_compare_exchange_strong_32(int32_t *ptr, int32_t expected, int32_t desired) - { - return _InterlockedCompareExchange((void*)ptr, desired, expected); - } - #define BOOST_ATOMIC_HAVE_CAS32 1 - #if defined(__x86_64) - static inline int64_t - fenced_compare_exchange_strong(int64_t *ptr, int64_t expected, int64_t desired) - { - return cas64(ptr, expected, desired); - } - #define BOOST_ATOMIC_HAVE_CAS64 1 - #elif defined(__ECC) //IA-64 version - static inline int64_t - fenced_compare_exchange_strong(int64_t *ptr, int64_t expected, int64_t desired) - { - return _InterlockedCompareExchange64((void*)ptr, desired, expected); - } - #define BOOST_ATOMIC_HAVE_CAS64 1 - #endif - }}} - -#elif (defined(__SUNPRO_CC) && defined(__sparc)) - #include - namespace boost { namespace detail { namespace atomic { - static inline int32_t - fenced_compare_exchange_strong_32(int32_t *ptr, int32_t expected, int32_t desired) - { - return atomic_cas_32((volatile unsigned int*)ptr, expected, desired); - } - #define BOOST_ATOMIC_HAVE_CAS32 1 - - /* FIXME: check for 64 bit mode */ - static inline int64_t - fenced_compare_exchange_strong_64(int64_t *ptr, int64_t expected, int64_t desired) - { - return atomic_cas_64((volatile unsigned long long*)ptr, expected, desired); - } - #define BOOST_ATOMIC_HAVE_CAS64 1 - }}} -#endif - - -namespace boost { namespace detail { namespace atomic { - -#ifdef BOOST_ATOMIC_HAVE_CAS32 -template -class atomic_generic_cas32 { -private: - typedef atomic_generic_cas32 this_type; -public: - explicit atomic_generic_cas32(T v) : i((int32_t)v) {} - atomic_generic_cas32() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T expected=(T)i; - do { } while(!const_cast(this)->compare_exchange_weak(expected, expected, order, memory_order_relaxed)); - return expected; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - exchange(v); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - T found; - found=(T)fenced_compare_exchange_strong_32(&i, (int32_t)expected, (int32_t)desired); - bool success=(found==expected); - expected=found; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - T expected=(T)i; - do { } while(!compare_exchange_weak(expected, r, order, memory_order_relaxed)); - return expected; - } - - bool is_lock_free(void) const volatile {return true;} - typedef T integral_type; -private: - mutable int32_t i; -}; - -template -class platform_atomic_integral : public build_atomic_from_exchange > { -public: - typedef build_atomic_from_exchange > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; -#endif - -} } } - -#endif diff --git a/include/boost/atomic/detail/integral-casts.hpp b/include/boost/atomic/detail/integral-casts.hpp deleted file mode 100644 index 2b045d2e4..000000000 --- a/include/boost/atomic/detail/integral-casts.hpp +++ /dev/null @@ -1,465 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_INTEGRAL_CASTS_HPP -#define BOOST_DETAIL_ATOMIC_INTEGRAL_CASTS_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace boost { namespace detail { namespace atomic { - -template -class platform_atomic : private platform_atomic_integral { -public: - typedef platform_atomic_integral super; -#if defined(BOOST_ATOMIC_ENFORCE_PODNESS) - typedef union { T e; boost::uint8_t i;} conv; -#endif - - platform_atomic() {} - explicit platform_atomic(T t) : super(to_integral(t)) - { - } - - void store(T t, memory_order order=memory_order_seq_cst) volatile - { - super::store(to_integral(t), order); - } - T load(memory_order order=memory_order_seq_cst) volatile const - { - return from_integral(super::load(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint8_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint8_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - return from_integral(super::exchange(to_integral(replacement), order)); - } - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; -protected: - static inline boost::uint8_t to_integral(T &t) - { - boost::uint8_t tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } - static inline T from_integral(boost::uint8_t t) - { - T tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } -}; - -template -class platform_atomic : private platform_atomic_integral { -public: - typedef platform_atomic_integral super; -#if defined(BOOST_ATOMIC_ENFORCE_PODNESS) - typedef union { T e; boost::uint16_t i;} conv; -#endif - - platform_atomic() {} - explicit platform_atomic(T t) : super(to_integral(t)) - { - } - - void store(T t, memory_order order=memory_order_seq_cst) volatile - { - super::store(to_integral(t), order); - } - T load(memory_order order=memory_order_seq_cst) volatile const - { - return from_integral(super::load(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint16_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint16_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - return from_integral(super::exchange(to_integral(replacement), order)); - } - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; -protected: - static inline boost::uint16_t to_integral(T &t) - { - boost::uint16_t tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } - static inline T from_integral(boost::uint16_t t) - { - T tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } -}; - -template -class platform_atomic : private platform_atomic_integral { -public: - typedef platform_atomic_integral super; -#if defined(BOOST_ATOMIC_ENFORCE_PODNESS) - typedef union { T e; boost::uint32_t i;} conv; -#endif - - platform_atomic() {} - explicit platform_atomic(T t) : super(to_integral(t)) - { - } - - void store(T t, memory_order order=memory_order_seq_cst) volatile - { - super::store(to_integral(t), order); - } - T load(memory_order order=memory_order_seq_cst) volatile const - { - return from_integral(super::load(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint32_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint32_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - return from_integral(super::exchange(to_integral(replacement), order)); - } - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; -protected: - static inline boost::uint32_t to_integral(T &t) - { - boost::uint32_t tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } - static inline T from_integral(boost::uint32_t t) - { - T tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } -}; - -template -class platform_atomic : private platform_atomic_integral { -public: - typedef platform_atomic_integral super; -#if defined(BOOST_ATOMIC_ENFORCE_PODNESS) - typedef union { T e; boost::uint64_t i;} conv; -#endif - - platform_atomic() {} - explicit platform_atomic(T t) : super(to_integral(t)) - { - } - - void store(T t, memory_order order=memory_order_seq_cst) volatile - { - super::store(to_integral(t), order); - } - T load(memory_order order=memory_order_seq_cst) volatile const - { - return from_integral(super::load(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint64_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::uint64_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - return from_integral(super::exchange(to_integral(replacement), order)); - } - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; -protected: - static inline boost::uint64_t to_integral(T &t) - { - boost::uint64_t tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } - static inline T from_integral(boost::uint64_t t) - { - T tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } -}; - -#if (defined(__amd64__) || defined(__x86_64__)) && \ - defined(BOOST_ATOMIC_HAVE_SSE2) && \ - defined(BOOST_ATOMIC_HAVE_GNU_SYNC_16) && \ - defined(BOOST_ATOMIC_HAVE_GNU_ALIGNED_16) && \ - defined(BOOST_ATOMIC_HAVE_GNU_128BIT_INTEGERS) - -#define BOOST_ATOMIC_HAVE_128BIT_SUPPORT - -template -class platform_atomic : private platform_atomic_integral<__uint128_t> { -public: - typedef platform_atomic_integral<__uint128_t> super; -#if defined(BOOST_ATOMIC_ENFORCE_PODNESS) - typedef union { T e; __uint128_t i;} conv; -#endif - - platform_atomic() {} - explicit platform_atomic(T t) : super(to_integral(t)) - { - } - - void store(T t, memory_order order=memory_order_seq_cst) volatile - { - super::store(to_integral(t), order); - } - T load(memory_order order=memory_order_seq_cst) volatile const - { - return from_integral(super::load(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - __uint128_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - __uint128_t _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - return from_integral(super::exchange(to_integral(replacement), order)); - } - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; -protected: - static inline __uint128_t to_integral(T &t) - { - __uint128_t tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } - static inline T from_integral(__uint128_t t) - { - T tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } -}; - -#elif BOOST_MSVC >= 1500 && (defined(_M_IA64) || defined(_M_AMD64)) && defined(BOOST_ATOMIC_HAVE_SSE2) - -#define BOOST_ATOMIC_HAVE_128BIT_SUPPORT - -#include - -template -class platform_atomic : private platform_atomic_integral<__m128i> { -public: - typedef platform_atomic_integral<__m128i> super; -#if defined(BOOST_ATOMIC_ENFORCE_PODNESS) - typedef union { T e; __m128i i;} conv; -#endif - - platform_atomic() {} - explicit platform_atomic(T t) : super(to_integral(t)) - { - } - - void store(T t, memory_order order=memory_order_seq_cst) volatile - { - super::store(to_integral(t), order); - } - T load(memory_order order=memory_order_seq_cst) volatile const - { - return from_integral(super::load(order)); - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - __m128i _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - __m128i _expected, _desired; - _expected=to_integral(expected); - _desired=to_integral(desired); - bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); - expected=from_integral(_expected); - return success; - } - - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - return from_integral(super::exchange(to_integral(replacement), order)); - } - - operator T(void) const volatile {return load();} - T operator=(T v) volatile {store(v); return v;} - - using super::is_lock_free; -protected: - static inline __m128i to_integral(T &t) - { - __m128i tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } - static inline T from_integral(__m128i t) - { - T tmp; - memcpy(&tmp, &t, sizeof(t)); - return tmp; - } -}; - -#endif - -} } } - -#endif diff --git a/include/boost/atomic/detail/interlocked.hpp b/include/boost/atomic/detail/interlocked.hpp deleted file mode 100644 index dcb8502f3..000000000 --- a/include/boost/atomic/detail/interlocked.hpp +++ /dev/null @@ -1,365 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_INTERLOCKED_HPP -#define BOOST_DETAIL_ATOMIC_INTERLOCKED_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include - -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - -static inline void full_fence(void) -{ - long tmp; - BOOST_INTERLOCKED_EXCHANGE(&tmp, 0); -} - -template<> -inline void platform_atomic_thread_fence(memory_order order) -{ - switch(order) { - case memory_order_seq_cst: - full_fence(); - default:; - } -} - -static inline void fence_after_load(memory_order order) -{ - switch(order) { - case memory_order_seq_cst: - full_fence(); - case memory_order_acquire: - case memory_order_acq_rel: - default:; - } -} - - -template -class atomic_interlocked_32 { -public: - explicit atomic_interlocked_32(T v) : i(v) {} - atomic_interlocked_32() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - *reinterpret_cast(&i)=v; - } else { - exchange(v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - T prev=expected; - expected=(T)BOOST_INTERLOCKED_COMPARE_EXCHANGE((long *)(&i), (long)desired, (long)expected); - bool success=(prev==expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - return (T)BOOST_INTERLOCKED_EXCHANGE((long *)&i, (long)r); - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - return (T)BOOST_INTERLOCKED_EXCHANGE_ADD((long *)&i, c); - } - - bool is_lock_free(void) const volatile {return true;} - - typedef T integral_type; -private: - T i; -}; - -# if defined(_M_IA64) || defined(_M_AMD64) - -#if defined( BOOST_USE_WINDOWS_H ) - -# include - -# define BOOST_INTERLOCKED_EXCHANGE_ADD64 InterlockedExchangeAdd64 -# define BOOST_INTERLOCKED_EXCHANGE64 InterlockedExchange64 -# define BOOST_INTERLOCKED_COMPARE_EXCHANGE64 InterlockedCompareExchange64 - -#else - -extern "C" boost::int64_t __cdecl _InterlockedExchangeAdd64(boost::int64_t volatile *, boost::int64_t); -extern "C" boost::int64_t __cdecl _InterlockedExchange64(boost::int64_t volatile *, boost::int64_t); -extern "C" boost::int64_t __cdecl _InterlockedCompareExchange64(boost::int64_t volatile *, boost::int64_t, boost::int64_t); - -# pragma intrinsic( _InterlockedExchangeAdd64 ) -# pragma intrinsic( _InterlockedExchange64 ) -# pragma intrinsic( _InterlockedCompareExchange64 ) - -# define BOOST_INTERLOCKED_EXCHANGE_ADD64 _InterlockedExchangeAdd64 -# define BOOST_INTERLOCKED_EXCHANGE64 _InterlockedExchange64 -# define BOOST_INTERLOCKED_COMPARE_EXCHANGE64 _InterlockedCompareExchange64 - -#endif - -template -class __declspec(align(64)) atomic_interlocked_64 { -public: - explicit atomic_interlocked_64(T v) : i(v) {} - atomic_interlocked_64() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=*reinterpret_cast(&i); - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - *reinterpret_cast(&i)=v; - } else { - exchange(v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - T prev=expected; - expected=(T)BOOST_INTERLOCKED_COMPARE_EXCHANGE64((boost::int64_t *)(&i), (boost::int64_t)desired, (boost::int64_t)expected); - bool success=(prev==expected); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - return (T)BOOST_INTERLOCKED_EXCHANGE64((boost::int64_t *)&i, (boost::int64_t)r); - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - return (T)BOOST_INTERLOCKED_EXCHANGE_ADD64((boost::int64_t *)&i, c); - } - - bool is_lock_free(void) const volatile {return true;} - - typedef T integral_type; -private: - T i; -}; - -// _InterlockedCompareExchange128 is available only starting with VS2008 -#if BOOST_MSVC >= 1500 && defined(BOOST_ATOMIC_HAVE_SSE2) - -#if defined( BOOST_USE_WINDOWS_H ) - -# include -# include - -# define BOOST_INTERLOCKED_COMPARE_EXCHANGE128 InterlockedCompareExchange128 - -# pragma intrinsic( _mm_load_si128 ) -# pragma intrinsic( _mm_store_si128 ) - -#else - -# include - -extern "C" unsigned char __cdecl _InterlockedCompareExchange128( - boost::int64_t volatile *Destination, - boost::int64_t ExchangeHigh, boost::int64_t ExchangeLow, - boost::int64_t *Comparand) -extern "C" __m128i _mm_load_si128(__m128i const*_P); -extern "C" void _mm_store_si128(__m128i *_P, __m128i _B); - -# pragma intrinsic( _InterlockedCompareExchange128 ) -# pragma intrinsic( _mm_load_si128 ) -# pragma intrinsic( _mm_store_si128 ) - -# define BOOST_INTERLOCKED_COMPARE_EXCHANGE128 _InterlockedCompareExchange128 - -#endif - -template -class __declspec(align(128)) atomic_interlocked_128 { -public: - explicit atomic_interlocked_128(T v) : i(v) {} - atomic_interlocked_128() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v; - if (order!=memory_order_seq_cst) { - v = _mm_load_si128(*(__m128i*)(&i)); - } - else { - v = *reinterpret_cast(&i); - } - fence_after_load(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - if (order!=memory_order_seq_cst) { - *reinterpret_cast(&i)=v; - } - else { - _mm_store_si128(*(__m128i*)(&i), v); - } - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - boost::int64_t* desired_raw = &desired; - T prev = i; - bool success = BOOST_INTERLOCKED_COMPARE_EXCHANGE128( - (boost::int64_t volatile *)(&i), - desired_raw[1], desired_raw[0], (boost::int64_t*)&expected); - if (!success) - expected = prev; - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T r, memory_order order=memory_order_seq_cst) volatile - { - boost::int64_t* desired_raw = &r; - T prev = i; - - while (!BOOST_INTERLOCKED_COMPARE_EXCHANGE128( - (boost::int64_t volatile*)&i, desired_raw[1], desired_raw[0], - (boost::int64_t*)&i)) - {} - - return prev; - } - T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile - { - T expected = i; - __m128i desired; - - do { - desired = _mm_add_epi32(*(__m128i*)(&expected), *(__m128i*)(&c)); - } while (!compare_exchange_strong(expected, *(T*)(&desired), order, memory_order_relaxed)); - - return expected; - } - - bool is_lock_free(void) const volatile {return true;} - - typedef T integral_type; -private: - T i; -}; -#endif - -#endif - -template -class platform_atomic_integral : public build_atomic_from_add > { -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template -class platform_atomic_integral: public build_atomic_from_larger_type, T> { -public: - typedef build_atomic_from_larger_type, T> super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -# if defined(_M_IA64) || defined(_M_AMD64) -template -class platform_atomic_integral - : public build_atomic_from_add > -{ -public: - typedef build_atomic_from_add > super; - - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -template<> -class platform_atomic_integral - : public build_atomic_from_add > -{ -public: - typedef build_atomic_from_add > super; - - explicit platform_atomic_integral(void* v) : super(v) {} - platform_atomic_integral(void) {} -}; - -#if BOOST_MSVC >= 1500 && defined(BOOST_ATOMIC_HAVE_SSE2) - -template -class platform_atomic_integral - : public build_atomic_from_add > -{ -public: - typedef build_atomic_from_add > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - -#endif - -#endif - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/linux-arm.hpp b/include/boost/atomic/detail/linux-arm.hpp deleted file mode 100644 index 142c20a46..000000000 --- a/include/boost/atomic/detail/linux-arm.hpp +++ /dev/null @@ -1,171 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_LINUX_ARM_HPP -#define BOOST_DETAIL_ATOMIC_LINUX_ARM_HPP - -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// Copyright (c) 2009 Helge Bahmann -// Copyright (c) 2009 Phil Endecott -// ARM Code by Phil Endecott, based on other architectures. - -#include -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - - -// Different ARM processors have different atomic instructions. In particular, -// architecture versions before v6 (which are still in widespread use, e.g. the -// Intel/Marvell XScale chips like the one in the NSLU2) have only atomic swap. -// On Linux the kernel provides some support that lets us abstract away from -// these differences: it provides emulated CAS and barrier functions at special -// addresses that are garaunteed not to be interrupted by the kernel. Using -// this facility is slightly slower than inline assembler would be, but much -// faster than a system call. -// -// For documentation, see arch/arm/kernel/entry-armv.S in the kernel source -// (search for "User Helpers"). - - -typedef void (kernel_dmb_t)(void); -#define BOOST_ATOMIC_KERNEL_DMB (*(kernel_dmb_t *)0xffff0fa0) - -static inline void fence_before(memory_order order) -{ - switch(order) { - // FIXME I really don't know which of these cases should call - // kernel_dmb() and which shouldn't... - case memory_order_consume: - case memory_order_release: - case memory_order_acq_rel: - case memory_order_seq_cst: - BOOST_ATOMIC_KERNEL_DMB(); - default:; - } -} - -static inline void fence_after(memory_order order) -{ - switch(order) { - // FIXME I really don't know which of these cases should call - // kernel_dmb() and which shouldn't... - case memory_order_acquire: - case memory_order_acq_rel: - case memory_order_seq_cst: - BOOST_ATOMIC_KERNEL_DMB(); - default:; - } -} - -#undef BOOST_ATOMIC_KERNEL_DMB - - -template -class atomic_linux_arm_4 { - -// typedef int (kernel_cmpxchg_t)(T oldval, T newval, T *ptr); - typedef int (kernel_cmpxchg_t)(T oldval, T newval, volatile T *ptr); -# define BOOST_ATOMIC_KERNEL_CMPXCHG (*(kernel_cmpxchg_t *)0xffff0fc0) - // Returns 0 if *ptr was changed. - -public: - explicit atomic_linux_arm_4(T v) : i(v) {} - atomic_linux_arm_4() {} - T load(memory_order order=memory_order_seq_cst) const volatile - { - T v=const_cast(i); - fence_after(order); - return v; - } - void store(T v, memory_order order=memory_order_seq_cst) volatile - { - fence_before(order); - const_cast(i)=v; - } - bool compare_exchange_strong( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - // Aparently we can consider kernel_cmpxchg to be strong if it is retried - // by the kernel after being interrupted, which I think it is. - // Also it seems that when an ll/sc implementation is used the kernel - // loops until the store succeeds. - bool success = BOOST_ATOMIC_KERNEL_CMPXCHG(expected,desired,&i)==0; - if (!success) expected = load(memory_order_relaxed); - return success; - } - bool compare_exchange_weak( - T &expected, - T desired, - memory_order success_order, - memory_order failure_order) volatile - { - return compare_exchange_strong(expected, desired, success_order, failure_order); - } - T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile - { - // Copied from build_exchange. - T o=load(memory_order_relaxed); - do {} while(!compare_exchange_weak(o, replacement, order, order)); - return o; - // Note that ARM has an atomic swap instruction that we could use here: - // T oldval; - // asm volatile ("swp\t%0, %1, [%2]" : "=&r"(oldval) : "r" (replacement), "r" (&i) : "memory"); - // return oldval; - // This instruction is deprecated in architecture >= 6. I'm unsure how inefficient - // its implementation is on those newer architectures. - // I don't think this would gain - // much since exchange() is not used often. - } - - bool is_lock_free(void) const volatile {return true;} - typedef T integral_type; -protected: -private: - T i; - -# undef BOOST_ATOMIC_KERNEL_CMPXCHG - -}; - -template -class platform_atomic_integral : public build_atomic_from_exchange > { -public: - typedef build_atomic_from_exchange > super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - - -template -class platform_atomic_integral : public build_atomic_from_larger_type, T > { -public: - typedef build_atomic_from_larger_type, T> super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - - -template -class platform_atomic_integral : public build_atomic_from_larger_type, T > { -public: - typedef build_atomic_from_larger_type, T> super; - explicit platform_atomic_integral(T v) : super(v) {} - platform_atomic_integral(void) {} -}; - - -typedef atomic_linux_arm_4 platform_atomic_address; - - -} -} -} - -#endif diff --git a/include/boost/atomic/detail/valid_integral_types.hpp b/include/boost/atomic/detail/valid_integral_types.hpp deleted file mode 100644 index b091c9f43..000000000 --- a/include/boost/atomic/detail/valid_integral_types.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef BOOST_DETAIL_ATOMIC_VALID_INTEGRAL_TYPES_HPP -#define BOOST_DETAIL_ATOMIC_VALID_INTEGRAL_TYPES_HPP - -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace boost { -namespace detail { -namespace atomic { - -template struct is_integral_type {typedef void test;}; - -template<> struct is_integral_type {typedef int test;}; - -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -#ifdef BOOST_HAS_LONG_LONG -template<> struct is_integral_type {typedef int test;}; -template<> struct is_integral_type {typedef int test;}; -#endif -#ifdef BOOST_ATOMIC_HAVE_GNU_128BIT_INTEGERS -template<> struct is_integral_type<__uint128_t> {typedef int test;}; -template<> struct is_integral_type<__int128_t> {typedef int test;}; -#endif -#if BOOST_MSVC >= 1500 && (defined(_M_IA64) || defined(_M_AMD64)) && defined(BOOST_ATOMIC_HAVE_SSE2) -#include -template<> struct is_integral_type<__m128i> {typedef int test;}; -#endif -} -} -} - -#endif diff --git a/include/boost/atomic/platform.hpp b/include/boost/atomic/platform.hpp deleted file mode 100644 index 0cdfb5632..000000000 --- a/include/boost/atomic/platform.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2009 Helge Bahmann -// -// Distributed under the Boost Software License, Version 1.0. -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include - -#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__) || defined(__x86_64__)) - - #include - -#elif defined(__GNUC__) && defined(__alpha__) - - #include - -#elif defined(__GNUC__) && (defined(__POWERPC__) || defined(__PPC__)) - - #include - -// This list of ARM architecture versions comes from Apple's arm/arch.h header. -// I don't know how complete it is. -#elif defined(__GNUC__) && (defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_7A__)) - - #include - -#elif defined(__linux__) && defined(__arm__) - - #include - -#elif defined(BOOST_USE_WINDOWS_H) || defined(_WIN32_CE) || defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) - - #include - -#else - - #warning "Using slow fallback atomic implementation" - #include - -#endif diff --git a/include/fc/abstract_types.hpp b/include/fc/abstract_types.hpp deleted file mode 100644 index 0f54e7e14..000000000 --- a/include/fc/abstract_types.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef _FC_ABSTRACT_TYPES_HPP_ -#define _FC_ABSTRACT_TYPES_HPP_ -#include -#include - -namespace fc { - - struct abstract_type { - virtual ~abstract_type(){} - virtual size_t size_of()const = 0; - /* - * @brief Inplace destructor (does not free memory) ((T*)dst)->~T(); - */ - virtual void destructor( void* dst )const = 0; - - /** @brief 'delete T' */ - virtual void destroy( void* dst )const = 0; - }; - - template - struct type : virtual abstract_type { - virtual size_t size_of()const { return sizeof(T); } - virtual void destructor( void* dst )const { ((T*)dst)->~T(); } - virtual void destroy( void* dst )const { delete ((T*)dst); } - }; - - struct abstract_moveable_type : virtual abstract_type { - virtual void move_construct( void* dst, void* src )const = 0; - virtual void move( void* dst, void* src )const = 0; - }; - - template - struct moveable_type : virtual type, virtual abstract_moveable_type { - static abstract_moveable_type& instance() { static moveable_type inst; return inst; } - virtual void destruct( void* dst )const { ((T*)dst)->~T(); } - virtual void move_construct( void* dst, void* src )const { slog( "move construct" ); new ((char*)dst) T( fc::move(*((T*)src)) ); } - virtual void move( void* dst, void* src )const { *((T*)dst) = fc::move(*((T*)src)); } - }; - - struct abstract_value_type : virtual abstract_moveable_type { - virtual void construct( void* dst )const = 0; - virtual void copy_construct( void* dst, const void* src )const = 0; - virtual void assign( void* dst, const void* src )const = 0; - }; - - /** - * Default constructable, moveable, copyable, assignable. - */ - template - struct value_type : virtual moveable_type, virtual abstract_value_type { - static abstract_value_type& instance() { static value_type inst; return inst; } - - virtual void construct( void* dst )const { new ((char*)dst) T(); } - virtual void copy_construct( void* dst, const void* src )const { new ((char*)dst) T( *((const T*)src) ); } - virtual void assign( void* dst, const void* src )const { *((T*)dst) = *((const T*)src); } - }; - - struct abstract_less_than_comparable_type { - virtual bool less_than( const void* left, const void* right )const = 0; - }; - - - template - struct less_than_comparable_type : abstract_less_than_comparable_type { - virtual bool less_than( const void* left, const void* right )const { - return *((const T*)left) < *((const T*)right); - } - }; - - struct abstract_equal_comparable_type { - virtual bool equal( const void* left, const void* right )const = 0; - }; - - template - struct equal_comparable_type : abstract_equal_comparable_type { - virtual bool equal( const void* left, const void* right )const { - return *((const T*)left) == *((const T*)right); - } - }; - - struct abstract_callable_type { - virtual void call( const void* self )const = 0; - }; - - template - struct callable_type : virtual abstract_callable_type, virtual value_type { - virtual void call( const void* self )const { (*((const T*)self))(); } - }; - -} // namespace fc - -#endif diff --git a/include/fc/actor.hpp b/include/fc/actor.hpp index 007b937e1..920d74276 100644 --- a/include/fc/actor.hpp +++ b/include/fc/actor.hpp @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include namespace fc { @@ -50,7 +50,7 @@ namespace fc { * returns a future. */ template - class actor : public ptr { + class actor : public api { public: actor(){} diff --git a/include/fc/aligned.hpp b/include/fc/aligned.hpp index 7bda080f9..b107ed15c 100644 --- a/include/fc/aligned.hpp +++ b/include/fc/aligned.hpp @@ -1,5 +1,4 @@ -#ifndef _FC_ALIGNED_HPP_ -#define _FC_ALIGNED_HPP_ +#pragma once namespace fc { template @@ -13,4 +12,3 @@ namespace fc { }; } -#endif // _FC_ALIGNED_HPP_ diff --git a/include/fc/any.hpp b/include/fc/any.hpp index 632fdb589..022d29eee 100644 --- a/include/fc/any.hpp +++ b/include/fc/any.hpp @@ -1,9 +1,7 @@ -#ifndef _FC_ANY_HPP_ -#define _FC_ANY_HPP_ +#pragma once #include namespace fc { + // TODO: define this without using boost typedef boost::any any; } - -#endif // _FC_ANY_HPP_ diff --git a/include/fc/api.hpp b/include/fc/api.hpp new file mode 100644 index 000000000..e84f50b66 --- /dev/null +++ b/include/fc/api.hpp @@ -0,0 +1,107 @@ +#pragma once +#include +#include +#include +#include + +// ms visual c++ (as of 2013) doesn't accept the standard syntax for calling a +// templated member function (foo->template bar();) +#ifdef _MSC_VER +# define FC_CALL_MEMBER_TEMPLATE_KEYWORD +#else +# define FC_CALL_MEMBER_TEMPLATE_KEYWORD template +#endif + +namespace fc { + struct identity_member { + template + static std::function functor( P&& p, R (C::*mem_func)(Args...) ); + template + static std::function functor( P&& p, R (C::*mem_func)(Args...)const ); + }; + + template< typename Interface, typename Transform > + struct vtable : public std::enable_shared_from_this> + { private: vtable(); }; + + template + struct vtable_copy_visitor { + typedef OtherType other_type; + + vtable_copy_visitor( OtherType& s):_source( s ){} + + template + void operator()( const char* name, std::function& memb, MemberPtr m )const { + OtherType* src = &_source; + memb = [src,m]( Args... args ){ return (src->*m)(args...); }; + } + OtherType& _source; + }; + + template + class api { + public: + typedef vtable vtable_type; + + api():_vtable( std::make_shared() ) {} + + /** T is anything with pointer semantics */ + template + api( const T& p ) + :_vtable( std::make_shared() ) + { + _data = std::make_shared(p); + T& ptr = boost::any_cast(*_data); + auto& pointed_at = *ptr; + typedef typename std::remove_reference::type source_vtable_type; + _vtable->FC_CALL_MEMBER_TEMPLATE_KEYWORD visit_other( vtable_copy_visitor(pointed_at) ); + } + + api( const api& cpy ):_vtable(cpy._vtable),_data(cpy._data) {} + + friend bool operator == ( const api& a, const api& b ) { return a._data == b._data && a._vtable == b._vtable; } + friend bool operator != ( const api& a, const api& b ) { return !(a._data == b._data && a._vtable == b._vtable); } + uint64_t get_handle()const { return uint64_t(_data.get()); } + + vtable_type& operator*()const { FC_ASSERT( _vtable ); return *_vtable; } + vtable_type* operator->()const { FC_ASSERT( _vtable ); return _vtable.get(); } + + protected: + std::shared_ptr _vtable; + std::shared_ptr _data; + }; + +} // namespace fc + +#include +#include +#include +#include +#include +#include +#include + +#define FC_API_VTABLE_DEFINE_MEMBER( r, data, elem ) \ + decltype(Transform::functor( (data*)nullptr, &data::elem)) elem; +#define FC_API_VTABLE_DEFINE_VISIT_OTHER( r, data, elem ) \ + { typedef typename Visitor::other_type OtherType; \ + v( BOOST_PP_STRINGIZE(elem), elem, &OtherType::elem ); } +#define FC_API_VTABLE_DEFINE_VISIT( r, data, elem ) \ + v( BOOST_PP_STRINGIZE(elem), elem ); + +#define FC_API( CLASS, METHODS ) \ +namespace fc { \ + template \ + struct vtable : public std::enable_shared_from_this> { \ + BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \ + template \ + void visit_other( Visitor&& v ){ \ + BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_VISIT_OTHER, CLASS, METHODS ) \ + } \ + template \ + void visit( Visitor&& v ){ \ + BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_VISIT, CLASS, METHODS ) \ + } \ + }; \ +} + diff --git a/include/fc/array.hpp b/include/fc/array.hpp index f97ffe501..6a2053cca 100644 --- a/include/fc/array.hpp +++ b/include/fc/array.hpp @@ -1,14 +1,139 @@ -#ifndef _FC_ARRAY_HPP_ -#define _FC_ARRAY_HPP_ +#pragma once +#include +#include +#include namespace fc { + /** + * Provides a fixed size array that is easier for templates to specialize + * against or overload than T[N]. + */ template class array { public: + /** + * Checked indexing (when in debug build) that also simplifies dereferencing + * when you have an array*. + */ + ///@{ + T& at( size_t pos ) { assert( pos < N); return data[pos]; } + const T& at( size_t pos )const { assert( pos < N); return data[pos]; } + ///@} + + T* begin() { return &data[0]; } + const T* begin()const { return &data[0]; } + const T* end()const { return &data[N]; } + + size_t size()const { return N; } + + T data[N]; + }; + + /** provided for default 0 init */ + template + class array + { + public: + typedef unsigned char T; + array(){ memset( data, 0, sizeof(data) ); } + /** + * Checked indexing (when in debug build) that also simplifies dereferencing + * when you have an array*. + */ + ///@{ + T& at( size_t pos ) { assert( pos < N); return data[pos]; } + const T& at( size_t pos )const { assert( pos < N); return data[pos]; } + ///@} + + T* begin() { return &data[0]; } + const T* begin()const { return &data[0]; } + const T* end()const { return &data[N]; } + + size_t size()const { return N; } + T data[N]; }; + /** provided for default 0 init */ + template + class array + { + public: + typedef char T; + array(){ memset( data, 0, sizeof(data) ); } + /** + * Checked indexing (when in debug build) that also simplifies dereferencing + * when you have an array*. + */ + ///@{ + T& at( size_t pos ) { assert( pos < N); return data[pos]; } + const T& at( size_t pos )const { assert( pos < N); return data[pos]; } + ///@} + + T* begin() { return &data[0]; } + const T* begin()const { return &data[0]; } + const T* end()const { return &data[N]; } + + size_t size()const { return N; } + + T data[N]; + }; + + template + bool operator == ( const array& a, const array& b ) + { return 0 == memcmp( a.data, b.data, N*sizeof(T) ); } + template + bool operator < ( const array& a, const array& b ) + { return memcmp( a.data, b.data, N*sizeof(T) ) < 0 ; } + + template + bool operator > ( const array& a, const array& b ) + { return memcmp( a.data, b.data, N*sizeof(T) ) > 0 ; } + + template + bool operator != ( const array& a, const array& b ) + { return 0 != memcmp( a.data, b.data, N*sizeof(T) ); } + + template + void to_variant( const array& bi, variant& v ) + { + v = std::vector( (const char*)&bi, ((const char*)&bi) + sizeof(bi) ); + } + template + void from_variant( const variant& v, array& bi ) + { + std::vector ve = v.as< std::vector >(); + if( ve.size() ) + { + memcpy(&bi, ve.data(), fc::min(ve.size(),sizeof(bi)) ); + } + else + memset( &bi, char(0), sizeof(bi) ); + } + + + template struct get_typename< fc::array > + { + static const char* name() + { + static std::string _name = std::string("fc::array<")+std::string(fc::get_typename::name())+","+ fc::to_string(N) + ">"; + return _name.c_str(); + } + }; +} + +#include +#include +namespace std +{ + template + struct hash > + { + size_t operator()( const fc::array& e )const + { + return fc::city_hash_size_t( (char*)&e, sizeof(e) ); + } + }; } -#endif // _FC_ARRAY_HPP_ diff --git a/include/fc/asio.hpp b/include/fc/asio.hpp index c63067e6e..93107740d 100644 --- a/include/fc/asio.hpp +++ b/include/fc/asio.hpp @@ -5,9 +5,8 @@ #pragma once #include #include -#include -#include -#include +#include +#include namespace fc { /** @@ -20,14 +19,34 @@ namespace asio { namespace detail { using namespace fc; - void read_write_handler( const promise::ptr& p, - const boost::system::error_code& ec, - size_t bytes_transferred ); + class read_write_handler + { + public: + read_write_handler(const promise::ptr& p); + void operator()(const boost::system::error_code& ec, size_t bytes_transferred); + private: + promise::ptr _completion_promise; + }; + + class read_write_handler_with_buffer + { + public: + read_write_handler_with_buffer(const promise::ptr& p, + const std::shared_ptr& buffer); + void operator()(const boost::system::error_code& ec, size_t bytes_transferred); + private: + promise::ptr _completion_promise; + std::shared_ptr _buffer; + }; + + //void read_write_handler( const promise::ptr& p, + // const boost::system::error_code& ec, + // size_t bytes_transferred ); void read_write_handler_ec( promise* p, boost::system::error_code* oec, const boost::system::error_code& ec, size_t bytes_transferred ); - void error_handler( const promise::ptr& p, + void error_handler( const promise::ptr& p, const boost::system::error_code& ec ); void error_handler_ec( promise* p, const boost::system::error_code& ec ); @@ -53,7 +72,7 @@ namespace asio { * This IO service is automatically running in its own thread to service asynchronous * requests without blocking any other threads. */ - boost::asio::io_service& default_io_service(); + boost::asio::io_service& default_io_service(bool cleanup = false); /** * @brief wraps boost::asio::async_read @@ -62,20 +81,8 @@ namespace asio { */ template size_t read( AsyncReadStream& s, const MutableBufferSequence& buf ) { - detail::non_blocking non_blocking; - - // TODO: determine if non_blocking query results in a system call that - // will slow down every read... - if( non_blocking(s) || non_blocking(s,true) ) { - boost::system::error_code ec; - size_t r = boost::asio::read( s, buf, ec ); - if( !ec ) return r; - if( ec != boost::asio::error::would_block ) - BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); - } - promise::ptr p(new promise("fc::asio::read")); - boost::asio::async_read( s, buf, boost::bind( detail::read_write_handler, p, _1, _2 ) ); + boost::asio::async_read( s, buf, detail::read_write_handler(p) ); return p->wait(); } /** @@ -92,22 +99,58 @@ namespace asio { * @return the number of bytes read. */ template - size_t read_some( AsyncReadStream& s, const MutableBufferSequence& buf ) { - detail::non_blocking non_blocking; + future read_some(AsyncReadStream& s, const MutableBufferSequence& buf) + { + promise::ptr completion_promise(new promise("fc::asio::async_read_some")); + s.async_read_some(buf, detail::read_write_handler(completion_promise)); + return completion_promise;//->wait(); + } - // TODO: determine if non_blocking query results in a system call that - // will slow down every read... - if( non_blocking(s) || non_blocking(s,true) ) { - boost::system::error_code ec; - size_t r = s.read_some( buf, ec ); - if( !ec ) return r; - if( ec != boost::asio::error::would_block ) - BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); - } - - promise::ptr p(new promise("fc::asio::read_some")); - s.async_read_some( buf, boost::bind( detail::read_write_handler, p, _1, _2 ) ); - return p->wait(); + template + future read_some(AsyncReadStream& s, char* buffer, size_t length, size_t offset = 0) + { + promise::ptr completion_promise(new promise("fc::asio::async_read_some")); + s.async_read_some(boost::asio::buffer(buffer + offset, length), + detail::read_write_handler(completion_promise)); + return completion_promise;//->wait(); + } + + template + future read_some(AsyncReadStream& s, const std::shared_ptr& buffer, size_t length, size_t offset) + { + promise::ptr completion_promise(new promise("fc::asio::async_read_some")); + s.async_read_some(boost::asio::buffer(buffer.get() + offset, length), + detail::read_write_handler_with_buffer(completion_promise, buffer)); + return completion_promise;//->wait(); + } + + template + void async_read_some(AsyncReadStream& s, const MutableBufferSequence& buf, promise::ptr completion_promise) + { + s.async_read_some(buf, detail::read_write_handler(completion_promise)); + } + + template + void async_read_some(AsyncReadStream& s, char* buffer, + size_t length, promise::ptr completion_promise) + { + s.async_read_some(boost::asio::buffer(buffer, length), detail::read_write_handler(completion_promise)); + } + + template + void async_read_some(AsyncReadStream& s, const std::shared_ptr& buffer, + size_t length, size_t offset, promise::ptr completion_promise) + { + s.async_read_some(boost::asio::buffer(buffer.get() + offset, length), detail::read_write_handler_with_buffer(completion_promise, buffer)); + } + + template + size_t read_some( AsyncReadStream& s, boost::asio::streambuf& buf ) + { + char buffer[1024]; + size_t bytes_read = read_some( s, boost::asio::buffer( buffer, sizeof(buffer) ) ); + buf.sputn( buffer, bytes_read ); + return bytes_read; } /** @brief wraps boost::asio::async_write @@ -115,18 +158,8 @@ namespace asio { */ template size_t write( AsyncWriteStream& s, const ConstBufferSequence& buf ) { - detail::non_blocking non_blocking; - - if( non_blocking(s) || non_blocking(s,true) ) { - boost::system::error_code ec; - size_t r = boost::asio::write( s, buf, ec ); - if( !ec ) return r; - if( ec != boost::asio::error::would_block) { - BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); - } - } promise::ptr p(new promise("fc::asio::write")); - boost::asio::async_write(s, buf, boost::bind( detail::read_write_handler, p, _1, _2 ) ); + boost::asio::async_write(s, buf, detail::read_write_handler(p)); return p->wait(); } @@ -136,81 +169,51 @@ namespace asio { * @return the number of bytes written */ template - size_t write_some( AsyncWriteStream& s, const ConstBufferSequence& buf ) { - detail::non_blocking non_blocking; - - if( non_blocking(s) || non_blocking(s,true) ) { - boost::system::error_code ec; - size_t r = s.write_some( buf, ec ); - if( !ec ) return r; - if( ec != boost::asio::error::would_block) { - BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); - } - } + future write_some( AsyncWriteStream& s, const ConstBufferSequence& buf ) { promise::ptr p(new promise("fc::asio::write_some")); - s.async_write_some( buf, boost::bind( detail::read_write_handler, p, _1, _2 ) ); - return p->wait(); + s.async_write_some( buf, detail::read_write_handler(p)); + return p; //->wait(); } template - class sink : public boost::iostreams::sink { - public: - // struct category : boost::iostreams::sink::category {}; - typedef char type; - - sink( AsyncWriteStream& p ):m_stream(p){} - - std::streamsize write( const char* s, std::streamsize n ) { - return fc::asio::write( m_stream, boost::asio::const_buffers_1(s,n) ); - } - void close() { m_stream.close(); } - - private: - AsyncWriteStream& m_stream; - }; + future write_some( AsyncWriteStream& s, const char* buffer, + size_t length, size_t offset = 0) { + promise::ptr p(new promise("fc::asio::write_some")); + s.async_write_some( boost::asio::buffer(buffer + offset, length), detail::read_write_handler(p)); + return p; //->wait(); + } - template - class source : public boost::iostreams::source { - public: - // struct category : boost::iostreams::sink::category {}; - typedef char type; + template + future write_some( AsyncWriteStream& s, const std::shared_ptr& buffer, + size_t length, size_t offset ) { + promise::ptr p(new promise("fc::asio::write_some")); + s.async_write_some( boost::asio::buffer(buffer.get() + offset, length), detail::read_write_handler_with_buffer(p, buffer)); + return p; //->wait(); + } - source( AsyncReadStream& p ):m_stream(p){} - - std::streamsize read( char* s, std::streamsize n ) { - return fc::asio::read_some( m_stream, boost::asio::buffer(s,n) ); - } - void close() { m_stream.close(); } - - private: - AsyncReadStream& m_stream; - }; - template - class io_device { - public: - typedef boost::iostreams::bidirectional_device_tag category; - typedef char char_type; + /** + * @pre s.non_blocking() == true + * @brief wraps boost::asio::async_write_some + * @return the number of bytes written + */ + template + void async_write_some(AsyncWriteStream& s, const ConstBufferSequence& buf, promise::ptr completion_promise) { + s.async_write_some(buf, detail::read_write_handler(completion_promise)); + } - io_device( AsyncStream& p ):m_stream(p){} - - std::streamsize write( const char* s, std::streamsize n ) { - return fc::asio::write( m_stream, boost::asio::const_buffers_1(s,static_cast(n)) ); - } - std::streamsize read( char* s, std::streamsize n ) { - try { - return fc::asio::read_some( m_stream, boost::asio::buffer(s,n) ); - } catch ( const boost::system::system_error& e ) { - if( e.code() == boost::asio::error::eof ) - return -1; - throw; - } - } - void close() { m_stream.close(); } - - private: - AsyncStream& m_stream; - }; + template + void async_write_some(AsyncWriteStream& s, const char* buffer, + size_t length, promise::ptr completion_promise) { + s.async_write_some(boost::asio::buffer(buffer, length), + detail::read_write_handler(completion_promise)); + } + template + void async_write_some(AsyncWriteStream& s, const std::shared_ptr& buffer, + size_t length, size_t offset, promise::ptr completion_promise) { + s.async_write_some(boost::asio::buffer(buffer.get() + offset, length), + detail::read_write_handler_with_buffer(completion_promise, buffer)); + } namespace tcp { typedef boost::asio::ip::tcp::endpoint endpoint; @@ -225,11 +228,11 @@ namespace asio { */ template void accept( AcceptorType& acc, SocketType& sock ) { - promise::ptr p( new promise("fc::asio::tcp::accept") ); + //promise::ptr p( new promise("fc::asio::tcp::accept") ); + promise::ptr p( new promise("fc::asio::tcp::accept") ); acc.async_accept( sock, boost::bind( fc::asio::detail::error_handler, p, _1 ) ); - auto ec = p->wait(); - if( !ec ) sock.non_blocking(true); - if( ec ) BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); + p->wait(); + //if( ec ) BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); } /** @brief wraps boost::asio::socket::async_connect @@ -238,26 +241,64 @@ namespace asio { */ template void connect( AsyncSocket& sock, const EndpointType& ep ) { - promise::ptr p(new promise("fc::asio::tcp::connect")); + promise::ptr p(new promise("fc::asio::tcp::connect")); sock.async_connect( ep, boost::bind( fc::asio::detail::error_handler, p, _1 ) ); - auto ec = p->wait(); - if( !ec ) sock.non_blocking(true); - if( ec ) BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); + p->wait(); + //if( ec ) BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); } - - typedef boost::iostreams::stream > ostream; - typedef boost::iostreams::stream > istream; - typedef boost::iostreams::stream > iostream; - } namespace udp { typedef boost::asio::ip::udp::endpoint endpoint; typedef boost::asio::ip::udp::resolver::iterator resolver_iterator; typedef boost::asio::ip::udp::resolver resolver; /// @brief resolve all udp::endpoints for hostname:port - std::vector resolve( resolver& r, const std::string& hostname, const std::string& port ); + std::vector resolve( resolver& r, const std::string& hostname, + const std::string& port ); } + template + class istream : public virtual fc::istream + { + public: + istream( std::shared_ptr str ) + :_stream( fc::move(str) ){} + + virtual size_t readsome( char* buf, size_t len ) + { + return fc::asio::read_some(*_stream, buf, len).wait(); + } + virtual size_t readsome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return fc::asio::read_some(*_stream, buf, len, offset).wait(); + } + + private: + std::shared_ptr _stream; + }; + + template + class ostream : public virtual fc::ostream + { + public: + ostream( std::shared_ptr str ) + :_stream( fc::move(str) ){} + + virtual size_t writesome( const char* buf, size_t len ) + { + return fc::asio::write_some(*_stream, buf, len).wait(); + } + + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return fc::asio::write_some(*_stream, buf, len, offset).wait(); + } + + virtual void close(){ _stream->close(); } + virtual void flush() {} + private: + std::shared_ptr _stream; + }; + } } // namespace fc::asio diff --git a/include/fc/base58.hpp b/include/fc/base58.hpp deleted file mode 100644 index 71beb15c1..000000000 --- a/include/fc/base58.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include - -namespace fc { - fc::string to_base58( const char* d, size_t s ); - fc::vector from_base58( const fc::string& base58_str ); - size_t from_base58( const fc::string& base58_str, char* out_data, size_t out_data_len ); -} diff --git a/include/fc/bigint.hpp b/include/fc/bigint.hpp deleted file mode 100644 index 7c68f1d5f..000000000 --- a/include/fc/bigint.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _FC_BIGINT_HPP -#define _FC_BIGINT_HPP -#include -#include - -struct bignum_st; -typedef bignum_st BIGNUM; - -namespace fc { - class bigint { - public: - bigint( const char* bige, uint32_t l ); - bigint( unsigned long i = 0 ); - bigint( const bigint& c ); - bigint( bigint&& c ); - ~bigint(); - - bool is_negative()const; - int64_t to_int64()const; - - int64_t log2()const; - bool operator < ( const bigint& c )const; - bool operator > ( const bigint& c )const; - bool operator >= ( const bigint& c )const; - bool operator == ( const bigint& c )const; - - bigint operator + ( const bigint& a )const; - bigint operator * ( const bigint& a )const; - bigint operator / ( const bigint& a )const; - bigint operator - ( const bigint& a )const; - - bigint& operator = ( const bigint& a ); - bigint& operator = ( bigint&& a ); - - operator fc::string()const; - - private: - BIGNUM* n; - }; -} // namespace fc - -#endif diff --git a/include/fc/bitutil.hpp b/include/fc/bitutil.hpp new file mode 100644 index 000000000..4d6c3ab69 --- /dev/null +++ b/include/fc/bitutil.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +namespace fc { + +inline uint64_t endian_reverse_u64( uint64_t x ) +{ + return (((x >> 0x38) & 0xFF) ) + | (((x >> 0x30) & 0xFF) << 0x08) + | (((x >> 0x28) & 0xFF) << 0x10) + | (((x >> 0x20) & 0xFF) << 0x18) + | (((x >> 0x18) & 0xFF) << 0x20) + | (((x >> 0x10) & 0xFF) << 0x28) + | (((x >> 0x08) & 0xFF) << 0x30) + | (((x ) & 0xFF) << 0x38) + ; +} + +inline uint32_t endian_reverse_u32( uint32_t x ) +{ + return (((x >> 0x18) & 0xFF) ) + | (((x >> 0x10) & 0xFF) << 0x08) + | (((x >> 0x08) & 0xFF) << 0x10) + | (((x ) & 0xFF) << 0x18) + ; +} + +} // namespace fc diff --git a/include/fc/bloom_filter.hpp b/include/fc/bloom_filter.hpp new file mode 100644 index 000000000..67aa5cb03 --- /dev/null +++ b/include/fc/bloom_filter.hpp @@ -0,0 +1,621 @@ +#pragma once + +/* + ********************************************************************* + * * + * Open Bloom Filter * + * * + * Author: Arash Partow - 2000 * + * URL: http://www.partow.net * + * URL: http://www.partow.net/programming/hashfunctions/index.html * + * * + * Copyright notice: * + * Free use of the Open Bloom Filter Library is permitted under the * + * guidelines and in accordance with the most current version of the * + * Common Public License. * + * http://www.opensource.org/licenses/cpl1.0.php * + * * + ********************************************************************* +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace fc { + + +static const std::size_t bits_per_char = 0x08; // 8 bits in 1 char(unsigned) +static const unsigned char bit_mask[bits_per_char] = { + 0x01, //00000001 + 0x02, //00000010 + 0x04, //00000100 + 0x08, //00001000 + 0x10, //00010000 + 0x20, //00100000 + 0x40, //01000000 + 0x80 //10000000 + }; + +class bloom_parameters +{ +public: + + bloom_parameters() + : minimum_size(1), + maximum_size(std::numeric_limits::max()), + minimum_number_of_hashes(1), + maximum_number_of_hashes(std::numeric_limits::max()), + projected_element_count(10000), + false_positive_probability(1.0 / projected_element_count), + random_seed(0xA5A5A5A55A5A5A5AULL) + {} + + virtual ~bloom_parameters() + {} + + inline bool operator!() + { + return (minimum_size > maximum_size) || + (minimum_number_of_hashes > maximum_number_of_hashes) || + (minimum_number_of_hashes < 1) || + (0 == maximum_number_of_hashes) || + (0 == projected_element_count) || + (false_positive_probability < 0.0) || + (std::numeric_limits::infinity() == std::abs(false_positive_probability)) || + (0 == random_seed) || + (0xFFFFFFFFFFFFFFFFULL == random_seed); + } + + //Allowed min/max size of the bloom filter in bits + unsigned long long int minimum_size; + unsigned long long int maximum_size; + + //Allowed min/max number of hash functions + unsigned int minimum_number_of_hashes; + unsigned int maximum_number_of_hashes; + + //The approximate number of elements to be inserted + //into the bloom filter, should be within one order + //of magnitude. The default is 10000. + unsigned long long int projected_element_count; + + //The approximate false positive probability expected + //from the bloom filter. The default is the reciprocal + //of the projected_element_count. + double false_positive_probability; + + unsigned long long int random_seed; + + struct optimal_parameters_t + { + optimal_parameters_t() + : number_of_hashes(0), + table_size(0) + {} + + unsigned int number_of_hashes; + unsigned long long int table_size; + }; + + optimal_parameters_t optimal_parameters; + + virtual bool compute_optimal_parameters() + { + /* + Note: + The following will attempt to find the number of hash functions + and minimum amount of storage bits required to construct a bloom + filter consistent with the user defined false positive probability + and estimated element insertion count. + */ + + if (!(*this)) + return false; + + double min_m = std::numeric_limits::infinity(); + double min_k = 0.0; + double curr_m = 0.0; + double k = 1.0; + + while (k < 1000.0) + { + double numerator = (- k * projected_element_count); + double denominator = std::log(1.0 - std::pow(false_positive_probability, 1.0 / k)); + curr_m = numerator / denominator; + if (curr_m < min_m) + { + min_m = curr_m; + min_k = k; + } + k += 1.0; + } + + optimal_parameters_t& optp = optimal_parameters; + + optp.number_of_hashes = static_cast(min_k); + optp.table_size = static_cast(min_m); + optp.table_size += (((optp.table_size % bits_per_char) != 0) ? (bits_per_char - (optp.table_size % bits_per_char)) : 0); + + if (optp.number_of_hashes < minimum_number_of_hashes) + optp.number_of_hashes = minimum_number_of_hashes; + else if (optp.number_of_hashes > maximum_number_of_hashes) + optp.number_of_hashes = maximum_number_of_hashes; + + if (optp.table_size < minimum_size) + optp.table_size = minimum_size; + else if (optp.table_size > maximum_size) + optp.table_size = maximum_size; + + return true; + } + +}; + +class bloom_filter +{ +protected: + + typedef unsigned int bloom_type; + typedef unsigned char cell_type; + +public: + + bloom_filter() + : salt_count_(0), + table_size_(0), + raw_table_size_(0), + projected_element_count_(0), + inserted_element_count_(0), + random_seed_(0), + desired_false_positive_probability_(0.0) + {} + + bloom_filter(const bloom_parameters& p) + : projected_element_count_(p.projected_element_count), + inserted_element_count_(0), + random_seed_((p.random_seed * 0xA5A5A5A5) + 1), + desired_false_positive_probability_(p.false_positive_probability) + { + salt_count_ = p.optimal_parameters.number_of_hashes; + table_size_ = p.optimal_parameters.table_size; + generate_unique_salt(); + raw_table_size_ = table_size_ / bits_per_char; + + bit_table_.resize( static_cast(raw_table_size_) ); + //bit_table_ = new cell_type[static_cast(raw_table_size_)]; + std::fill_n(bit_table_.data(),raw_table_size_,0x00); + } + + bloom_filter(const bloom_filter& filter) + { + this->operator=(filter); + } + + inline bool operator == (const bloom_filter& f) const + { + if (this != &f) + { + return + (salt_count_ == f.salt_count_) && + (table_size_ == f.table_size_) && + (raw_table_size_ == f.raw_table_size_) && + (projected_element_count_ == f.projected_element_count_) && + (inserted_element_count_ == f.inserted_element_count_) && + (random_seed_ == f.random_seed_) && + (desired_false_positive_probability_ == f.desired_false_positive_probability_) && + (salt_ == f.salt_) && + std::equal(f.bit_table_.data(),f.bit_table_.data() + raw_table_size_,bit_table_.data()); + } + else + return true; + } + + inline bool operator != (const bloom_filter& f) const + { + return !operator==(f); + } + + inline bloom_filter& operator = (const bloom_filter& f) + { + if (this != &f) + { + salt_count_ = f.salt_count_; + table_size_ = f.table_size_; + raw_table_size_ = f.raw_table_size_; + projected_element_count_ = f.projected_element_count_; + inserted_element_count_ = f.inserted_element_count_; + random_seed_ = f.random_seed_; + desired_false_positive_probability_ = f.desired_false_positive_probability_; + bit_table_.resize( raw_table_size_ ); + std::copy(f.bit_table_.data(),f.bit_table_.data() + raw_table_size_,bit_table_.data()); + salt_ = f.salt_; + } + return *this; + } + + virtual ~bloom_filter() + { + } + + inline bool operator!() const + { + return (0 == table_size_); + } + + inline void clear() + { + std::fill_n(bit_table_.data(),raw_table_size_,0x00); + inserted_element_count_ = 0; + } + + inline void insert(const unsigned char* key_begin, const std::size_t& length) + { + std::size_t bit_index = 0; + std::size_t bit = 0; + for (std::size_t i = 0; i < salt_.size(); ++i) + { + compute_indices(hash_ap(key_begin,length,salt_[i]),bit_index,bit); + bit_table_[bit_index / bits_per_char] |= bit_mask[bit]; + } + ++inserted_element_count_; + } + + template + inline void insert(const T& t) + { + // Note: T must be a C++ POD type. + insert(reinterpret_cast(&t),sizeof(T)); + } + + inline void insert(const std::string& key) + { + insert(reinterpret_cast(key.c_str()),key.size()); + } + + inline void insert(const char* data, const std::size_t& length) + { + insert(reinterpret_cast(data),length); + } + + template + inline void insert(const InputIterator begin, const InputIterator end) + { + InputIterator itr = begin; + while (end != itr) + { + insert(*(itr++)); + } + } + + inline virtual bool contains(const unsigned char* key_begin, const std::size_t length) const + { + std::size_t bit_index = 0; + std::size_t bit = 0; + for (std::size_t i = 0; i < salt_.size(); ++i) + { + compute_indices(hash_ap(key_begin,length,salt_[i]),bit_index,bit); + if ((bit_table_[bit_index / bits_per_char] & bit_mask[bit]) != bit_mask[bit]) + { + return false; + } + } + return true; + } + + template + inline bool contains(const T& t) const + { + return contains(reinterpret_cast(&t),static_cast(sizeof(T))); + } + + inline bool contains(const std::string& key) const + { + return contains(reinterpret_cast(key.c_str()),key.size()); + } + + inline bool contains(const char* data, const std::size_t& length) const + { + return contains(reinterpret_cast(data),length); + } + + template + inline InputIterator contains_all(const InputIterator begin, const InputIterator end) const + { + InputIterator itr = begin; + while (end != itr) + { + if (!contains(*itr)) + { + return itr; + } + ++itr; + } + return end; + } + + template + inline InputIterator contains_none(const InputIterator begin, const InputIterator end) const + { + InputIterator itr = begin; + while (end != itr) + { + if (contains(*itr)) + { + return itr; + } + ++itr; + } + return end; + } + + inline virtual unsigned long long int size() const + { + return table_size_; + } + + inline std::size_t element_count() const + { + return inserted_element_count_; + } + + inline double effective_fpp() const + { + /* + Note: + The effective false positive probability is calculated using the + designated table size and hash function count in conjunction with + the current number of inserted elements - not the user defined + predicated/expected number of inserted elements. + */ + return std::pow(1.0 - std::exp(-1.0 * salt_.size() * inserted_element_count_ / size()), 1.0 * salt_.size()); + } + + inline bloom_filter& operator &= (const bloom_filter& f) + { + /* intersection */ + if ( + (salt_count_ == f.salt_count_) && + (table_size_ == f.table_size_) && + (random_seed_ == f.random_seed_) + ) + { + for (std::size_t i = 0; i < raw_table_size_; ++i) + { + bit_table_[i] &= f.bit_table_[i]; + } + } + return *this; + } + + inline bloom_filter& operator |= (const bloom_filter& f) + { + /* union */ + if ( + (salt_count_ == f.salt_count_) && + (table_size_ == f.table_size_) && + (random_seed_ == f.random_seed_) + ) + { + for (std::size_t i = 0; i < raw_table_size_; ++i) + { + bit_table_[i] |= f.bit_table_[i]; + } + } + return *this; + } + + inline bloom_filter& operator ^= (const bloom_filter& f) + { + /* difference */ + if ( + (salt_count_ == f.salt_count_) && + (table_size_ == f.table_size_) && + (random_seed_ == f.random_seed_) + ) + { + for (std::size_t i = 0; i < raw_table_size_; ++i) + { + bit_table_[i] ^= f.bit_table_[i]; + } + } + return *this; + } + + inline const cell_type* table() const + { + return bit_table_.data(); + } + + inline std::size_t hash_count() + { + return salt_.size(); + } + +protected: + + inline virtual void compute_indices(const bloom_type& hash, std::size_t& bit_index, std::size_t& bit) const + { + bit_index = hash % table_size_; + bit = bit_index % bits_per_char; + } + + void generate_unique_salt() + { + /* + Note: + A distinct hash function need not be implementation-wise + distinct. In the current implementation "seeding" a common + hash function with different values seems to be adequate. + */ + const unsigned int predef_salt_count = 128; + static const bloom_type predef_salt[predef_salt_count] = + { + 0xAAAAAAAA, 0x55555555, 0x33333333, 0xCCCCCCCC, + 0x66666666, 0x99999999, 0xB5B5B5B5, 0x4B4B4B4B, + 0xAA55AA55, 0x55335533, 0x33CC33CC, 0xCC66CC66, + 0x66996699, 0x99B599B5, 0xB54BB54B, 0x4BAA4BAA, + 0xAA33AA33, 0x55CC55CC, 0x33663366, 0xCC99CC99, + 0x66B566B5, 0x994B994B, 0xB5AAB5AA, 0xAAAAAA33, + 0x555555CC, 0x33333366, 0xCCCCCC99, 0x666666B5, + 0x9999994B, 0xB5B5B5AA, 0xFFFFFFFF, 0xFFFF0000, + 0xB823D5EB, 0xC1191CDF, 0xF623AEB3, 0xDB58499F, + 0xC8D42E70, 0xB173F616, 0xA91A5967, 0xDA427D63, + 0xB1E8A2EA, 0xF6C0D155, 0x4909FEA3, 0xA68CC6A7, + 0xC395E782, 0xA26057EB, 0x0CD5DA28, 0x467C5492, + 0xF15E6982, 0x61C6FAD3, 0x9615E352, 0x6E9E355A, + 0x689B563E, 0x0C9831A8, 0x6753C18B, 0xA622689B, + 0x8CA63C47, 0x42CC2884, 0x8E89919B, 0x6EDBD7D3, + 0x15B6796C, 0x1D6FDFE4, 0x63FF9092, 0xE7401432, + 0xEFFE9412, 0xAEAEDF79, 0x9F245A31, 0x83C136FC, + 0xC3DA4A8C, 0xA5112C8C, 0x5271F491, 0x9A948DAB, + 0xCEE59A8D, 0xB5F525AB, 0x59D13217, 0x24E7C331, + 0x697C2103, 0x84B0A460, 0x86156DA9, 0xAEF2AC68, + 0x23243DA5, 0x3F649643, 0x5FA495A8, 0x67710DF8, + 0x9A6C499E, 0xDCFB0227, 0x46A43433, 0x1832B07A, + 0xC46AFF3C, 0xB9C8FFF0, 0xC9500467, 0x34431BDF, + 0xB652432B, 0xE367F12B, 0x427F4C1B, 0x224C006E, + 0x2E7E5A89, 0x96F99AA5, 0x0BEB452A, 0x2FD87C39, + 0x74B2E1FB, 0x222EFD24, 0xF357F60C, 0x440FCB1E, + 0x8BBE030F, 0x6704DC29, 0x1144D12F, 0x948B1355, + 0x6D8FD7E9, 0x1C11A014, 0xADD1592F, 0xFB3C712E, + 0xFC77642F, 0xF9C4CE8C, 0x31312FB9, 0x08B0DD79, + 0x318FA6E7, 0xC040D23D, 0xC0589AA7, 0x0CA5C075, + 0xF874B172, 0x0CF914D5, 0x784D3280, 0x4E8CFEBC, + 0xC569F575, 0xCDB2A091, 0x2CC016B4, 0x5C5F4421 + }; + + if (salt_count_ <= predef_salt_count) + { + std::copy(predef_salt, + predef_salt + salt_count_, + std::back_inserter(salt_)); + for (unsigned int i = 0; i < salt_.size(); ++i) + { + /* + Note: + This is done to integrate the user defined random seed, + so as to allow for the generation of unique bloom filter + instances. + */ + salt_[i] = salt_[i] * salt_[(i + 3) % salt_.size()] + static_cast(random_seed_); + } + } + else + { + std::copy(predef_salt,predef_salt + predef_salt_count,std::back_inserter(salt_)); + srand(static_cast(random_seed_)); + while (salt_.size() < salt_count_) + { + bloom_type current_salt = static_cast(rand()) * static_cast(rand()); + if (0 == current_salt) continue; + if (salt_.end() == std::find(salt_.begin(), salt_.end(), current_salt)) + { + salt_.push_back(current_salt); + } + } + } + } + + inline bloom_type hash_ap(const unsigned char* begin, std::size_t remaining_length, bloom_type hash) const + { + const unsigned char* itr = begin; + unsigned int loop = 0; + while (remaining_length >= 8) + { + const unsigned int& i1 = *(reinterpret_cast(itr)); itr += sizeof(unsigned int); + const unsigned int& i2 = *(reinterpret_cast(itr)); itr += sizeof(unsigned int); + hash ^= (hash << 7) ^ i1 * (hash >> 3) ^ + (~((hash << 11) + (i2 ^ (hash >> 5)))); + remaining_length -= 8; + } + if (remaining_length) + { + if (remaining_length >= 4) + { + const unsigned int& i = *(reinterpret_cast(itr)); + if (loop & 0x01) + hash ^= (hash << 7) ^ i * (hash >> 3); + else + hash ^= (~((hash << 11) + (i ^ (hash >> 5)))); + ++loop; + remaining_length -= 4; + itr += sizeof(unsigned int); + } + if (remaining_length >= 2) + { + const unsigned short& i = *(reinterpret_cast(itr)); + if (loop & 0x01) + hash ^= (hash << 7) ^ i * (hash >> 3); + else + hash ^= (~((hash << 11) + (i ^ (hash >> 5)))); + ++loop; + remaining_length -= 2; + itr += sizeof(unsigned short); + } + if (remaining_length) + { + hash += ((*itr) ^ (hash * 0xA5A5A5A5)) + loop; + } + } + return hash; + } + +public: + std::vector salt_; + std::vector bit_table_; + unsigned int salt_count_; + unsigned long long int table_size_; + unsigned long long int raw_table_size_; + unsigned long long int projected_element_count_; + unsigned int inserted_element_count_; + unsigned long long int random_seed_; + double desired_false_positive_probability_; +}; + +inline bloom_filter operator & (const bloom_filter& a, const bloom_filter& b) +{ + bloom_filter result = a; + result &= b; + return result; +} + +inline bloom_filter operator | (const bloom_filter& a, const bloom_filter& b) +{ + bloom_filter result = a; + result |= b; + return result; +} + +inline bloom_filter operator ^ (const bloom_filter& a, const bloom_filter& b) +{ + bloom_filter result = a; + result ^= b; + return result; +} + + +} // namespace fc + + +FC_REFLECT( fc::bloom_filter, (salt_)(bit_table_)(salt_count_)(table_size_)(raw_table_size_)(projected_element_count_)(inserted_element_count_)(random_seed_)(desired_false_positive_probability_) ) +FC_REFLECT( fc::bloom_parameters::optimal_parameters_t, (number_of_hashes)(table_size) ) +FC_REFLECT( fc::bloom_parameters, (minimum_size)(maximum_size)(minimum_number_of_hashes)(maximum_number_of_hashes)(projected_element_count)(false_positive_probability)(random_seed)(optimal_parameters) ) + +/* + Note 1: + If it can be guaranteed that bits_per_char will be of the form 2^n then + the following optimization can be used: + + hash_table[bit_index >> n] |= bit_mask[bit_index & (bits_per_char - 1)]; + + Note 2: + For performance reasons where possible when allocating memory it should + be aligned (aligned_alloc) according to the architecture being used. +*/ diff --git a/include/fc/buffer.hpp b/include/fc/buffer.hpp deleted file mode 100644 index e6fce6d34..000000000 --- a/include/fc/buffer.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _FC_BUFFER_HPP_ -#define _FC_BUFFER_HPP_ -namespace fc { - - struct const_buffer { - const_buffer( const char* const c = 0, size_t l = 0 ) - :data(c),size(l){} - const char* const data; - size_t size; - }; - - struct mutable_buffer { - mutable_buffer( char* c = 0, size_t l = 0 ) - :data(c),size(l){} - char* data; - size_t size; - }; - -} - -#endif // _FC_BUFFER_HPP_ diff --git a/include/fc/compress/smaz.hpp b/include/fc/compress/smaz.hpp new file mode 100644 index 000000000..ad60292fe --- /dev/null +++ b/include/fc/compress/smaz.hpp @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace fc { + + std::string smaz_compress( const std::string& in ); + std::string smaz_decompress( const std::string& compressed ); + +} // namespace fc diff --git a/include/fc/compress/zlib.hpp b/include/fc/compress/zlib.hpp new file mode 100644 index 000000000..10a3a0e6c --- /dev/null +++ b/include/fc/compress/zlib.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace fc +{ + + string zlib_compress(const string& in); + +} // namespace fc diff --git a/include/fc/container/deque.hpp b/include/fc/container/deque.hpp new file mode 100644 index 000000000..6a05dc8ff --- /dev/null +++ b/include/fc/container/deque.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace fc { + namespace raw { + + + } // namespace raw + +} // namespace fc diff --git a/include/fc/container/deque_fwd.hpp b/include/fc/container/deque_fwd.hpp new file mode 100644 index 000000000..80359ea67 --- /dev/null +++ b/include/fc/container/deque_fwd.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace fc { + + namespace raw { + template + void pack( Stream& s, const std::deque& value ); + template + void unpack( Stream& s, std::deque& value ); + } +} // namespace fc diff --git a/include/fc/container/flat.hpp b/include/fc/container/flat.hpp new file mode 100644 index 000000000..8fe606a6e --- /dev/null +++ b/include/fc/container/flat.hpp @@ -0,0 +1,98 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { + namespace raw { + template + inline void pack( Stream& s, const flat_set& value ) { + pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + template + inline void unpack( Stream& s, flat_set& value ) { + unsigned_int size; unpack( s, size ); + value.clear(); + FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); + value.reserve(size.value); + for( uint32_t i = 0; i < size.value; ++i ) + { + T tmp; + fc::raw::unpack( s, tmp ); + value.insert( std::move(tmp) ); + } + } + template + inline void pack( Stream& s, const flat_map& value ) { + pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + template + inline void unpack( Stream& s, flat_map& value ) + { + unsigned_int size; unpack( s, size ); + value.clear(); + FC_ASSERT( size.value*(sizeof(K)+sizeof(V)) < MAX_ARRAY_ALLOC_SIZE ); + value.reserve(size.value); + for( uint32_t i = 0; i < size.value; ++i ) + { + std::pair tmp; + fc::raw::unpack( s, tmp ); + value.insert( std::move(tmp) ); + } + } + } // namespace raw + + + template + void to_variant( const flat_set& var, variant& vo ) + { + std::vector vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, flat_set& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + vo.reserve( vars.size() ); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as() ); + } + + template + void to_variant( const flat_map& var, variant& vo ) + { + std::vector< variant > vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = fc::variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, flat_map& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as< std::pair >() ); + + } + +} diff --git a/include/fc/container/flat_fwd.hpp b/include/fc/container/flat_fwd.hpp new file mode 100644 index 000000000..999a9766d --- /dev/null +++ b/include/fc/container/flat_fwd.hpp @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +namespace fc { + + using boost::container::flat_map; + using boost::container::flat_set; + + namespace raw { + template + void pack( Stream& s, const flat_set& value ); + template + void unpack( Stream& s, flat_set& value ); + template + void pack( Stream& s, const flat_map& value ); + template + void unpack( Stream& s, flat_map& value ) ; + } // namespace raw + +} // fc diff --git a/include/fc/crypto/aes.hpp b/include/fc/crypto/aes.hpp new file mode 100644 index 000000000..f0c761687 --- /dev/null +++ b/include/fc/crypto/aes.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { + class path; + + class aes_encoder + { + public: + aes_encoder(); + ~aes_encoder(); + + void init( const fc::sha256& key, const fc::uint128& init_value ); + uint32_t encode( const char* plaintxt, uint32_t len, char* ciphertxt ); + // uint32_t final_encode( char* ciphertxt ); + + private: + struct impl; + fc::fwd my; + }; + class aes_decoder + { + public: + aes_decoder(); + ~aes_decoder(); + + void init( const fc::sha256& key, const fc::uint128& init_value ); + uint32_t decode( const char* ciphertxt, uint32_t len, char* plaintext ); +// uint32_t final_decode( char* plaintext ); + + private: + struct impl; + fc::fwd my; + }; + + unsigned aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, + unsigned char *iv, unsigned char *ciphertext); + unsigned aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, + unsigned char *iv, unsigned char *plaintext); + unsigned aes_cfb_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, + unsigned char *iv, unsigned char *plaintext); + + std::vector aes_encrypt( const fc::sha512& key, const std::vector& plain_text ); + std::vector aes_decrypt( const fc::sha512& key, const std::vector& cipher_text ); + + /** encrypts plain_text and then includes a checksum that enables us to verify the integrety of + * the file / key prior to decryption. + */ + void aes_save( const fc::path& file, const fc::sha512& key, std::vector plain_text ); + + /** + * recovers the plain_text saved via aes_save() + */ + std::vector aes_load( const fc::path& file, const fc::sha512& key ); + +} // namespace fc diff --git a/include/fc/crypto/base32.hpp b/include/fc/crypto/base32.hpp new file mode 100644 index 000000000..15bf16982 --- /dev/null +++ b/include/fc/crypto/base32.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace fc +{ + std::vector from_base32( const fc::string& b32 ); + fc::string to_base32( const std::vector& vec ); + fc::string to_base32( const char* data, size_t len ); +} diff --git a/include/fc/crypto/base36.hpp b/include/fc/crypto/base36.hpp new file mode 100644 index 000000000..aeb9c352c --- /dev/null +++ b/include/fc/crypto/base36.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace fc +{ + std::vector from_base36( const fc::string& b36 ); + fc::string to_base36( const std::vector& vec ); + fc::string to_base36( const char* data, size_t len ); +} diff --git a/include/fc/crypto/base58.hpp b/include/fc/crypto/base58.hpp new file mode 100644 index 000000000..2f4fb38aa --- /dev/null +++ b/include/fc/crypto/base58.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace fc { + std::string to_base58( const char* d, size_t s ); + std::string to_base58( const std::vector& data ); + std::vector from_base58( const std::string& base58_str ); + size_t from_base58( const std::string& base58_str, char* out_data, size_t out_data_len ); +} diff --git a/include/fc/base64.hpp b/include/fc/crypto/base64.hpp similarity index 60% rename from include/fc/base64.hpp rename to include/fc/crypto/base64.hpp index 3a69a2ef0..012a19f06 100644 --- a/include/fc/base64.hpp +++ b/include/fc/crypto/base64.hpp @@ -1,10 +1,9 @@ -#ifndef _FC_BASE64_HPP -#define _FC_BASE64_HPP +#pragma once #include namespace fc { std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); +inline std::string base64_encode(char const* bytes_to_encode, unsigned int in_len) { return base64_encode( (unsigned char const*)bytes_to_encode, in_len); } std::string base64_encode( const std::string& enc ); std::string base64_decode( const std::string& encoded_string); } // namespace fc -#endif // _FC_BASE64_HPP diff --git a/include/fc/crypto/bigint.hpp b/include/fc/crypto/bigint.hpp new file mode 100644 index 000000000..b91631a1e --- /dev/null +++ b/include/fc/crypto/bigint.hpp @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include + +struct bignum_st; +typedef bignum_st BIGNUM; + +namespace fc { + class bigint { + public: + bigint( const std::vector& bige ); + bigint( const char* bige, uint32_t l ); + bigint(uint64_t value); + bigint( ); + bigint( const bigint& c ); + bigint( bigint&& c ); + explicit bigint( BIGNUM* n ); + ~bigint(); + + bigint& operator = ( const bigint& a ); + bigint& operator = ( bigint&& a ); + + explicit operator bool()const; + + bool is_negative()const; + int64_t to_int64()const; + + int64_t log2()const; + bigint exp( const bigint& c )const; + + static bigint random( uint32_t bits, int t, int ); + + bool operator < ( const bigint& c )const; + bool operator > ( const bigint& c )const; + bool operator >= ( const bigint& c )const; + bool operator == ( const bigint& c )const; + bool operator != ( const bigint& c )const; + + bigint operator + ( const bigint& a )const; + bigint operator * ( const bigint& a )const; + bigint operator / ( const bigint& a )const; + bigint operator % ( const bigint& a )const; + bigint operator /= ( const bigint& a ); + bigint operator *= ( const bigint& a ); + bigint& operator += ( const bigint& a ); + bigint& operator -= ( const bigint& a ); + bigint& operator <<= ( uint32_t i ); + bigint& operator >>= ( uint32_t i ); + bigint operator - ( const bigint& a )const; + + + bigint operator++(int); + bigint& operator++(); + bigint operator--(int); + bigint& operator--(); + + operator fc::string()const; + + // returns bignum as bigendian bytes + operator std::vector()const; + + BIGNUM* dup()const; + + BIGNUM* get()const { return n; } + private: + BIGNUM* n; + }; + + class variant; + /** encodes the big int as base64 string, or a number */ + void to_variant( const bigint& bi, variant& v ); + /** decodes the big int as base64 string, or a number */ + void from_variant( const variant& v, bigint& bi ); +} // namespace fc + diff --git a/include/fc/blowfish.hpp b/include/fc/crypto/blowfish.hpp similarity index 94% rename from include/fc/blowfish.hpp rename to include/fc/crypto/blowfish.hpp index 3cee331b7..fcda4e56b 100644 --- a/include/fc/blowfish.hpp +++ b/include/fc/crypto/blowfish.hpp @@ -120,8 +120,7 @@ E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA */ -#ifndef __FC_BLOWFISH_HPP__ -#define __FC_BLOWFISH_HPP__ +#pragma once #include namespace fc { @@ -150,12 +149,12 @@ class blowfish void reset_chain() { m_oChain = m_oChain0; } // encrypt/decrypt Buffer in Place - void encrypt(unsigned char* buf, uint64_t n, int iMode=ECB); - void decrypt(unsigned char* buf, uint64_t n, int iMode=ECB); + void encrypt(unsigned char* buf, uint64_t n, int iMode=CFB); + void decrypt(unsigned char* buf, uint64_t n, int iMode=CFB); // encrypt/decrypt from Input Buffer to Output Buffer - void encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=ECB); - void decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=ECB); + void encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB); + void decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB); //Private Functions private: @@ -176,5 +175,4 @@ class blowfish } // namespace fc -#endif // __BLOWFISH_H__ diff --git a/include/fc/crypto/city.hpp b/include/fc/crypto/city.hpp new file mode 100644 index 000000000..7047ae8be --- /dev/null +++ b/include/fc/crypto/city.hpp @@ -0,0 +1,76 @@ +// Copyright (c) 2011 Google, Inc. +// +// 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. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// This file provides a few functions for hashing strings. On x86-64 +// hardware in 2011, CityHash64() is faster than other high-quality +// hash functions, such as Murmur. This is largely due to higher +// instruction-level parallelism. CityHash64() and CityHash128() also perform +// well on hash-quality tests. +// +// CityHash128() is optimized for relatively long strings and returns +// a 128-bit hash. For strings more than about 2000 bytes it can be +// faster than CityHash64(). +// +// Functions in the CityHash family are not suitable for cryptography. +// +// WARNING: This code has not been tested on big-endian platforms! +// It is known to work well on little-endian platforms that have a small penalty +// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. +// +// By the way, for some hash functions, given strings a and b, the hash +// of a+b is easily derived from the hashes of a and b. This property +// doesn't hold for any hash functions in this file. +#pragma once + +#include // for size_t. +#include +#include + +namespace fc { + +template +class array; +class uint128; + +// Hash function for a byte array. +uint64_t city_hash64(const char *buf, size_t len); + +uint32_t city_hash32(const char *buf, size_t len); + +#if SIZE_MAX > UINT32_MAX +inline size_t city_hash_size_t(const char *buf, size_t len) { return city_hash64(buf, len); } +#else +inline size_t city_hash_size_t(const char *buf, size_t len) { return city_hash32(buf, len); } +#endif + +// Hash function for a byte array. +uint128 city_hash128(const char *s, size_t len); + +// Hash function for a byte array. +uint64_t city_hash_crc_64(const char *buf, size_t len); + +// Hash function for a byte array. +uint128 city_hash_crc_128(const char *s, size_t len); +array city_hash_crc_256(const char *s, size_t len); + + +} // namespace fc diff --git a/include/fc/dh.hpp b/include/fc/crypto/dh.hpp similarity index 82% rename from include/fc/dh.hpp rename to include/fc/crypto/dh.hpp index ec9112f0c..9bd4d7be8 100644 --- a/include/fc/dh.hpp +++ b/include/fc/crypto/dh.hpp @@ -1,13 +1,12 @@ -#ifndef _FC_DH_HPP_ -#define _FC_DH_HPP_ -//#include +#pragma once +#include #include #include namespace fc { struct diffie_hellman { - diffie_hellman():valid(0),g(5){} + diffie_hellman():valid(0),g(5){ fc::init_openssl(); } bool generate_params( int s, uint8_t g ); bool generate_pub_key(); bool compute_shared_key( const char* buf, uint32_t s ); @@ -25,4 +24,3 @@ namespace fc { } // namespace fc -#endif diff --git a/include/fc/crypto/digest.hpp b/include/fc/crypto/digest.hpp new file mode 100644 index 000000000..6777bc7af --- /dev/null +++ b/include/fc/crypto/digest.hpp @@ -0,0 +1,15 @@ +#pragma once +#include +#include +#include + +namespace fc { + + template + fc::sha256 digest( const T& value ) + { + fc::sha256::encoder enc; + fc::raw::pack( enc, value ); + return enc.result(); + } +} diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp new file mode 100644 index 000000000..dd60da736 --- /dev/null +++ b/include/fc/crypto/elliptic.hpp @@ -0,0 +1,297 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace fc { + + namespace ecc { + namespace detail + { + class public_key_impl; + class private_key_impl; + } + + typedef fc::sha256 blind_factor_type; + typedef fc::array commitment_type; + typedef fc::array public_key_data; + typedef fc::sha256 private_key_secret; + typedef fc::array public_key_point_data; ///< the full non-compressed version of the ECC point + typedef fc::array signature; + typedef fc::array compact_signature; + typedef std::vector range_proof_type; + typedef fc::array extended_key_data; + typedef fc::sha256 blinded_hash; + typedef fc::sha256 blind_signature; + + /** + * @class public_key + * @brief contains only the public point of an elliptic curve key. + */ + class public_key + { + public: + public_key(); + public_key(const public_key& k); + ~public_key(); +// bool verify( const fc::sha256& digest, const signature& sig ); + public_key_data serialize()const; + public_key_point_data serialize_ecc_point()const; + + operator public_key_data()const { return serialize(); } + + + public_key( const public_key_data& v ); + public_key( const public_key_point_data& v ); + public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical = true ); + + public_key child( const fc::sha256& offset )const; + + bool valid()const; + /** Computes new pubkey = generator * offset + old pubkey ?! */ +// public_key mult( const fc::sha256& offset )const; + /** Computes new pubkey = regenerate(offset).pubkey + old pubkey + * = offset * G + 1 * old pubkey ?! */ + public_key add( const fc::sha256& offset )const; + + public_key( public_key&& pk ); + public_key& operator=( public_key&& pk ); + public_key& operator=( const public_key& pk ); + + inline friend bool operator==( const public_key& a, const public_key& b ) + { + return a.serialize() == b.serialize(); + } + inline friend bool operator!=( const public_key& a, const public_key& b ) + { + return a.serialize() != b.serialize(); + } + + /// Allows to convert current public key object into base58 number. + std::string to_base58() const; + static std::string to_base58( const public_key_data &key ); + static public_key from_base58( const std::string& b58 ); + + unsigned int fingerprint() const; + + private: + friend class private_key; + static public_key from_key_data( const public_key_data& v ); + static bool is_canonical( const compact_signature& c ); + fc::fwd my; + }; + + /** + * @class private_key + * @brief an elliptic curve private key. + */ + class private_key + { + public: + private_key(); + private_key( private_key&& pk ); + private_key( const private_key& pk ); + ~private_key(); + + private_key& operator=( private_key&& pk ); + private_key& operator=( const private_key& pk ); + + static private_key generate(); + static private_key regenerate( const fc::sha256& secret ); + + private_key child( const fc::sha256& offset )const; + + /** + * This method of generation enables creating a new private key in a deterministic manner relative to + * an initial seed. A public_key created from the seed can be multiplied by the offset to calculate + * the new public key without having to know the private key. + */ + static private_key generate_from_seed( const fc::sha256& seed, const fc::sha256& offset = fc::sha256() ); + + private_key_secret get_secret()const; // get the private key secret + + operator private_key_secret ()const { return get_secret(); } + + /** + * Given a public key, calculatse a 512 bit shared secret between that + * key and this private key. + */ + fc::sha512 get_shared_secret( const public_key& pub )const; + +// signature sign( const fc::sha256& digest )const; + compact_signature sign_compact( const fc::sha256& digest, bool require_canonical = true )const; +// bool verify( const fc::sha256& digest, const signature& sig ); + + public_key get_public_key()const; + + inline friend bool operator==( const private_key& a, const private_key& b ) + { + return a.get_secret() == b.get_secret(); + } + inline friend bool operator!=( const private_key& a, const private_key& b ) + { + return a.get_secret() != b.get_secret(); + } + inline friend bool operator<( const private_key& a, const private_key& b ) + { + return a.get_secret() < b.get_secret(); + } + + unsigned int fingerprint() const { return get_public_key().fingerprint(); } + + private: + private_key( EC_KEY* k ); + static fc::sha256 get_secret( const EC_KEY * const k ); + fc::fwd my; + }; + + class extended_public_key : public public_key + { + public: + extended_public_key( const public_key& k, const sha256& c, + int child = 0, int parent_fp = 0, uint8_t depth = 0 ); + + extended_public_key derive_child( int i ) const; + extended_public_key derive_normal_child( int i ) const; + + extended_key_data serialize_extended() const; + static extended_public_key deserialize( const extended_key_data& data ); + fc::string str() const; + fc::string to_base58() const { return str(); } + static extended_public_key from_base58( const fc::string& base58 ); + + public_key generate_p( int i ) const; + public_key generate_q( int i ) const; + private: + sha256 c; + int child_num, parent_fp; + uint8_t depth; + }; + + class extended_private_key : public private_key + { + public: + extended_private_key( const private_key& k, const sha256& c, + int child = 0, int parent_fp = 0, uint8_t depth = 0 ); + + extended_public_key get_extended_public_key()const; + + extended_private_key derive_child( int i ) const; + extended_private_key derive_normal_child( int i ) const; + extended_private_key derive_hardened_child( int i ) const; + + extended_key_data serialize_extended() const; + static extended_private_key deserialize( const extended_key_data& data ); + fc::string str() const; + fc::string to_base58() const { return str(); } + static extended_private_key from_base58( const fc::string& base58 ); + static extended_private_key generate_master( const fc::string& seed ); + static extended_private_key generate_master( const char* seed, uint32_t seed_len ); + + // Oleg Andreev's blind signature scheme, + // see http://blog.oleganza.com/post/77474860538/blind-signatures + public_key blind_public_key( const extended_public_key& bob, int i ) const; + blinded_hash blind_hash( const fc::sha256& hash, int i ) const; + blind_signature blind_sign( const blinded_hash& hash, int i ) const; + // WARNING! This may produce non-canonical signatures! + compact_signature unblind_signature( const extended_public_key& bob, + const blind_signature& sig, + const fc::sha256& hash, int i ) const; + + private: + extended_private_key private_derive_rest( const fc::sha512& hash, + int num ) const; + private_key generate_a( int i ) const; + private_key generate_b( int i ) const; + private_key generate_c( int i ) const; + private_key generate_d( int i ) const; + private_key_secret compute_p( int i ) const; + private_key_secret compute_q( int i, const private_key_secret& p ) const; + sha256 c; + int child_num, parent_fp; + uint8_t depth; + }; + + struct range_proof_info + { + int exp; + int mantissa; + uint64_t min_value; + uint64_t max_value; + }; + + commitment_type blind( const blind_factor_type& blind, uint64_t value ); + blind_factor_type blind_sum( const std::vector& blinds, uint32_t non_neg ); + /** verifies taht commnits + neg_commits + excess == 0 */ + bool verify_sum( const std::vector& commits, const std::vector& neg_commits, int64_t excess ); + bool verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const range_proof_type& proof ); + + range_proof_type range_proof_sign( uint64_t min_value, + const commitment_type& commit, + const blind_factor_type& commit_blind, + const blind_factor_type& nonce, + int8_t base10_exp, + uint8_t min_bits, + uint64_t actual_value + ); + + bool verify_range_proof_rewind( blind_factor_type& blind_out, + uint64_t& value_out, + string& message_out, + const blind_factor_type& nonce, + uint64_t& min_val, + uint64_t& max_val, + commitment_type commit, + const range_proof_type& proof ); + range_proof_info range_get_info( const range_proof_type& proof ); + + + + } // namespace ecc + void to_variant( const ecc::private_key& var, variant& vo ); + void from_variant( const variant& var, ecc::private_key& vo ); + void to_variant( const ecc::public_key& var, variant& vo ); + void from_variant( const variant& var, ecc::public_key& vo ); + + namespace raw + { + template + void unpack( Stream& s, fc::ecc::public_key& pk) + { + ecc::public_key_data ser; + fc::raw::unpack(s,ser); + pk = fc::ecc::public_key( ser ); + } + + template + void pack( Stream& s, const fc::ecc::public_key& pk) + { + fc::raw::pack( s, pk.serialize() ); + } + + template + void unpack( Stream& s, fc::ecc::private_key& pk) + { + fc::sha256 sec; + unpack( s, sec ); + pk = ecc::private_key::regenerate(sec); + } + + template + void pack( Stream& s, const fc::ecc::private_key& pk) + { + fc::raw::pack( s, pk.get_secret() ); + } + + } // namespace raw + +} // namespace fc +#include + +FC_REFLECT_TYPENAME( fc::ecc::private_key ) +FC_REFLECT_TYPENAME( fc::ecc::public_key ) +FC_REFLECT( fc::ecc::range_proof_info, (exp)(mantissa)(min_value)(max_value) ) diff --git a/include/fc/hex.hpp b/include/fc/crypto/hex.hpp similarity index 80% rename from include/fc/hex.hpp rename to include/fc/crypto/hex.hpp index f3ca29ea6..201f16784 100644 --- a/include/fc/hex.hpp +++ b/include/fc/crypto/hex.hpp @@ -1,10 +1,12 @@ #pragma once #include #include +#include namespace fc { uint8_t from_hex( char c ); fc::string to_hex( const char* d, uint32_t s ); + std::string to_hex( const std::vector& data ); /** * @return the number of bytes decoded diff --git a/include/fc/crypto/hmac.hpp b/include/fc/crypto/hmac.hpp new file mode 100644 index 000000000..d1a18c62d --- /dev/null +++ b/include/fc/crypto/hmac.hpp @@ -0,0 +1,63 @@ +/* + * File: hmac.hpp + * Author: Peter Conrad + * + * Created on 1. Juli 2015, 21:48 + */ + +#ifndef HMAC_HPP +#define HMAC_HPP + +#include +#include +#include + +namespace fc { + + template + class hmac + { + public: + hmac() {} + + H digest( const char* c, uint32_t c_len, const char* d, uint32_t d_len ) + { + encoder.reset(); + add_key(c, c_len, 0x36); + encoder.write( d, d_len ); + H intermediate = encoder.result(); + + encoder.reset(); + add_key(c, c_len, 0x5c); + encoder.write( intermediate.data(), intermediate.data_size() ); + return encoder.result(); + } + + private: + void add_key( const char* c, const uint32_t c_len, char pad ) + { + if ( c_len > internal_block_size() ) + { + H hash = H::hash( c, c_len ); + add_key( hash.data(), hash.data_size(), pad ); + } + else + for (unsigned int i = 0; i < internal_block_size(); i++ ) + { + encoder.put( pad ^ ((i < c_len) ? *c++ : 0) ); + } + } + + unsigned int internal_block_size() const; + + H dummy; + typename H::encoder encoder; + }; + + typedef hmac hmac_sha224; + typedef hmac hmac_sha256; + typedef hmac hmac_sha512; +} + +#endif /* HMAC_HPP */ + diff --git a/include/fc/crypto/openssl.hpp b/include/fc/crypto/openssl.hpp new file mode 100644 index 000000000..af883d600 --- /dev/null +++ b/include/fc/crypto/openssl.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file openssl.hpp + * Provides common utility calls for wrapping openssl c api. + */ +namespace fc +{ + class path; + + template + struct ssl_wrapper + { + ssl_wrapper(ssl_type* obj):obj(obj) {} + + operator ssl_type*() { return obj; } + operator const ssl_type*() const { return obj; } + ssl_type* operator->() { return obj; } + const ssl_type* operator->() const { return obj; } + + ssl_type* obj; + }; + + #define SSL_TYPE(name, ssl_type, free_func) \ + struct name : public ssl_wrapper \ + { \ + name(ssl_type* obj=nullptr) \ + : ssl_wrapper(obj) {} \ + ~name() \ + { \ + if( obj != nullptr ) \ + free_func(obj); \ + } \ + }; + + SSL_TYPE(ec_group, EC_GROUP, EC_GROUP_free) + SSL_TYPE(ec_point, EC_POINT, EC_POINT_free) + SSL_TYPE(ecdsa_sig, ECDSA_SIG, ECDSA_SIG_free) + SSL_TYPE(bn_ctx, BN_CTX, BN_CTX_free) + SSL_TYPE(evp_cipher_ctx, EVP_CIPHER_CTX, EVP_CIPHER_CTX_free ) + + /** allocates a bignum by default.. */ + struct ssl_bignum : public ssl_wrapper + { + ssl_bignum() : ssl_wrapper(BN_new()) {} + ~ssl_bignum() { BN_free(obj); } + }; + + /** Allows to explicitly specify OpenSSL configuration file path to be loaded at OpenSSL library init. + If not set OpenSSL will try to load the conf. file (openssl.cnf) from the path it was + configured with what caused serious Keyhotee startup bugs on some Win7, Win8 machines. + \warning to be effective this method should be used before any part using OpenSSL, especially + before init_openssl call + */ + void store_configuration_path(const path& filePath); + int init_openssl(); + +} // namespace fc diff --git a/include/fc/crypto/pke.hpp b/include/fc/crypto/pke.hpp new file mode 100644 index 000000000..3f1c607dd --- /dev/null +++ b/include/fc/crypto/pke.hpp @@ -0,0 +1,113 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace fc { + namespace detail { class pke_impl; } + + class private_key; + class public_key; + void generate_key_pair( public_key&, private_key& ); + + typedef std::vector bytes; + typedef bytes signature; + + class public_key + { + public: + public_key(); + explicit public_key( const bytes& d ); + public_key( const public_key& k ); + public_key( public_key&& k ); + ~public_key(); + + operator bool()const; + + public_key& operator=(const public_key& p ); + public_key& operator=(public_key&& p ); + + bool verify( const sha1& digest, const array& sig )const; + bool verify( const sha1& digest, const signature& sig )const; + bool verify( const sha256& digest, const signature& sig )const; + bytes encrypt( const char* data, size_t len )const; + bytes encrypt( const bytes& )const; + bytes decrypt( const bytes& )const; + + bytes serialize()const; + friend void generate_key_pair( public_key&, private_key& ); + private: + std::shared_ptr my; + }; + + class private_key + { + public: + private_key(); + explicit private_key( const bytes& d ); + private_key( const private_key& k ); + private_key( private_key&& k ); + ~private_key(); + + operator bool()const; + + private_key& operator=(const private_key& p ); + private_key& operator=(private_key&& p ); + + void sign( const sha1& digest, array& sig )const; + signature sign( const sha1& digest )const; + signature sign( const sha256& digest )const; + + bytes decrypt( const char* bytes, size_t len )const; + bytes decrypt( const bytes& )const; + bytes encrypt( const bytes& )const; + + bytes serialize()const; + friend void generate_key_pair( public_key&, private_key& ); + + private: + std::shared_ptr my; + }; + bool operator==( const private_key& a, const private_key& b ); + + namespace raw + { + template + void unpack( Stream& s, fc::public_key& pk) + { + bytes ser; + fc::raw::unpack(s,ser); + pk = fc::public_key( ser ); + } + + template + void pack( Stream& s, const fc::public_key& pk) + { + fc::raw::pack( s, pk.serialize() ); + } + + template + void unpack( Stream& s, fc::private_key& pk) + { + bytes ser; + fc::raw::unpack(s,ser); + pk = fc::private_key( ser ); + } + + template + void pack( Stream& s, const fc::private_key& pk) + { + fc::raw::pack( s, pk.serialize() ); + } + } + class variant; + void to_variant( const public_key& bi, variant& v ); + void from_variant( const variant& v, public_key& bi ); + void to_variant( const private_key& bi, variant& v ); + void from_variant( const variant& v, private_key& bi ); + +} // fc + diff --git a/include/fc/crypto/rand.hpp b/include/fc/crypto/rand.hpp new file mode 100644 index 000000000..749547fc1 --- /dev/null +++ b/include/fc/crypto/rand.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace fc { + + /* provides access to the OpenSSL random number generator */ + void rand_bytes(char* buf, int count); + void rand_pseudo_bytes(char* buf, int count); +} // namespace fc diff --git a/include/fc/crypto/ripemd160.hpp b/include/fc/crypto/ripemd160.hpp new file mode 100644 index 000000000..912c3929e --- /dev/null +++ b/include/fc/crypto/ripemd160.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include + +namespace fc{ +class sha512; +class sha256; + +class ripemd160 +{ + public: + ripemd160(); + explicit ripemd160( const string& hex_str ); + + string str()const; + explicit operator string()const; + + char* data()const; + size_t data_size()const { return 160/8; } + + static ripemd160 hash( const fc::sha512& h ); + static ripemd160 hash( const fc::sha256& h ); + static ripemd160 hash( const char* d, uint32_t dlen ); + static ripemd160 hash( const string& ); + + template + static ripemd160 hash( const T& t ) + { + ripemd160::encoder e; + fc::raw::pack(e,t); + return e.result(); + } + + class encoder + { + public: + encoder(); + ~encoder(); + + void write( const char* d, uint32_t dlen ); + void put( char c ) { write( &c, 1 ); } + void reset(); + ripemd160 result(); + + private: + struct impl; + fc::fwd my; + }; + + template + inline friend T& operator<<( T& ds, const ripemd160& ep ) { + ds.write( ep.data(), sizeof(ep) ); + return ds; + } + + template + inline friend T& operator>>( T& ds, ripemd160& ep ) { + ds.read( ep.data(), sizeof(ep) ); + return ds; + } + friend ripemd160 operator << ( const ripemd160& h1, uint32_t i ); + friend bool operator == ( const ripemd160& h1, const ripemd160& h2 ); + friend bool operator != ( const ripemd160& h1, const ripemd160& h2 ); + friend ripemd160 operator ^ ( const ripemd160& h1, const ripemd160& h2 ); + friend bool operator >= ( const ripemd160& h1, const ripemd160& h2 ); + friend bool operator > ( const ripemd160& h1, const ripemd160& h2 ); + friend bool operator < ( const ripemd160& h1, const ripemd160& h2 ); + + uint32_t _hash[5]; +}; + + class variant; + void to_variant( const ripemd160& bi, variant& v ); + void from_variant( const variant& v, ripemd160& bi ); + + typedef ripemd160 uint160_t; + typedef ripemd160 uint160; + + template<> struct get_typename { static const char* name() { return "uint160_t"; } }; + +} // namespace fc + +namespace std +{ + template<> + struct hash + { + size_t operator()( const fc::ripemd160& s )const + { + return *((size_t*)&s); + } + }; +} diff --git a/include/fc/crypto/sha1.hpp b/include/fc/crypto/sha1.hpp new file mode 100644 index 000000000..32fc2e49c --- /dev/null +++ b/include/fc/crypto/sha1.hpp @@ -0,0 +1,84 @@ +#pragma once +#include +#include + +namespace fc{ + +class sha1 +{ + public: + sha1(); + explicit sha1( const string& hex_str ); + + string str()const; + operator string()const; + + char* data()const; + size_t data_size()const { return 20; } + + static sha1 hash( const char* d, uint32_t dlen ); + static sha1 hash( const string& ); + + template + static sha1 hash( const T& t ) + { + sha1::encoder e; + e << t; + return e.result(); + } + + class encoder + { + public: + encoder(); + ~encoder(); + + void write( const char* d, uint32_t dlen ); + void put( char c ) { write( &c, 1 ); } + void reset(); + sha1 result(); + + private: + struct impl; + fc::fwd my; + }; + + template + inline friend T& operator<<( T& ds, const sha1& ep ) { + ds.write( ep.data(), sizeof(ep) ); + return ds; + } + + template + inline friend T& operator>>( T& ds, sha1& ep ) { + ds.read( ep.data(), sizeof(ep) ); + return ds; + } + friend sha1 operator << ( const sha1& h1, uint32_t i ); + friend bool operator == ( const sha1& h1, const sha1& h2 ); + friend bool operator != ( const sha1& h1, const sha1& h2 ); + friend sha1 operator ^ ( const sha1& h1, const sha1& h2 ); + friend bool operator >= ( const sha1& h1, const sha1& h2 ); + friend bool operator > ( const sha1& h1, const sha1& h2 ); + friend bool operator < ( const sha1& h1, const sha1& h2 ); + + uint32_t _hash[5]; +}; + + class variant; + void to_variant( const sha1& bi, variant& v ); + void from_variant( const variant& v, sha1& bi ); + +} // namespace fc + +namespace std +{ + template<> + struct hash + { + size_t operator()( const fc::sha1& s )const + { + return *((size_t*)&s); + } + }; +} diff --git a/include/fc/crypto/sha224.hpp b/include/fc/crypto/sha224.hpp new file mode 100644 index 000000000..a6212082f --- /dev/null +++ b/include/fc/crypto/sha224.hpp @@ -0,0 +1,90 @@ +#pragma once +#include +#include +#include +#include + +namespace fc +{ + +class sha224 +{ + public: + sha224(); + explicit sha224( const string& hex_str ); + + string str()const; + operator string()const; + + char* data()const; + size_t data_size()const { return 224 / 8; } + + static sha224 hash( const char* d, uint32_t dlen ); + static sha224 hash( const string& ); + + template + static sha224 hash( const T& t ) + { + sha224::encoder e; + fc::raw::pack(e,t); + return e.result(); + } + + class encoder + { + public: + encoder(); + ~encoder(); + + void write( const char* d, uint32_t dlen ); + void put( char c ) { write( &c, 1 ); } + void reset(); + sha224 result(); + + private: + struct impl; + fc::fwd my; + }; + + template + inline friend T& operator<<( T& ds, const sha224& ep ) { + static_assert( sizeof(ep) == (8*3+4), "sha224 size mismatch" ); + ds.write( ep.data(), sizeof(ep) ); + return ds; + } + + template + inline friend T& operator>>( T& ds, sha224& ep ) { + ds.read( ep.data(), sizeof(ep) ); + return ds; + } + friend sha224 operator << ( const sha224& h1, uint32_t i ); + friend bool operator == ( const sha224& h1, const sha224& h2 ); + friend bool operator != ( const sha224& h1, const sha224& h2 ); + friend sha224 operator ^ ( const sha224& h1, const sha224& h2 ); + friend bool operator >= ( const sha224& h1, const sha224& h2 ); + friend bool operator > ( const sha224& h1, const sha224& h2 ); + friend bool operator < ( const sha224& h1, const sha224& h2 ); + friend std::size_t hash_value( const sha224& v ) { return uint64_t(v._hash[1])<<32 | v._hash[2]; } + + uint32_t _hash[7]; +}; + + class variant; + void to_variant( const sha224& bi, variant& v ); + void from_variant( const variant& v, sha224& bi ); + +} // fc +namespace std +{ + template<> + struct hash + { + size_t operator()( const fc::sha224& s )const + { + return *((size_t*)&s); + } + }; +} +#include +FC_REFLECT_TYPENAME( fc::sha224 ) diff --git a/include/fc/crypto/sha256.hpp b/include/fc/crypto/sha256.hpp new file mode 100644 index 000000000..87fffa2c5 --- /dev/null +++ b/include/fc/crypto/sha256.hpp @@ -0,0 +1,103 @@ +#pragma once +#include +#include +#include +#include + +namespace fc +{ + +class sha256 +{ + public: + sha256(); + explicit sha256( const string& hex_str ); + explicit sha256( const char *data, size_t size ); + + string str()const; + operator string()const; + + char* data()const; + size_t data_size()const { return 256 / 8; } + + static sha256 hash( const char* d, uint32_t dlen ); + static sha256 hash( const string& ); + static sha256 hash( const sha256& ); + + template + static sha256 hash( const T& t ) + { + sha256::encoder e; + fc::raw::pack(e,t); + return e.result(); + } + + class encoder + { + public: + encoder(); + ~encoder(); + + void write( const char* d, uint32_t dlen ); + void put( char c ) { write( &c, 1 ); } + void reset(); + sha256 result(); + + private: + struct impl; + fc::fwd my; + }; + + template + inline friend T& operator<<( T& ds, const sha256& ep ) { + ds.write( ep.data(), sizeof(ep) ); + return ds; + } + + template + inline friend T& operator>>( T& ds, sha256& ep ) { + ds.read( ep.data(), sizeof(ep) ); + return ds; + } + friend sha256 operator << ( const sha256& h1, uint32_t i ); + friend sha256 operator >> ( const sha256& h1, uint32_t i ); + friend bool operator == ( const sha256& h1, const sha256& h2 ); + friend bool operator != ( const sha256& h1, const sha256& h2 ); + friend sha256 operator ^ ( const sha256& h1, const sha256& h2 ); + friend bool operator >= ( const sha256& h1, const sha256& h2 ); + friend bool operator > ( const sha256& h1, const sha256& h2 ); + friend bool operator < ( const sha256& h1, const sha256& h2 ); + + uint32_t pop_count() + { + return (uint32_t)(__builtin_popcountll(_hash[0]) + + __builtin_popcountll(_hash[1]) + + __builtin_popcountll(_hash[2]) + + __builtin_popcountll(_hash[3])); + } + + uint64_t _hash[4]; +}; + + typedef sha256 uint256; + + class variant; + void to_variant( const sha256& bi, variant& v ); + void from_variant( const variant& v, sha256& bi ); + + uint64_t hash64(const char* buf, size_t len); + +} // fc +namespace std +{ + template<> + struct hash + { + size_t operator()( const fc::sha256& s )const + { + return *((size_t*)&s); + } + }; +} +#include +FC_REFLECT_TYPENAME( fc::sha256 ) diff --git a/include/fc/crypto/sha512.hpp b/include/fc/crypto/sha512.hpp new file mode 100644 index 000000000..ef10887c5 --- /dev/null +++ b/include/fc/crypto/sha512.hpp @@ -0,0 +1,78 @@ +#pragma once +#include +#include + +namespace fc +{ + +class sha512 +{ + public: + sha512(); + explicit sha512( const string& hex_str ); + + string str()const; + operator string()const; + + char* data()const; + size_t data_size()const { return 512 / 8; } + + static sha512 hash( const char* d, uint32_t dlen ); + static sha512 hash( const string& ); + + template + static sha512 hash( const T& t ) + { + sha512::encoder e; + e << t; + return e.result(); + } + + class encoder + { + public: + encoder(); + ~encoder(); + + void write( const char* d, uint32_t dlen ); + void put( char c ) { write( &c, 1 ); } + void reset(); + sha512 result(); + + private: + struct impl; + fc::fwd my; + }; + + template + inline friend T& operator<<( T& ds, const sha512& ep ) { + ds.write( ep.data(), sizeof(ep) ); + return ds; + } + + template + inline friend T& operator>>( T& ds, sha512& ep ) { + ds.read( ep.data(), sizeof(ep) ); + return ds; + } + friend sha512 operator << ( const sha512& h1, uint32_t i ); + friend bool operator == ( const sha512& h1, const sha512& h2 ); + friend bool operator != ( const sha512& h1, const sha512& h2 ); + friend sha512 operator ^ ( const sha512& h1, const sha512& h2 ); + friend bool operator >= ( const sha512& h1, const sha512& h2 ); + friend bool operator > ( const sha512& h1, const sha512& h2 ); + friend bool operator < ( const sha512& h1, const sha512& h2 ); + + uint64_t _hash[8]; +}; + + typedef fc::sha512 uint512; + + class variant; + void to_variant( const sha512& bi, variant& v ); + void from_variant( const variant& v, sha512& bi ); + +} // fc + +#include +FC_REFLECT_TYPENAME( fc::sha512 ) diff --git a/include/fc/endpoint.hpp b/include/fc/endpoint.hpp deleted file mode 100644 index f1805f33a..000000000 --- a/include/fc/endpoint.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include - -namespace fc { - - namespace ip { - class address { - public: - address( uint32_t _ip = 0 ); - address( const fc::string& s ); - - - address& operator=( const fc::string& s ); - operator fc::string()const; - - uint32_t ip()const { return _ip; } - - - private: - uint32_t _ip; - }; - - class endpoint { - public: - endpoint(); - endpoint( const fc::string& i, uint16_t p ); - endpoint( const address& i, uint16_t p ); - - uint16_t port()const { return _port; } - fc::ip::address get_address()const { return _ip; } - - private: - uint16_t _port; - address _ip; - }; - } -} diff --git a/include/fc/error.hpp b/include/fc/error.hpp deleted file mode 100644 index b08087bd2..000000000 --- a/include/fc/error.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include - -namespace fc { - struct future_wait_timeout: public std::exception{ - future_wait_timeout( const fc::string& msg = "" ):m_msg(msg){} - ~future_wait_timeout()throw() {} - const char* what()const throw() { return m_msg.c_str(); } - private: - fc::string m_msg; - }; - struct task_canceled: public std::exception{}; - struct thread_quit: public std::exception{}; - struct wait_any_error: public std::exception{}; - - struct pke_exception : public std::exception {}; - struct invalid_buffer_length : public pke_exception {}; - struct invalid_key_length : public pke_exception {}; - - struct generic_exception : public std::exception { - generic_exception( const fc::string& msg = "" ):m_msg(msg){} - ~generic_exception()throw() {} - const char* what()const throw() { return m_msg.c_str(); } - private: - fc::string m_msg; - }; - - - struct bad_cast: public std::exception{ - const char* what()const throw(){ return "bad cast"; } - }; - struct range_error: public std::exception{ - const char* what()const throw(){ return "range error"; } - }; -} - diff --git a/include/fc/error_report.hpp b/include/fc/error_report.hpp deleted file mode 100644 index 726f47df9..000000000 --- a/include/fc/error_report.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace fc { - - /** - * Represents one stack frame within an error_report. - */ - class error_frame { - public: - error_frame( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value m ); - error_frame( bool detail, const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value m ); - error_frame():file("unknown-file"),line(0){} - error_frame(const error_frame& ); - error_frame(error_frame&& ); - - error_frame& operator=(const error_frame& ); - error_frame& operator=(error_frame&& ); - - fc::string to_string()const; - fc::string to_detail_string()const; - - fc::string desc; - fc::string file; - int64_t line; - fc::string method; - fc::optional meta; - fc::string time; - bool detail; - }; - typedef fc::vector error_context; - - /** - * This class is used for rich error reporting that captures relevant errors the - * whole way up the stack. By using FC_THROW_REPORT(...) and FC_REPORT_PUSH( e, ...) - * you can capture the file, line, and method where the error was caught / rethrown. - */ - class error_report { - public: - error_report(); - error_report( const fc::string& desc, fc::value meta = fc::value() ); - error_report( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta = fc::value() ); - - error_frame& current(); - error_report& pop_frame(); - error_report& push_frame( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta = fc::value() ); - error_report& push_frame( bool detail, const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta = fc::value() ); - error_report& append( const error_report& e ); - - fc::string to_string()const; - fc::string to_detail_string()const; - error_context stack; ///< Human readable stack of what we were atempting to do. - - fc::exception_ptr copy_exception(); - }; - - fc::string substitute( const fc::string& format, const fc::value& keys ); - fc::value recursive_substitute( const value& in, const fc::value& keys ); - -} // namespace fc - -#include -FC_REFLECT( fc::error_frame, (desc)(file)(line)(method)(time)(meta)(detail) ) -FC_REFLECT( fc::error_report, (stack) ) - -#define FC_IDENT(...) __VA_ARGS__ -#define FC_REPORT( X, ... ) fc::error_report X( __FILE__, __LINE__, __func__, __VA_ARGS__ ) -#define FC_THROW_REPORT( ... ) FC_THROW( fc::error_report( __FILE__, __LINE__, __func__, __VA_ARGS__ )) -#define FC_REPORT_CURRENT(ER, ... ) (ER).pop_frame().push_frame( __FILE__, __LINE__, __func__, __VA_ARGS__ ) -#define FC_REPORT_PUSH( ER, ... ) (ER).push_frame( __FILE__, __LINE__, __func__, __VA_ARGS__ ); -#define FC_REPORT_PUSH_DETAIL( ER, ... ) (ER).push_frame( true, __FILE__, __LINE__, __func__, __VA_ARGS__ ) -#define FC_REPORT_POP(ER) (ER).pop_frame() diff --git a/include/fc/example.hpp b/include/fc/example.hpp deleted file mode 100644 index 0feaa7e17..000000000 --- a/include/fc/example.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _EXAMPLE_HPP_ -#define _EXAMPLE_HPP_ -#include - - struct example { - int a; - int b; - }; - - FC_REFLECTABLE( example ) - -#endif // _EXAMPLE_HPP_ diff --git a/include/fc/exception.hpp b/include/fc/exception.hpp deleted file mode 100644 index 3b4fc552b..000000000 --- a/include/fc/exception.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef _FC_EXCEPTION_HPP_ -#define _FC_EXCEPTION_HPP_ -#include -#include -#include - -// TODO: Remove boost exception dependency here!! -// TODO: Remove boost format dependency here!! - -// provided for easy integration with boost. -namespace boost { class exception_ptr; } - -namespace fc { - /** - * Simply including boost/exception_ptr.hpp is enough to significantly - * lengthen compile times. This header defines an 'opaque' exception - * type that provides the most 'general' exception handling needs without - * requiring a significant amount of code to be included. - */ - class exception_ptr { - public: - exception_ptr(); - exception_ptr( const boost::exception_ptr& c ); - exception_ptr( boost::exception_ptr&& c ); - exception_ptr( const exception_ptr& c ); - exception_ptr( exception_ptr&& c ); - ~exception_ptr(); - - exception_ptr& operator=(const boost::exception_ptr& c); - exception_ptr& operator=(boost::exception_ptr&& c); - - exception_ptr& operator=(const exception_ptr& c); - exception_ptr& operator=(exception_ptr&& c); - - fc::string diagnostic_information()const; - - operator bool()const; - - operator const boost::exception_ptr& ()const; - operator boost::exception_ptr& (); - private: - char my[sizeof(void*)*2]; - }; - - exception_ptr current_exception(); - template - inline exception_ptr copy_exception( T&& e ) { - try { throw e; } catch (...) { return current_exception(); } - return exception_ptr(); - } - void rethrow_exception( const exception_ptr& e ); - - void throw_exception( const char* func, const char* file, int line, const char* msg ); - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1 ); - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1, const fc::string& a2 ); - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1, const fc::string& a2, const fc::string& a3 ); - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1, const fc::string& a2, const fc::string& a3, const fc::string& a4 ); - - template - fc::string to_string( T&& v ) { return fc::string(fc::forward(v)); } - fc::string to_string( char v ); // { return fc::string(&v,1); } - fc::string to_string( uint64_t v ); - fc::string to_string( int64_t v ); - fc::string to_string( double v ); - fc::string to_string( float v ); - fc::string to_string( int8_t v ); - fc::string to_string( uint8_t v ); - fc::string to_string( int32_t v ); - fc::string to_string( uint32_t v ); - fc::string to_string( int16_t v ); - fc::string to_string( uint16_t v ); -// fc::string to_string( size_t v ); -// fc::string to_string( long int v ); - - template - void throw_exception( const char* func, const char* file, int line, const char* msg, T&& a1 ) { - throw_exception_( func, file, line, msg, to_string(fc::forward(a1) ) ); - } - - template - void throw_exception( const char* func, const char* file, int line, const char* msg, T1&& a1, T2&& a2 ) { - throw_exception_( func, file, line, msg, to_string(fc::forward(a1) ), to_string( fc::forward(a2) ) ); - } - template - void throw_exception( const char* func, const char* file, int line, const char* msg, T1&& a1, T2&& a2, T3&& a3 ) { - throw_exception_( func, file, line, msg, to_string(fc::forward(a1) ), to_string( fc::forward(a2) ), to_string( fc::forward(a3) ) ); - } - template - void throw_exception( const char* func, const char* file, int line, const char* msg, T1&& a1, T2&& a2, T3&& a3, T4&& a4 ) { - throw_exception_( func, file, line, msg, to_string(fc::forward(a1) ), to_string( fc::forward(a2) ), to_string( fc::forward(a3) ), to_string( fc::forward(a4) ) ); - } - - fc::string except_str(); - -} // namespace fc -#define FC_THROW(X) throw X -#define FC_THROW_MSG( ... ) \ - do { \ - fc::throw_exception( BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, __VA_ARGS__ ); \ - } while(0) - - -#endif // _FC_EXCEPTION_HPP_ diff --git a/include/fc/exception/exception.hpp b/include/fc/exception/exception.hpp new file mode 100644 index 000000000..028b172da --- /dev/null +++ b/include/fc/exception/exception.hpp @@ -0,0 +1,476 @@ +#pragma once +/** + * @file exception.hpp + * @brief Defines exception's used by fc + */ +#include +#include +#include +#include +#include + +namespace fc +{ + namespace detail { class exception_impl; } + + enum exception_code + { + /** for exceptions we threw that don't have an assigned code */ + unspecified_exception_code = 0, + unhandled_exception_code = 1, ///< for unhandled 3rd party exceptions + timeout_exception_code = 2, ///< timeout exceptions + file_not_found_exception_code = 3, + parse_error_exception_code = 4, + invalid_arg_exception_code = 5, + key_not_found_exception_code = 6, + bad_cast_exception_code = 7, + out_of_range_exception_code = 8, + canceled_exception_code = 9, + assert_exception_code = 10, + eof_exception_code = 11, + std_exception_code = 13, + invalid_operation_exception_code = 14, + unknown_host_exception_code = 15, + null_optional_code = 16, + udt_error_code = 17, + aes_error_code = 18, + overflow_code = 19, + underflow_code = 20, + divide_by_zero_code = 21 + }; + + /** + * @brief Used to generate a useful error report when an exception is thrown. + * @ingroup serializable + * + * At each level in the stack where the exception is caught and rethrown a + * new log_message is added to the exception. + * + * exception's are designed to be serialized to a variant and + * deserialized from an variant. + * + * @see FC_THROW_EXCEPTION + * @see FC_RETHROW_EXCEPTION + * @see FC_RETHROW_EXCEPTIONS + */ + class exception + { + public: + enum code_enum + { + code_value = unspecified_exception_code + }; + + exception( int64_t code = unspecified_exception_code, + const std::string& name_value = "exception", + const std::string& what_value = "unspecified"); + exception( log_message&&, int64_t code = unspecified_exception_code, + const std::string& name_value = "exception", + const std::string& what_value = "unspecified"); + exception( log_messages&&, int64_t code = unspecified_exception_code, + const std::string& name_value = "exception", + const std::string& what_value = "unspecified"); + exception( const log_messages&, + int64_t code = unspecified_exception_code, + const std::string& name_value = "exception", + const std::string& what_value = "unspecified"); + exception( const exception& e ); + exception( exception&& e ); + ~exception(); + + const char* name()const throw(); + int64_t code()const throw(); + virtual const char* what()const throw(); + + /** + * @return a reference to log messages that have + * been added to this log. + */ + const log_messages& get_log()const; + void append_log( log_message m ); + + /** + * Generates a detailed string including file, line, method, + * and other information that is generally only useful for + * developers. + */ + std::string to_detail_string( log_level ll = log_level::all )const; + + /** + * Generates a user-friendly error report. + */ + std::string to_string( log_level ll = log_level::info )const; + + /** + * Throw this exception as its most derived type. + * + * @note does not return. + */ + virtual NO_RETURN void dynamic_rethrow_exception()const; + + /** + * This is equivalent to: + * @code + * try { throwAsDynamic_exception(); } + * catch( ... ) { return std::current_exception(); } + * @endcode + */ + virtual std::shared_ptr dynamic_copy_exception()const; + + friend void to_variant( const exception& e, variant& v ); + friend void from_variant( const variant& e, exception& ll ); + + exception& operator=( const exception& copy ); + exception& operator=( exception&& copy ); + protected: + std::unique_ptr my; + }; + + void to_variant( const exception& e, variant& v ); + void from_variant( const variant& e, exception& ll ); + typedef std::shared_ptr exception_ptr; + + typedef optional oexception; + + + /** + * @brief re-thrown whenever an unhandled exception is caught. + * @ingroup serializable + * Any exceptions thrown by 3rd party libraries that are not + * caught get wrapped in an unhandled_exception exception. + * + * The original exception is captured as a std::exception_ptr + * which may be rethrown. The std::exception_ptr does not + * propgate across process boundaries. + */ + class unhandled_exception : public exception + { + public: + enum code_enum { + code_value = unhandled_exception_code, + }; + unhandled_exception( log_message&& m, std::exception_ptr e = std::current_exception() ); + unhandled_exception( log_messages ); + unhandled_exception( const exception& ); + + std::exception_ptr get_inner_exception()const; + + virtual NO_RETURN void dynamic_rethrow_exception()const; + virtual std::shared_ptr dynamic_copy_exception()const; + private: + std::exception_ptr _inner; + }; + + template + fc::exception_ptr copy_exception( T&& e ) + { +#if defined(_MSC_VER) && (_MSC_VER < 1700) + return std::make_shared( log_message(), + std::copy_exception(fc::forward(e)) ); +#else + return std::make_shared( log_message(), + std::make_exception_ptr(fc::forward(e)) ); +#endif + } + + + class exception_factory + { + public: + struct base_exception_builder + { + virtual NO_RETURN void rethrow( const exception& e )const = 0; + }; + + template + struct exception_builder : public base_exception_builder + { + virtual NO_RETURN void rethrow( const exception& e )const override + { + throw T( e ); + } + }; + + template + void register_exception() + { + static exception_builder builder; + auto itr = _registered_exceptions.find( T::code_value ); + assert( itr == _registered_exceptions.end() ); + (void)itr; // in release builds this hides warnings + _registered_exceptions[T::code_value] = &builder; + } + + void NO_RETURN rethrow( const exception& e )const; + + static exception_factory& instance() + { + static exception_factory once; + return once; + } + + private: + std::unordered_map _registered_exceptions; + }; +#define FC_REGISTER_EXCEPTION(r, unused, base) \ + fc::exception_factory::instance().register_exception(); + +#define FC_REGISTER_EXCEPTIONS( SEQ )\ + \ + static bool exception_init = []()->bool{ \ + BOOST_PP_SEQ_FOR_EACH( FC_REGISTER_EXCEPTION, v, SEQ ) \ + return true; \ + }(); \ + + +#define FC_DECLARE_DERIVED_EXCEPTION( TYPE, BASE, CODE, WHAT ) \ + class TYPE : public BASE \ + { \ + public: \ + enum code_enum { \ + code_value = CODE, \ + }; \ + explicit TYPE( int64_t code, const std::string& name_value, const std::string& what_value ) \ + :BASE( code, name_value, what_value ){} \ + explicit TYPE( fc::log_message&& m, int64_t code, const std::string& name_value, const std::string& what_value ) \ + :BASE( std::move(m), code, name_value, what_value ){} \ + explicit TYPE( fc::log_messages&& m, int64_t code, const std::string& name_value, const std::string& what_value )\ + :BASE( std::move(m), code, name_value, what_value ){}\ + explicit TYPE( const fc::log_messages& m, int64_t code, const std::string& name_value, const std::string& what_value )\ + :BASE( m, code, name_value, what_value ){}\ + TYPE( const std::string& what_value, const fc::log_messages& m ) \ + :BASE( m, CODE, BOOST_PP_STRINGIZE(TYPE), what_value ){} \ + TYPE( fc::log_message&& m ) \ + :BASE( fc::move(m), CODE, BOOST_PP_STRINGIZE(TYPE), WHAT ){}\ + TYPE( fc::log_messages msgs ) \ + :BASE( fc::move( msgs ), CODE, BOOST_PP_STRINGIZE(TYPE), WHAT ) {} \ + TYPE( const TYPE& c ) \ + :BASE(c){} \ + TYPE( const BASE& c ) \ + :BASE(c){} \ + TYPE():BASE(CODE, BOOST_PP_STRINGIZE(TYPE), WHAT){}\ + \ + virtual std::shared_ptr dynamic_copy_exception()const\ + { return std::make_shared( *this ); } \ + virtual NO_RETURN void dynamic_rethrow_exception()const \ + { if( code() == CODE ) throw *this;\ + else fc::exception::dynamic_rethrow_exception(); \ + } \ + }; + + #define FC_DECLARE_EXCEPTION( TYPE, CODE, WHAT ) \ + FC_DECLARE_DERIVED_EXCEPTION( TYPE, fc::exception, CODE, WHAT ) + + FC_DECLARE_EXCEPTION( timeout_exception, timeout_exception_code, "Timeout" ); + FC_DECLARE_EXCEPTION( file_not_found_exception, file_not_found_exception_code, "File Not Found" ); + /** + * @brief report's parse errors + */ + FC_DECLARE_EXCEPTION( parse_error_exception, parse_error_exception_code, "Parse Error" ); + FC_DECLARE_EXCEPTION( invalid_arg_exception, invalid_arg_exception_code, "Invalid Argument" ); + /** + * @brief reports when a key, guid, or other item is not found. + */ + FC_DECLARE_EXCEPTION( key_not_found_exception, key_not_found_exception_code, "Key Not Found" ); + FC_DECLARE_EXCEPTION( bad_cast_exception, bad_cast_exception_code, "Bad Cast" ); + FC_DECLARE_EXCEPTION( out_of_range_exception, out_of_range_exception_code, "Out of Range" ); + + /** @brief if an operation is unsupported or not valid this may be thrown */ + FC_DECLARE_EXCEPTION( invalid_operation_exception, + invalid_operation_exception_code, + "Invalid Operation" ); + /** @brief if an host name can not be resolved this may be thrown */ + FC_DECLARE_EXCEPTION( unknown_host_exception, + unknown_host_exception_code, + "Unknown Host" ); + + /** + * @brief used to report a canceled Operation + */ + FC_DECLARE_EXCEPTION( canceled_exception, canceled_exception_code, "Canceled" ); + /** + * @brief used inplace of assert() to report violations of pre conditions. + */ + FC_DECLARE_EXCEPTION( assert_exception, assert_exception_code, "Assert Exception" ); + FC_DECLARE_EXCEPTION( eof_exception, eof_exception_code, "End Of File" ); + FC_DECLARE_EXCEPTION( null_optional, null_optional_code, "null optional" ); + FC_DECLARE_EXCEPTION( udt_exception, udt_error_code, "UDT error" ); + FC_DECLARE_EXCEPTION( aes_exception, aes_error_code, "AES error" ); + FC_DECLARE_EXCEPTION( overflow_exception, overflow_code, "Integer Overflow" ); + FC_DECLARE_EXCEPTION( underflow_exception, underflow_code, "Integer Underflow" ); + FC_DECLARE_EXCEPTION( divide_by_zero_exception, divide_by_zero_code, "Integer Divide By Zero" ); + + std::string except_str(); + + void record_assert_trip( + const char* filename, + uint32_t lineno, + const char* expr + ); + + extern bool enable_record_assert_trip; +} // namespace fc + +#if __APPLE__ + #define LIKELY(x) __builtin_expect((long)!!(x), 1L) + #define UNLIKELY(x) __builtin_expect((long)!!(x), 0L) +#else + #define LIKELY(x) (x) + #define UNLIKELY(x) (x) +#endif + +/** + *@brief: Workaround for varying preprocessing behavior between MSVC and gcc + */ +#define FC_EXPAND_MACRO( x ) x +/** + * @brief Checks a condition and throws an assert_exception if the test is FALSE + */ +#define FC_ASSERT( TEST, ... ) \ + FC_EXPAND_MACRO( \ + FC_MULTILINE_MACRO_BEGIN \ + if( UNLIKELY(!(TEST)) ) \ + { \ + if( fc::enable_record_assert_trip ) \ + fc::record_assert_trip( __FILE__, __LINE__, #TEST ); \ + FC_THROW_EXCEPTION( fc::assert_exception, #TEST ": " __VA_ARGS__ ); \ + } \ + FC_MULTILINE_MACRO_END \ + ) + +#define FC_CAPTURE_AND_THROW( EXCEPTION_TYPE, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + throw EXCEPTION_TYPE( FC_LOG_MESSAGE( error, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ) ); \ + FC_MULTILINE_MACRO_END + +//#define FC_THROW( FORMAT, ... ) +// FC_INDIRECT_EXPAND workas around a bug in Visual C++ variadic macro processing that prevents it +// from separating __VA_ARGS__ into separate tokens +#define FC_INDIRECT_EXPAND(MACRO, ARGS) MACRO ARGS +#define FC_THROW( ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + throw fc::exception( FC_INDIRECT_EXPAND(FC_LOG_MESSAGE, ( error, __VA_ARGS__ )) ); \ + FC_MULTILINE_MACRO_END + +#define FC_EXCEPTION( EXCEPTION_TYPE, FORMAT, ... ) \ + EXCEPTION_TYPE( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ) +/** + * @def FC_THROW_EXCEPTION( EXCEPTION, FORMAT, ... ) + * @param EXCEPTION a class in the Phoenix::Athena::API namespace that inherits + * @param format - a const char* string with "${keys}" + */ +#define FC_THROW_EXCEPTION( EXCEPTION, FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + throw EXCEPTION( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + + +/** + * @def FC_RETHROW_EXCEPTION(ER,LOG_LEVEL,FORMAT,...) + * @brief Appends a log_message to the exception ER and rethrows it. + */ +#define FC_RETHROW_EXCEPTION( ER, LOG_LEVEL, FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + ER.append_log( FC_LOG_MESSAGE( LOG_LEVEL, FORMAT, __VA_ARGS__ ) ); \ + throw; \ + FC_MULTILINE_MACRO_END + +#define FC_LOG_AND_RETHROW( ) \ + catch( fc::exception& er ) { \ + wlog( "${details}", ("details",er.to_detail_string()) ); \ + FC_RETHROW_EXCEPTION( er, warn, "rethrow" ); \ + } catch( const std::exception& e ) { \ + fc::exception fce( \ + FC_LOG_MESSAGE( warn, "rethrow ${what}: ", ("what",e.what())), \ + fc::std_exception_code,\ + typeid(e).name(), \ + e.what() ) ; \ + wlog( "${details}", ("details",fce.to_detail_string()) ); \ + throw fce;\ + } catch( ... ) { \ + fc::unhandled_exception e( \ + FC_LOG_MESSAGE( warn, "rethrow"), \ + std::current_exception() ); \ + wlog( "${details}", ("details",e.to_detail_string()) ); \ + throw e; \ + } + +#define FC_CAPTURE_LOG_AND_RETHROW( ... ) \ + catch( fc::exception& er ) { \ + wlog( "${details}", ("details",er.to_detail_string()) ); \ + wdump( __VA_ARGS__ ); \ + FC_RETHROW_EXCEPTION( er, warn, "rethrow", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \ + } catch( const std::exception& e ) { \ + fc::exception fce( \ + FC_LOG_MESSAGE( warn, "rethrow ${what}: ", FC_FORMAT_ARG_PARAMS( __VA_ARGS__ )("what",e.what())), \ + fc::std_exception_code,\ + typeid(e).name(), \ + e.what() ) ; \ + wlog( "${details}", ("details",fce.to_detail_string()) ); \ + wdump( __VA_ARGS__ ); \ + throw fce;\ + } catch( ... ) { \ + fc::unhandled_exception e( \ + FC_LOG_MESSAGE( warn, "rethrow", FC_FORMAT_ARG_PARAMS( __VA_ARGS__) ), \ + std::current_exception() ); \ + wlog( "${details}", ("details",e.to_detail_string()) ); \ + wdump( __VA_ARGS__ ); \ + throw e; \ + } + +#define FC_CAPTURE_AND_LOG( ... ) \ + catch( fc::exception& er ) { \ + wlog( "${details}", ("details",er.to_detail_string()) ); \ + wdump( __VA_ARGS__ ); \ + } catch( const std::exception& e ) { \ + fc::exception fce( \ + FC_LOG_MESSAGE( warn, "rethrow ${what}: ",FC_FORMAT_ARG_PARAMS( __VA_ARGS__ )("what",e.what()) ), \ + fc::std_exception_code,\ + typeid(e).name(), \ + e.what() ) ; \ + wlog( "${details}", ("details",fce.to_detail_string()) ); \ + wdump( __VA_ARGS__ ); \ + } catch( ... ) { \ + fc::unhandled_exception e( \ + FC_LOG_MESSAGE( warn, "rethrow", FC_FORMAT_ARG_PARAMS( __VA_ARGS__) ), \ + std::current_exception() ); \ + wlog( "${details}", ("details",e.to_detail_string()) ); \ + wdump( __VA_ARGS__ ); \ + } + + +/** + * @def FC_RETHROW_EXCEPTIONS(LOG_LEVEL,FORMAT,...) + * @brief Catchs all exception's, std::exceptions, and ... and rethrows them after + * appending the provided log message. + */ +#define FC_RETHROW_EXCEPTIONS( LOG_LEVEL, FORMAT, ... ) \ + catch( fc::exception& er ) { \ + FC_RETHROW_EXCEPTION( er, LOG_LEVEL, FORMAT, __VA_ARGS__ ); \ + } catch( const std::exception& e ) { \ + fc::exception fce( \ + FC_LOG_MESSAGE( LOG_LEVEL, "${what}: " FORMAT,__VA_ARGS__("what",e.what())), \ + fc::std_exception_code,\ + typeid(e).name(), \ + e.what() ) ; throw fce;\ + } catch( ... ) { \ + throw fc::unhandled_exception( \ + FC_LOG_MESSAGE( LOG_LEVEL, FORMAT,__VA_ARGS__), \ + std::current_exception() ); \ + } + +#define FC_CAPTURE_AND_RETHROW( ... ) \ + catch( fc::exception& er ) { \ + FC_RETHROW_EXCEPTION( er, warn, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \ + } catch( const std::exception& e ) { \ + fc::exception fce( \ + FC_LOG_MESSAGE( warn, "${what}: ",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)("what",e.what())), \ + fc::std_exception_code,\ + typeid(e).name(), \ + e.what() ) ; throw fce;\ + } catch( ... ) { \ + throw fc::unhandled_exception( \ + FC_LOG_MESSAGE( warn, "",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)), \ + std::current_exception() ); \ + } + diff --git a/include/fc/file_appender.hpp b/include/fc/file_appender.hpp deleted file mode 100644 index 0ffae0d7d..000000000 --- a/include/fc/file_appender.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - -class file_appender : public appender { - public: - struct config { - config( const fc::path& p = "log.txt" ); - - fc::string format; - fc::path filename; - bool flush; - bool truncate; - }; - file_appender( const value& args ); - ~file_appender(); - virtual void log( const log_message& m ); - - private: - class impl; - fc::shared_ptr my; - }; -} // namespace fc - -#include -FC_REFLECT( fc::file_appender::config, (format)(filename)(flush)(truncate) ) diff --git a/include/fc/filesystem.hpp b/include/fc/filesystem.hpp index 5cea39e88..1a16926a1 100644 --- a/include/fc/filesystem.hpp +++ b/include/fc/filesystem.hpp @@ -1,6 +1,10 @@ #pragma once +#include +#include + #include -#include +#include +#include #include namespace boost { @@ -28,6 +32,8 @@ namespace fc { ~path(); path( const boost::filesystem::path& ); path( const fc::string& p ); + /// Constructor to build path using unicode native characters. + path(const std::wstring& p); path( const char* ); path( const path& p ); path( path&& p ); @@ -43,13 +49,28 @@ namespace fc { operator boost::filesystem::path& (); operator const boost::filesystem::path& ()const; - void replace_extension( const fc::path& e ); - fc::path stem()const; - fc::path extension()const; - fc::path filename()const; - fc::path parent_path()const; - fc::string string()const; - fc::string generic_string()const; + void replace_extension( const fc::path& e ); + fc::path stem()const; + fc::path extension()const; + fc::path filename()const; + fc::path parent_path()const; + fc::string string()const; + fc::string generic_string()const; + /** On windows, returns a path where all path separators are '\' suitable for displaying + * to users. On other platforms, it does the same as generic_string() + */ + fc::string preferred_string() const; + + std::wstring wstring() const; + std::wstring generic_wstring() const; + std::wstring preferred_wstring() const; + + /** Retrieves native string path representation and next converts it into + ANSI UTF-8 representation. + It is needed since not all parts of fc library accept unicode paths + (fc::file_mapping). + */ + std::string to_native_ansi_path() const; /** * @brief replaces '/' with '\' in the result of generic_string() @@ -60,10 +81,35 @@ namespace fc { bool is_relative()const; bool is_absolute()const; + + static char separator_char; + private: + #ifdef _WIN64 + fwd _p; + #else fwd _p; + #endif }; + namespace detail + { + class path_wrapper + { + public: + path_wrapper(path p) : + _path(p) + { + } + const path* operator->() const + { + return &_path; + } + private: + path _path; + }; + } + class directory_iterator { public: directory_iterator( const fc::path& p ); @@ -71,6 +117,7 @@ namespace fc { ~directory_iterator(); fc::path operator*()const; + detail::path_wrapper operator->() const; directory_iterator& operator++(int); directory_iterator& operator++(); @@ -104,20 +151,114 @@ namespace fc { void create_directories( const path& p ); void remove_all( const path& p ); path absolute( const path& p ); + path make_relative(const path& from, const path& to); path canonical( const path& p ); uint64_t file_size( const path& p ); + uint64_t directory_size( const path& p ); bool remove( const path& p ); void copy( const path& from, const path& to ); void rename( const path& from, const path& to ); + void resize_file( const path& file, size_t s ); + + // setuid, setgid not implemented. + // translates octal permission like 0755 to S_ stuff defined in sys/stat.h + // no-op on Windows. + void chmod( const path& p, int perm ); + void create_hard_link( const path& from, const path& to ); - path unique_path(); - path temp_directory_path(); + path unique_path(); + path temp_directory_path(); + + /** @return the home directory on Linux and OS X and the Profile directory on Windows */ + const path& home_path(); - class value; - void pack( fc::value& , const fc::path& ); - void unpack( const fc::value& , fc::path& ); + /** @return the home_path() on Linux, home_path()/Library/Application Support/ on OS X, + * and APPDATA on windows + */ + const path& app_path(); + + /** @return application executable path */ + const fc::path& current_path(); + + class variant; + void to_variant( const fc::path&, fc::variant& ); + void from_variant( const fc::variant& , fc::path& ); template<> struct get_typename { static const char* name() { return "path"; } }; + + /** + * Class which creates a temporary directory inside an existing temporary directory. + */ + class temp_file_base + { + public: + inline ~temp_file_base() { remove(); } + inline operator bool() const { return _path.valid(); } + inline bool operator!() const { return !_path; } + const fc::path& path() const; + void remove(); + void release(); + protected: + typedef fc::optional path_t; + inline temp_file_base(const path_t& path) : _path(path) {} + inline temp_file_base(path_t&& path) : _path(std::move(path)) {} + path_t _path; + }; + + /** + * Class which creates a temporary directory inside an existing temporary directory. + */ + class temp_file : public temp_file_base + { + public: + temp_file(temp_file&& other); + temp_file& operator=(temp_file&& other); + temp_file(const fc::path& tempFolder = fc::temp_directory_path(), bool create = false); + }; + + /** + * Class which creates a temporary directory inside an existing temporary directory. + */ + class temp_directory : public temp_file_base + { + public: + temp_directory(temp_directory&& other); + temp_directory& operator=(temp_directory&& other); + temp_directory(const fc::path& tempFolder = fc::temp_directory_path()); + }; + + +#if !defined(__APPLE__) + // this code is known to work on linux and windows. It may work correctly on mac, + // or it may need slight tweaks or extra includes. It's disabled now to avoid giving + // a false sense of security. +# define FC_HAS_SIMPLE_FILE_LOCK +#endif +#ifdef FC_HAS_SIMPLE_FILE_LOCK + /** simple class which only allows one process to open any given file. + * approximate usage: + * int main() { + * fc::simple_file_lock instance_lock("~/.my_app/.lock"); + * if (!instance_lock.try_lock()) { + * elog("my_app is already running"); + * return 1; + * } + * // do stuff here, file will be unlocked when instance_lock goes out of scope + * } + */ + class simple_lock_file + { + public: + simple_lock_file(const path& lock_file_path); + ~simple_lock_file(); + bool try_lock(); + void unlock(); + private: + class impl; + std::unique_ptr my; + }; +#endif // FC_HAS_SIMPLE_FILE_LOCK + } diff --git a/include/fc/function.hpp b/include/fc/function.hpp deleted file mode 100644 index 6aec96e72..000000000 --- a/include/fc/function.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#if 0 -#pragma once -#include -#include -namespace fc { -template -class function { - public: - function(){} - - template - function( Functor&& f ) - :func( new impl( fc::forward(f) ) ){}; - - function( const function& c ):func(c.func){} - function( function&& c ) { fc::swap( func, c.func); } - ~function(){} - - template - function& operator=( Functor&& f ) { - func.reset( new impl( fc::forward(f) ) ); - return *this; - } - - function& operator=( const function& c ) { func = c.func; return *this; } - function& operator=( function&& c ) { fc::swap(func,c.func); return *this; } - - R operator()( Args... args)const { return func->call(args...); } - - bool operator!()const { return !func; } - - protected: - - struct impl_base : public fc::retainable { - virtual ~impl_base(){} - virtual R call(Args...)const = 0; - }; - - template - struct impl : impl_base { - template - impl( U&& u ):func( fc::forward(u) ){} - - virtual R call(Args... args)const { return func(args...); } - - Functor func; - }; - function( const fc::shared_ptr& f ):func(f){} - function( fc::shared_ptr&& f ):func(fc::move(f)){} - - fc::shared_ptr func; -}; - -/** - * Provides functionality similar to boost::function. - * - * Functions have 'reference semantics', meaning that copies will all - * refer to the same underlying function. - * - * TODO: Small functions are allocated on the stack, large functors are - * allocated on the heap. - * - * Simply including boost/function adds an additional 0.6 seconds to every - * object file compared to using fc/function. - * - * Including on the other hand adds a mere 0.05 - * seconds to every object file compared to fc/function. - */ -template -class function : public function { - public: - function(){} - template - function( U&& u ) { *this = fc::forward(u); } - using function::operator=; -}; - -template -class function : public function { - public: - function(){} - function( const function& u ):function(u){} - function( function&& u ):function(u){} - function( const function& u ):function(u.func){} - function( function&& u ):function(fc::move(u.func)){} - - using function::operator=; -}; - -template -class function : public function { - public: - function(){} - template - function( U&& u ):function( fc::forward(u) ){} - function( const function& c ):function(c.func){} - using function::operator=; -}; - -template -class function : public function { - public: - function(){} - template - function( U&& u ):function( fc::forward(u) ){} - function( const function& c ):function(c.func){} - using function::operator=; -}; - -} -#endif - diff --git a/include/fc/fwd_impl.hpp b/include/fc/fwd_impl.hpp index c5862cead..639085abe 100644 --- a/include/fc/fwd_impl.hpp +++ b/include/fc/fwd_impl.hpp @@ -1,5 +1,4 @@ -#ifndef _FC_FWD_IMPL_HPP_ -#define _FC_FWD_IMPL_HPP_ +#pragma once #include #include @@ -134,4 +133,3 @@ namespace fc { } // namespace fc -#endif //_FC_FWD_IMPL_HPP_ diff --git a/include/fc/fwd_reflect.hpp b/include/fc/fwd_reflect.hpp deleted file mode 100644 index 62b5bfe16..000000000 --- a/include/fc/fwd_reflect.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _FC_FWD_REFLECT_HPP_ -#define _FC_FWD_REFLECT_HPP_ -#include -#include - -namespace fc { - template - class reflector> : public detail::reflector_impl, reflector> >{ - public: - virtual const char* name()const { return instance().name(); } - virtual void visit( void* s, const abstract_visitor& v )const { - instance().visit(s,v); - } - virtual void visit( const void* s, const abstract_const_visitor& v )const { - instance().visit(s,v); - } - - static reflector& instance() { return reflector::instance(); } - }; -} // namespace fc -#endif //_FC_FWD_REFLECT_HPP_ diff --git a/include/fc/git_revision.hpp b/include/fc/git_revision.hpp new file mode 100644 index 000000000..6232f3c3c --- /dev/null +++ b/include/fc/git_revision.hpp @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace fc { + +extern const char* const git_revision_sha; +extern const uint32_t git_revision_unix_timestamp; + +} // end namespace fc diff --git a/include/fc/http/connection.hpp b/include/fc/http/connection.hpp deleted file mode 100644 index 0f71530b6..000000000 --- a/include/fc/http/connection.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - namespace ip { class endpoint; } - class tcp_socket; - - namespace http { - - struct header { - header( fc::string k, fc::string v ) - :key(fc::move(k)),val(fc::move(v)){} - header(){} - fc::string key; - fc::string val; - }; - - struct reply { - enum status_code { - OK = 200, - RecordCreated = 201, - NotFound = 404, - Found = 302, - InternalServerError = 500 - }; - reply( status_code c = OK):status(c){} - int status; - fc::vector
headers; - fc::vector body; - }; - - struct request { - fc::string get_header( const fc::string& key )const; - fc::string method; - fc::string domain; - fc::string path; - fc::vector
headers; - fc::vector body; - }; - - fc::vector
parse_urlencoded_params( const fc::string& f ); - - /** - * Connections have reference semantics, all copies refer to the same - * underlying socket. - */ - class connection { - public: - // used for clients - void connect_to( const fc::ip::endpoint& ep ); - http::reply request( const fc::string& method, const fc::string& url, const fc::string& body ); - - // used for servers - fc::tcp_socket& get_socket()const; - - http::request read_request()const; - - FC_REFERENCE_TYPE(connection) - }; - -} } // fc::http - diff --git a/include/fc/interprocess/file_mapping.hpp b/include/fc/interprocess/file_mapping.hpp index 18f9e0134..e6b681740 100644 --- a/include/fc/interprocess/file_mapping.hpp +++ b/include/fc/interprocess/file_mapping.hpp @@ -21,11 +21,16 @@ namespace fc { ~file_mapping(); private: friend class mapped_region; + #ifdef _WIN64 + fc::fwd my; + #else fc::fwd my; + #endif }; + class mapped_region { public: - mapped_region( const file_mapping& fm, mode_t m, size_t start, size_t size ); + mapped_region( const file_mapping& fm, mode_t m, uint64_t start, size_t size ); mapped_region( const file_mapping& fm, mode_t m ); ~mapped_region(); void flush(); diff --git a/include/fc/interprocess/iprocess.hpp b/include/fc/interprocess/iprocess.hpp new file mode 100644 index 000000000..c7f8c4e2a --- /dev/null +++ b/include/fc/interprocess/iprocess.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc +{ + /** + * @brief abstract interface for interacting with external processes + * + * At the very least we have ssh::process and direct child processes, and + * there may be other processes that need to implement this protocol. + */ + class iprocess + { + public: + enum exec_opts { + open_none = 0, + open_stdin = 0x01, + open_stdout = 0x02, + open_stderr = 0x04, + open_all = open_stdin|open_stdout|open_stderr, + suppress_console = 0x08 + }; + + virtual ~iprocess(){} + + /** + * + * @return *this + */ + virtual iprocess& exec( const path& exe, std::vector args, + const path& work_dir = path(), int opts = open_all ) = 0; + + /** + * @return blocks until the process exits + */ + virtual int result(const microseconds& timeout = microseconds::maximum()) = 0; + + + /** + * Forcefully kills the process. + */ + virtual void kill() = 0; + + /** + * @brief returns a stream that writes to the process' stdin + */ + virtual buffered_ostream_ptr in_stream() = 0; + + /** + * @brief returns a stream that reads from the process' stdout + */ + virtual buffered_istream_ptr out_stream() = 0; + /** + * @brief returns a stream that reads from the process' stderr + */ + virtual buffered_istream_ptr err_stream() = 0; + + }; + + typedef std::shared_ptr iprocess_ptr; + + +} // namespace fc diff --git a/include/fc/interprocess/mmap_struct.hpp b/include/fc/interprocess/mmap_struct.hpp new file mode 100644 index 000000000..0913deb76 --- /dev/null +++ b/include/fc/interprocess/mmap_struct.hpp @@ -0,0 +1,59 @@ +#pragma once +#include +#include + +namespace fc +{ + class path; + namespace detail + { + /** + * Base class used to hide common implementation details. + */ + class mmap_struct_base + { + public: + size_t size()const; + void flush(); + + protected: + void open( const fc::path& file, size_t s, bool create ); + std::unique_ptr _file_mapping; + std::unique_ptr _mapped_region; + }; + }; + + /** + * @class mmap_struct + * @brief A struct that has been mapped from a file. + * + * @note T must be POD + */ + template + class mmap_struct : public detail::mmap_struct_base + { + public: + mmap_struct():_mapped_struct(nullptr){} + /** + * Create the file if it does not exist or is of the wrong size if create is true, then maps + * the file to memory. + * + * @throw an exception if the file does not exist or is the wrong size and create is false + */ + void open( const fc::path& file, bool create = false ) + { + detail::mmap_struct_base::open( file, sizeof(T), create ); + _mapped_struct = (T*)_mapped_region->get_address(); + } + + T* operator->() { return _mapped_struct; } + const T* operator->()const { return _mapped_struct; } + + T& operator*() { return *_mapped_struct; } + const T& operator*()const { return *_mapped_struct; } + + private: + T* _mapped_struct; + }; + +} diff --git a/include/fc/interprocess/process.hpp b/include/fc/interprocess/process.hpp new file mode 100644 index 000000000..777c154a7 --- /dev/null +++ b/include/fc/interprocess/process.hpp @@ -0,0 +1,37 @@ +#pragma once +#include + +namespace fc { + + fc::path find_executable_in_path( const fc::string name ); + + /** + * @brief start and manage an local process + * @note this class implements reference semantics. + */ + class process : public iprocess + { + public: + process(); + ~process(); + + virtual iprocess& exec( const fc::path& exe, + std::vector args, + const fc::path& work_dir = fc::path(), + int opts = open_all ); + + + virtual int result(const microseconds& timeout = microseconds::maximum()); + virtual void kill(); + virtual fc::buffered_ostream_ptr in_stream(); + virtual fc::buffered_istream_ptr out_stream(); + virtual fc::buffered_istream_ptr err_stream(); + + class impl; + private: + std::unique_ptr my; + }; + + typedef std::shared_ptr process_ptr; + +} // namespace fc diff --git a/include/fc/interprocess/signals.hpp b/include/fc/interprocess/signals.hpp new file mode 100644 index 000000000..876c68497 --- /dev/null +++ b/include/fc/interprocess/signals.hpp @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace fc +{ + /// handler will be called from ASIO thread + void set_signal_handler( std::function handler, int signal_num ); +} diff --git a/include/fc/invokeable.hpp b/include/fc/invokeable.hpp deleted file mode 100644 index ee7d9e489..000000000 --- a/include/fc/invokeable.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _FC_INVOKEABLE_HPP_ -#define _FC_INVOKEABLE_HPP_ - -namespace fc { - - class invokeable { - public: - virtual ~invokeable(){}; - - virtual void invoke( const promise::ptr& prom, const string& name, size_t num_params, reflect::cref* params ); - - void invoke( const std::string& name ) { invoke( promise::ptr(), name, 0, 0 ); } - }; - -} - -#endif // _FC_INVOKEABLE_HPP_ diff --git a/include/fc/io/buffered_iostream.hpp b/include/fc/io/buffered_iostream.hpp new file mode 100644 index 000000000..470b1fd11 --- /dev/null +++ b/include/fc/io/buffered_iostream.hpp @@ -0,0 +1,73 @@ +#pragma once +#include + +namespace fc +{ + namespace detail + { + class buffered_istream_impl; + class buffered_ostream_impl; + } + + /** + * @brief Reads data from an unbuffered stream + * and enables peek functionality. + */ + class buffered_istream : public virtual istream + { + public: + buffered_istream( istream_ptr is ); + buffered_istream( buffered_istream&& o ); + + buffered_istream& operator=( buffered_istream&& i ); + + virtual ~buffered_istream(); + + /** read at least 1 byte or throw, if no data is available + * this method should block cooperatively until data is + * available or fc::eof_exception is thrown. + * + * @pre len > 0 + * @pre buf != nullptr + * @throws fc::eof if at least 1 byte cannot be read + **/ + virtual std::size_t readsome( char* buf, std::size_t len ); + virtual size_t readsome( const std::shared_ptr& buf, size_t len, size_t offset ); + + /** + * This method may block until at least 1 character is + * available. + */ + virtual char peek() const; + + private: + std::unique_ptr my; + }; + typedef std::shared_ptr buffered_istream_ptr; + + + /** + * + */ + class buffered_ostream : public virtual ostream + { + public: + buffered_ostream( ostream_ptr o, size_t bufsize = 4096 ); + buffered_ostream( buffered_ostream&& m ); + ~buffered_ostream(); + + buffered_ostream& operator=( buffered_ostream&& m ); + /** + * This method will return immediately unless the buffer + * is full, in which case it will flush which may block. + */ + virtual size_t writesome( const char* buf, size_t len ); + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ); + + virtual void close(); + virtual void flush(); + private: + std::unique_ptr my; + }; + typedef std::shared_ptr buffered_ostream_ptr; +} diff --git a/include/fc/io/console.hpp b/include/fc/io/console.hpp new file mode 100644 index 000000000..11a9d47b9 --- /dev/null +++ b/include/fc/io/console.hpp @@ -0,0 +1,8 @@ +#pragma once +namespace fc +{ + /** enables / disables echoing of console input, useful for + * entering passwords on the console. + */ + void set_console_echo( bool enable_echo ); +} // namespace fc diff --git a/include/fc/io/datastream.hpp b/include/fc/io/datastream.hpp new file mode 100644 index 000000000..75a426976 --- /dev/null +++ b/include/fc/io/datastream.hpp @@ -0,0 +1,182 @@ +#pragma once +#include +#include +#include + +namespace fc { + +namespace detail +{ + NO_RETURN void throw_datastream_range_error( const char* file, size_t len, int64_t over ); +} + +/** + * The purpose of this datastream is to provide a fast, effecient, means + * of calculating the amount of data "about to be written" and then + * writing it. This means having two modes of operation, "test run" where + * you call the entire pack sequence calculating the size, and then + * actually packing it after doing a single allocation. + */ +template +class datastream { + public: + datastream( T start, size_t s ) + :_start(start),_pos(start),_end(start+s){}; + + + inline void skip( size_t s ){ _pos += s; } + inline bool read( char* d, size_t s ) { + if( size_t(_end - _pos) >= (size_t)s ) { + memcpy( d, _pos, s ); + _pos += s; + return true; + } + detail::throw_datastream_range_error( "read", _end-_start, int64_t(-((_end-_pos) - 1))); + } + + inline bool write( const char* d, size_t s ) { + if( _end - _pos >= (int32_t)s ) { + memcpy( _pos, d, s ); + _pos += s; + return true; + } + detail::throw_datastream_range_error( "write", _end-_start, int64_t(-((_end-_pos) - 1))); + } + + inline bool put(char c) { + if( _pos < _end ) { + *_pos = c; + ++_pos; + return true; + } + detail::throw_datastream_range_error( "put", _end-_start, int64_t(-((_end-_pos) - 1))); + } + + inline bool get( unsigned char& c ) { return get( *(char*)&c ); } + inline bool get( char& c ) + { + if( _pos < _end ) { + c = *_pos; + ++_pos; + return true; + } + detail::throw_datastream_range_error( "get", _end-_start, int64_t(-((_end-_pos) - 1))); + } + + T pos()const { return _pos; } + inline bool valid()const { return _pos <= _end && _pos >= _start; } + inline bool seekp(size_t p) { _pos = _start + p; return _pos <= _end; } + inline size_t tellp()const { return _pos - _start; } + inline size_t remaining()const { return _end - _pos; } + private: + T _start; + T _pos; + T _end; +}; + +template<> +class datastream { + public: + datastream( size_t init_size = 0):_size(init_size){}; + inline bool skip( size_t s ) { _size += s; return true; } + inline bool write( const char* ,size_t s ) { _size += s; return true; } + inline bool put(char ) { ++_size; return true; } + inline bool valid()const { return true; } + inline bool seekp(size_t p) { _size = p; return true; } + inline size_t tellp()const { return _size; } + inline size_t remaining()const { return 0; } + private: + size_t _size; +}; + +template +inline datastream& operator<<(datastream& ds, const int32_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator>>(datastream& ds, int32_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const uint32_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, uint32_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const int64_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, int64_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const uint64_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, uint64_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const int16_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, int16_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const uint16_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, uint16_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const int8_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, int8_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} +template +inline datastream& operator<<(datastream& ds, const uint8_t& d) { + ds.write( (const char*)&d, sizeof(d) ); + return ds; +} + +template +inline datastream& operator>>(datastream& ds, uint8_t& d) { + ds.read((char*)&d, sizeof(d) ); + return ds; +} + + +} // namespace fc + diff --git a/include/fc/datastream.hpp b/include/fc/io/datastream_back.hpp similarity index 53% rename from include/fc/datastream.hpp rename to include/fc/io/datastream_back.hpp index d4f965c9f..8c4e092df 100644 --- a/include/fc/datastream.hpp +++ b/include/fc/io/datastream_back.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include #include #include @@ -14,96 +13,78 @@ namespace fc { * actually packing it after doing a single allocation. */ template -struct datastream { +class datastream { + public: datastream( T start, size_t s ) - :m_start(start),m_pos(start),m_end(start+s){}; + :_start(start),_pos(start),_end(start+s){}; - inline void skip( size_t s ){ m_pos += s; } + inline void skip( size_t s ){ _pos += s; } inline bool read( char* d, size_t s ) { - if( size_t(m_end - m_pos) >= (size_t)s ) { - memcpy( d, m_pos, s ); - m_pos += s; + if( size_t(_end - _pos) >= (size_t)s ) { + memcpy( d, _pos, s ); + _pos += s; return true; } - FC_THROW_REPORT( "Attempt to read ${bytes_past} bytes beyond end of buffer with size ${buffer_size} ", - fc::value("bytes_past",int64_t(-((m_end-m_pos) - s))) - ("buffer_size", int64_t(m_end-m_start)) ); + detail::throw_datastream_range_error( _end-start, int64_t(-((_end-_pos) - 1))) return false; } inline bool write( const char* d, size_t s ) { - if( m_end - m_pos >= (int32_t)s ) { - memcpy( m_pos, d, s ); - m_pos += s; + if( _end - _pos >= (int32_t)s ) { + memcpy( _pos, d, s ); + _pos += s; return true; } - FC_THROW_REPORT( "Attempt to write ${bytes_past} bytes beyond end of buffer with size ${buffer_size} ", - fc::value("bytes_past",int64_t(-((m_end-m_pos) - s))) - ("buffer_size", int64_t(m_end-m_start)) ); + detail::throw_datastream_range_error( _end-start, int64_t(-((_end-_pos) - 1))) return false; } inline bool put(char c) { - if( m_pos < m_end ) { - *m_pos = c; - ++m_pos; + if( _pos < _end ) { + *_pos = c; + ++_pos; return true; } - FC_THROW_REPORT( "Attempt to write ${bytes_past} bytes beyond end of buffer with size ${buffer_size} ", - fc::value("bytes_past",int64_t(-((m_end-m_pos) - 1))) - ("buffer_size", int64_t(m_end-m_start)) ); + detail::throw_datastream_range_error( _end-start, int64_t(-((_end-_pos) - 1))) } inline bool get( unsigned char& c ) { return get( *(char*)&c ); } inline bool get( char& c ) { - if( m_pos < m_end ) { - c = *m_pos; - ++m_pos; + if( _pos < _end ) { + c = *_pos; + ++_pos; return true; } - FC_THROW_REPORT( "Attempt to read ${bytes_past} bytes beyond end of buffer of size ${buffer_size} ", - fc::value("bytes_past",int64_t(-((m_end-m_pos) - 1))) - ("buffer_size", int64_t(m_end-m_start)) ); + detail::throw_datastream_range_error( _end-start, int64_t(-((_end-_pos) - 1))) } - T pos()const { return m_pos; } - inline bool valid()const { return m_pos <= m_end && m_pos >= m_start; } - inline bool seekp(size_t p) { m_pos = m_start + p; return m_pos <= m_end; } - inline size_t tellp()const { return m_pos - m_start; } - inline size_t remaining()const { return m_end - m_pos; } + T pos()const { return _pos; } + inline bool valid()const { return _pos <= _end && _pos >= _start; } + inline bool seekp(size_t p) { _pos = _start + p; return _pos <= _end; } + inline size_t tellp()const { return _pos - _start; } + inline size_t remaining()const { return _end - _pos; } private: - T m_start; - T m_pos; - T m_end; + T _start; + T _pos; + T _end; }; template<> -struct datastream { - datastream( size_t init_size = 0):m_size(init_size){}; - inline bool skip( size_t s ) { m_size += s; return true; } - inline bool write( const char* d,size_t s ) { m_size += s; return true; } - inline bool put(char c) { ++m_size; return true; } - inline bool valid()const { return true; } - inline bool seekp(size_t p) { m_size = p; return true; } - inline size_t tellp()const { return m_size; } - inline size_t remaining()const { return 0; } -private: - size_t m_size; +class datastream { + public: + datastream( size_t init_size = 0):_size(init_size){}; + inline bool skip( size_t s ) { _size += s; return true; } + inline bool write( const char* d,size_t s ) { _size += s; return true; } + inline bool put(char c) { ++_size; return true; } + inline bool valid()const { return true; } + inline bool seekp(size_t p) { _size = p; return true; } + inline size_t tellp()const { return _size; } + inline size_t remaining()const { return 0; } + private: + size_t _size; }; -/* -template -inline datastream& operator<<(datastream& ds, const size_t& d) { - ds.write( (const char*)&d, sizeof(d) ); - return *this; -} -template -inline datastream& operator>>(datastream& ds, size_t& d) { - ds.read((char*)&d, sizeof(d) ); - return *this; -} -*/ template inline datastream& operator<<(datastream& ds, const int32_t& d) { ds.write( (const char*)&d, sizeof(d) ); diff --git a/include/fc/io/enum_type.hpp b/include/fc/io/enum_type.hpp new file mode 100644 index 000000000..e41770ced --- /dev/null +++ b/include/fc/io/enum_type.hpp @@ -0,0 +1,82 @@ +#pragma once +#include +#include +#include + +namespace fc +{ + template + class enum_type + { + public: + enum_type( EnumType t ) + :value(t){} + + enum_type( IntType t ) + :value( (EnumType)t ){} + + enum_type(){} + + explicit operator IntType()const { return static_cast(value); } + operator EnumType()const { return value; } + operator std::string()const { return fc::reflector::to_string(value); } + + enum_type& operator=( IntType i ) { value = (EnumType)i; return *this;} + enum_type& operator=( EnumType i ) { value = i; return *this;} + bool operator<( EnumType i ) const { return value < i; } + bool operator>( EnumType i ) const { return value < i; } + + bool operator<(const enum_type& e) const { return value < e.value;} + bool operator>(const enum_type& e) const { return value > e.value;} + + bool operator<=(const enum_type& e) const { return value <= e.value;} + bool operator>=(const enum_type& e) const { return value >= e.value;} + + friend bool operator==( const enum_type& e, IntType i ){ return e.value == (EnumType)i;} + friend bool operator==( const enum_type& e, EnumType i ){ return e.value == i; } + + friend bool operator==( const enum_type& e, const enum_type& i ){ return e.value == i.value; } + friend bool operator==( IntType i, const enum_type& e){ return e.value == (EnumType)i; } + friend bool operator==( EnumType i, const enum_type& e ){ return e.value == i; } + + friend bool operator!=( const enum_type& e, IntType i ){ return e.value != (EnumType)i;} + friend bool operator!=( const enum_type& e, EnumType i ){ return e.value != i; } + friend bool operator!=( const enum_type& e, const enum_type& i ){ return e.value != i.value; } + + EnumType value; + }; + + + template + void to_variant( const enum_type& var, variant& vo ) + { + vo = (EnumType)var.value; + } + template + void from_variant( const variant& var, enum_type& vo ) + { + vo.value = var.as(); + } + + + /** serializes like an IntType */ + namespace raw + { + template + inline void pack( Stream& s, const fc::enum_type& tp ) + { + fc::raw::pack( s, static_cast(tp) ); + } + + template + inline void unpack( Stream& s, fc::enum_type& tp ) + { + IntType t; + fc::raw::unpack( s, t ); + tp = t; + } + } + +} + + diff --git a/include/fc/fstream.hpp b/include/fc/io/fstream.hpp similarity index 61% rename from include/fc/fstream.hpp rename to include/fc/io/fstream.hpp index fd318628c..8344ce98e 100644 --- a/include/fc/fstream.hpp +++ b/include/fc/io/fstream.hpp @@ -1,7 +1,7 @@ -#pragma once +#pragma once #include #include -#include +#include namespace fc { class path; @@ -13,7 +13,8 @@ namespace fc { ~ofstream(); void open( const fc::path& file, int m = binary ); - ofstream& write( const char* buf, size_t len ); + size_t writesome( const char* buf, size_t len ); + size_t writesome(const std::shared_ptr& buffer, size_t len, size_t offset); void put( char c ); void close(); void flush(); @@ -29,13 +30,15 @@ namespace fc { enum seekdir { beg, cur, end }; ifstream(); - ifstream( const fc::path& file, int m ); + ifstream( const fc::path& file, int m = binary); ~ifstream(); void open( const fc::path& file, int m ); size_t readsome( char* buf, size_t len ); + size_t readsome(const std::shared_ptr& buffer, size_t max, size_t offset); ifstream& read( char* buf, size_t len ); ifstream& seekg( size_t p, seekdir d = beg ); + using istream::get; void get( char& c ) { read( &c, 1 ); } void close(); bool eof()const; @@ -43,5 +46,12 @@ namespace fc { class impl; fc::shared_ptr my; }; - -} // namespace fc + + /** + * Grab the full contents of a file into a string object. + * NB reading a full file into memory is a poor choice + * if the file may be very large. + */ + void read_file_contents( const fc::path& filename, std::string& result ); + +} // namespace fc diff --git a/include/fc/io/iobuffer.hpp b/include/fc/io/iobuffer.hpp new file mode 100644 index 000000000..02f919b06 --- /dev/null +++ b/include/fc/io/iobuffer.hpp @@ -0,0 +1,81 @@ +#pragma once +#include +#include + +namespace fc +{ + /** + * Records the size, but discards the data. + */ + class size_stream : public virtual fc::ostream + { + public: + size_stream( size_t s = 0):_size(s){} + + size_t size()const { return _size; } + size_t seek( size_t pos ) { return _size = pos; } + + virtual size_t writesome( const char* /*ignored buf*/, size_t len ) + { + _size += len; + return len; + } + + virtual void close(){} + virtual void flush(){} + + private: + size_t _size; + }; + + + class iobuffer : public virtual fc::iostream + { + public: + iobuffer( size_t s ) + :_data(s){} + + size_t size()const { return _data.size(); } + size_t pos()const { return _pos; } + size_t seek( size_t pos ) + { + return _pos = std::min(_data.size(),pos); + } + + virtual size_t readsome( char* buf, size_t len ) + { + auto avail = std::min( _data.size()-_pos, len ); + if( avail == 0 ) throw fc::eof_exception(); + memcpy( buf, _data.data()+_pos, avail ); + _pos += avail; + return avail; + } + /** + * This method may block until at least 1 character is + * available. + */ + char peek()const + { + if( _pos == _data.size() ) throw fc::eof_exception(); + return _data[_pos]; + } + + virtual size_t writesome( const char* buf, size_t len ) + { + auto avail = std::max( _data.size(), _pos + len ); + _data.resize(avail); + memcpy( _data.data()+_pos, buf, len ); + _pos += avail; + return avail; + } + char* data() { return _data.data(); } + + virtual void close(){} + virtual void flush(){} + + private: + std::vector _data; + size_t _pos; + }; + +} diff --git a/include/fc/io/iostream.hpp b/include/fc/io/iostream.hpp new file mode 100644 index 000000000..1ec632f01 --- /dev/null +++ b/include/fc/io/iostream.hpp @@ -0,0 +1,103 @@ +#pragma once +#include +#include +#include + +namespace fc { + + /** + * Provides a fc::thread friendly cooperatively multi-tasked stream that + * will block 'cooperatively' instead of hard blocking. + */ + class istream + { + public: + virtual ~istream(){}; + + /** read at least 1 byte or throw, if no data is available + * this method should block cooperatively until data is + * available or fc::eof is thrown. + * + * @throws fc::eof if at least 1 byte cannot be read + **/ + virtual size_t readsome( char* buf, size_t len ) = 0; + virtual size_t readsome( const std::shared_ptr& buf, size_t len, size_t offset ) = 0; + + /** read len bytes or throw, this method is implemented + * in terms of readsome. + * + * @throws fc::eof_exception if len bytes cannot be read + **/ + istream& read( char* buf, size_t len ); + istream& read( const std::shared_ptr& buf, size_t len, size_t offset = 0 ); + virtual char get(); + void get( char& c ) { c = get(); } + }; + typedef std::shared_ptr istream_ptr; + + /** + * Provides a fc::thread friendly cooperatively multi-tasked stream that + * will block 'cooperatively' instead of hard blocking. + */ + class ostream + { + public: + virtual ~ostream(){}; + virtual size_t writesome( const char* buf, size_t len ) = 0; + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ) = 0; + virtual void close() = 0; + virtual void flush() = 0; + + void put( char c ) { write(&c,1); } + + /** implemented in terms of writesome, guarantees len bytes are sent + * but not flushed. + **/ + ostream& write( const char* buf, size_t len ); + ostream& write( const std::shared_ptr& buf, size_t len, size_t offset = 0 ); + }; + + typedef std::shared_ptr ostream_ptr; + + class iostream : public virtual ostream, public virtual istream {}; + + fc::istream& getline( fc::istream&, fc::string&, char delim = '\n' ); + + template + ostream& operator<<( ostream& o, char (&array)[N] ) + { + return o.write( array, N ); + } + + ostream& operator<<( ostream& o, char ); + ostream& operator<<( ostream& o, const char* v ); + ostream& operator<<( ostream& o, const std::string& v ); + ostream& operator<<( ostream& o, const fc::string& v ); + ostream& operator<<( ostream& o, const double& v ); + ostream& operator<<( ostream& o, const float& v ); + ostream& operator<<( ostream& o, const int64_t& v ); + ostream& operator<<( ostream& o, const uint64_t& v ); + ostream& operator<<( ostream& o, const int32_t& v ); + ostream& operator<<( ostream& o, const uint32_t& v ); + ostream& operator<<( ostream& o, const int16_t& v ); + ostream& operator<<( ostream& o, const uint16_t& v ); + ostream& operator<<( ostream& o, const int8_t& v ); + ostream& operator<<( ostream& o, const uint8_t& v ); +#ifndef _MSC_VER + ostream& operator<<( ostream& o, const size_t& v ); +#endif + + istream& operator>>( istream& o, std::string& v ); + istream& operator>>( istream& o, fc::string& v ); + istream& operator>>( istream& o, char& v ); + istream& operator>>( istream& o, double& v ); + istream& operator>>( istream& o, float& v ); + istream& operator>>( istream& o, int64_t& v ); + istream& operator>>( istream& o, uint64_t& v ); + istream& operator>>( istream& o, int32_t& v ); + istream& operator>>( istream& o, uint32_t& v ); + istream& operator>>( istream& o, int16_t& v ); + istream& operator>>( istream& o, uint16_t& v ); + istream& operator>>( istream& o, int8_t& v ); + istream& operator>>( istream& o, uint8_t& v ); +} diff --git a/include/fc/io/json.hpp b/include/fc/io/json.hpp new file mode 100644 index 000000000..8a46d134f --- /dev/null +++ b/include/fc/io/json.hpp @@ -0,0 +1,79 @@ +#pragma once +#include +#include + +namespace fc +{ + class ostream; + class buffered_istream; + + /** + * Provides interface for json serialization. + * + * json strings are always UTF8 + */ + class json + { + public: + enum parse_type + { + legacy_parser = 0, + strict_parser = 1, + relaxed_parser = 2, + legacy_parser_with_string_doubles = 3 + }; + enum output_formatting + { + stringify_large_ints_and_doubles = 0, + legacy_generator = 1 + }; + + static ostream& to_stream( ostream& out, const fc::string&); + static ostream& to_stream( ostream& out, const variant& v, output_formatting format = stringify_large_ints_and_doubles ); + static ostream& to_stream( ostream& out, const variants& v, output_formatting format = stringify_large_ints_and_doubles ); + static ostream& to_stream( ostream& out, const variant_object& v, output_formatting format = stringify_large_ints_and_doubles ); + + static variant from_stream( buffered_istream& in, parse_type ptype = legacy_parser ); + + static variant from_string( const string& utf8_str, parse_type ptype = legacy_parser ); + static variants variants_from_string( const string& utf8_str, parse_type ptype = legacy_parser ); + static string to_string( const variant& v, output_formatting format = stringify_large_ints_and_doubles ); + static string to_pretty_string( const variant& v, output_formatting format = stringify_large_ints_and_doubles ); + + static bool is_valid( const std::string& json_str, parse_type ptype = legacy_parser ); + + template + static void save_to_file( const T& v, const fc::path& fi, bool pretty = true, output_formatting format = stringify_large_ints_and_doubles ) + { + save_to_file( variant(v), fi, pretty, format ); + } + + static void save_to_file( const variant& v, const fc::path& fi, bool pretty = true, output_formatting format = stringify_large_ints_and_doubles ); + static variant from_file( const fc::path& p, parse_type ptype = legacy_parser ); + + template + static T from_file( const fc::path& p, parse_type ptype = legacy_parser ) + { + return json::from_file(p, ptype).as(); + } + + template + static string to_string( const T& v, output_formatting format = stringify_large_ints_and_doubles ) + { + return to_string( variant(v), format ); + } + + template + static string to_pretty_string( const T& v, output_formatting format = stringify_large_ints_and_doubles ) + { + return to_pretty_string( variant(v), format ); + } + + template + static void save_to_file( const T& v, const std::string& p, bool pretty = true, output_formatting format = stringify_large_ints_and_doubles ) + { + save_to_file( variant(v), fc::path(p), pretty ); + } + }; + +} // fc diff --git a/include/fc/io/json_relaxed.hpp b/include/fc/io/json_relaxed.hpp new file mode 100644 index 000000000..a3793a458 --- /dev/null +++ b/include/fc/io/json_relaxed.hpp @@ -0,0 +1,744 @@ +#pragma once + +// This file is an internal header, +// it is not meant to be included except internally from json.cpp in fc + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include + +namespace fc { namespace json_relaxed +{ + template + variant variant_from_stream( T& in ); + + template + fc::string tokenFromStream( T& in ) + { + fc::stringstream token; + try + { + char c = in.peek(); + + while( true ) + { + switch( c = in.peek() ) + { + case '\\': + token << parseEscape( in ); + break; + case '\t': + case ' ': + case ',': + case ':': + case '\0': + case '\n': + case '\x04': + in.get(); + return token.str(); + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + case '8': case '9': + case '_': case '-': case '.': case '+': case '/': + token << c; + in.get(); + break; + default: + return token.str(); + } + } + return token.str(); + } + catch( const fc::eof_exception& eof ) + { + return token.str(); + } + catch (const std::ios_base::failure&) + { + return token.str(); + } + + FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'", + ("token", token.str() ) ); + } + + template + fc::string quoteStringFromStream( T& in ) + { + fc::stringstream token; + try + { + char q = in.get(); + switch( q ) + { + case '\'': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected: '\"' at beginning of string, got '\''" ); + // falls through + case '"': + break; + default: + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected: '\"' at beginning of string" ); + else + FC_THROW_EXCEPTION( parse_error_exception, "expected: '\"' | '\\\'' at beginning of string" ); + } + if( in.peek() == q ) + { + in.get(); + if( in.peek() != q ) + return fc::string(); + + // triple quote processing + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "triple quote unsupported in strict mode" ); + else + { + in.get(); + + while( true ) + { + char c = in.peek(); + if( c == q ) + { + in.get(); + char c2 = in.peek(); + if( c2 == q ) + { + char c3 = in.peek(); + if( c3 == q ) + { + in.get(); + return token.str(); + } + token << q << q; + continue; + } + token << q; + continue; + } + else if( c == '\x04' ) + FC_THROW_EXCEPTION( parse_error_exception, "unexpected EOF in string '${token}'", + ("token", token.str() ) ); + else if( allow_escape && (c == '\\') ) + token << parseEscape( in ); + else + { + in.get(); + token << c; + } + } + } + } + + while( true ) + { + char c = in.peek(); + + if( c == q ) + { + in.get(); + return token.str(); + } + else if( c == '\x04' ) + FC_THROW_EXCEPTION( parse_error_exception, "unexpected EOF in string '${token}'", + ("token", token.str() ) ); + else if( allow_escape && (c == '\\') ) + token << parseEscape( in ); + else if( (c == '\r') | (c == '\n') ) + FC_THROW_EXCEPTION( parse_error_exception, "unexpected EOL in string '${token}'", + ("token", token.str() ) ); + else + { + in.get(); + token << c; + } + } + + } FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'", + ("token", token.str() ) ); + } + + template + fc::string stringFromStream( T& in ) + { + try + { + char c = in.peek(), c2; + + switch( c ) + { + case '\'': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected: '\"' at beginning of string, got '\''" ); + // falls through + case '"': + return quoteStringFromStream( in ); + case 'r': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "raw strings not supported in strict mode" ); + case 'R': + in.get(); + c2 = in.peek(); + switch( c2 ) + { + case '"': + case '\'': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "raw strings not supported in strict mode" ); + return quoteStringFromStream( in ); + default: + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "unquoted strings not supported in strict mode" ); + return c+tokenFromStream( in ); + } + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + case '8': case '9': + case '_': case '-': case '.': case '+': case '/': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "unquoted strings not supported in strict mode" ); + return tokenFromStream( in ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "expected: string" ); + } + + } FC_RETHROW_EXCEPTIONS( warn, "while parsing string" ); + } + + struct CharValueTable + { + public: + CharValueTable() + { + for( size_t i=0; i<0x100; i++ ) + c2v[i] = 0xFF; + c2v[(unsigned char)'0'] = 0; + c2v[(unsigned char)'1'] = 1; + c2v[(unsigned char)'2'] = 2; + c2v[(unsigned char)'3'] = 3; + c2v[(unsigned char)'4'] = 4; + c2v[(unsigned char)'5'] = 5; + c2v[(unsigned char)'6'] = 6; + c2v[(unsigned char)'7'] = 7; + c2v[(unsigned char)'8'] = 8; + c2v[(unsigned char)'9'] = 9; + c2v[(unsigned char)'a'] = c2v[(unsigned char)'A'] = 10; + c2v[(unsigned char)'b'] = c2v[(unsigned char)'B'] = 11; + c2v[(unsigned char)'c'] = c2v[(unsigned char)'C'] = 12; + c2v[(unsigned char)'d'] = c2v[(unsigned char)'D'] = 13; + c2v[(unsigned char)'e'] = c2v[(unsigned char)'E'] = 14; + c2v[(unsigned char)'f'] = c2v[(unsigned char)'F'] = 15; + c2v[(unsigned char)'g'] = c2v[(unsigned char)'G'] = 16; + c2v[(unsigned char)'h'] = c2v[(unsigned char)'H'] = 17; + c2v[(unsigned char)'i'] = c2v[(unsigned char)'I'] = 18; + c2v[(unsigned char)'j'] = c2v[(unsigned char)'J'] = 19; + c2v[(unsigned char)'k'] = c2v[(unsigned char)'K'] = 20; + c2v[(unsigned char)'l'] = c2v[(unsigned char)'L'] = 21; + c2v[(unsigned char)'m'] = c2v[(unsigned char)'M'] = 22; + c2v[(unsigned char)'n'] = c2v[(unsigned char)'N'] = 23; + c2v[(unsigned char)'o'] = c2v[(unsigned char)'O'] = 24; + c2v[(unsigned char)'p'] = c2v[(unsigned char)'P'] = 25; + c2v[(unsigned char)'q'] = c2v[(unsigned char)'Q'] = 26; + c2v[(unsigned char)'r'] = c2v[(unsigned char)'R'] = 27; + c2v[(unsigned char)'s'] = c2v[(unsigned char)'S'] = 28; + c2v[(unsigned char)'t'] = c2v[(unsigned char)'T'] = 29; + c2v[(unsigned char)'u'] = c2v[(unsigned char)'U'] = 30; + c2v[(unsigned char)'v'] = c2v[(unsigned char)'V'] = 31; + c2v[(unsigned char)'w'] = c2v[(unsigned char)'W'] = 32; + c2v[(unsigned char)'x'] = c2v[(unsigned char)'X'] = 33; + c2v[(unsigned char)'y'] = c2v[(unsigned char)'Y'] = 34; + c2v[(unsigned char)'z'] = c2v[(unsigned char)'Z'] = 35; + return; + } + + uint8_t operator[]( char index ) const { return c2v[index & 0xFF]; } + + uint8_t c2v[0x100]; + }; + + template + fc::variant parseInt( const fc::string& token, size_t start ) + { + static const CharValueTable ctbl; + static const uint64_t INT64_MAX_PLUS_ONE = static_cast(INT64_MAX) + 1; + + size_t i = start, n = token.length(); + if( i >= n ) + FC_THROW_EXCEPTION( parse_error_exception, "zero-length integer" ); + + uint64_t val = 0; + uint64_t maxb4mul = UINT64_MAX / base; + + while(true) + { + char c = token[i]; + uint8_t vc = ctbl[c]; + if( vc == 0xFF ) + FC_THROW_EXCEPTION( parse_error_exception, "illegal character {c} in integer of base {b}", ("c", c)("b", base) ); + if( val > maxb4mul ) + FC_THROW_EXCEPTION( parse_error_exception, "integer literal overflow" ); + val *= base; + uint64_t newval = val + vc; + if( newval < val ) + FC_THROW_EXCEPTION( parse_error_exception, "integer literal overflow" ); + val = newval; + i++; + if( i >= n ) + break; + } + if( token[0] == '-' ) + { + if( val > INT64_MAX_PLUS_ONE ) + FC_THROW_EXCEPTION( parse_error_exception, "negative integer literal overflow" ); + // special cased to avoid trying to compute -INT64_MIN which is probably undefined or something + if( val == INT64_MAX_PLUS_ONE ) + return fc::variant( INT64_MIN ); + return fc::variant( -static_cast(val) ); + } + return fc::variant( val ); + } + + template + fc::variant maybeParseInt( const fc::string& token, size_t start ) + { + try + { + return parseInt( token, start ); + } + catch( const parse_error_exception &e ) + { + if( strict ) + throw( e ); + else + return fc::variant( token ); + } + } + + template + fc::variant parseNumberOrStr( const fc::string& token ) + { try { + //ilog( (token) ); + size_t i = 0, n = token.length(); + if( n == 0 ) + FC_THROW_EXCEPTION( parse_error_exception, "expected: non-empty token, got: empty token" ); + switch( token[0] ) + { + case '+': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "unary + not supported in strict mode" ); + i++; + break; + case '-': + i++; + break; + default: + break; + } + char c = token[i++]; + switch( c ) + { + case '0': + if( i >= n ) + return fc::variant( uint64_t( 0 ) ); + switch( token[i] ) + { + case 'b': + case 'B': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "binary numeric literals not supported in strict mode" ); + i++; + if( i >= n ) + FC_THROW_EXCEPTION( parse_error_exception, "empty binary numeric literal" ); + return maybeParseInt( token, i+1 ); + case 'o': + case 'O': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "octal numeric literals not supported in strict mode" ); + return maybeParseInt( token, i+1 ); + case 'x': + case 'X': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "hex numeric literals not supported in strict mode" ); + return maybeParseInt( token, i+1 ); + case '.': + case 'e': + case 'E': + break; + default: + // since this is a lookahead, other cases will be treated later + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected '.'|'e'|'E' parsing number, got '{c}'", + ( "c", c ) ); + } + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': case '-': case '.': case '+': case '/': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' parsing number", ( "c", c ) ); + return fc::variant( token ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in token", ( "c", c ) ); + } + size_t start = i-1; + + bool dot_ok = true; + + // int frac? exp? + while(true) + { + if( i >= n ) + return parseInt<10>( token, start ); + char c = token[i++]; + //idump((c)(std::string()+c)); + switch( c ) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + case '.': + return fc::variant(token); + if( dot_ok ) + { + dot_ok = false; + if( i == n ) + { + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "number cannot end with '.' in strict mode" ); + return fc::variant( fc::to_double(token.c_str()) ); + } + + //idump((i)); + c = token[i+1]; + //idump((c)); + switch( c ) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + case 'e': + case 'E': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected digit after '.'" ); + break; + case 'a': case 'b': case 'c': case 'd': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': case '-': case '.': case '+': case '/': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected digit after '.'" ); + return fc::variant( token ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in token", ( "c", c )("i",int(c)) ); + } + } + else + { + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "illegal multiple . in number" ); + return fc::variant( token ); + } + break; + case 'e': + case 'E': + if( i == n ) + { + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected exponent after 'e'|'E' parsing number" ); + return fc::variant( token ); + } + c = token[i++]; + switch( c ) + { + case '+': case '-': + if( i == n ) + { + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "expected exponent" ); + return fc::variant( token ); + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': case '.': case '/': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in number", ( "c", c ) ); + return fc::variant( token ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in token", ( "c", c ) ); + } + while( true ) + { + if( i == n ) + break; + c = token[i++]; + switch( c ) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': case '-': case '.': case '+': case '/': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in number", ( "c", c ) ); + return fc::variant( token ); + } + } + return fc::variant( fc::to_double(token.c_str()) ); + case 'a': case 'b': case 'c': case 'd': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': case '-': case '+': case '/': + if( strict ) + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' parsing number", ( "c", c ) ); + return fc::variant( token ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in number", ( "c", c ) ); + } + } + } FC_CAPTURE_AND_RETHROW( (token) ) } + + template + variant_object objectFromStream( T& in ) + { + mutable_variant_object obj; + try + { + char c = in.peek(); + if( c != '{' ) + FC_THROW_EXCEPTION( parse_error_exception, + "Expected '{', but read '${char}'", + ("char",string(&c, &c + 1)) ); + in.get(); + skip_white_space(in); + while( in.peek() != '}' ) + { + if( in.peek() == ',' ) + { + in.get(); + continue; + } + if( skip_white_space(in) ) continue; + string key = json_relaxed::stringFromStream( in ); + skip_white_space(in); + if( in.peek() != ':' ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Expected ':' after key \"${key}\"", + ("key", key) ); + } + in.get(); + auto val = json_relaxed::variant_from_stream( in ); + + obj(std::move(key),std::move(val)); + skip_white_space(in); + } + if( in.peek() == '}' ) + { + in.get(); + return obj; + } + FC_THROW_EXCEPTION( parse_error_exception, "Expected '}' after ${variant}", ("variant", obj ) ); + } + catch( const fc::eof_exception& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected EOF: ${e}", ("e", e.to_detail_string() ) ); + } + catch( const std::ios_base::failure& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected EOF: ${e}", ("e", e.what() ) ); + } FC_RETHROW_EXCEPTIONS( warn, "Error parsing object" ); + } + + template + variants arrayFromStream( T& in ) + { + variants ar; + try + { + if( in.peek() != '[' ) + FC_THROW_EXCEPTION( parse_error_exception, "Expected '['" ); + in.get(); + skip_white_space(in); + + while( in.peek() != ']' ) + { + if( in.peek() == ',' ) + { + in.get(); + continue; + } + if( skip_white_space(in) ) continue; + ar.push_back( json_relaxed::variant_from_stream(in) ); + skip_white_space(in); + } + if( in.peek() != ']' ) + FC_THROW_EXCEPTION( parse_error_exception, "Expected ']' after parsing ${variant}", + ("variant", ar) ); + + in.get(); + } FC_RETHROW_EXCEPTIONS( warn, "Attempting to parse array ${array}", + ("array", ar ) ); + return ar; + } + + template + variant numberFromStream( T& in ) + { try { + fc::string token = tokenFromStream(in); + variant result = json_relaxed::parseNumberOrStr( token ); + if( strict && !(result.is_int64() || result.is_uint64() || result.is_double()) ) + FC_THROW_EXCEPTION( parse_error_exception, "expected: number" ); + return result; + } FC_CAPTURE_AND_RETHROW() } + + template + variant wordFromStream( T& in ) + { + fc::string token = tokenFromStream(in); + + FC_ASSERT( token.length() > 0 ); + + switch( token[0] ) + { + case 'n': + if( token == "null" ) + return variant(); + break; + case 't': + if( token == "true" ) + return variant( true ); + break; + case 'f': + if( token == "false" ) + return variant( false ); + break; + default: + break; + } + + if( !strict ) + return token; + + FC_THROW_EXCEPTION( parse_error_exception, "expected: null|true|false" ); + } + + template + variant variant_from_stream( T& in ) + { + skip_white_space(in); + variant var; + while( signed char c = in.peek() ) + { + switch( c ) + { + case ' ': + case '\t': + case '\n': + case '\r': + in.get(); + continue; + case '"': + return json_relaxed::stringFromStream( in ); + case '{': + return json_relaxed::objectFromStream( in ); + case '[': + return json_relaxed::arrayFromStream( in ); + case '-': + case '+': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return json_relaxed::numberFromStream( in ); + // null, true, false, or 'warning' / string + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': case '/': + return json_relaxed::wordFromStream( in ); + case 0x04: // ^D end of transmission + case EOF: + FC_THROW_EXCEPTION( eof_exception, "unexpected end of file" ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected char '${c}' in \"${s}\"", + ("c", c)("s", stringFromToken(in)) ); + } + } + return variant(); + } + +} } // fc::json_relaxed diff --git a/include/fc/io/raw.hpp b/include/fc/io/raw.hpp new file mode 100644 index 000000000..bc5cd22b4 --- /dev/null +++ b/include/fc/io/raw.hpp @@ -0,0 +1,636 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc { + namespace raw { + template + inline void pack( Stream& s, const fc::exception& e ) + { + fc::raw::pack( s, e.code() ); + fc::raw::pack( s, std::string(e.name()) ); + fc::raw::pack( s, std::string(e.what()) ); + fc::raw::pack( s, e.get_log() ); + } + template + inline void unpack( Stream& s, fc::exception& e ) + { + int64_t code; + std::string name, what; + log_messages msgs; + + fc::raw::unpack( s, code ); + fc::raw::unpack( s, name ); + fc::raw::unpack( s, what ); + fc::raw::unpack( s, msgs ); + + e = fc::exception( fc::move(msgs), code, name, what ); + } + + template + inline void pack( Stream& s, const fc::log_message& msg ) + { + fc::raw::pack( s, variant(msg) ); + } + template + inline void unpack( Stream& s, fc::log_message& msg ) + { + fc::variant vmsg; + fc::raw::unpack( s, vmsg ); + msg = vmsg.as(); + } + + template + inline void pack( Stream& s, const fc::path& tp ) + { + fc::raw::pack( s, tp.generic_string() ); + } + + template + inline void unpack( Stream& s, fc::path& tp ) + { + std::string p; + fc::raw::unpack( s, p ); + tp = p; + } + + template + inline void pack( Stream& s, const fc::time_point_sec& tp ) + { + uint32_t usec = tp.sec_since_epoch(); + s.write( (const char*)&usec, sizeof(usec) ); + } + + template + inline void unpack( Stream& s, fc::time_point_sec& tp ) + { try { + uint32_t sec; + s.read( (char*)&sec, sizeof(sec) ); + tp = fc::time_point() + fc::seconds(sec); + } FC_RETHROW_EXCEPTIONS( warn, "" ) } + + template + inline void pack( Stream& s, const fc::time_point& tp ) + { + uint64_t usec = tp.time_since_epoch().count(); + s.write( (const char*)&usec, sizeof(usec) ); + } + + template + inline void unpack( Stream& s, fc::time_point& tp ) + { try { + uint64_t usec; + s.read( (char*)&usec, sizeof(usec) ); + tp = fc::time_point() + fc::microseconds(usec); + } FC_RETHROW_EXCEPTIONS( warn, "" ) } + + template + inline void pack( Stream& s, const fc::microseconds& usec ) + { + uint64_t usec_as_int64 = usec.count(); + s.write( (const char*)&usec_as_int64, sizeof(usec_as_int64) ); + } + + template + inline void unpack( Stream& s, fc::microseconds& usec ) + { try { + uint64_t usec_as_int64; + s.read( (char*)&usec_as_int64, sizeof(usec_as_int64) ); + usec = fc::microseconds(usec_as_int64); + } FC_RETHROW_EXCEPTIONS( warn, "" ) } + + template + inline void pack( Stream& s, const fc::array& v) { + s.write((const char*)&v.data[0],N*sizeof(T)); + } + + template + inline void pack( Stream& s, const std::shared_ptr& v) + { + fc::raw::pack( s, *v ); + } + + template + inline void unpack( Stream& s, fc::array& v) + { try { + s.read((char*)&v.data[0],N*sizeof(T)); + } FC_RETHROW_EXCEPTIONS( warn, "fc::array", ("type",fc::get_typename::name())("length",N) ) } + + template + inline void unpack( Stream& s, std::shared_ptr& v) + { try { + v = std::make_shared(); + fc::raw::unpack( s, *v ); + } FC_RETHROW_EXCEPTIONS( warn, "std::shared_ptr", ("type",fc::get_typename::name()) ) } + + template inline void pack( Stream& s, const signed_int& v ) { + uint32_t val = (v.value<<1) ^ (v.value>>31); + do { + uint8_t b = uint8_t(val) & 0x7f; + val >>= 7; + b |= ((val > 0) << 7); + s.write((char*)&b,1);//.put(b); + } while( val ); + } + + template inline void pack( Stream& s, const unsigned_int& v ) { + uint64_t val = v.value; + do { + uint8_t b = uint8_t(val) & 0x7f; + val >>= 7; + b |= ((val > 0) << 7); + s.write((char*)&b,1);//.put(b); + }while( val ); + } + + template inline void unpack( Stream& s, signed_int& vi ) { + uint32_t v = 0; char b = 0; int by = 0; + do { + s.get(b); + v |= uint32_t(uint8_t(b) & 0x7f) << by; + by += 7; + } while( uint8_t(b) & 0x80 ); + vi.value = ((v>>1) ^ (v>>31)) + (v&0x01); + vi.value = v&0x01 ? vi.value : -vi.value; + vi.value = -vi.value; + } + template inline void unpack( Stream& s, unsigned_int& vi ) { + uint64_t v = 0; char b = 0; uint8_t by = 0; + do { + s.get(b); + v |= uint32_t(uint8_t(b) & 0x7f) << by; + by += 7; + } while( uint8_t(b) & 0x80 ); + vi.value = static_cast(v); + } + + template inline void unpack( Stream& s, const T& vi ) + { + T tmp; + fc::raw::unpack( s, tmp ); + FC_ASSERT( vi == tmp ); + } + + template inline void pack( Stream& s, const char* v ) { fc::raw::pack( s, fc::string(v) ); } + + template + void pack( Stream& s, const safe& v ) { fc::raw::pack( s, v.value ); } + + template + void unpack( Stream& s, fc::safe& v ) { fc::raw::unpack( s, v.value ); } + + template + void pack( Stream& s, const fc::fwd& v ) { + fc::raw::pack( *v ); + } + + template + void unpack( Stream& s, fc::fwd& v ) { + fc::raw::unpack( *v ); + } + template + void pack( Stream& s, const fc::smart_ref& v ) { fc::raw::pack( s, *v ); } + + template + void unpack( Stream& s, fc::smart_ref& v ) { fc::raw::unpack( s, *v ); } + + // optional + template + void pack( Stream& s, const fc::optional& v ) { + fc::raw::pack( s, bool(!!v) ); + if( !!v ) fc::raw::pack( s, *v ); + } + + template + void unpack( Stream& s, fc::optional& v ) + { try { + bool b; fc::raw::unpack( s, b ); + if( b ) { v = T(); fc::raw::unpack( s, *v ); } + } FC_RETHROW_EXCEPTIONS( warn, "optional<${type}>", ("type",fc::get_typename::name() ) ) } + + // std::vector + template inline void pack( Stream& s, const std::vector& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + if( value.size() ) + s.write( &value.front(), (uint32_t)value.size() ); + } + template inline void unpack( Stream& s, std::vector& value ) { + unsigned_int size; fc::raw::unpack( s, size ); + FC_ASSERT( size.value < MAX_ARRAY_ALLOC_SIZE ); + value.resize(size.value); + if( value.size() ) + s.read( value.data(), value.size() ); + } + + // fc::string + template inline void pack( Stream& s, const fc::string& v ) { + fc::raw::pack( s, unsigned_int((uint32_t)v.size())); + if( v.size() ) s.write( v.c_str(), v.size() ); + } + + template inline void unpack( Stream& s, fc::string& v ) { + std::vector tmp; fc::raw::unpack(s,tmp); + if( tmp.size() ) + v = fc::string(tmp.data(),tmp.data()+tmp.size()); + else v = fc::string(); + } + + // bool + template inline void pack( Stream& s, const bool& v ) { fc::raw::pack( s, uint8_t(v) ); } + template inline void unpack( Stream& s, bool& v ) + { + uint8_t b; + fc::raw::unpack( s, b ); + FC_ASSERT( (b & ~1) == 0 ); + v=(b!=0); + } + + namespace detail { + + template + struct pack_object_visitor { + pack_object_visitor(const Class& _c, Stream& _s) + :c(_c),s(_s){} + + template + void operator()( const char* name )const { + fc::raw::pack( s, c.*p ); + } + private: + const Class& c; + Stream& s; + }; + + template + struct unpack_object_visitor { + unpack_object_visitor(Class& _c, Stream& _s) + :c(_c),s(_s){} + + template + inline void operator()( const char* name )const + { try { + fc::raw::unpack( s, c.*p ); + } FC_RETHROW_EXCEPTIONS( warn, "Error unpacking field ${field}", ("field",name) ) } + private: + Class& c; + Stream& s; + }; + + template + struct if_class{ + template + static inline void pack( Stream& s, const T& v ) { s << v; } + template + static inline void unpack( Stream& s, T& v ) { s >> v; } + }; + + template<> + struct if_class { + template + static inline void pack( Stream& s, const T& v ) { + s.write( (char*)&v, sizeof(v) ); + } + template + static inline void unpack( Stream& s, T& v ) { + s.read( (char*)&v, sizeof(v) ); + } + }; + + template + struct if_enum { + template + static inline void pack( Stream& s, const T& v ) { + fc::reflector::visit( pack_object_visitor( v, s ) ); + } + template + static inline void unpack( Stream& s, T& v ) { + fc::reflector::visit( unpack_object_visitor( v, s ) ); + } + }; + template<> + struct if_enum { + template + static inline void pack( Stream& s, const T& v ) { + fc::raw::pack(s, (int64_t)v); + } + template + static inline void unpack( Stream& s, T& v ) { + int64_t temp; + fc::raw::unpack(s, temp); + v = (T)temp; + } + }; + + template + struct if_reflected { + template + static inline void pack( Stream& s, const T& v ) { + if_class::type>::pack(s,v); + } + template + static inline void unpack( Stream& s, T& v ) { + if_class::type>::unpack(s,v); + } + }; + template<> + struct if_reflected { + template + static inline void pack( Stream& s, const T& v ) { + if_enum< typename fc::reflector::is_enum >::pack(s,v); + } + template + static inline void unpack( Stream& s, T& v ) { + if_enum< typename fc::reflector::is_enum >::unpack(s,v); + } + }; + + } // namesapce detail + + template + inline void pack( Stream& s, const std::unordered_set& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + template + inline void unpack( Stream& s, std::unordered_set& value ) { + unsigned_int size; fc::raw::unpack( s, size ); + value.clear(); + FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); + value.reserve(size.value); + for( uint32_t i = 0; i < size.value; ++i ) + { + T tmp; + fc::raw::unpack( s, tmp ); + value.insert( std::move(tmp) ); + } + } + + + template + inline void pack( Stream& s, const std::pair& value ) { + fc::raw::pack( s, value.first ); + fc::raw::pack( s, value.second ); + } + template + inline void unpack( Stream& s, std::pair& value ) + { + fc::raw::unpack( s, value.first ); + fc::raw::unpack( s, value.second ); + } + + template + inline void pack( Stream& s, const std::unordered_map& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + template + inline void unpack( Stream& s, std::unordered_map& value ) + { + unsigned_int size; fc::raw::unpack( s, size ); + value.clear(); + FC_ASSERT( size.value*(sizeof(K)+sizeof(V)) < MAX_ARRAY_ALLOC_SIZE ); + value.reserve(size.value); + for( uint32_t i = 0; i < size.value; ++i ) + { + std::pair tmp; + fc::raw::unpack( s, tmp ); + value.insert( std::move(tmp) ); + } + } + template + inline void pack( Stream& s, const std::map& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + template + inline void unpack( Stream& s, std::map& value ) + { + unsigned_int size; fc::raw::unpack( s, size ); + value.clear(); + FC_ASSERT( size.value*(sizeof(K)+sizeof(V)) < MAX_ARRAY_ALLOC_SIZE ); + for( uint32_t i = 0; i < size.value; ++i ) + { + std::pair tmp; + fc::raw::unpack( s, tmp ); + value.insert( std::move(tmp) ); + } + } + + template + inline void pack( Stream& s, const std::deque& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + + template + inline void unpack( Stream& s, std::deque& value ) { + unsigned_int size; fc::raw::unpack( s, size ); + FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); + value.resize(size.value); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::unpack( s, *itr ); + ++itr; + } + } + + template + inline void pack( Stream& s, const std::vector& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + + template + inline void unpack( Stream& s, std::vector& value ) { + unsigned_int size; fc::raw::unpack( s, size ); + FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); + value.resize(size.value); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::unpack( s, *itr ); + ++itr; + } + } + + template + inline void pack( Stream& s, const std::set& value ) { + fc::raw::pack( s, unsigned_int((uint32_t)value.size()) ); + auto itr = value.begin(); + auto end = value.end(); + while( itr != end ) { + fc::raw::pack( s, *itr ); + ++itr; + } + } + + template + inline void unpack( Stream& s, std::set& value ) { + unsigned_int size; fc::raw::unpack( s, size ); + for( uint64_t i = 0; i < size.value; ++i ) + { + T tmp; + fc::raw::unpack( s, tmp ); + value.insert( std::move(tmp) ); + } + } + + + + template + inline void pack( Stream& s, const T& v ) { + fc::raw::detail::if_reflected< typename fc::reflector::is_defined >::pack(s,v); + } + template + inline void unpack( Stream& s, T& v ) + { try { + fc::raw::detail::if_reflected< typename fc::reflector::is_defined >::unpack(s,v); + } FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type",fc::get_typename::name() ) ) } + + template + inline size_t pack_size( const T& v ) + { + datastream ps; + fc::raw::pack(ps,v ); + return ps.tellp(); + } + + template + inline std::vector pack( const T& v ) { + datastream ps; + fc::raw::pack(ps,v ); + std::vector vec(ps.tellp()); + + if( vec.size() ) { + datastream ds( vec.data(), size_t(vec.size()) ); + fc::raw::pack(ds,v); + } + return vec; + } + + template + inline T unpack( const std::vector& s ) + { try { + T tmp; + if( s.size() ) { + datastream ds( s.data(), size_t(s.size()) ); + fc::raw::unpack(ds,tmp); + } + return tmp; + } FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type",fc::get_typename::name() ) ) } + + template + inline void unpack( const std::vector& s, T& tmp ) + { try { + if( s.size() ) { + datastream ds( s.data(), size_t(s.size()) ); + fc::raw::unpack(ds,tmp); + } + } FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type",fc::get_typename::name() ) ) } + + template + inline void pack( char* d, uint32_t s, const T& v ) { + datastream ds(d,s); + fc::raw::pack(ds,v ); + } + + template + inline T unpack( const char* d, uint32_t s ) + { try { + T v; + datastream ds( d, s ); + fc::raw::unpack(ds,v); + return v; + } FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type",fc::get_typename::name() ) ) } + + template + inline void unpack( const char* d, uint32_t s, T& v ) + { try { + datastream ds( d, s ); + fc::raw::unpack(ds,v); + return v; + } FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type",fc::get_typename::name() ) ) } + + template + struct pack_static_variant + { + Stream& stream; + pack_static_variant( Stream& s ):stream(s){} + + typedef void result_type; + template void operator()( const T& v )const + { + fc::raw::pack( stream, v ); + } + }; + + template + struct unpack_static_variant + { + Stream& stream; + unpack_static_variant( Stream& s ):stream(s){} + + typedef void result_type; + template void operator()( T& v )const + { + fc::raw::unpack( stream, v ); + } + }; + + + template + void pack( Stream& s, const static_variant& sv ) + { + fc::raw::pack( s, unsigned_int(sv.which()) ); + sv.visit( pack_static_variant(s) ); + } + + template void unpack( Stream& s, static_variant& sv ) + { + unsigned_int w; + fc::raw::unpack( s, w ); + sv.set_which(w.value); + sv.visit( unpack_static_variant(s) ); + } + +} } // namespace fc::raw + diff --git a/include/fc/io/raw_fwd.hpp b/include/fc/io/raw_fwd.hpp new file mode 100644 index 000000000..9192ca3af --- /dev/null +++ b/include/fc/io/raw_fwd.hpp @@ -0,0 +1,114 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ARRAY_ALLOC_SIZE (1024*1024*10) + +namespace fc { + class time_point; + class time_point_sec; + class variant; + class variant_object; + class path; + template class static_variant; + + template class enum_type; + namespace ip { class endpoint; } + + namespace ecc { class public_key; class private_key; } + namespace raw { + template + inline void pack( Stream& s, const fc::enum_type& tp ); + template + inline void unpack( Stream& s, fc::enum_type& tp ); + + template inline void pack( Stream& s, const std::set& value ); + template inline void unpack( Stream& s, std::set& value ); + template inline void pack( Stream& s, const std::unordered_set& value ); + template inline void unpack( Stream& s, std::unordered_set& value ); + + template void pack( Stream& s, const static_variant& sv ); + template void unpack( Stream& s, static_variant& sv ); + + template inline void pack( Stream& s, const flat_set& value ); + template inline void unpack( Stream& s, flat_set& value ); + + template inline void pack( Stream& s, const std::deque& value ); + template inline void unpack( Stream& s, std::deque& value ); + + template inline void pack( Stream& s, const std::unordered_map& value ); + template inline void unpack( Stream& s, std::unordered_map& value ); + + template inline void pack( Stream& s, const std::map& value ); + template inline void unpack( Stream& s, std::map& value ); + + template inline void pack( Stream& s, const flat_map& value ); + template inline void unpack( Stream& s, flat_map& value ); + + template inline void pack( Stream& s, const std::pair& value ); + template inline void unpack( Stream& s, std::pair& value ); + + template inline void pack( Stream& s, const variant_object& v ); + template inline void unpack( Stream& s, variant_object& v ); + template inline void pack( Stream& s, const variant& v ); + template inline void unpack( Stream& s, variant& v ); + + template inline void pack( Stream& s, const path& v ); + template inline void unpack( Stream& s, path& v ); + template inline void pack( Stream& s, const ip::endpoint& v ); + template inline void unpack( Stream& s, ip::endpoint& v ); + + + template void unpack( Stream& s, fc::optional& v ); + template void unpack( Stream& s, const T& v ); + template void pack( Stream& s, const fc::optional& v ); + template void pack( Stream& s, const safe& v ); + template void unpack( Stream& s, fc::safe& v ); + + template void unpack( Stream& s, time_point& ); + template void pack( Stream& s, const time_point& ); + template void unpack( Stream& s, time_point_sec& ); + template void pack( Stream& s, const time_point_sec& ); + template void unpack( Stream& s, std::string& ); + template void pack( Stream& s, const std::string& ); + template void unpack( Stream& s, fc::ecc::public_key& ); + template void pack( Stream& s, const fc::ecc::public_key& ); + template void unpack( Stream& s, fc::ecc::private_key& ); + template void pack( Stream& s, const fc::ecc::private_key& ); + + template inline void pack( Stream& s, const T& v ); + template inline void unpack( Stream& s, T& v ); + + template inline void pack( Stream& s, const std::vector& v ); + template inline void unpack( Stream& s, std::vector& v ); + + template inline void pack( Stream& s, const signed_int& v ); + template inline void unpack( Stream& s, signed_int& vi ); + + template inline void pack( Stream& s, const unsigned_int& v ); + template inline void unpack( Stream& s, unsigned_int& vi ); + + template inline void pack( Stream& s, const char* v ); + template inline void pack( Stream& s, const std::vector& value ); + template inline void unpack( Stream& s, std::vector& value ); + + template inline void pack( Stream& s, const fc::array& v); + template inline void unpack( Stream& s, fc::array& v); + + template inline void pack( Stream& s, const bool& v ); + template inline void unpack( Stream& s, bool& v ); + + template inline std::vector pack( const T& v ); + template inline T unpack( const std::vector& s ); + template inline T unpack( const char* d, uint32_t s ); + template inline void unpack( const char* d, uint32_t s, T& v ); +} } diff --git a/include/fc/io/raw_unpack_file.hpp b/include/fc/io/raw_unpack_file.hpp new file mode 100644 index 000000000..580a64cce --- /dev/null +++ b/include/fc/io/raw_unpack_file.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include + +namespace fc +{ + namespace raw + { + template + void unpack_file( const fc::path& filename, T& obj ) + { + try { + fc::file_mapping fmap( filename.generic_string().c_str(), fc::read_only); + fc::mapped_region mapr( fmap, fc::read_only, 0, fc::file_size(filename) ); + auto cs = (const char*)mapr.get_address(); + + fc::datastream ds( cs, mapr.get_size() ); + fc::raw::unpack(ds,obj); + } FC_RETHROW_EXCEPTIONS( info, "unpacking file ${file}", ("file",filename) ); + } + } +} diff --git a/include/fc/io/raw_variant.hpp b/include/fc/io/raw_variant.hpp new file mode 100644 index 000000000..24fe3f6e3 --- /dev/null +++ b/include/fc/io/raw_variant.hpp @@ -0,0 +1,148 @@ +#pragma once +#include +#include +#include +#include + +namespace fc { namespace raw { + + template + class variant_packer : public variant::visitor + { + public: + variant_packer( Stream& _s ):s(_s){} + virtual void handle()const { } + virtual void handle( const int64_t& v )const + { + fc::raw::pack( s, v ); + } + virtual void handle( const uint64_t& v )const + { + fc::raw::pack( s, v ); + } + virtual void handle( const double& v )const + { + fc::raw::pack( s, v ); + } + virtual void handle( const bool& v )const + { + fc::raw::pack( s, v ); + } + virtual void handle( const string& v )const + { + fc::raw::pack( s, v ); + } + virtual void handle( const variant_object& v)const + { + fc::raw::pack( s, v ); + } + virtual void handle( const variants& v)const + { + fc::raw::pack( s, v ); + } + + Stream& s; + + }; + + + template + inline void pack( Stream& s, const variant& v ) + { + pack( s, uint8_t(v.get_type()) ); + v.visit( variant_packer(s) ); + } + template + inline void unpack( Stream& s, variant& v ) + { + uint8_t t; + unpack( s, t ); + switch( t ) + { + case variant::null_type: + return; + case variant::int64_type: + { + int64_t val; + raw::unpack(s,val); + v = val; + return; + } + case variant::uint64_type: + { + uint64_t val; + raw::unpack(s,val); + v = val; + return; + } + case variant::double_type: + { + double val; + raw::unpack(s,val); + v = val; + return; + } + case variant::bool_type: + { + bool val; + raw::unpack(s,val); + v = val; + return; + } + case variant::string_type: + { + fc::string val; + raw::unpack(s,val); + v = fc::move(val); + return; + } + case variant::array_type: + { + variants val; + raw::unpack(s,val); + v = fc::move(val); + return; + } + case variant::object_type: + { + variant_object val; + raw::unpack(s,val); + v = fc::move(val); + return; + } + default: + FC_THROW_EXCEPTION( parse_error_exception, "Unknown Variant Type ${t}", ("t", t) ); + } + } + + template + inline void pack( Stream& s, const variant_object& v ) + { + unsigned_int vs = (uint32_t)v.size(); + pack( s, vs ); + for( auto itr = v.begin(); itr != v.end(); ++itr ) + { + pack( s, itr->key() ); + pack( s, itr->value() ); + } + } + template + inline void unpack( Stream& s, variant_object& v ) + { + unsigned_int vs; + unpack( s, vs ); + + mutable_variant_object mvo; + mvo.reserve(vs.value); + for( uint32_t i = 0; i < vs.value; ++i ) + { + fc::string key; + fc::variant value; + fc::raw::unpack(s,key); + fc::raw::unpack(s,value); + mvo.set( fc::move(key), fc::move(value) ); + } + v = fc::move(mvo); + } + +} } // fc::raw diff --git a/include/fc/io/sstream.hpp b/include/fc/io/sstream.hpp new file mode 100644 index 000000000..285c481ed --- /dev/null +++ b/include/fc/io/sstream.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +namespace fc { + + class stringstream : virtual public iostream { + public: + stringstream(); + stringstream( fc::string& s); + stringstream( const fc::string& s); + ~stringstream(); + + fc::string str(); + void str(const fc::string& s); + + void clear(); + + virtual bool eof()const; + virtual size_t writesome( const char* buf, size_t len ); + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual size_t readsome( char* buf, size_t len ); + virtual size_t readsome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual void close(); + virtual void flush(); + char peek(); + + private: + class impl; + fwd my; + }; + +} diff --git a/include/fc/io/stdio.hpp b/include/fc/io/stdio.hpp new file mode 100644 index 000000000..36ba091ac --- /dev/null +++ b/include/fc/io/stdio.hpp @@ -0,0 +1,39 @@ +#pragma once +#include + +namespace fc +{ + + class cout_t : virtual public ostream { + public: + virtual size_t writesome( const char* buf, size_t len ); + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual void close(); + virtual void flush(); + }; + + class cerr_t : virtual public ostream { + public: + virtual size_t writesome( const char* buf, size_t len ); + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual void close(); + virtual void flush(); + }; + + class cin_t : virtual public istream { + public: + ~cin_t(); + virtual size_t readsome( char* buf, size_t len ); + virtual size_t readsome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual istream& read( char* buf, size_t len ); + virtual bool eof()const; + }; + + extern cout_t& cout; + extern cerr_t& cerr; + extern cin_t& cin; + + extern std::shared_ptr cin_ptr; + extern std::shared_ptr cout_ptr; + extern std::shared_ptr cerr_ptr; +} diff --git a/include/fc/io/varint.hpp b/include/fc/io/varint.hpp new file mode 100644 index 000000000..b4988c530 --- /dev/null +++ b/include/fc/io/varint.hpp @@ -0,0 +1,101 @@ +#pragma once +#include + +namespace fc { + +struct unsigned_int { + unsigned_int( uint32_t v = 0 ):value(v){} + + template + unsigned_int( T v ):value(v){} + + //operator uint32_t()const { return value; } + //operator uint64_t()const { return value; } + + template + operator T()const { return static_cast(value); } + + unsigned_int& operator=( int32_t v ) { value = v; return *this; } + + uint32_t value; + + friend bool operator==( const unsigned_int& i, const uint32_t& v ) { return i.value == v; } + friend bool operator==( const uint32_t& i, const unsigned_int& v ) { return i == v.value; } + friend bool operator==( const unsigned_int& i, const unsigned_int& v ) { return i.value == v.value; } + + friend bool operator!=( const unsigned_int& i, const uint32_t& v ) { return i.value != v; } + friend bool operator!=( const uint32_t& i, const unsigned_int& v ) { return i != v.value; } + friend bool operator!=( const unsigned_int& i, const unsigned_int& v ) { return i.value != v.value; } + + friend bool operator<( const unsigned_int& i, const uint32_t& v ) { return i.value < v; } + friend bool operator<( const uint32_t& i, const unsigned_int& v ) { return i < v.value; } + friend bool operator<( const unsigned_int& i, const unsigned_int& v ) { return i.value < v.value; } + + friend bool operator>=( const unsigned_int& i, const uint32_t& v ) { return i.value >= v; } + friend bool operator>=( const uint32_t& i, const unsigned_int& v ) { return i >= v.value; } + friend bool operator>=( const unsigned_int& i, const unsigned_int& v ) { return i.value >= v.value; } +}; + +/** + * @brief serializes a 32 bit signed interger in as few bytes as possible + * + * Uses the google protobuf algorithm for seralizing signed numbers + */ +struct signed_int { + signed_int( int32_t v = 0 ):value(v){} + operator int32_t()const { return value; } + template + signed_int& operator=( const T& v ) { value = v; return *this; } + signed_int operator++(int) { return value++; } + signed_int& operator++(){ ++value; return *this; } + + int32_t value; + + friend bool operator==( const signed_int& i, const int32_t& v ) { return i.value == v; } + friend bool operator==( const int32_t& i, const signed_int& v ) { return i == v.value; } + friend bool operator==( const signed_int& i, const signed_int& v ) { return i.value == v.value; } + + friend bool operator!=( const signed_int& i, const int32_t& v ) { return i.value != v; } + friend bool operator!=( const int32_t& i, const signed_int& v ) { return i != v.value; } + friend bool operator!=( const signed_int& i, const signed_int& v ) { return i.value != v.value; } + + friend bool operator<( const signed_int& i, const int32_t& v ) { return i.value < v; } + friend bool operator<( const int32_t& i, const signed_int& v ) { return i < v.value; } + friend bool operator<( const signed_int& i, const signed_int& v ) { return i.value < v.value; } + + friend bool operator>=( const signed_int& i, const int32_t& v ) { return i.value >= v; } + friend bool operator>=( const int32_t& i, const signed_int& v ) { return i >= v.value; } + friend bool operator>=( const signed_int& i, const signed_int& v ) { return i.value >= v.value; } +}; + +class variant; + +void to_variant( const signed_int& var, variant& vo ); +void from_variant( const variant& var, signed_int& vo ); +void to_variant( const unsigned_int& var, variant& vo ); +void from_variant( const variant& var, unsigned_int& vo ); + +} // namespace fc + +#include +namespace std +{ + template<> + struct hash + { + public: + size_t operator()(const fc::signed_int &a) const + { + return std::hash()(a.value); + } + }; + template<> + struct hash + { + public: + size_t operator()(const fc::signed_int &a) const + { + return std::hash()(a.value); + } + }; +} diff --git a/include/fc/iostream.hpp b/include/fc/iostream.hpp deleted file mode 100644 index 4f04fced5..000000000 --- a/include/fc/iostream.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - - class istream { - public: - virtual ~istream(){}; - - virtual size_t readsome( char* buf, size_t len ) = 0; - virtual istream& read( char* buf, size_t len ) = 0; - - virtual bool eof()const = 0; - }; - - class ostream { - public: - virtual ~ostream(){}; - - virtual ostream& write( const char* buf, size_t len ) = 0; - virtual void close(){} - virtual void flush(){} - }; - - class iostream : public virtual ostream, public virtual istream {}; - - - struct cout_t : virtual public ostream { - virtual ostream& write( const char* buf, size_t len ); - virtual void close(); - virtual void flush(); - - virtual ostream& write( const fc::string& ); - }; - - struct cerr_t : virtual public ostream { - virtual ostream& write( const char* buf, size_t len ); - virtual void close(); - virtual void flush(); - - virtual ostream& write( const fc::string& ); - }; - - struct cin_t : virtual public istream { - ~cin_t(); - virtual size_t readsome( char* buf, size_t len ); - virtual istream& read( char* buf, size_t len ); - virtual bool eof()const; - }; - fc::istream& getline( fc::istream&, fc::string&, char delim = '\n' ); - - - extern cout_t cout; - extern cerr_t cerr; - extern cin_t cin; - - - template - ostream& operator<<( ostream& o, const T& v ) { - auto str = fc::lexical_cast(v); - o.write( str.c_str(), static_cast(str.size()) ); - return o; - } - ostream& operator<<( ostream& o, const char* v ); - - template - ostream& operator<<( ostream& o, const fc::string& str ) { - o.write( str.c_str(), static_cast(str.size()) ); - return o; - } - template - istream& operator>>( istream& o, T& v ) { - fc::string str; - getline( o, str, ' ' ); - v = fc::lexical_cast(str); - return o; - } -} diff --git a/include/fc/iostream_wrapper.hpp b/include/fc/iostream_wrapper.hpp deleted file mode 100644 index 179813773..000000000 --- a/include/fc/iostream_wrapper.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - /** - * Used to wrap references to other streams - */ - class ostream_wrapper : public ostream { - public: - template - ostream_wrapper( Stream& s ) - :my( new impl(s) ){} - - virtual ~ostream_wrapper(){}; - - virtual ostream& write( const char* buf, size_t len ) { - my->write(buf,len); - return *this; - } - virtual void close() { - // TODO: move to cpp - my->close(); - } - virtual void flush() { - my->flush(); - } - - protected: - virtual ostream& write( const fc::string& s ) { - return write( s.c_str(), s.size() ); - } - - struct impl_base : public fc::retainable { - virtual ~impl_base(){} - virtual void write( const char* buf, size_t len ) = 0; - virtual void close() = 0; - virtual void flush() = 0; - }; - - template - struct impl : public impl_base { - impl(T& i):st(i){} - - virtual void write( const char* buf, size_t len ) { - st.write(buf,len); - } - virtual void close() { st.close(); } - virtual void flush() { st.flush(); } - T& st; - }; - - fc::shared_ptr my; - }; - /** - * Used to wrap references to other streams - */ - class istream_wrapper : public istream { - public: - template - istream_wrapper( Stream& s ) - :my( new impl(s) ){} - - virtual ~istream_wrapper(){}; - - virtual size_t readsome( char* buf, size_t len ) { - return my->readsome(buf,len); - } - virtual istream& read( char* buf, size_t len ) { - // slog( "%p %lld", my.get(), len ); - my->read(buf,len); - return *this; - } - virtual void close() { } - virtual bool eof()const{ return my->eof(); } - - /* - virtual istream& read( int64_t& ) { return *this; } - virtual istream& read( uint64_t& ) { return *this; } - virtual istream& read( int32_t& ) { return *this; } - virtual istream& read( uint32_t& ) { return *this; } - virtual istream& read( int16_t& ) { return *this; } - virtual istream& read( uint16_t& ) { return *this; } - virtual istream& read( int8_t& ) { return *this; } - virtual istream& read( uint8_t& ) { return *this; } - virtual istream& read( float& ) { return *this; } - virtual istream& read( double& ) { return *this; } - virtual istream& read( bool& ) { return *this; } - virtual istream& read( char& ) { return *this; } - virtual istream& read( fc::string& ) { return *this; } - */ - - protected: - struct impl_base : public fc::retainable { - virtual ~impl_base(){} - virtual void read( char* buf, size_t len )=0; - virtual size_t readsome( char* buf, size_t len )=0; - virtual bool eof()const=0; - }; - - template - struct impl : public impl_base { - impl(T& i):st(i){} - - virtual size_t readsome( char* buf, size_t len ) { return size_t(st.readsome(buf,len)); } - virtual void read( char* buf, size_t len ) { - st.read(buf,len); - } - virtual bool eof()const { return st.eof() || !st.good(); } - T& st; - }; - - fc::shared_ptr my; - }; - -} // namespace fc - diff --git a/include/fc/ip.hpp b/include/fc/ip.hpp deleted file mode 100644 index ea64ed1ec..000000000 --- a/include/fc/ip.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -#include - -namespace fc { - - namespace ip { - class address { - public: - address( uint32_t _ip = 0 ); - address( const fc::string& s ); - - address& operator=( const fc::string& s ); - operator fc::string()const; - operator uint32_t()const; - - friend bool operator==( const address& a, const address& b ); - private: - uint32_t _ip; - }; - - class endpoint { - public: - endpoint(); - endpoint( const address& i, uint16_t p = 0); - - /** Converts "IP:PORT" to an endpoint */ - static endpoint from_string( const string& s ); - /** returns "IP:PORT" */ - operator string()const; - - void set_port(uint16_t p ) { _port = p; } - uint16_t port()const; - const address& get_address()const; - - friend bool operator==( const endpoint& a, const endpoint& b ); - - private: - /** - * The compiler pads endpoint to a full 8 bytes, so while - * a port number is limited in range to 16 bits, we specify - * a full 32 bits so that memcmp can be used with sizeof(), - * otherwise 2 bytes will be 'random' and you do not know - * where they are stored. - */ - uint32_t _port; - address _ip; - }; - } - class value; - void pack( fc::value& , const fc::ip::endpoint& ); - void unpack( const fc::value& , fc::ip::endpoint& ); -} diff --git a/include/fc/json.hpp b/include/fc/json.hpp deleted file mode 100644 index ccfc129cd..000000000 --- a/include/fc/json.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace fc { - class istream; - class ostream; - class path; - - namespace json { - string to_string( const value& o ); - string to_pretty_string( const value& v ); - - - value from_string( const string& s ); - value from_string( const char* s, const char* e ); - value from_string( const fc::vector& v ); - - - string escape_string( const string& ); - string unescape_string( const string& ); - - void write( ostream& out, const value& val ); - - template - void write( ostream& out, const T& val ) { - write( out, value(val) ); - } - - template - string to_string( const T& o ) { - return json::to_string(value(o)); - } - template - string to_pretty_string( const T& o ) { - return json::to_pretty_string(value(o)); - } - - template - T from_string( const string& s ) { - return value_cast( from_string(s) ); - } - - value from_file( const fc::path& s ); - template - T from_file( const fc::path& s ) { - return value_cast( fc::json::from_file(s) ); - } - - } // namespace json -} // fc - diff --git a/include/fc/json_rpc_client.hpp b/include/fc/json_rpc_client.hpp deleted file mode 100644 index 88bc08151..000000000 --- a/include/fc/json_rpc_client.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include - - -namespace fc { namespace json { - - namespace detail { - struct rpc_member { - #ifdef BOOST_NO_VARIADIC_TEMPLATES - #define RPC_MEMBER_FUNCTOR(z,n,IS_CONST) \ - template \ - static std::function( BOOST_PP_ENUM_PARAMS(n,A) ) > \ - functor( P, R (C::*mem_func)(BOOST_PP_ENUM_PARAMS(n,A)) IS_CONST, \ - const rpc_connection::ptr& c = rpc_connection::ptr(), const char* name = nullptr ) { \ - return [=](BOOST_PP_ENUM_BINARY_PARAMS(n,A,a))->fc::future{ \ - return c->invoke( name, make_tuple(BOOST_PP_ENUM_PARAMS(n,a)) ); }; \ - } - BOOST_PP_REPEAT( 8, RPC_MEMBER_FUNCTOR, const ) - BOOST_PP_REPEAT( 8, RPC_MEMBER_FUNCTOR, BOOST_PP_EMPTY() ) - #undef RPC_MEMBER_FUNCTOR - - #else - template - static std::function(Args...)> functor( P&& p, R (C::*mem_func)(Args...), - const rpc_connection::ptr& c = rpc_connection::ptr(), const char* name = nullptr ) { - return [=](Args... args)->fc::future{ - return c->invoke( name, make_tuple(args...) ); }; - } - template - static std::function(Args...)> functor( P&& p, R (C::*mem_func)(Args...)const, - const rpc_connection::ptr& c = rpc_connection::ptr(), const char* name = nullptr ) { - return [=](Args... args)->fc::future{ - return c->invoke( name, make_tuple(args...) ); }; - } - #endif - }; - - struct vtable_visitor { - vtable_visitor( rpc_connection::ptr& c ):_con(c){} - - template - void operator()( const char* name, Function& memb, MemberPtr m )const { - memb = rpc_member::functor( nullptr, m, _con, name ); - } - rpc_connection::ptr& _con; - }; - }; - - - template - class rpc_client : public actor { //ptr { - public: - rpc_client(){} - rpc_client( const rpc_connection::ptr& c ){ set_connection(c); } - //rpc_client( const rpc_client& c ):_con(c._con){} - - void set_connection( const rpc_connection::ptr& c ) { - _con = c; - this->_vtable.reset(new fc::detail::vtable() ); - this->_vtable->template visit_other( fc::json::detail::vtable_visitor(_con) ); - } - const rpc_connection::ptr& connection()const { return _con; } - - private: - rpc_connection::ptr _con; - }; - -} } // fc::json diff --git a/include/fc/json_rpc_connection.hpp b/include/fc/json_rpc_connection.hpp deleted file mode 100644 index 7b5a9cb43..000000000 --- a/include/fc/json_rpc_connection.hpp +++ /dev/null @@ -1,196 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace fc { namespace json { - class rpc_connection; - class error_object; - - struct rpc_server_method : public fc::retainable { - typedef fc::shared_ptr ptr; - virtual value call( const value& v ) = 0; - }; - - namespace detail { - struct pending_result : virtual public promise_base { - typedef shared_ptr ptr; - virtual void handle_result( const fc::value& ) = 0; - void handle_error( const fc::string& ); - int64_t id; - pending_result::ptr next; - protected: - ~pending_result(){} - }; - template - struct pending_result_impl : virtual public promise, virtual public pending_result { - virtual void handle_result( const fc::value& s ) { - this->set_value( value_cast(s) ); - } - protected: - ~pending_result_impl(){} - }; - template<> - struct pending_result_impl : virtual public promise, virtual public pending_result { - virtual void handle_result( const fc::value& ) { - set_value(); - } - protected: - ~pending_result_impl(){} - }; - - template - struct named_param { - typedef fc::false_type type; - static T cast( const value& v ) { return fc::value_cast(v); } - - template - static value to_value( X&& x ) { return value(fc::forward(x)); } - }; - - #define FC_JSON_NAMED_PARAMS( T ) \ - namespace fc { namespace json {namespace detail { \ - template<> \ - struct named_param< fc::tuple > { \ - typedef fc::true_type type; \ - static tuple cast( const value& v ) { return make_tuple(fc::value_cast(v)); } \ - template \ - static value to_value( X&& x ) { return value( x.a0 ); }\ - }; \ - } } } - - template - struct rpc_server_method_impl : public rpc_server_method { - rpc_server_method_impl( const std::function& f ):func(f){} - virtual value call( const value& v ) { - return value( fc::call_fused(func, named_param::type>::cast(v) ) ); - } - std::function func; - }; - template - struct rpc_server_method_impl : public rpc_server_method { - rpc_server_method_impl( const std::function& f ):func(f){} - virtual value call( const value& v ) { - fc::call_fused(func, named_param::type>::cast(v) ); - return value(); - } - std::function func; - }; - - template - struct add_method_visitor { - public: - add_method_visitor( const fc::ptr& p, fc::json::rpc_connection& c ):_ptr(p),_con(c){} - - template - void operator()( const char* name, std::function& meth); - template - void operator()( const char* name, std::function& meth); - template - void operator()( const char* name, std::function& meth); - template - void operator()( const char* name, std::function& meth); - - const fc::ptr& _ptr; - fc::json::rpc_connection& _con; - }; - } - - /** - * This is the base JSON RPC connection that handles the protocol - * level issues. It does not implement a transport which should - * be provided separately and use the handle_message and set_send_delegate - * methods to manage the protocol. - */ - class rpc_connection : public fc::retainable { - public: - rpc_connection(); - rpc_connection(const rpc_connection&); - rpc_connection(rpc_connection&&); - ~rpc_connection(); - rpc_connection& operator=(const rpc_connection&); - rpc_connection& operator=(rpc_connection&&); - - typedef fc::shared_ptr ptr; - - void cancel_pending_requests(); - - template - future invoke( const fc::string& method, Args&& a = nullptr ){ - auto r = new detail::pending_result_impl(); - typename promise::ptr rtn( r, true ); - invoke( detail::pending_result::ptr(r), method, - value(detail::named_param::type>::to_value(a)) ); - return rtn; - } - - template - void notice( const fc::string& method, Args&& a = nullptr ){ - send_notice( method, - value(detail::named_param::type>::to_value(a)) ); - } - - template - void add_interface( const fc::ptr& it ) { - it->TEMPLATE visit( detail::add_method_visitor( it, *this ) ); - } - - void add_method( const fc::string& name, const fc::json::rpc_server_method::ptr& func ); - - virtual void close(){}; - - protected: - void handle_message( const value& m ); - virtual void send_notice( const fc::string& m, value&& param ) = 0; - virtual void send_invoke( uint64_t id, const fc::string& m, value&& param ) = 0; - virtual void send_error( uint64_t id, const error_object& e ) = 0; - virtual void send_result( uint64_t id, value&& r ) = 0; - - - private: - void invoke( detail::pending_result::ptr&& p, const fc::string& m, value&& param ); - void add_method( const fc::string& name, rpc_server_method::ptr&& m ); - template - friend struct detail::add_method_visitor; - - class impl; - fc::shared_ptr my; - }; - - namespace detail { - - template - template - void add_method_visitor::operator()( const char* name, - std::function& meth) { - _con.add_method( name, rpc_server_method::ptr( - new rpc_server_method_impl,R(A1) >(meth) ) ); - } - template - template - void add_method_visitor::operator()( const char* name, - std::function& meth) { - _con.add_method( name, rpc_server_method::ptr( - new rpc_server_method_impl,R(A1,A2) >(meth) ) ); - } - template - template - void add_method_visitor::operator()( const char* name, - std::function& meth) { - _con.add_method( name, rpc_server_method::ptr( - new rpc_server_method_impl,R(A1,A2,A3) >(meth) ) ); - } - template - template - void add_method_visitor::operator()( const char* name, - std::function& meth) { - _con.add_method( name, rpc_server_method::ptr( - new rpc_server_method_impl,R() >(meth) ) ); - } - - } // namespace detail - -} } // fc::json - diff --git a/include/fc/json_rpc_error_object.hpp b/include/fc/json_rpc_error_object.hpp deleted file mode 100644 index d898356a7..000000000 --- a/include/fc/json_rpc_error_object.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include -#include -#include -#include - - -namespace fc { - class value; - - namespace json { - - class error_object { - public: - error_object( const fc::string& msg, const fc::value& v , int64_t c = -32000); - error_object( const fc::string& msg=fc::string(), int64_t c = -32000 ); - error_object( const error_object& e ); - ~error_object(); - - int64_t code; - fc::string message; - fc::optional data; - }; - -} } - -FC_REFLECT( fc::json::error_object, (code)(message)(data) ) diff --git a/include/fc/json_rpc_process_client.hpp b/include/fc/json_rpc_process_client.hpp deleted file mode 100644 index d1022e5ac..000000000 --- a/include/fc/json_rpc_process_client.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace fc { namespace json { - - template - class rpc_process_client : public ptr { - public: - - fc::future exec( const fc::path& exe, int opt = fc::process::open_all ) { - return exec( exe, fc::path("."), opt ); - } - fc::future exec( const fc::path& exe, const fc::path& wd, - int opt = fc::process::open_all ) { - return exec( exe, fc::vector(), wd, opt ); - } - fc::future exec( const fc::path& exe, fc::vector&& args , - int opt = fc::process::open_all ) { - return exec( exe, fc::move(args), fc::path("."), opt ); - } - fc::future exec( const fc::path& exe, fc::vector&& args, - const fc::path& wd, int opt = fc::process::open_all ) { - auto r = _proc.exec( canonical(exe), fc::move(args), wd, opt ); - _con.reset( new fc::json::rpc_stream_connection( _proc.out_stream(), _proc.in_stream() ) ); - this->_vtable.reset(new fc::detail::vtable() ); - rpc_connection::ptr p(_con); - this->_vtable->template visit_other( fc::json::detail::vtable_visitor(p) ); - return r; - } - - void kill() { _con->close(); _proc.kill(); } - - /** - * @brief returns a stream that reads from the process' stderr - */ - fc::istream& err_stream() { return _proc.err_stream(); } - - template - void on_close( T&& f) { _con->on_close( fc::forward(f) ); } - - const fc::json::rpc_stream_connection::ptr& connection()const { return _con; } - - ~rpc_process_client() { - if(_con) - _con->close(); - } - private: - fc::process _proc; - fc::json::rpc_stream_connection::ptr _con; - }; -} } diff --git a/include/fc/json_rpc_ssh_process_client.hpp b/include/fc/json_rpc_ssh_process_client.hpp deleted file mode 100644 index cadae754b..000000000 --- a/include/fc/json_rpc_ssh_process_client.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { namespace json { - - template - class rpc_ssh_process_client : public rpc_client { - public: - rpc_ssh_process_client(){} - bool valid()const { return proc.valid(); } - - rpc_ssh_process_client( const fc::ssh::process& proc ) - :rpc_client(rpc_connection::ptr(new fc::json::rpc_stream_connection( proc.out_stream(), proc.in_stream() ) ) ){} - - rpc_ssh_process_client& operator = ( const fc::ssh::process& proc ) { - this->set_connection( rpc_connection::ptr(new fc::json::rpc_stream_connection( proc.out_stream(), proc.in_stream() ) ) ); - return *this; - } - - /** - * @brief returns a stream that reads from the process' stderr - */ - fc::istream& err_stream() { return proc.err_stream(); } - - fc::ssh::process& get_ssh_process() { return proc; } - private: - fc::ssh::process proc; - fc::json::rpc_stream_connection::ptr _con; - - }; -} } diff --git a/include/fc/json_rpc_stream_connection.hpp b/include/fc/json_rpc_stream_connection.hpp deleted file mode 100644 index acf689b21..000000000 --- a/include/fc/json_rpc_stream_connection.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include - -namespace fc { - class istream; - class ostream; - - namespace json { - class rpc_stream_connection : public rpc_connection { - public: - typedef fc::shared_ptr ptr; - rpc_stream_connection( fc::istream&, fc::ostream& ); - rpc_stream_connection(const rpc_stream_connection& ); - rpc_stream_connection(); - - // the life of the streams must exceed the life of all copies - // of this rpc_stream_connection - void open( fc::istream&, fc::ostream& ); - - // cancels all pending requests, closes the ostream - // results on_close() being called if the stream is not already closed. - virtual void close(); - - /** - * When the connection is closed, call the given method - */ - void on_close( const std::function& ); - - protected: - ~rpc_stream_connection(); - virtual void send_invoke( uint64_t id, const fc::string& m, value&& param ); - virtual void send_notice( const fc::string& m, value&& param ); - virtual void send_error( uint64_t id, const error_object& eo ); - virtual void send_result( uint64_t id, value&& r ); - - private: - class impl; - fc::shared_ptr my; - }; -} } // fc::json diff --git a/include/fc/json_rpc_tcp_connection.hpp b/include/fc/json_rpc_tcp_connection.hpp deleted file mode 100644 index 7f57256fa..000000000 --- a/include/fc/json_rpc_tcp_connection.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include - -namespace fc { - class tcp_socket; - namespace ip { class endpoint; } - - namespace json { - class rpc_tcp_connection : public rpc_stream_connection { - public: - typedef fc::shared_ptr ptr; - - rpc_tcp_connection(); - rpc_tcp_connection( const rpc_tcp_connection& c ); - ~rpc_tcp_connection(); - - void connect_to( const fc::ip::endpoint& e ); - void start(); - tcp_socket& get_socket()const; - - virtual void close(); - - private: - class impl; - fc::shared_ptr my; - }; - } // json -} // fc diff --git a/include/fc/json_rpc_tcp_server.hpp b/include/fc/json_rpc_tcp_server.hpp deleted file mode 100644 index 7c334b8f1..000000000 --- a/include/fc/json_rpc_tcp_server.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include - -namespace fc { - - namespace json { - class rpc_tcp_server { - private: - template - struct add_method_visitor { - add_method_visitor( fc::ptr& p, rpc_connection& s ):_ptr(p),_rpcc(s) { } - - template - void operator()( const char* name, Functor& fun ) { - _rpcc.add_method( name, fun ); - } - - fc::ptr& _ptr; - rpc_connection& _rpcc; - }; - - public: - rpc_tcp_server(); - ~rpc_tcp_server(); - - template - void add_interface( const fc::ptr& ptr ) { - on_new_connection( [=]( rpc_connection& c ) { - ptr->visit( detail::add_method_visitor( ptr, c ) ); - }); - } - - void on_new_connection( const std::function& c ); - - void listen( uint16_t port ); - - private: - class impl; - impl* my; - }; - } -} diff --git a/include/fc/lexical_cast.hpp b/include/fc/lexical_cast.hpp deleted file mode 100644 index 3c0aa3aef..000000000 --- a/include/fc/lexical_cast.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include - -namespace fc { - - namespace detail { - template - struct lexical_cast { }; - - double to_double( const fc::string& s ); - inline double to_double( double d ) { return d; } - - int64_t to_int64( const fc::string& s ); - inline int64_t to_int64( double d ) { return static_cast(d); } - inline int64_t to_int64( int64_t d ) { return d; } - - uint64_t to_uint64( const fc::string& s ); - inline uint64_t to_uint64( double d ) { return static_cast(d); } - inline uint64_t to_uint64( uint64_t d ) { return d; } - - fc::string to_string( double d ); - fc::string to_string( size_t d ); - fc::string to_string( uint64_t d ); - fc::string to_string( uint32_t d ); - fc::string to_string( uint16_t d ); - fc::string to_string( uint8_t d ); - fc::string to_string( int64_t d ); - fc::string to_string( int32_t d ); - fc::string to_string( int16_t d ); - fc::string to_string( int8_t d ); - fc::string to_string( char d ); - inline fc::string to_string( const char* d ) { return d; } - inline fc::string to_string( fc::string s ) { return s; } - - template - struct lexical_cast { - static double cast( const R& v ) { return to_double( v ); } - }; - - template - struct lexical_cast { - static fc::string cast( const R& v ) { return to_string( v ); } - }; - template - struct lexical_cast { - static std::string cast( const R& v ) { return to_string( v ); } - }; - - template - struct lexical_cast { - static uint64_t cast( const R& v ) { return to_uint64( v ); } - }; - - template - struct lexical_cast { static int64_t cast( const R& v ) { return static_cast(to_int64( v )); } }; - - template - struct lexical_cast { static int32_t cast( const R& v ) { return static_cast(to_int64( v )); } }; - - template - struct lexical_cast { static int16_t cast( const R& v ) { return static_cast(to_int64( v )); } }; - - template - struct lexical_cast { static int8_t cast( const R& v ) { return static_cast(to_int64( v )); } }; - - - template - struct lexical_cast { static uint32_t cast( const R& v ) { return static_cast(to_uint64( v )); } }; - - template - struct lexical_cast { static uint16_t cast( const R& v ) { return static_cast(to_uint64( v )); } }; - - template - struct lexical_cast { static uint8_t cast( const R& v ) { return static_cast(to_uint64( v )); } }; - - template - struct lexical_cast { static bool cast( const R& v ) { return v != 0; } }; - - template<> - struct lexical_cast { static char cast( const fc::string& v ) - { return v[0]; } };// TODO: check string len - - template<> - struct lexical_cast { static bool cast( const fc::string& v ) { return v == "true"; } }; - - template<> - struct lexical_cast { static fc::string cast( const bool& v ) { return v ? "true" : "false";} }; - - template - struct lexical_cast { static float cast( const R& v ) { return static_cast(to_double( v )); } }; - } - - - template - T lexical_cast( const R& v ) { - return detail::lexical_cast::cast(v); - } -} diff --git a/include/fc/log.hpp b/include/fc/log.hpp deleted file mode 100644 index 4ad5dd9f8..000000000 --- a/include/fc/log.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include - -namespace boost { class mutex; } - -namespace fc { - /** wrapper on printf */ - void log( const char* color, const char* file_name, size_t line_num, const char* method_name, const char* format, ... ); - - /** used to add extra fields to be printed (thread,fiber,time,etc) */ - void add_log_field( void (*f)() ); - void remove_log_field( void (*f)() ); - - boost::mutex& log_mutex(); -} - -#ifndef __func__ -#define __func__ __FUNCTION__ -#endif - -#ifndef WIN32 -#define COLOR_CONSOLE 1 -#endif -#include - -#define dlog(...) do { fc::log( CONSOLE_DEFAULT, __FILE__, __LINE__, __func__, __VA_ARGS__ ); }while(false) -#define slog(...) do { fc::log( CONSOLE_DEFAULT, __FILE__, __LINE__, __func__, __VA_ARGS__ ); }while(false) -#define wlog(...) do { fc::log( CONSOLE_BROWN, __FILE__, __LINE__, __func__, __VA_ARGS__ ); }while(false) -#define elog(...) do { fc::log( CONSOLE_RED, __FILE__, __LINE__, __func__, __VA_ARGS__ ); }while(false) - - diff --git a/include/fc/appender.hpp b/include/fc/log/appender.hpp similarity index 81% rename from include/fc/appender.hpp rename to include/fc/log/appender.hpp index f8fa1d48b..f1f5aa8d2 100644 --- a/include/fc/appender.hpp +++ b/include/fc/log/appender.hpp @@ -1,25 +1,25 @@ #pragma once #include +#include namespace fc { class appender; class log_message; - class value; - class string; + class variant; class appender_factory : public fc::retainable { public: typedef fc::shared_ptr ptr; virtual ~appender_factory(){}; - virtual fc::shared_ptr create( const value& args ) = 0; + virtual fc::shared_ptr create( const variant& args ) = 0; }; namespace detail { template class appender_factory_impl : public appender_factory { public: - virtual fc::shared_ptr create( const value& args ) { + virtual fc::shared_ptr create( const variant& args ) { return fc::shared_ptr(new T(args)); } }; @@ -34,7 +34,7 @@ namespace fc { return register_appender( type, new detail::appender_factory_impl() ); } - static appender::ptr create( const fc::string& name, const fc::string& type, const value& args ); + static appender::ptr create( const fc::string& name, const fc::string& type, const variant& args ); static appender::ptr get( const fc::string& name ); static bool register_appender( const fc::string& type, const appender_factory::ptr& f ); diff --git a/include/fc/console_appender.hpp b/include/fc/log/console_appender.hpp similarity index 55% rename from include/fc/console_appender.hpp rename to include/fc/log/console_appender.hpp index 371d2b9e5..c15fcf089 100644 --- a/include/fc/console_appender.hpp +++ b/include/fc/log/console_appender.hpp @@ -1,11 +1,15 @@ #pragma once -#include -#include +#include +#include +#include -namespace fc { - class console_appender : public appender { +namespace fc +{ + class console_appender : public appender + { public: - struct color { + struct color + { enum type { red, green, @@ -17,39 +21,51 @@ namespace fc { console_default, }; }; + struct stream { enum type { std_out, std_error }; }; - struct level_color { - level_color( log_level::type l=log_level::all, + struct level_color + { + level_color( log_level l=log_level::all, color::type c=color::console_default ) :level(l),color(c){} - log_level::type level; + log_level level; console_appender::color::type color; }; - struct config { + struct config + { config() - :format( "${when} ${thread} ${context} ${file}:${line} ${method} ${level}] ${message}" ), + :format( "${timestamp} ${thread_name} ${context} ${file}:${line} ${method} ${level}] ${message}" ), stream(console_appender::stream::std_error),flush(true){} fc::string format; console_appender::stream::type stream; - fc::vector level_colors; + std::vector level_colors; bool flush; }; - console_appender( const value& args ); - const char* get_color( log_level::type l )const; + console_appender( const variant& args ); + console_appender( const config& cfg ); + console_appender(); + + ~console_appender(); virtual void log( const log_message& m ); + + void print( const std::string& text_to_print, + color::type text_color = color::console_default ); + + void configure( const config& cfg ); + private: - config cfg; - color::type lc[log_level::off+1]; + class impl; + std::unique_ptr my; }; } // namespace fc -#include +#include FC_REFLECT_ENUM( fc::console_appender::stream::type, (std_out)(std_error) ) FC_REFLECT_ENUM( fc::console_appender::color::type, (red)(green)(brown)(blue)(magenta)(cyan)(white)(console_default) ) FC_REFLECT( fc::console_appender::level_color, (level)(color) ) diff --git a/include/fc/log/file_appender.hpp b/include/fc/log/file_appender.hpp new file mode 100644 index 000000000..a05fcd3e6 --- /dev/null +++ b/include/fc/log/file_appender.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + +namespace fc { + +class file_appender : public appender { + public: + struct config { + config( const fc::path& p = "log.txt" ); + + fc::string format; + fc::path filename; + bool flush = true; + bool rotate = false; + microseconds rotation_interval; + microseconds rotation_limit; + }; + file_appender( const variant& args ); + ~file_appender(); + virtual void log( const log_message& m )override; + + private: + class impl; + fc::shared_ptr my; + }; +} // namespace fc + +#include +FC_REFLECT( fc::file_appender::config, + (format)(filename)(flush)(rotate)(rotation_interval)(rotation_limit) ) diff --git a/include/fc/log/gelf_appender.hpp b/include/fc/log/gelf_appender.hpp new file mode 100644 index 000000000..22283139c --- /dev/null +++ b/include/fc/log/gelf_appender.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +namespace fc +{ + // Log appender that sends log messages in JSON format over UDP + // https://www.graylog2.org/resources/gelf/specification + class gelf_appender : public appender + { + public: + struct config + { + string endpoint = "127.0.0.1:12201"; + string host = "fc"; // the name of the host, source or application that sent this message (just passed through to GELF server) + }; + + gelf_appender(const variant& args); + ~gelf_appender(); + virtual void log(const log_message& m) override; + + private: + class impl; + fc::shared_ptr my; + }; +} // namespace fc + +#include +FC_REFLECT(fc::gelf_appender::config, + (endpoint)(host)) diff --git a/include/fc/log/log_message.hpp b/include/fc/log/log_message.hpp new file mode 100644 index 000000000..1928a986a --- /dev/null +++ b/include/fc/log/log_message.hpp @@ -0,0 +1,162 @@ +#pragma once +/** + * @file log_message.hpp + * @brief Defines types and helper macros necessary for generating log messages. + */ +#include +#include +#include +#include + +namespace fc +{ + namespace detail + { + class log_context_impl; + class log_message_impl; + } + + /** + * Named scope for log_level enumeration. + */ + class log_level + { + public: + /** + * @brief Define's the various log levels for reporting. + * + * Each log level includes all higher levels such that + * Debug includes Error, but Error does not include Debug. + */ + enum values + { + all, + debug, + info, + warn, + error, + off + }; + log_level( values v = off ):value(v){} + explicit log_level( int v ):value( static_cast(v)){} + operator int()const { return value; } + values value; + }; + + void to_variant( log_level e, variant& v ); + void from_variant( const variant& e, log_level& ll ); + + /** + * @brief provides information about where and when a log message was generated. + * @ingroup AthenaSerializable + * + * @see FC_LOG_CONTEXT + */ + class log_context + { + public: + log_context(); + log_context( log_level ll, + const char* file, + uint64_t line, + const char* method ); + ~log_context(); + explicit log_context( const variant& v ); + variant to_variant()const; + + string get_file()const; + uint64_t get_line_number()const; + string get_method()const; + string get_thread_name()const; + string get_task_name()const; + string get_host_name()const; + time_point get_timestamp()const; + log_level get_log_level()const; + string get_context()const; + + void append_context( const fc::string& c ); + + string to_string()const; + private: + std::shared_ptr my; + }; + + void to_variant( const log_context& l, variant& v ); + void from_variant( const variant& l, log_context& c ); + + /** + * @brief aggregates a message along with the context and associated meta-information. + * @ingroup AthenaSerializable + * + * @note log_message has reference semantics, all copies refer to the same log message + * and the message is read-only after construction. + * + * When converted to JSON, log_message has the following form: + * @code + * { + * "context" : { ... }, + * "format" : "string with ${keys}", + * "data" : { "keys" : "values" } + * } + * @endcode + * + * @see FC_LOG_MESSAGE + */ + class log_message + { + public: + log_message(); + /** + * @param ctx - generally provided using the FC_LOG_CONTEXT(LEVEL) macro + */ + log_message( log_context ctx, std::string format, variant_object args = variant_object() ); + ~log_message(); + + log_message( const variant& v ); + variant to_variant()const; + + string get_message()const; + + log_context get_context()const; + string get_format()const; + variant_object get_data()const; + + private: + std::shared_ptr my; + }; + + void to_variant( const log_message& l, variant& v ); + void from_variant( const variant& l, log_message& c ); + + typedef std::vector log_messages; + + +} // namespace fc + +FC_REFLECT_TYPENAME( fc::log_message ); + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +/** + * @def FC_LOG_CONTEXT(LOG_LEVEL) + * @brief Automatically captures the File, Line, and Method names and passes them to + * the constructor of fc::log_context along with LOG_LEVEL + * @param LOG_LEVEL - a valid log_level::Enum name. + */ +#define FC_LOG_CONTEXT(LOG_LEVEL) \ + fc::log_context( fc::log_level::LOG_LEVEL, __FILE__, __LINE__, __func__ ) + +/** + * @def FC_LOG_MESSAGE(LOG_LEVEL,FORMAT,...) + * + * @brief A helper method for generating log messages. + * + * @param LOG_LEVEL a valid log_level::Enum name to be passed to the log_context + * @param FORMAT A const char* string containing zero or more references to keys as "${key}" + * @param ... A set of key/value pairs denoted as ("key",val)("key2",val2)... + */ +#define FC_LOG_MESSAGE( LOG_LEVEL, FORMAT, ... ) \ + fc::log_message( FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::mutable_variant_object()__VA_ARGS__ ) + diff --git a/include/fc/log/logger.hpp b/include/fc/log/logger.hpp new file mode 100644 index 000000000..9005d7524 --- /dev/null +++ b/include/fc/log/logger.hpp @@ -0,0 +1,179 @@ +#pragma once +#include +#include +#include +#include + +namespace fc +{ + + class appender; + + /** + * + * + @code + void my_class::func() + { + fc_dlog( my_class_logger, "Format four: ${arg} five: ${five}", ("arg",4)("five",5) ); + } + @endcode + */ + class logger + { + public: + static logger get( const fc::string& name = "default"); + + logger(); + logger( const string& name, const logger& parent = nullptr ); + logger( std::nullptr_t ); + logger( const logger& c ); + logger( logger&& c ); + ~logger(); + logger& operator=(const logger&); + logger& operator=(logger&&); + friend bool operator==( const logger&, nullptr_t ); + friend bool operator!=( const logger&, nullptr_t ); + + logger& set_log_level( log_level e ); + log_level get_log_level()const; + logger& set_parent( const logger& l ); + logger get_parent()const; + + void set_name( const fc::string& n ); + const fc::string& name()const; + + void add_appender( const fc::shared_ptr& a ); + std::vector > get_appenders()const; + void remove_appender( const fc::shared_ptr& a ); + + bool is_enabled( log_level e )const; + void log( log_message m ); + + private: + class impl; + fc::shared_ptr my; + }; + +} // namespace fc + +#ifndef DEFAULT_LOGGER +#define DEFAULT_LOGGER +#endif + +// suppress warning "conditional expression is constant" in the while(0) for visual c++ +// http://cnicholson.net/2009/03/stupid-c-tricks-dowhile0-and-c4127/ +#define FC_MULTILINE_MACRO_BEGIN do { +#ifdef _MSC_VER +# define FC_MULTILINE_MACRO_END \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + } while (0) \ + __pragma(warning(pop)) +#else +# define FC_MULTILINE_MACRO_END } while (0) +#endif + +#define fc_dlog( LOGGER, FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (LOGGER).is_enabled( fc::log_level::debug ) ) \ + (LOGGER).log( FC_LOG_MESSAGE( debug, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#define fc_ilog( LOGGER, FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (LOGGER).is_enabled( fc::log_level::info ) ) \ + (LOGGER).log( FC_LOG_MESSAGE( info, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#define fc_wlog( LOGGER, FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (LOGGER).is_enabled( fc::log_level::warn ) ) \ + (LOGGER).log( FC_LOG_MESSAGE( warn, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#define fc_elog( LOGGER, FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (LOGGER).is_enabled( fc::log_level::error ) ) \ + (LOGGER).log( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#define dlog( FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (fc::logger::get(DEFAULT_LOGGER)).is_enabled( fc::log_level::debug ) ) \ + (fc::logger::get(DEFAULT_LOGGER)).log( FC_LOG_MESSAGE( debug, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +/** + * Sends the log message to a special 'user' log stream designed for messages that + * the end user may like to see. + */ +#define ulog( FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (fc::logger::get("user")).is_enabled( fc::log_level::debug ) ) \ + (fc::logger::get("user")).log( FC_LOG_MESSAGE( debug, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + + +#define ilog( FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (fc::logger::get(DEFAULT_LOGGER)).is_enabled( fc::log_level::info ) ) \ + (fc::logger::get(DEFAULT_LOGGER)).log( FC_LOG_MESSAGE( info, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#define wlog( FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (fc::logger::get(DEFAULT_LOGGER)).is_enabled( fc::log_level::warn ) ) \ + (fc::logger::get(DEFAULT_LOGGER)).log( FC_LOG_MESSAGE( warn, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#define elog( FORMAT, ... ) \ + FC_MULTILINE_MACRO_BEGIN \ + if( (fc::logger::get(DEFAULT_LOGGER)).is_enabled( fc::log_level::error ) ) \ + (fc::logger::get(DEFAULT_LOGGER)).log( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ); \ + FC_MULTILINE_MACRO_END + +#include +#include +#include +#include +#include +#include + + +#define FC_FORMAT_ARG(r, unused, base) \ + BOOST_PP_STRINGIZE(base) ": ${" BOOST_PP_STRINGIZE( base ) "} " + +#define FC_FORMAT_ARGS(r, unused, base) \ + BOOST_PP_LPAREN() BOOST_PP_STRINGIZE(base),fc::variant(base) BOOST_PP_RPAREN() + +#define FC_FORMAT( SEQ )\ + BOOST_PP_SEQ_FOR_EACH( FC_FORMAT_ARG, v, SEQ ) + +// takes a ... instead of a SEQ arg because it can be called with an empty SEQ +// from FC_CAPTURE_AND_THROW() +#define FC_FORMAT_ARG_PARAMS( ... )\ + BOOST_PP_SEQ_FOR_EACH( FC_FORMAT_ARGS, v, __VA_ARGS__ ) + +#define idump( SEQ ) \ + ilog( FC_FORMAT(SEQ), FC_FORMAT_ARG_PARAMS(SEQ) ) +#define wdump( SEQ ) \ + wlog( FC_FORMAT(SEQ), FC_FORMAT_ARG_PARAMS(SEQ) ) +#define edump( SEQ ) \ + elog( FC_FORMAT(SEQ), FC_FORMAT_ARG_PARAMS(SEQ) ) + +// this disables all normal logging statements -- not something you'd normally want to do, +// but it's useful if you're benchmarking something and suspect logging is causing +// a slowdown. +#ifdef FC_DISABLE_LOGGING +# undef ulog +# define ulog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END +# undef elog +# define elog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END +# undef wlog +# define wlog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END +# undef ilog +# define ilog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END +# undef dlog +# define dlog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END +#endif \ No newline at end of file diff --git a/include/fc/logger_config.hpp b/include/fc/log/logger_config.hpp similarity index 55% rename from include/fc/logger_config.hpp rename to include/fc/log/logger_config.hpp index a089052de..f2fc1e220 100644 --- a/include/fc/logger_config.hpp +++ b/include/fc/log/logger_config.hpp @@ -1,43 +1,49 @@ #pragma once -#include +#include namespace fc { class path; struct appender_config { - appender_config(const fc::string& n="",const fc::string& t="", const value& a=value()) - :name(n),type(t),args(a),enabled(true){} - string name; - string type; - value args; - bool enabled; + appender_config(const string& name = "", + const string& type = "", + variant args = variant()) : + name(name), + type(type), + args(fc::move(args)), + enabled(true) + {} + string name; + string type; + variant args; + bool enabled; }; struct logger_config { - logger_config(const fc::string& n=""):name(n),enabled(true),additivity(false){} + logger_config(const fc::string& name = ""):name(name),enabled(true),additivity(false){} string name; ostring parent; /// if not set, then parents level is used. - fc::optional level; + fc::optional level; bool enabled; /// if any appenders are sepecified, then parent's appenders are not set. bool additivity; - fc::vector appenders; + std::vector appenders; logger_config& add_appender( const string& s ); }; struct logging_config { static logging_config default_config(); - fc::vector includes; - fc::vector appenders; - fc::vector loggers; + std::vector includes; + std::vector appenders; + std::vector loggers; }; void configure_logging( const fc::path& log_config ); bool configure_logging( const logging_config& l ); } -#include +#include FC_REFLECT( fc::appender_config, (name)(type)(args)(enabled) ) FC_REFLECT( fc::logger_config, (name)(parent)(level)(enabled)(additivity)(appenders) ) FC_REFLECT( fc::logging_config, (includes)(appenders)(loggers) ) diff --git a/include/fc/logger.hpp b/include/fc/logger.hpp deleted file mode 100644 index 4b2fafcec..000000000 --- a/include/fc/logger.hpp +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once -#include -#include -#include -#include - - -namespace fc { - - struct log_level { - enum type { - all, trace, debug, info, warn, error, fatal, off - }; - }; - - struct log_message { - log_message(log_level::type, const string& file, int line, const string& func, const string& format ); - log_message(); - - otime_point when; - log_level::type level; - ostring context; - ostring thread; - ostring fiber; - ostring host; - string file; - int line; - string method; - string format; - value args; - ovalue meta; - - // key based args - template - log_message& operator()( const string& arg, const T& v ) { - return (*this)(arg,value(v)); - } - - log_message& operator()( const string& arg, value&& v ); - log_message& operator()( const string& arg, const value& v ); - // position based args... - log_message& operator()( value&& v ); - log_message& operator()( const value& v ); - }; - - class appender; - - /** - * - * - @code - void my_class::func() - { - fc_dlog( my_class_logger, "Format four: ${arg} five: ${five}", ("arg",4)("five",5) ); - } - @endcode - */ - class logger { - public: - static logger get( const fc::string& name = "default"); - - logger(); - logger( const string& name, const logger& parent = nullptr ); - logger( std::nullptr_t ); - logger( const logger& c ); - logger( logger&& c ); - ~logger(); - logger& operator=(const logger&); - logger& operator=(logger&&); - friend bool operator==( const logger&, std::nullptr_t ); - friend bool operator!=( const logger&, std::nullptr_t ); - - logger& set_log_level( log_level::type e ); - log_level::type get_log_level()const; - logger& set_parent( const logger& l ); - logger get_parent()const; - - void set_name( const fc::string& n ); - const fc::string& name()const; - - void add_appender( const fc::shared_ptr& a ); - - - bool is_enabled( log_level::type e )const; - void log( log_message m ); - - private: - class impl; - fc::shared_ptr my; - }; - - /** - * This helper class is used to automatically print a log message - * once upon construction, and again upon destruction and is therefore - * helpful in catching scope changes. - struct tracer { - tracer( const logger::ptr& lgr ); - ~tracer(); - - void set_message( log_message&& ms g); - - private: - logger::ptr logger; - log_message msg; - }; - */ - -} // namespace fc - -#include -FC_REFLECT( fc::log_message, (when)(level)(context)(thread)(fiber)(host)(method)(file)(line)(format)(args)(meta) ) -FC_REFLECT_ENUM( fc::log_level::type, (all)(trace)(debug)(info)(warn)(error)(fatal)(off) ) - -#define fc_scope_log( LOGGER, FORMAT, ... ) \ - fc::tracer __tracer; \ - if( (LOGGER).is_enabled( fc::log_level::trace ) ) { \ - __tracer.set_message( fc::log_message( fc::log_level::trace, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\ - } - -#define fc_dlog( LOGGER, FORMAT, ... ) \ - if( (LOGGER).is_enabled( fc::log_level::debug ) ) { \ - (LOGGER).log( fc::log_message( fc::log_level::debug, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\ - } - -#define fc_ilog( LOGGER, FORMAT, ... ) \ - if( (LOGGER).is_enabled( fc::log_level::info ) ) { \ - (LOGGER).log( fc::log_message( fc::log_level::info, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\ - } - -#define fc_wlog( LOGGER, FORMAT, ... ) \ - if( (LOGGER).is_enabled( fc::log_level::warn ) ) { \ - (LOGGER).log( fc::log_message( fc::log_level::warn, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\ - } - -#define fc_elog( LOGGER, FORMAT, ... ) \ - if( (LOGGER).is_enabled( fc::log_level::error ) ) { \ - (LOGGER).log( fc::log_message( fc::log_level::error, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\ - } - -#define fc_flog( LOGGER, FORMAT, ... ) \ - if( (LOGGER).is_enabled( fc::log_level::fatal ) ) { \ - (LOGGER).log( fc::log_message( fc::log_level::fatal, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\ - } - diff --git a/include/fc/make_fused.hpp b/include/fc/make_fused.hpp index cc82ff8a7..acbf90107 100644 --- a/include/fc/make_fused.hpp +++ b/include/fc/make_fused.hpp @@ -4,23 +4,23 @@ namespace fc { template - fc::function > make_fused( const fc::function& f ) { + std::function > make_fused( const std::function& f ) { return [=]( fc::tuple<> ){ return f(); }; } template - fc::function) > make_fused( const fc::function& f ) { + std::function) > make_fused( const std::function& f ) { return [f]( fc::tuple t){ return f(t.a); }; } template - fc::function) > make_fused( const fc::function& f ) { + std::function) > make_fused( const std::function& f ) { return [f]( fc::tuple t){ return f(t.a,t.b); }; } template - fc::function) > make_fused( const fc::function& f ) { + std::function) > make_fused( const std::function& f ) { return [f]( fc::tuple t){ return f(t.a,t.b,t.c); }; } template - fc::function) > make_fused( const fc::function& f ) { + std::function) > make_fused( const std::function& f ) { return [f]( fc::tuple t){ return f(t.a,t.b,t.c,t.d); }; } } diff --git a/include/fc/map.hpp b/include/fc/map.hpp deleted file mode 100644 index 1cdb7455e..000000000 --- a/include/fc/map.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _FC_MAP_HPP_ -namespace fc { - - namespace detail { - class map_impl { - public: - - - }; - } - template - class map : public map_impl { - - - }; - - -} diff --git a/include/fc/network/gntp.hpp b/include/fc/network/gntp.hpp new file mode 100644 index 000000000..fcfa6563c --- /dev/null +++ b/include/fc/network/gntp.hpp @@ -0,0 +1,57 @@ +#pragma once +#include +#include +#include +#include + +#include +#include + +namespace fc +{ + namespace detail { + class gntp_icon_impl; + } + class gntp_notifier; + + class gntp_icon { + public: + gntp_icon(const char* buffer, size_t length); + ~gntp_icon(); + private: + std::unique_ptr my; + friend class gntp_notifier; + }; + typedef std::shared_ptr gntp_icon_ptr; + + class gntp_notification_type { + public: + std::string name; + std::string display_name; + bool enabled; + gntp_icon_ptr icon; + }; + typedef std::vector gntp_notification_type_list; + + namespace detail { + class gntp_notifier_impl; + } + + typedef uint160_t gntp_guid; + + class gntp_notifier { + public: + gntp_notifier(const std::string& host_to_notify = "127.0.0.1", uint16_t port = 23053, + const optional& password = optional()); + ~gntp_notifier(); + void set_application_name(std::string application_name); + void set_application_icon(const gntp_icon_ptr& icon); + void register_notifications(); + gntp_guid send_notification(std::string name, std::string title, std::string text, const gntp_icon_ptr& icon = gntp_icon_ptr(), optional coalescingId = optional()); + void add_notification_type(const gntp_notification_type& notificationType); + private: + std::unique_ptr my; + }; + + +} // namespace fc diff --git a/include/fc/network/http/connection.hpp b/include/fc/network/http/connection.hpp new file mode 100644 index 000000000..3c8de77b3 --- /dev/null +++ b/include/fc/network/http/connection.hpp @@ -0,0 +1,81 @@ +#pragma once +#include +#include +#include + +namespace fc { + namespace ip { class endpoint; } + class tcp_socket; + + namespace http { + + struct header + { + header( fc::string k, fc::string v ) + :key(fc::move(k)),val(fc::move(v)){} + header(){} + fc::string key; + fc::string val; + }; + + typedef std::vector
headers; + + struct reply + { + enum status_code { + OK = 200, + RecordCreated = 201, + BadRequest = 400, + NotAuthorized = 401, + NotFound = 404, + Found = 302, + InternalServerError = 500 + }; + reply( status_code c = OK):status(c){} + int status; + std::vector
headers; + std::vector body; + }; + + struct request + { + fc::string get_header( const fc::string& key )const; + fc::string method; + fc::string domain; + fc::string path; + std::vector
headers; + std::vector body; + }; + + std::vector
parse_urlencoded_params( const fc::string& f ); + + /** + * Connections have reference semantics, all copies refer to the same + * underlying socket. + */ + class connection + { + public: + connection(); + ~connection(); + // used for clients + void connect_to( const fc::ip::endpoint& ep ); + http::reply request( const fc::string& method, const fc::string& url, const fc::string& body = std::string(), const headers& = headers()); + + // used for servers + fc::tcp_socket& get_socket()const; + + http::request read_request()const; + + class impl; + private: + std::unique_ptr my; + }; + + typedef std::shared_ptr connection_ptr; + +} } // fc::http + +#include +FC_REFLECT( fc::http::header, (key)(val) ) + diff --git a/include/fc/http/server.hpp b/include/fc/network/http/server.hpp similarity index 81% rename from include/fc/http/server.hpp rename to include/fc/network/http/server.hpp index e8764f1d3..b5eda8bf4 100644 --- a/include/fc/http/server.hpp +++ b/include/fc/network/http/server.hpp @@ -1,7 +1,8 @@ #pragma once -#include +#include #include #include +#include namespace fc { namespace http { @@ -11,18 +12,18 @@ namespace fc { namespace http { * function for every http request. * */ - class server { + class server + { public: server(); server( uint16_t port ); - server( const server& s ); server( server&& s ); ~server(); - server& operator=(const server& s); server& operator=(server&& s); - class response { + class response + { public: class impl; @@ -44,7 +45,8 @@ namespace fc { namespace http { fc::shared_ptr my; }; - void listen( uint16_t p ); + void listen( const fc::ip::endpoint& p ); + fc::ip::endpoint get_local_endpoint() const; /** * Set the callback to be called for every http request made. @@ -53,8 +55,8 @@ namespace fc { namespace http { private: class impl; - fc::shared_ptr my; - + std::unique_ptr my; }; + typedef std::shared_ptr server_ptr; } } diff --git a/include/fc/network/http/websocket.hpp b/include/fc/network/http/websocket.hpp new file mode 100644 index 000000000..bcb022e0b --- /dev/null +++ b/include/fc/network/http/websocket.hpp @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace fc { namespace http { + namespace detail { + class abstract_websocket_server; + class websocket_client_impl; + class websocket_tls_client_impl; + } // namespace detail; + + class websocket_connection + { + public: + virtual ~websocket_connection(){} + virtual void send_message( const std::string& message ) = 0; + virtual void close( int64_t code, const std::string& reason ){}; + void on_message( const std::string& message ) { _on_message(message); } + string on_http( const std::string& message ) { return _on_http(message); } + + void on_message_handler( const std::function& h ) { _on_message = h; } + void on_http_handler( const std::function& h ) { _on_http = h; } + + void set_session_data( fc::any d ){ _session_data = std::move(d); } + fc::any& get_session_data() { return _session_data; } + + fc::signal closed; + private: + fc::any _session_data; + std::function _on_message; + std::function _on_http; + }; + typedef std::shared_ptr websocket_connection_ptr; + + typedef std::function on_connection_handler; + + class websocket_server + { + public: + websocket_server(bool enable_permessage_deflate = true); + ~websocket_server(); + + void on_connection( const on_connection_handler& handler); + void listen( uint16_t port ); + void listen( const fc::ip::endpoint& ep ); + void start_accept(); + + private: + std::unique_ptr my; + }; + + + class websocket_tls_server + { + public: + websocket_tls_server(const std::string& server_pem = std::string(), + const std::string& ssl_password = std::string(), + bool enable_permessage_deflate = false); + ~websocket_tls_server(); + + void on_connection( const on_connection_handler& handler); + void listen( uint16_t port ); + void listen( const fc::ip::endpoint& ep ); + void start_accept(); + + private: + std::unique_ptr my; + }; + + class websocket_client + { + public: + websocket_client(); + ~websocket_client(); + + websocket_connection_ptr connect( const std::string& uri ); + websocket_connection_ptr secure_connect( const std::string& uri ); + private: + std::unique_ptr my; + std::unique_ptr smy; + }; + class websocket_tls_client + { + public: + websocket_tls_client(); + ~websocket_tls_client(); + + websocket_connection_ptr connect( const std::string& uri ); + private: + std::unique_ptr my; + }; + +} } diff --git a/include/fc/network/ip.hpp b/include/fc/network/ip.hpp new file mode 100644 index 000000000..5a1bd0c98 --- /dev/null +++ b/include/fc/network/ip.hpp @@ -0,0 +1,128 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { + + namespace ip { + class address { + public: + address( uint32_t _ip = 0 ); + address( const fc::string& s ); + + address& operator=( const fc::string& s ); + operator fc::string()const; + operator uint32_t()const; + + friend bool operator==( const address& a, const address& b ); + friend bool operator!=( const address& a, const address& b ); + + /** + * @return true if the ip is in the following ranges: + * + * 10.0.0.0 to 10.255.255.255 + * 172.16.0.0 to 172.31.255.255 + * 192.168.0.0 to 192.168.255.255 + * 169.254.0.0 to 169.254.255.255 + * + */ + bool is_private_address()const; + /** + * 224.0.0.0 to 239.255.255.255 + */ + bool is_multicast_address()const; + + /** !private & !multicast */ + bool is_public_address()const; + private: + uint32_t _ip; + }; + + class endpoint { + public: + endpoint(); + endpoint( const address& i, uint16_t p = 0); + + /** Converts "IP:PORT" to an endpoint */ + static endpoint from_string( const string& s ); + /** returns "IP:PORT" */ + operator string()const; + + void set_port(uint16_t p ) { _port = p; } + uint16_t port()const; + const address& get_address()const; + + friend bool operator==( const endpoint& a, const endpoint& b ); + friend bool operator!=( const endpoint& a, const endpoint& b ); + friend bool operator< ( const endpoint& a, const endpoint& b ); + + private: + /** + * The compiler pads endpoint to a full 8 bytes, so while + * a port number is limited in range to 16 bits, we specify + * a full 32 bits so that memcmp can be used with sizeof(), + * otherwise 2 bytes will be 'random' and you do not know + * where they are stored. + */ + uint32_t _port; + address _ip; + }; + + } + class variant; + void to_variant( const ip::endpoint& var, variant& vo ); + void from_variant( const variant& var, ip::endpoint& vo ); + + void to_variant( const ip::address& var, variant& vo ); + void from_variant( const variant& var, ip::address& vo ); + + + namespace raw + { + template + inline void pack( Stream& s, const ip::address& v ) + { + fc::raw::pack( s, uint32_t(v) ); + } + template + inline void unpack( Stream& s, ip::address& v ) + { + uint32_t _ip; + fc::raw::unpack( s, _ip ); + v = ip::address(_ip); + } + + template + inline void pack( Stream& s, const ip::endpoint& v ) + { + fc::raw::pack( s, v.get_address() ); + fc::raw::pack( s, v.port() ); + } + template + inline void unpack( Stream& s, ip::endpoint& v ) + { + ip::address a; + uint16_t p; + fc::raw::unpack( s, a ); + fc::raw::unpack( s, p ); + v = ip::endpoint(a,p); + } + + } +} // namespace fc +FC_REFLECT_TYPENAME( fc::ip::address ) +FC_REFLECT_TYPENAME( fc::ip::endpoint ) +namespace std +{ + template<> + struct hash + { + size_t operator()( const fc::ip::endpoint& e )const + { + return fc::city_hash_size_t( (char*)&e, sizeof(e) ); + } + }; +} diff --git a/include/fc/network/ntp.hpp b/include/fc/network/ntp.hpp new file mode 100644 index 000000000..6067b3cae --- /dev/null +++ b/include/fc/network/ntp.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include + + +namespace fc { + + namespace detail { class ntp_impl; } + + class ntp + { + public: + ntp(); + ~ntp(); + + void add_server( const std::string& hostname, uint16_t port = 123 ); + void set_request_interval( uint32_t interval_sec ); + void request_now(); + optional get_time()const; + + private: + std::unique_ptr my; + }; + +} // namespace fc diff --git a/include/fc/network/rate_limiting.hpp b/include/fc/network/rate_limiting.hpp new file mode 100644 index 000000000..4b798d537 --- /dev/null +++ b/include/fc/network/rate_limiting.hpp @@ -0,0 +1,41 @@ +#pragma once +#include + +#include + +#include + +namespace fc +{ + namespace detail + { + class rate_limiting_group_impl; + } + + class tcp_socket; + + class rate_limiting_group + { + public: + rate_limiting_group(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second, uint32_t burstiness_in_seconds = 1); + ~rate_limiting_group(); + + void set_upload_limit(uint32_t upload_bytes_per_second); + uint32_t get_upload_limit() const; + + void set_download_limit(uint32_t download_bytes_per_second); + uint32_t get_download_limit() const; + + uint32_t get_actual_upload_rate() const; + uint32_t get_actual_download_rate() const; + void set_actual_rate_time_constant(microseconds time_constant); + + void add_tcp_socket(tcp_socket* tcp_socket_to_limit); + void remove_tcp_socket(tcp_socket* tcp_socket_to_stop_limiting); + private: + std::unique_ptr my; + }; + typedef std::shared_ptr rate_limiting_group_ptr; + +} // namesapce fc + diff --git a/include/fc/network/resolve.hpp b/include/fc/network/resolve.hpp new file mode 100644 index 000000000..f6bf6ec7e --- /dev/null +++ b/include/fc/network/resolve.hpp @@ -0,0 +1,8 @@ +#pragma once +#include +#include + +namespace fc +{ + std::vector resolve( const std::string& host, uint16_t port ); +} diff --git a/include/fc/network/tcp_socket.hpp b/include/fc/network/tcp_socket.hpp new file mode 100644 index 000000000..4c2e7d60d --- /dev/null +++ b/include/fc/network/tcp_socket.hpp @@ -0,0 +1,86 @@ +#pragma once +#include +#include +#include +#include + +namespace fc { + namespace ip { class endpoint; } + + class tcp_socket_io_hooks; + + class tcp_socket : public virtual iostream + { + public: + tcp_socket(); + ~tcp_socket(); + + void connect_to( const fc::ip::endpoint& remote_endpoint ); + void bind( const fc::ip::endpoint& local_endpoint ); + void enable_keep_alives(const fc::microseconds& interval); + void set_io_hooks(tcp_socket_io_hooks* new_hooks); + void set_reuse_address(bool enable = true); // set SO_REUSEADDR + fc::ip::endpoint remote_endpoint() const; + fc::ip::endpoint local_endpoint() const; + + using istream::get; + void get( char& c ) + { + read( &c, 1 ); + } + + + /// istream interface + /// @{ + virtual size_t readsome( char* buffer, size_t max ); + virtual size_t readsome(const std::shared_ptr& buffer, size_t max, size_t offset); + virtual bool eof()const; + /// @} + + /// ostream interface + /// @{ + virtual size_t writesome( const char* buffer, size_t len ); + virtual size_t writesome(const std::shared_ptr& buffer, size_t len, size_t offset); + virtual void flush(); + virtual void close(); + /// @} + + void open(); + bool is_open()const; + + private: + friend class tcp_server; + class impl; + #ifdef _WIN64 + fc::fwd my; + #else + fc::fwd my; + #endif + }; + typedef std::shared_ptr tcp_socket_ptr; + + + class tcp_server + { + public: + tcp_server(); + ~tcp_server(); + + void close(); + void accept( tcp_socket& s ); + void set_reuse_address(bool enable = true); // set SO_REUSEADDR, call before listen + void listen( uint16_t port ); + void listen( const fc::ip::endpoint& ep ); + fc::ip::endpoint get_local_endpoint() const; + uint16_t get_port()const; + private: + // non copyable + tcp_server( const tcp_server& ); + tcp_server& operator=(const tcp_server& s ); + + class impl; + impl* my; + }; + +} // namesapce fc + diff --git a/include/fc/network/tcp_socket_io_hooks.hpp b/include/fc/network/tcp_socket_io_hooks.hpp new file mode 100644 index 000000000..0d373a786 --- /dev/null +++ b/include/fc/network/tcp_socket_io_hooks.hpp @@ -0,0 +1,15 @@ +#include +#include + +namespace fc +{ + class tcp_socket_io_hooks + { + public: + virtual ~tcp_socket_io_hooks() {} + virtual size_t readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length) = 0; + virtual size_t readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) = 0; + virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length) = 0; + virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) = 0; + }; +} // namesapce fc diff --git a/include/fc/udp_socket.hpp b/include/fc/network/udp_socket.hpp similarity index 82% rename from include/fc/udp_socket.hpp rename to include/fc/network/udp_socket.hpp index e5f0d5ecb..65d1107f6 100644 --- a/include/fc/udp_socket.hpp +++ b/include/fc/network/udp_socket.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace fc { namespace ip { @@ -22,7 +23,9 @@ namespace fc { void set_receive_buffer_size( size_t s ); void bind( const fc::ip::endpoint& ); size_t receive_from( char* b, size_t l, fc::ip::endpoint& from ); + size_t receive_from( const std::shared_ptr& b, size_t l, fc::ip::endpoint& from ); size_t send_to( const char* b, size_t l, const fc::ip::endpoint& to ); + size_t send_to( const std::shared_ptr& b, size_t l, const fc::ip::endpoint& to ); void close(); void set_multicast_enable_loopback( bool ); diff --git a/include/fc/network/udt_socket.hpp b/include/fc/network/udt_socket.hpp new file mode 100644 index 000000000..1210427a9 --- /dev/null +++ b/include/fc/network/udt_socket.hpp @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { + namespace ip { class endpoint; } + + class udt_socket : public virtual iostream, public noncopyable + { + public: + udt_socket(); + ~udt_socket(); + + void bind( const fc::ip::endpoint& local_endpoint ); + void connect_to( const fc::ip::endpoint& remote_endpoint ); + + fc::ip::endpoint remote_endpoint() const; + fc::ip::endpoint local_endpoint() const; + + using istream::get; + void get( char& c ) + { + read( &c, 1 ); + } + + + /// istream interface + /// @{ + virtual size_t readsome( char* buffer, size_t max ); + virtual size_t readsome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual bool eof()const; + /// @} + + /// ostream interface + /// @{ + virtual size_t writesome( const char* buffer, size_t len ); + virtual size_t writesome( const std::shared_ptr& buf, size_t len, size_t offset ); + virtual void flush(); + virtual void close(); + /// @} + + void open(); + bool is_open()const; + + private: + friend class udt_server; + int _udt_socket_id; + }; + typedef std::shared_ptr udt_socket_ptr; + + class udt_server : public noncopyable + { + public: + udt_server(); + ~udt_server(); + + void close(); + void accept( udt_socket& s ); + + void listen( const fc::ip::endpoint& ep ); + fc::ip::endpoint local_endpoint() const; + + private: + int _udt_socket_id; + }; + +} // fc diff --git a/include/fc/network/url.hpp b/include/fc/network/url.hpp new file mode 100644 index 000000000..6f8c7451f --- /dev/null +++ b/include/fc/network/url.hpp @@ -0,0 +1,106 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace fc { + + typedef fc::optional ostring; + typedef fc::optional opath; + typedef fc::optional ovariant_object; + + namespace detail { class url_impl; } + + class mutable_url; + + /** + * Used to pass an immutable URL and + * query its parts. + */ + class url + { + public: + url(); + explicit url( const string& u ); + url( const url& c ); + url( url&& c ); + url( mutable_url&& c ); + url( const mutable_url& c ); + ~url(); + + url& operator=( const url& c ); + url& operator=( url&& c ); + + url& operator=( const mutable_url& c ); + url& operator=( mutable_url&& c ); + + bool operator==( const url& cmp )const; + + operator string()const; + + //// file, ssh, tcp, http, ssl, etc... + string proto()const; + ostring host()const; + ostring user()const; + ostring pass()const; + opath path()const; + ovariant_object args()const; + fc::optional port()const; + + private: + friend class mutable_url; + std::shared_ptr my; + }; + + void to_variant( const url& u, fc::variant& v ); + void from_variant( const fc::variant& v, url& u ); + + /** + * Used to create / manipulate a URL + */ + class mutable_url + { + public: + mutable_url(); + explicit mutable_url( const string& mutable_url ); + mutable_url( const mutable_url& c ); + mutable_url( const url& c ); + mutable_url( mutable_url&& c ); + ~mutable_url(); + + mutable_url& operator=( const url& c ); + mutable_url& operator=( const mutable_url& c ); + mutable_url& operator=( mutable_url&& c ); + + bool operator==( const mutable_url& cmp )const; + bool operator==( const url& cmp )const; + + operator string()const; + + //// file, ssh, tcp, http, ssl, etc... + string proto()const; + ostring host()const; + ostring user()const; + ostring pass()const; + opath path()const; + ovariant_object args()const; + fc::optional port()const; + + void set_proto( string ); + void set_host( string ); + void set_user( string ); + void set_pass( string ); + void set_path( fc::path p ); + void set_args( variant_object ); + void set_port( uint16_t ); + + private: + friend class url; + std::unique_ptr my; + }; + +} // namespace fc + diff --git a/include/fc/noncopyable.hpp b/include/fc/noncopyable.hpp new file mode 100644 index 000000000..87fad6b03 --- /dev/null +++ b/include/fc/noncopyable.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace fc +{ + class noncopyable + { + public: + noncopyable(){} + private: + noncopyable( const noncopyable& ) = delete; + noncopyable& operator=( const noncopyable& ) = delete; + }; +} + diff --git a/include/fc/numeric_cast.hpp b/include/fc/numeric_cast.hpp deleted file mode 100644 index 3fdb7664c..000000000 --- a/include/fc/numeric_cast.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -namespace fc { - template - T numeric_cast( const R& v ) { - // TODO: do something smarter here, check ranges, etc - return static_cast(v); - } -} diff --git a/include/fc/optional.hpp b/include/fc/optional.hpp index e32ff746c..bb760a58d 100644 --- a/include/fc/optional.hpp +++ b/include/fc/optional.hpp @@ -1,7 +1,16 @@ #pragma once #include +#include + namespace fc { +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4521) /* multiple copy ctors */ +# pragma warning(disable:4522) /* multiple assignment operators */ +#endif + bool assert_optional(bool is_valid ); // defined in exception.cpp + /** * @brief provides stack-based nullable value similar to boost::optional * @@ -9,68 +18,227 @@ namespace fc { * fc::optional adds less than 400. */ template - class optional { + class optional + { public: - optional():_valid(0){} - ~optional(){ if( _valid ) (**this).~T(); } + typedef T value_type; + + optional():_valid(false){} + ~optional(){ reset(); } + + optional( optional& o ) + :_valid(false) + { + if( o._valid ) new (ptr()) T( *o ); + _valid = o._valid; + } optional( const optional& o ) - :_valid(false) { - if( o._valid ) new (&**this) T( *o ); + :_valid(false) + { + if( o._valid ) new (ptr()) T( *o ); _valid = o._valid; } optional( optional&& o ) - :_valid(false) { - if( o._valid ) new (&**this) T( fc::move(*o) ); + :_valid(false) + { + if( o._valid ) new (ptr()) T( fc::move(*o) ); + _valid = o._valid; + o.reset(); + } + + template + optional( const optional& o ) + :_valid(false) + { + if( o._valid ) new (ptr()) T( *o ); + _valid = o._valid; + } + + template + optional( optional& o ) + :_valid(false) + { + if( o._valid ) + { + new (ptr()) T( *o ); + } _valid = o._valid; } + template + optional( optional&& o ) + :_valid(false) + { + if( o._valid ) new (ptr()) T( fc::move(*o) ); + _valid = o._valid; + o.reset(); + } + template optional( U&& u ) - :_valid(false) { - new (&**this) T( fc::forward(u) ); + :_valid(true) + { + new ((char*)ptr()) T( fc::forward(u) ); + } + + template + optional& operator=( U&& u ) + { + reset(); + new (ptr()) T( fc::forward(u) ); _valid = true; + return *this; } template - optional& operator=( U&& u ) { - if( !_valid ) { - new (&**this) T( fc::forward(u) ); - _valid = true; - } else { - **this = fc::forward(u); + optional& operator=( optional& o ) { + if (this != &o) { + if( _valid && o._valid ) { + ref() = *o; + } else if( !_valid && o._valid ) { + new (ptr()) T( *o ); + _valid = true; + } else if (_valid) { + reset(); + } + } + return *this; + } + template + optional& operator=( const optional& o ) { + if (this != &o) { + if( _valid && o._valid ) { + ref() = *o; + } else if( !_valid && o._valid ) { + new (ptr()) T( *o ); + _valid = true; + } else if (_valid) { + reset(); + } + } + return *this; + } + + optional& operator=( optional& o ) { + if (this != &o) { + if( _valid && o._valid ) { + ref() = *o; + } else if( !_valid && o._valid ) { + new (ptr()) T( *o ); + _valid = true; + } else if (_valid) { + reset(); + } } return *this; } optional& operator=( const optional& o ) { - if( _valid && o._valid ) { **this = *o; } - else if( !_valid && o._valid ) { - *this = *o; - } // else !_valid && !o._valid == same! + if (this != &o) { + if( _valid && o._valid ) { + ref() = *o; + } else if( !_valid && o._valid ) { + new (ptr()) T( *o ); + _valid = true; + } else if (_valid) { + reset(); + } + } return *this; } - optional& operator=( optional&& o ) { - if( _valid && o._valid ) { **this = fc::move(*o); } - else if( !_valid && o._valid ) { - *this = fc::move(*o); - } + + template + optional& operator=( optional&& o ) + { + if (this != &o) + { + if( _valid && o._valid ) + { + ref() = fc::move(*o); + o.reset(); + } else if ( !_valid && o._valid ) { + *this = fc::move(*o); + } else if (_valid) { + reset(); + } + } + return *this; + } + + optional& operator=( optional&& o ) + { + if (this != &o) + { + if( _valid && o._valid ) + { + ref() = fc::move(*o); + o.reset(); + } else if ( !_valid && o._valid ) { + *this = fc::move(*o); + } else if (_valid) { + reset(); + } + } return *this; } + bool valid()const { return _valid; } bool operator!()const { return !_valid; } - operator bool()const { return _valid; } - T& operator*() { void* v = &_value[0]; return *static_cast(v); } - const T& operator*()const { const void* v = &_value[0]; return *static_cast(v); } + // this operation is not safe and can result in unintential + // casts and comparisons, use valid() or !! + explicit operator bool()const { return _valid; } - T* operator->() { void* v = &_value[0]; return static_cast(v); } - const T* operator->()const { const void* v = &_value[0]; return static_cast(v); } + T& operator*() { assert(_valid); return ref(); } + const T& operator*()const { assert(_valid); return ref(); } + T* operator->() + { + assert(_valid); + return ptr(); + } + const T* operator->()const + { + assert(_valid); + return ptr(); + } + + optional& operator=(std::nullptr_t) + { + reset(); + return *this; + } + + friend bool operator < ( const optional a, optional b ) + { + if( a.valid() && b.valid() ) return *a < *b; + return a.valid() < b.valid(); + } + friend bool operator == ( const optional a, optional b ) + { + if( a.valid() && b.valid() ) return *a == *b; + return a.valid() == b.valid(); + } + + void reset() + { + if( _valid ) + { + ref().~T(); // cal destructor + } + _valid = false; + } private: + template friend class optional; + T& ref() { return *ptr(); } + const T& ref()const { return *ptr(); } + T* ptr() { void* v = &_value[0]; return static_cast(v); } + const T* ptr()const { const void* v = &_value[0]; return static_cast(v); } + // force alignment... to 8 byte boundaries - double _value[8 * ((sizeof(T)+7)/8)]; + double _value[((sizeof(T)+7)/8)]; bool _valid; }; @@ -91,5 +259,9 @@ namespace fc { return !left || *left != u; } +#ifdef _MSC_VER +# pragma warning(pop) +#endif + } // namespace fc diff --git a/include/fc/pke.hpp b/include/fc/pke.hpp deleted file mode 100644 index b0edba976..000000000 --- a/include/fc/pke.hpp +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include -#include -#include - -/** - * Define common crypto methods and data types to abstract underlying implementation. - */ -namespace fc { - - template - struct signature { - char data[KeySize/8]; - template - friend T& operator<<( T& ds, const fc::signature& sig ) - { - ds.write(sig.data, KS/8 ); - return ds; - } - template - friend T& operator>>( T& ds, fc::signature& sig ) - { - ds.read(sig.data, KS/8 ); - return ds; - } - bool operator != ( const signature& s )const { - return memcmp( s.data, data, sizeof(data) ) != 0; - } - bool operator == ( const signature& s )const { - return memcmp( s.data, data, sizeof(data) ) == 0; - } - }; - - bool verify_data( const char* key, uint32_t key_size, uint32_t pe, const sha1& hc, const char* sig ); - bool sign_data( const fc::vector& key, uint32_t key_size, uint32_t pe, const sha1& hc, char* sig ); - bool public_encrypt( const char* key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ); - bool public_decrypt( const char* key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ); - bool private_encrypt( const fc::vector& key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ); - bool private_decrypt( const fc::vector& key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ); - bool generate_keys( char* pubkey, fc::vector& privkey, uint32_t key_size, uint32_t pe ); - - template - struct private_key; - - template - struct public_key { - public_key() { memset( key, 0, sizeof(key) ); } - public_key( const public_key& pk ) { memcpy( key, pk.key, sizeof(key) ); } - - bool verify( const sha1& digest, const signature& sig )const { - return verify_data( key, sizeof(key), PublicExponent, digest, sig.data ); - } - bool encrypt( const fc::vector& in, fc::vector& out )const { - return public_encrypt( key, KeySize, PublicExponent, in, out ); - } - bool decrypt( const fc::vector& in, fc::vector& out )const { - return public_decrypt( key, KeySize, PublicExponent, in, out ); - } - - public_key& operator = ( const public_key& pk ) { - memcpy( key, pk.key, sizeof(key) ); - return *this; - } - bool operator == ( const public_key& pk )const { - return 0 == memcmp( key, pk.key, sizeof(key) ); - } - bool operator != ( const public_key& pk )const { - return 0 != memcmp( key, pk.key, sizeof(key) ); - } - bool operator > ( const public_key& pk )const { - return memcmp( key, pk.key, sizeof(key) ) > 0; - } - bool operator < ( const public_key& pk )const { - return memcmp( key, pk.key, sizeof(key) ) < 0; - } - - template - inline friend T& operator<<( T& ds, const fc::public_key& pk ) { - ds.write(pk.key, KS/8 ); - return ds; - } - template - inline friend T& operator>>( T& ds, fc::public_key& pk ) { - ds.read( pk.key, KS/8 ); - return ds; - } - - private: - template - friend void generate_keys( public_key& pub, private_key& priv ); - - char key[KeySize/8]; - }; - - - template - struct private_key { - bool encrypt( const fc::vector& in, fc::vector& out )const { - return private_encrypt( key, KeySize, PublicExponent, in, out ); - } - bool decrypt( const fc::vector& in, fc::vector& out )const { - return private_decrypt( key, KeySize, PublicExponent, in, out ); - } - bool sign( const sha1& digest, signature& sig )const { - return sign_data( key, KeySize, PublicExponent, digest, sig.data ); - } - - template - friend T& operator<<( T& ds, const fc::private_key& pk ) { - uint16_t s = pk.key.size(); - ds.write( (const char*)&s, sizeof(s) ); - ds.write( pk.key.data(), pk.key.size() ); - return ds; - } - template - friend T& operator>>( T& ds, fc::private_key& pk ) { - uint16_t s; - ds.read((char*)&s,sizeof(s) ); - pk.key.resize(s); - ds.read( pk.key.data(), pk.key.size() ); - return ds; - } - private: - template - friend void generate_keys( public_key& pub, private_key& priv ); - fc::vector key; - }; - - template - void generate_keys( public_key& pub, private_key& priv ) { - generate_keys( pub.key, priv.key, KeySize, PublicExponent ); - } - /* - template - inline std::ostream& operator<< ( std::ostream& os, const signature& s ) { - for( uint32_t i = 0; i < KeySize; ++i ) - os << std::hex << int(s.data[i]) << ' '; - return os; - } - */ - - typedef public_key<> public_key_t; - typedef private_key<> private_key_t; - typedef signature<> signature_t; - - class value; - void pack( fc::value& , const fc::signature_t& ); - void unpack( const fc::value& , fc::signature_t& ); - void pack( fc::value& , const fc::public_key_t& ); - void unpack( const fc::value& , fc::private_key_t& ); - void pack( fc::value& , const fc::private_key_t& ); - void unpack( const fc::value& , fc::public_key_t& ); -} // namespace fc - - diff --git a/include/fc/platform_independence.hpp b/include/fc/platform_independence.hpp new file mode 100644 index 000000000..a3963e40c --- /dev/null +++ b/include/fc/platform_independence.hpp @@ -0,0 +1,15 @@ +#ifdef _MSC_VER +#include + #ifdef _M_X64 + #define __builtin_popcountll __popcnt64 + #else + inline int __builtin_popcountll(unsigned __int64 value) + { + unsigned int lowBits = (unsigned int)value; + int count = __popcnt(lowBits); + unsigned int highBits = (unsigned int)(value >> 32); + count += __popcnt(highBits); + return count; + } + #endif +#endif \ No newline at end of file diff --git a/include/fc/process.hpp b/include/fc/process.hpp deleted file mode 100644 index 4a596a7b2..000000000 --- a/include/fc/process.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - class istream; - class ostream; - class path; - template class vector; - - fc::path find_executable_in_path( const fc::string name ); - - /** - * @brief start and manage an external process - * - * @note this class implements reference semantics. - */ - class process { - public: - enum exec_opts { - open_none = 0, - open_stdin = 0x01, - open_stdout = 0x02, - open_stderr = 0x04, - open_all = open_stdin|open_stdout|open_stderr, - }; - - /** - * Return a new process executing the specified exe with the specified args. - */ - fc::future exec( const fc::path& exe, int opt = open_all ); - fc::future exec( const fc::path& exe, const fc::path& wd, int opt = open_all ); - fc::future exec( const fc::path& exe, fc::vector&& args , int opt = open_all ); - fc::future exec( const fc::path& exe, fc::vector&& args, const fc::path& wd, int opt = open_all ); - - /** - * Forcefully kills the process. - */ - void kill(); - - /** - * @brief returns a stream that writes to the process' stdin - */ - fc::ostream& in_stream(); - - /** - * @brief returns a stream that reads from the process' stdout - */ - fc::istream& out_stream(); - /** - * @brief returns a stream that reads from the process' stderr - */ - fc::istream& err_stream(); - - FC_REFERENCE_TYPE(process) - }; - -} // namespace fc diff --git a/include/fc/program_options.hpp b/include/fc/program_options.hpp deleted file mode 100644 index f839d729f..000000000 --- a/include/fc/program_options.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _FC_PROGRAM_OPTIONS_HPP_ -#define _FC_PROGRAM_OPTIONS_HPP_ -#include -#include -#include -#include -#include - -namespace boost { - namespace program_options { - class variables_map; - } -} - -namespace fc { - class ostream; - - namespace program_options { - template - class value { - public: - value( T* v ):_v(v){} - value& default_value( const T& d ) { _default = d; } - - T* get()const { return _v; } - private: - fc::optional _default; - T* _v; - }; - - class options_description { - public: - options_description( const char* c ); - ~options_description(); - - options_description& add_options(); - options_description& operator()( const char* o, const char* desc ); - options_description& operator()( const char* o, const value&, const char* desc ); - options_description& operator()( const char* o, const value&, const char* desc ); - options_description& operator()( const char* o, const value >&, const char* desc ); - - private: - class impl; - fwd my; - - friend class variables_map; - friend fc::ostream& operator<<( fc::ostream& o, const options_description& ); - }; - - class variables_map { - public: - variables_map(); - ~variables_map(); - - void parse_command_line( int argc, char** argv, const options_description& d ); - int count( const char* opt ); - private: - class impl; - fwd my; - }; - - } -} -#endif // _FC_PROGRAM_OPTIONS_HPP_ diff --git a/include/fc/ptr.hpp b/include/fc/ptr.hpp deleted file mode 100644 index b52da2841..000000000 --- a/include/fc/ptr.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - - namespace detail { - struct identity_member { - #ifdef BOOST_NO_VARIADIC_TEMPLATES - #define RPC_MEMBER_FUNCTOR(z,n,IS_CONST) \ - template \ - static std::function \ - functor( P p, R (C::*mem_func)(BOOST_PP_ENUM_PARAMS(n,A)) IS_CONST ){ \ - return [=](BOOST_PP_ENUM_BINARY_PARAMS(n,A,a)){ return (p->*mem_func)(BOOST_PP_ENUM_PARAMS(n,a)); }; \ - } - BOOST_PP_REPEAT( 8, RPC_MEMBER_FUNCTOR, const ) - BOOST_PP_REPEAT( 8, RPC_MEMBER_FUNCTOR, BOOST_PP_EMPTY() ) - #undef RPC_MEMBER_FUNCTOR - #else - template - static std::function functor( P&& p, R (C::*mem_func)(Args...) ) { - return std::function([=](Args... args){ return (p->*mem_func)(args...); }); - } - template - static std::function functor( P&& p, R (C::*mem_func)(Args...)const ) { - return std::function([=](Args... args){ return (p->*mem_func)(args...); }); - } - #endif - }; - - template< typename Interface, typename Transform = detail::identity_member > - struct vtable{}; - - template - struct vtable_visitor { - template - vtable_visitor( U&& u ):_this( fc::forward(u) ){} - - template - void operator()( const char* name, Function& memb, MemberPtr m )const { - memb = identity_member::functor( _this, m ); - } - ThisPtr _this; - }; - } // namespace detail - - template - class ptr { - public: - typedef detail::vtable vtable_type; - - ptr(){} - - template - ptr( InterfaceType* p ) - :_vtable( new vtable_type() ) { - _vtable->template visit_other( detail::vtable_visitor(p) ); - } - - template - ptr( const fc::shared_ptr& p ) - :_vtable( new vtable_type() ),_self(p){ - _vtable->template visit_other( detail::vtable_visitor(p.get()) ); - } - - //vtable_type& operator*() { return *_vtable; } - vtable_type& operator*()const { return *_vtable; } - - //vtable_type* operator->() { return _vtable.get(); } - vtable_type* operator->()const { return _vtable.get(); } - - protected: - fc::shared_ptr< vtable_type > _vtable; - fc::shared_ptr< fc::retainable > _self; - }; - - -} - -#include -#include - -#define FC_STUB_VTABLE_DEFINE_MEMBER( r, data, elem ) \ - decltype(Transform::functor( (data*)nullptr, &data::elem)) elem; -#define FC_STUB_VTABLE_DEFINE_VISIT_OTHER( r, data, elem ) \ - v( BOOST_PP_STRINGIZE(elem), elem, &T::elem ); -#define FC_STUB_VTABLE_DEFINE_VISIT( r, data, elem ) \ - v( BOOST_PP_STRINGIZE(elem), elem ); - -#define FC_STUB( CLASS, METHODS ) \ -namespace fc { namespace detail { \ - template \ - struct vtable : public fc::retainable { \ - vtable(){} \ - BOOST_PP_SEQ_FOR_EACH( FC_STUB_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \ - template \ - void visit_other( Visitor&& v ){ \ - BOOST_PP_SEQ_FOR_EACH( FC_STUB_VTABLE_DEFINE_VISIT_OTHER, CLASS, METHODS ) \ - } \ - template \ - void visit( Visitor&& v ){ \ - BOOST_PP_SEQ_FOR_EACH( FC_STUB_VTABLE_DEFINE_VISIT, CLASS, METHODS ) \ - } \ - }; \ -} } -//#undef FC_STUB_VTABLE_DEFINE_MEMBER -//#undef FC_STUB_VTABLE_DEFINE_VISIT diff --git a/include/fc/raw.hpp b/include/fc/raw.hpp deleted file mode 100644 index 073340815..000000000 --- a/include/fc/raw.hpp +++ /dev/null @@ -1,280 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - class value; - namespace raw { - - template - void unpack( Stream& s, fc::optional& v ); - template - void pack( Stream& s, const fc::optional& v ); - - template - void unpack( Stream& s, fc::value& ); - template - void pack( Stream& s, const fc::value& ); - - template - void unpack( Stream& s, fc::string& ); - - template - void pack( Stream& s, const fc::string& ); - - template - inline void pack( Stream& s, const T& v ); - - template - inline void unpack( Stream& s, T& v ); - - template - inline void pack( Stream& s, const fc::vector& v ); - template - inline void unpack( Stream& s, fc::vector& v ); - - - template - inline void pack( Stream& s, const fc::array& v) { - s.write((const char*)&v.data[0],N*sizeof(T)); - } - template - inline void unpack( Stream& s, fc::array& v) { - s.read((char*)&v.data[0],N*sizeof(T)); - } - template inline void pack( Stream& s, const signed_int& v ) { - uint32_t val = (v.value<<1) ^ (v.value>>31); - do { - uint8_t b = uint8_t(val) & 0x7f; - val >>= 7; - b |= ((val > 0) << 7); - s.write((char*)&b,1);//.put(b); - } while( val ); - } - - template inline void pack( Stream& s, const unsigned_int& v ) { - uint64_t val = v.value; - do { - uint8_t b = uint8_t(val) & 0x7f; - val >>= 7; - b |= ((val > 0) << 7); - s.write((char*)&b,1);//.put(b); - }while( val ); - } - - template inline void unpack( Stream& s, signed_int& vi ) { - uint32_t v = 0; char b = 0; int by = 0; - do { - s.get(b); - v |= uint32_t(uint8_t(b) & 0x7f) << by; - by += 7; - } while( uint8_t(b) & 0x80 ); - vi.value = ((v>>1) ^ (v>>31)) + (v&0x01); - vi.value = v&0x01 ? vi.value : -vi.value; - vi.value = -vi.value; - } - template inline void unpack( Stream& s, unsigned_int& vi ) { - uint64_t v = 0; char b = 0; uint8_t by = 0; - do { - s.get(b); - v |= uint32_t(uint8_t(b) & 0x7f) << by; - by += 7; - } while( uint8_t(b) & 0x80 ); - vi.value = v; - } - - template inline void pack( Stream& s, const char* v ) { pack( s, fc::string(v) ); } - - // optional - template - inline void pack( Stream& s, const fc::optional& v ) { - pack( s, bool(!!v) ); - if( !!v ) pack( s, *v ); - } - - template - void unpack( Stream& s, fc::optional& v ) { - bool b; unpack( s, b ); - if( b ) { v = T(); unpack( s, *v ); } - } - - // fc::vector - template inline void pack( Stream& s, const fc::vector& value ) { - pack( s, unsigned_int(value.size()) ); - if( value.size() ) - s.write( &value.front(), value.size() ); - } - template inline void unpack( Stream& s, fc::vector& value ) { - unsigned_int size; unpack( s, size ); - value.resize(size.value); - if( value.size() ) - s.read( value.data(), value.size() ); - } - - // fc::string - template inline void pack( Stream& s, const fc::string& v ) { - pack( s, unsigned_int(v.size()) ); - if( v.size() ) s.write( v.c_str(), v.size() ); - } - - template inline void unpack( Stream& s, fc::string& v ) { - fc::vector tmp; unpack(s,tmp); - v = fc::string(tmp.begin(),tmp.end()); - } - - // bool - template inline void pack( Stream& s, const bool& v ) { pack( s, uint8_t(v) ); } - template inline void unpack( Stream& s, bool& v ) { uint8_t b; unpack( s, b ); v=b; } - - namespace detail { - - template - struct pack_object_visitor { - pack_object_visitor(const Class& _c, Stream& _s) - :c(_c),s(_s){} - - template - void operator()( const char* name )const { - raw::pack( s, c.*p ); - } - private: - const Class& c; - Stream& s; - }; - - template - struct unpack_object_visitor { - unpack_object_visitor(Class& _c, Stream& _s) - :c(_c),s(_s){} - - template - inline void operator()( const char* name )const { - raw::unpack( s, c.*p ); - } - private: - Class& c; - Stream& s; - }; - - template - struct if_class{ - template - static inline void pack( Stream& s, const T& v ) { s << v; } - template - static inline void unpack( Stream& s, T& v ) { s >> v; } - }; - - template<> - struct if_class { - template - static inline void pack( Stream& s, const T& v ) { - s.write( (char*)&v, sizeof(v) ); - } - template - static inline void unpack( Stream& s, T& v ) { - s.read( (char*)&v, sizeof(v) ); - } - }; - - template - struct if_reflected { - template - static inline void pack( Stream& s, const T& v ) { - if_class::type>::pack(s,v); - } - template - static inline void unpack( Stream& s, T& v ) { - if_class::type>::unpack(s,v); - } - }; - template<> - struct if_reflected { - template - static inline void pack( Stream& s, const T& v ) { - fc::reflector::visit( pack_object_visitor( v, s ) ); - } - template - static inline void unpack( Stream& s, T& v ) { - fc::reflector::visit( unpack_object_visitor( v, s ) ); - } - }; - - } // namesapce detail - - - template - inline void pack( Stream& s, const fc::vector& value ) { - pack( s, unsigned_int(value.size()) ); - auto itr = value.begin(); - auto end = value.end(); - while( itr != end ) { - fc::raw::pack( s, *itr ); - ++itr; - } - } - - template - inline void unpack( Stream& s, fc::vector& value ) { - unsigned_int size; unpack( s, size ); - value.resize(size.value); - auto itr = value.begin(); - auto end = value.end(); - while( itr != end ) { - fc::raw::unpack( s, *itr ); - ++itr; - } - } - - template - inline void pack( Stream& s, const T& v ) { - fc::raw::detail::if_reflected< typename fc::reflector::is_defined >::pack(s,v); - } - template - inline void unpack( Stream& s, T& v ) { - fc::raw::detail::if_reflected< typename fc::reflector::is_defined >::unpack(s,v); - } - - - template - inline fc::vector pack( const T& v ) { - datastream ps; - raw::pack(ps,v ); - fc::vector vec(ps.tellp()); - if( vec.size() ) { - datastream ds( vec.data(), size_t(vec.size()) ); - raw::pack(ds,v); - } - return vec; - } - - template - inline T unpack( const fc::vector& s ) { - T tmp; - if( s.size() ) { - datastream ds( s.data(), size_t(s.size()) ); - raw::unpack(ds,tmp); - } - return tmp; - } - - template - inline void pack( char* d, uint32_t s, const T& v ) { - datastream ds(d,s); - raw::pack(ds,v ); - } - - template - inline T unpack( const char* d, uint32_t s ) { - T v; - datastream ds( d, s ); - raw::unpack(ds,v); - return v; - } - -} } // namespace fc::raw - diff --git a/include/fc/real128.hpp b/include/fc/real128.hpp new file mode 100644 index 000000000..3a7d26dbe --- /dev/null +++ b/include/fc/real128.hpp @@ -0,0 +1,52 @@ +#pragma once +#include + +#define FC_REAL128_PRECISION (uint64_t(1000000) * uint64_t(1000000) * uint64_t(1000000)) + +namespace fc { + class variant; + + /** + * Provides fixed point math operations based on decimal fractions + * with 18 places. + * Delegates to fc::bigint for multiplication and division. + */ + class real128 + { + public: + real128( uint64_t integer = 0); + explicit real128( const std::string& str ); + operator std::string()const; + + friend real128 operator * ( real128 a, const real128& b ) { a *= b; return a; } + friend real128 operator / ( real128 a, const real128& b ) { a /= b; return a; } + friend real128 operator + ( real128 a, const real128& b ) { a += b; return a; } + friend real128 operator - ( real128 a, const real128& b ) { a -= b; return a; } + + real128& operator += ( const real128& o ); + real128& operator -= ( const real128& o ); + real128& operator /= ( const real128& o ); + real128& operator *= ( const real128& o ); + + static real128 from_fixed( const uint128& fixed ); + + uint64_t to_uint64()const; + + private: + uint128 fixed; + }; + + void to_variant( const real128& var, variant& vo ); + void from_variant( const variant& var, real128& vo ); + + namespace raw + { + template + inline void pack( Stream& s, const real128& value_to_pack ) { s.write( (char*)&value_to_pack, sizeof(value_to_pack) ); } + template + inline void unpack( Stream& s, real128& value_to_unpack ) { s.read( (char*)&value_to_unpack, sizeof(value_to_unpack) ); } + } + + + +} // namespace fc diff --git a/include/fc/reflect.hpp b/include/fc/reflect/reflect.hpp similarity index 68% rename from include/fc/reflect.hpp rename to include/fc/reflect/reflect.hpp index 44d86c228..4deea27bf 100644 --- a/include/fc/reflect.hpp +++ b/include/fc/reflect/reflect.hpp @@ -1,12 +1,10 @@ - +#pragma once /** * @file fc/reflect.hpp * * @brief Defines types and macros used to provide reflection. * */ -#ifndef _FC_REFLECT_HPP_ -#define _FC_REFLECT_HPP_ #include #include @@ -15,11 +13,11 @@ #include #include #include +#include -#include -#include +#include -namespace fc { +namespace fc { /** * @brief defines visit functions for T @@ -34,33 +32,33 @@ template struct reflector{ typedef T type; typedef fc::false_type is_defined; - typedef fc::false_type is_enum; + typedef fc::false_type is_enum; /** * @tparam Visitor a function object of the form: - * + * * @code - * struct functor { - * template + * struct functor { + * template * void operator()( const char* name )const; * }; * @endcode * * If T is an enum then the functor has the following form: * @code - * struct functor { + * struct functor { * template * void operator()( const char* name )const; * }; * @endcode - * + * * @param v a functor that will be called for each member on T * * @note - this method is not defined for non-reflected types. */ #ifdef DOXYGEN template - static inline void visit( const Visitor& v ); + static inline void visit( const Visitor& v ); #endif // DOXYGEN }; @@ -79,7 +77,7 @@ void throw_bad_enum_cast( const char* k, const char* e ); #define TEMPLATE template #else // Disable warning C4482: nonstandard extention used: enum 'enum_type::enum_value' used in qualified name - #pragma warning( disable: 4482 ) + #pragma warning( disable: 4482 ) #define TEMPLATE #endif @@ -90,21 +88,24 @@ void throw_bad_enum_cast( const char* k, const char* e ); #define FC_REFLECT_BASE_MEMBER_COUNT( r, OP, elem ) \ - OP fc::reflector::member_count + OP fc::reflector::total_member_count + +#define FC_REFLECT_MEMBER_COUNT( r, OP, elem ) \ + OP 1 #define FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \ template\ static inline void visit( const Visitor& v ) { \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_BASE, v, INHERITS ) \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_MEMBER, v, MEMBERS ) \ -} +} #define FC_REFLECT_DERIVED_IMPL_EXT( TYPE, INHERITS, MEMBERS ) \ template\ void fc::reflector::visit( const Visitor& v ) { \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_BASE, v, INHERITS ) \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_MEMBER, v, MEMBERS ) \ -} +} #endif // DOXYGEN @@ -113,6 +114,8 @@ void fc::reflector::visit( const Visitor& v ) { \ v.TEMPLATE operator()(BOOST_PP_STRINGIZE(elem)); #define FC_REFLECT_ENUM_TO_STRING( r, enum_type, elem ) \ case enum_type::elem: return BOOST_PP_STRINGIZE(elem); +#define FC_REFLECT_ENUM_TO_FC_STRING( r, enum_type, elem ) \ + case enum_type::elem: return fc::string(BOOST_PP_STRINGIZE(elem)); #define FC_REFLECT_ENUM_FROM_STRING( r, enum_type, elem ) \ if( strcmp( s, BOOST_PP_STRINGIZE(elem) ) == 0 ) return enum_type::elem; @@ -123,32 +126,46 @@ namespace fc { \ template<> struct reflector { \ typedef fc::true_type is_defined; \ typedef fc::true_type is_enum; \ - template \ - static inline void visit( const Visitor& v ) { \ - BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_ENUM, ENUM, FIELDS ) \ - }\ - static const char* to_string(int64_t i) { \ - switch( ENUM(i) ) { \ + static const char* to_string(ENUM elem) { \ + switch( elem ) { \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_TO_STRING, ENUM, FIELDS ) \ default: \ - fc::throw_bad_enum_cast( i, BOOST_PP_STRINGIZE(ENUM) ); \ + fc::throw_bad_enum_cast( fc::to_string(int64_t(elem)).c_str(), BOOST_PP_STRINGIZE(ENUM) ); \ }\ return nullptr; \ } \ + static const char* to_string(int64_t i) { \ + return to_string(ENUM(i)); \ + } \ + static fc::string to_fc_string(ENUM elem) { \ + switch( elem ) { \ + BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_TO_FC_STRING, ENUM, FIELDS ) \ + } \ + return fc::to_string(int64_t(elem)); \ + } \ + static fc::string to_fc_string(int64_t i) { \ + return to_fc_string(ENUM(i)); \ + } \ static ENUM from_string( const char* s ) { \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_FROM_STRING, ENUM, FIELDS ) \ - fc::throw_bad_enum_cast( s, BOOST_PP_STRINGIZE(ENUM) ); \ - return ENUM();\ + return ENUM(atoi(s));\ } \ }; \ -} - +} +/* Note: FC_REFLECT_ENUM previously defined this function, but I don't think it ever + * did what we expected it to do. I've disabled it for now. + * + * template \ + * static inline void visit( const Visitor& v ) { \ + * BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_ENUM, ENUM, FIELDS ) \ + * }\ + */ /** * @def FC_REFLECT_DERIVED(TYPE,INHERITS,MEMBERS) * - * @brief Specializes fc::reflector for TYPE where + * @brief Specializes fc::reflector for TYPE where * type inherits other reflected classes * * @param INHERITS - a sequence of base class names (basea)(baseb)(basec) @@ -162,12 +179,26 @@ template<> struct reflector {\ typedef fc::true_type is_defined; \ typedef fc::false_type is_enum; \ enum member_count_enum { \ - local_member_count = BOOST_PP_SEQ_SIZE(MEMBERS), \ + local_member_count = 0 BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_MEMBER_COUNT, +, MEMBERS ),\ + total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\ + }; \ + FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \ +}; } +#define FC_REFLECT_DERIVED_TEMPLATE( TEMPLATE_ARGS, TYPE, INHERITS, MEMBERS ) \ +namespace fc { \ + template struct get_typename { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \ +template struct reflector {\ + typedef TYPE type; \ + typedef fc::true_type is_defined; \ + typedef fc::false_type is_enum; \ + enum member_count_enum { \ + local_member_count = 0 BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_MEMBER_COUNT, +, MEMBERS ),\ total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\ }; \ FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \ -}; } +}; } +//BOOST_PP_SEQ_SIZE(MEMBERS), /** * @def FC_REFLECT(TYPE,MEMBERS) @@ -180,6 +211,12 @@ template<> struct reflector {\ #define FC_REFLECT( TYPE, MEMBERS ) \ FC_REFLECT_DERIVED( TYPE, BOOST_PP_SEQ_NIL, MEMBERS ) +#define FC_REFLECT_TEMPLATE( TEMPLATE_ARGS, TYPE, MEMBERS ) \ + FC_REFLECT_DERIVED_TEMPLATE( TEMPLATE_ARGS, TYPE, BOOST_PP_SEQ_NIL, MEMBERS ) + +#define FC_REFLECT_EMPTY( TYPE ) \ + FC_REFLECT_DERIVED( TYPE, BOOST_PP_SEQ_NIL, BOOST_PP_SEQ_NIL ) + #define FC_REFLECT_TYPENAME( TYPE ) \ namespace fc { \ template<> struct get_typename { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \ @@ -207,4 +244,3 @@ template<> struct reflector {\ -#endif diff --git a/include/fc/reflect/typename.hpp b/include/fc/reflect/typename.hpp new file mode 100644 index 000000000..312c92711 --- /dev/null +++ b/include/fc/reflect/typename.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +namespace fc { + class value; + class exception; + namespace ip { class address; } + + template class get_typename{}; + template<> struct get_typename { static const char* name() { return "int32_t"; } }; + template<> struct get_typename { static const char* name() { return "int64_t"; } }; + template<> struct get_typename { static const char* name() { return "int16_t"; } }; + template<> struct get_typename { static const char* name() { return "int8_t"; } }; + template<> struct get_typename { static const char* name() { return "uint32_t"; } }; + template<> struct get_typename { static const char* name() { return "uint64_t"; } }; + template<> struct get_typename { static const char* name() { return "uint16_t"; } }; + template<> struct get_typename { static const char* name() { return "uint8_t"; } }; + template<> struct get_typename { static const char* name() { return "double"; } }; + template<> struct get_typename { static const char* name() { return "float"; } }; + template<> struct get_typename { static const char* name() { return "bool"; } }; + template<> struct get_typename { static const char* name() { return "char"; } }; + template<> struct get_typename { static const char* name() { return "char"; } }; + template<> struct get_typename { static const char* name() { return "string"; } }; + template<> struct get_typename { static const char* name() { return "value"; } }; + template<> struct get_typename { static const char* name() { return "fc::exception"; } }; + template<> struct get_typename> { static const char* name() { return "std::vector"; } }; + template struct get_typename> + { + static const char* name() { + static std::string n = std::string("std::vector<") + get_typename::name() + ">"; + return n.c_str(); + } + }; + template struct get_typename> + { + static const char* name() { + static std::string n = std::string("flat_set<") + get_typename::name() + ">"; + return n.c_str(); + } + }; + template struct get_typename< std::deque > + { + static const char* name() + { + static std::string n = std::string("std::deque<") + get_typename::name() + ">"; + return n.c_str(); + } + }; + template struct get_typename> + { + static const char* name() { + static std::string n = std::string("optional<") + get_typename::name() + ">"; + return n.c_str(); + } + }; + template struct get_typename> + { + static const char* name() { + static std::string n = std::string("std::map<") + get_typename::name() + ","+get_typename::name()+">"; + return n.c_str(); + } + }; + + struct signed_int; + struct unsigned_int; + template<> struct get_typename { static const char* name() { return "signed_int"; } }; + template<> struct get_typename { static const char* name() { return "unsigned_int"; } }; + +} diff --git a/include/fc/reflect/variant.hpp b/include/fc/reflect/variant.hpp new file mode 100644 index 000000000..f48fe9a2b --- /dev/null +++ b/include/fc/reflect/variant.hpp @@ -0,0 +1,109 @@ +#pragma once +#include +#include + +namespace fc +{ + template + void to_variant( const T& o, variant& v ); + template + void from_variant( const variant& v, T& o ); + + + template + class to_variant_visitor + { + public: + to_variant_visitor( mutable_variant_object& mvo, const T& v ) + :vo(mvo),val(v){} + + template + void operator()( const char* name )const + { + this->add(vo,name,(val.*member)); + } + + private: + template + void add( mutable_variant_object& vo, const char* name, const optional& v )const + { + if( v.valid() ) + vo(name,*v); + } + template + void add( mutable_variant_object& vo, const char* name, const M& v )const + { vo(name,v); } + + mutable_variant_object& vo; + const T& val; + }; + + template + class from_variant_visitor + { + public: + from_variant_visitor( const variant_object& _vo, T& v ) + :vo(_vo),val(v){} + + template + void operator()( const char* name )const + { + auto itr = vo.find(name); + if( itr != vo.end() ) + from_variant( itr->value(), val.*member ); + } + + const variant_object& vo; + T& val; + }; + + template + struct if_enum + { + template + static inline void to_variant( const T& v, fc::variant& vo ) + { + mutable_variant_object mvo; + fc::reflector::visit( to_variant_visitor( mvo, v ) ); + vo = fc::move(mvo); + } + template + static inline void from_variant( const fc::variant& v, T& o ) + { + const variant_object& vo = v.get_object(); + fc::reflector::visit( from_variant_visitor( vo, o ) ); + } + }; + + template<> + struct if_enum + { + template + static inline void to_variant( const T& o, fc::variant& v ) + { + v = fc::reflector::to_fc_string(o); + } + template + static inline void from_variant( const fc::variant& v, T& o ) + { + if( v.is_string() ) + o = fc::reflector::from_string( v.get_string().c_str() ); + else + o = static_cast(v.as_int64()); + } + }; + + + template + void to_variant( const T& o, variant& v ) + { + if_enum::is_enum>::to_variant( o, v ); + } + + template + void from_variant( const variant& v, T& o ) + { + if_enum::is_enum>::from_variant( v, o ); + } + +} diff --git a/include/fc/rpc/api_connection.hpp b/include/fc/rpc/api_connection.hpp new file mode 100644 index 000000000..c06d2d0a3 --- /dev/null +++ b/include/fc/rpc/api_connection.hpp @@ -0,0 +1,429 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +namespace fc { + class api_connection; + + typedef uint32_t api_id_type; + + namespace detail { + template + class callback_functor + { + public: + typedef typename std::function::result_type result_type; + + callback_functor( std::weak_ptr< fc::api_connection > con, uint64_t id ) + :_callback_id(id),_api_connection(con){} + + template + result_type operator()( Args... args )const; + + private: + uint64_t _callback_id; + std::weak_ptr< fc::api_connection > _api_connection; + }; + + template + std::function bind_first_arg( const std::function& f, Arg0 a0 ) + { + return [=]( Args... args ) { return f( a0, args... ); }; + } + template + R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) + { + return f(); + } + + template + R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) + { + FC_ASSERT( a0 != e ); + return call_generic( bind_first_arg( f, a0->as< typename std::decay::type >() ), a0+1, e ); + } + + template + std::function to_generic( const std::function& f ) + { + return [=]( const variants& args ) { + return variant( call_generic( f, args.begin(), args.end() ) ); + }; + } + + template + std::function to_generic( const std::function& f ) + { + return [=]( const variants& args ) { + call_generic( f, args.begin(), args.end() ); + return variant(); + }; + } + + } // namespace detail + + class generic_api + { + public: + template + generic_api( const Api& a, const std::shared_ptr& c ); + + generic_api( const generic_api& cpy ) = delete; + + variant call( const string& name, const variants& args ) + { + auto itr = _by_name.find(name); + FC_ASSERT( itr != _by_name.end(), "no method with name '${name}'", ("name",name)("api",_by_name) ); + return call( itr->second, args ); + } + + variant call( uint32_t method_id, const variants& args ) + { + FC_ASSERT( method_id < _methods.size() ); + return _methods[method_id](args); + } + + std::weak_ptr< fc::api_connection > get_connection() + { + return _api_connection; + } + + std::vector get_method_names()const + { + std::vector result; + result.reserve( _by_name.size() ); + for( auto& m : _by_name ) result.push_back(m.first); + return result; + } + + private: + friend struct api_visitor; + + template + std::function bind_first_arg( const std::function& f, Arg0 a0 )const + { + return [=]( Args... args ) { return f( a0, args... ); }; + } + + template + R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e )const + { + return f(); + } + + template + R call_generic( const std::function,Args...)>& f, variants::const_iterator a0, variants::const_iterator e ) + { + FC_ASSERT( a0 != e, "too few arguments passed to method" ); + detail::callback_functor arg0( get_connection(), a0->as() ); + return call_generic( this->bind_first_arg,Args...>( f, std::function(arg0) ), a0+1, e ); + } + template + R call_generic( const std::function&,Args...)>& f, variants::const_iterator a0, variants::const_iterator e ) + { + FC_ASSERT( a0 != e, "too few arguments passed to method" ); + detail::callback_functor arg0( get_connection(), a0->as() ); + return call_generic( this->bind_first_arg&,Args...>( f, arg0 ), a0+1, e ); + } + + template + R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) + { + FC_ASSERT( a0 != e, "too few arguments passed to method" ); + return call_generic( this->bind_first_arg( f, a0->as< typename std::decay::type >() ), a0+1, e ); + } + + struct api_visitor + { + api_visitor( generic_api& a, const std::weak_ptr& s ):api(a),_api_con(s){ } + + template + std::function to_generic( const std::function(Args...)>& f )const; + + template + std::function to_generic( const std::function>(Args...)>& f )const; + + template + std::function to_generic( const std::function& f )const; + + template + std::function to_generic( const std::function& f )const; + + template + void operator()( const char* name, std::function& memb )const { + api._methods.emplace_back( to_generic( memb ) ); + api._by_name[name] = api._methods.size() - 1; + } + + generic_api& api; + const std::weak_ptr& _api_con; + }; + + + std::weak_ptr _api_connection; + fc::any _api; + std::map< std::string, uint32_t > _by_name; + std::vector< std::function > _methods; + }; // class generic_api + + + + class api_connection : public std::enable_shared_from_this + { + public: + api_connection(){} + virtual ~api_connection(){}; + + + template + api get_remote_api( api_id_type api_id = 0 ) + { + api result; + result->visit( api_visitor( api_id, this->shared_from_this() ) ); + return result; + } + + /** makes calls to the remote server */ + virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ) = 0; + virtual variant send_callback( uint64_t callback_id, variants args = variants() ) = 0; + virtual void send_notice( uint64_t callback_id, variants args = variants() ) = 0; + + variant receive_call( api_id_type api_id, const string& method_name, const variants& args = variants() )const + { + FC_ASSERT( _local_apis.size() > api_id ); + return _local_apis[api_id]->call( method_name, args ); + } + variant receive_callback( uint64_t callback_id, const variants& args = variants() )const + { + FC_ASSERT( _local_callbacks.size() > callback_id ); + return _local_callbacks[callback_id]( args ); + } + void receive_notice( uint64_t callback_id, const variants& args = variants() )const + { + FC_ASSERT( _local_callbacks.size() > callback_id ); + _local_callbacks[callback_id]( args ); + } + + template + api_id_type register_api( const Interface& a ) + { + auto handle = a.get_handle(); + auto itr = _handle_to_id.find(handle); + if( itr != _handle_to_id.end() ) return itr->second; + + _local_apis.push_back( std::unique_ptr( new generic_api(a, shared_from_this() ) ) ); + _handle_to_id[handle] = _local_apis.size() - 1; + return _local_apis.size() - 1; + } + + template + uint64_t register_callback( const std::function& cb ) + { + _local_callbacks.push_back( detail::to_generic( cb ) ); + return _local_callbacks.size() - 1; + } + + std::vector get_method_names( api_id_type local_api_id = 0 )const { return _local_apis[local_api_id]->get_method_names(); } + + fc::signal closed; + private: + std::vector< std::unique_ptr > _local_apis; + std::map< uint64_t, api_id_type > _handle_to_id; + std::vector< std::function > _local_callbacks; + + + struct api_visitor + { + uint32_t _api_id; + std::shared_ptr _connection; + + api_visitor( uint32_t api_id, std::shared_ptr con ) + :_api_id(api_id),_connection(std::move(con)) + { + } + + api_visitor() = delete; + + template + static Result from_variant( const variant& v, Result*, const std::shared_ptr& ) + { + return v.as(); + } + + template + static fc::api from_variant( const variant& v, + fc::api* /*used for template deduction*/, + const std::shared_ptr& con + ) + { + return con->get_remote_api( v.as_uint64() ); + } + + template + static fc::variant convert_callbacks( const std::shared_ptr&, const T& v ) + { + return fc::variant(v); + } + + template + static fc::variant convert_callbacks( const std::shared_ptr& con, const std::function& v ) + { + return con->register_callback( v ); + } + + template + void operator()( const char* name, std::function& memb )const + { + auto con = _connection; + auto api_id = _api_id; + memb = [con,api_id,name]( Args... args ) { + auto var_result = con->send_call( api_id, name, { convert_callbacks(con,args)...} ); + return from_variant( var_result, (Result*)nullptr, con ); + }; + } + template + void operator()( const char* name, std::function& memb )const + { + auto con = _connection; + auto api_id = _api_id; + memb = [con,api_id,name]( Args... args ) { + con->send_call( api_id, name, { convert_callbacks(con,args)...} ); + }; + } + }; + }; + + class local_api_connection : public api_connection + { + public: + /** makes calls to the remote server */ + virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ) override + { + FC_ASSERT( _remote_connection ); + return _remote_connection->receive_call( api_id, method_name, std::move(args) ); + } + virtual variant send_callback( uint64_t callback_id, variants args = variants() ) override + { + FC_ASSERT( _remote_connection ); + return _remote_connection->receive_callback( callback_id, args ); + } + virtual void send_notice( uint64_t callback_id, variants args = variants() ) override + { + FC_ASSERT( _remote_connection ); + _remote_connection->receive_notice( callback_id, args ); + } + + + void set_remote_connection( const std::shared_ptr& rc ) + { + FC_ASSERT( !_remote_connection ); + FC_ASSERT( rc != this->shared_from_this() ); + _remote_connection = rc; + } + const std::shared_ptr& remote_connection()const { return _remote_connection; } + + std::shared_ptr _remote_connection; + }; + + template + generic_api::generic_api( const Api& a, const std::shared_ptr& c ) + :_api_connection(c),_api(a) + { + boost::any_cast(a)->visit( api_visitor( *this, c ) ); + } + + template + std::function generic_api::api_visitor::to_generic( + const std::function(Args...)>& f )const + { + auto api_con = _api_con; + auto gapi = &api; + return [=]( const variants& args ) { + auto con = api_con.lock(); + FC_ASSERT( con, "not connected" ); + + auto api_result = gapi->call_generic( f, args.begin(), args.end() ); + return con->register_api( api_result ); + }; + } + template + std::function generic_api::api_visitor::to_generic( + const std::function>(Args...)>& f )const + { + auto api_con = _api_con; + auto gapi = &api; + return [=]( const variants& args )-> fc::variant { + auto con = api_con.lock(); + FC_ASSERT( con, "not connected" ); + + auto api_result = gapi->call_generic( f, args.begin(), args.end() ); + if( api_result ) + return con->register_api( *api_result ); + return variant(); + }; + } + template + std::function generic_api::api_visitor::to_generic( const std::function& f )const + { + generic_api* gapi = &api; + return [f,gapi]( const variants& args ) { + return variant( gapi->call_generic( f, args.begin(), args.end() ) ); + }; + } + + template + std::function generic_api::api_visitor::to_generic( const std::function& f )const + { + generic_api* gapi = &api; + return [f,gapi]( const variants& args ) { + gapi->call_generic( f, args.begin(), args.end() ); + return variant(); + }; + } + + namespace detail { + template + template + typename callback_functor::result_type callback_functor::operator()( Args... args )const + { + std::shared_ptr< fc::api_connection > locked = _api_connection.lock(); + // TODO: make new exception type for this instead of recycling eof_exception + if( !locked ) + throw fc::eof_exception(); + locked->send_callback( _callback_id, fc::variants{ args... } ).template as< result_type >(); + } + + + template + class callback_functor + { + public: + typedef void result_type; + + callback_functor( std::weak_ptr< fc::api_connection > con, uint64_t id ) + :_callback_id(id),_api_connection(con){} + + void operator()( Args... args )const + { + std::shared_ptr< fc::api_connection > locked = _api_connection.lock(); + // TODO: make new exception type for this instead of recycling eof_exception + if( !locked ) + throw fc::eof_exception(); + locked->send_notice( _callback_id, fc::variants{ args... } ); + } + + private: + uint64_t _callback_id; + std::weak_ptr< fc::api_connection > _api_connection; + }; + } // namespace detail + +} // fc diff --git a/include/fc/rpc/cli.hpp b/include/fc/rpc/cli.hpp new file mode 100644 index 000000000..bb4975fe6 --- /dev/null +++ b/include/fc/rpc/cli.hpp @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include + +namespace fc { namespace rpc { + + /** + * Provides a simple wrapper for RPC calls to a given interface. + */ + class cli : public api_connection + { + public: + ~cli(); + + virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ); + virtual variant send_callback( uint64_t callback_id, variants args = variants() ); + virtual void send_notice( uint64_t callback_id, variants args = variants() ); + + void start(); + void stop(); + void wait(); + void format_result( const string& method, std::function formatter); + + virtual void getline( const fc::string& prompt, fc::string& line ); + + void set_prompt( const string& prompt ); + + private: + void run(); + + std::string _prompt = ">>>"; + std::map > _result_formatters; + fc::future _run_complete; + }; +} } diff --git a/include/fc/rpc/http_api.hpp b/include/fc/rpc/http_api.hpp new file mode 100644 index 000000000..47eb289fe --- /dev/null +++ b/include/fc/rpc/http_api.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace fc { namespace rpc { + + class http_api_connection : public api_connection + { + public: + http_api_connection(); + ~http_api_connection(); + + virtual variant send_call( + api_id_type api_id, + string method_name, + variants args = variants() ) override; + virtual variant send_callback( + uint64_t callback_id, + variants args = variants() ) override; + virtual void send_notice( + uint64_t callback_id, + variants args = variants() ) override; + + void on_request( + const fc::http::request& req, + const fc::http::server::response& resp ); + + fc::rpc::state _rpc_state; + }; + +} } // namespace fc::rpc diff --git a/include/fc/rpc/json_connection.hpp b/include/fc/rpc/json_connection.hpp new file mode 100644 index 000000000..04cd041b0 --- /dev/null +++ b/include/fc/rpc/json_connection.hpp @@ -0,0 +1,315 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { namespace rpc { + + namespace detail { class json_connection_impl; } + + /** + * @brief Implements JSON-RPC 2.0 over a set of io streams + * + * Each JSON RPC message is expected to be on its own line, violators + * will be prosecuted to the fullest extent of the law. + */ + class json_connection + { + public: + typedef std::function method; + typedef std::function named_param_method; + + json_connection( fc::buffered_istream_ptr in, fc::buffered_ostream_ptr out ); + ~json_connection(); + + /** + * Starts processing messages from input + */ + future exec(); + + bool is_open(); + void close(); + + void set_on_disconnected_callback(std::function callback); + + logger get_logger()const; + void set_logger( const logger& l ); + + /** + * @name server interface + * + * Adding methods to the interface allows the remote side + * to call them. + */ + ///@{ + void add_method( const fc::string& name, method ); + void add_named_param_method( const fc::string& name, named_param_method ); + void remove_method( const fc::string& name ); + //@} + + /** + * @name client interface + */ + ///@{ + void notice( const fc::string& method ); + void notice( const fc::string& method, const variants& args ); + void notice( const fc::string& method, const variant_object& named_args ); + + /// args will be handled as named params + future async_call( const fc::string& method, + const variant_object& args ); + + future async_call( const fc::string& method, mutable_variant_object args ); + + /// Sending in an array of variants will be handled as positional arguments + future async_call( const fc::string& method, + const variants& args ); + + future async_call( const fc::string& method ); + + future async_call( const fc::string& method, + const variant& a1 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7 + ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8 + ); + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + const variant& a9 + ); + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + const variant& a9, + const variant& a10 + ); + + template + Result call( const fc::string& method, + const variants& args, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, args ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3 ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4, a5).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4, a5, a6).wait(timeout).as(); + } + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4, a5, a6, a7).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4, a5, a6, a7, a8).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + const variant& a9, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4, a5, a6, a7, a8, a9).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + const variant& a9, + const variant& a10, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2 ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1 ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + variant_object a1, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, fc::move(a1) ).wait(timeout).as(); + } + template + Result call( const fc::string& method, + mutable_variant_object a1, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, variant_object( fc::move(a1) ) ).wait(timeout).as(); + } + + + template + Result call( const fc::string& method, microseconds timeout = microseconds::maximum() ) + { + return async_call( method ).wait(timeout).as(); + } + + /// Sending in a variant_object will be issued as named parameters + variant call( const fc::string& method, const variant_object& named_args ); + ///@} + + private: + std::unique_ptr my; + }; + typedef std::shared_ptr json_connection_ptr; + +}} // fc::rpc + + + diff --git a/include/fc/rpc/state.hpp b/include/fc/rpc/state.hpp new file mode 100644 index 000000000..3c36bcf9f --- /dev/null +++ b/include/fc/rpc/state.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include + +namespace fc { namespace rpc { + struct request + { + optional id; + std::string method; + variants params; + }; + + struct error_object + { + int64_t code; + std::string message; + optional data; + }; + + struct response + { + response(){} + response( int64_t i, fc::variant r ):id(i),result(r){} + response( int64_t i, error_object r ):id(i),error(r){} + int64_t id = 0; + optional result; + optional error; + }; + + class state + { + public: + typedef std::function method; + ~state(); + + void add_method( const fc::string& name, method m ); + void remove_method( const fc::string& name ); + + variant local_call( const string& method_name, const variants& args ); + void handle_reply( const response& response ); + + request start_remote_call( const string& method_name, variants args ); + variant wait_for_response( uint64_t request_id ); + + void close(); + + void on_unhandled( const std::function& unhandled ); + + private: + uint64_t _next_id = 1; + std::unordered_map::ptr> _awaiting; + std::unordered_map _methods; + std::function _unhandled; + }; +} } // namespace fc::rpc + +FC_REFLECT( fc::rpc::request, (id)(method)(params) ); +FC_REFLECT( fc::rpc::error_object, (code)(message)(data) ) +FC_REFLECT( fc::rpc::response, (id)(result)(error) ) diff --git a/include/fc/rpc/variant_connection.hpp b/include/fc/rpc/variant_connection.hpp new file mode 100644 index 000000000..93cb29b1e --- /dev/null +++ b/include/fc/rpc/variant_connection.hpp @@ -0,0 +1,140 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { namespace rpc { + + namespace detail { class variant_connection_impl; } + + /** + * @brief Implements JSON-RPC 2.0 over a set of io streams + * + * Each JSON RPC message is expected to be on its own line, violators + * will be prosecuted to the fullest extent of the law. + */ + class variant_connection + { + public: + typedef std::function method; + typedef std::function named_param_method; + + variant_connection( fc::variant_stream::ptr in, fc::variant_stream::ptr out ); + ~variant_connection(); + + /** + * Starts processing messages from input + */ + future exec(); + + logger get_logger()const; + void set_logger( const logger& l ); + + /** + * @name server interface + * + * Adding methods to the interface allows the remote side + * to call them. + */ + ///@{ + void add_method( const fc::string& name, method ); + void add_named_param_method( const fc::string& name, named_param_method ); + void remove_method( const fc::string& name ); + //@} + + /** + * @name client interface + */ + ///@{ + void notice( const fc::string& method ); + void notice( const fc::string& method, const variants& args ); + void notice( const fc::string& method, const variant_object& named_args ); + + /// args will be handled as named params + future async_call( const fc::string& method, + const variant_object& args ); + + future async_call( const fc::string& method, mutable_variant_object args ); + + /// Sending in an array of variants will be handled as positional arguments + future async_call( const fc::string& method, + const variants& args ); + + future async_call( const fc::string& method ); + + future async_call( const fc::string& method, + const variant& a1 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2 ); + + future async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3 ); + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2, a3 ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + const variant& a2, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1, a2 ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + const variant& a1, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, a1 ).wait(timeout).as(); + } + + template + Result call( const fc::string& method, + variant_object a1, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, fc::move(a1) ).wait(timeout).as(); + } + template + Result call( const fc::string& method, + mutable_variant_object a1, + microseconds timeout = microseconds::maximum()) + { + return async_call( method, variant_object( fc::move(a1) ) ).wait(timeout).as(); + } + + + template + Result call( const fc::string& method, microseconds timeout = microseconds::maximum() ) + { + return async_call( method ).wait(timeout).as(); + } + + /// Sending in a variant_object will be issued as named parameters + variant call( const fc::string& method, const variant_object& named_args ); + ///@} + + private: + std::unique_ptr my; + }; + typedef std::shared_ptr variant_connection_ptr; + +}} // fc::rpc + + + diff --git a/include/fc/rpc/variant_stream.hpp b/include/fc/rpc/variant_stream.hpp new file mode 100644 index 000000000..585b776b0 --- /dev/null +++ b/include/fc/rpc/variant_stream.hpp @@ -0,0 +1,36 @@ +#pragma once + +namespace fc +{ + + /** + * Thread-safe, circular buffer for passing variants + * between threads. + */ + class variant_stream + { + public: + variant_stream( size_t s ); + ~variant_stream(); + + /** producer api */ + int64_t free(); // number of spaces available + int64_t claim( int64_t num ); + int64_t publish( int64_t pos ); + int64_t wait_free(); // wait for free space + + // producer/consumer api + variant& get( int64_t pos ); + + /** consumer api */ + int64_t begin(); // returns the first index ready to be read + int64_t end(); // returns the first index that cannot be read + int64_t wait(); // wait for variants to be posted + + private: + std::vector _variants; + uint64_t _read_pos; + uint64_t _write_pos; + }; + +} diff --git a/include/fc/rpc/websocket_api.hpp b/include/fc/rpc/websocket_api.hpp new file mode 100644 index 000000000..92d7f2eb5 --- /dev/null +++ b/include/fc/rpc/websocket_api.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace fc { namespace rpc { + + class websocket_api_connection : public api_connection + { + public: + websocket_api_connection( fc::http::websocket_connection& c ); + ~websocket_api_connection(); + + virtual variant send_call( + api_id_type api_id, + string method_name, + variants args = variants() ) override; + virtual variant send_callback( + uint64_t callback_id, + variants args = variants() ) override; + virtual void send_notice( + uint64_t callback_id, + variants args = variants() ) override; + + protected: + std::string on_message( + const std::string& message, + bool send_message = true ); + + fc::http::websocket_connection& _connection; + fc::rpc::state _rpc_state; + }; + +} } // namespace fc::rpc diff --git a/include/fc/safe.hpp b/include/fc/safe.hpp new file mode 100644 index 000000000..d9290b9f4 --- /dev/null +++ b/include/fc/safe.hpp @@ -0,0 +1,230 @@ +#pragma once +#include +#include + +#include + +namespace fc { + + /** + * This type is designed to provide automatic checks for + * integer overflow and default initialization. It will + * throw an exception on overflow conditions. + * + * It can only be used on built-in types. In particular, + * safe is buggy and should not be used. + * + * Implemented using spec from: + * https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow + */ + template + struct safe + { + T value = 0; + + template + safe( O o ):value(o){} + safe(){} + safe( const safe& o ):value(o.value){} + + static safe min() + { + return std::numeric_limits::min(); + } + static safe max() + { + return std::numeric_limits::max(); + } + + friend safe operator + ( const safe& a, const safe& b ) + { + if( b.value > 0 && a.value > (std::numeric_limits::max() - b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + if( b.value < 0 && a.value < (std::numeric_limits::min() - b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + return safe( a.value + b.value ); + } + friend safe operator - ( const safe& a, const safe& b ) + { + if( b.value > 0 && a.value < (std::numeric_limits::min() + b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + if( b.value < 0 && a.value > (std::numeric_limits::max() + b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + return safe( a.value - b.value ); + } + + friend safe operator * ( const safe& a, const safe& b ) + { + if( a.value > 0 ) + { + if( b.value > 0 ) + { + if( a.value > (std::numeric_limits::max() / b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + } + else + { + if( b.value < (std::numeric_limits::min() / a.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + } + } + else + { + if( b.value > 0 ) + { + if( a.value < (std::numeric_limits::min() / b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + } + else + { + if( a.value != 0 && b.value < (std::numeric_limits::max() / a.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + } + } + + return safe( a.value * b.value ); + } + + friend safe operator / ( const safe& a, const safe& b ) + { + if( b.value == 0 ) FC_CAPTURE_AND_THROW( divide_by_zero_exception, (a)(b) ); + if( a.value == std::numeric_limits::min() && b.value == -1 ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + return safe( a.value / b.value ); + } + friend safe operator % ( const safe& a, const safe& b ) + { + if( b.value == 0 ) FC_CAPTURE_AND_THROW( divide_by_zero_exception, (a)(b) ); + if( a.value == std::numeric_limits::min() && b.value == -1 ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + return safe( a.value % b.value ); + } + + safe operator - ()const + { + if( value == std::numeric_limits::min() ) FC_CAPTURE_AND_THROW( overflow_exception, (*this) ); + return safe( -value ); + } + + safe& operator += ( const safe& b ) + { + value = (*this + b).value; + return *this; + } + safe& operator -= ( const safe& b ) + { + value = (*this - b).value; + return *this; + } + safe& operator *= ( const safe& b ) + { + value = (*this * b).value; + return *this; + } + safe& operator /= ( const safe& b ) + { + value = (*this / b).value; + return *this; + } + safe& operator %= ( const safe& b ) + { + value = (*this % b).value; + return *this; + } + + safe& operator++() + { + *this += 1; + return *this; + } + safe operator++( int ) + { + safe bak = *this; + *this += 1; + return bak; + } + + safe& operator--() + { + *this -= 1; + return *this; + } + safe operator--( int ) + { + safe bak = *this; + *this -= 1; + return bak; + } + + friend bool operator == ( const safe& a, const safe& b ) + { + return a.value == b.value; + } + friend bool operator == ( const safe& a, const T& b ) + { + return a.value == b; + } + friend bool operator == ( const T& a, const safe& b ) + { + return a == b.value; + } + + friend bool operator < ( const safe& a, const safe& b ) + { + return a.value < b.value; + } + friend bool operator < ( const safe& a, const T& b ) + { + return a.value < b; + } + friend bool operator < ( const T& a, const safe& b ) + { + return a < b.value; + } + + friend bool operator > ( const safe& a, const safe& b ) + { + return a.value > b.value; + } + friend bool operator > ( const safe& a, const T& b ) + { + return a.value > b; + } + friend bool operator > ( const T& a, const safe& b ) + { + return a > b.value; + } + + friend bool operator != ( const safe& a, const safe& b ) + { + return !(a == b); + } + friend bool operator != ( const safe& a, const T& b ) + { + return !(a == b); + } + friend bool operator != ( const T& a, const safe& b ) + { + return !(a == b); + } + + friend bool operator <= ( const safe& a, const safe& b ) + { + return !(a > b); + } + friend bool operator <= ( const safe& a, const T& b ) + { + return !(a > b); + } + friend bool operator <= ( const T& a, const safe& b ) + { + return !(a > b); + } + + friend bool operator >= ( const safe& a, const safe& b ) + { + return !(a < b); + } + friend bool operator >= ( const safe& a, const T& b ) + { + return !(a < b); + } + friend bool operator >= ( const T& a, const safe& b ) + { + return !(a < b); + } + }; + +} + +FC_REFLECT_TEMPLATE( (typename T), safe, (value) ) diff --git a/include/fc/server.hpp b/include/fc/server.hpp deleted file mode 100644 index a2976c10e..000000000 --- a/include/fc/server.hpp +++ /dev/null @@ -1,27 +0,0 @@ - - -class istream { - public: - template - istream( T& s ) { - - } - - istream& read( char* buf, uint64_t s ); - int64_t readsome( char* buf, uint64_t s ); - bool eof()const; - - private: - struct vtable { - void* (*read)(void*, char* buf, uint64_t s ); - int64_t (*readsome)(void*, char* buf, uint64_t s ); - bool (*eof)(void* ) - }; - - vtable& _vtable; - void* _stream; -}; - - - - diff --git a/include/fc/sha1.hpp b/include/fc/sha1.hpp deleted file mode 100644 index 60a34499f..000000000 --- a/include/fc/sha1.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - class path; - - class sha1 { - public: - sha1(); - explicit sha1( const fc::string& hex_str ); - - fc::string str()const; - - operator fc::string()const; - - char* data()const; - - static sha1 hash( const char* d, uint32_t dlen ); - static sha1 hash( const fc::string& ); - static sha1 hash( const fc::path& ); - - template - static sha1 hash( const T& t ) { sha1::encoder e; e << t; return e.result(); } - - class encoder { - public: - encoder(); - ~encoder(); - - void write( const char* d, uint32_t dlen ); - void put( char c ) { write( &c, 1 ); } - void reset(); - sha1 result(); - - private: - struct impl; - fwd my; - }; - - template - inline friend T& operator<<( T& ds, const sha1& ep ) { - ds.write( ep.data(), sizeof(ep) ); - return ds; - } - - template - inline friend T& operator>>( T& ds, sha1& ep ) { - ds.read( ep.data(), sizeof(ep) ); - return ds; - } - friend sha1 operator << ( const sha1& h1, uint32_t i ); - friend bool operator == ( const sha1& h1, const sha1& h2 ); - friend bool operator != ( const sha1& h1, const sha1& h2 ); - friend sha1 operator ^ ( const sha1& h1, const sha1& h2 ); - friend bool operator >= ( const sha1& h1, const sha1& h2 ); - friend bool operator > ( const sha1& h1, const sha1& h2 ); - friend bool operator < ( const sha1& h1, const sha1& h2 ); - - uint32_t _hash[5]; - }; - - class value; - void pack( fc::value& , const fc::sha1& ); - void unpack( const fc::value& , fc::sha1& ); -} - - diff --git a/include/fc/sha256.hpp b/include/fc/sha256.hpp deleted file mode 100644 index 1b04db535..000000000 --- a/include/fc/sha256.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - class path; - - class sha256 { - public: - sha256(); - explicit sha256( const fc::string& hex_str ); - - fc::string str()const; - - operator fc::string()const; - - char* data()const; - - static sha256 hash( const char* d, uint32_t dlen ); - static sha256 hash( const fc::string& ); - static sha256 hash( const fc::path& ); - - template - static sha256 hash( const T& t ) { sha256::encoder e; e << t; return e.result(); } - - class encoder { - public: - encoder(); - ~encoder(); - - void write( const char* d, uint32_t dlen ); - void put( char c ) { write( &c, 1 ); } - void reset(); - sha256 result(); - - private: - struct impl; - fwd my; - }; - - template - inline friend T& operator<<( T& ds, const sha256& ep ) { - ds.write( ep.data(), sizeof(ep) ); - return ds; - } - - template - inline friend T& operator>>( T& ds, sha256& ep ) { - ds.read( ep.data(), sizeof(ep) ); - return ds; - } - friend sha256 operator << ( const sha256& h1, uint32_t i ); - friend bool operator == ( const sha256& h1, const sha256& h2 ); - friend bool operator != ( const sha256& h1, const sha256& h2 ); - friend sha256 operator ^ ( const sha256& h1, const sha256& h2 ); - friend bool operator >= ( const sha256& h1, const sha256& h2 ); - friend bool operator > ( const sha256& h1, const sha256& h2 ); - friend bool operator < ( const sha256& h1, const sha256& h2 ); - - uint64_t _hash[4]; - }; - - class value; - void pack( fc::value& , const fc::sha256& ); - void unpack( const fc::value& , fc::sha256& ); -} - - diff --git a/include/fc/shared_impl.cpp b/include/fc/shared_impl.cpp deleted file mode 100644 index 6781f1764..000000000 --- a/include/fc/shared_impl.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once -#include - -namespace fc { -template -typename shared_impl::impl& shared_impl::operator* ()const { return *_impl; } - -template -typename shared_impl::impl* shared_impl::operator-> ()const { return _impl.get(); } - -template -template -shared_impl::shared_impl( U&& u ):_impl(fc::forward(u)){} - -template -shared_impl::shared_impl( const shared_impl& u ):_impl(u._impl){} -//template -//shared_impl::shared_impl( shared_impl& u ):_impl(u._impl){} - -template -shared_impl::shared_impl( shared_impl&& u ):_impl(fc::move(u._impl)){} - -template -shared_impl& shared_impl::operator=( shared_impl&& u ) { - fc_swap(_impl,u._impl); - return *this; -} -template -shared_impl& shared_impl::operator=( const shared_impl& u ) { - _impl = u._impl; - return *this; -} - -template -bool shared_impl::operator !()const { return !_impl; } - -template -shared_impl::~shared_impl(){} - -} - -#define FC_REFERENCE_TYPE_IMPL( TYPE ) \ -template \ -TYPE::TYPE( A1&& a1 ) \ -:my( new typename fc::shared_impl::impl( fc::forward(a1) ) ){}\ -template \ -TYPE::TYPE( A1&& a1, A2&& a2 ) \ -:my( new typename fc::shared_impl::impl( fc::forward(a1), fc::forward(a2) ) ){}\ -template \ -TYPE::TYPE( A1&& a1, A2&& a2, A3&& a3 ) \ -:my( new fc::shared_impl::impl( fc::forward(a1), fc::forward(a2), fc::forward(a3) ) ){}\ -TYPE::TYPE( shared_impl::impl* m ) \ -:my(m){}\ -TYPE::TYPE( TYPE* c )\ -:my(fc::move(c->my)){ delete c; }\ -TYPE::TYPE( TYPE&& c )\ -:my(fc::move(c.my)){}\ -TYPE::TYPE( const TYPE& c )\ -:my(c.my){}\ -TYPE::TYPE() \ -:my( new fc::shared_impl::impl( ) ){}\ -TYPE::~TYPE(){}\ -bool TYPE::operator !()const { return !my; }\ -TYPE& TYPE::operator = ( const TYPE& c ) {\ - my = c.my;\ - return *this;\ -}\ -TYPE& TYPE::operator = ( TYPE&& c ) {\ - fc_swap( my._impl, c.my._impl );\ - return *this;\ -}\ -TYPE& TYPE::operator = ( TYPE* c ) {\ - fc_swap( my._impl, c->my._impl );\ - delete c;\ - return *this;\ -} \ -bool operator==( const TYPE& a, const TYPE& b ) {\ - return a.my._impl.get() == b.my._impl.get(); \ -} \ -bool operator!=( const TYPE& a, const TYPE& b ) {\ - return a.my._impl.get() != b.my._impl.get(); \ -} - - diff --git a/include/fc/shared_impl.hpp b/include/fc/shared_impl.hpp deleted file mode 100644 index 6b43ace22..000000000 --- a/include/fc/shared_impl.hpp +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once -#include - -namespace fc { - /** - * @class shared_impl - * @brief used to create types with reference semantics - * - * A type with reference semantics is effectively a shared pointer except that - * it is used like a Java or C# type using '.' notation instead of -> notation. - * - * It is ideal for use with classes that almost always get managed by a shared - * pointer (sockets and long-lived resources). These types are rarely, if ever - * copied by value and often by-value copy or 'deep-copy' has no real meaning. - * - * An additional feature of shared_impl is that your classes implementation - * should be private and defined entirely in your source file. There should be - * no member variables defined in your types header. - * - * To make this design pattern work requires a lot of 'boiler-plate' code for - * handling assignments, moves, copies, etc that cannot be provided via tradtional - * means such as templates or base classes. - * - * To create a new type with reference semantics you place FC_REFERENCE_TYPE(type) - * inside the private section of 'type'. Then in your source file you will - * need to define your 'private' data. - * - * @code - * #include - * - * FC_START_SHARED_IMPL( your_namespace::your_type ) - * impl( int x, int y ); // custom constructor - * - * ... - * int member_variable; - * your private member variables / methods go here. - * ... - * FC_END_SHARED_IMPL - * #include - * @endcode - * - * - * Lastly, you will need to provide the implementation your class below. This - * implementation will need to provide the matching implementations for the - * methods declared by FC_REFERENCE_TYPE(type) in your header. To do this - * use the FC_REFERENCE_TYPE_IMPL(type) - * - * @code - * namespace your_namespace { - * FC_REFERENCE_TYPE(your_type) - * ... your methods here... - * } - * @endcode - * - * Within the body of your methods you can access your private data and members - * via using 'my->member_variable' - * - * If you want to define custom constructors for your reference type, you will - * need to implement them inside the FC_START_SHARED_IMPL block using the pattern: - * - * @code - * FC_START_SHARED_IMPL( your_namespace::your_type ) - * impl( int x, int y ){} // custom constructor - * FC_END_SHARED_IMPL - * @code - * - * A limited number (3) of arguments are supported for custom constructors. - * - * Once you have defined your reference type you can use it like so: - * - * @code - * your_type val = nullptr; // create a null type - * your_type val2 = new your_type(...); // construct a new instance, unnecessary temporary heap alloc - * your_type val3(...); // constructs a new instance, more effecient - * - * val2.your_method(); - * val2 = nullptr; // reset val2 to a null object - * - * val2 = val3; // val2 and val3 now reference the same data - * if( !!val2 ){} // val2 is not null - * else{} // val2 is null - * @endcode - * - * As you can see, when creating types with this method your code will - * look and act like a Java or C# garbage collected type. - * - * Often times your private methods will need to call your public methods, to achieve - * this you can use the following techinque: - * - * @code - * FC_START_SHARED_IMPL( your_namespace::your_type ) - * void private_method( int x, int y ){ - * auto s = self(); - * s.public_method(); - * } - * FC_END_SHARED_IMPL - * @code - * - * For performance reasons, it is best to only call 'self()' once and save the - * result to avoid unnecessary copies of shared pointers which require atomic - * operations. - * - */ - template - struct shared_impl { - class impl; - impl& operator* ()const; - impl* operator-> ()const; - - bool operator !()const; - - template - explicit shared_impl( U&& u ); - - shared_impl( const shared_impl& u ); - // shared_impl( shared_impl& u ); - shared_impl( shared_impl&& u ); - shared_impl& operator=( shared_impl&& u ); - shared_impl& operator=( const shared_impl& u ); - - ~shared_impl(); - - fc::shared_ptr::impl> _impl; - }; - -} - -#define FC_REFERENCE_TYPE( TYPE ) \ - public:\ - TYPE(); \ - TYPE( std::nullptr_t ); \ - TYPE( TYPE* ); \ - TYPE( TYPE&& ); \ - TYPE( const TYPE& ); \ - template \ - TYPE( A1&& ); \ - template \ - TYPE( A1&&, A2&& ); \ - template \ - TYPE( A1&&, A2&&, A3&& ); \ - ~TYPE(); \ - bool operator !()const; \ - friend bool operator==( const TYPE& a, const TYPE& b ); \ - friend bool operator!=( const TYPE& a, const TYPE& b ); \ - TYPE& operator = ( const TYPE& ); \ - TYPE& operator = ( TYPE&& );\ - TYPE& operator = ( TYPE* );\ - TYPE& operator = ( std::nullptr_t );\ - private: \ - friend class shared_impl::impl; \ - TYPE( shared_impl::impl* m ); \ - shared_impl my; - -#define FC_START_SHARED_IMPL( SCOPED_TYPE ) \ -namespace fc { \ -template<> \ -class fc::shared_impl::impl : public fc::retainable { \ - public:\ - SCOPED_TYPE self() { return SCOPED_TYPE(this); } \ - - -#define FC_END_SHARED_IMPL }; } - diff --git a/include/fc/shared_ptr.hpp b/include/fc/shared_ptr.hpp index e7f0ab840..60a2cd36f 100644 --- a/include/fc/shared_ptr.hpp +++ b/include/fc/shared_ptr.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include namespace fc { diff --git a/include/fc/signal.hpp b/include/fc/signal.hpp index b2ed9eebf..73c2b7fb7 100644 --- a/include/fc/signal.hpp +++ b/include/fc/signal.hpp @@ -1,74 +1,130 @@ #pragma once -#include #include - +#include +#include +#include +#include +#include +#ifdef emit +#undef emit +#endif namespace fc { template class signal { + private: + typedef std::function func_type; + typedef std::vector> list_type; public: - typedef int64_t connection_id_type; + typedef void* connection_id_type; template connection_id_type connect( Functor&& f ) { - auto c = new std::function( fc::forward(f) ); - _handlers.push_back( c ); - return reinterpret_cast(c); + fc::unique_lock lock(_mutex); + //auto c = new std::function( fc::forward(f) ); + _handlers.push_back( std::make_shared(f) ); + return reinterpret_cast(_handlers.back().get()); } #ifdef WIN32 template void emit( Arg&& arg ) { - for( size_t i = 0; i < _handlers.size(); ++i ) { - (*_handlers[i])( fc::forward(arg) ); + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(arg) ); + } + } + void operator()() { + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])(); } } template void operator()( Arg&& arg ) { - for( size_t i = 0; i < _handlers.size(); ++i ) { - (*_handlers[i])( fc::forward(arg) ); + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(arg) ); } } template void emit( Arg&& arg, Arg2&& arg2 ) { - for( size_t i = 0; i < _handlers.size(); ++i ) { - (*_handlers[i])( fc::forward(arg), fc::forward(arg2) ); + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(arg), fc::forward(arg2) ); } } template void operator()( Arg&& arg, Arg2&& arg2 ) { - for( size_t i = 0; i < _handlers.size(); ++i ) { - (*_handlers[i])( fc::forward(arg), fc::forward(arg2) ); + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(arg), fc::forward(arg2) ); + } + } + template + void emit( Arg&& arg, Arg2&& arg2, Arg3&& arg3 ) { + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(arg), fc::forward(arg2), fc::forward(arg3) ); + } + } + template + void operator()( Arg&& arg, Arg2&& arg2, Arg3&& arg3 ) { + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(arg), fc::forward(arg2), fc::forward(arg3) ); } } #else template void emit( Args&&... args ) { - for( size_t i = 0; i < _handlers.size(); ++i ) { - (*_handlers[i])( fc::forward(args)... ); + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(args)... ); } } template void operator()( Args&&... args ) { - for( size_t i = 0; i < _handlers.size(); ++i ) { - (*_handlers[i])( fc::forward(args)... ); + list_type handlers = getHandlers(); + for( size_t i = 0; i < handlers.size(); ++i ) { + (*handlers[i])( fc::forward(args)... ); } } #endif void disconnect( connection_id_type cid ) { + fc::unique_lock lock(_mutex); auto itr = _handlers.begin(); while( itr != _handlers.end() ) { - if( reinterpret_cast(*itr) == cid ) { - delete *itr; + if( reinterpret_cast(itr->get()) == cid ) { _handlers.erase(itr); } ++itr; } } + signal() + { + _handlers.reserve(4); + } + //~signal() + //{ + // for( auto itr = _handlers.begin(); itr != _handlers.end(); ++itr ) + // { + // delete *itr; + // } + // _handlers.clear(); + //} private: - fc::vector< std::function* > _handlers; + fc::mutex _mutex; + list_type _handlers; + + list_type getHandlers() + { + fc::unique_lock lock(_mutex); + list_type handlers(_handlers); + return handlers; + } }; } diff --git a/include/fc/signals.hpp b/include/fc/signals.hpp index 97e351850..dd886a6e2 100644 --- a/include/fc/signals.hpp +++ b/include/fc/signals.hpp @@ -1,27 +1,49 @@ -#include -#include -#include +#pragma once -#include +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#pragma warning(push) +#pragma warning(disable:4996) +#endif + +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#pragma warning(pop) +#endif + +#include +#include namespace fc { #if !defined(BOOST_NO_TEMPLATE_ALIASES) template - using signal = boost::signal; + using signal = boost::signals2::signal; + + using scoped_connection = boost::signals2::scoped_connection; #else + /** Workaround for missing Template Aliases feature in the VS 2012. + \warning Class defined below cannot have defined constructor (even base class has it) + since it is impossible to reference directly template class arguments outside this class. + This code will work until someone will use non-default constructor as it is defined in + boost::signals2::signal. + */ + template + class signal : public boost::signals2::signal + { + public: + }; #endif template - inline T wait( boost::signal& sig, const microseconds& timeout_us=microseconds::max() ) { - typename promise::ptr p(new promise()); - boost::signals::scoped_connection c = sig.connect( [=]( T t ) { p->set_value(t); } ); + inline T wait( boost::signals2::signal& sig, const microseconds& timeout_us=microseconds::maximum() ) { + typename promise::ptr p(new promise("fc::signal::wait")); + boost::signals2::scoped_connection c( sig.connect( [=]( T t ) { p->set_value(t); } )); return p->wait( timeout_us ); } - inline void wait( boost::signal& sig, const microseconds& timeout_us=microseconds::max() ) { - promise::ptr p(new promise()); - boost::signals::scoped_connection c = sig.connect( [=]() { p->set_value(); } ); + inline void wait( boost::signals2::signal& sig, const microseconds& timeout_us=microseconds::maximum() ) { + promise::ptr p(new promise("fc::signal::wait")); + boost::signals2::scoped_connection c( sig.connect( [=]() { p->set_value(); } )); p->wait( timeout_us ); } } - diff --git a/include/fc/smart_ref_fwd.hpp b/include/fc/smart_ref_fwd.hpp new file mode 100644 index 000000000..145dae5d9 --- /dev/null +++ b/include/fc/smart_ref_fwd.hpp @@ -0,0 +1,41 @@ +#pragma once + +namespace fc { + /** + * @brief Used to forward declare value types and break circular dependencies or use heap allocation + * + * A smart reference is heap allocated, move-aware, and is gauranteed to never be null (except after a move) + */ + template + class smart_ref { + public: + template smart_ref( U&& u ); + template smart_ref( U&& u, V&& v ); + template smart_ref( U&& u, V&& v, X&&, Y&& ); + smart_ref(); + + smart_ref( const smart_ref& f ); + smart_ref( smart_ref&& f ); + + operator const T&()const; + operator T&(); + + T& operator*(); + const T& operator*()const; + const T* operator->()const; + + T* operator->(); + bool operator !()const; + + template + T& operator = ( U&& u ); + + T& operator = ( smart_ref&& u ); + T& operator = ( const smart_ref& u ); + + ~smart_ref(); + + private: + T* impl; + }; +} diff --git a/include/fc/smart_ref_impl.hpp b/include/fc/smart_ref_impl.hpp new file mode 100644 index 000000000..7c31ceaa3 --- /dev/null +++ b/include/fc/smart_ref_impl.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include +#include + +namespace fc { + + namespace detail { + + template + struct insert_op { + typedef decltype( *((A*)0) << *((typename fc::remove_reference::type*)0) ) type; + }; + + template + struct extract_op { + A* a; + U* u; + typedef decltype( *a >> *u ) type; + }; + } + + template + auto operator << ( U& u, const smart_ref& f ) -> typename detail::insert_op::type { return u << *f; } + + template + auto operator >> ( U& u, smart_ref& f ) -> typename detail::extract_op::type { return u >> *f; } + + template + bool smart_ref::operator !()const { return !(**this); } + + template + template + smart_ref::smart_ref( U&& u ) { + impl = new (this) T( fc::forward(u) ); + } + + template + template + smart_ref::smart_ref( U&& u, V&& v ) { + impl = new T( fc::forward(u), fc::forward(v) ); + } + template + template + smart_ref::smart_ref( U&& u, V&& v, X&& x, Y&& y ) { + impl = new T( fc::forward(u), fc::forward(v), fc::forward(x), fc::forward(y) ); + } + + template + smart_ref::smart_ref() { + impl = new T; + } + template + smart_ref::smart_ref( const smart_ref& f ){ + impl = new T( *f ); + } + template + smart_ref::smart_ref( smart_ref&& f ){ + impl = new T( fc::move(*f) ); + } + + template + smart_ref::operator T&() { return *impl; } + template + smart_ref::operator const T&()const { return *impl; } + + template + T& smart_ref::operator*() { return *impl; } + template + const T& smart_ref::operator*()const { return *impl; } + template + const T* smart_ref::operator->()const { return impl; } + + template + T* smart_ref::operator->(){ return impl; } + + template + smart_ref::~smart_ref() { + delete impl; + } + + template + template + T& smart_ref::operator = ( U&& u ) { + return **this = fc::forward(u); + } + + template + T& smart_ref::operator = ( smart_ref&& u ) { + if( &u == this ) return *impl; + if( impl ) delete impl; + impl = u.impl; + u.impl = nullptr; + return *impl; + } + + template + T& smart_ref::operator = ( const smart_ref& u ) { + if( &u == this ) return *impl; + return **this = *u; + } + +} // namespace fc + diff --git a/include/fc/ssh/client.hpp b/include/fc/ssh/client.hpp index b88451d62..eb24a8e43 100644 --- a/include/fc/ssh/client.hpp +++ b/include/fc/ssh/client.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -59,7 +59,8 @@ namespace fc { * via client::create(); * */ - class client { + class client + { public: enum trace_level { TRACE_NONE = 0, @@ -88,14 +89,26 @@ namespace fc { void set_logger( const logger& lgr ); const logger& get_logger()const; + /** + * Connect, with no password specified. Authentication will try public key, + * (via agent or explicitly-set key), empty password, then keyboard-interactive + */ void connect( const fc::string& user, const fc::string& host, uint16_t port = 22); /** - * Connect via password or keyboard-interactive + * Connect, specifying a password to be used for password authentication */ void connect( const fc::string& user, const fc::string& pass, const fc::string& host, uint16_t port = 22); /** + * @note THIS METHOD IS DEPRECATED and should be replace with: + * + * ssh::client_ptr sshc = std::make_shared(); + * sshc->connect( ... ) + * ssh::process_ptr proc = std::make_shared( sshc ); + * proc->exec( ... ) + * + * * @brief execute command on remote machine * @param pty_type - whether or not to request a PTY when executing this process, this is necessary * for interactive (non-buffered) IO with the remote process, if left empty no pty will be @@ -104,8 +117,8 @@ namespace fc { * @note Processes launched in this manner will fully buffer stdin and stdout regardless of whether * the process calls flush(). If you need unbuffered (streaming, realtime) access to standard * out then you must launch the process via a shell. - */ ssh::process exec( const fc::string& cmd, const fc::string& pty_type = "" ); + */ /** @@ -115,7 +128,7 @@ namespace fc { * transfer, the callback should return true. To cancel the callback should return false. */ void scp_send( const fc::path& local_path, const fc::path& remote_path, - std::function progress = [](size_t,size_t){return true;} ); + std::function progress = [](uint64_t,uint64_t){return true;} ); /** * @brief recursively sends the contents of local_dir to the remote_path @@ -126,13 +139,22 @@ namespace fc { * Progress will be reported as total bytes transferred for all files. */ void scp_send_dir( const fc::path& local_dir, const fc::path& remote_path, - std::function progress = [](size_t,size_t){return true;} ); + std::function progress = [](uint64_t,uint64_t){return true;} ); /** * @pre remote_path is not a directory * @post remote file is removed from the remote filesystem */ void rm( const fc::path& remote_path ); + + /** + * @pre remote_path is a directory + * @post remote directory is removed from the remote filesystem + */ + void rmdir( const fc::path& remote_path ); + + void rmdir_recursive( const fc::path& remote_path ); + file_attrib stat( const fc::path& remote_path ); /** @@ -149,6 +171,13 @@ namespace fc { */ void create_directories( const fc::path& remote_dir, int mode = owner_read|owner_write|owner_exec ); + /** + * Sets whether the remote system is believed to be a Windows box (by default, it's + * assumed to be running UNIX. This alters how command-line arguments are quoted + * and possibly how filenames are altered when copying files + */ + void set_remote_system_is_windows(bool is_windows = true); + void close(); client(); @@ -157,7 +186,8 @@ namespace fc { private: friend class process; friend class detail::process_impl; - fc::shared_ptr my; + std::unique_ptr my; }; + typedef std::shared_ptr client_ptr; } } // namespace fc::ssh diff --git a/include/fc/ssh/process.hpp b/include/fc/ssh/process.hpp index 8d4c4b8b2..08eb089b5 100644 --- a/include/fc/ssh/process.hpp +++ b/include/fc/ssh/process.hpp @@ -1,13 +1,8 @@ #pragma once -#include -#include +#include -namespace fc { - - class istream; - class ostream; - - namespace ssh { +namespace fc { namespace ssh +{ class client; @@ -21,39 +16,43 @@ namespace fc { * * Process can only be created by mace::ssh::client. */ - class process { //: public fc::retainable { + class process : public iprocess + { public: - //typedef fc::shared_ptr ptr; + virtual iprocess& exec( const fc::path& exe, std::vector args, + const fc::path& work_dir = fc::path(), exec_opts opts = open_all ); - process( const process& p ); - process( process&& p ); - process(); - ~process(); + /** + * Blocks until the result code of the process has been returned. + */ + virtual int result(); - bool valid()const; /** - * Blocks until the result code of the process has been returned. + * Not supported. libssh2 does not support sending signals to remote processes. + * closing in_stream() is the best you can do */ - int result(); + virtual void kill(); + + /** - * @brief returns a stream that writes to the procss' stdin + * @brief returns a stream that writes to the process' stdin */ - fc::ostream& in_stream()const; + virtual fc::buffered_ostream_ptr in_stream(); + /** * @brief returns a stream that reads from the process' stdout */ - fc::istream& out_stream()const; + virtual fc::buffered_istream_ptr out_stream(); /** * @brief returns a stream that reads from the process' stderr */ - fc::istream& err_stream()const; + virtual fc::buffered_istream_ptr err_stream(); - process& operator=( const process& p ); + process( fc::ssh::client_ptr c ); + ~process(); private: - friend class client; - process( client& c, const fc::string& cmd, const fc::string& pty_type = fc::string() ); - - fc::shared_ptr my; + std::unique_ptr my; }; + } } // fc::ssh diff --git a/include/fc/sstream.hpp b/include/fc/sstream.hpp deleted file mode 100644 index 79baac377..000000000 --- a/include/fc/sstream.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include -#include - -namespace fc { - - class stringstream : virtual public iostream { - public: - stringstream(); - stringstream( fc::string& s); - stringstream( const fc::string& s); - ~stringstream(); - - fc::string str(); - - virtual bool eof()const; - virtual ostream& write( const char* buf, size_t len ); - virtual size_t readsome( char* buf, size_t len ); - virtual istream& read( char* buf, size_t len ); - virtual void close(); - virtual void flush(); - - protected: - virtual istream& read( int64_t& ); - virtual istream& read( uint64_t& ); - virtual istream& read( int32_t& ); - virtual istream& read( uint32_t& ); - virtual istream& read( int16_t& ); - virtual istream& read( uint16_t& ); - virtual istream& read( int8_t& ); - virtual istream& read( uint8_t& ); - virtual istream& read( float& ); - virtual istream& read( double& ); - virtual istream& read( bool& ); - virtual istream& read( char& ); - virtual istream& read( fc::string& ); - - virtual ostream& write( const fc::string& ); - - private: - class impl; - fwd my; - }; - -} diff --git a/include/fc/static_variant.hpp b/include/fc/static_variant.hpp new file mode 100644 index 000000000..9aab79037 --- /dev/null +++ b/include/fc/static_variant.hpp @@ -0,0 +1,386 @@ +/** This source adapted from https://github.com/kmicklas/variadic-static_variant + * + * Copyright (C) 2013 Kenneth Micklas + * + * 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. + * + **/ +#pragma once +#include +#include +#include + +namespace fc { + +// Implementation details, the user should not import this: +namespace impl { + +template +struct storage_ops; + +template +struct position; + +template +struct type_info; + +template +struct copy_construct +{ + typedef void result_type; + StaticVariant& sv; + copy_construct( StaticVariant& s ):sv(s){} + template + void operator()( const T& v )const + { + sv.init(v); + } +}; + +template +struct move_construct +{ + typedef void result_type; + StaticVariant& sv; + move_construct( StaticVariant& s ):sv(s){} + template + void operator()( T& v )const + { + sv.init( std::move(v) ); + } +}; + +template +struct storage_ops { + static void del(int n, void *data) {} + static void con(int n, void *data) {} + + template + static typename visitor::result_type apply(int n, void *data, visitor& v) {} + + template + static typename visitor::result_type apply(int n, void *data, const visitor& v) {} + + template + static typename visitor::result_type apply(int n, const void *data, visitor& v) {} + + template + static typename visitor::result_type apply(int n, const void *data, const visitor& v) {} +}; + +template +struct storage_ops { + static void del(int n, void *data) { + if(n == N) reinterpret_cast(data)->~T(); + else storage_ops::del(n, data); + } + static void con(int n, void *data) { + if(n == N) new(reinterpret_cast(data)) T(); + else storage_ops::con(n, data); + } + + template + static typename visitor::result_type apply(int n, void *data, visitor& v) { + if(n == N) return v(*reinterpret_cast(data)); + else return storage_ops::apply(n, data, v); + } + + template + static typename visitor::result_type apply(int n, void *data, const visitor& v) { + if(n == N) return v(*reinterpret_cast(data)); + else return storage_ops::apply(n, data, v); + } + + template + static typename visitor::result_type apply(int n, const void *data, visitor& v) { + if(n == N) return v(*reinterpret_cast(data)); + else return storage_ops::apply(n, data, v); + } + + template + static typename visitor::result_type apply(int n, const void *data, const visitor& v) { + if(n == N) return v(*reinterpret_cast(data)); + else return storage_ops::apply(n, data, v); + } +}; + +template +struct storage_ops { + static void del(int n, void *data) { + FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid."); + } + static void con(int n, void *data) { + FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." ); + } + + template + static typename visitor::result_type apply(int n, void *data, visitor& v) { + FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." ); + } + template + static typename visitor::result_type apply(int n, void *data, const visitor& v) { + FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." ); + } + template + static typename visitor::result_type apply(int n, const void *data, visitor& v) { + FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." ); + } + template + static typename visitor::result_type apply(int n, const void *data, const visitor& v) { + FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." ); + } +}; + +template +struct position { + static const int pos = -1; +}; + +template +struct position { + static const int pos = 0; +}; + +template +struct position { + static const int pos = position::pos != -1 ? position::pos + 1 : -1; +}; + +template +struct type_info { + static const bool no_reference_types = false; + static const bool no_duplicates = position::pos == -1 && type_info::no_duplicates; + static const size_t size = type_info::size > sizeof(T&) ? type_info::size : sizeof(T&); + static const size_t count = 1 + type_info::count; +}; + +template +struct type_info { + static const bool no_reference_types = type_info::no_reference_types; + static const bool no_duplicates = position::pos == -1 && type_info::no_duplicates; + static const size_t size = type_info::size > sizeof(T) ? type_info::size : sizeof(T&); + static const size_t count = 1 + type_info::count; +}; + +template<> +struct type_info<> { + static const bool no_reference_types = true; + static const bool no_duplicates = true; + static const size_t count = 0; + static const size_t size = 0; +}; + +} // namespace impl + +template +class static_variant { + static_assert(impl::type_info::no_reference_types, "Reference types are not permitted in static_variant."); + static_assert(impl::type_info::no_duplicates, "static_variant type arguments contain duplicate types."); + + int _tag; + char storage[impl::type_info::size]; + + template + void init(const X& x) { + _tag = impl::position::pos; + new(storage) X(x); + } + + template + void init(X&& x) { + _tag = impl::position::pos; + new(storage) X( std::move(x) ); + } + + template + friend struct impl::copy_construct; + template + friend struct impl::move_construct; +public: + template + struct tag + { + static_assert( + impl::position::pos != -1, + "Type not in static_variant." + ); + static const int value = impl::position::pos; + }; + static_variant() + { + _tag = 0; + impl::storage_ops<0, Types...>::con(0, storage); + } + + template + static_variant( const static_variant& cpy ) + { + cpy.visit( impl::copy_construct(*this) ); + } + static_variant( const static_variant& cpy ) + { + cpy.visit( impl::copy_construct(*this) ); + } + + static_variant( static_variant&& mv ) + { + mv.visit( impl::move_construct(*this) ); + } + + template + static_variant(const X& v) { + static_assert( + impl::position::pos != -1, + "Type not in static_variant." + ); + init(v); + } + ~static_variant() { + impl::storage_ops<0, Types...>::del(_tag, storage); + } + + + template + static_variant& operator=(const X& v) { + static_assert( + impl::position::pos != -1, + "Type not in static_variant." + ); + this->~static_variant(); + init(v); + return *this; + } + static_variant& operator=( const static_variant& v ) + { + if( this == &v ) return *this; + this->~static_variant(); + v.visit( impl::copy_construct(*this) ); + return *this; + } + static_variant& operator=( static_variant&& v ) + { + if( this == &v ) return *this; + this->~static_variant(); + v.visit( impl::move_construct(*this) ); + return *this; + } + friend bool operator == ( const static_variant& a, const static_variant& b ) + { + return a.which() == b.which(); + } + friend bool operator < ( const static_variant& a, const static_variant& b ) + { + return a.which() < b.which(); + } + + template + X& get() { + static_assert( + impl::position::pos != -1, + "Type not in static_variant." + ); + if(_tag == impl::position::pos) { + void* tmp(storage); + return *reinterpret_cast(tmp); + } else { + FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); + // std::string("static_variant does not contain value of type ") + typeid(X).name() + // ); + } + } + template + const X& get() const { + static_assert( + impl::position::pos != -1, + "Type not in static_variant." + ); + if(_tag == impl::position::pos) { + const void* tmp(storage); + return *reinterpret_cast(tmp); + } else { + FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); + } + } + template + typename visitor::result_type visit(visitor& v) { + return impl::storage_ops<0, Types...>::apply(_tag, storage, v); + } + + template + typename visitor::result_type visit(const visitor& v) { + return impl::storage_ops<0, Types...>::apply(_tag, storage, v); + } + + template + typename visitor::result_type visit(visitor& v)const { + return impl::storage_ops<0, Types...>::apply(_tag, storage, v); + } + + template + typename visitor::result_type visit(const visitor& v)const { + return impl::storage_ops<0, Types...>::apply(_tag, storage, v); + } + + static int count() { return impl::type_info::count; } + void set_which( int w ) { + FC_ASSERT( w < count() ); + this->~static_variant(); + _tag = w; + impl::storage_ops<0, Types...>::con(_tag, storage); + } + + int which() const {return _tag;} +}; + +template +struct visitor { + typedef Result result_type; +}; + + struct from_static_variant + { + variant& var; + from_static_variant( variant& dv ):var(dv){} + + typedef void result_type; + template void operator()( const T& v )const + { + to_variant( v, var ); + } + }; + + struct to_static_variant + { + const variant& var; + to_static_variant( const variant& dv ):var(dv){} + + typedef void result_type; + template void operator()( T& v )const + { + from_variant( var, v ); + } + }; + + + template void to_variant( const fc::static_variant& s, fc::variant& v ) + { + variant tmp; + variants vars(2); + vars[0] = s.which(); + s.visit( from_static_variant(vars[1]) ); + v = std::move(vars); + } + template void from_variant( const fc::variant& v, fc::static_variant& s ) + { + auto ar = v.get_array(); + if( ar.size() < 2 ) return; + s.set_which( ar[0].as_uint64() ); + s.visit( to_static_variant(ar[1]) ); + } + + template struct get_typename { static const char* name() { return typeid(static_variant).name(); } }; +} // namespace fc diff --git a/include/fc/stream.hpp b/include/fc/stream.hpp deleted file mode 100644 index f5f97fc07..000000000 --- a/include/fc/stream.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -#include -#include - -namespace fc { - class string; - - class istream { - public: - virtual ~istream(){}; - - virtual size_t readsome( char* buf, size_t len ) = 0; - - template - friend istream& operator>>( istream& i, T& v ){ return i.read(v); } - - virtual bool eof()const = 0; - - protected: - virtual istream& read( int64_t& ) = 0; - virtual istream& read( uint64_t& ) = 0; - virtual istream& read( int32_t& ) = 0; - virtual istream& read( uint32_t& ) = 0; - virtual istream& read( int16_t& ) = 0; - virtual istream& read( uint16_t& ) = 0; - virtual istream& read( int8_t& ) = 0; - virtual istream& read( uint8_t& ) = 0; - virtual istream& read( float& ) = 0; - virtual istream& read( double& ) = 0; - virtual istream& read( bool& ) = 0; - virtual istream& read( char& ) = 0; - virtual istream& read( fc::string& ) = 0; - }; - - class ostream { - public: - virtual ~ostream(){}; - - virtual size_t write( const char* buf, size_t len ) = 0; - virtual void close() = 0; - virtual void flush() = 0; - - template - friend ostream& operator<<( ostream& o, const T& v ){ return o.write(fc::lexical_cast(v)); } - - protected: - virtual ostream& write( const fc::string& ) = 0; - }; - class iostream : public virtual ostream, public virtual istream {}; - - bool getline( fc::istream&, fc::string&, char delim = '\n' ); - - struct cout_t : virtual public ostream { - virtual size_t write( const char* buf, size_t len ); - virtual void close(); - virtual void flush(); - - virtual ostream& write( const fc::string& ); - }; - - struct cerr_t : virtual public ostream { - virtual size_t write( const char* buf, size_t len ); - virtual void close(); - virtual void flush(); - - virtual ostream& write( const fc::string& ); - }; - - struct cin_t : virtual public istream { - virtual size_t readsome( char* buf, size_t len ); - virtual bool eof()const; - - virtual istream& read( int64_t& ); - virtual istream& read( uint64_t& ); - virtual istream& read( int32_t& ); - virtual istream& read( uint32_t& ); - virtual istream& read( int16_t& ); - virtual istream& read( uint16_t& ); - virtual istream& read( int8_t& ); - virtual istream& read( uint8_t& ); - virtual istream& read( float& ); - virtual istream& read( double& ); - virtual istream& read( bool& ); - virtual istream& read( char& ); - virtual istream& read( fc::string& ); - }; - - - extern cout_t cout; - extern cerr_t cerr; - extern cin_t cin; -} - diff --git a/include/fc/string.hpp b/include/fc/string.hpp index 50215b0a4..0ae999b6b 100644 --- a/include/fc/string.hpp +++ b/include/fc/string.hpp @@ -3,6 +3,35 @@ #include #include +#ifndef USE_FC_STRING +#include +namespace fc +{ + typedef std::string string; + + int64_t to_int64( const fc::string& ); + uint64_t to_uint64( const fc::string& ); + double to_double( const fc::string& ); + fc::string to_string( double ); + fc::string to_string( uint64_t ); + fc::string to_string( int64_t ); + fc::string to_string( uint16_t ); + std::string to_pretty_string( int64_t ); + inline fc::string to_string( int32_t v ) { return to_string( int64_t(v) ); } + inline fc::string to_string( uint32_t v ){ return to_string( uint64_t(v) ); } +#ifdef __APPLE__ + inline fc::string to_string( size_t s) { return to_string(uint64_t(s)); } +#endif + + typedef fc::optional ostring; + class variant_object; + fc::string format_string( const fc::string&, const variant_object& ); + fc::string trim( const fc::string& ); + fc::string to_lower( const fc::string& ); + string trim_and_normalize_spaces( const string& s ); +} + +#else /** * There is debate about whether doing this is 'standard conforming', but @@ -22,10 +51,8 @@ namespace std { typedef basic_string, allocator > string; } -#include namespace fc { - // typedef std::string string; /** * @brief wrapper on std::string * @@ -44,7 +71,8 @@ namespace fc { public: typedef char* iterator; typedef const char* const_iterator; - static const size_t npos = -1; + enum { npos = size_t(-1) }; + // static const size_t npos;// = -1; string(); string( const std::string& s ); @@ -74,14 +102,21 @@ namespace fc { void reserve( size_t ); size_t size()const; size_t find( char c, size_t pos = 0 )const; + size_t find(const fc::string& str, size_t pos = 0) const; + size_t find(const char* s, size_t pos = 0) const; size_t rfind( char c, size_t pos = npos )const; size_t rfind( const char* c, size_t pos = npos )const; size_t rfind( const fc::string& c, size_t pos = npos )const; + size_t find_first_of (const fc::string& str, size_t pos = 0) const; + size_t find_first_of (const char* s, size_t pos = 0) const; + string& replace(size_t pos, size_t len, const fc::string& str); + string& replace(size_t pos, size_t len, const char* s); void resize( size_t s ); void clear(); const char* c_str()const; + char* data(); bool operator == ( const char* s )const; bool operator == ( const string& s )const; @@ -101,7 +136,17 @@ namespace fc { fc::fwd my; }; + int64_t to_int64( const fc::string& ); + uint64_t to_uint64( const fc::string& ); + double to_double( const fc::string& ); + fc::string to_string( double ); + fc::string to_string( uint64_t ); + fc::string to_string( int64_t ); + typedef fc::optional ostring; + class variant_object; + fc::string format_string( const fc::string&, const variant_object& ); } // namespace fc +#endif diff --git a/include/fc/super_fast_hash.hpp b/include/fc/super_fast_hash.hpp deleted file mode 100644 index ab163909e..000000000 --- a/include/fc/super_fast_hash.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include - -namespace fc { - uint32_t super_fast_hash (const char * data, int len); - uint32_t super_fast_hash (const fc::string& str ); -} - - diff --git a/include/fc/tcp_socket.hpp b/include/fc/tcp_socket.hpp deleted file mode 100644 index e2eb354a1..000000000 --- a/include/fc/tcp_socket.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - namespace ip { class endpoint; } - class tcp_socket : public iostream { - public: - tcp_socket(); - ~tcp_socket(); - - void connect_to( const fc::ip::endpoint& e ); - - /// istream interface - /// @{ - virtual size_t readsome( char* buffer, size_t max ); - virtual istream& read( char* buffer, size_t s ); - virtual bool eof()const; - /// @} - - /// ostream interface - /// @{ - virtual ostream& write( const char* buffer, size_t len ); - virtual void flush(); - virtual void close(); - /// @} - - bool is_open()const; - - private: - friend class tcp_server; - class impl; - fc::fwd my; - }; - - class tcp_server { - public: - tcp_server(); - ~tcp_server(); - - void close(); - bool accept( tcp_socket& s ); - void listen( uint16_t port ); - - private: - // non copyable - tcp_server( const tcp_server& ); - tcp_server& operator=(const tcp_server& s ); - - class impl; - impl* my; - }; - -} // namesapce fc - diff --git a/include/fc/future.hpp b/include/fc/thread/future.hpp similarity index 65% rename from include/fc/future.hpp rename to include/fc/thread/future.hpp index 438c5b272..cc2c317ef 100644 --- a/include/fc/future.hpp +++ b/include/fc/thread/future.hpp @@ -1,17 +1,29 @@ -#ifndef _FC_FUTURE_HPP_ -#define _FC_FUTURE_HPP_ +#pragma once #include #include #include -#include -#include +#include +#include #include +//#define FC_TASK_NAMES_ARE_MANDATORY 1 +#ifdef FC_TASK_NAMES_ARE_MANDATORY +# define FC_TASK_NAME_DEFAULT_ARG +#else +# define FC_TASK_NAME_DEFAULT_ARG = "?" +#endif + +//#define FC_CANCELATION_REASONS_ARE_MANDATORY 1 +#ifdef FC_CANCELATION_REASONS_ARE_MANDATORY +# define FC_CANCELATION_REASON_DEFAULT_ARG +#else +# define FC_CANCELATION_REASON_DEFAULT_ARG = nullptr +#endif + namespace fc { class abstract_thread; - struct void_t; + struct void_t{}; class priority; - class exception_ptr; class thread; namespace detail { @@ -46,14 +58,15 @@ namespace fc { }; } - class promise_base : virtual public retainable { + class promise_base : public virtual retainable{ public: - typedef shared_ptr ptr; - promise_base(const char* desc="?"); + typedef fc::shared_ptr ptr; + promise_base(const char* desc FC_TASK_NAME_DEFAULT_ARG); const char* get_desc()const; - void cancel(); + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG); + bool canceled()const { return _canceled; } bool ready()const; bool error()const; @@ -63,6 +76,7 @@ namespace fc { void _wait( const microseconds& timeout_us ); void _wait_until( const time_point& timeout_us ); void _enqueue_thread(); + void _dequeue_thread(); void _notify(); void _set_timeout(); void _set_value(const void* v); @@ -78,9 +92,15 @@ namespace fc { bool _ready; mutable spin_yield_lock _spin_yield; thread* _blocked_thread; + unsigned _blocked_fiber_count; time_point _timeout; fc::exception_ptr _exceptp; bool _canceled; +#ifndef NDEBUG + protected: + const char* _cancellation_reason; + private: +#endif const char* _desc; detail::completion_handler* _compl; }; @@ -88,12 +108,12 @@ namespace fc { template class promise : virtual public promise_base { public: - typedef shared_ptr< promise > ptr; - promise( const char* desc = "?" ):promise_base(desc){} + typedef fc::shared_ptr< promise > ptr; + promise( const char* desc FC_TASK_NAME_DEFAULT_ARG):promise_base(desc){} promise( const T& val ){ set_value(val); } promise( T&& val ){ set_value(fc::move(val) ); } - const T& wait(const microseconds& timeout = microseconds::max() ){ + const T& wait(const microseconds& timeout = microseconds::maximum() ){ this->_wait( timeout ); return *result; } @@ -124,11 +144,11 @@ namespace fc { template<> class promise : virtual public promise_base { public: - typedef shared_ptr< promise > ptr; - promise( const char* desc = "?" ):promise_base(desc){} - promise( const void_t& v ){ set_value(); } + typedef fc::shared_ptr< promise > ptr; + promise( const char* desc FC_TASK_NAME_DEFAULT_ARG):promise_base(desc){} + //promise( const void_t& ){ set_value(); } - void wait(const microseconds& timeout = microseconds::max() ){ + void wait(const microseconds& timeout = microseconds::maximum() ){ this->_wait( timeout ); } void wait_until(const time_point& tp ) { @@ -136,7 +156,7 @@ namespace fc { } void set_value(){ this->_set_value(nullptr); } - void set_value( const void_t& v ) { this->_set_value(nullptr); } + void set_value( const void_t& ) { this->_set_value(nullptr); } template void on_complete( CompletionHandler&& c ) { @@ -164,24 +184,32 @@ namespace fc { template class future { public: - future( const shared_ptr>& p ):m_prom(p){} - future( shared_ptr>&& p ):m_prom(fc::move(p)){} + future( const fc::shared_ptr>& p ):m_prom(p){} + future( fc::shared_ptr>&& p ):m_prom(fc::move(p)){} + future(const future& f ) : m_prom(f.m_prom){} future(){} + future& operator=(future&& f ) { + fc_swap(m_prom,f.m_prom); + return *this; + } + + operator const T&()const { return wait(); } /// @pre valid() /// @post ready() /// @throws timeout - const T& wait( const microseconds& timeout = microseconds::max() )const { - return m_prom->wait(timeout); + const T& wait( const microseconds& timeout = microseconds::maximum() )const { + return m_prom->wait(timeout); } /// @pre valid() /// @post ready() /// @throws timeout const T& wait_until( const time_point& tp )const { - return m_prom->wait_until(tp); + if( m_prom ) + return m_prom->wait_until(tp); } bool valid()const { return !!m_prom; } @@ -192,7 +220,23 @@ namespace fc { /// @pre valid() bool error()const { return m_prom->error(); } - void cancel()const { m_prom->cancel(); } + void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) const { if( m_prom ) m_prom->cancel(reason); } + bool canceled()const { if( m_prom ) return m_prom->canceled(); else return true;} + + void cancel_and_wait(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) + { + if( valid() ) + { + cancel(reason); + try + { + wait(); + } + catch (const canceled_exception&) + { + } + } + } /** * @pre valid() @@ -207,21 +251,29 @@ namespace fc { } private: friend class thread; - shared_ptr> m_prom; + fc::shared_ptr> m_prom; }; template<> class future { public: - future( const shared_ptr>& p ):m_prom(p){} - future( shared_ptr>&& p ):m_prom(fc::move(p)){} + future( const fc::shared_ptr>& p ):m_prom(p){} + future( fc::shared_ptr>&& p ):m_prom(fc::move(p)){} + future(const future& f ) : m_prom(f.m_prom){} future(){} + + future& operator=(future&& f ) { + fc_swap(m_prom,f.m_prom); + return *this; + } + /// @pre valid() /// @post ready() /// @throws timeout - void wait( const microseconds& timeout = microseconds::max() ){ - m_prom->wait(timeout); + void wait( const microseconds& timeout = microseconds::maximum() ){ + if( m_prom ) + m_prom->wait(timeout); } /// @pre valid() @@ -231,7 +283,20 @@ namespace fc { m_prom->wait_until(tp); } - bool valid()const { return !!m_prom; } + bool valid()const { return !!m_prom; } + bool canceled()const { return m_prom ? m_prom->canceled() : true; } + + void cancel_and_wait(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) + { + cancel(reason); + try + { + wait(); + } + catch (const canceled_exception&) + { + } + } /// @pre valid() bool ready()const { return m_prom->ready(); } @@ -239,7 +304,7 @@ namespace fc { /// @pre valid() bool error()const { return m_prom->error(); } - void cancel()const { m_prom->cancel(); } + void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) const { if( m_prom ) m_prom->cancel(reason); } template void on_complete( CompletionHandler&& c ) { @@ -248,8 +313,7 @@ namespace fc { private: friend class thread; - shared_ptr> m_prom; + fc::shared_ptr> m_prom; }; } -#endif // _FC_FUTURE_HPP_ diff --git a/include/fc/mutex.hpp b/include/fc/thread/mutex.hpp similarity index 97% rename from include/fc/mutex.hpp rename to include/fc/thread/mutex.hpp index c8a1a79f9..544253e06 100644 --- a/include/fc/mutex.hpp +++ b/include/fc/thread/mutex.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace fc { class microseconds; @@ -102,6 +102,7 @@ namespace fc { private: fc::spin_yield_lock m_blist_lock; fc::context* m_blist; + unsigned recursive_lock_count; }; } // namespace fc diff --git a/include/fc/thread/non_preemptable_scope_check.hpp b/include/fc/thread/non_preemptable_scope_check.hpp new file mode 100644 index 000000000..e52a1b9ad --- /dev/null +++ b/include/fc/thread/non_preemptable_scope_check.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +// This file defines a macro: +// ASSERT_TASK_NOT_PREEMPTED() +// This macro is used to declare that the current scope is not expected to yield. +// If the task does yield, it will generate an assertion. +// +// Use this when you you're coding somethign that must not yield, and you think you +// have done it (just by virtue of the fact that you don't think you've called any +// functions that could yield). This will help detect when your assumptions are +// wrong and you accidentally call something that yields. +// +// This has no cost in release mode, and is extremely cheap in debug mode. + +#define FC_NON_PREEMPTABLE_SCOPE_COMBINE_HELPER(x,y) x ## y +#define FC_NON_PREEMPTABLE_SCOPE_COMBINE(x,y) FC_NON_PREEMPTABLE_SCOPE_COMBINE_HELPER(x,y) + +#ifdef NDEBUG +# define ASSERT_TASK_NOT_PREEMPTED() do {} while (0) +#else +# define ASSERT_TASK_NOT_PREEMPTED() fc::non_preemptable_scope_check FC_NON_PREEMPTABLE_SCOPE_COMBINE(scope_checker_, __LINE__) + +namespace fc +{ + class non_preemptable_scope_check + { + public: + non_preemptable_scope_check(); + ~non_preemptable_scope_check(); + }; +} // namespace fc +#endif + diff --git a/include/fc/priority.hpp b/include/fc/thread/priority.hpp similarity index 86% rename from include/fc/priority.hpp rename to include/fc/thread/priority.hpp index 90749abdd..01fd61ee6 100644 --- a/include/fc/priority.hpp +++ b/include/fc/thread/priority.hpp @@ -15,6 +15,7 @@ namespace fc { } static priority max() { return priority(10000); } static priority min() { return priority(-10000); } + static priority _internal__priority_for_short_sleeps() { return priority(-100000); } int value; }; } diff --git a/include/fc/scoped_lock.hpp b/include/fc/thread/scoped_lock.hpp similarity index 100% rename from include/fc/scoped_lock.hpp rename to include/fc/thread/scoped_lock.hpp diff --git a/include/fc/spin_lock.hpp b/include/fc/thread/spin_lock.hpp similarity index 94% rename from include/fc/spin_lock.hpp rename to include/fc/thread/spin_lock.hpp index 6dfbd1ad6..5aaa4b370 100644 --- a/include/fc/spin_lock.hpp +++ b/include/fc/thread/spin_lock.hpp @@ -1,6 +1,4 @@ -namespace boost { - template class atomic; -} +#pragma once namespace fc { class microseconds; diff --git a/include/fc/spin_yield_lock.hpp b/include/fc/thread/spin_yield_lock.hpp similarity index 94% rename from include/fc/spin_yield_lock.hpp rename to include/fc/thread/spin_yield_lock.hpp index 55d857f05..5ad8fdd0f 100644 --- a/include/fc/spin_yield_lock.hpp +++ b/include/fc/thread/spin_yield_lock.hpp @@ -1,9 +1,5 @@ #pragma once -namespace boost { - template class atomic; -} - namespace fc { class microseconds; class time_point; diff --git a/include/fc/task.hpp b/include/fc/thread/task.hpp similarity index 62% rename from include/fc/task.hpp rename to include/fc/thread/task.hpp index 9c36e6dd8..dd52b7717 100644 --- a/include/fc/task.hpp +++ b/include/fc/thread/task.hpp @@ -1,7 +1,6 @@ -#ifndef _FC_TASK_HPP_ -#define _FC_TASK_HPP_ -#include -#include +#pragma once +#include +#include #include #include @@ -9,12 +8,33 @@ namespace fc { struct context; class spin_lock; + namespace detail + { + struct specific_data_info + { + void* value; + void (*cleanup)(void*); + specific_data_info() : + value(0), + cleanup(0) + {} + specific_data_info(void* value, void (*cleanup)(void*)) : + value(value), + cleanup(cleanup) + {} + }; + void* get_task_specific_data(unsigned slot); + void set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)); + } + class task_base : virtual public promise_base { public: - void run(); + void run(); + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override; + protected: ~task_base(); - + /// Task priority looks like unsupported feature. uint64_t _posted_num; priority _prio; time_point _when; @@ -22,6 +42,12 @@ namespace fc { context* _active_context; task_base* _next; + // support for task-specific data + std::vector *_task_specific_data; + + friend void* detail::get_task_specific_data(unsigned slot); + friend void detail::set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)); + task_base(void* func); // opaque internal / private data used by // thread/thread_private @@ -34,6 +60,10 @@ namespace fc { void* _functor; void (*_destroy_functor)(void*); void (*_run_functor)(void*, void* ); + + void run_impl(); + + void cleanup_task_specific_data(); }; namespace detail { @@ -60,7 +90,7 @@ namespace fc { class task : virtual public task_base, virtual public promise { public: template - task( Functor&& f ):task_base(&_functor) { + task( Functor&& f, const char* desc ):promise_base(desc), task_base(&_functor), promise(desc) { typedef typename fc::deduce::type FunctorType; static_assert( sizeof(f) <= sizeof(_functor), "sizeof(Functor) is larger than FunctorSize" ); new ((char*)&_functor) FunctorType( fc::forward(f) ); @@ -69,15 +99,18 @@ namespace fc { _promise_impl = static_cast*>(this); _run_functor = &detail::functor_run::run; } + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override { task_base::cancel(reason); } + aligned _functor; private: ~task(){} }; + template class task : virtual public task_base, virtual public promise { public: template - task( Functor&& f ):task_base(&_functor) { + task( Functor&& f, const char* desc ):promise_base(desc), task_base(&_functor), promise(desc) { typedef typename fc::deduce::type FunctorType; static_assert( sizeof(f) <= sizeof(_functor), "sizeof(Functor) is larger than FunctorSize" ); new ((char*)&_functor) FunctorType( fc::forward(f) ); @@ -86,11 +119,11 @@ namespace fc { _promise_impl = static_cast*>(this); _run_functor = &detail::void_functor_run::run; } - aligned _functor; + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override { task_base::cancel(reason); } + + aligned _functor; private: ~task(){} }; } - -#endif // _FC_TASK_HPP_ diff --git a/include/fc/thread.hpp b/include/fc/thread/thread.hpp similarity index 52% rename from include/fc/thread.hpp rename to include/fc/thread/thread.hpp index 3fb16348f..bb8271076 100644 --- a/include/fc/thread.hpp +++ b/include/fc/thread/thread.hpp @@ -1,5 +1,8 @@ #pragma once -#include + +#define FC_CONTEXT_STACK_SIZE (2048*1024) + +#include #include #include @@ -7,14 +10,26 @@ namespace fc { class time_point; class microseconds; + namespace detail + { + void* get_thread_specific_data(unsigned slot); + void set_thread_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)); + unsigned get_next_unused_task_storage_slot(); + void* get_task_specific_data(unsigned slot); + void set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)); + } + class thread { public: - thread( const char* name = "" ); + thread( const std::string& name = "" ); thread( thread&& m ); thread& operator=(thread&& t ); /** * Returns the current thread. + Note: Creates fc::thread object (but not a boost thread) if no current thread assigned yet + (this can happend if current() is called from the main thread of application or from + an existing "unknown" boost thread). In such cases, thread_d doesn't have access boost::thread object. */ static thread& current(); @@ -29,6 +44,8 @@ namespace fc { */ void set_name( const string& n ); + const char* current_task_desc() const; + /** * @brief print debug info about the state of every context / promise. * @@ -49,13 +66,13 @@ namespace fc { * @param prio the priority relative to other tasks */ template - auto async( Functor&& f, const char* desc ="", priority prio = priority()) -> fc::future { + auto async( Functor&& f, const char* desc FC_TASK_NAME_DEFAULT_ARG, priority prio = priority()) -> fc::future { typedef decltype(f()) Result; typedef typename fc::deduce::type FunctorType; fc::task* tsk = - new fc::task( fc::forward(f) ); + new fc::task( fc::forward(f), desc ); fc::future r(fc::shared_ptr< fc::promise >(tsk,true) ); - async_task(tsk,prio,desc); + async_task(tsk,prio); return r; } void poke(); @@ -72,12 +89,12 @@ namespace fc { */ template auto schedule( Functor&& f, const fc::time_point& when, - const char* desc = "", priority prio = priority()) -> fc::future { + const char* desc FC_TASK_NAME_DEFAULT_ARG, priority prio = priority()) -> fc::future { typedef decltype(f()) Result; fc::task* tsk = - new fc::task( fc::forward(f) ); + new fc::task( fc::forward(f), desc ); fc::future r(fc::shared_ptr< fc::promise >(tsk,true) ); - async_task(tsk,prio,when,desc); + async_task(tsk,prio,when); return r; } @@ -105,8 +122,8 @@ namespace fc { ~thread(); template - int wait_any( const fc::future& f1, const fc::future& f2, const microseconds& timeout_us = microseconds::max()) { - fc::vector proms(2); + int wait_any( const fc::future& f1, const fc::future& f2, const microseconds& timeout_us = microseconds::maximum()) { + std::vector proms(2); proms[0] = fc::static_pointer_cast(f1.m_prom); proms[1] = fc::static_pointer_cast(f2.m_prom); return wait_any_until(fc::move(proms), fc::time_point::now()+timeout_us ); @@ -114,26 +131,38 @@ namespace fc { private: thread( class thread_d* ); friend class promise_base; + friend class task_base; friend class thread_d; friend class mutex; + friend void* detail::get_thread_specific_data(unsigned slot); + friend void detail::set_thread_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)); + friend unsigned detail::get_next_unused_task_storage_slot(); + friend void* detail::get_task_specific_data(unsigned slot); + friend void detail::set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)); +#ifndef NDEBUG + friend class non_preemptable_scope_check; +#endif friend void yield(); friend void usleep(const microseconds&); friend void sleep_until(const time_point&); friend void exec(); - friend int wait_any( fc::vector&& v, const microseconds& ); - friend int wait_any_until( fc::vector&& v, const time_point& tp ); + friend int wait_any( std::vector&& v, const microseconds& ); + friend int wait_any_until( std::vector&& v, const time_point& tp ); void wait_until( promise_base::ptr && v, const time_point& tp ); void notify( const promise_base::ptr& v ); void yield(bool reschedule=true); void sleep_until( const time_point& t ); void exec(); - int wait_any_until( fc::vector&& v, const time_point& ); + int wait_any_until( std::vector&& v, const time_point& ); - void async_task( task_base* t, const priority& p, const char* desc ); - void async_task( task_base* t, const priority& p, const time_point& tp, const char* desc ); - class thread_d* my; + void async_task( task_base* t, const priority& p ); + void async_task( task_base* t, const priority& p, const time_point& tp ); + + void notify_task_has_been_canceled(); + void unblock(fc::context* c); + class thread_d* my; }; /** @@ -162,15 +191,59 @@ namespace fc { * @return 0 if f1 is ready, 1 if f2 is ready or throw on error. */ template - int wait_any( const fc::future& f1, const fc::future& f2, const microseconds timeout_us = microseconds::max()) { + int wait_any( const fc::future& f1, const fc::future& f2, const microseconds timeout_us = microseconds::maximum()) { return fc::thread::current().wait_any(f1,f2,timeout_us); } - int wait_any( fc::vector&& v, const microseconds& timeout_us = microseconds::max() ); - int wait_any_until( fc::vector&& v, const time_point& tp ); + int wait_any( std::vector&& v, const microseconds& timeout_us = microseconds::maximum() ); + int wait_any_until( std::vector&& v, const time_point& tp ); template - auto async( Functor&& f, const char* desc ="", priority prio = priority()) -> fc::future { + auto async( Functor&& f, const char* desc FC_TASK_NAME_DEFAULT_ARG, priority prio = priority()) -> fc::future { return fc::thread::current().async( fc::forward(f), desc, prio ); } + template + auto schedule( Functor&& f, const fc::time_point& t, const char* desc FC_TASK_NAME_DEFAULT_ARG, priority prio = priority()) -> fc::future { + return fc::thread::current().schedule( fc::forward(f), t, desc, prio ); + } + + /** + * Call f() in thread t and block the current thread until it returns. + * + * If t is null, simply execute f in the current thread. + */ + template + auto sync_call( thread* t, Functor&& f, const char* desc FC_TASK_NAME_DEFAULT_ARG, priority prio = priority()) -> decltype(f()) + { + if( t == nullptr ) + return f(); + + typedef decltype(f()) Result; + future r = t->async( f, desc, prio ); + return r.wait(); + } + +} // end namespace fc + +#ifdef _MSC_VER +struct _EXCEPTION_POINTERS; + +namespace fc { + /* There's something about the setup of the stacks created for fc::async tasks + * that screws up the global structured exception filters installed by + * SetUnhandledExceptionFilter(). The only way I've found to catch an + * unhaldned structured exception thrown in an async task is to put a + * __try/__except block inside the async task. + * We do just that, and if a SEH escapes outside the function running + * in the async task, fc will call an exception filter privided by + * set_unhandled_structured_exception_filter(), passing as arguments + * the result of GetExceptionCode() and GetExceptionInformation(). + * + * Right now there is only one global exception filter, used for any + * async task. + */ + typedef int (*unhandled_exception_filter_type)(unsigned, _EXCEPTION_POINTERS*); + void set_unhandled_structured_exception_filter(unhandled_exception_filter_type new_filter); + unhandled_exception_filter_type get_unhandled_structured_exception_filter(); } +#endif diff --git a/include/fc/thread/thread_specific.hpp b/include/fc/thread/thread_specific.hpp new file mode 100644 index 000000000..f62297d1e --- /dev/null +++ b/include/fc/thread/thread_specific.hpp @@ -0,0 +1,84 @@ +#pragma once +#include "thread.hpp" + +namespace fc +{ + namespace detail + { + unsigned get_next_unused_thread_storage_slot(); + unsigned get_next_unused_task_storage_slot(); + } + + template + class thread_specific_ptr + { + private: + unsigned slot; + public: + thread_specific_ptr() : + slot(detail::get_next_unused_thread_storage_slot()) + {} + + T* get() const + { + return static_cast(detail::get_thread_specific_data(slot)); + } + T* operator->() const + { + return get(); + } + T& operator*() const + { + return *get(); + } + operator bool() const + { + return get() != static_cast(0); + } + static void cleanup_function(void* obj) + { + delete static_cast(obj); + } + void reset(T* new_value = 0) + { + detail::set_thread_specific_data(slot, new_value, cleanup_function); + } + }; + + template + class task_specific_ptr + { + private: + unsigned slot; + public: + task_specific_ptr() : + slot(detail::get_next_unused_task_storage_slot()) + {} + + T* get() const + { + return static_cast(detail::get_task_specific_data(slot)); + } + T* operator->() const + { + return get(); + } + T& operator*() const + { + return *get(); + } + operator bool() const + { + return get() != static_cast(0); + } + static void cleanup_function(void* obj) + { + delete static_cast(obj); + } + void reset(T* new_value = 0) + { + detail::set_task_specific_data(slot, new_value, cleanup_function); + } + }; + +} diff --git a/include/fc/unique_lock.hpp b/include/fc/thread/unique_lock.hpp similarity index 63% rename from include/fc/unique_lock.hpp rename to include/fc/thread/unique_lock.hpp index c1531feb1..0facf4d94 100644 --- a/include/fc/unique_lock.hpp +++ b/include/fc/thread/unique_lock.hpp @@ -1,5 +1,5 @@ -#ifndef _FC_UNIQUE_LOCK_HPP_ -#define _FC_UNIQUE_LOCK_HPP_ +#pragma once +#include namespace fc { struct try_to_lock_t{}; @@ -14,9 +14,11 @@ namespace fc { public: unique_lock( T& l, const fc::time_point& abs ):_lock(l) { _locked = _lock.try_lock_until(abs); } unique_lock( T& l, try_to_lock_t ):_lock(l) { _locked = _lock.try_lock(); } - unique_lock( T& l ):_lock(l) { _lock.lock(); _locked = true; } - ~unique_lock() { _lock.unlock(); _locked = false; } - operator bool()const { return _locked; } + unique_lock( T& l ): _locked(false), _lock(l) { lock(); } + ~unique_lock() { if (_locked) unlock(); } + operator bool() const { return _locked; } + void unlock() { assert(_locked); if (_locked) { _lock.unlock(); _locked = false;} } + void lock() { assert(!_locked); if (!_locked) { _lock.lock(); _locked = true; } } private: unique_lock( const unique_lock& ); unique_lock& operator=( const unique_lock& ); @@ -38,4 +40,3 @@ namespace fc { */ #define synchronized(X) fc::unique_lock __lock(((X))); -#endif diff --git a/include/fc/thread/wait_condition.hpp b/include/fc/thread/wait_condition.hpp new file mode 100644 index 000000000..5006f879b --- /dev/null +++ b/include/fc/thread/wait_condition.hpp @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include + +namespace fc +{ + /** + * A thread-safe, fiber-aware condition variable that + * can be used to signal/wait on a certain condition between + * threads. + */ + template + class wait_condition + { + public: + wait_condition(const char* name) : _name(name) {} + + void wait( const microseconds& timeout = microseconds::maximum() ) + { + typename fc::promise::ptr p = new fc::promise(_name); + { synchronized( _prom_lock ) + _promises.push_back( p ); + } + p->wait(timeout); + } + + template + T wait( LockType& l, const microseconds& timeout = microseconds::maximum() ) + { + typename fc::promise::ptr p( new fc::promise(_name)); + { synchronized( _prom_lock ) + _promises.push_back( p ); + } + l.unlock(); + struct relocker { + LockType& _lock; + relocker(LockType& l) : _lock(l) {} + ~relocker() { _lock.lock(); } + } lock_on_exit(l); + return p->wait(timeout); + } + + void notify_one( const T& t=T()) + { + typename fc::promise::ptr prom; + { synchronized( _prom_lock ) + if( _promises.size() ) + { + prom = _promises.front(); + _promises.pop_front(); + } + } + + if( prom && prom->retain_count() > 1 ) + prom->set_value(t); + } + void notify_all(const T& t=T()) + { + std::deque::ptr> all; + { synchronized( _prom_lock ) + std::swap(all, _promises); + } + for( auto itr = all.begin(); itr != all.end(); ++itr ) + { + if( (*itr)->retain_count() > 1 ) + (*itr)->set_value(t); + } + } + private: + fc::spin_yield_lock _prom_lock; + std::deque::ptr> _promises; + const char *const _name; + }; +} diff --git a/include/fc/thread_d.hpp b/include/fc/thread_d.hpp deleted file mode 100644 index 8a0c245b2..000000000 --- a/include/fc/thread_d.hpp +++ /dev/null @@ -1,393 +0,0 @@ -#include -#include - -#include -#include "context.hpp" -#include -#include -#include -#include - -namespace fc { - class thread_d { - public: - thread_d(fc::thread& s) - :self(s), boost_thread(0), - task_in_queue(0), - done(false), - current(0), - pt_head(0), - ready_head(0), - ready_tail(0), - blocked(0) - { - static char cnt = 0; - name = fc::string("th_") + char('a'+cnt); - cnt++; - } - fc::thread& self; - boost::thread* boost_thread; - bc::stack_allocator stack_alloc; - boost::mutex task_ready_mutex; - boost::condition_variable task_ready; - - boost::atomic task_in_queue; - std::vector task_pqueue; - std::vector task_sch_queue; - std::vector sleep_pqueue; - std::vector free_list; - - bool done; - std::string name; - cmt::context* current; - - cmt::context* pt_head; - - cmt::context* ready_head; - cmt::context* ready_tail; - - cmt::context* blocked; - - time_point check_for_timeouts(); - - - void debug( const std::string& s ) { - boost::unique_lock lock(detail::log_mutex()); - - std::cerr<<"--------------------- "<cur_task ) std::cerr<<'('<cur_task->get_desc()<<')'; - std::cerr<<" ---------------------------\n"; - std::cerr<<" Ready\n"; - cmt::context* c = ready_head; - while( c ) { - std::cerr<<" "<cur_task ) std::cerr<<'('<cur_task->get_desc()<<')'; - cmt::context* p = c->caller_context; - while( p ) { - std::cerr<<" -> "<caller_context; - } - std::cerr<<"\n"; - c = c->next; - } - std::cerr<<" Blocked\n"; - c = blocked; - while( c ) { - std::cerr<<" ctx: "<< c; - if( c->cur_task ) std::cerr<<'('<cur_task->get_desc()<<')'; - std::cerr << " blocked on prom: "; - for( uint32_t i = 0; i < c->blocking_prom.size(); ++i ) { - std::cerr<blocking_prom[i].prom<<'('<blocking_prom[i].prom->get_desc()<<')'; - if( i + 1 < c->blocking_prom.size() ) { - std::cerr<<","; - } - } - - cmt::context* p = c->caller_context; - while( p ) { - std::cerr<<" -> "<caller_context; - } - std::cerr<<"\n"; - c = c->next_blocked; - } - std::cerr<<"-------------------------------------------------\n"; - } - - // insert at from of blocked linked list - inline void add_to_blocked( cmt::context* c ) { - c->next_blocked = blocked; - blocked = c; - } - - void pt_push_back(cmt::context* c) { - c->next = pt_head; - pt_head = c; - /* - cmt::context* n = pt_head; - int i = 0; - while( n ) { - ++i; - n = n->next; - } - wlog( "idle context...%2% %1%", c, i ); - */ - } - cmt::context::ptr ready_pop_front() { - cmt::context::ptr tmp = 0; - if( ready_head ) { - tmp = ready_head; - ready_head = tmp->next; - if( !ready_head ) - ready_tail = 0; - tmp->next = 0; - } - return tmp; - } - void ready_push_front( const cmt::context::ptr& c ) { - c->next = ready_head; - ready_head = c; - if( !ready_tail ) - ready_tail = c; - } - void ready_push_back( const cmt::context::ptr& c ) { - c->next = 0; - if( ready_tail ) { - ready_tail->next = c; - } else { - ready_head = c; - } - ready_tail = c; - } - struct task_priority_less { - bool operator()( const task::ptr& a, const task::ptr& b ) { - return a->prio.value < b->prio.value ? true : (a->prio.value > b->prio.value ? false : a->posted_num > b->posted_num ); - } - }; - struct task_when_less { - bool operator()( const task::ptr& a, const task::ptr& b ) { - return a->when < b->when; - } - }; - - void enqueue( const task::ptr& t ) { - time_point now = system_clock::now(); - task::ptr cur = t; - while( cur ) { - if( cur->when > now ) { - task_sch_queue.push_back(cur); - std::push_heap( task_sch_queue.begin(), - task_sch_queue.end(), task_when_less() ); - } else { - task_pqueue.push_back(cur); - BOOST_ASSERT( this == thread::current().my ); - std::push_heap( task_pqueue.begin(), - task_pqueue.end(), task_priority_less() ); - } - cur = cur->next; - } - } - task* dequeue() { - // get a new task - BOOST_ASSERT( this == thread::current().my ); - - task* pending = 0; - - pending = task_in_queue.exchange(0,boost::memory_order_consume); - if( pending ) { enqueue( pending ); } - - task::ptr p(0); - if( task_sch_queue.size() ) { - if( task_sch_queue.front()->when <= system_clock::now() ) { - p = task_sch_queue.front(); - std::pop_heap(task_sch_queue.begin(), task_sch_queue.end(), task_when_less() ); - task_sch_queue.pop_back(); - return p; - } - } - if( task_pqueue.size() ) { - p = task_pqueue.front(); - std::pop_heap(task_pqueue.begin(), task_pqueue.end(), task_priority_less() ); - task_pqueue.pop_back(); - } - return p; - } - - /** - * This should be before or after a context switch to - * detect quit/cancel operations and throw an exception. - */ - void check_fiber_exceptions() { - if( current && current->canceled ) { - BOOST_THROW_EXCEPTION( error::task_canceled() ); - } else if( done ) { - BOOST_THROW_EXCEPTION( error::thread_quit() ); - } - } - - /** - * Find the next available context and switch to it. - * If none are available then create a new context and - * have it wait for something to do. - */ - bool start_next_fiber( bool reschedule = false ) { - check_for_timeouts(); - if( !current ) current = new cmt::context( &fc::thread::current() ); - - // check to see if any other contexts are ready - if( ready_head ) { - cmt::context* next = ready_pop_front(); - // jump to next context, saving current context - cmt::context* prev = current; - current = next; - if( reschedule ) ready_push_back(current); - - slog( "jump from %p to %p", prev, next ); - bc::jump_fcontext( &prev->my_context, &next->my_context, 0 ); - current = prev; - BOOST_ASSERT( current ); - } else { // all contexts are blocked, create a new context - // that will process posted tasks... - if( reschedule ) ready_push_back(current); - - cmt::context* next; - if( pt_head ) { - next = pt_head; - pt_head = pt_head->next; - next->next = 0; - } else { - next = new cmt::context( &thread_d::start_process_tasks, stack_alloc, - &fc::thread::current() ); - } - cmt::context* prev = current; - current = next; - slog( "jump from %p to %p", prev, next ); - bc::jump_fcontext( &prev->my_context, &next->my_context, (intptr_t)this ); - current = prev; - BOOST_ASSERT( current ); - } - - if( current->canceled ) - BOOST_THROW_EXCEPTION( cmt::error::task_canceled() ); - - return true; - } - - static void start_process_tasks( intptr_t my ) { - thread_d* self = (thread_d*)my; - try { - self->process_tasks(); - } catch ( ... ) { - std::cerr<<"fiber exited with uncaught exception:\n "<< - boost::current_exception_diagnostic_information() <free_list.push_back(self->current); - self->start_next_fiber( false ); - } - - bool run_next_task() { - time_point timeout_time = check_for_timeouts(); - task* next = dequeue(); - if( next ) { - next->set_active_context( current ); - current->cur_task = next; - next->run(); - current->cur_task = 0; - next->set_active_context(0); - next->release(); - //delete next; - return true; - } - return false; - } - bool has_next_task() { - if( task_pqueue.size() || - (task_sch_queue.size() && task_sch_queue.front()->when <= system_clock::now()) || - task_in_queue.load( boost::memory_order_relaxed ) ) - return true; - return false; - } - void clear_free_list() { - for( uint32_t i = 0; i < free_list.size(); ++i ) { - delete free_list[i]; - } - free_list.clear(); - } - void process_tasks() { - while( !done || blocked ) { - if( run_next_task() ) continue; - - // if I have something else to do other than - // process tasks... do it. - if( ready_head ) { - pt_push_back( current ); - start_next_fiber(false); - continue; - } - - clear_free_list(); - - { // lock scope - boost::unique_lock lock(task_ready_mutex); - if( has_next_task() ) continue; - time_point timeout_time = check_for_timeouts(); - - if( timeout_time == time_point::max() ) { - task_ready.wait( lock ); - } else if( timeout_time != time_point::min() ) { - task_ready.wait_until( lock, timeout_time ); - } - } - } - } - - void yield_until( const time_point& tp, bool reschedule ) { - check_fiber_exceptions(); - - if( tp <= system_clock::now() ) - return; - - if( !current ) { - current = new cmt::context(&cmt::thread::current()); - } - - current->resume_time = tp; - current->clear_blocking_promises(); - - sleep_pqueue.push_back(current); - std::push_heap( sleep_pqueue.begin(), - sleep_pqueue.end(), sleep_priority_less() ); - - start_next_fiber(reschedule); - - // clear current context from sleep queue... - for( uint32_t i = 0; i < sleep_pqueue.size(); ++i ) { - if( sleep_pqueue[i] == current ) { - sleep_pqueue[i] = sleep_pqueue.back(); - sleep_pqueue.pop_back(); - std::make_heap( sleep_pqueue.begin(), - sleep_pqueue.end(), sleep_priority_less() ); - break; - } - } - - current->resume_time = time_point::max(); - check_fiber_exceptions(); - } - - void wait( const promise_base::ptr& p, const time_point& timeout ) { - if( p->ready() ) return; - if( timeout < system_clock::now() ) - BOOST_THROW_EXCEPTION( cmt::error::future_wait_timeout() ); - - if( !current ) { - current = new cmt::context(&cmt::thread::current()); - } - - //slog( " %1% blocking on %2%", current, p.get() ); - current->add_blocking_promise(p.get(),true); - - // if not max timeout, added to sleep pqueue - if( timeout != time_point::max() ) { - current->resume_time = timeout; - sleep_pqueue.push_back(current); - std::push_heap( sleep_pqueue.begin(), - sleep_pqueue.end(), - sleep_priority_less() ); - } - - // elog( "blocking %1%", current ); - add_to_blocked( current ); - // debug("swtiching fibers..." ); - - - start_next_fiber(); - // slog( "resuming %1%", current ); - - //slog( " %1% unblocking blocking on %2%", current, p.get() ); - current->remove_blocking_promise(p.get()); - - check_fiber_exceptions(); - } - }; -} // namespace fc diff --git a/include/fc/time.hpp b/include/fc/time.hpp index d2bda1975..11aa96d64 100644 --- a/include/fc/time.hpp +++ b/include/fc/time.hpp @@ -1,64 +1,145 @@ #pragma once #include #include -#include #include +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable : 4244) +#endif //// _MSC_VER + namespace fc { - class microseconds { + class microseconds { public: explicit microseconds( int64_t c = 0) :_count(c){} - static microseconds max() { return microseconds(0x7fffffffffffffffll); } + static microseconds maximum() { return microseconds(0x7fffffffffffffffll); } friend microseconds operator + (const microseconds& l, const microseconds& r ) { return microseconds(l._count+r._count); } friend microseconds operator - (const microseconds& l, const microseconds& r ) { return microseconds(l._count-r._count); } + bool operator==(const microseconds& c)const { return _count == c._count; } + bool operator!=(const microseconds& c)const { return _count != c._count; } friend bool operator>(const microseconds& a, const microseconds& b){ return a._count > b._count; } + friend bool operator>=(const microseconds& a, const microseconds& b){ return a._count >= b._count; } friend bool operator<(const microseconds& a, const microseconds& b){ return a._count < b._count; } + friend bool operator<=(const microseconds& a, const microseconds& b){ return a._count <= b._count; } microseconds& operator+=(const microseconds& c) { _count += c._count; return *this; } + microseconds& operator-=(const microseconds& c) { _count -= c._count; return *this; } int64_t count()const { return _count; } + int64_t to_seconds()const { return _count/1000000; } private: friend class time_point; - int64_t _count; + int64_t _count; }; inline microseconds seconds( int64_t s ) { return microseconds( s * 1000000 ); } inline microseconds milliseconds( int64_t s ) { return microseconds( s * 1000 ); } + inline microseconds minutes(int64_t m) { return seconds(60*m); } + inline microseconds hours(int64_t h) { return minutes(60*h); } + inline microseconds days(int64_t d) { return hours(24*d); } + + class variant; + void to_variant( const fc::microseconds&, fc::variant& ); + void from_variant( const fc::variant& , fc::microseconds& ); - class time_point { + class time_point { public: explicit time_point( microseconds e = microseconds() ) :elapsed(e){} static time_point now(); - static time_point max() { return time_point( microseconds::max() ); } + static time_point maximum() { return time_point( microseconds::maximum() ); } static time_point min() { return time_point(); } + operator fc::string()const; - static time_point from_iso_string( const fc::string& s ); const microseconds& time_since_epoch()const { return elapsed; } + uint32_t sec_since_epoch()const { return elapsed.count() / 1000000; } bool operator > ( const time_point& t )const { return elapsed._count > t.elapsed._count; } + bool operator >=( const time_point& t )const { return elapsed._count >=t.elapsed._count; } bool operator < ( const time_point& t )const { return elapsed._count < t.elapsed._count; } bool operator <=( const time_point& t )const { return elapsed._count <=t.elapsed._count; } bool operator ==( const time_point& t )const { return elapsed._count ==t.elapsed._count; } bool operator !=( const time_point& t )const { return elapsed._count !=t.elapsed._count; } - time_point& operator += ( const microseconds& m ) { elapsed+=m; return *this; } - friend time_point operator + ( const time_point& t, const microseconds& m ) { return time_point(t.elapsed+m); } - friend time_point operator - ( const time_point& t, const microseconds& m ) { return time_point(t.elapsed-m); } - friend microseconds operator - ( const time_point& t, const time_point& m ) { return microseconds(t.elapsed.count() - m.elapsed.count()); } + time_point& operator += ( const microseconds& m) { elapsed+=m; return *this; } + time_point& operator -= ( const microseconds& m) { elapsed-=m; return *this; } + time_point operator + (const microseconds& m) const { return time_point(elapsed+m); } + time_point operator - (const microseconds& m) const { return time_point(elapsed-m); } + microseconds operator - (const time_point& m) const { return microseconds(elapsed.count() - m.elapsed.count()); } private: - microseconds elapsed; + microseconds elapsed; }; - // forward declare io - class value; - void pack( fc::value& , const fc::time_point& ); - void unpack( const fc::value& , fc::time_point& ); + /** + * A lower resolution time_point accurate only to seconds from 1970 + */ + class time_point_sec + { + public: + time_point_sec() + :utc_seconds(0){} + + explicit time_point_sec(uint32_t seconds ) + :utc_seconds(seconds){} + + time_point_sec( const time_point& t ) + :utc_seconds( t.time_since_epoch().count() / 1000000ll ){} + + static time_point_sec maximum() { return time_point_sec(0xffffffff); } + static time_point_sec min() { return time_point_sec(0); } + + operator time_point()const { return time_point( fc::seconds( utc_seconds) ); } + uint32_t sec_since_epoch()const { return utc_seconds; } + + time_point_sec operator = ( const fc::time_point& t ) + { + utc_seconds = t.time_since_epoch().count() / 1000000ll; + return *this; + } + friend bool operator < ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds < b.utc_seconds; } + friend bool operator > ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds > b.utc_seconds; } + friend bool operator <= ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds <= b.utc_seconds; } + friend bool operator >= ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds >= b.utc_seconds; } + friend bool operator == ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds == b.utc_seconds; } + friend bool operator != ( const time_point_sec& a, const time_point_sec& b ) { return a.utc_seconds != b.utc_seconds; } + time_point_sec& operator += ( uint32_t m ) { utc_seconds+=m; return *this; } + time_point_sec& operator += ( microseconds m ) { utc_seconds+=m.to_seconds(); return *this; } + time_point_sec& operator -= ( uint32_t m ) { utc_seconds-=m; return *this; } + time_point_sec& operator -= ( microseconds m ) { utc_seconds-=m.to_seconds(); return *this; } + time_point_sec operator +( uint32_t offset )const { return time_point_sec(utc_seconds + offset); } + time_point_sec operator -( uint32_t offset )const { return time_point_sec(utc_seconds - offset); } - namespace raw { - template - void unpack( Stream& s, fc::time_point& v ); - template - void pack( Stream& s, const fc::time_point& v ); - } + friend time_point operator + ( const time_point_sec& t, const microseconds& m ) { return time_point(t) + m; } + friend time_point operator - ( const time_point_sec& t, const microseconds& m ) { return time_point(t) - m; } + friend microseconds operator - ( const time_point_sec& t, const time_point_sec& m ) { return time_point(t) - time_point(m); } + friend microseconds operator - ( const time_point& t, const time_point_sec& m ) { return time_point(t) - time_point(m); } + + fc::string to_non_delimited_iso_string()const; + fc::string to_iso_string()const; + + operator fc::string()const; + static time_point_sec from_iso_string( const fc::string& s ); + + private: + uint32_t utc_seconds; + }; typedef fc::optional otime_point; + + /** return a human-readable approximate time, relative to now() + * e.g., "4 hours ago", "2 months ago", etc. + */ + string get_approximate_relative_time_string(const time_point_sec& event_time, + const time_point_sec& relative_to_time = fc::time_point::now(), + const std::string& ago = " ago"); + string get_approximate_relative_time_string(const time_point& event_time, + const time_point& relative_to_time = fc::time_point::now(), + const std::string& ago = " ago"); } + +#include +FC_REFLECT_TYPENAME( fc::time_point ) +FC_REFLECT_TYPENAME( fc::microseconds ) +FC_REFLECT_TYPENAME( fc::time_point_sec ) + +#ifdef _MSC_VER + #pragma warning (pop) +#endif /// #ifdef _MSC_VER diff --git a/include/fc/time_io.hpp b/include/fc/time_io.hpp deleted file mode 100644 index 4fb284bda..000000000 --- a/include/fc/time_io.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include -#include - -namespace fc { - namespace raw { - template - void unpack( Stream& s, fc::time_point& v ) { - int64_t micro; - fc::raw::unpack(s, micro ); - v = fc::time_point( fc::microseconds(micro); - } - template - void pack( Stream& s, const fc::time_point& v ) { - fc::raw::pack( s, v.time_since_epoch().count() ); - } - } -} diff --git a/include/fc/typename.hpp b/include/fc/typename.hpp deleted file mode 100644 index 0fbb1ddfb..000000000 --- a/include/fc/typename.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -namespace fc { - class value; - template class get_typename{}; - template<> struct get_typename { static const char* name() { return "int32_t"; } }; - template<> struct get_typename { static const char* name() { return "int64_t"; } }; - template<> struct get_typename { static const char* name() { return "int16_t"; } }; - template<> struct get_typename { static const char* name() { return "int8_t"; } }; - template<> struct get_typename { static const char* name() { return "uint32_t"; } }; - template<> struct get_typename { static const char* name() { return "uint64_t"; } }; - template<> struct get_typename { static const char* name() { return "uint16_t"; } }; - template<> struct get_typename { static const char* name() { return "uint8_t"; } }; - template<> struct get_typename { static const char* name() { return "double"; } }; - template<> struct get_typename { static const char* name() { return "float"; } }; - template<> struct get_typename { static const char* name() { return "bool"; } }; - template<> struct get_typename { static const char* name() { return "char"; } }; - template<> struct get_typename { static const char* name() { return "char"; } }; - template<> struct get_typename { static const char* name() { return "string"; } }; - template<> struct get_typename { static const char* name() { return "value"; } }; -} diff --git a/include/fc/uint128.hpp b/include/fc/uint128.hpp new file mode 100644 index 000000000..5e599dc38 --- /dev/null +++ b/include/fc/uint128.hpp @@ -0,0 +1,153 @@ +#pragma once +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable : 4244) +#endif //// _MSC_VER + +namespace fc +{ + class bigint; + /** + * @brief an implementation of 128 bit unsigned integer + * + */ + class uint128 + { + + + public: + uint128():hi(0),lo(0){} + uint128( uint32_t l ):hi(0),lo(l){} + uint128( int32_t l ):hi( -(l<0) ),lo(l){} + uint128( int64_t l ):hi( -(l<0) ),lo(l){} + uint128( uint64_t l ):hi(0),lo(l){} + uint128( const std::string& s ); + uint128( uint64_t _h, uint64_t _l ) + :hi(_h),lo(_l){} + uint128( const fc::bigint& bi ); + + operator std::string()const; + operator fc::bigint()const; + + bool operator == ( const uint128& o )const{ return hi == o.hi && lo == o.lo; } + bool operator != ( const uint128& o )const{ return hi != o.hi || lo != o.lo; } + bool operator < ( const uint128& o )const { return (hi == o.hi) ? lo < o.lo : hi < o.hi; } + bool operator < ( const int64_t& o )const { return *this < uint128(o); } + bool operator !()const { return !(hi !=0 || lo != 0); } + uint128 operator -()const { return ++uint128( ~hi, ~lo ); } + uint128 operator ~()const { return uint128( ~hi, ~lo ); } + + uint128& operator++() { hi += (++lo == 0); return *this; } + uint128& operator--() { hi -= (lo-- == 0); return *this; } + uint128 operator++(int) { auto tmp = *this; ++(*this); return tmp; } + uint128 operator--(int) { auto tmp = *this; --(*this); return tmp; } + + uint128& operator |= ( const uint128& u ) { hi |= u.hi; lo |= u.lo; return *this; } + uint128& operator &= ( const uint128& u ) { hi &= u.hi; lo &= u.lo; return *this; } + uint128& operator ^= ( const uint128& u ) { hi ^= u.hi; lo ^= u.lo; return *this; } + uint128& operator <<= ( const uint128& u ); + uint128& operator >>= ( const uint128& u ); + + uint128& operator += ( const uint128& u ) { const uint64_t old = lo; lo += u.lo; hi += u.hi + (lo < old); return *this; } + uint128& operator -= ( const uint128& u ) { return *this += -u; } + uint128& operator *= ( const uint128& u ); + uint128& operator /= ( const uint128& u ); + uint128& operator %= ( const uint128& u ); + + + friend uint128 operator + ( const uint128& l, const uint128& r ) { return uint128(l)+=r; } + friend uint128 operator - ( const uint128& l, const uint128& r ) { return uint128(l)-=r; } + friend uint128 operator * ( const uint128& l, const uint128& r ) { return uint128(l)*=r; } + friend uint128 operator / ( const uint128& l, const uint128& r ) { return uint128(l)/=r; } + friend uint128 operator % ( const uint128& l, const uint128& r ) { return uint128(l)%=r; } + friend uint128 operator | ( const uint128& l, const uint128& r ) { return uint128(l)=(r); } + friend uint128 operator & ( const uint128& l, const uint128& r ) { return uint128(l)&=r; } + friend uint128 operator ^ ( const uint128& l, const uint128& r ) { return uint128(l)^=r; } + friend uint128 operator << ( const uint128& l, const uint128& r ) { return uint128(l)<<=r; } + friend uint128 operator >> ( const uint128& l, const uint128& r ) { return uint128(l)>>=r; } + friend bool operator > ( const uint128& l, const uint128& r ) { return r < l; } + friend bool operator > ( const uint128& l, const int64_t& r ) { return uint128(r) < l; } + friend bool operator > ( const int64_t& l, const uint128& r ) { return r < uint128(l); } + + friend bool operator >= ( const uint128& l, const uint128& r ) { return l == r || l > r; } + friend bool operator >= ( const uint128& l, const int64_t& r ) { return l >= uint128(r); } + friend bool operator >= ( const int64_t& l, const uint128& r ) { return uint128(l) >= r; } + friend bool operator <= ( const uint128& l, const uint128& r ) { return l == r || l < r; } + friend bool operator <= ( const uint128& l, const int64_t& r ) { return l <= uint128(r); } + friend bool operator <= ( const int64_t& l, const uint128& r ) { return uint128(l) <= r; } + + friend std::size_t hash_value( const uint128& v ) { return city_hash_size_t((const char*)&v, sizeof(v)); } + + uint32_t to_integer()const + { + FC_ASSERT( hi == 0 ); + uint32_t lo32 = (uint32_t) lo; + FC_ASSERT( lo == lo32 ); + return lo32; + } + uint64_t to_uint64()const + { + FC_ASSERT( hi == 0 ); + return lo; + } + uint32_t low_32_bits()const { return (uint32_t) lo; } + uint64_t low_bits()const { return lo; } + uint64_t high_bits()const { return hi; } + + static uint128 max_value() { + const uint64_t max64 = std::numeric_limits::max(); + return uint128( max64, max64 ); + } + + static void full_product( const uint128& a, const uint128& b, uint128& result_hi, uint128& result_lo ); + + uint8_t popcount() const; + + // fields must be public for serialization + uint64_t hi; + uint64_t lo; + }; + static_assert( sizeof(uint128) == 2*sizeof(uint64_t), "validate packing assumptions" ); + + typedef uint128 uint128_t; + + class variant; + + void to_variant( const uint128& var, variant& vo ); + void from_variant( const variant& var, uint128& vo ); + + namespace raw + { + template + inline void pack( Stream& s, const uint128& u ) { s.write( (char*)&u, sizeof(u) ); } + template + inline void unpack( Stream& s, uint128& u ) { s.read( (char*)&u, sizeof(u) ); } + } + + size_t city_hash_size_t(const char *buf, size_t len); +} // namespace fc + +namespace std +{ + template<> + struct hash + { + size_t operator()( const fc::uint128& s )const + { + return fc::city_hash_size_t((char*)&s, sizeof(s)); + } + }; +} + +FC_REFLECT( fc::uint128_t, (hi)(lo) ) + +#ifdef _MSC_VER + #pragma warning (pop) +#endif ///_MSC_VER diff --git a/include/fc/unique_ptr.hpp b/include/fc/unique_ptr.hpp new file mode 100644 index 000000000..609eff8b3 --- /dev/null +++ b/include/fc/unique_ptr.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include + +namespace fc +{ + template + class unique_ptr + { + public: + typedef T* pointer; + + explicit unique_ptr( pointer t = nullptr ):_p(t){} + + unique_ptr( unique_ptr&& m ) + :_p(m._p){ m._p = nullptr; } + + ~unique_ptr() { delete _p; } + + operator bool()const { return _p != nullptr; } + friend bool operator==(const unique_ptr& p, nullptr_t) + { + return p._p == nullptr; + } + + friend bool operator!=(const unique_ptr& p, nullptr_t) + { + return p._p != nullptr; + } + + unique_ptr& operator=( nullptr_t ) + { + delete _p; _p = nullptr; + } + + unique_ptr& operator=( unique_ptr&& o ) + { + fc_swap( _p, o._p ); + return *this; + } + + pointer operator->()const { return _p; } + T& operator*()const { return *_p; } + + void reset( pointer v ) + { + delete _p; _p = v; + } + pointer release() + { + auto tmp = _p; + _p = nullptr; + return tmp; + } + + private: + pointer _p; + }; + +} diff --git a/include/fc/url.hpp b/include/fc/url.hpp deleted file mode 100644 index a1bfc91a7..000000000 --- a/include/fc/url.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include -#include - -namespace fc { - - typedef fc::optional ostring; - - struct url { - url(){} - url( const string& url ); - /* - url( const url& u ); - - url( url&& u ); - url& operator=(url&& c); - url& operator=(const url& c); - */ - - operator string()const { return to_string(); } - string to_string()const; - url& from_string( const string& s ); - - bool operator==( const url& cmp )const; - - string proto; // file, ssh, tcp, http, ssl, etc... - ostring host; - ostring user; - ostring pass; - ostring path; - ostring args; - fc::optional port; - }; - -} // namespace fc - -#include -FC_REFLECT( fc::url, (proto)(host)(user)(pass)(path)(args)(port) ) - diff --git a/include/fc/utf8.hpp b/include/fc/utf8.hpp new file mode 100644 index 000000000..3af2abe32 --- /dev/null +++ b/include/fc/utf8.hpp @@ -0,0 +1,28 @@ +#ifndef __UTF8_HPP +#define __UTF8_HPP + +#include + +/// This file contains general purpose utilities related to UTF-8 <-> Unicode conversions + +namespace fc +{ + +bool is_utf8( const std::string& str ); + +/** Decodes utf 8 std::string into unicode string. + @param input - input string to be decoded and stored in 'storage' + @param storage - buffer for converted text. Cannot be nullptr. +*/ +void decodeUtf8(const std::string& input, std::wstring* storage); + +/** Encodes given wide (unicode) string into UTF-8 representation. + @param input - input string to be encoded and stored in 'storage' + @param storage - buffer for converted text. Cannot be nullptr. +*/ +void encodeUtf8(const std::wstring& input, std::string* storage); + +} /// namespace fc + +#endif ///__UTF8_HPP + diff --git a/include/fc/utility.hpp b/include/fc/utility.hpp index 96742aeac..0096ac56c 100644 --- a/include/fc/utility.hpp +++ b/include/fc/utility.hpp @@ -2,14 +2,23 @@ #include #include -//#define nullptr 0 +#ifdef _MSC_VER +#pragma warning(disable: 4482) // nonstandard extension used enum Name::Val, standard in C++11 +#define NO_RETURN __declspec(noreturn) +#else +#define NO_RETURN __attribute__((noreturn)) +#endif -typedef decltype(sizeof(int)) size_t; -namespace std { - typedef decltype(sizeof(int)) size_t; -} + +//namespace std { +// typedef decltype(sizeof(int)) size_t; +// typedef decltype(nullptr) nullptr_t; +//} namespace fc { + using std::size_t; + typedef decltype(nullptr) nullptr_t; + template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; @@ -36,15 +45,21 @@ namespace fc { template struct is_class { typedef decltype(detail::is_class_helper(0)) type; enum value_enum { value = type::value }; }; - +#ifdef min +#undef min +#endif template const T& min( const T& a, const T& b ) { return a < b ? a: b; } } // outside of namespace fc becuase of VC++ conflict with std::swap template - void fc_swap( T& a, T& b ) { + void fc_swap( T& a, T& b ) { T tmp = fc::move(a); a = fc::move(b); b = fc::move(tmp); } + +#define LLCONST(constant) static_cast(constant##ll) +#define ULLCONST(constant) static_cast(constant##ull) + diff --git a/include/fc/value.hpp b/include/fc/value.hpp deleted file mode 100644 index 0f095e8bb..000000000 --- a/include/fc/value.hpp +++ /dev/null @@ -1,240 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - // Disable warning C4482: nonstandard extention used: enum 'enum_type::enum_value' used in qualified name - #pragma warning( disable: 4482 ) -#endif - -namespace fc { - template struct tuple; - - /** - * @brief a dynamic container that can hold - * integers, reals, strings, booleans, arrays, and - * or null. - * - * This type serves as an intermediate representation between - * C++ type and serialized type (JSON,XML,etc). - * - * As much as possible value attempts to preserve 'type' information, but - * type information is not always provided equally by all serialization formats. - * - * value is move aware, so move it when you can to avoid expensive copies - */ - class value { - public: - enum value_type { - null_type, string_type, bool_type, - int8_type, int16_type, int32_type, int64_type, - uint8_type, uint16_type, uint32_type, uint64_type, - double_type, float_type, - array_type, object_type - }; - - class key_val; - class object { - public: - typedef fc::vector::const_iterator const_iterator; - fc::vector fields; - }; - typedef fc::vector array; - - struct const_visitor { - virtual ~const_visitor(){} - virtual void operator()(const int8_t& v )=0; - virtual void operator()(const int16_t& v )=0; - virtual void operator()(const int32_t& v )=0; - virtual void operator()(const int64_t& v )=0; - virtual void operator()(const uint8_t& v )=0; - virtual void operator()(const uint16_t& v )=0; - virtual void operator()(const uint32_t& v )=0; - virtual void operator()(const uint64_t& v )=0; - virtual void operator()(const float& v )=0; - virtual void operator()(const double& v )=0; - virtual void operator()(const bool& v )=0; - virtual void operator()(const fc::string& v )=0; - virtual void operator()(const object& )=0; - virtual void operator()(const fc::vector& )=0; - virtual void operator()()=0; - }; - - value(); - value(value&& m ); - value(const value& m ); - - value(const char* c ); - value(char* c ); - value(int8_t ); - value(int16_t ); - value(int32_t ); - value(int64_t ); - value(uint8_t ); - value(uint16_t ); - value(uint32_t ); - value(uint64_t ); - value(double ); - value(float ); - value(bool ); - value(fc::string&& ); - value(fc::string& ); - value(const fc::string& ); - - value(object&& o ); - value(const object& o ); - value(object& o ); - - /// initialize an object with a single key/value pair - value(const fc::string&, const value& v ); - template - value(const fc::string& s, const T& v ) { - set( s, v ); - } - - value(fc::vector&& a ); - value(fc::vector& a ); - value(const fc::vector& a ); - - ~value(); - - value& operator=(value&& v ); - value& operator=(const value& v ); - - - /** - * Include fc/value_cast.hpp for implementation - */ - template - explicit value(const T& v ); - - template - value& operator=( const T& v ); - - template - T cast()const; - - /** used to iterate over object fields, use array index + size to iterate over array */ - object::const_iterator find(const char* key )const; - object::const_iterator begin()const; - object::const_iterator end()const; - - /** avoid creating temporary string just for comparisons! **/ - value& operator[](const char* key ); - const value& operator[](const char* key )const; - value& operator[](const fc::string& key ); - const value& operator[](const fc::string& key )const; - - /** array & object interface **/ - void clear(); - size_t size()const; - - /** array interface **/ - void resize(size_t s ); - void reserve(size_t s ); - value& push_back(value&& v ); - value& push_back(const value& v ); - const fc::vector& as_array()const; - fc::vector& as_array(); - const fc::string& as_string()const; - fc::string& as_string(); - const value::object& as_object()const; - value::object& as_object(); - - /** same as push_back(), used for short-hand construction of value()(1)(2)(3)(4) */ - value& operator()(fc::value v ); - value& operator[](int32_t idx ); - const value& operator[](int32_t idx )const; - - /** gets the stored type **/ - value_type type()const; - bool is_null()const; - bool is_string()const; - bool is_object()const; - bool is_array()const; - - void visit(const_visitor&& v )const; - - /** same as set(key, v ), used for short-hand construction of value()("key",1)("key2",2) */ - value& operator()(const char* key, fc::value v ); - value& set(const char* key, fc::value v ); - value& set(const fc::string& key, fc::value v ); - value& clear(const fc::string& key ); - - template - value& set(S&& key, T&& v ) { return set(fc::forward(key), fc::value(fc::forward(v) ) ); } - - private: - /** throws exceptions on errors - * - * Defined in fc/value_cast.hpp because it depends upon - * reflection - */ - template - friend T value_cast(const value& v ); - - aligned<40> holder; - }; - typedef fc::optional ovalue; - bool operator == (const value& v, std::nullptr_t ); - bool operator != (const value& v, std::nullptr_t ); - - class value::key_val { - public: - key_val(){}; - - key_val(fc::string k ) - :key(fc::move(k)){} - - key_val(const fc::string& k, const value& v ) - :key(k),val(v){} - - key_val(key_val&& m ) - :key(fc::move(m.key)),val(fc::move(m.val)){ } - - key_val(const key_val& m ) - :key(m.key),val(m.val){} - - ~key_val(){ } - - key_val& operator=(key_val&& k ) { - fc_swap(key, k.key ); - fc_swap(val, k.val ); - return *this; - } - - key_val& operator=(const key_val& k ) { - key = k.key; - val = k.val; - return *this; - } - - fc::string key; - value val; - }; - -} // namespace fc - -#include -FC_REFLECT_ENUM(fc::value::value_type, - (null_type) - (string_type) - (bool_type) - (int8_type) - (int16_type) - (int32_type) - (int64_type) - (uint8_type) - (uint16_type) - (uint32_type) - (uint64_type) - (double_type) - (float_type) - (array_type) - (object_type) -) diff --git a/include/fc/value_cast.hpp b/include/fc/value_cast.hpp deleted file mode 100644 index d29ca2c62..000000000 --- a/include/fc/value_cast.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#include - -namespace fc { - - namespace detail { - - void cast_value( const value& v, int8_t& ); - void cast_value( const value& v, int16_t& ); - void cast_value( const value& v, int32_t& ); - void cast_value( const value& v, int64_t& ); - void cast_value( const value& v, uint8_t& ); - void cast_value( const value& v, uint16_t& ); - void cast_value( const value& v, uint32_t& ); - void cast_value( const value& v, uint64_t& ); - void cast_value( const value& v, double& ); - void cast_value( const value& v, float& ); - void cast_value( const value& v, bool& ); - void cast_value( const value& v, fc::string& ); - void cast_value( const value& v, value& ); - - template - void cast_value( const value& v, T& t) { - unpack(v,t); - } - template - void cast_value( const value& v, std::vector& out ) { - if( v.type() != value::array_type ) { - FC_THROW_REPORT( "Error casting ${type} to array", fc::value("type", fc::reflector::to_string(v.type()) ) ); - } - out.resize(v.size()); - slog( "out .size %d", out.size() ); - const fc::vector& val = v.as_array(); - auto oitr = out.begin(); - int idx = 0; - for( auto itr = val.begin(); itr != val.end(); ++itr, ++oitr, ++idx ) { - try { - *oitr = itr->cast(); //value_cast(*itr); - // value_cast( *itr, *oitr ); - } catch ( fc::error_report& er ) { - throw FC_REPORT_PUSH( er, "Error casting value[${index}] to ${type}", - fc::value("index",idx) - ("type", fc::get_typename::name()) - ); - } - } - } - - template - void cast_value( const value& v, fc::vector& out ) { - if( v.type() != value::array_type ) { - FC_THROW_REPORT( "Error casting ${type} to array", fc::value("type", fc::reflector::to_string(v.type()) ) ); - } - out.resize(v.size()); - slog( "out .size %d", out.size() ); - const fc::vector& val = v.as_array(); - auto oitr = out.begin(); - int idx = 0; - for( auto itr = val.begin(); itr != val.end(); ++itr, ++oitr, ++idx ) { - try { - *oitr = itr->cast(); //value_cast(*itr); - // value_cast( *itr, *oitr ); - } catch ( fc::error_report& er ) { - throw FC_REPORT_PUSH( er, "Error casting value[${index}] to ${type}", - fc::value("index",idx) - ("type", fc::get_typename::name()) - ); - } - } - } - - template - struct cast_if_tuple { - template - static T cast( const value& v ) { - typename fc::deduce::type out; - cast_value(v,out); - // v.visit(cast_visitor(out)); - return out; - } - }; - template<> - struct cast_if_tuple { - struct member_visitor { - member_visitor( const value& v ) - :_val(v),idx(0){} - template - void operator()( Member& m ) { - try { - m = value_cast(_val[idx]); - } catch ( fc::error_report& er ) { - throw FC_REPORT_PUSH( er, "Error parsing tuple element ${index}", fc::value().set("index",idx) ); - } - ++idx; - } - const value& _val; - int idx; - }; - - template - static Tuple cast( const value& v ) { - typename fc::deduce::type out; - out.visit( member_visitor(v) ); - return out; - } - }; - - template - struct cast_if_reflected { - template - static T cast( const value& v ) { - return cast_if_tuple::type>::template cast(v); - } - }; - - template<> - struct cast_if_reflected { - template - static T cast( const value& v ) { - T tmp; - unpack(v,tmp); - return tmp; - } - }; - - void new_value_holder_void( value* v ); - } // namespace detail - - - /** - * Convert from value v to T - * - * Performs the following conversions - * true -> 1.0, 1, "true" - * - * Not all casts are 'clean', the following conversions - * could cause errors: - * - * signed int -> unsigned - * large int -> smaller int - * real -> int - * non-numeric string -> number - * object -> string or number - * array -> string or number - * number,string,array -> object - */ - template - T value_cast( const value& v ) { - return detail::cast_if_reflected::type>::is_defined>::template cast< typename fc::deduce::type >(v); - } - - template - value::value( const T& v ) { - detail::new_value_holder_void(this); - fc::pack( *this, v); - } - template - value& value::operator=( const T& v ) { - this->~value(); - value tmp(v); - *this = fc::move(tmp); - return *this; - } - template - T value::cast()const { - return value_cast(*this); - } -} diff --git a/include/fc/value_io.hpp b/include/fc/value_io.hpp deleted file mode 100644 index e69f00213..000000000 --- a/include/fc/value_io.hpp +++ /dev/null @@ -1,332 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace fc { - template - T value_cast( const value& v ); - - struct void_t{}; - - template - void pack(fc::value& jsv, const T& v ); - - template - void unpack( const fc::value& jsv, T& v ); - - template - void pack( fc::value& v, const tuple& t ); - template - void unpack( const fc::value& val, tuple& t ); - - template - void pack( fc::value& jsv, const fc::optional& v ); - - template - void unpack( const fc::value& jsv, fc::optional& v ); - - inline void pack( fc::value& jsv, const char& v ) { jsv = fc::string(&v,1); } - inline void pack( fc::value& jsv, const fc::value& v ) { jsv = v; } - inline void pack( fc::value& jsv, fc::value& v ) { jsv = v; } - inline void pack( fc::value& jsv, fc::value&& v ) { jsv = fc::move(v); } - inline void pack( fc::value& jsv, const void_t& v ) { jsv = fc::value(); } - inline void pack( fc::value& jsv, const bool& v ) { jsv = v; } - inline void pack( fc::value& jsv, const float& v ) { jsv = v; } - inline void pack( fc::value& jsv, const double& v ) { jsv = v; } - inline void pack( fc::value& jsv, const uint8_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const uint16_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const uint32_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const uint64_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const int8_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const int16_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const int32_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const int64_t& v ) { jsv = v; } - inline void pack( fc::value& jsv, const fc::string& v ) { jsv = value(v); } - inline void pack( fc::value& jsv, fc::string& v ) { jsv = v; } - inline void pack( fc::value& jsv, fc::string&& v ) { jsv = fc::move(v); } - inline void pack( fc::value& jsv, const char* v ) { jsv = fc::string(v); } - - void pack( fc::value& jsv, const fc::vector& value ); - template - void pack( fc::value& jsv, const fc::vector& value ); - template - void pack( fc::value& jsv, const std::vector& value ); - - - inline void unpack( const fc::value& jsv, fc::value& v ) { v = jsv; } - template - void unpack( const fc::value& jsv, const T& v ); - template - void unpack( const fc::value& jsv, T& v ); - void unpack( const fc::value& jsv, bool& v ); - - inline void unpack( const fc::value& jsv, void_t& v ){ }; - - void unpack( const fc::value& jsv, float& v ); - void unpack( const fc::value& jsv, double& v ); - void unpack( const fc::value& jsv, uint8_t& v ); - void unpack( const fc::value& jsv, uint16_t& v ); - void unpack( const fc::value& jsv, uint32_t& v ); - void unpack( const fc::value& jsv, uint64_t& v ); - void unpack( const fc::value& jsv, int8_t& v ); - void unpack( const fc::value& jsv, int16_t& v ); - void unpack( const fc::value& jsv, int32_t& v ); - void unpack( const fc::value& jsv, int64_t& v ); - void unpack( const fc::value& jsv, fc::string& v ); - - void unpack( const fc::value& jsv, fc::vector& value ); - - void unpack( const fc::value& jsv, fc::vector& value ); - template - void unpack( const fc::value& jsv, fc::vector& value ); - template - void unpack( const fc::value& jsv, std::vector& value ); - - namespace detail { - template - struct pack_object_visitor { - pack_object_visitor(const Class& _c, fc::value& _val) - :c(_c),obj(_val){} - - /** - VC++ does not understand the difference of return types, so an extra layer is needed. - */ - template - inline void pack_helper( const T& v, const char* name )const { - value* o = &obj[name]; - fc::pack( *o, v ); - } - template - inline void pack_helper( const fc::optional& v, const char* name )const { - if( !!v ) { - fc::pack( obj[name], *v ); - } - } - template - inline void operator()( const char* name )const { - pack_helper( c.*p, name ); - } - - private: - const Class& c; - fc::value& obj; - }; - - template - struct is_optional { - typedef fc::false_type type; - }; - template - struct is_optional > { - typedef fc::true_type type; - }; - - template - struct unpack_object_visitor { - unpack_object_visitor(Class& _c, const fc::value& _val) - :c(_c),obj(_val){} - - template - void operator()( const char* name )const { - if( obj.find(name) != obj.end()) { - try { - fc::unpack( obj[name], c.*p ); - } catch ( fc::error_report& er ) { - throw FC_REPORT_PUSH( er, "Error parsing field '${field_name}'", fc::value("field_name",name) ); - } - } - else { - if( !is_optional< typename fc::remove_reference::type >::type::value ) { - wlog( "unable to find name: '%s'",name); - } - } - } - Class& c; - const fc::value& obj; - }; - - template - struct if_enum { - template - static inline void pack( fc::value& jsv, const T& v ) { - jsv = fc::value::object(); - detail::pack_object_visitor pov(v,jsv); - fc::reflector::visit(pov); - } - template - static inline void unpack( const fc::value& jsv, T& v ) { - detail::unpack_object_visitor pov(v,jsv ); - fc::reflector::visit(pov); - } - }; - - template<> struct if_enum { - template - static inline void pack( fc::value& jsv, const T& v ) { - fc::pack( jsv, fc::reflector::to_string(v) ); - } - template - static inline void unpack( const fc::value& jsv, T& v ) { - if( jsv.is_string() ) { - v = fc::reflector::from_string( fc::value_cast(jsv).c_str() ); - } else { - // throw if invalid int, by attempting to convert to string - fc::reflector::to_string( v = static_cast(value_cast(jsv)) ); - } - } - }; - - - template - struct if_reflected { - template - static inline void pack(fc::value& s, const T& v ) { - v.did_not_implement_reflect_macro(); - } - template - static inline void unpack( const fc::value& s, T& v ) { - v.did_not_implement_reflect_macro(); - } - }; - - template<> - struct if_reflected { - template - static inline void pack( fc::value& jsv, const T& v ) { - if_enum::is_enum>::pack( jsv,v ); - } - template - static inline void unpack( const fc::value& jsv, T& v ) { - if_enum::is_enum>::unpack( jsv,v ); - } - }; - - } // namesapce detail - - inline void unpack( const fc::value& jsv, char& v ) { - auto s = value_cast(jsv); - if( s.size() ) v = s[0]; - } - inline void unpack( const fc::value& jsv, bool& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, float& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, double& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, uint8_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, uint16_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, uint32_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, uint64_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, int8_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, int16_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, int32_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, int64_t& v ) { v = value_cast(jsv); } - inline void unpack( const fc::value& jsv, fc::string& v ) { v = value_cast(jsv); } - - template - void pack( fc::value& jsv, const fc::optional& v ) { - if( v ) pack( jsv, *v ); - else jsv = fc::value(); - } - template - void unpack( const fc::value& jsv, fc::optional& v ) { - if( !jsv.is_null() ) { - T tmp; - unpack( jsv, tmp ); - v = fc::move(tmp); - } - } - - - template - inline void pack( fc::value& jsv, const fc::vector& value ) { - jsv = fc::value::array(); - jsv.resize(value.size()); - typename fc::vector::const_iterator itr = value.begin(); - typename fc::vector::const_iterator end = value.end(); - uint32_t i = 0; - while( itr != end ) { - fc::pack( jsv[i], *itr ); - ++itr; - ++i; - } - } - template - inline void pack( fc::value& jsv, const std::vector& value ) { - jsv = fc::value::array(); - jsv.resize(value.size()); - auto itr = value.begin(); - auto end = value.end(); - uint32_t i = 0; - while( itr != end ) { - fc::pack( jsv[i], *itr ); - ++itr; - ++i; - } - } - struct tuple_to_value_visitor { - tuple_to_value_visitor( value& v ):_val(v),_count(0) { } - template - void operator()( T&& t ) { - _val[_count] = value(fc::forward(t) ); - ++_count; - } - value& _val; - int _count; - }; - struct tuple_from_value_visitor { - tuple_from_value_visitor( const value& v ):_val(v),_count(0) { } - template - void operator()( T&& t ) { - if( _count < _val.size() ) unpack( _val[_count], t ); - ++_count; - } - const value& _val; - int _count; - }; - - template - inline void pack( fc::value& val, const tuple& t ) { - val = fc::value::array( tuple::size ); - t.visit( tuple_to_value_visitor(val) ); - } - template - inline void unpack( const fc::value& val, tuple& t ) { - if( val.size() < tuple::size ) - FC_THROW_REPORT( "Attempt to unpack tuple of size ${size} from array of size ${array_size}", - fc::value( "size", tuple::size)("array_size",val.size() ) ); - t.visit( tuple_from_value_visitor(val) ); - } - - template - inline void unpack( const fc::value& jsv, fc::vector& val ) { - val.resize( jsv.size() ); - uint32_t s = jsv.size(); - for( uint32_t i = 0; i < s; ++i ) { - unpack( jsv[i], val[i] ); - } - } - template - inline void unpack( const fc::value& jsv, std::vector& val ) { - val.resize( jsv.size() ); - uint32_t s = jsv.size(); - for( uint32_t i = 0; i < s; ++i ) { - unpack( jsv[i], val[i] ); - } - } - - // default case - template - inline void pack( fc::value& jsv, const T& v ) { - detail::if_reflected< typename fc::reflector::is_defined >::pack(jsv,v); - } - - template - inline void unpack( const fc::value& jsv, T& v ) { - detail::if_reflected< typename fc::reflector::is_defined >::unpack(jsv,v); - } - -} // namespace fc - diff --git a/include/fc/variant.hpp b/include/fc/variant.hpp new file mode 100644 index 000000000..11ab95af6 --- /dev/null +++ b/include/fc/variant.hpp @@ -0,0 +1,611 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include // memset + +#include +#include +#include +#include +#include +#include + +namespace fc +{ + /** + * @defgroup serializable Serializable _types + * @brief Clas_ses that may be converted to/from an variant + * + * To make a class 'serializable' the following methods must be available + * for your Serializable_type + * + * @code + * void to_variant( const Serializable_type& e, variant& v ); + * void from_variant( const variant& e, Serializable_type& ll ); + * @endcode + */ + + class variant; + class variant_object; + class mutable_variant_object; + class time_point; + class time_point_sec; + class microseconds; + template struct safe; + template + class static_variant; + + struct blob { std::vector data; }; + + void to_variant( const blob& var, variant& vo ); + void from_variant( const variant& var, blob& vo ); + + + template void to_variant( const boost::multi_index_container& s, variant& v ); + template void from_variant( const variant& v, boost::multi_index_container& s ); + + template void to_variant( const smart_ref& s, variant& v ); + template void from_variant( const variant& v, smart_ref& s ); + template void to_variant( const safe& s, variant& v ); + template void from_variant( const variant& v, safe& s ); + template void to_variant( const std::unique_ptr& s, variant& v ); + template void from_variant( const variant& v, std::unique_ptr& s ); + + template void to_variant( const static_variant& s, variant& v ); + template void from_variant( const variant& v, static_variant& s ); + + void to_variant( const uint8_t& var, variant& vo ); + void from_variant( const variant& var, uint8_t& vo ); + void to_variant( const int8_t& var, variant& vo ); + void from_variant( const variant& var, int8_t& vo ); + + void to_variant( const uint16_t& var, variant& vo ); + void from_variant( const variant& var, uint16_t& vo ); + void to_variant( const int16_t& var, variant& vo ); + void from_variant( const variant& var, int16_t& vo ); + + void to_variant( const uint32_t& var, variant& vo ); + void from_variant( const variant& var, uint32_t& vo ); + void to_variant( const int32_t& var, variant& vo ); + void from_variant( const variant& var, int32_t& vo ); + + void to_variant( const variant_object& var, variant& vo ); + void from_variant( const variant& var, variant_object& vo ); + void to_variant( const mutable_variant_object& var, variant& vo ); + void from_variant( const variant& var, mutable_variant_object& vo ); + void to_variant( const std::vector& var, variant& vo ); + void from_variant( const variant& var, std::vector& vo ); + + template + void to_variant( const std::unordered_map& var, variant& vo ); + template + void from_variant( const variant& var, std::unordered_map& vo ); + + template + void to_variant( const fc::flat_map& var, variant& vo ); + template + void from_variant( const variant& var, fc::flat_map& vo ); + + template + void to_variant( const std::map& var, variant& vo ); + template + void from_variant( const variant& var, std::map& vo ); + template + void to_variant( const std::multimap& var, variant& vo ); + template + void from_variant( const variant& var, std::multimap& vo ); + + + template + void to_variant( const std::unordered_set& var, variant& vo ); + template + void from_variant( const variant& var, std::unordered_set& vo ); + + template + void to_variant( const std::deque& var, variant& vo ); + template + void from_variant( const variant& var, std::deque& vo ); + + template + void to_variant( const fc::flat_set& var, variant& vo ); + template + void from_variant( const variant& var, fc::flat_set& vo ); + + template + void to_variant( const std::set& var, variant& vo ); + template + void from_variant( const variant& var, std::set& vo ); + + void to_variant( const time_point& var, variant& vo ); + void from_variant( const variant& var, time_point& vo ); + + void to_variant( const time_point_sec& var, variant& vo ); + void from_variant( const variant& var, time_point_sec& vo ); + + void to_variant( const microseconds& input_microseconds, variant& output_variant ); + void from_variant( const variant& input_variant, microseconds& output_microseconds ); + + #ifdef __APPLE__ + void to_variant( size_t s, variant& v ); + #elif !defined(_MSC_VER) + void to_variant( long long int s, variant& v ); + void to_variant( unsigned long long int s, variant& v ); + #endif + void to_variant( const std::string& s, variant& v ); + + template + void to_variant( const std::shared_ptr& var, variant& vo ); + + template + void from_variant( const variant& var, std::shared_ptr& vo ); + + typedef std::vector variants; + template + void to_variant( const std::pair& t, variant& v ); + template + void from_variant( const variant& v, std::pair& p ); + + + + /** + * @brief stores null, int64, uint64, double, bool, string, std::vector, + * and variant_object's. + * + * variant's allocate everything but strings, arrays, and objects on the + * stack and are 'move aware' for values allcoated on the heap. + * + * Memory usage on 64 bit systems is 16 bytes and 12 bytes on 32 bit systems. + */ + class variant + { + public: + enum type_id + { + null_type = 0, + int64_type = 1, + uint64_type = 2, + double_type = 3, + bool_type = 4, + string_type = 5, + array_type = 6, + object_type = 7, + blob_type = 8 + }; + + /// Constructs a null_type variant + variant(); + /// Constructs a null_type variant + variant( nullptr_t ); + + /// @param str - UTF8 string + variant( const char* str ); + variant( char* str ); + variant( wchar_t* str ); + variant( const wchar_t* str ); + variant( float val ); + variant( uint8_t val ); + variant( int8_t val ); + variant( uint16_t val ); + variant( int16_t val ); + variant( uint32_t val ); + variant( int32_t val ); + variant( uint64_t val ); + variant( int64_t val ); + variant( double val ); + variant( bool val ); + variant( blob val ); + variant( fc::string val ); + variant( variant_object ); + variant( mutable_variant_object ); + variant( variants ); + variant( const variant& ); + variant( variant&& ); + ~variant(); + + /** + * Read-only access to the content of the variant. + */ + class visitor + { + public: + virtual ~visitor(){} + /// handles null_type variants + virtual void handle()const = 0; + virtual void handle( const int64_t& v )const = 0; + virtual void handle( const uint64_t& v )const = 0; + virtual void handle( const double& v )const = 0; + virtual void handle( const bool& v )const = 0; + virtual void handle( const string& v )const = 0; + virtual void handle( const variant_object& v)const = 0; + virtual void handle( const variants& v)const = 0; + }; + + void visit( const visitor& v )const; + + type_id get_type()const; + + bool is_null()const; + bool is_string()const; + bool is_bool()const; + bool is_int64()const; + bool is_uint64()const; + bool is_double()const; + bool is_object()const; + bool is_array()const; + bool is_blob()const; + /** + * int64, uint64, double,bool + */ + bool is_numeric()const; + /** + * int64, uint64, bool + */ + bool is_integer()const; + + int64_t as_int64()const; + uint64_t as_uint64()const; + bool as_bool()const; + double as_double()const; + + blob& get_blob(); + const blob& get_blob()const; + blob as_blob()const; + + /** Convert's double, ints, bools, etc to a string + * @throw if get_type() == array_type | get_type() == object_type + */ + string as_string()const; + + /// @pre get_type() == string_type + const string& get_string()const; + + /// @throw if get_type() != array_type | null_type + variants& get_array(); + + /// @throw if get_type() != array_type + const variants& get_array()const; + + /// @throw if get_type() != object_type | null_type + variant_object& get_object(); + + /// @throw if get_type() != object_type + const variant_object& get_object()const; + + /// @pre is_object() + const variant& operator[]( const char* )const; + /// @pre is_array() + const variant& operator[]( size_t pos )const; + /// @pre is_array() + size_t size()const; + + /** + * _types that use non-intrusive variant conversion can implement the + * following method to implement conversion from variant to T. + * + * + * void from_variant( const Variant& var, T& val ) + * + * + * The above form is not always convienant, so the this templated + * method is used to enable conversion from Variants to other + * types. + */ + template + T as()const + { + T tmp; + from_variant( *this, tmp ); + return tmp; + } + + variant& operator=( variant&& v ); + variant& operator=( const variant& v ); + + template + variant& operator=( T&& v ) + { + return *this = variant( fc::forward(v) ); + } + + template + variant( const optional& v ) + { + memset( this, 0, sizeof(*this) ); + if( v.valid() ) *this = variant(*v); + } + + template + explicit variant( const T& val ); + + + void clear(); + private: + void init(); + double _data; ///< Alligned according to double requirements + char _type[sizeof(void*)]; ///< pad to void* size + }; + typedef optional ovariant; + + /** @ingroup Serializable */ + void from_variant( const variant& var, string& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, variants& vo ); + void from_variant( const variant& var, variant& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, int64_t& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, uint64_t& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, bool& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, double& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, float& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, int32_t& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, uint32_t& vo ); + /** @ingroup Serializable */ + template + void from_variant( const variant& var, optional& vo ) + { + if( var.is_null() ) vo = optional(); + else + { + vo = T(); + from_variant( var, *vo ); + } + } + template + void to_variant( const std::unordered_set& var, variant& vo ) + { + std::vector vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, std::unordered_set& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + vo.reserve( vars.size() ); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as() ); + } + + + template + void to_variant( const std::unordered_map& var, variant& vo ) + { + std::vector< variant > vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = fc::variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, std::unordered_map& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as< std::pair >() ); + + } + template + void to_variant( const std::map& var, variant& vo ) + { + std::vector< variant > vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = fc::variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, std::map& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as< std::pair >() ); + } + + template + void to_variant( const std::multimap& var, variant& vo ) + { + std::vector< variant > vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = fc::variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, std::multimap& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as< std::pair >() ); + } + + + template + void to_variant( const std::set& var, variant& vo ) + { + std::vector vars(var.size()); + size_t i = 0; + for( auto itr = var.begin(); itr != var.end(); ++itr, ++i ) + vars[i] = variant(*itr); + vo = vars; + } + template + void from_variant( const variant& var, std::set& vo ) + { + const variants& vars = var.get_array(); + vo.clear(); + //vo.reserve( vars.size() ); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + vo.insert( itr->as() ); + } + + /** @ingroup Serializable */ + template + void from_variant( const variant& var, std::deque& tmp ) + { + const variants& vars = var.get_array(); + tmp.clear(); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + tmp.push_back( itr->as() ); + } + + /** @ingroup Serializable */ + template + void to_variant( const std::deque& t, variant& v ) + { + std::vector vars(t.size()); + for( size_t i = 0; i < t.size(); ++i ) + vars[i] = variant(t[i]); + v = std::move(vars); + } + + + /** @ingroup Serializable */ + template + void from_variant( const variant& var, std::vector& tmp ) + { + const variants& vars = var.get_array(); + tmp.clear(); + tmp.reserve( vars.size() ); + for( auto itr = vars.begin(); itr != vars.end(); ++itr ) + tmp.push_back( itr->as() ); + } + + /** @ingroup Serializable */ + template + void to_variant( const std::vector& t, variant& v ) + { + std::vector vars(t.size()); + for( size_t i = 0; i < t.size(); ++i ) + vars[i] = variant(t[i]); + v = std::move(vars); + } + + + /** @ingroup Serializable */ + template + void to_variant( const std::pair& t, variant& v ) + { + std::vector vars(2); + vars[0] = variant(t.first); + vars[1] = variant(t.second); + v = vars; + } + template + void from_variant( const variant& v, std::pair& p ) + { + const variants& vars = v.get_array(); + if( vars.size() > 0 ) + p.first = vars[0].as(); + if( vars.size() > 1 ) + p.second = vars[1].as(); + } + + + template + variant::variant( const T& val ) + { + memset( this, 0, sizeof(*this) ); + to_variant( val, *this ); + } + #ifdef __APPLE__ + inline void to_variant( size_t s, variant& v ) { v = variant(uint64_t(s)); } + #endif + template + void to_variant( const std::shared_ptr& var, variant& vo ) + { + if( var ) to_variant( *var, vo ); + else vo = nullptr; + } + + template + void from_variant( const variant& var, std::shared_ptr& vo ) + { + if( var.is_null() ) vo = nullptr; + else if( vo ) from_variant( var, *vo ); + else { + vo = std::make_shared(); + from_variant( var, *vo ); + } + } + template + void to_variant( const std::unique_ptr& var, variant& vo ) + { + if( var ) to_variant( *var, vo ); + else vo = nullptr; + } + + template + void from_variant( const variant& var, std::unique_ptr& vo ) + { + if( var.is_null() ) vo.reset(); + else if( vo ) from_variant( var, *vo ); + else { + vo.reset( new T() ); + from_variant( var, *vo ); + } + } + + + template + void to_variant( const safe& s, variant& v ) { v = s.value; } + + template + void from_variant( const variant& v, safe& s ) { s.value = v.as_uint64(); } + + template + void to_variant( const smart_ref& s, variant& v ) { v = *s; } + + template + void from_variant( const variant& v, smart_ref& s ) { from_variant( v, *s ); } + + template void to_variant( const boost::multi_index_container& c, variant& v ) + { + std::vector vars; + vars.reserve( c.size() ); + for( const auto& item : c ) + vars.emplace_back( variant(item) ); + v = std::move(vars); + } + + template void from_variant( const variant& v, boost::multi_index_container& c ) + { + const variants& vars = v.get_array(); + c.clear(); + for( const auto& item : vars ) + c.insert( item.as() ); + } + + variant operator + ( const variant& a, const variant& b ); + variant operator - ( const variant& a, const variant& b ); + variant operator * ( const variant& a, const variant& b ); + variant operator / ( const variant& a, const variant& b ); + variant operator == ( const variant& a, const variant& b ); + variant operator != ( const variant& a, const variant& b ); + variant operator < ( const variant& a, const variant& b ); + variant operator > ( const variant& a, const variant& b ); + variant operator ! ( const variant& a ); +} // namespace fc + +#include +FC_REFLECT_TYPENAME( fc::variant ) +FC_REFLECT_ENUM( fc::variant::type_id, (null_type)(int64_type)(uint64_type)(double_type)(bool_type)(string_type)(array_type)(object_type)(blob_type) ) +FC_REFLECT( fc::blob, (data) ); diff --git a/include/fc/variant_object.hpp b/include/fc/variant_object.hpp new file mode 100644 index 000000000..5a39c8074 --- /dev/null +++ b/include/fc/variant_object.hpp @@ -0,0 +1,224 @@ +#pragma once +#include +#include +#include + +namespace fc +{ + class mutable_variant_object; + + /** + * @ingroup Serializable + * + * @brief An order-perserving dictionary of variant's. + * + * Keys are kept in the order they are inserted. + * This dictionary implements copy-on-write + * + * @note This class is not optimized for random-access on large + * sets of key-value pairs. + */ + class variant_object + { + public: + /** @brief a key/value pair */ + class entry + { + public: + entry(); + entry( string k, variant v ); + entry( entry&& e ); + entry( const entry& e); + entry& operator=(const entry&); + entry& operator=(entry&&); + + const string& key()const; + const variant& value()const; + void set( variant v ); + + variant& value(); + + private: + string _key; + variant _value; + }; + + typedef std::vector< entry >::const_iterator iterator; + + /** + * @name Immutable Interface + * + * Calling these methods will not result in copies of the + * underlying type. + */ + ///@{ + iterator begin()const; + iterator end()const; + iterator find( const string& key )const; + iterator find( const char* key )const; + const variant& operator[]( const string& key )const; + const variant& operator[]( const char* key )const; + size_t size()const; + bool contains( const char* key ) const { return find(key) != end(); } + ///@} + + variant_object(); + + /** initializes the first key/value pair in the object */ + variant_object( string key, variant val ); + + template + variant_object( string key, T&& val ) + :_key_value( std::make_shared >() ) + { + *this = variant_object( std::move(key), variant(forward(val)) ); + } + variant_object( const variant_object& ); + variant_object( variant_object&& ); + + variant_object( const mutable_variant_object& ); + variant_object( mutable_variant_object&& ); + + variant_object& operator=( variant_object&& ); + variant_object& operator=( const variant_object& ); + + variant_object& operator=( mutable_variant_object&& ); + variant_object& operator=( const mutable_variant_object& ); + + private: + std::shared_ptr< std::vector< entry > > _key_value; + friend class mutable_variant_object; + }; + /** @ingroup Serializable */ + void to_variant( const variant_object& var, variant& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, variant_object& vo ); + + + /** + * @ingroup Serializable + * + * @brief An order-perserving dictionary of variant's. + * + * Keys are kept in the order they are inserted. + * This dictionary implements copy-on-write + * + * @note This class is not optimized for random-access on large + * sets of key-value pairs. + */ + class mutable_variant_object + { + public: + /** @brief a key/value pair */ + typedef variant_object::entry entry; + + typedef std::vector< entry >::iterator iterator; + typedef std::vector< entry >::const_iterator const_iterator; + + /** + * @name Immutable Interface + * + * Calling these methods will not result in copies of the + * underlying type. + */ + ///@{ + iterator begin()const; + iterator end()const; + iterator find( const string& key )const; + iterator find( const char* key )const; + const variant& operator[]( const string& key )const; + const variant& operator[]( const char* key )const; + size_t size()const; + ///@} + variant& operator[]( const string& key ); + variant& operator[]( const char* key ); + + /** + * @name mutable Interface + * + * Calling these methods will result in a copy of the underlying type + * being created if there is more than one reference to this object. + */ + ///@{ + void reserve( size_t s); + iterator begin(); + iterator end(); + void erase( const string& key ); + /** + * + * @return end() if key is not found + */ + iterator find( const string& key ); + iterator find( const char* key ); + + + /** replaces the value at \a key with \a var or insert's \a key if not found */ + mutable_variant_object& set( string key, variant var ); + /** Appends \a key and \a var without checking for duplicates, designed to + * simplify construction of dictionaries using (key,val)(key2,val2) syntax + */ + /** + * Convenience method to simplify the manual construction of + * variant_object's + * + * Instead of: + * mutable_variant_object("c",c).set("a",a).set("b",b); + * + * You can use: + * mutable_variant_object( "c", c )( "b", b)( "c",c ) + * + * @return *this; + */ + mutable_variant_object& operator()( string key, variant var ); + template + mutable_variant_object& operator()( string key, T&& var ) + { + set(std::move(key), variant( fc::forward(var) ) ); + return *this; + } + /** + * Copy a variant_object into this mutable_variant_object. + */ + mutable_variant_object& operator()( const variant_object& vo ); + /** + * Copy another mutable_variant_object into this mutable_variant_object. + */ + mutable_variant_object& operator()( const mutable_variant_object& mvo ); + ///@} + + + template + explicit mutable_variant_object( T&& v ) + :_key_value( new std::vector() ) + { + *this = variant(fc::forward(v)).get_object(); + } + + mutable_variant_object(); + + /** initializes the first key/value pair in the object */ + mutable_variant_object( string key, variant val ); + template + mutable_variant_object( string key, T&& val ) + :_key_value( new std::vector() ) + { + set( std::move(key), variant(forward(val)) ); + } + + mutable_variant_object( mutable_variant_object&& ); + mutable_variant_object( const mutable_variant_object& ); + mutable_variant_object( const variant_object& ); + + mutable_variant_object& operator=( mutable_variant_object&& ); + mutable_variant_object& operator=( const mutable_variant_object& ); + mutable_variant_object& operator=( const variant_object& ); + private: + std::unique_ptr< std::vector< entry > > _key_value; + friend class variant_object; + }; + /** @ingroup Serializable */ + void to_variant( const mutable_variant_object& var, variant& vo ); + /** @ingroup Serializable */ + void from_variant( const variant& var, mutable_variant_object& vo ); + +} // namespace fc diff --git a/include/fc/varint.hpp b/include/fc/varint.hpp deleted file mode 100644 index 2e98be5f2..000000000 --- a/include/fc/varint.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _FC_VARINT_HPP_ -#define _FC_VARINT_HPP_ -#include - -namespace fc { - -struct unsigned_int { - unsigned_int( uint32_t v = 0 ):value(v){} - - operator uint32_t()const { return value; } - - template - unsigned_int& operator=( const T& v ) { value = v; return *this; } - - uint32_t value; -}; - -struct signed_int { - signed_int( int32_t v = 0 ):value(v){} - operator int32_t()const { return value; } - template - signed_int& operator=( const T& v ) { value = v; return *this; } - - int32_t value; -}; - -} // namespace fc - - -#endif diff --git a/include/fc/vector.hpp b/include/fc/vector.hpp index 4396c4e31..29b3388ab 100644 --- a/include/fc/vector.hpp +++ b/include/fc/vector.hpp @@ -1,383 +1,2 @@ #pragma once -#include -#include -#include -#include -#include #include - - -namespace fc { - namespace detail { - template - struct data { - size_t size; - size_t capacity; - T first; - - static data* allocate( size_t cap ) { - data* d = nullptr; - if( cap ){ - d = (data*)malloc(sizeof(data) + sizeof(T)*(static_cast(cap)-1)); - d->capacity = static_cast(cap); - } else { - d = (data*)malloc(sizeof(data)); - d->capacity = 1; - } - d->size = 0; - return d; - } - static data* reallocate( data* d, size_t cap ) { - if( cap ){ - d = (data*)realloc(d,sizeof(data) + sizeof(T)*(static_cast(cap)-1)); - d->capacity = static_cast(cap); - } else { - d = (data*)realloc(d,sizeof(data)); - d->capacity = 1; - } - if( d->size > d->capacity ) - d->size = d->capacity; - return d; - } - private: - data(){}; - }; - - template - struct vector_impl { - public: - typedef T* iterator; - typedef const T* const_iterator; - vector_impl():_data(nullptr){} - vector_impl( vector_impl&& c):_data(c._data){c._data =nullptr; } - vector_impl( const vector_impl& c):_data(nullptr) { - //slog( "copy: c.size %d", c.size() ); - if( c.size() ) { - _data = detail::data::allocate( c.size() ); - _data->size = c.size(); - memcpy(begin(),c.begin(), static_cast(c.size()) ); - } - //slog( "copy: this.size %d", size() ); - } - vector_impl(const_iterator b, const_iterator e ):_data(nullptr) { - resize(e-b); - if( size() ) memcpy( data(), b, static_cast(size()) ); - } - vector_impl(size_t s):_data(nullptr){ - resize(s); - } - ~vector_impl() { - clear(); - } - - - size_t size()const { return _data ? _data->size : 0; } - size_t capacity()const { return _data ? _data->capacity : 0; } - - T& back() { return (&_data->first)[-1+_data->size]; } - const T& back()const { return (&_data->first)[-1+_data->size]; } - T& front() { return (&_data->first)[0]; } - const T& front()const { return (&_data->first)[0]; } - const T* data()const { return (&_data->first); } - T* data() { return (&_data->first); } - - iterator begin() { return _data ? &front() : 0;} - const_iterator begin()const { return _data ? &front() : 0;} - iterator end() { return _data ? (&back())+1: 0;} - const_iterator end()const { return _data ? (&back())+1: 0;} - - T& operator[]( size_t i ) { return (&_data->first)[i]; } - const T& operator[]( size_t i )const { return (&_data->first)[i]; } - - T& at( size_t i ) { return (&_data->first)[i]; } - const T& at( size_t i )const { return (&_data->first)[i]; } - - void pop_back() { erase( &back() ); } - - void clear() { - if( _data != nullptr ) { - free(_data); - } - _data = nullptr; - } - - void reserve( size_t i ) { - _data = detail::data::reallocate( _data, i ); - } - - void resize( size_t i ) { - if( capacity() < i ) { - if( _data ) - _data = detail::data::reallocate( _data, i ); - else - _data = detail::data::allocate( i ); - } - if( _data ) _data->size = i; - } - - template - void push_back( U&& v ) { - resize( size()+1 ); - back() = fc::forward(v); - } - - template - iterator insert( const_iterator loc, U&& t ) { - size_t pos = loc - begin(); - resize( size()+1 ); - char* src = &at(pos); - if( src != &back() ) - memmove( src+1, src, (&back() - src) ); - &back = fc::forward(t); - return &at(pos); - } - - iterator insert( iterator pos, const_iterator first, const_iterator last ) { - if( first >= last ) return pos; - - size_t loc = pos - begin(); - size_t right_size = size() - loc; - resize( size() + (last-first) ); - char* src = &at(loc); - size_t s = last-first; - memmove( src + s, src, right_size ); - memcpy( src, first, s ); - _data->size += (last-first); - return src; - } - - iterator erase( iterator pos ) { - memmove( pos, pos+1, (&back() - pos) ); - _data->size--; - return pos; - } - - iterator erase( iterator first, iterator last ) { - if( first != last ) { - memmove( first, first + (last-first), (&back() - last) ); - _data->size -= last-first; - } - return first; - } - - vector_impl& operator=( vector_impl&& v ) { - fc_swap(_data,v._data); - return *this; - } - vector_impl& operator=( const vector_impl& v ) { - vector_impl tmp(v); - fc_swap(tmp._data,_data); - return *this; - } - protected: - detail::data* _data; - }; - - template - struct vector_impl { - public: - typedef T* iterator; - typedef const T* const_iterator; - vector_impl():_data(nullptr){} - vector_impl( vector_impl&& c):_data(c._data){c._data =nullptr; } - vector_impl( const vector_impl& c):_data(nullptr) { - if( c.size() ) { - _data = detail::data::allocate( c.size() ); - auto i = begin(); - auto ci = c.begin(); - auto ce = c.end(); - while( ci != ce ) { - new (i) T(*ci); - ++i; - ++_data->size; - ++ci; - } - } - } - vector_impl(const_iterator b, const_iterator e ):_data(nullptr) { - resize(e-b); - for( auto i = begin(); i != end(); ++i ) { - *i = *b; - ++b; - } - } - - vector_impl(size_t s):_data(nullptr){ - resize(s); - } - ~vector_impl() { - clear(); - } - - - size_t size()const { return _data ? _data->size : 0; } - size_t capacity()const { return _data ? _data->capacity : 0; } - - T& back() { return (&_data->first)[-1+_data->size]; } - const T& back()const { return (&_data->first)[-1+_data->size]; } - T& front() { return (&_data->first)[0]; } - const T& front()const { return (&_data->first)[0]; } - const T* data()const { return (&_data->first); } - T* data() { return (&_data->first); } - - iterator begin() { return _data ? &front() : 0;} - const_iterator begin()const { return _data ? &front() : 0;} - const_iterator end()const { return _data ? (&back())+1: 0;} - - T& operator[]( size_t i ) { return (&_data->first)[i]; } - const T& operator[]( size_t i )const { return (&_data->first)[i]; } - - T& at( size_t i ) { return (&_data->first)[i]; } - const T& at( size_t i )const { return (&_data->first)[i]; } - - void pop_back() { erase( &back() ); } - - - void clear() { - if( this->_data != nullptr ) { - auto c = this->begin(); - auto e = this->end(); - while( c != e ) { - (*c).~T(); - ++c; - } - free(this->_data); - } - this->_data = nullptr; - } - - void reserve( size_t i ) { - if( nullptr != this->_data && i <= this->_data->capacity ) - return; - - auto _ndata = detail::data::allocate( i ); - auto nc = &_ndata->first; - auto c = this->begin(); - auto e = this->end(); - while( c != e ) { - new (nc) T(fc::move( *c )); - (*c).~T(); - ++_ndata->size; - ++c; - ++nc; - } - fc_swap( _ndata, this->_data ); - if( _ndata ) free(_ndata); - } - - void resize( size_t i ) { - this->reserve(i); - while( i < this->_data->size ) { - this->back().~T(); - --this->_data->size; - } - while( this->_data->size < i ) { - new (&this->back()+1) T(); - ++this->_data->size; - } - } - - template - void push_back( U&& v ) { - this->reserve( this->size()+1 ); - new (&back()+1) T(fc::forward(v)); - ++this->_data->size; - } - - template - iterator insert( const_iterator loc, U&& t ) { - size_t pos = loc - this->begin(); - this->reserve( this->size()+1 ); - loc = this->begin() + pos; - if( this->size() != 0 ) { - new ((void*)this->end()) T( fc::move(this->back()) ); - auto cur = this->back(); - ++this->_data->size; - while( cur != loc ) { - *cur = fc::move( *(cur-1) ); - } - *cur = fc::forward(t); - } else { - new (this->end()) T( fc::forward(t) ); - ++this->_data->size; - } - return &this->at(pos); - } - - iterator insert( iterator pos, const_iterator first, const_iterator last ) { - //static_assert( false, "Not Implemented" ); - return 0; - } - - iterator erase( iterator pos ) { - if( pos == this->end() ) { return pos; } - auto next = pos + 1; - while( next != this->end() ) { - *pos = fc::move(*next); - ++pos; ++next; - } - pos->~T(); - this->_data->size--; - return pos; - } - - iterator erase( iterator first, iterator last ) { - iterator c = first; - iterator m = last; - iterator e = this->end(); - while( c != e ) { - if( m != e ) *c = fc::move( *m ); - else c->~T(); - ++c; - ++m; - } - this->_data->size -= last-first; - return last; - } - vector_impl& operator=( vector_impl&& v ) { - fc_swap(_data,v._data); - return *this; - } - vector_impl& operator=( const vector_impl& v ) { - vector_impl tmp(v); - fc_swap(tmp._data,_data); - return *this; - } - private: - detail::data* _data; - }; - } - - /** - * @brief provides a light-weight vector similar to std::vector except that - * it prevents needing to include which requires many more headers - * and increases compile times. - * - * This class should have the same API as std::vector and can be expanded as - * additional features of std::vector are required. - * - */ - template - class vector : public detail::vector_impl::type> { - public: - vector(){} - vector( size_t s ):detail::vector_impl::type>(s){} - vector( const vector& v ):detail::vector_impl::type>(v){} - vector( vector&& v ):detail::vector_impl::type>(fc::move(v)){} - - vector( const T* b, const T* e ):detail::vector_impl::type>(b,e){} - - vector& operator=( vector&& v ) { - *((base*)this) = fc::move(v); - return *this; - } - vector& operator=( const vector& v ) { - *((base*)this) = v; - return *this; - } - private: - typedef detail::vector_impl::type> base; - }; - -}; - diff --git a/include/fc/vector_g.hpp b/include/fc/vector_g.hpp deleted file mode 100644 index 08760228b..000000000 --- a/include/fc/vector_g.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef _FC_VECTOR_HPP_ -#define _FC_VECTOR_HPP_ -#include - -namespace fc { - class vector_impl { - public: - size_t size()const; - size_t capacity()const; - void pop_back(); - void clear(); - void resize( size_t ); - void reserve( size_t ); - - protected: - vector_impl( abstract_value_type& v, size_t size ); - vector_impl( const vector_impl& ); - vector_impl( vector_impl&& ); - ~vector_impl(); - - vector_impl& operator=( const vector_impl& v ); - vector_impl& operator=( vector_impl&& v ); - - void _push_back( const void* v ); - void _push_back_m( void* v ); - - void* _back(); - const void* _back()const; - - void* _at(size_t); - const void* _at(size_t)const; - - void* _insert( void* pos, const void* t ); - void* _insert( void* pos, void* t ); - void* _erase( void* pos ); - void* _erase( void* first, void* last ); - - struct vector_impl_d* my; - }; - - class vector_pod_impl { - public: - size_t size()const; - size_t capacity()const; - void pop_back(); - void clear(); - void resize( size_t ); - void reserve( size_t ); - - protected: - vector_pod_impl( unsigned int size_of, size_t size ); - vector_pod_impl( const vector_pod_impl& ); - vector_pod_impl( vector_pod_impl&& ); - ~vector_pod_impl(); - - vector_pod_impl& operator=( const vector_pod_impl& v ); - vector_pod_impl& operator=( vector_pod_impl&& v ); - - void _push_back( const void* v ); - void _push_back_m( void* v ); - - void* _back(); - const void* _back()const; - - void* _at(size_t); - const void* _at(size_t)const; - - void* _insert( void* pos, const void* t ); - void* _erase( void* pos ); - void* _erase( void* first, void* last ); - - struct vector_pod_impl_d* my; - }; - - template - class vector_base : public vector_impl { - public: - vector_base( size_t s ):vector_impl( value_type::instance(), s ){}; - vector_base( const vector_base& c ):vector_impl( c ){}; - vector_base( vector_base&& c ):vector_impl( fc::move(c) ){}; - - vector_base& operator=( const vector_base& v ){ vector_impl::operator=(v); return *this; } - vector_base& operator=( vector_base&& v ) { vector_impl::operator=(fc::move(v)); return *this; } - }; - - template - class vector_base : public vector_pod_impl { - public: - vector_base( size_t s ):vector_pod_impl( sizeof(T), s ){}; - vector_base( const vector_base& c ):vector_pod_impl( c ){}; - vector_base( vector_base&& c ):vector_pod_impl( fc::move(c) ){}; - - vector_base& operator=( const vector_base& v ){ vector_pod_impl::operator=(v); return *this; } - vector_base& operator=( vector_base&& v ) { vector_pod_impl::operator=(fc::move(v)); return *this; } - }; - - template - class vector : public vector_base::value > { - public: - vector( size_t size = 0 ):vector_base::value>( size ){} - vector( const vector& v ):vector_base::value>(v){} - vector( vector&& v ):vector_base::value>(fc::move(v)){} - - vector& operator=( const vector& v ){ vector_base::value>::operator=(v); return *this; } - vector& operator=( vector&& v ) { vector_base::value>::operator=(fc::move(v)); return *this; } - - typedef T* iterator; - typedef const T* const_iterator; - - T* begin() { return &front(); } - const T* begin()const { return &front(); } - const T* end()const { return &back() + 1; } - - void push_back( const T& t ) { _push_back(&t); } - void push_back( T&& t ) { _push_back_m(&t); } - - T& back() { return *((T*)this->_back()); } - const T& back()const { return *((const T*)this->_back()); } - - T& front() { return *((T*)this->_at(0)); } - const T& front()const { return *((const T*)this->_at(0)); } - - T& operator[]( size_t p ) { return *((T*)this->_at(p)); } - const T& operator[]( size_t p )const { return *((const T*)this->_at(p)); } - - T& at( size_t p ) { return *((T*)this->_at(p)); } - const T& at( size_t p )const { return *((const T*)this->_at(p)); } - - iterator insert( iterator pos, const T& t ) { return (iterator*)this->_insert( pos, &t ); } - iterator insert( iterator pos, T&& t ) { return (iterator*)this->_insert_m(pos, &t); } - iterator erase( iterator pos ) { return (iterator*)this->_erase(pos); } - iterator erase( iterator first, iterator last ) { return (iterator*)this->_erase(first,last); } - }; - namespace reflect { - template class reflector; - template class reflector>; - } -} // namespace fc - -#endif // _FC_VECTOR_HPP_ diff --git a/include/fc/wait_any.hpp b/include/fc/wait_any.hpp index f31428bcf..c3d620749 100644 --- a/include/fc/wait_any.hpp +++ b/include/fc/wait_any.hpp @@ -1,7 +1,6 @@ -#ifndef _FC_WAIT_ANY_HPP_ -#define _FC_WAIT_ANY_HPP_ +#pragma once #include -#include +#include namespace fc { template @@ -12,4 +11,3 @@ namespace fc { return wait( fc::move(p), timeout_us ); } } -#endif // _FC_WAIT_ANY_HPP_ diff --git a/src/asio.cpp b/src/asio.cpp index 90cbc143d..0319eaa8b 100644 --- a/src/asio.cpp +++ b/src/asio.cpp @@ -1,33 +1,76 @@ #include -#include -#include +#include #include +#include +#include namespace fc { namespace asio { namespace detail { - void read_write_handler( const promise::ptr& p, const boost::system::error_code& ec, size_t bytes_transferred ) { - if( !ec ) p->set_value(bytes_transferred); - else p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); - } + + read_write_handler::read_write_handler(const promise::ptr& completion_promise) : + _completion_promise(completion_promise) + { + // assert(false); // to detect anywhere we're not passing in a shared buffer + } + void read_write_handler::operator()(const boost::system::error_code& ec, size_t bytes_transferred) + { + // assert(false); // to detect anywhere we're not passing in a shared buffer + if( !ec ) + _completion_promise->set_value(bytes_transferred); + else if( ec == boost::asio::error::eof ) + _completion_promise->set_exception( fc::exception_ptr( new fc::eof_exception( FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); + else + _completion_promise->set_exception( fc::exception_ptr( new fc::exception( FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); + } + read_write_handler_with_buffer::read_write_handler_with_buffer(const promise::ptr& completion_promise, + const std::shared_ptr& buffer) : + _completion_promise(completion_promise), + _buffer(buffer) + {} + void read_write_handler_with_buffer::operator()(const boost::system::error_code& ec, size_t bytes_transferred) + { + if( !ec ) + _completion_promise->set_value(bytes_transferred); + else if( ec == boost::asio::error::eof ) + _completion_promise->set_exception( fc::exception_ptr( new fc::eof_exception( FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); + else + _completion_promise->set_exception( fc::exception_ptr( new fc::exception( FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); + } + void read_write_handler_ec( promise* p, boost::system::error_code* oec, const boost::system::error_code& ec, size_t bytes_transferred ) { p->set_value(bytes_transferred); *oec = ec; } - void error_handler( const promise::ptr& p, + void error_handler( const promise::ptr& p, const boost::system::error_code& ec ) { - p->set_value(ec); + if( !ec ) + p->set_value(); + else + { + if( ec == boost::asio::error::eof ) + { + p->set_exception( fc::exception_ptr( new fc::eof_exception( + FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); + } + else + { + //elog( "${message} ", ("message", boost::system::system_error(ec).what())); + p->set_exception( fc::exception_ptr( new fc::exception( + FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); + } + } } - void error_handler_ec( promise* p, + void error_handler_ec( promise* p, const boost::system::error_code& ec ) { p->set_value(ec); } template - void resolve_handler( + void resolve_handler( const typename promise >::ptr& p, - const boost::system::error_code& ec, + const boost::system::error_code& ec, IteratorType itr) { if( !ec ) { std::vector eps; @@ -37,36 +80,99 @@ namespace fc { } p->set_value( eps ); } else { - p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); + //elog( "%s", boost::system::system_error(ec).what() ); + //p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); + p->set_exception( + fc::exception_ptr( new fc::exception( + FC_LOG_MESSAGE( error, "process exited with: ${message} ", + ("message", boost::system::system_error(ec).what())) ) ) ); } } } - boost::asio::io_service& default_io_service() { - static boost::asio::io_service* io = new boost::asio::io_service(); - static boost::asio::io_service::work the_work(*io); - static boost::thread io_t([=] { fc::thread::current().set_name("asio1"); io->run(); }); - static boost::thread io_t2([=]{ fc::thread::current().set_name("asio2"); io->run(); }); - static boost::thread io_t3([=]{ fc::thread::current().set_name("asio3"); io->run(); }); - return *io; + + struct default_io_service_scope + { + boost::asio::io_service* io; + boost::thread* asio_thread; + boost::asio::io_service::work* the_work; + + default_io_service_scope() + { + io = new boost::asio::io_service(); + the_work = new boost::asio::io_service::work(*io); + asio_thread = new boost::thread( [=]() + { + fc::thread::current().set_name("asio"); + while (!io->stopped()) + { + try + { + io->run(); + } + catch (const fc::exception& e) + { + elog("Caught unhandled exception in asio service loop: ${e}", ("e", e)); + } + catch (const std::exception& e) + { + elog("Caught unhandled exception in asio service loop: ${e}", ("e", e.what())); + } + catch (...) + { + elog("Caught unhandled exception in asio service loop"); + } + } + }); + } + + void cleanup() + { + delete the_work; + io->stop(); + asio_thread->join(); + delete io; + delete asio_thread; + } + + ~default_io_service_scope() + {} + }; + + /// If cleanup is true, do not use the return value; it is a null reference + boost::asio::io_service& default_io_service(bool cleanup) { + static default_io_service_scope fc_asio_service; + if (cleanup) + fc_asio_service.cleanup(); + return *fc_asio_service.io; } namespace tcp { - std::vector resolve( const std::string& hostname, const std::string& port) { - resolver res( fc::asio::default_io_service() ); - promise >::ptr p( new promise >() ); - res.async_resolve( boost::asio::ip::tcp::resolver::query(hostname,port), - boost::bind( detail::resolve_handler, p, _1, _2 ) ); - return p->wait();; + std::vector resolve( const std::string& hostname, const std::string& port) + { + try + { + resolver res( fc::asio::default_io_service() ); + promise >::ptr p( new promise >("tcp::resolve completion") ); + res.async_resolve( boost::asio::ip::tcp::resolver::query(hostname,port), + boost::bind( detail::resolve_handler, p, _1, _2 ) ); + return p->wait();; } + FC_RETHROW_EXCEPTIONS(warn, "") + } } namespace udp { - std::vector resolve( resolver& r, const std::string& hostname, const std::string& port) { - resolver res( fc::asio::default_io_service() ); - promise >::ptr p( new promise >() ); - res.async_resolve( resolver::query(hostname,port), - boost::bind( detail::resolve_handler, p, _1, _2 ) ); - return p->wait(); + std::vector resolve( resolver& r, const std::string& hostname, const std::string& port) + { + try + { + resolver res( fc::asio::default_io_service() ); + promise >::ptr p( new promise >("udp::resolve completion") ); + res.async_resolve( resolver::query(hostname,port), + boost::bind( detail::resolve_handler, p, _1, _2 ) ); + return p->wait(); } + FC_RETHROW_EXCEPTIONS(warn, "") + } } - + } } // namespace fc::asio diff --git a/src/bigint.cpp b/src/bigint.cpp deleted file mode 100644 index ad8d5ce12..000000000 --- a/src/bigint.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include - -namespace fc { - bigint::bigint( const char* bige, uint32_t l ) { - n = BN_bin2bn( (const unsigned char*)bige, l, NULL ); - } - - bigint::bigint( unsigned long i ) - :n(BN_new()) { - BN_set_word( n, i ); - } - - bigint::bigint( const bigint& c ) { - n = BN_dup( c.n ); - } - - bigint::bigint( bigint&& b ) { - n = b.n; - b.n = 0; - } - - bigint::~bigint() { - if(n!=0) BN_free(n); - } - - bool bigint::is_negative()const { return BN_is_negative(n); } - int64_t bigint::to_int64()const { return BN_get_word(n); } - - int64_t bigint::log2()const { return BN_num_bits(n); } - bool bigint::operator < ( const bigint& c )const { - return BN_cmp( n, c.n ) < 0; - } - bool bigint::operator > ( const bigint& c )const { - return BN_cmp( n, c.n ) > 0; - } - bool bigint::operator >= ( const bigint& c )const { - return BN_cmp( n, c.n ) >= 0; - } - bool bigint::operator == ( const bigint& c )const { - return BN_cmp( n, c.n ) == 0; - } - - bigint bigint::operator + ( const bigint& a )const { - bigint tmp(*this); - BN_add( tmp.n, n, a.n ); - return tmp; - } - bigint bigint::operator * ( const bigint& a )const { - BN_CTX* ctx = BN_CTX_new(); - bigint tmp(*this); - BN_mul( tmp.n, n, a.n, ctx ); - BN_CTX_free(ctx); - return tmp; - } - bigint bigint::operator / ( const bigint& a ) const { - BN_CTX* ctx = BN_CTX_new(); - bigint tmp(*this); - BN_div( tmp.n, NULL, n, a.n, ctx ); - BN_CTX_free(ctx); - return tmp; - } - bigint bigint::operator - ( const bigint& a )const { - bigint tmp(*this); - BN_sub( tmp.n, n, a.n ); - return tmp; - } - - - bigint& bigint::operator = ( bigint&& a ) { - fc_swap( a.n, n ); - return *this; - } - bigint& bigint::operator = ( const bigint& a ) { - if( &a == this ) - return *this; - BN_copy( n, a.n ); - return *this; - } - bigint::operator fc::string()const { - return BN_bn2dec(n); - } -} // namespace fc diff --git a/src/byteswap.hpp b/src/byteswap.hpp new file mode 100644 index 000000000..1a0e5347c --- /dev/null +++ b/src/byteswap.hpp @@ -0,0 +1,11 @@ +#pragma once + +#ifdef _MSC_VER +# include +# define bswap_64(x) _byteswap_uint64(x) +#elif defined(__APPLE__) +# include +# define bswap_64(x) OSSwapInt64(x) +#else +# include +#endif diff --git a/src/compress/miniz.c b/src/compress/miniz.c new file mode 100644 index 000000000..358143a6a --- /dev/null +++ b/src/compress/miniz.c @@ -0,0 +1,4916 @@ +/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): + - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug + would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() + (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). + - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size + - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. + Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). + - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes + - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed + - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. + - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti + - Merged MZ_FORCEINLINE fix from hdeanclark + - Fix include before config #ifdef, thanks emil.brink + - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can + set it to 1 for real-time compression). + - Merged in some compiler fixes from paulharris's github repro. + - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. + - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. + - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. + - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled + - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch + 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. + - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly + "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. + - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. + - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. + - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). + 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. + level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. + 5/28/11 v1.11 - Added statement from unlicense.org + 5/27/11 v1.10 - Substantial compressor optimizations: + - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. + - Refactored the compression code for better readability and maintainability. + - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large + drop in throughput on some files). + 5/15/11 v1.09 - Initial stable release. + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times, and the C run-time funcs that get/set times won't be called. +// The current downside is the times written to your archives will be from 1979. +//#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) + // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux + #define MINIZ_NO_TIME +#endif + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) + #include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_VERSION "9.1.15" +#define MZ_VERNUM 0x91F0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 15 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). +// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). +// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct mz_zip_archive_tag +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. +// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. +// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must call mz_free() on the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. +// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). +// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) +// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. +// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL +// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#else +enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#endif + +// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. +// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. +// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. +// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; + +#include +#include + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC + #define MZ_MALLOC(x) NULL + #define MZ_FREE(x) (void)x, ((void)0) + #define MZ_REALLOC(p, x) NULL +#else + #define MZ_MALLOC(x) malloc(x) + #define MZ_FREE(x) free(x) + #define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#ifdef _MSC_VER + #define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) + #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) +#else + #define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// ------------------- zlib-style API's + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + return (s2 << 16) + s1; +} + +// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) return MZ_CRC32_INIT; + crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } + return ~crcu32; +} + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +#ifndef MINIZ_NO_ZLIB_APIS + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; + for ( ; ; ) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) + { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct { int m_err; const char *m_pDesc; } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, + { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif //MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never +// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. +// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a +// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the +// bit buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read +// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully +// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. +// The slow path is only executed at the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; + static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } + + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } +#else + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, + 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, + 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, + 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, + 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, + 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, + 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, + 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7 }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. +typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next=1; next < n-1; next++) + { + if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; + avbl = 1; used = dpth = 0; root = n-2; next = n-1; + while (avbl>0) + { + while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } + while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } + avbl = 2*used; dpth++; used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; mz_uint32 total = 0; if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) do { \ + mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ +} MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ +} rle_repeat_count = 0; } } + +#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ +} rle_z_count = 0; } } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. + if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) + { + mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } + } + else + { + mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + if (!probe_len) + { + *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; + if (probe_len > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output buffer. + if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) + { + int n; + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; + d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; + do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; + *pOut_len = out_buf.m_size; return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; + return out_buf.m_size; +} + +#ifndef MINIZ_NO_ZLIB_APIS +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} +#endif //MINIZ_NO_ZLIB_APIS + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; + if (!pComp) return NULL; + MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } + // write dummy header + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + // write real header + *pLen_out = out_buf.m_size-41; + { + static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; + mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, + 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, + (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; + c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef MINIZ_NO_STDIO + #define MZ_FILE void * +#else + #include + #include + + #if defined(_MSC_VER) || defined(__MINGW64__) + static FILE *mz_fopen(const char *pFilename, const char *pMode) + { + FILE* pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; + } + static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) + { + FILE* pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; + } + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN mz_fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 _ftelli64 + #define MZ_FSEEK64 _fseeki64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN mz_freopen + #define MZ_DELETE_FILE remove + #elif defined(__MINGW32__) + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #elif defined(__TINYC__) + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftell + #define MZ_FSEEK64 fseek + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #elif defined(__GNUC__) && _LARGEFILE64_SOURCE + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen64(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT stat64 + #define MZ_FILE_STAT stat64 + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) + #define MZ_DELETE_FILE remove + #else + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello + #define MZ_FSEEK64 fseeko + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. +enum +{ + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + // Central directory header record offsets + MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + // Local directory header offsets + MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + MZ_FILE *m_pFile; + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; + if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; + pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif + +#ifndef MINIZ_NO_STDIO +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef MINIZ_NO_TIME + (void)pFilename; *pDOS_date = *pDOS_time = 0; +#else + struct MZ_FILE_STAT_STRUCT file_stat; + // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); +#endif // #ifdef MINIZ_NO_TIME + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +} +#endif // #ifndef MINIZ_NO_TIME +#endif // #ifndef MINIZ_NO_STDIO + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END + +// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) + { + int child, root = start; + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= size) + break; + child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + start--; + } + + end = size - 1; + while (end > 0) + { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= end) + break; + child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +{ + mz_uint cdir_size, num_this_disk, cdir_disk_index; + mz_uint64 cdir_ofs; + mz_int64 cur_file_ofs; + const mz_uint8 *p; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + // Find the end of central directory record by scanning the file from the end towards the beginning. + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for ( ; ; ) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + for (i = n - 4; i >= 0; --i) + if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + break; + if (i >= 0) + { + cur_file_ofs += i; + break; + } + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + // Read and verify the end of central directory record. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || + ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) + return MZ_FALSE; + + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return MZ_FALSE; + + if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return MZ_FALSE; + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + + // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return MZ_FALSE; + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return MZ_FALSE; + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return MZ_FALSE; + + // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, comp_size, decomp_size, disk_index; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return MZ_FALSE; + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) + return MZ_FALSE; + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index != num_this_disk) && (disk_index != 1)) + return MZ_FALSE; + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return MZ_FALSE; + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return MZ_FALSE; + n -= total_header_size; p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +{ + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + // First see if the filename ends with a '/' character. + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. + // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. + // FIXME: Remove this check? Is it necessary - we already check the filename. + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & 0x10) != 0) + return MZ_TRUE; + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) + { + int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint file_index; size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); if (name_len > 0xFFFF) return -1; + comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). + // I'm torn how to handle this case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return MZ_FALSE; + return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); + } + + // Decompress the file either directly from memory or from a file input buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#endif + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#endif + return NULL; + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). + // I'm torn how to handle this case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input buffer. + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) + { +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#endif + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} +#endif + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } +static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) + { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); +#ifdef _MSC_VER + if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#else + if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#endif + return 0; + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max size + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + pFilename; return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } + else if (pState->m_pMem) + { + // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; time(&cur_time); + mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + return MZ_FALSE; + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) + { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for ( ; ; ) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) + return MZ_FALSE; + + while (comp_bytes_remaining) + { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } + else + { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) + { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) + if (!mz_zip_writer_finalize_archive(&zip_archive)) + status = MZ_FALSE; + if (!mz_zip_writer_end(&zip_archive)) + status = MZ_FALSE; + if ((!status) && (created_new_archive)) + { + // It's a new archive and something went wrong, so just delete it. + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + 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 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. + + For more information, please refer to +*/ diff --git a/src/compress/smaz.cpp b/src/compress/smaz.cpp new file mode 100644 index 000000000..da1521acc --- /dev/null +++ b/src/compress/smaz.cpp @@ -0,0 +1,223 @@ +#include +#include +#include +#include +namespace fc { + +typedef const char* const_char_ptr; +/* Our compression codebook, used for compression */ +static const_char_ptr Smaz_cb[241] = { +"\002s,\266", "\003had\232\002leW", "\003on \216", "", "\001yS", +"\002ma\255\002li\227", "\003or \260", "", "\002ll\230\003s t\277", +"\004fromg\002mel", "", "\003its\332", "\001z\333", "\003ingF", "\001>\336", +"\001 \000\003 (\002nc\344", "\002nd=\003 on\312", +"\002ne\213\003hat\276\003re q", "", "\002ngT\003herz\004have\306\003s o\225", +"", "\003ionk\003s a\254\002ly\352", "\003hisL\003 inN\003 be\252", "", +"\003 fo\325\003 of \003 ha\311", "", "\002of\005", +"\003 co\241\002no\267\003 ma\370", "", "", "\003 cl\356\003enta\003 an7", +"\002ns\300\001\"e", "\003n t\217\002ntP\003s, \205", +"\002pe\320\003 we\351\002om\223", "\002on\037", "", "\002y G", "\003 wa\271", +"\003 re\321\002or*", "", "\002=\"\251\002ot\337", "\003forD\002ou[", +"\003 toR", "\003 th\r", "\003 it\366", +"\003but\261\002ra\202\003 wi\363\002<\346", "\002to\024", "\003arew", "\001d\030", +"\002tr\303", "", "\001\n1\003 a \222", "\003f tv\002veo", "\002un\340", "", +"\003e o\242", "\002a \243\002wa\326\001e\002", "\002ur\226\003e a\274", +"\002us\244\003\n\r\n\247", "\002ut\304\003e c\373", "\002we\221", "", "", +"\002wh\302", "\001f,", "", "", "", "\003d t\206", "", "", "\003th \343", +"\001g;", "", "", "\001\r9\003e s\265", "\003e t\234", "", "\003to Y", +"\003e\r\n\236", "\002d \036\001h\022", "", "\001,Q", "\002 a\031", "\002 b^", +"\002\r\n\025\002 cI", "\002 d\245", "\002 e\253", "\002 fh\001i\b\002e \v", +"", "\002 hU\001-\314", "\002 i8", "", "", "\002 l\315", "\002 m{", +"\002f :\002 n\354", "\002 o\035", "\002 p}\001.n\003\r\n\r\250", "", +"\002 r\275", "\002 s>", "\002 t\016", "", "\002g \235\005which+\003whi\367", +"\002 w5", "\001/\305", "\003as \214", "\003at \207", "", "\003who\331", "", +"\001l\026\002h \212", "", "\002, $", "", "\004withV", "", "", "", "\001m-", "", +"", "\002ac\357", "\002ad\350", "\003TheH", "", "", "\004this\233\001n\t", +"", "\002. y", "", "\002alX\003e, \365", "\003tio\215\002be\\", +"\002an\032\003ver\347", "", "\004that0\003tha\313\001o\006", "\003was2", +"\002arO", "\002as.", "\002at'\003the\001\004they\200\005there\322\005theird", +"\002ce\210", "\004were]", "", "\002ch\231\002l \264\001p<", "", "", +"\003one\256", "", "\003he \023\002dej", "\003ter\270", "\002cou", "", +"\002by\177\002di\201\002eax", "", "\002ec\327", "\002edB", "\002ee\353", "", +"", "\001r\f\002n )", "", "", "", "\002el\262", "", "\003in i\002en3", "", +"\002o `\001s\n", "", "\002er\033", "\003is t\002es6", "", "\002ge\371", +"\004.com\375", "\002fo\334\003our\330", "\003ch \301\001t\003", "\002hab", "", +"\003men\374", "", "\002he\020", "", "", "\001u&", "\002hif", "", +"\003not\204\002ic\203", "\003ed @\002id\355", "", "", "\002ho\273", +"\002r K\001vm", "", "", "", "\003t t\257\002il\360", "\002im\342", +"\003en \317\002in\017", "\002io\220", "\002s \027\001wA", "", "\003er |", +"\003es ~\002is%", "\002it/", "", "\002iv\272", "", +"\002t #\ahttp://C\001x\372", "\002la\211", "\001<\341", "\003, a\224" +}; + + +/* Reverse compression codebook, used for decompression */ +static const_char_ptr Smaz_rcb[254] = { +" ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th", +" t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an", +"er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at", +" ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en", +" ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ", +"w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his", +"st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ", +"b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"", +"hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ", +"ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p", +"es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la", +"h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o", +"ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere", +" co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e", +"s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no", +"ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr", +"ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ", +"pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z", +"fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad", +" we", "ly", "ee", " n", "id", " cl", "ac", "il", " 1) h2 += in[1]; + if (inlen > 2) h3 = h2^in[2]; + if (j > inlen) j = inlen; + + /* Try to lookup substrings into the hash table, starting from the + * longer to the shorter substrings */ + for (; j > 0; j--) { + switch(j) { + case 1: slot = Smaz_cb[h1%241]; break; + case 2: slot = Smaz_cb[h2%241]; break; + default: slot = Smaz_cb[h3%241]; break; + } + while(slot[0]) { + if (slot[0] == j && memcmp(slot+1,in,j) == 0) { + /* Match found in the hash table, + * prepare a verbatim bytes flush if needed */ + if (verblen) { + needed = (verblen == 1) ? 2 : 2+verblen; + flush = out; + out += needed; + outlen -= needed; + } + /* Emit the byte */ + if (outlen <= 0) return _outlen+1; + out[0] = slot[slot[0]+1]; + out++; + outlen--; + inlen -= j; + in += j; + goto out; + } else { + slot += slot[0]+2; + } + } + } + /* Match not found - add the byte to the verbatim buffer */ + verb[verblen] = in[0]; + verblen++; + inlen--; + in++; +out: + /* Prepare a flush if we reached the flush length limit, and there + * is not already a pending flush operation. */ + if (!flush && (verblen == 256 || (verblen > 0 && inlen == 0))) { + needed = (verblen == 1) ? 2 : 2+verblen; + flush = out; + out += needed; + outlen -= needed; + if (outlen < 0) return _outlen+1; + } + /* Perform a verbatim flush if needed */ + if (flush) { + if (verblen == 1) { + flush[0] = (signed char)254; + flush[1] = verb[0]; + } else { + flush[0] = (signed char)255; + flush[1] = (signed char)(verblen-1); + memcpy(flush+2,verb,verblen); + } + flush = NULL; + verblen = 0; + } + } + return out-_out; +} + +void smaz_decompress(const char *in, uint32_t inlen, std::stringstream& ss ) { + const unsigned char *c = (const unsigned char*) in; +// char *_out = out; +// int _outlen = outlen; + + while(inlen) { + if (*c == 254) { + /* Verbatim byte */ + //if (outlen < 1) return _outlen+1; + //*out = *(c+1); + ss.put( *(c+1) ); + //out++; + //outlen--; + c += 2; + inlen -= 2; + } else if (*c == 255) { + /* Verbatim string */ + int len = (*(c+1))+1; + //if (outlen < len) return _outlen+1; + ss.write( (const char*)(c+2),len ); + + //memcpy(out,c+2,len); + //out += len; + //outlen -= len; + c += 2+len; + inlen -= 2+len; + } else { + /* Codebook entry */ + const char *s = Smaz_rcb[*c]; + int len = strlen(s); + + //if (outlen < len) return _outlen+1; + //memcpy(out,s,len); + ss.write( s, len ); + //out += len; + //outlen -= len; + c++; + inlen--; + } + } +} + + + + std::string smaz_compress( const std::string& in ) + { + std::string out; + out.resize(in.size()); + auto out_len = smaz_compress( in.c_str(), in.size(), &out[0], out.size() ); + FC_ASSERT( out_len <= out.size() ); + out.resize(out_len); + return out; + } + std::string smaz_decompress( const std::string& compressed ) + { + std::stringstream ss; + smaz_decompress( compressed.c_str(), compressed.length(), ss ); + return ss.str(); + } + +} // namespace fc diff --git a/src/compress/zlib.cpp b/src/compress/zlib.cpp new file mode 100644 index 000000000..dd9d23bc7 --- /dev/null +++ b/src/compress/zlib.cpp @@ -0,0 +1,15 @@ +#include + +#include "miniz.c" + +namespace fc +{ + string zlib_compress(const string& in) + { + size_t compressed_message_length; + char* compressed_message = (char*)tdefl_compress_mem_to_heap(in.c_str(), in.size(), &compressed_message_length, TDEFL_WRITE_ZLIB_HEADER | TDEFL_DEFAULT_MAX_PROBES); + string result(compressed_message, compressed_message_length); + free(compressed_message); + return result; + } +} diff --git a/src/console_appender.cpp b/src/console_appender.cpp deleted file mode 100644 index 84f5f1ae9..000000000 --- a/src/console_appender.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif - -namespace fc { - console_appender::console_appender( const value& args ) { - cfg = fc::value_cast(args); - for( int i = 0; i < log_level::off+1; ++i ) - lc[i] = color::console_default; - for( auto itr = cfg.level_colors.begin(); itr != cfg.level_colors.end(); ++itr ) - lc[itr->level] = itr->color; - } - const char* get_console_color(console_appender::color::type t ) { - switch( t ) { - case console_appender::color::red: return CONSOLE_RED; - case console_appender::color::green: return CONSOLE_GREEN; - case console_appender::color::brown: return CONSOLE_BROWN; - case console_appender::color::blue: return CONSOLE_BLUE; - case console_appender::color::magenta: return CONSOLE_MAGENTA; - case console_appender::color::cyan: return CONSOLE_CYAN; - case console_appender::color::white: return CONSOLE_WHITE; - case console_appender::color::console_default: - default: - return CONSOLE_DEFAULT; - } - } - const char* console_appender::get_color( log_level::type l )const { - return get_console_color( lc[l] ); - } - void console_appender::log( const log_message& m ) { - fc::string message = fc::substitute( m.format, m.args ); - fc::value lmsg(m); - - FILE* out = stream::std_error ? stderr : stdout; - fc::string fmt_str = fc::substitute( cfg.format, value(m).set( "message", message) ); - - fc::unique_lock lock(log_mutex()); - #ifndef WIN32 - if(isatty(fileno(out))) fprintf( out, "\r%s", get_color( m.level ) ); - #endif - - fprintf( out, "%s", fmt_str.c_str() ); - - #ifndef WIN32 - if(isatty(fileno(out))) fprintf( out, "\r%s", CONSOLE_DEFAULT ); - #endif - fprintf( out, "\n" ); - if( cfg.flush ) fflush( out ); - } - -} diff --git a/src/context.hpp b/src/context.hpp deleted file mode 100644 index fb1cfe475..000000000 --- a/src/context.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef _FC_CONTEXT_HPP_ -#define _FC_CONTEXT_HPP_ -#include -#include -#include -#include -#include - -namespace fc { - class thread; - class promise_base; - class task_base; - - namespace bc = boost::ctx; - - /** - * maintains information associated with each context such as - * where it is blocked, what time it should resume, priority, - * etc. - */ - struct context { - typedef fc::context* ptr; - - - context( void (*sf)(intptr_t), bc::stack_allocator& alloc, fc::thread* t ) - : caller_context(0), - stack_alloc(&alloc), - next_blocked(0), - next_blocked_mutex(0), - next(0), - ctx_thread(t), - canceled(false), - complete(false), - cur_task(0) - { - my_context.fc_stack.base = alloc.allocate( bc::default_stacksize() ); - my_context.fc_stack.limit = - static_cast( my_context.fc_stack.base) - bc::default_stacksize(); - make_fcontext( &my_context, sf ); - } - - context( fc::thread* t) - :caller_context(0), - stack_alloc(0), - next_blocked(0), - next_blocked_mutex(0), - next(0), - ctx_thread(t), - canceled(false), - complete(false), - cur_task(0) - {} - - ~context() { - if(stack_alloc) { - stack_alloc->deallocate( my_context.fc_stack.base, bc::default_stacksize() ); - // slog("deallocate stack" ); - } - } - - struct blocked_promise { - blocked_promise( promise_base* p=0, bool r=true ) - :prom(p),required(r){} - - promise_base* prom; - bool required; - }; - - /** - * @todo Have a list of promises so that we can wait for - * P1 or P2 and either will unblock instead of requiring both - * @param req - require this promise to 'unblock', otherwise try_unblock - * will allow it to be one of many that could 'unblock' - */ - void add_blocking_promise( promise_base* p, bool req = true ) { - for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { - if( i->prom == p ) { - i->required = req; - return; - } - } - blocking_prom.push_back( blocked_promise(p,req) ); - } - /** - * If all of the required promises and any optional promises then - * return true, else false. - * @todo check list - */ - bool try_unblock( promise_base* p ) { - if( blocking_prom.size() == 0 ) { - return true; - } - bool req = false; - for( uint32_t i = 0; i < blocking_prom.size(); ++i ) { - if( blocking_prom[i].prom == p ) { - blocking_prom[i].required = false; - return true; - } - req = req || blocking_prom[i].required; - } - return !req; - } - - void remove_blocking_promise( promise_base* p ) { - for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { - if( i->prom == p ) { - blocking_prom.erase(i); - return; - } - } - } - - void timeout_blocking_promises() { - for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { - i->prom->set_exception( fc::copy_exception( future_wait_timeout() ) ); - } - } - template - void except_blocking_promises( const Exception& e ) { - for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { - i->prom->set_exception( fc::copy_exception( e ) ); - } - } - void clear_blocking_promises() { - blocking_prom.clear(); - } - - bool is_complete()const { return complete; } - - - - bc::fcontext_t my_context; - fc::context* caller_context; - bc::stack_allocator* stack_alloc; - priority prio; - //promise_base* prom; - std::vector blocking_prom; - time_point resume_time; - // time_point ready_time; // time that this context was put on ready queue - fc::context* next_blocked; - fc::context* next_blocked_mutex; - fc::context* next; - fc::thread* ctx_thread; - bool canceled; - bool complete; - task_base* cur_task; - }; - -} // naemspace fc - -#endif // _FC_CONTEXT_HPP_ diff --git a/src/crypto/_digest_common.cpp b/src/crypto/_digest_common.cpp new file mode 100644 index 000000000..67a015bfb --- /dev/null +++ b/src/crypto/_digest_common.cpp @@ -0,0 +1,53 @@ +#include +#include "_digest_common.hpp" + +namespace fc { namespace detail { + static void shift_l( const uint8_t* in, uint8_t* out, std::size_t n, unsigned int i) { + if (i < n) { + memcpy( out, in + i, n-i ); + } else { + i = n; + } + memset( out + (n-i), 0, i ); + } + + void shift_l( const char* in, char* out, std::size_t n, unsigned int i) { + const uint8_t* in8 = (uint8_t*) in; + uint8_t* out8 = (uint8_t*) out; + + if (i >= 8) { + shift_l( in8, out8, n, i / 8 ); + i &= 7; + in8 = out8; + } + + std::size_t p; + for( p = 0; p < n-1; ++p ) + out8[p] = (in8[p] << i) | (in8[p+1]>>(8-i)); + out8[p] = in8[p] << i; + } + static void shift_r( const uint8_t* in, uint8_t* out, std::size_t n, unsigned int i) { + if (i < n) { + memcpy( out+i, in, n-i ); + } else { + i = n; + } + memset( out, 0, i ); + } + + void shift_r( const char* in, char* out, std::size_t n, unsigned int i) { + const uint8_t* in8 = (uint8_t*) in; + uint8_t* out8 = (uint8_t*) out; + + if (i >= 8) { + shift_r( in8, out8, n, i / 8 ); + i &= 7; + in8 = out8; + } + + std::size_t p; + for( p = n-1; p > 0; --p ) + out8[p] = (in8[p] >> i) | (in8[p-1]<<(8-i)); + out8[p] = in8[p] >> i; + } +}} diff --git a/src/crypto/_digest_common.hpp b/src/crypto/_digest_common.hpp new file mode 100644 index 000000000..8582309bf --- /dev/null +++ b/src/crypto/_digest_common.hpp @@ -0,0 +1,8 @@ +#pragma once + +/* Common stuff for cryptographic hashes + */ +namespace fc { namespace detail { + void shift_l( const char* in, char* out, std::size_t n, unsigned int i); + void shift_r( const char* in, char* out, std::size_t n, unsigned int i); +}} diff --git a/src/crypto/_elliptic_impl_priv.hpp b/src/crypto/_elliptic_impl_priv.hpp new file mode 100644 index 000000000..8c2a1abff --- /dev/null +++ b/src/crypto/_elliptic_impl_priv.hpp @@ -0,0 +1,25 @@ +#pragma once +#include + +/* private_key_impl based on libsecp256k1 + * used by mixed + secp256k1 + */ + +namespace fc { namespace ecc { namespace detail { + + +const secp256k1_context_t* _get_context(); +void _init_lib(); + +class private_key_impl +{ + public: + private_key_impl() BOOST_NOEXCEPT; + private_key_impl( const private_key_impl& cpy ) BOOST_NOEXCEPT; + + private_key_impl& operator=( const private_key_impl& pk ) BOOST_NOEXCEPT; + + private_key_secret _key; +}; + +}}} diff --git a/src/crypto/_elliptic_impl_pub.hpp b/src/crypto/_elliptic_impl_pub.hpp new file mode 100644 index 000000000..b982ba125 --- /dev/null +++ b/src/crypto/_elliptic_impl_pub.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +/* public_key_impl implementation based on openssl + * used by mixed + openssl + */ + +namespace fc { namespace ecc { namespace detail { + +void _init_lib(); + +class public_key_impl +{ + public: + public_key_impl() BOOST_NOEXCEPT; + public_key_impl( const public_key_impl& cpy ) BOOST_NOEXCEPT; + public_key_impl( public_key_impl&& cpy ) BOOST_NOEXCEPT; + ~public_key_impl() BOOST_NOEXCEPT; + + public_key_impl& operator=( const public_key_impl& pk ) BOOST_NOEXCEPT; + + public_key_impl& operator=( public_key_impl&& pk ) BOOST_NOEXCEPT; + + static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check); + + EC_KEY* _key = nullptr; + + private: + void free_key() BOOST_NOEXCEPT; +}; + +}}} diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp new file mode 100644 index 000000000..0704b7b83 --- /dev/null +++ b/src/crypto/aes.cpp @@ -0,0 +1,439 @@ +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#ifndef OPENSSL_THREADS +# error "OpenSSL must be configured to support threads" +#endif +#include + +#if defined(_MSC_VER) +# include +#endif + +namespace fc { + +struct aes_encoder::impl +{ + evp_cipher_ctx ctx; +}; + +aes_encoder::aes_encoder() +{ + static int init = init_openssl(); +} + +aes_encoder::~aes_encoder() +{ +} + +void aes_encoder::init( const fc::sha256& key, const fc::uint128& init_value ) +{ + my->ctx.obj = EVP_CIPHER_CTX_new(); + /* Create and initialise the context */ + if(!my->ctx) + { + FC_THROW_EXCEPTION( aes_exception, "error allocating evp cipher context", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Initialise the encryption operation. IMPORTANT - ensure you use a key + * and IV size appropriate for your cipher + * In this example we are using 256 bit AES (i.e. a 256 bit key). The + * IV size for *most* modes is the same as the block size. For AES this + * is 128 bits */ + if(1 != EVP_EncryptInit_ex(my->ctx, EVP_aes_256_cbc(), NULL, (unsigned char*)&key, (unsigned char*)&init_value)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption init", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + EVP_CIPHER_CTX_set_padding( my->ctx, 0 ); +} + +uint32_t aes_encoder::encode( const char* plaintxt, uint32_t plaintext_len, char* ciphertxt ) +{ + int ciphertext_len = 0; + /* Provide the message to be encrypted, and obtain the encrypted output. + * * EVP_EncryptUpdate can be called multiple times if necessary + * */ + if(1 != EVP_EncryptUpdate(my->ctx, (unsigned char*)ciphertxt, &ciphertext_len, (const unsigned char*)plaintxt, plaintext_len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption update", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + FC_ASSERT( ciphertext_len == plaintext_len, "", ("ciphertext_len",ciphertext_len)("plaintext_len",plaintext_len) ); + return ciphertext_len; +} +#if 0 +uint32_t aes_encoder::final_encode( char* ciphertxt ) +{ + int ciphertext_len = 0; + /* Finalise the encryption. Further ciphertext bytes may be written at + * * this stage. + * */ + if(1 != EVP_EncryptFinal_ex(my->ctx, (unsigned char*)ciphertxt, &ciphertext_len)) + { + FC_THROW_EXCEPTION( exception, "error during aes 256 cbc encryption final", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + return ciphertext_len; +} +#endif + + +struct aes_decoder::impl +{ + evp_cipher_ctx ctx; +}; + +aes_decoder::aes_decoder() + { + static int init = init_openssl(); + } + +void aes_decoder::init( const fc::sha256& key, const fc::uint128& init_value ) +{ + my->ctx.obj = EVP_CIPHER_CTX_new(); + /* Create and initialise the context */ + if(!my->ctx) + { + FC_THROW_EXCEPTION( aes_exception, "error allocating evp cipher context", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Initialise the encryption operation. IMPORTANT - ensure you use a key + * and IV size appropriate for your cipher + * In this example we are using 256 bit AES (i.e. a 256 bit key). The + * IV size for *most* modes is the same as the block size. For AES this + * is 128 bits */ + if(1 != EVP_DecryptInit_ex(my->ctx, EVP_aes_256_cbc(), NULL, (unsigned char*)&key, (unsigned char*)&init_value)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption init", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + EVP_CIPHER_CTX_set_padding( my->ctx, 0 ); +} +aes_decoder::~aes_decoder() +{ +} + +uint32_t aes_decoder::decode( const char* ciphertxt, uint32_t ciphertxt_len, char* plaintext ) +{ + int plaintext_len = 0; + /* Provide the message to be decrypted, and obtain the decrypted output. + * * EVP_DecryptUpdate can be called multiple times if necessary + * */ + if (1 != EVP_DecryptUpdate(my->ctx, (unsigned char*)plaintext, &plaintext_len, (const unsigned char*)ciphertxt, ciphertxt_len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decryption update", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + FC_ASSERT( ciphertxt_len == plaintext_len, "", ("ciphertxt_len",ciphertxt_len)("plaintext_len",plaintext_len) ); + return plaintext_len; +} +#if 0 +uint32_t aes_decoder::final_decode( char* plaintext ) +{ + return 0; + int ciphertext_len = 0; + /* Finalise the encryption. Further ciphertext bytes may be written at + * * this stage. + * */ + if(1 != EVP_DecryptFinal_ex(my->ctx, (unsigned char*)plaintext, &ciphertext_len)) + { + FC_THROW_EXCEPTION( exception, "error during aes 256 cbc encryption final", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + return ciphertext_len; +} +#endif + + + + + + + + + + + + +/** example method from wiki.opensslfoundation.com */ +unsigned aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, + unsigned char *iv, unsigned char *ciphertext) +{ + evp_cipher_ctx ctx( EVP_CIPHER_CTX_new() ); + + int len = 0; + unsigned ciphertext_len = 0; + + /* Create and initialise the context */ + if(!ctx) + { + FC_THROW_EXCEPTION( aes_exception, "error allocating evp cipher context", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Initialise the encryption operation. IMPORTANT - ensure you use a key + * and IV size appropriate for your cipher + * In this example we are using 256 bit AES (i.e. a 256 bit key). The + * IV size for *most* modes is the same as the block size. For AES this + * is 128 bits */ + if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption init", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Provide the message to be encrypted, and obtain the encrypted output. + * * EVP_EncryptUpdate can be called multiple times if necessary + * */ + if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption update", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + ciphertext_len = len; + + /* Finalise the encryption. Further ciphertext bytes may be written at + * * this stage. + * */ + if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption final", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + ciphertext_len += len; + + return ciphertext_len; +} + +unsigned aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, + unsigned char *iv, unsigned char *plaintext) +{ + evp_cipher_ctx ctx( EVP_CIPHER_CTX_new() ); + int len = 0; + unsigned plaintext_len = 0; + + /* Create and initialise the context */ + if(!ctx) + { + FC_THROW_EXCEPTION( aes_exception, "error allocating evp cipher context", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Initialise the decryption operation. IMPORTANT - ensure you use a key + * * and IV size appropriate for your cipher + * * In this example we are using 256 bit AES (i.e. a 256 bit key). The + * * IV size for *most* modes is the same as the block size. For AES this + * * is 128 bits */ + if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decrypt init", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Provide the message to be decrypted, and obtain the plaintext output. + * * EVP_DecryptUpdate can be called multiple times if necessary + * */ + if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decrypt update", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + plaintext_len = len; + + /* Finalise the decryption. Further plaintext bytes may be written at + * * this stage. + * */ + if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decrypt final", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + plaintext_len += len; + + return plaintext_len; +} + +unsigned aes_cfb_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, + unsigned char *iv, unsigned char *plaintext) +{ + evp_cipher_ctx ctx( EVP_CIPHER_CTX_new() ); + int len = 0; + unsigned plaintext_len = 0; + + /* Create and initialise the context */ + if(!ctx) + { + FC_THROW_EXCEPTION( aes_exception, "error allocating evp cipher context", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Initialise the decryption operation. IMPORTANT - ensure you use a key + * * and IV size appropriate for your cipher + * * In this example we are using 256 bit AES (i.e. a 256 bit key). The + * * IV size for *most* modes is the same as the block size. For AES this + * * is 128 bits */ + if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cfb128(), NULL, key, iv)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decrypt init", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + /* Provide the message to be decrypted, and obtain the plaintext output. + * * EVP_DecryptUpdate can be called multiple times if necessary + * */ + if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decrypt update", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + + plaintext_len = len; + + /* Finalise the decryption. Further plaintext bytes may be written at + * * this stage. + * */ + if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) + { + FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc decrypt final", + ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + plaintext_len += len; + + return plaintext_len; +} + +std::vector aes_encrypt( const fc::sha512& key, const std::vector& plain_text ) +{ + std::vector cipher_text(plain_text.size()+16); + auto cipher_len = aes_encrypt( (unsigned char*)plain_text.data(), (int)plain_text.size(), + (unsigned char*)&key, ((unsigned char*)&key)+32, + (unsigned char*)cipher_text.data() ); + FC_ASSERT( cipher_len <= cipher_text.size() ); + cipher_text.resize(cipher_len); + return cipher_text; + +} +std::vector aes_decrypt( const fc::sha512& key, const std::vector& cipher_text ) +{ + std::vector plain_text( cipher_text.size() ); + auto plain_len = aes_decrypt( (unsigned char*)cipher_text.data(), (int)cipher_text.size(), + (unsigned char*)&key, ((unsigned char*)&key)+32, + (unsigned char*)plain_text.data() ); + plain_text.resize(plain_len); + return plain_text; +} + + +/** encrypts plain_text and then includes a checksum that enables us to verify the integrety of + * the file / key prior to decryption. + */ +void aes_save( const fc::path& file, const fc::sha512& key, std::vector plain_text ) +{ try { + auto cipher = aes_encrypt( key, plain_text ); + fc::sha512::encoder check_enc; + fc::raw::pack( check_enc, key ); + fc::raw::pack( check_enc, cipher ); + auto check = check_enc.result(); + + fc::ofstream out(file); + fc::raw::pack( out, check ); + fc::raw::pack( out, cipher ); +} FC_RETHROW_EXCEPTIONS( warn, "", ("file",file) ) } + +/** + * recovers the plain_text saved via aes_save() + */ +std::vector aes_load( const fc::path& file, const fc::sha512& key ) +{ try { + FC_ASSERT( fc::exists( file ) ); + + fc::ifstream in( file, fc::ifstream::binary ); + fc::sha512 check; + std::vector cipher; + + fc::raw::unpack( in, check ); + fc::raw::unpack( in, cipher ); + + fc::sha512::encoder check_enc; + fc::raw::pack( check_enc, key ); + fc::raw::pack( check_enc, cipher ); + + FC_ASSERT( check_enc.result() == check ); + + return aes_decrypt( key, cipher ); +} FC_RETHROW_EXCEPTIONS( warn, "", ("file",file) ) } + +/* This stuff has to go somewhere, I guess this is as good a place as any... + OpenSSL isn't thread-safe unless you give it access to some mutexes, + so the CRYPTO_set_id_callback() function needs to be called before there's any + chance of OpenSSL being accessed from multiple threads. +*/ +struct openssl_thread_config +{ + static boost::mutex* openssl_mutexes; + static unsigned long get_thread_id(); + static void locking_callback(int mode, int type, const char *file, int line); + openssl_thread_config(); + ~openssl_thread_config(); +}; +openssl_thread_config openssl_thread_config_manager; + +boost::mutex* openssl_thread_config::openssl_mutexes = nullptr; + +unsigned long openssl_thread_config::get_thread_id() +{ +#ifdef _MSC_VER + return (unsigned long)::GetCurrentThreadId(); +#else + return (unsigned long)(&fc::thread::current()); // TODO: should expose boost thread id +#endif +} + +void openssl_thread_config::locking_callback(int mode, int type, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + openssl_mutexes[type].lock(); + else + openssl_mutexes[type].unlock(); +} + +// Warning: Things get complicated if third-party libraries also try to install their their own +// OpenSSL thread functions. Right now, we don't install our own handlers if another library has +// installed them before us which is a partial solution, but you'd really need to evaluate +// each library that does this to make sure they will play nice. +openssl_thread_config::openssl_thread_config() +{ + if (CRYPTO_get_id_callback() == NULL && + CRYPTO_get_locking_callback() == NULL) + { + openssl_mutexes = new boost::mutex[CRYPTO_num_locks()]; + CRYPTO_set_id_callback(&get_thread_id); + CRYPTO_set_locking_callback(&locking_callback); + } +} +openssl_thread_config::~openssl_thread_config() +{ + if (CRYPTO_get_id_callback() == &get_thread_id) + { + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + delete[] openssl_mutexes; + openssl_mutexes = nullptr; + } +} + +} // namespace fc diff --git a/src/crypto/base32.cpp b/src/crypto/base32.cpp new file mode 100644 index 000000000..bb5354d82 --- /dev/null +++ b/src/crypto/base32.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +namespace fc +{ + std::vector from_base32( const std::string& b32 ) + { + auto len = cyoBase32DecodeGetLength( b32.size() ); + std::vector v(len); + len = cyoBase32Decode( v.data(), b32.c_str(), b32.size() ); + v.resize( len ); + return v; + } + + std::string to_base32( const char* data, size_t len ) + { + auto s = cyoBase32EncodeGetLength(len); + std::vector b32; + b32.resize(s); + cyoBase32Encode( b32.data(), data, len ); + b32.resize( b32.size()-1); // strip the nullterm + return std::string(b32.begin(),b32.end()); + } + + std::string to_base32( const std::vector& vec ) + { + return to_base32( vec.data(), vec.size() ); + } +} diff --git a/src/crypto/base36.cpp b/src/crypto/base36.cpp new file mode 100644 index 000000000..d0685aea2 --- /dev/null +++ b/src/crypto/base36.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +namespace fc +{ + fc::string to_base36( const char* data, size_t len ) + { + if( len == 0 ) return fc::string(); + + const char* src = data; + int src_len = len; + std::unique_ptr buffer(new char[len+1]); + if (*data & 0x80) { + buffer[0] = 0; + memcpy( buffer.get() + 1, data, len ); + src = buffer.get(); + src_len++; + } + fc::bigint value( src, src_len ); + + auto base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; + std::vector out( static_cast(len * 1.6) + 2 ); + int pos = out.size() - 1; + out[pos] = '\0'; + fc::bigint _36(36); + do { + if( value ) { + --pos; + out[pos] = base36[(value % _36).to_int64()]; + } + } while (value /= _36); + while (len-- > 0 && *data++ == 0) { + out[--pos] = '0'; + } + return &out[pos]; //fc::string( &out[pos], out.size() - pos); + } + + fc::string to_base36( const std::vector& vec ) + { + return to_base36( (const char*)vec.data(), vec.size() ); + } + + std::vector from_base36( const fc::string& b36 ) + { + if ( b36.empty() ) { + std::vector empty; + return empty; + } + + fc::bigint value; + + fc::bigint pos = 0; + fc::bigint _36(36); + for( auto itr = b36.rbegin(); itr != b36.rend(); ++itr ) + { + if( *itr >= '0' && *itr <= '9' ) + value = value + _36.exp(pos) * fc::bigint(*itr - '0'); + else if( *itr >= 'a' && *itr <= 'z' ) + value = value + (_36.exp(pos) * fc::bigint(10+*itr - 'a')); + else if( *itr >= 'A' && *itr <= 'Z' ) + value = value + (_36.exp(pos) * fc::bigint(10+*itr - 'A')); + else + { + wlog("unknown '${char}'", ("char",fc::string(&*itr,1)) ); + } + ++pos; + } + + std::vector bytes = value; + int leading_zeros = 0, len = bytes.size(); + const char *in = b36.c_str(); + while (*in++ == '0') { leading_zeros++; } + char* first = bytes.data(); + while (len > 0 && *first == 0) { first++; len--; } + std::vector result; + result.resize(leading_zeros + len, 0); + memcpy( result.data() + leading_zeros, first, len ); + return result; + } +} diff --git a/src/base58.cpp b/src/crypto/base58.cpp similarity index 95% rename from src/base58.cpp rename to src/crypto/base58.cpp index 2b1905f53..e1d5d333e 100644 --- a/src/base58.cpp +++ b/src/crypto/base58.cpp @@ -21,10 +21,9 @@ #include #include -#include +#include #include -#include -#include +#include #include #include @@ -567,7 +566,7 @@ inline bool DecodeBase58(const char* psz, std::vector& vchRet) while (isspace(*p)) p++; if (*p != '\0') { - slog( "%s '%c'", pszBase58,*p ); + //slog( "%s '%c'", pszBase58,*p ); return false; } break; @@ -606,27 +605,33 @@ inline bool DecodeBase58(const std::string& str, std::vector& vch namespace fc { -fc::string to_base58( const char* d, size_t s ) { +std::string to_base58( const char* d, size_t s ) { return EncodeBase58( (const unsigned char*)d, (const unsigned char*)d+s ).c_str(); } -fc::vector from_base58( const fc::string& base58_str ) { +std::string to_base58( const std::vector& d ) +{ + if( d.size() ) + return to_base58( d.data(), d.size() ); + return std::string(); +} +std::vector from_base58( const std::string& base58_str ) { std::vector out; if( !DecodeBase58( base58_str.c_str(), out ) ) { - FC_THROW_REPORT( "Unable to decode base58 string ${base58_str}", fc::value().set("base58_str",base58_str) ); + FC_THROW_EXCEPTION( parse_error_exception, "Unable to decode base58 string ${base58_str}", ("base58_str",base58_str) ); } - return fc::vector((const char*)out.data(), ((const char*)out.data())+out.size() ); + return std::vector((const char*)out.data(), ((const char*)out.data())+out.size() ); } /** * @return the number of bytes decoded */ -size_t from_base58( const fc::string& base58_str, char* out_data, size_t out_data_len ) { +size_t from_base58( const std::string& base58_str, char* out_data, size_t out_data_len ) { //slog( "%s", base58_str.c_str() ); std::vector out; if( !DecodeBase58( base58_str.c_str(), out ) ) { - FC_THROW_REPORT( "Unable to decode base58 string ${base58_str}", fc::value().set("base58_str",base58_str) ); + FC_THROW_EXCEPTION( parse_error_exception, "Unable to decode base58 string ${base58_str}", ("base58_str",base58_str) ); } - + FC_ASSERT( out.size() <= out_data_len ); memcpy( out_data, out.data(), out.size() ); return out.size(); } diff --git a/src/base64.cpp b/src/crypto/base64.cpp similarity index 99% rename from src/base64.cpp rename to src/crypto/base64.cpp index 05e25e169..2752c5782 100644 --- a/src/base64.cpp +++ b/src/crypto/base64.cpp @@ -1,4 +1,4 @@ -#include +#include #include /* base64.cpp and base64.h diff --git a/src/crypto/bigint.cpp b/src/crypto/bigint.cpp new file mode 100644 index 000000000..5bc96d411 --- /dev/null +++ b/src/crypto/bigint.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include + +#include +#include "../byteswap.hpp" + +namespace fc { + bigint::bigint( const char* bige, uint32_t l ) { + n = BN_bin2bn( (const unsigned char*)bige, l, NULL ); + FC_ASSERT( n != nullptr ); + } + bigint::bigint( const std::vector& bige ) { + n = BN_bin2bn( (const unsigned char*)bige.data(), bige.size(), NULL ); + FC_ASSERT( n != nullptr ); + } + bigint::bigint( BIGNUM* in ) + { + n = BN_dup(in); + } + bigint::bigint( ) + :n(BN_new()) + { } + + BIGNUM* bigint::dup()const + { + return BN_dup( n ); + } + + bigint::bigint(uint64_t value) + { + uint64_t big_endian_value = bswap_64(value); + n = BN_bin2bn((const unsigned char*)&big_endian_value, sizeof(big_endian_value), NULL); + } + + bigint::bigint( const bigint& c ) { + n = BN_dup( c.n ); + } + + bigint::bigint( bigint&& b ) { + n = b.n; + b.n = 0; + } + + bigint::~bigint() { + if(n!=0) BN_free(n); + } + + bool bigint::is_negative()const { return BN_is_negative(n); } + + int64_t bigint::to_int64() const + { + FC_ASSERT(BN_num_bits(n) <= 63); + size_t size = BN_num_bytes(n); + uint64_t abs_value = 0; + BN_bn2bin(n, (unsigned char*)&abs_value + (sizeof(uint64_t) - size)); + return BN_is_negative(n) ? -(int64_t)bswap_64(abs_value) : bswap_64(abs_value); + } + + int64_t bigint::log2()const { return BN_num_bits(n); } + bool bigint::operator < ( const bigint& c )const { + return BN_cmp( n, c.n ) < 0; + } + bool bigint::operator > ( const bigint& c )const { + return BN_cmp( n, c.n ) > 0; + } + bool bigint::operator >= ( const bigint& c )const { + return BN_cmp( n, c.n ) >= 0; + } + bool bigint::operator == ( const bigint& c )const { + return BN_cmp( n, c.n ) == 0; + } + bool bigint::operator != ( const bigint& c )const { + return BN_cmp( n, c.n ) != 0; + } + bigint::operator bool()const + { + return !BN_is_zero( n ); + } + bigint bigint::operator++(int) + { + bigint tmp = *this; + *this = *this + bigint(1); + return tmp; + } + bigint& bigint::operator++() + { + return *this = *this + bigint(1); + } + bigint bigint::operator--(int) + { + bigint tmp = *this; + *this = *this - bigint(1); + return tmp; + } + bigint& bigint::operator--() + { + return *this = *this - bigint(1); + } + + bigint bigint::operator + ( const bigint& a )const { + bigint tmp(*this); + BN_add( tmp.n, n, a.n ); + return tmp; + } + bigint& bigint::operator += ( const bigint& a ){ + bigint tmp(*this); + BN_add( tmp.n, n, a.n ); + std::swap(*this,tmp); + return *this; + } + bigint& bigint::operator -= ( const bigint& a ){ + bigint tmp(*this); + BN_sub( tmp.n, n, a.n ); + std::swap(*this,tmp); + return *this; + } + + + bigint bigint::operator * ( const bigint& a )const { + BN_CTX* ctx = BN_CTX_new(); + bigint tmp(*this); + BN_mul( tmp.n, n, a.n, ctx ); + BN_CTX_free(ctx); + return tmp; + } + bigint bigint::operator / ( const bigint& a ) const { + BN_CTX* ctx = BN_CTX_new(); + bigint tmp;//(*this); + BN_div( tmp.n, NULL, n, a.n, ctx ); + BN_CTX_free(ctx); + return tmp; + } + bigint bigint::operator % ( const bigint& a ) const { + BN_CTX* ctx = BN_CTX_new(); + bigint tmp;//(*this); + BN_mod( tmp.n, n, a.n, ctx ); + BN_CTX_free(ctx); + return tmp; + } + + bigint bigint::operator /= ( const bigint& a ) { + BN_CTX* ctx = BN_CTX_new(); + bigint tmp;//*this); + BN_div( tmp.n, NULL, n, a.n, ctx ); + fc_swap( tmp.n, n ); + BN_CTX_free(ctx); + return tmp; + } + bigint bigint::operator *= ( const bigint& a ) { + auto tmp = *this * a; + *this = std::move(tmp); + return *this; + } + bigint& bigint::operator >>= ( uint32_t i ) + { + bigint tmp; + BN_rshift( tmp.n, n, i ); + std::swap(*this,tmp); + return *this; + } + + bigint& bigint::operator <<= ( uint32_t i ) + { + bigint tmp; + FC_ASSERT( tmp.n != nullptr ); + FC_ASSERT( n != nullptr ); + BN_lshift( tmp.n, n, i ); + std::swap(*this,tmp); + return *this; + } + + bigint bigint::operator - ( const bigint& a )const { + bigint tmp; + BN_sub( tmp.n, n, a.n ); + return tmp; + } + bigint bigint::exp( const bigint& a )const + { + BN_CTX* ctx = BN_CTX_new(); + bigint tmp; + BN_exp( tmp.n, n, a.n, ctx ); + BN_CTX_free(ctx); + return tmp; + } + + + bigint& bigint::operator = ( bigint&& a ) { + fc_swap( a.n, n ); + return *this; + } + bigint& bigint::operator = ( const bigint& a ) { + if( &a == this ) + return *this; + BN_copy( n, a.n ); + return *this; + } + bigint::operator fc::string()const { + return BN_bn2dec(n); + } + + bigint::operator std::vector()const { + std::vector to(BN_num_bytes(n)); + BN_bn2bin(n,(unsigned char*)to.data()); + return to; + } + + /** encodes the big int as base64 string, or a number */ + void to_variant( const bigint& bi, variant& v ) + { + std::vector ve = bi; + v = fc::variant(base64_encode((unsigned char*)ve.data(),ve.size())); + } + + /** decodes the big int as base64 string, or a number */ + void from_variant( const variant& v, bigint& bi ) + { + if( v.is_numeric() ) bi = bigint( static_cast(v.as_uint64()) ); + else + { + std::string b64 = v.as_string(); + std::string bin = base64_decode(b64); + bi = bigint(bin.c_str(), bin.size() ); + } + } + +} // namespace fc diff --git a/src/blowfish.cpp b/src/crypto/blowfish.cpp similarity index 94% rename from src/blowfish.cpp rename to src/crypto/blowfish.cpp index 74f58dc3d..5d3cbb4f3 100644 --- a/src/blowfish.cpp +++ b/src/crypto/blowfish.cpp @@ -6,9 +6,8 @@ // Cryptography", Second Edition. #include -#include -#include -#include +#include +#include namespace fc { //Extract low order byte @@ -310,13 +309,13 @@ void blowfish::start(unsigned char* ucKey, uint64_t keysize, const sblock& roCha m_oChain = roChain; if(keysize<1) - FC_THROW(invalid_key_length()); + FC_THROW_EXCEPTION( exception, "invalid key length" ); //Check the Key - the key length should be between 1 and 56 bytes if(keysize>56) keysize = 56; unsigned char aucLocalKey[56]; unsigned int i, j; - memcpy(aucLocalKey, ucKey, keysize); + memcpy(aucLocalKey, ucKey, static_cast(keysize)); //Reflexive Initialization of the Blowfish. //Generating the Subkeys from the Key flood P and S boxes with PI memcpy(m_auiP, scm_auiInitP, sizeof m_auiP); @@ -452,7 +451,7 @@ void blowfish::encrypt(unsigned char* buf, uint64_t n, int iMode) { //Check the buffer's length - should be > 0 and multiple of 8 if((n==0)||(n%8!=0)) - FC_THROW(invalid_buffer_length()); + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); sblock work; if(iMode == CBC) //CBC mode, using the Chain { @@ -495,7 +494,7 @@ void blowfish::decrypt(unsigned char* buf, uint64_t n, int iMode) { //Check the buffer's length - should be > 0 and multiple of 8 if((n==0)||(n%8!=0)) - FC_THROW(invalid_buffer_length()); + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); sblock work; if(iMode == CBC) //CBC mode, using the Chain { @@ -540,7 +539,7 @@ void blowfish::encrypt(const unsigned char* in, unsigned char* out, uint64_t n, { //Check the buffer's length - should be > 0 and multiple of 8 if((n==0)||(n%8!=0)) - FC_THROW(invalid_buffer_length()); + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); sblock work; if(iMode == CBC) //CBC mode, using the Chain { @@ -583,7 +582,7 @@ void blowfish::decrypt(const unsigned char* in, unsigned char* out, uint64_t n, { //Check the buffer's length - should be > 0 and multiple of 8 if((n==0)||(n%8!=0)) - FC_THROW(invalid_buffer_length()); + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); sblock work; if(iMode == CBC) //CBC mode, using the Chain { diff --git a/src/crypto/city.cpp b/src/crypto/city.cpp new file mode 100644 index 000000000..18367fbcf --- /dev/null +++ b/src/crypto/city.cpp @@ -0,0 +1,683 @@ +// Copyright (c) 2011 Google, Inc. +// +// 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. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// This file provides CityHash64() and related functions. +// +// It's probably possible to create even faster hash functions by +// writing a program that systematically explores some of the space of +// possible hash functions, by using SIMD instructions, or by +// compromising on hash quality. + +//#include "config.h" +//#include + +#include +#include // for memcpy and memset +#include +#include +#include + +#if defined(__SSE4_2__) && defined(__x86_64__) +#include +#else +uint64_t _mm_crc32_u64(uint64_t a, uint64_t b ); +#endif + +namespace fc { + +using namespace std; + + +inline uint64_t Uint128Low64(const uint128& x) { return x.low_bits(); } +inline uint64_t Uint128High64(const uint128& x) { return x.high_bits(); } + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +inline uint64_t Hash128to64(const uint128& x) { + // Murmur-inspired hashing. + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; + a ^= (a >> 47); + uint64_t b = (Uint128High64(x) ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +static uint64_t UNALIGNED_LOAD64(const char *p) { + uint64_t result; + memcpy(&result, p, sizeof(result)); + return result; +} + +static uint32_t UNALIGNED_LOAD32(const char *p) { + uint32_t result; + memcpy(&result, p, sizeof(result)); + return result; +} + +#ifdef _MSC_VER + +#include +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) + +// Mac OS X / Darwin features +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(__sun) || defined(sun) + +#include +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#elif defined(__FreeBSD__) + +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(__OpenBSD__) + +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) + +#elif defined(__NetBSD__) + +#include +#include +#if defined(__BSWAP_RENAME) && !defined(__bswap_32) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#endif + +#else + +#include + +#endif + +#ifdef WORDS_BIGENDIAN +#define uint32_in_expected_order(x) (bswap_32(x)) +#define uint64_in_expected_order(x) (bswap_64(x)) +#else +#define uint32_in_expected_order(x) (x) +#define uint64_in_expected_order(x) (x) +#endif + +#if !defined(LIKELY) +#if HAVE_BUILTIN_EXPECT +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#else +#define LIKELY(x) (x) +#endif +#endif + +static uint64_t Fetch64(const char *p) { + return uint64_in_expected_order(UNALIGNED_LOAD64(p)); +} + +static uint32_t Fetch32(const char *p) { + return uint32_in_expected_order(UNALIGNED_LOAD32(p)); +} + +// Some primes between 2^63 and 2^64 for various uses. +static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; +static const uint64_t k1 = 0xb492b66fbe98f273ULL; +static const uint64_t k2 = 0x9ae16a3b2f90404fULL; + +// Magic numbers for 32-bit hashing. Copied from Murmur3. +static const uint32_t c1 = 0xcc9e2d51; +static const uint32_t c2 = 0x1b873593; + +// A 32-bit to 32-bit integer hash copied from Murmur3. +static uint32_t fmix(uint32_t h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + +static uint32_t Rotate32(uint32_t val, int shift) { + // Avoid shifting by 32: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); +} + +#undef PERMUTE3 +#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0) + +static uint32_t Mur(uint32_t a, uint32_t h) { + // Helper from Murmur3 for combining two 32-bit values. + a *= c1; + a = Rotate32(a, 17); + a *= c2; + h ^= a; + h = Rotate32(h, 19); + return h * 5 + 0xe6546b64; +} + +static uint32_t Hash32Len13to24(const char *s, size_t len) { + uint32_t a = Fetch32(s - 4 + (len >> 1)); + uint32_t b = Fetch32(s + 4); + uint32_t c = Fetch32(s + len - 8); + uint32_t d = Fetch32(s + (len >> 1)); + uint32_t e = Fetch32(s); + uint32_t f = Fetch32(s + len - 4); + uint32_t h = len; + + return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); +} + +static uint32_t Hash32Len0to4(const char *s, size_t len) { + uint32_t b = 0; + uint32_t c = 9; + for (size_t i = 0; i < len; i++) { + signed char v = s[i]; + b = b * c1 + v; + c ^= b; + } + return fmix(Mur(b, Mur(len, c))); +} + +static uint32_t Hash32Len5to12(const char *s, size_t len) { + uint32_t a = len, b = len * 5, c = 9, d = b; + a += Fetch32(s); + b += Fetch32(s + len - 4); + c += Fetch32(s + ((len >> 1) & 4)); + return fmix(Mur(c, Mur(b, Mur(a, d)))); +} + +uint32_t city_hash32(const char *s, size_t len) { + if (len <= 24) { + return len <= 12 ? + (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : + Hash32Len13to24(s, len); + } + + // len > 24 + uint32_t h = len, g = c1 * len, f = g; + uint32_t a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; + uint32_t a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; + uint32_t a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; + uint32_t a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; + uint32_t a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; + h ^= a0; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + h ^= a2; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a1; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + g ^= a3; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + f += a4; + f = Rotate32(f, 19); + f = f * 5 + 0xe6546b64; + size_t iters = (len - 1) / 20; + do { + uint32_t a0 = Rotate32(Fetch32(s) * c1, 17) * c2; + uint32_t a1 = Fetch32(s + 4); + uint32_t a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2; + uint32_t a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2; + uint32_t a4 = Fetch32(s + 16); + h ^= a0; + h = Rotate32(h, 18); + h = h * 5 + 0xe6546b64; + f += a1; + f = Rotate32(f, 19); + f = f * c1; + g += a2; + g = Rotate32(g, 18); + g = g * 5 + 0xe6546b64; + h ^= a3 + a1; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a4; + g = bswap_32(g) * 5; + h += a4 * 5; + h = bswap_32(h); + f += a0; + PERMUTE3(f, h, g); + s += 20; + } while (--iters != 0); + g = Rotate32(g, 11) * c1; + g = Rotate32(g, 17) * c1; + f = Rotate32(f, 11) * c1; + f = Rotate32(f, 17) * c1; + h = Rotate32(h + g, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + h = Rotate32(h + f, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + return h; +} + +// Bitwise right rotate. Normally this will compile to a single +// instruction, especially if the shift is a manifest constant. +static uint64_t Rotate(uint64_t val, int shift) { + // Avoid shifting by 64: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); +} + +static uint64_t ShiftMix(uint64_t val) { + return val ^ (val >> 47); +} + +static uint64_t HashLen16(uint64_t u, uint64_t v) { + return Hash128to64(uint128(u, v)); +} + +static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) { + // Murmur-inspired hashing. + uint64_t a = (u ^ v) * mul; + a ^= (a >> 47); + uint64_t b = (v ^ a) * mul; + b ^= (b >> 47); + b *= mul; + return b; +} + +static uint64_t HashLen0to16(const char *s, size_t len) { + if (len >= 8) { + uint64_t mul = k2 + len * 2; + uint64_t a = Fetch64(s) + k2; + uint64_t b = Fetch64(s + len - 8); + uint64_t c = Rotate(b, 37) * mul + a; + uint64_t d = (Rotate(a, 25) + b) * mul; + return HashLen16(c, d, mul); + } + if (len >= 4) { + uint64_t mul = k2 + len * 2; + uint64_t a = Fetch32(s); + return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); + } + if (len > 0) { + uint8_t a = s[0]; + uint8_t b = s[len >> 1]; + uint8_t c = s[len - 1]; + uint32_t y = static_cast(a) + (static_cast(b) << 8); + uint32_t z = len + (static_cast(c) << 2); + return ShiftMix(y * k2 ^ z * k0) * k2; + } + return k2; +} + +// This probably works well for 16-byte strings as well, but it may be overkill +// in that case. +static uint64_t HashLen17to32(const char *s, size_t len) { + uint64_t mul = k2 + len * 2; + uint64_t a = Fetch64(s) * k1; + uint64_t b = Fetch64(s + 8); + uint64_t c = Fetch64(s + len - 8) * mul; + uint64_t d = Fetch64(s + len - 16) * k2; + return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, + a + Rotate(b + k2, 18) + c, mul); +} + +// Return a 16-byte hash for 48 bytes. Quick and dirty. +// Callers do best to use "random-looking" values for a and b. +static pair WeakHashLen32WithSeeds( + uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) { + a += w; + b = Rotate(b + a + z, 21); + uint64_t c = a; + a += x; + a += y; + b += Rotate(a, 44); + return make_pair(a + z, b + c); +} + +// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. +static pair WeakHashLen32WithSeeds( + const char* s, uint64_t a, uint64_t b) { + return WeakHashLen32WithSeeds(Fetch64(s), + Fetch64(s + 8), + Fetch64(s + 16), + Fetch64(s + 24), + a, + b); +} + +// Return an 8-byte hash for 33 to 64 bytes. +static uint64_t HashLen33to64(const char *s, size_t len) { + uint64_t mul = k2 + len * 2; + uint64_t a = Fetch64(s) * k2; + uint64_t b = Fetch64(s + 8); + uint64_t c = Fetch64(s + len - 24); + uint64_t d = Fetch64(s + len - 32); + uint64_t e = Fetch64(s + 16) * k2; + uint64_t f = Fetch64(s + 24) * 9; + uint64_t g = Fetch64(s + len - 8); + uint64_t h = Fetch64(s + len - 16) * mul; + uint64_t u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; + uint64_t v = ((a + g) ^ d) + f + 1; + uint64_t w = bswap_64((u + v) * mul) + h; + uint64_t x = Rotate(e + f, 42) + c; + uint64_t y = (bswap_64((v + w) * mul) + g) * mul; + uint64_t z = e + f + c; + a = bswap_64((x + z) * mul + y) + b; + b = ShiftMix((z + a) * mul + d + h) * mul; + return b + x; +} + +uint64_t city_hash64(const char *s, size_t len) { + if (len <= 32) { + if (len <= 16) { + return HashLen0to16(s, len); + } else { + return HashLen17to32(s, len); + } + } else if (len <= 64) { + return HashLen33to64(s, len); + } + + // For strings over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + uint64_t x = Fetch64(s + len - 40); + uint64_t y = Fetch64(s + len - 16) + Fetch64(s + len - 56); + uint64_t z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); + pair v = WeakHashLen32WithSeeds(s + len - 64, len, z); + pair w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); + x = x * k1 + Fetch64(s); + + // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. + len = (len - 1) & ~static_cast(63); + do { + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + len -= 64; + } while (len != 0); + return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, + HashLen16(v.second, w.second) + x); +} + +uint64_t CityHash64WithSeeds(const char *s, size_t len, + uint64_t seed0, uint64_t seed1) { + return HashLen16(city_hash64(s, len) - seed0, seed1); +} + +uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed) { + return CityHash64WithSeeds(s, len, k2, seed); +} + +// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings +// of any length representable in signed long. Based on City and Murmur. +static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { + uint64_t a = Uint128Low64(seed); + uint64_t b = Uint128High64(seed); + uint64_t c = 0; + uint64_t d = 0; + signed long l = len - 16; + if (l <= 0) { // len <= 16 + a = ShiftMix(a * k1) * k1; + c = b * k1 + HashLen0to16(s, len); + d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); + } else { // len > 16 + c = HashLen16(Fetch64(s + len - 8) + k1, a); + d = HashLen16(b + len, c + Fetch64(s + len - 16)); + a += d; + do { + a ^= ShiftMix(Fetch64(s) * k1) * k1; + a *= k1; + b ^= a; + c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; + c *= k1; + d ^= c; + s += 16; + l -= 16; + } while (l > 0); + } + a = HashLen16(a, c); + b = HashLen16(d, b); + return uint128(a ^ b, HashLen16(b, a)); +} + +uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) { + if (len < 128) { + return CityMurmur(s, len, seed); + } + + // We expect len >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + pair v, w; + uint64_t x = Uint128Low64(seed); + uint64_t y = Uint128High64(seed); + uint64_t z = len * k1; + v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); + v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); + w.first = Rotate(y + z, 35) * k1 + x; + w.second = Rotate(x + Fetch64(s + 88), 53) * k1; + + // This is the same inner loop as CityHash64(), manually unrolled. + do { + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; + x ^= w.second; + y += v.first + Fetch64(s + 40); + z = Rotate(z + w.first, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); + std::swap(z, x); + s += 64; + len -= 128; + } while (LIKELY(len >= 128)); + x += Rotate(v.first + z, 49) * k0; + y = y * k0 + Rotate(w.second, 37); + z = z * k0 + Rotate(w.first, 27); + w.first *= 9; + v.first *= k0; + // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. + for (size_t tail_done = 0; tail_done < len; ) { + tail_done += 32; + y = Rotate(x + y, 42) * k0 + v.second; + w.first += Fetch64(s + len - tail_done + 16); + x = x * k0 + w.first; + z += w.second + Fetch64(s + len - tail_done); + w.second += v.first; + v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); + v.first *= k0; + } + // At this point our 56 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 56-byte-to-8-byte hashes to get a 16-byte final result. + x = HashLen16(x, v.first); + y = HashLen16(y + z, w.first); + return uint128(HashLen16(x + v.second, w.second) + y, + HashLen16(x + w.second, y + v.second)); +} + +uint128 city_hash128(const char *s, size_t len) { + return len >= 16 ? + CityHash128WithSeed(s + 16, len - 16, + uint128(Fetch64(s), Fetch64(s + 8) + k0)) : + CityHash128WithSeed(s, len, uint128(k0, k1)); +} + +//#ifdef __SSE4_2__ +//#include +//#include + +// Requires len >= 240. +static void CityHashCrc256Long(const char *s, size_t len, + uint32_t seed, uint64_t *result) { + uint64_t a = Fetch64(s + 56) + k0; + uint64_t b = Fetch64(s + 96) + k0; + uint64_t c = result[0] = HashLen16(b, len); + uint64_t d = result[1] = Fetch64(s + 120) * k0 + len; + uint64_t e = Fetch64(s + 184) + seed; + uint64_t f = 0; + uint64_t g = 0; + uint64_t h = c + d; + uint64_t x = seed; + uint64_t y = 0; + uint64_t z = 0; + + // 240 bytes of input per iter. + size_t iters = len / 240; + len -= iters * 240; + do { +#undef CHUNK +#define CHUNK(r) \ + PERMUTE3(x, z, y); \ + b += Fetch64(s); \ + c += Fetch64(s + 8); \ + d += Fetch64(s + 16); \ + e += Fetch64(s + 24); \ + f += Fetch64(s + 32); \ + a += b; \ + h += f; \ + b += c; \ + f += d; \ + g += e; \ + e += z; \ + g += x; \ + z = _mm_crc32_u64(z, b + g); \ + y = _mm_crc32_u64(y, e + h); \ + x = _mm_crc32_u64(x, f + a); \ + e = Rotate(e, r); \ + c += e; \ + s += 40 + + CHUNK(0); PERMUTE3(a, h, c); + CHUNK(33); PERMUTE3(a, h, f); + CHUNK(0); PERMUTE3(b, h, f); + CHUNK(42); PERMUTE3(b, h, d); + CHUNK(0); PERMUTE3(b, h, e); + CHUNK(33); PERMUTE3(a, h, e); + } while (--iters > 0); + + while (len >= 40) { + CHUNK(29); + e ^= Rotate(a, 20); + h += Rotate(b, 30); + g ^= Rotate(c, 40); + f += Rotate(d, 34); + PERMUTE3(c, h, g); + len -= 40; + } + if (len > 0) { + s = s + len - 40; + CHUNK(33); + e ^= Rotate(a, 43); + h += Rotate(b, 42); + g ^= Rotate(c, 41); + f += Rotate(d, 40); + } + result[0] ^= h; + result[1] ^= g; + g += h; + a = HashLen16(a, g + z); + x += y << 32; + b += x; + c = HashLen16(c, z) + h; + d = HashLen16(d, e + result[0]); + g += e; + h += HashLen16(x, f); + e = HashLen16(a, d) + g; + z = HashLen16(b, c) + a; + y = HashLen16(g, h) + c; + result[0] = e + z + y + x; + a = ShiftMix((a + y) * k0) * k0 + b; + result[1] += a + result[0]; + a = ShiftMix(a * k0) * k0 + c; + result[2] = a + result[1]; + a = ShiftMix((a + e) * k0) * k0; + result[3] = a + result[2]; +} + +// Requires len < 240. +static void CityHashCrc256Short(const char *s, size_t len, uint64_t *result) { + char buf[240]; + memcpy(buf, s, len); + memset(buf + len, 0, 240 - len); + CityHashCrc256Long(buf, 240, ~static_cast(len), result); +} + +void CityHashCrc256(const char *s, size_t len, uint64_t *result) { + if (LIKELY(len >= 240)) { + CityHashCrc256Long(s, len, 0, result); + } else { + CityHashCrc256Short(s, len, result); + } +} + +array city_hash_crc_256(const char *s, size_t len) +{ + array buf; + CityHashCrc256( s, len, (uint64_t*)&buf ); + return buf; +} + +uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) { + if (len <= 900) { + return CityHash128WithSeed(s, len, seed); + } else { + uint64_t result[4]; + CityHashCrc256(s, len, result); + uint64_t u = Uint128High64(seed) + result[0]; + uint64_t v = Uint128Low64(seed) + result[1]; + return uint128(HashLen16(u, v + result[2]), + HashLen16(Rotate(v, 32), u * k0 + result[3])); + } +} + +uint128 city_hash_crc_128(const char *s, size_t len) { + if (len <= 900) { + return city_hash128(s, len); + } else { + uint64_t result[4]; + CityHashCrc256(s, len, result); + return uint128(result[2], result[3]); + } +} + +} // end namespace fc + +//#endif diff --git a/src/crypto/crc.cpp b/src/crypto/crc.cpp new file mode 100644 index 000000000..3b9545503 --- /dev/null +++ b/src/crypto/crc.cpp @@ -0,0 +1,627 @@ +#include +#include +//#include +/* Tables generated with code like the following: + +#define CRCPOLY 0x82f63b78 // reversed 0x1EDC6F41 +#define CRCINIT 0xFFFFFFFF + +void init() { + for (uint32_t i = 0; i <= 0xFF; i++) { + uint32_t x = i; + for (uint32_t j = 0; j < 8; j++) + x = (x>>1) ^ (CRCPOLY & (-(int32_t)(x & 1))); + g_crc_slicing[0][i] = x; + } + + for (uint32_t i = 0; i <= 0xFF; i++) { + uint32_t c = g_crc_slicing[0][i]; + for (uint32_t j = 1; j < 8; j++) { + c = g_crc_slicing[0][c & 0xFF] ^ (c >> 8); + g_crc_slicing[j][i] = c; + } + } +} +*/ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o32[256] = +{ + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 +}; + +/* + * end of the CRC lookup table crc_tableil8_o32 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o40[256] = +{ + 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB, 0x69CF5132, 0x7A6DC945, + 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21, 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD, + 0x3FC5F181, 0x2C6769F6, 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4, + 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92, 0xCB1E630B, 0xD8BCFB7C, + 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B, 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, + 0xE29F20BA, 0xF13DB8CD, 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF, + 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28, 0x298143B1, 0x3A23DBC6, + 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2, 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, + 0xFF17C604, 0xECB55E73, 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41, + 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17, 0x0BCC548E, 0x186ECCF9, + 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C, 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0, + 0x5DC6F43D, 0x4E646C4A, 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78, + 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD, 0xE9537434, 0xFAF1EC43, + 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27, 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, + 0xBF59D487, 0xACFB4CF0, 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2, + 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94, 0x4B82460D, 0x5820DE7A, + 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260, 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, + 0x66D73941, 0x7575A136, 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004, + 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3, 0xADC95A4A, 0xBE6BC23D, + 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059, 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185, + 0x844819FB, 0x97EA818C, 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE, + 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8, 0x70938B71, 0x63311306, + 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3, 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, + 0x26992BC2, 0x353BB3B5, 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287, + 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556, 0x6D1B6DCF, 0x7EB9F5B8, + 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC, 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, + 0x3B11CD7C, 0x28B3550B, 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439, + 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F, 0xCFCA5FF6, 0xDC68C781, + 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766, 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA, + 0xE64B1C47, 0xF5E98430, 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502, + 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5, 0x2D557F4C, 0x3EF7E73B, + 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F, 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483 +}; + +/* + * end of the CRC lookup table crc_tableil8_o40 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o48[256] = +{ + 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664, 0xD1B1F617, 0x74F06469, + 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6, 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC, + 0x70A27D8A, 0xD5E3EFF4, 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3, + 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B, 0x9942B558, 0x3C032726, + 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67, 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, + 0xD915C5D1, 0x7C5457AF, 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8, + 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA, 0x40577089, 0xE516E2F7, + 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828, 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, + 0xC76580D9, 0x622412A7, 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0, + 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878, 0x2E85480B, 0x8BC4DA75, + 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20, 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A, + 0x8F96C396, 0x2AD751E8, 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF, + 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9, 0xF7908DDA, 0x52D11FA4, + 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B, 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, + 0x56830647, 0xF3C29439, 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E, + 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6, 0xBF63CE95, 0x1A225CEB, + 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730, 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, + 0xB3764986, 0x1637DBF8, 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF, + 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD, 0x2A34FCDE, 0x8F756EA0, + 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F, 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065, + 0x6A638C57, 0xCF221E29, 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E, + 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6, 0x83834485, 0x26C2D6FB, + 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE, 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, + 0x2290CF18, 0x87D15D66, 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71, + 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE, 0x9DF3018D, 0x38B293F3, + 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C, 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, + 0x3CE08A10, 0x99A1186E, 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79, + 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1, 0xD50042C2, 0x7041D0BC, + 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD, 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7, + 0x9557324B, 0x3016A035, 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622, + 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760, 0x0C158713, 0xA954156D, + 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2, 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8 +}; + +/* + * end of the CRC lookup table crc_tableil8_o48 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o56[256] = +{ + 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B, 0xC4451272, 0x1900B8CA, + 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF, 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C, + 0xE964B13D, 0x34211B85, 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7, + 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990, 0xDB65C0A9, 0x06206A11, + 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2, 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, + 0x2161776D, 0xFC24DDD5, 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7, + 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD, 0xFA04B7C4, 0x27411D7C, + 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69, 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, + 0xABA65FE7, 0x76E3F55F, 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D, + 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A, 0x99A72E73, 0x44E284CB, + 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3, 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610, + 0xB4868D3C, 0x69C32784, 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6, + 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027, 0xB8C6591E, 0x6583F3A6, + 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3, 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, + 0x95E7FA51, 0x48A250E9, 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B, + 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC, 0xA7E68BC5, 0x7AA3217D, + 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006, 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, + 0xA4E4AAD9, 0x79A10061, 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213, + 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349, 0x7F816A70, 0xA2C4C0C8, + 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD, 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E, + 0x8585DDB4, 0x58C0770C, 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E, + 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519, 0xB784AC20, 0x6AC10698, + 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0, 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, + 0x9AA50F6F, 0x47E0A5D7, 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5, + 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93, 0x3D4384AA, 0xE0062E12, + 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07, 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, + 0x106227E5, 0xCD278D5D, 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F, + 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48, 0x22635671, 0xFF26FCC9, + 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A, 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99, + 0xD867E1B5, 0x05224B0D, 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F, + 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825, 0x0302211C, 0xDE478BA4, + 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1, 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842 +}; + +/* + * end of the CRC lookup table crc_tableil8_o56 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o64[256] = +{ + 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C, 0x906761E8, 0xA8760E44, + 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65, 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5, + 0x8F2261D3, 0xB7330E7F, 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97, + 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E, 0xDA220BAA, 0xE2336406, + 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3, 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, + 0xDECFBEC6, 0xE6DED16A, 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082, + 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598, 0x04EDB56C, 0x3CFCDAC0, + 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1, 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, + 0x37516AAE, 0x0F400502, 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA, + 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023, 0x625100D7, 0x5A406F7B, + 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89, 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539, + 0x7D1400EC, 0x45056F40, 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8, + 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5, 0xBC9EBE11, 0x848FD1BD, + 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C, 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, + 0xA3DBBE2A, 0x9BCAD186, 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E, + 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7, 0xF6DBD453, 0xCECABBFF, + 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8, 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, + 0xABC5DECD, 0x93D4B161, 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089, + 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593, 0x71E7D567, 0x49F6BACB, + 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA, 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A, + 0x750A600B, 0x4D1B0FA7, 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F, + 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86, 0x200A0A72, 0x181B65DE, + 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C, 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, + 0x3F4F0A49, 0x075E65E5, 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D, + 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE, 0xC994DE1A, 0xF185B1B6, + 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497, 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, + 0xD6D1DE21, 0xEEC0B18D, 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065, + 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC, 0x83D1B458, 0xBBC0DBF4, + 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51, 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1, + 0x873C0134, 0xBF2D6E98, 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70, + 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A, 0x5D1E0A9E, 0x650F6532, + 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013, 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3 +}; + +/* + * end of the CRC lookup table crc_tableil8_o64 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o72[256] = +{ + 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E, 0x697997B4, 0x8649FCAD, + 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5, 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2, + 0xC00C303E, 0x2F3C5B27, 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93, + 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F, 0xC973BF95, 0x2643D48C, + 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57, 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, + 0xE5F20E92, 0x0AC2658B, 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F, + 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD, 0x2C81B107, 0xC3B1DA1E, + 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576, 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, + 0x0E045BEB, 0xE13430F2, 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746, + 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A, 0x077BD440, 0xE84BBF59, + 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F, 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778, + 0xAE0E73CA, 0x413E18D3, 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67, + 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108, 0xE289DAD2, 0x0DB9B1CB, + 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3, 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, + 0x4BFC7D58, 0xA4CC1641, 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5, + 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929, 0x4283F2F3, 0xADB399EA, + 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C, 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, + 0x7C0EAFC9, 0x933EC4D0, 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364, + 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86, 0xB57D105C, 0x5A4D7B45, + 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D, 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A, + 0x99FCA15B, 0x76CCCA42, 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6, + 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A, 0x90832EF0, 0x7FB345E9, + 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF, 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, + 0x39F6897A, 0xD6C6E263, 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7, + 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053, 0x7B757B89, 0x94451090, + 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8, 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, + 0xD200DC03, 0x3D30B71A, 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE, + 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872, 0xDB7F53A8, 0x344F38B1, + 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A, 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D, + 0xF7FEE2AF, 0x18CE89B6, 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02, + 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0, 0x3E8D5D3A, 0xD1BD3623, + 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B, 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C +}; + +/* + * end of the CRC lookup table crc_tableil8_o72 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o80[256] = +{ + 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919, 0x75E69C41, 0x1DE5B089, + 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B, 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA, + 0x9C5BFAA6, 0xF458D66E, 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F, + 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC, 0xA7909BB4, 0xCF93B77C, + 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5, 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, + 0x73767EEE, 0x1B755226, 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67, + 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002, 0xD4E6E55A, 0xBCE5C992, + 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110, 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, + 0x7AB7077A, 0x12B42BB2, 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3, + 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330, 0x417C6668, 0x297F4AA0, + 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884, 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55, + 0xA8C1008F, 0xC0C22C47, 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006, + 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE, 0x320A1886, 0x5A09344E, + 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC, 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, + 0xDBB77E61, 0xB3B452A9, 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8, + 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B, 0xE07C1F73, 0x887F33BB, + 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC, 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, + 0xBB43F3A7, 0xD340DF6F, 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E, + 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B, 0x1CD36813, 0x74D044DB, + 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59, 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988, + 0xC8358D49, 0xA036A181, 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0, + 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903, 0xF3FEEC5B, 0x9BFDC093, + 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7, 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, + 0x1A438ABC, 0x7240A674, 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35, + 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097, 0xFA3F95CF, 0x923CB907, + 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185, 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, + 0x1382F328, 0x7B81DFE0, 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1, + 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762, 0x2849923A, 0x404ABEF2, + 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B, 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA, + 0xFCAF7760, 0x94AC5BA8, 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9, + 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C, 0x5B3FECD4, 0x333CC01C, + 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E, 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F +}; + +/* + * end of the CRC lookup table crc_tableil8_o80 + */ + + + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +const uint32_t crc_tableil8_o88[256] = +{ + 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A, 0xB3657823, 0xFA590504, + 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3, 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE, + 0x847609B4, 0xCD4A7493, 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0, + 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224, 0x7528754D, 0x3C14086A, + 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0, 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, + 0x4F3B6143, 0x06071C64, 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447, + 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367, 0x3A13140E, 0x732F6929, + 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E, 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, + 0x1A00CB32, 0x533CB615, 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36, + 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2, 0xEB5EB7CB, 0xA262CAEC, + 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF, 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782, + 0xDC4DC65C, 0x9571BB7B, 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358, + 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1, 0xA465D688, 0xED59ABAF, + 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18, 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, + 0x9376A71F, 0xDA4ADA38, 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B, + 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F, 0x6228DBE6, 0x2B14A6C1, + 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D, 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, + 0x763A92BE, 0x3F06EF99, 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA, + 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A, 0x0312E7F3, 0x4A2E9AD4, + 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63, 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E, + 0x3901F3FD, 0x703D8EDA, 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9, + 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D, 0xC85F8F04, 0x8163F223, + 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20, 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, + 0xFF4CFE93, 0xB67083B4, 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97, + 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C, 0x9D642575, 0xD4585852, + 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5, 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, + 0xAA7754E2, 0xE34B29C5, 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6, + 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72, 0x5B29281B, 0x1215553C, + 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6, 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB, + 0x613A3C15, 0x28064132, 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911, + 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31, 0x14124958, 0x5D2E347F, + 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8, 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5 +}; + +/* + * end of the CRC lookup table crc_tableil8_o88 + */ + + + + +uint32_t crc32cSlicingBy8(uint32_t crc, const void* data, size_t length) { + const char* p_buf = (const char*) data; + + // Handle leading misaligned bytes + size_t initial_bytes = (sizeof(int32_t) - (intptr_t)p_buf) & (sizeof(int32_t) - 1); + if (length < initial_bytes) initial_bytes = length; + for (size_t li = 0; li < initial_bytes; li++) { + crc = crc_tableil8_o32[(crc ^ *p_buf++) & 0x000000FF] ^ (crc >> 8); + } + + length -= initial_bytes; + size_t running_length = length & ~(sizeof(uint64_t) - 1); + size_t end_bytes = length - running_length; + + for (size_t li = 0; li < running_length/8; li++) { + crc ^= *(uint32_t*) p_buf; + p_buf += 4; + uint32_t term1 = crc_tableil8_o88[crc & 0x000000FF] ^ + crc_tableil8_o80[(crc >> 8) & 0x000000FF]; + uint32_t term2 = crc >> 16; + crc = term1 ^ + crc_tableil8_o72[term2 & 0x000000FF] ^ + crc_tableil8_o64[(term2 >> 8) & 0x000000FF]; + term1 = crc_tableil8_o56[(*(uint32_t *)p_buf) & 0x000000FF] ^ + crc_tableil8_o48[((*(uint32_t *)p_buf) >> 8) & 0x000000FF]; + + term2 = (*(uint32_t *)p_buf) >> 16; + crc = crc ^ term1 ^ + crc_tableil8_o40[term2 & 0x000000FF] ^ + crc_tableil8_o32[(term2 >> 8) & 0x000000FF]; + p_buf += 4; + } + + for (size_t li=0; li < end_bytes; li++) { + crc = crc_tableil8_o32[(crc ^ *p_buf++) & 0x000000FF] ^ (crc >> 8); + } + + return crc; +} + +#define CRC32C_POLY 0x1EDC6F41 + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Copyright 2001, D. Otis. Use this program, code or tables */ +/* extracted from it, as desired without restriction. */ +/* */ +/* 32 Bit Reflected CRC table generation for SCTP. */ +/* To accommodate serial byte data being shifted out least */ +/* significant bit first, the table's 32 bit words are reflected */ +/* which flips both byte and bit MS and LS positions. The CRC */ +/* is calculated MS bits first from the perspective of the serial*/ +/* stream. The x^32 term is implied and the x^0 term may also */ +/* be shown as +1. The polynomial code used is 0x1EDC6F41. */ +/* Castagnoli93 */ +/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+ */ +/* x^11+x^10+x^9+x^8+x^6+x^0 */ +/* Guy Castagnoli Stefan Braeuer and Martin Herrman */ +/* "Optimization of Cyclic Redundancy-Check Codes */ +/* with 24 and 32 Parity Bits", */ +/* IEEE Transactions on Communications, Vol.41, No.6, June 1993 */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static const uint32_t crc_c[256] = { + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, + 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, + 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, + 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, + 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, + 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, + 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, + 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, + 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, + 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, + 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, + 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, + 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, + 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, + 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, + 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, + 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, + 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, + 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, + 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, + 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, + 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, + 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, + 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, + 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, + 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, + 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, + 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, + 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, + 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, + 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, + 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, + 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, + }; +#if !defined __SSE4_2__ || (defined __SSE4_2__ && !defined __x86_64__) + + +uint64_t _mm_crc32_u64(uint64_t a, uint64_t b ) +{ + return crc32cSlicingBy8(a, (unsigned char*)&b, sizeof(b)); +} +/* +//#include "citycrc.h" +#include +#include + +int main( int argc, char** argv ) +{ + uint64_t f = 0x1234; + uint64_t a = 0x5678; + uint32_t f1 = _mm_crc32_u64(f, a); + uint32_t f4 = crc32cSlicingBy8(f, (unsigned char*)&a, sizeof(a)); + std::cout< +#include #include namespace fc { + SSL_TYPE(ssl_dh, DH, DH_free) + + static bool validate( const ssl_dh& dh, bool& valid ) { + int check; + DH_check(dh,&check); + return valid = !(check /*& DH_CHECK_P_NOT_SAFE_PRIME*/); + } + bool diffie_hellman::generate_params( int s, uint8_t g ) { - DH* dh = DH_generate_parameters( s, g, NULL, NULL ); + ssl_dh dh = DH_generate_parameters( s, g, NULL, NULL ); p.resize( BN_num_bytes( dh->p ) ); if( p.size() ) BN_bn2bin( dh->p, (unsigned char*)&p.front() ); this->g = g; - - int check; - DH_check(dh,&check); - DH_free(dh); - - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) - return valid = false; - return valid = true; + return fc::validate( dh, valid ); } + bool diffie_hellman::validate() { if( !p.size() ) return valid = false; - DH* dh = DH_new(); + ssl_dh dh = DH_new(); dh->p = BN_bin2bn( (unsigned char*)&p.front(), p.size(), NULL ); dh->g = BN_bin2bn( (unsigned char*)&g, 1, NULL ); - - int check; - DH_check(dh,&check); - DH_free(dh); - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) - return valid = false; - return valid = true; + return fc::validate( dh, valid ); } bool diffie_hellman::generate_pub_key() { if( !p.size() ) return valid = false; - DH* dh = DH_new(); + ssl_dh dh = DH_new(); dh->p = BN_bin2bn( (unsigned char*)&p.front(), p.size(), NULL ); dh->g = BN_bin2bn( (unsigned char*)&g, 1, NULL ); - int check; - DH_check(dh,&check); - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) + if( !fc::validate( dh, valid ) ) { - DH_free(dh); - return valid = false; + return false; } DH_generate_key(dh); @@ -58,11 +51,10 @@ namespace fc { if( priv_key.size() ) BN_bn2bin( dh->priv_key, (unsigned char*)&priv_key.front() ); - DH_free(dh); - return valid = true; + return true; } bool diffie_hellman::compute_shared_key( const char* buf, uint32_t s ) { - DH* dh = DH_new(); + ssl_dh dh = DH_new(); dh->p = BN_bin2bn( (unsigned char*)&p.front(), p.size(), NULL ); dh->pub_key = BN_bin2bn( (unsigned char*)&pub_key.front(), pub_key.size(), NULL ); dh->priv_key = BN_bin2bn( (unsigned char*)&priv_key.front(), priv_key.size(), NULL ); @@ -70,22 +62,19 @@ namespace fc { int check; DH_check(dh,&check); - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) + if( !fc::validate( dh, valid ) ) { - DH_free(dh); - return valid = false; + return false; } - - BIGNUM* pk = BN_bin2bn( (unsigned char*)buf, s, NULL ); + ssl_bignum pk; + BN_bin2bn( (unsigned char*)buf, s, pk ); shared_key.resize( DH_size(dh) ); DH_compute_key( (unsigned char*)&shared_key.front(), pk, dh ); - BN_free(pk); - DH_free(dh); - return valid = true; + + return true; } bool diffie_hellman::compute_shared_key( const std::vector& pubk ) { return compute_shared_key( &pubk.front(), pubk.size() ); } - } diff --git a/src/crypto/elliptic_common.cpp b/src/crypto/elliptic_common.cpp new file mode 100644 index 000000000..88228b0e7 --- /dev/null +++ b/src/crypto/elliptic_common.cpp @@ -0,0 +1,415 @@ +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +# include +#else +# include +#endif + +/* stuff common to all ecc implementations */ + +#define BTC_EXT_PUB_MAGIC (0x0488B21E) +#define BTC_EXT_PRIV_MAGIC (0x0488ADE4) + +namespace fc { namespace ecc { + + namespace detail { + typedef fc::array chr37; + + fc::sha256 _left( const fc::sha512& v ) + { + fc::sha256 result; + memcpy( result.data(), v.data(), 32 ); + return result; + } + + fc::sha256 _right( const fc::sha512& v ) + { + fc::sha256 result; + memcpy( result.data(), v.data() + 32, 32 ); + return result; + } + + static void _put( unsigned char** dest, unsigned int i) + { + *(*dest)++ = (i >> 24) & 0xff; + *(*dest)++ = (i >> 16) & 0xff; + *(*dest)++ = (i >> 8) & 0xff; + *(*dest)++ = i & 0xff; + } + + static unsigned int _get( unsigned char** src ) + { + unsigned int result = *(*src)++ << 24; + result |= *(*src)++ << 16; + result |= *(*src)++ << 8; + result |= *(*src)++; + return result; + } + + static chr37 _derive_message( char first, const char* key32, int i ) + { + chr37 result; + unsigned char* dest = (unsigned char*) result.begin(); + *dest++ = first; + memcpy( dest, key32, 32 ); dest += 32; + _put( &dest, i ); + return result; + } + + chr37 _derive_message( const public_key_data& key, int i ) + { + return _derive_message( *key.begin(), key.begin() + 1, i ); + } + + static chr37 _derive_message( const private_key_secret& key, int i ) + { + return _derive_message( 0, key.data(), i ); + } + + const ec_group& get_curve() + { + static const ec_group secp256k1( EC_GROUP_new_by_curve_name( NID_secp256k1 ) ); + return secp256k1; + } + + static private_key_secret _get_curve_order() + { + const ec_group& group = get_curve(); + bn_ctx ctx(BN_CTX_new()); + ssl_bignum order; + FC_ASSERT( EC_GROUP_get_order( group, order, ctx ) ); + private_key_secret bin; + FC_ASSERT( BN_num_bytes( order ) == bin.data_size() ); + FC_ASSERT( BN_bn2bin( order, (unsigned char*) bin.data() ) == bin.data_size() ); + return bin; + } + + const private_key_secret& get_curve_order() + { + static private_key_secret order = _get_curve_order(); + return order; + } + + static private_key_secret _get_half_curve_order() + { + const ec_group& group = get_curve(); + bn_ctx ctx(BN_CTX_new()); + ssl_bignum order; + FC_ASSERT( EC_GROUP_get_order( group, order, ctx ) ); + BN_rshift1( order, order ); + private_key_secret bin; + FC_ASSERT( BN_num_bytes( order ) == bin.data_size() ); + FC_ASSERT( BN_bn2bin( order, (unsigned char*) bin.data() ) == bin.data_size() ); + return bin; + } + + const private_key_secret& get_half_curve_order() + { + static private_key_secret half_order = _get_half_curve_order(); + return half_order; + } + } + + public_key public_key::from_key_data( const public_key_data &data ) { + return public_key(data); + } + + public_key public_key::child( const fc::sha256& offset )const + { + fc::sha256::encoder enc; + fc::raw::pack( enc, *this ); + fc::raw::pack( enc, offset ); + + return add( enc.result() ); + } + + private_key private_key::child( const fc::sha256& offset )const + { + fc::sha256::encoder enc; + fc::raw::pack( enc, get_public_key() ); + fc::raw::pack( enc, offset ); + return generate_from_seed( get_secret(), enc.result() ); + } + + std::string public_key::to_base58( const public_key_data &key ) + { + uint32_t check = (uint32_t)sha256::hash(key.data, sizeof(key))._hash[0]; + assert(key.size() + sizeof(check) == 37); + array data; + memcpy(data.data, key.begin(), key.size()); + memcpy(data.begin() + key.size(), (const char*)&check, sizeof(check)); + return fc::to_base58(data.begin(), data.size()); + } + + public_key public_key::from_base58( const std::string& b58 ) + { + array data; + size_t s = fc::from_base58(b58, (char*)&data, sizeof(data) ); + FC_ASSERT( s == sizeof(data) ); + + public_key_data key; + uint32_t check = (uint32_t)sha256::hash(data.data, sizeof(key))._hash[0]; + FC_ASSERT( memcmp( (char*)&check, data.data + sizeof(key), sizeof(check) ) == 0 ); + memcpy( (char*)key.data, data.data, sizeof(key) ); + return from_key_data(key); + } + + unsigned int public_key::fingerprint() const + { + public_key_data key = serialize(); + ripemd160 hash = ripemd160::hash( sha256::hash( key.begin(), key.size() ) ); + unsigned char* fp = (unsigned char*) hash._hash; + return (fp[0] << 24) | (fp[1] << 16) | (fp[2] << 8) | fp[3]; + } + + bool public_key::is_canonical( const compact_signature& c ) { + return !(c.data[1] & 0x80) + && !(c.data[1] == 0 && !(c.data[2] & 0x80)) + && !(c.data[33] & 0x80) + && !(c.data[33] == 0 && !(c.data[34] & 0x80)); + } + + private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset ) + { + ssl_bignum z; + BN_bin2bn((unsigned char*)&offset, sizeof(offset), z); + + ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1)); + bn_ctx ctx(BN_CTX_new()); + ssl_bignum order; + EC_GROUP_get_order(group, order, ctx); + + // secexp = (seed + z) % order + ssl_bignum secexp; + BN_bin2bn((unsigned char*)&seed, sizeof(seed), secexp); + BN_add(secexp, secexp, z); + BN_mod(secexp, secexp, order, ctx); + + fc::sha256 secret; + assert(BN_num_bytes(secexp) <= int64_t(sizeof(secret))); + auto shift = sizeof(secret) - BN_num_bytes(secexp); + BN_bn2bin(secexp, ((unsigned char*)&secret)+shift); + return regenerate( secret ); + } + + fc::sha256 private_key::get_secret( const EC_KEY * const k ) + { + if( !k ) + { + return fc::sha256(); + } + + fc::sha256 sec; + const BIGNUM* bn = EC_KEY_get0_private_key(k); + if( bn == NULL ) + { + FC_THROW_EXCEPTION( exception, "get private key failed" ); + } + int nbytes = BN_num_bytes(bn); + BN_bn2bin(bn, &((unsigned char*)&sec)[32-nbytes] ); + return sec; + } + + private_key private_key::generate() + { + EC_KEY* k = EC_KEY_new_by_curve_name( NID_secp256k1 ); + if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" ); + if( !EC_KEY_generate_key( k ) ) + { + FC_THROW_EXCEPTION( exception, "ecc key generation error" ); + + } + + return private_key( k ); + } + + static fc::string _to_base58( const extended_key_data& key ) + { + char *buffer = (char*)alloca(key.size() + 4); + memcpy( buffer, key.begin(), key.size() ); + fc::sha256 double_hash = fc::sha256::hash( fc::sha256::hash( key.begin(), key.size() )); + memcpy( buffer + key.size(), double_hash.data(), 4 ); + return fc::to_base58( buffer, sizeof(buffer) ); + } + + static void _parse_extended_data( unsigned char* buffer, fc::string base58 ) + { + memset( buffer, 0, 78 ); + std::vector decoded = fc::from_base58( base58 ); + unsigned int i = 0; + for ( char c : decoded ) + { + if ( i >= 78 || i > decoded.size() - 4 ) { break; } + buffer[i++] = c; + } + } + + extended_public_key extended_public_key::derive_child(int i) const + { + FC_ASSERT( !(i&0x80000000), "Can't derive hardened public key!" ); + return derive_normal_child(i); + } + + extended_key_data extended_public_key::serialize_extended() const + { + extended_key_data result; + unsigned char* dest = (unsigned char*) result.begin(); + detail::_put( &dest, BTC_EXT_PUB_MAGIC ); + *dest++ = depth; + detail::_put( &dest, parent_fp ); + detail::_put( &dest, child_num ); + memcpy( dest, c.data(), c.data_size() ); dest += 32; + public_key_data key = serialize(); + memcpy( dest, key.begin(), key.size() ); + return result; + } + + extended_public_key extended_public_key::deserialize( const extended_key_data& data ) + { + return from_base58( _to_base58( data ) ); + } + + fc::string extended_public_key::str() const + { + return _to_base58( serialize_extended() ); + } + + extended_public_key extended_public_key::from_base58( const fc::string& base58 ) + { + unsigned char buffer[78]; + unsigned char* ptr = buffer; + _parse_extended_data( buffer, base58 ); + FC_ASSERT( detail::_get( &ptr ) == BTC_EXT_PUB_MAGIC, "Invalid extended private key" ); + uint8_t d = *ptr++; + int fp = detail::_get( &ptr ); + int cn = detail::_get( &ptr ); + fc::sha256 chain; + memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); + public_key_data key; + memcpy( key.begin(), ptr, key.size() ); + return extended_public_key( key, chain, cn, fp, d ); + } + + extended_public_key extended_private_key::get_extended_public_key() const + { + return extended_public_key( get_public_key(), c, child_num, parent_fp, depth ); + } + + public_key extended_public_key::generate_p(int i) const { return derive_normal_child(2*i + 0); } + public_key extended_public_key::generate_q(int i) const { return derive_normal_child(2*i + 1); } + + extended_private_key extended_private_key::derive_child(int i) const + { + return i < 0 ? derive_hardened_child(i) : derive_normal_child(i); + } + + extended_private_key extended_private_key::derive_normal_child(int i) const + { + const detail::chr37 data = detail::_derive_message( get_public_key().serialize(), i ); + hmac_sha512 mac; + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + return private_derive_rest( l, i ); + } + + extended_private_key extended_private_key::derive_hardened_child(int i) const + { + hmac_sha512 mac; + private_key_secret key = get_secret(); + const detail::chr37 data = detail::_derive_message( key, i ); + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + return private_derive_rest( l, i ); + } + + extended_key_data extended_private_key::serialize_extended() const + { + extended_key_data result; + unsigned char* dest = (unsigned char*) result.begin(); + detail::_put( &dest, BTC_EXT_PRIV_MAGIC ); + *dest++ = depth; + detail::_put( &dest, parent_fp ); + detail::_put( &dest, child_num ); + memcpy( dest, c.data(), c.data_size() ); dest += 32; + *dest++ = 0; + private_key_secret key = get_secret(); + memcpy( dest, key.data(), key.data_size() ); + return result; + } + + extended_private_key extended_private_key::deserialize( const extended_key_data& data ) + { + return from_base58( _to_base58( data ) ); + } + + private_key extended_private_key::generate_a(int i) const { return derive_hardened_child(4*i + 0); } + private_key extended_private_key::generate_b(int i) const { return derive_hardened_child(4*i + 1); } + private_key extended_private_key::generate_c(int i) const { return derive_hardened_child(4*i + 2); } + private_key extended_private_key::generate_d(int i) const { return derive_hardened_child(4*i + 3); } + + fc::string extended_private_key::str() const + { + return _to_base58( serialize_extended() ); + } + + extended_private_key extended_private_key::from_base58( const fc::string& base58 ) + { + unsigned char buffer[78]; + unsigned char* ptr = buffer; + _parse_extended_data( buffer, base58 ); + FC_ASSERT( detail::_get( &ptr ) == BTC_EXT_PRIV_MAGIC, "Invalid extended private key" ); + uint8_t d = *ptr++; + int fp = detail::_get( &ptr ); + int cn = detail::_get( &ptr ); + fc::sha256 chain; + memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); + ptr++; + private_key_secret key; + memcpy( key.data(), ptr, key.data_size() ); + return extended_private_key( private_key::regenerate(key), chain, cn, fp, d ); + } + + extended_private_key extended_private_key::generate_master( const fc::string& seed ) + { + return generate_master( seed.c_str(), seed.size() ); + } + + extended_private_key extended_private_key::generate_master( const char* seed, uint32_t seed_len ) + { + hmac_sha512 mac; + fc::sha512 hash = mac.digest( "Bitcoin seed", 12, seed, seed_len ); + extended_private_key result( private_key::regenerate( detail::_left(hash) ), + detail::_right(hash) ); + return result; + } +} + +void to_variant( const ecc::private_key& var, variant& vo ) +{ + vo = var.get_secret(); +} + +void from_variant( const variant& var, ecc::private_key& vo ) +{ + fc::sha256 sec; + from_variant( var, sec ); + vo = ecc::private_key::regenerate(sec); +} + +void to_variant( const ecc::public_key& var, variant& vo ) +{ + vo = var.serialize(); +} + +void from_variant( const variant& var, ecc::public_key& vo ) +{ + ecc::public_key_data dat; + from_variant( var, dat ); + vo = ecc::public_key(dat); +} + +} diff --git a/src/crypto/elliptic_impl_priv.cpp b/src/crypto/elliptic_impl_priv.cpp new file mode 100644 index 000000000..585ffdef1 --- /dev/null +++ b/src/crypto/elliptic_impl_priv.cpp @@ -0,0 +1,102 @@ +#include + +#include + +#include "_elliptic_impl_priv.hpp" + +/* used by mixed + secp256k1 */ + +namespace fc { namespace ecc { + namespace detail { + + private_key_impl::private_key_impl() BOOST_NOEXCEPT + { + _init_lib(); + } + + private_key_impl::private_key_impl( const private_key_impl& cpy ) BOOST_NOEXCEPT + { + _init_lib(); + this->_key = cpy._key; + } + + private_key_impl& private_key_impl::operator=( const private_key_impl& pk ) BOOST_NOEXCEPT + { + _key = pk._key; + return *this; + } + } + + static const private_key_secret empty_priv; + + private_key::private_key() {} + + private_key::private_key( const private_key& pk ) : my( pk.my ) {} + + private_key::private_key( private_key&& pk ) : my( std::move( pk.my ) ) {} + + private_key::~private_key() {} + + private_key& private_key::operator=( private_key&& pk ) + { + my = std::move( pk.my ); + return *this; + } + + private_key& private_key::operator=( const private_key& pk ) + { + my = pk.my; + return *this; + } + + private_key private_key::regenerate( const fc::sha256& secret ) + { + private_key self; + self.my->_key = secret; + return self; + } + + fc::sha256 private_key::get_secret()const + { + return my->_key; + } + + private_key::private_key( EC_KEY* k ) + { + my->_key = get_secret( k ); + EC_KEY_free(k); + } + + public_key private_key::get_public_key()const + { + FC_ASSERT( my->_key != empty_priv ); + public_key_data pub; + unsigned int pk_len; + FC_ASSERT( secp256k1_ec_pubkey_create( detail::_get_context(), (unsigned char*) pub.begin(), (int*) &pk_len, (unsigned char*) my->_key.data(), 1 ) ); + FC_ASSERT( pk_len == pub.size() ); + return public_key(pub); + } + + static int extended_nonce_function( unsigned char *nonce32, const unsigned char *msg32, + const unsigned char *key32, unsigned int attempt, + const void *data ) { + unsigned int* extra = (unsigned int*) data; + (*extra)++; + return secp256k1_nonce_function_default( nonce32, msg32, key32, *extra, nullptr ); + } + + compact_signature private_key::sign_compact( const fc::sha256& digest, bool require_canonical )const + { + FC_ASSERT( my->_key != empty_priv ); + compact_signature result; + int recid; + unsigned int counter = 0; + do + { + FC_ASSERT( secp256k1_ecdsa_sign_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) result.begin() + 1, (unsigned char*) my->_key.data(), extended_nonce_function, &counter, &recid )); + } while( require_canonical && !public_key::is_canonical( result ) ); + result.begin()[0] = 27 + 4 + recid; + return result; + } + +}} diff --git a/src/crypto/elliptic_impl_pub.cpp b/src/crypto/elliptic_impl_pub.cpp new file mode 100644 index 000000000..c9bbb0847 --- /dev/null +++ b/src/crypto/elliptic_impl_pub.cpp @@ -0,0 +1,358 @@ +#include +#include + +#include "_elliptic_impl_pub.hpp" + +/* used by mixed + openssl */ + +namespace fc { namespace ecc { + namespace detail { + + public_key_impl::public_key_impl() BOOST_NOEXCEPT + { + _init_lib(); + } + + public_key_impl::public_key_impl( const public_key_impl& cpy ) BOOST_NOEXCEPT + { + _init_lib(); + *this = cpy; + } + + public_key_impl::public_key_impl( public_key_impl&& cpy ) BOOST_NOEXCEPT + { + _init_lib(); + *this = cpy; + } + + public_key_impl::~public_key_impl() BOOST_NOEXCEPT + { + free_key(); + } + + public_key_impl& public_key_impl::operator=( const public_key_impl& pk ) BOOST_NOEXCEPT + { + if (pk._key == nullptr) + { + free_key(); + } else if ( _key == nullptr ) { + _key = EC_KEY_dup( pk._key ); + } else { + EC_KEY_copy( _key, pk._key ); + } + return *this; + } + + public_key_impl& public_key_impl::operator=( public_key_impl&& pk ) BOOST_NOEXCEPT + { + if ( this != &pk ) { + free_key(); + _key = pk._key; + pk._key = nullptr; + } + return *this; + } + + void public_key_impl::free_key() BOOST_NOEXCEPT + { + if( _key != nullptr ) + { + EC_KEY_free(_key); + _key = nullptr; + } + } + + // Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields + // recid selects which key is recovered + // if check is non-zero, additional checks are performed + int public_key_impl::ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, + const unsigned char *msg, + int msglen, int recid, int check) + { + if (!eckey) FC_THROW_EXCEPTION( exception, "null key" ); + + int ret = 0; + BN_CTX *ctx = NULL; + + BIGNUM *x = NULL; + BIGNUM *e = NULL; + BIGNUM *order = NULL; + BIGNUM *sor = NULL; + BIGNUM *eor = NULL; + BIGNUM *field = NULL; + EC_POINT *R = NULL; + EC_POINT *O = NULL; + EC_POINT *Q = NULL; + BIGNUM *rr = NULL; + BIGNUM *zero = NULL; + int n = 0; + int i = recid / 2; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } + BN_CTX_start(ctx); + order = BN_CTX_get(ctx); + if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } + x = BN_CTX_get(ctx); + if (!BN_copy(x, order)) { ret=-1; goto err; } + if (!BN_mul_word(x, i)) { ret=-1; goto err; } + if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } + field = BN_CTX_get(ctx); + if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } + if (BN_cmp(x, field) >= 0) { ret=0; goto err; } + if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } + if (check) + { + if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } + if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } + } + if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + n = EC_GROUP_get_degree(group); + e = BN_CTX_get(ctx); + if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } + if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); + zero = BN_CTX_get(ctx); + if (!BN_zero(zero)) { ret=-1; goto err; } + if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } + rr = BN_CTX_get(ctx); + if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } + sor = BN_CTX_get(ctx); + if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } + eor = BN_CTX_get(ctx); + if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } + if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } + if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } + + ret = 1; + + err: + if (ctx) { + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + if (R != NULL) EC_POINT_free(R); + if (O != NULL) EC_POINT_free(O); + if (Q != NULL) EC_POINT_free(Q); + return ret; + } + } + + public_key::public_key() {} + + public_key::public_key( const public_key& pk ) : my( pk.my ) {} + + public_key::public_key( public_key&& pk ) : my( std::move( pk.my ) ) {} + + public_key::~public_key() {} + + public_key& public_key::operator=( public_key&& pk ) + { + my = std::move(pk.my); + return *this; + } + + public_key& public_key::operator=( const public_key& pk ) + { + my = pk.my; + return *this; + } + + bool public_key::valid()const + { + return my->_key != nullptr; + } + + /* WARNING! This implementation is broken, it is actually equivalent to + * public_key::add()! + */ +// public_key public_key::mult( const fc::sha256& digest ) const +// { +// // get point from this public key +// const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key ); +// ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1)); +// +// ssl_bignum z; +// BN_bin2bn((unsigned char*)&digest, sizeof(digest), z); +// +// // multiply by digest +// ssl_bignum one; +// BN_one(one); +// bn_ctx ctx(BN_CTX_new()); +// +// ec_point result(EC_POINT_new(group)); +// EC_POINT_mul(group, result, z, master_pub, one, ctx); +// +// public_key rtn; +// rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); +// EC_KEY_set_public_key(rtn.my->_key,result); +// +// return rtn; +// } + public_key public_key::add( const fc::sha256& digest )const + { + try { + ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1)); + bn_ctx ctx(BN_CTX_new()); + + fc::bigint digest_bi( (char*)&digest, sizeof(digest) ); + + ssl_bignum order; + EC_GROUP_get_order(group, order, ctx); + if( digest_bi > fc::bigint(order) ) + { + FC_THROW_EXCEPTION( exception, "digest > group order" ); + } + + + public_key digest_key = private_key::regenerate(digest).get_public_key(); + const EC_POINT* digest_point = EC_KEY_get0_public_key( digest_key.my->_key ); + + // get point from this public key + const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key ); + +// ssl_bignum z; +// BN_bin2bn((unsigned char*)&digest, sizeof(digest), z); + + // multiply by digest +// ssl_bignum one; +// BN_one(one); + + ec_point result(EC_POINT_new(group)); + EC_POINT_add(group, result, digest_point, master_pub, ctx); + + if (EC_POINT_is_at_infinity(group, result)) + { + FC_THROW_EXCEPTION( exception, "point at infinity" ); + } + + + public_key rtn; + rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + EC_KEY_set_public_key(rtn.my->_key,result); + return rtn; + } FC_RETHROW_EXCEPTIONS( debug, "digest: ${digest}", ("digest",digest) ); + } + + std::string public_key::to_base58() const + { + public_key_data key = serialize(); + return to_base58( key ); + } + +// signature private_key::sign( const fc::sha256& digest )const +// { +// unsigned int buf_len = ECDSA_size(my->_key); +//// fprintf( stderr, "%d %d\n", buf_len, sizeof(sha256) ); +// signature sig; +// assert( buf_len == sizeof(sig) ); +// +// if( !ECDSA_sign( 0, +// (const unsigned char*)&digest, sizeof(digest), +// (unsigned char*)&sig, &buf_len, my->_key ) ) +// { +// FC_THROW_EXCEPTION( exception, "signing error" ); +// } +// +// +// return sig; +// } +// bool public_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig ) +// { +// return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key ); +// } + + public_key_data public_key::serialize()const + { + public_key_data dat; + if( !my->_key ) return dat; + EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED ); + /*size_t nbytes = i2o_ECPublicKey( my->_key, nullptr ); */ + /*assert( nbytes == 33 )*/ + char* front = &dat.data[0]; + i2o_ECPublicKey( my->_key, (unsigned char**)&front ); // FIXME: questionable memory handling + return dat; + /* + EC_POINT* pub = EC_KEY_get0_public_key( my->_key ); + EC_GROUP* group = EC_KEY_get0_group( my->_key ); + EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr ); + */ + } + public_key_point_data public_key::serialize_ecc_point()const + { + public_key_point_data dat; + if( !my->_key ) return dat; + EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_UNCOMPRESSED ); + char* front = &dat.data[0]; + i2o_ECPublicKey( my->_key, (unsigned char**)&front ); // FIXME: questionable memory handling + return dat; + } + + public_key::public_key( const public_key_point_data& dat ) + { + const char* front = &dat.data[0]; + if( *front == 0 ){} + else + { + my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(dat) ); + if( !my->_key ) + { + FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + } + } + public_key::public_key( const public_key_data& dat ) + { + const char* front = &dat.data[0]; + if( *front == 0 ){} + else + { + my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(public_key_data) ); + if( !my->_key ) + { + FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) ); + } + } + } + +// bool private_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig ) +// { +// return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key ); +// } + + public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical ) + { + int nV = c.data[0]; + if (nV<27 || nV>=35) + FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); + + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&c.data[1],32,sig->r); + BN_bin2bn(&c.data[33],32,sig->s); + + if( check_canonical ) + { + FC_ASSERT( is_canonical( c ), "signature is not canonical" ); + } + + my->_key = EC_KEY_new_by_curve_name(NID_secp256k1); + + if (nV >= 31) + { + EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED ); + nV -= 4; +// fprintf( stderr, "compressed\n" ); + } + + if (detail::public_key_impl::ECDSA_SIG_recover_key_GFp(my->_key, sig, (unsigned char*)&digest, sizeof(digest), nV - 27, 0) == 1) + { + ECDSA_SIG_free(sig); + return; + } + ECDSA_SIG_free(sig); + FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); + } +}} diff --git a/src/crypto/elliptic_mixed.cpp b/src/crypto/elliptic_mixed.cpp new file mode 100644 index 000000000..e9af233ad --- /dev/null +++ b/src/crypto/elliptic_mixed.cpp @@ -0,0 +1,40 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "_elliptic_impl_priv.hpp" +#include "_elliptic_impl_pub.hpp" + +namespace fc { namespace ecc { + namespace detail + { + const secp256k1_context_t* _get_context() { + static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; + } + + void _init_lib() { + static const secp256k1_context_t* ctx = _get_context(); + static int init_o = init_openssl(); + } + } + + static const private_key_secret empty_priv; + fc::sha512 private_key::get_shared_secret( const public_key& other )const + { + FC_ASSERT( my->_key != empty_priv ); + FC_ASSERT( other.my->_key != nullptr ); + public_key_data pub(other.serialize()); + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) pub.begin(), pub.size(), (unsigned char*) my->_key.data() ) ); + return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 ); + } + +} } diff --git a/src/crypto/elliptic_openssl.cpp b/src/crypto/elliptic_openssl.cpp new file mode 100644 index 000000000..78170a78a --- /dev/null +++ b/src/crypto/elliptic_openssl.cpp @@ -0,0 +1,261 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +#include "_elliptic_impl_pub.hpp" + +namespace fc { namespace ecc { + namespace detail + { + void _init_lib() { + static int init_o = init_openssl(); + } + + class private_key_impl + { + public: + private_key_impl() BOOST_NOEXCEPT + { + _init_lib(); + } + + private_key_impl( const private_key_impl& cpy ) BOOST_NOEXCEPT + { + _init_lib(); + *this = cpy; + } + + private_key_impl( private_key_impl&& cpy ) BOOST_NOEXCEPT + { + _init_lib(); + *this = cpy; + } + + ~private_key_impl() BOOST_NOEXCEPT + { + free_key(); + } + + private_key_impl& operator=( const private_key_impl& pk ) BOOST_NOEXCEPT + { + if (pk._key == nullptr) + { + free_key(); + } else if ( _key == nullptr ) { + _key = EC_KEY_dup( pk._key ); + } else { + EC_KEY_copy( _key, pk._key ); + } + return *this; + } + + private_key_impl& operator=( private_key_impl&& pk ) BOOST_NOEXCEPT + { + if ( this != &pk ) { + free_key(); + _key = pk._key; + pk._key = nullptr; + } + return *this; + } + + EC_KEY* _key = nullptr; + + private: + void free_key() BOOST_NOEXCEPT + { + if( _key != nullptr ) + { + EC_KEY_free(_key); + _key = nullptr; + } + } + }; + } + + private_key::private_key() {} + + private_key::private_key( const private_key& pk ) : my( pk.my ) {} + + private_key::private_key( private_key&& pk ) : my( std::move( pk.my ) ) {} + + private_key::~private_key() {} + + private_key& private_key::operator=( private_key&& pk ) + { + my = std::move(pk.my); + return *this; + } + + private_key& private_key::operator=( const private_key& pk ) + { + my = pk.my; + return *this; + } + static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen) + { + if (*olen < SHA512_DIGEST_LENGTH) { + return NULL; + } + *olen = SHA512_DIGEST_LENGTH; + return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output); + } + + int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const BIGNUM *priv_key) + { + int ok = 0; + BN_CTX *ctx = NULL; + EC_POINT *pub_key = NULL; + + if (!eckey) return 0; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + pub_key = EC_POINT_new(group); + + if (pub_key == NULL) + goto err; + + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + goto err; + + EC_KEY_set_private_key(eckey,priv_key); + EC_KEY_set_public_key(eckey,pub_key); + + ok = 1; + + err: + + if (pub_key) EC_POINT_free(pub_key); + if (ctx != NULL) BN_CTX_free(ctx); + + return(ok); + } + + private_key private_key::regenerate( const fc::sha256& secret ) + { + private_key self; + self.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + if( !self.my->_key ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" ); + + ssl_bignum bn; + BN_bin2bn( (const unsigned char*)&secret, 32, bn ); + + if( !EC_KEY_regenerate_key(self.my->_key,bn) ) + { + FC_THROW_EXCEPTION( exception, "unable to regenerate key" ); + } + return self; + } + + fc::sha256 private_key::get_secret()const + { + return get_secret( my->_key ); + } + + private_key::private_key( EC_KEY* k ) + { + my->_key = k; + } + + public_key private_key::get_public_key()const + { + public_key pub; + pub.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + EC_KEY_set_public_key( pub.my->_key, EC_KEY_get0_public_key( my->_key ) ); + return pub; + } + + + fc::sha512 private_key::get_shared_secret( const public_key& other )const + { + FC_ASSERT( my->_key != nullptr ); + FC_ASSERT( other.my->_key != nullptr ); + fc::sha512 buf; + ECDH_compute_key( (unsigned char*)&buf, sizeof(buf), EC_KEY_get0_public_key(other.my->_key), my->_key, ecies_key_derivation ); + return buf; + } + + compact_signature private_key::sign_compact( const fc::sha256& digest )const + { + try { + FC_ASSERT( my->_key != nullptr ); + auto my_pub_key = get_public_key().serialize(); // just for good measure + //ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); + public_key_data key_data; + while( true ) + { + ecdsa_sig sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); + + if (sig==nullptr) + FC_THROW_EXCEPTION( exception, "Unable to sign" ); + + compact_signature csig; + // memset( csig.data, 0, sizeof(csig) ); + + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) + { + int nRecId = -1; + EC_KEY* key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + FC_ASSERT( key ); + EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); + for (int i=0; i<4; i++) + { + if (detail::public_key_impl::ECDSA_SIG_recover_key_GFp(key, sig, (unsigned char*)&digest, sizeof(digest), i, 1) == 1) + { + unsigned char* buffer = (unsigned char*) key_data.begin(); + i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling + if ( key_data == my_pub_key ) + { + nRecId = i; + break; + } + } + } + EC_KEY_free( key ); + + if (nRecId == -1) + { + FC_THROW_EXCEPTION( exception, "unable to construct recoverable key"); + } + unsigned char* result = nullptr; + auto bytes = i2d_ECDSA_SIG( sig, &result ); + auto lenR = result[3]; + auto lenS = result[5+lenR]; + //idump( (result[0])(result[1])(result[2])(result[3])(result[3+lenR])(result[4+lenR])(bytes)(lenR)(lenS) ); + if( lenR != 32 ) { free(result); continue; } + if( lenS != 32 ) { free(result); continue; } + //idump( (33-(nBitsR+7)/8) ); + //idump( (65-(nBitsS+7)/8) ); + //idump( (sizeof(csig) ) ); + memcpy( &csig.data[1], &result[4], lenR ); + memcpy( &csig.data[33], &result[6+lenR], lenS ); + //idump( (csig.data[33]) ); + //idump( (csig.data[1]) ); + free(result); + //idump( (nRecId) ); + csig.data[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0); + /* + idump( (csig) ); + auto rlen = BN_bn2bin(sig->r,&csig.data[33-(nBitsR+7)/8]); + auto slen = BN_bn2bin(sig->s,&csig.data[65-(nBitsS+7)/8]); + idump( (rlen)(slen) ); + */ + } + return csig; + } // while true + } FC_RETHROW_EXCEPTIONS( warn, "sign ${digest}", ("digest", digest)("private_key",*this) ); + } +} } diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp new file mode 100644 index 000000000..f4175d0e1 --- /dev/null +++ b/src/crypto/elliptic_secp256k1.cpp @@ -0,0 +1,555 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +# include +#else +# include +#endif + +#include "_elliptic_impl_priv.hpp" + +namespace fc { namespace ecc { + namespace detail + { + const secp256k1_context_t* _get_context() { + static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_RANGEPROOF | SECP256K1_CONTEXT_COMMIT ); + return ctx; + } + + void _init_lib() { + static const secp256k1_context_t* ctx = _get_context(); + static int init_o = init_openssl(); + (void)ctx; + } + + class public_key_impl + { + public: + public_key_impl() BOOST_NOEXCEPT + { + _init_lib(); + } + + public_key_impl( const public_key_impl& cpy ) BOOST_NOEXCEPT + : _key( cpy._key ) + { + _init_lib(); + } + + public_key_data _key; + }; + + typedef fc::array chr37; + chr37 _derive_message( const public_key_data& key, int i ); + fc::sha256 _left( const fc::sha512& v ); + fc::sha256 _right( const fc::sha512& v ); + const ec_group& get_curve(); + const private_key_secret& get_curve_order(); + const private_key_secret& get_half_curve_order(); + } + + static const public_key_data empty_pub; + static const private_key_secret empty_priv; + + fc::sha512 private_key::get_shared_secret( const public_key& other )const + { + FC_ASSERT( my->_key != empty_priv ); + FC_ASSERT( other.my->_key != empty_pub ); + public_key_data pub(other.my->_key); + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) pub.begin(), pub.size(), (unsigned char*) my->_key.data() ) ); + return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 ); + } + + + public_key::public_key() {} + + public_key::public_key( const public_key &pk ) : my( pk.my ) {} + + public_key::public_key( public_key &&pk ) : my( std::move( pk.my ) ) {} + + public_key::~public_key() {} + + public_key& public_key::operator=( const public_key& pk ) + { + my = pk.my; + return *this; + } + + public_key& public_key::operator=( public_key&& pk ) + { + my = pk.my; + return *this; + } + + bool public_key::valid()const + { + return my->_key != empty_pub; + } + + public_key public_key::add( const fc::sha256& digest )const + { + FC_ASSERT( my->_key != empty_pub ); + public_key_data new_key; + memcpy( new_key.begin(), my->_key.begin(), new_key.size() ); + FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) new_key.begin(), new_key.size(), (unsigned char*) digest.data() ) ); + return public_key( new_key ); + } + + std::string public_key::to_base58() const + { + FC_ASSERT( my->_key != empty_pub ); + return to_base58( my->_key ); + } + + public_key_data public_key::serialize()const + { + FC_ASSERT( my->_key != empty_pub ); + return my->_key; + } + + public_key_point_data public_key::serialize_ecc_point()const + { + FC_ASSERT( my->_key != empty_pub ); + public_key_point_data dat; + unsigned int pk_len = my->_key.size(); + memcpy( dat.begin(), my->_key.begin(), pk_len ); + FC_ASSERT( secp256k1_ec_pubkey_decompress( detail::_get_context(), (unsigned char *) dat.begin(), (int*) &pk_len ) ); + FC_ASSERT( pk_len == dat.size() ); + return dat; + } + + public_key::public_key( const public_key_point_data& dat ) + { + const char* front = &dat.data[0]; + if( *front == 0 ){} + else + { + EC_KEY *key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + key = o2i_ECPublicKey( &key, (const unsigned char**)&front, sizeof(dat) ); + FC_ASSERT( key ); + EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); + unsigned char* buffer = (unsigned char*) my->_key.begin(); + i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling + EC_KEY_free( key ); + } + } + + public_key::public_key( const public_key_data& dat ) + { + my->_key = dat; + } + + public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical ) + { + int nV = c.data[0]; + if (nV<27 || nV>=35) + FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); + + if( check_canonical ) + { + FC_ASSERT( is_canonical( c ), "signature is not canonical" ); + } + + unsigned int pk_len; + FC_ASSERT( secp256k1_ecdsa_recover_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) c.begin() + 1, (unsigned char*) my->_key.begin(), (int*) &pk_len, 1, (*c.begin() - 27) & 3 ) ); + FC_ASSERT( pk_len == my->_key.size() ); + } + + extended_public_key::extended_public_key( const public_key& k, const fc::sha256& c, + int child, int parent, uint8_t depth ) + : public_key(k), c(c), child_num(child), parent_fp(parent), depth(depth) { } + + extended_public_key extended_public_key::derive_normal_child(int i) const + { + hmac_sha512 mac; + public_key_data key = serialize(); + const detail::chr37 data = detail::_derive_message( key, i ); + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + fc::sha256 left = detail::_left(l); + FC_ASSERT( left < detail::get_curve_order() ); + FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) key.begin(), key.size(), (unsigned char*) left.data() ) > 0 ); + // FIXME: check validity - if left + key == infinity then invalid + extended_public_key result( key, detail::_right(l), i, fingerprint(), depth + 1 ); + return result; + } + + static void to_bignum( const unsigned char* in, ssl_bignum& out, unsigned int len ) + { + if ( *in & 0x80 ) + { + unsigned char *buffer = (unsigned char*)alloca(len + 1); + *buffer = 0; + memcpy( buffer + 1, in, len ); + BN_bin2bn( buffer, sizeof(buffer), out ); + } + else + { + BN_bin2bn( in, len, out ); + } + } + + static void to_bignum( const private_key_secret& in, ssl_bignum& out ) + { + to_bignum( (unsigned char*) in.data(), out, in.data_size() ); + } + + static void from_bignum( const ssl_bignum& in, unsigned char* out, unsigned int len ) + { + unsigned int l = BN_num_bytes( in ); + if ( l > len ) + { + unsigned char *buffer = (unsigned char*)alloca(l); + BN_bn2bin( in, buffer ); + memcpy( out, buffer + l - len, len ); + } + else + { + memset( out, 0, len - l ); + BN_bn2bin( in, out + len - l ); + } + } + + static void from_bignum( const ssl_bignum& in, private_key_secret& out ) + { + from_bignum( in, (unsigned char*) out.data(), out.data_size() ); + } + + static void invert( const private_key_secret& in, private_key_secret& out ) + { + ssl_bignum bn_in; + to_bignum( in, bn_in ); + ssl_bignum bn_n; + to_bignum( detail::get_curve_order(), bn_n ); + ssl_bignum bn_inv; + bn_ctx ctx( BN_CTX_new() ); + FC_ASSERT( BN_mod_inverse( bn_inv, bn_in, bn_n, ctx ) ); + from_bignum( bn_inv, out ); + } + + static void to_point( const public_key_data& in, ec_point& out ) + { + bn_ctx ctx( BN_CTX_new() ); + const ec_group& curve = detail::get_curve(); + private_key_secret x; + memcpy( x.data(), in.begin() + 1, x.data_size() ); + ssl_bignum bn_x; + to_bignum( x, bn_x ); + FC_ASSERT( EC_POINT_set_compressed_coordinates_GFp( curve, out, bn_x, *in.begin() & 1, ctx ) > 0 ); + } + + static void from_point( const ec_point& in, public_key_data& out ) + { + bn_ctx ctx( BN_CTX_new() ); + const ec_group& curve = detail::get_curve(); + ssl_bignum bn_x; + ssl_bignum bn_y; + FC_ASSERT( EC_POINT_get_affine_coordinates_GFp( curve, in, bn_x, bn_y, ctx ) > 0 ); + private_key_secret x; + from_bignum( bn_x, x ); + memcpy( out.begin() + 1, x.data(), out.size() - 1 ); + *out.begin() = BN_is_bit_set( bn_y, 0 ) ? 3 : 2; + } + +// static void print(const unsigned char* data) { +// for (int i = 0; i < 32; i++) { +// printf("%02x", *data++); +// } +// } +// +// static void print(private_key_secret key) { +// print((unsigned char*) key.data()); +// } +// +// static void print(public_key_data key) { +// print((unsigned char*) key.begin() + 1); +// } + + static void canonicalize( unsigned char *int256 ) + { + fc::sha256 biggi( (char*) int256, 32 ); + if ( detail::get_half_curve_order() >= biggi ) + { + return; // nothing to do + } + ssl_bignum bn_k; + to_bignum( int256, bn_k, 32 ); + ssl_bignum bn_n; + to_bignum( detail::get_curve_order(), bn_n ); + FC_ASSERT( BN_sub( bn_k, bn_n, bn_k ) ); + from_bignum( bn_k, int256, 32 ); + } + + static public_key compute_k( const private_key_secret& a, const private_key_secret& c, + const public_key& p ) + { + private_key_secret prod = a; + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) prod.data(), (unsigned char*) c.data() ) > 0 ); + invert( prod, prod ); + public_key_data P = p.serialize(); + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) P.begin(), P.size(), (unsigned char*) prod.data() ) ); +// printf("K: "); print(P); printf("\n"); + return public_key( P ); + } + + static public_key compute_t( const private_key_secret& a, const private_key_secret& b, + const private_key_secret& c, const private_key_secret& d, + const public_key_data& p, const public_key_data& q ) + { + private_key_secret prod; + invert( c, prod ); // prod == c^-1 + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) prod.data(), (unsigned char*) d.data() ) > 0 ); + // prod == c^-1 * d + + public_key_data accu = p; + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) accu.begin(), accu.size(), (unsigned char*) prod.data() ) ); + // accu == prod * P == c^-1 * d * P + + ec_point point_accu( EC_POINT_new( detail::get_curve() ) ); + to_point( accu, point_accu ); + ec_point point_q( EC_POINT_new( detail::get_curve() ) ); + to_point( q, point_q ); + bn_ctx ctx(BN_CTX_new()); + FC_ASSERT( EC_POINT_add( detail::get_curve(), point_accu, point_accu, point_q, ctx ) > 0 ); + from_point( point_accu, accu ); + // accu == c^-1 * a * P + Q + + FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) accu.begin(), accu.size(), (unsigned char*) b.data() ) ); + // accu == c^-1 * a * P + Q + b*G + + public_key_data k = compute_k( a, c, p ).serialize(); + memcpy( prod.data(), k.begin() + 1, prod.data_size() ); + // prod == Kx + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) prod.data(), (unsigned char*) a.data() ) > 0 ); + // prod == Kx * a + invert( prod, prod ); + // prod == (Kx * a)^-1 + + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) accu.begin(), accu.size(), (unsigned char*) prod.data() ) ); + // accu == (c^-1 * a * P + Q + b*G) * (Kx * a)^-1 + +// printf("T: "); print(accu); printf("\n"); + return public_key( accu ); + } + + extended_private_key::extended_private_key( const private_key& k, const sha256& c, + int child, int parent, uint8_t depth ) + : private_key(k), c(c), child_num(child), parent_fp(parent), depth(depth) { } + + extended_private_key extended_private_key::private_derive_rest( const fc::sha512& hash, + int i) const + { + fc::sha256 left = detail::_left(hash); + FC_ASSERT( left < detail::get_curve_order() ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) left.data(), (unsigned char*) get_secret().data() ) > 0 ); + extended_private_key result( private_key::regenerate( left ), detail::_right(hash), + i, fingerprint(), depth + 1 ); + return result; + } + + public_key extended_private_key::blind_public_key( const extended_public_key& bob, int i ) const + { + private_key_secret a = generate_a(i).get_secret(); + private_key_secret b = generate_b(i).get_secret(); + private_key_secret c = generate_c(i).get_secret(); + private_key_secret d = generate_d(i).get_secret(); + public_key_data p = bob.generate_p(i).serialize(); + public_key_data q = bob.generate_q(i).serialize(); +// printf("a: "); print(a); printf("\n"); +// printf("b: "); print(b); printf("\n"); +// printf("c: "); print(c); printf("\n"); +// printf("d: "); print(d); printf("\n"); +// printf("P: "); print(p); printf("\n"); +// printf("Q: "); print(q); printf("\n"); + return compute_t( a, b, c, d, p, q ); + } + + blinded_hash extended_private_key::blind_hash( const fc::sha256& hash, int i ) const + { + private_key_secret a = generate_a(i).get_secret(); + private_key_secret b = generate_b(i).get_secret(); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) a.data(), (unsigned char*) hash.data() ) > 0 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) a.data(), (unsigned char*) b.data() ) > 0 ); +// printf("hash: "); print(hash); printf("\n"); +// printf("blinded: "); print(a); printf("\n"); + return a; + } + + private_key_secret extended_private_key::compute_p( int i ) const + { + private_key_secret p_inv = derive_normal_child( 2*i ).get_secret(); + invert( p_inv, p_inv ); +// printf("p: "); print(p_inv); printf("\n"); + return p_inv; + } + + private_key_secret extended_private_key::compute_q( int i, const private_key_secret& p ) const + { + private_key_secret q = derive_normal_child( 2*i + 1 ).get_secret(); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) q.data(), (unsigned char*) p.data() ) > 0 ); +// printf("q: "); print(q); printf("\n"); + return q; + } + + blind_signature extended_private_key::blind_sign( const blinded_hash& hash, int i ) const + { + private_key_secret p = compute_p( i ); + private_key_secret q = compute_q( i, p ); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) p.data(), (unsigned char*) hash.data() ) > 0 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) p.data(), (unsigned char*) q.data() ) > 0 ); +// printf("blind_sig: "); print(p); printf("\n"); + return p; + } + + compact_signature extended_private_key::unblind_signature( const extended_public_key& bob, + const blind_signature& sig, + const fc::sha256& hash, + int i ) const + { + private_key_secret a = generate_a(i).get_secret(); + private_key_secret b = generate_b(i).get_secret(); + private_key_secret c = generate_c(i).get_secret(); + private_key_secret d = generate_d(i).get_secret(); + public_key p = bob.generate_p(i); + public_key q = bob.generate_q(i); + public_key_data k = compute_k( a, c, p ); + public_key_data t = compute_t( a, b, c, d, p, q ).serialize(); + + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) c.data(), (unsigned char*) sig.data() ) > 0 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) c.data(), (unsigned char*) d.data() ) > 0 ); + + compact_signature result; + memcpy( result.begin() + 1, k.begin() + 1, 32 ); + memcpy( result.begin() + 33, c.data(), 32 ); + canonicalize( result.begin() + 33 ); +// printf("unblinded: "); print(result.begin() + 33); printf("\n"); + for ( int i = 0; i < 4; i++ ) + { + unsigned char pubkey[33]; + int pklen = 33; + if ( secp256k1_ecdsa_recover_compact( detail::_get_context(), (unsigned char*) hash.data(), + (unsigned char*) result.begin() + 1, + pubkey, &pklen, 1, i ) ) + { + if ( !memcmp( t.begin(), pubkey, sizeof(pubkey) ) ) + { + *result.begin() = 27 + 4 + i; + return result; +// } else { +// printf("Candidate: "); print( pubkey ); printf("\n"); + } + } + } + FC_ASSERT( 0, "Failed to unblind - use different i" ); + } + + commitment_type blind( const blind_factor_type& blind, uint64_t value ) + { + commitment_type result; + FC_ASSERT( secp256k1_pedersen_commit( detail::_get_context(), (unsigned char*)&result, (unsigned char*)&blind, value ) ); + return result; + } + + blind_factor_type blind_sum( const std::vector& blinds_in, uint32_t non_neg ) + { + blind_factor_type result; + std::vector blinds(blinds_in.size()); + for( uint32_t i = 0; i < blinds_in.size(); ++i ) blinds[i] = (const unsigned char*)&blinds_in[i]; + FC_ASSERT( secp256k1_pedersen_blind_sum( detail::_get_context(), (unsigned char*)&result, blinds.data(), blinds_in.size(), non_neg ) ); + return result; + } + + /** verifies taht commnits + neg_commits + excess == 0 */ + bool verify_sum( const std::vector& commits_in, const std::vector& neg_commits_in, int64_t excess ) + { + std::vector commits(commits_in.size()); + for( uint32_t i = 0; i < commits_in.size(); ++i ) commits[i] = (const unsigned char*)&commits_in[i]; + std::vector neg_commits(neg_commits_in.size()); + for( uint32_t i = 0; i < neg_commits_in.size(); ++i ) neg_commits[i] = (const unsigned char*)&neg_commits_in[i]; + + return secp256k1_pedersen_verify_tally( detail::_get_context(), commits.data(), commits.size(), neg_commits.data(), neg_commits.size(), excess ); + } + + bool verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const std::vector& proof ) + { + return secp256k1_rangeproof_verify( detail::_get_context(), &min_val, &max_val, (const unsigned char*)&commit, (const unsigned char*)proof.data(), proof.size() ); + } + + std::vector range_proof_sign( uint64_t min_value, + const commitment_type& commit, + const blind_factor_type& commit_blind, + const blind_factor_type& nonce, + int8_t base10_exp, + uint8_t min_bits, + uint64_t actual_value + ) + { + int proof_len = 5134; + std::vector proof(proof_len); + + FC_ASSERT( secp256k1_rangeproof_sign( detail::_get_context(), + (unsigned char*)proof.data(), + &proof_len, min_value, + (const unsigned char*)&commit, + (const unsigned char*)&commit_blind, + (const unsigned char*)&nonce, + base10_exp, min_bits, actual_value ) ); + proof.resize(proof_len); + return proof; + } + + + bool verify_range_proof_rewind( blind_factor_type& blind_out, + uint64_t& value_out, + string& message_out, + const blind_factor_type& nonce, + uint64_t& min_val, + uint64_t& max_val, + commitment_type commit, + const std::vector& proof ) + { + char msg[4096]; + int mlen = 0; + FC_ASSERT( secp256k1_rangeproof_rewind( detail::_get_context(), + (unsigned char*)&blind_out, + &value_out, + (unsigned char*)msg, + &mlen, + (const unsigned char*)&nonce, + &min_val, + &max_val, + (const unsigned char*)&commit, + (const unsigned char*)proof.data(), + proof.size() ) ); + + message_out = std::string( msg, mlen ); + return true; + } + + range_proof_info range_get_info( const std::vector& proof ) + { + range_proof_info result; + FC_ASSERT( secp256k1_rangeproof_info( detail::_get_context(), + (int*)&result.exp, + (int*)&result.mantissa, + (uint64_t*)&result.min_value, + (uint64_t*)&result.max_value, + (const unsigned char*)proof.data(), + (int)proof.size() ) ); + + return result; + } + + +} } diff --git a/src/hex.cpp b/src/crypto/hex.cpp similarity index 71% rename from src/hex.cpp rename to src/crypto/hex.cpp index ac51309fe..67ef91928 100644 --- a/src/hex.cpp +++ b/src/crypto/hex.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace fc { @@ -10,12 +10,13 @@ namespace fc { return c - 'a' + 10; if( c >= 'A' && c <= 'F' ) return c - 'A' + 10; - FC_THROW_MSG( "Invalid hex character '%s'", c ); + FC_THROW_EXCEPTION( exception, "Invalid hex character '${c}'", ("c", fc::string(&c,1) ) ); return 0; } - fc::string to_hex( const char* d, uint32_t s ) { - fc::string r; + std::string to_hex( const char* d, uint32_t s ) + { + std::string r; const char* to_hex="0123456789abcdef"; uint8_t* c = (uint8_t*)d; for( uint32_t i = 0; i < s; ++i ) @@ -38,5 +39,11 @@ namespace fc { } return out_pos - (uint8_t*)out_data; } + std::string to_hex( const std::vector& data ) + { + if( data.size() ) + return to_hex( data.data(), data.size() ); + return ""; + } } diff --git a/src/crypto/openssl.cpp b/src/crypto/openssl.cpp new file mode 100644 index 000000000..e3044ac8e --- /dev/null +++ b/src/crypto/openssl.cpp @@ -0,0 +1,55 @@ +#include + +#include + +#include + +#include +#include +#include + +namespace fc +{ + struct openssl_scope + { + static path _configurationFilePath; + openssl_scope() + { + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + + const boost::filesystem::path& boostPath = _configurationFilePath; + if(boostPath.empty() == false) + { + std::string varSetting("OPENSSL_CONF="); + varSetting += _configurationFilePath.to_native_ansi_path(); +#if defined(WIN32) + _putenv((char*)varSetting.c_str()); +#else + putenv((char*)varSetting.c_str()); +#endif + } + + OPENSSL_config(nullptr); + } + + ~openssl_scope() + { + EVP_cleanup(); + ERR_free_strings(); + } + }; + + path openssl_scope::_configurationFilePath; + + void store_configuration_path(const path& filePath) + { + openssl_scope::_configurationFilePath = filePath; + } + + int init_openssl() + { + static openssl_scope ossl; + return 0; + } +} diff --git a/src/crypto/pke.cpp b/src/crypto/pke.cpp new file mode 100644 index 000000000..a425621b7 --- /dev/null +++ b/src/crypto/pke.cpp @@ -0,0 +1,365 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc { + + namespace detail { + class pke_impl + { + public: + pke_impl():rsa(nullptr){} + ~pke_impl() + { + if( rsa != nullptr ) + RSA_free(rsa); + } + RSA* rsa; + }; + } // detail + + public_key::operator bool()const { return !!my; } + private_key::operator bool()const { return !!my; } + + public_key::public_key() + {} + + public_key::public_key( const bytes& d ) + :my( std::make_shared() ) + { + string pem = "-----BEGIN RSA PUBLIC KEY-----\n"; + auto b64 = fc::base64_encode( (const unsigned char*)d.data(), d.size() ); + for( size_t i = 0; i < b64.size(); i += 64 ) + pem += b64.substr( i, 64 ) + "\n"; + pem += "-----END RSA PUBLIC KEY-----\n"; + // fc::cerr<rsa = PEM_read_bio_RSAPublicKey(mem, NULL, NULL, NULL ); + BIO_free(mem); + } + public_key::public_key( const public_key& k ) + :my(k.my) + { + } + + public_key::public_key( public_key&& k ) + :my(std::move(k.my)) + { + } + + public_key::~public_key() { } + + public_key& public_key::operator=(const public_key& p ) + { + my = p.my; return *this; + } + public_key& public_key::operator=( public_key&& p ) + { + my = std::move(p.my); return *this; + } + bool public_key::verify( const sha1& digest, const array& sig )const + { + return 0 != RSA_verify( NID_sha1, (const uint8_t*)&digest, 20, + (uint8_t*)&sig, 2048/8, my->rsa ); + } + + bool public_key::verify( const sha1& digest, const signature& sig )const + { + assert( sig.size() == 2048/8 ); + return 0 != RSA_verify( NID_sha1, (const uint8_t*)&digest, 20, + (uint8_t*)sig.data(), 2048/8, my->rsa ); + } + bool public_key::verify( const sha256& digest, const signature& sig )const + { + assert( sig.size() == 2048/8 ); + return 0 != RSA_verify( NID_sha256, (const uint8_t*)&digest, 32, + (uint8_t*)sig.data(), 2048/8, my->rsa ); + } + bytes public_key::encrypt( const char* b, size_t l )const + { + FC_ASSERT( my && my->rsa ); + bytes out( RSA_size(my->rsa) ); //, char(0) ); + int rtn = RSA_public_encrypt( l, + (unsigned char*)b, + (unsigned char*)out.data(), + my->rsa, RSA_PKCS1_OAEP_PADDING ); + if( rtn >= 0 ) { + out.resize(rtn); + return out; + } + FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + + bytes public_key::encrypt( const bytes& in )const + { + FC_ASSERT( my && my->rsa ); + bytes out( RSA_size(my->rsa) ); //, char(0) ); + int rtn = RSA_public_encrypt( in.size(), + (unsigned char*)in.data(), + (unsigned char*)out.data(), + my->rsa, RSA_PKCS1_OAEP_PADDING ); + fc::cerr<<"rtn: "<= 0 ) { + out.resize(rtn); + return out; + } + FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + bytes public_key::decrypt( const bytes& in )const + { + FC_ASSERT( my && my->rsa ); + bytes out( RSA_size(my->rsa) );//, char(0) ); + int rtn = RSA_public_decrypt( in.size(), + (unsigned char*)in.data(), + (unsigned char*)out.data(), + my->rsa, RSA_PKCS1_OAEP_PADDING ); + if( rtn >= 0 ) { + out.resize(rtn); + return out; + } + FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + + bytes public_key::serialize()const + { + bytes ba; + if( !my ) { return ba; } + + BIO *mem = BIO_new(BIO_s_mem()); + int e = PEM_write_bio_RSAPublicKey( mem, my->rsa ); + if( e != 1 ) + { + BIO_free(mem); + FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + char* dat; + uint32_t l = BIO_get_mem_data( mem, &dat ); + + fc::stringstream ss( string( dat, l ) ); + fc::stringstream key; + fc::string tmp; + fc::getline( ss, tmp ); + fc::getline( ss, tmp ); + while( tmp.size() && tmp[0] != '-' ) + { + key << tmp; + fc::getline( ss, tmp ); + } + auto str = key.str(); + str = fc::base64_decode( str ); + ba = bytes( str.begin(), str.end() ); + + BIO_free(mem); + return ba; + } + + private_key::private_key() + { + } + private_key::private_key( const bytes& d ) + :my( std::make_shared() ) + { + + string pem = "-----BEGIN RSA PRIVATE KEY-----\n"; + auto b64 = fc::base64_encode( (const unsigned char*)d.data(), d.size() ); + for( size_t i = 0; i < b64.size(); i += 64 ) + pem += b64.substr( i, 64 ) + "\n"; + pem += "-----END RSA PRIVATE KEY-----\n"; + // fc::cerr<rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL ); + BIO_free(mem); + + FC_ASSERT( my->rsa, "read private key" ); + } + + private_key::private_key( const private_key& k ) + :my(k.my) + { + } + private_key::private_key( private_key&& k ) + :my(std::move(k.my) ) + { + } + private_key::~private_key() { } + + private_key& private_key::operator=(const private_key& p ) + { + my = p.my; return *this; + } + private_key& private_key::operator=(private_key&& p ) + { + my = std::move(p.my); return *this; + } + + void private_key::sign( const sha1& digest, array& sig )const + { + FC_ASSERT( (size_t(RSA_size(my->rsa)) <= sizeof(sig)), "Invalid RSA size" ); + uint32_t slen = 0; + if( 1 != RSA_sign( NID_sha1, (uint8_t*)&digest, + 20, (unsigned char*)&sig, &slen, my->rsa ) ) + { + FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + } + + signature private_key::sign( const sha1& digest )const + { + if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" ); + signature sig; + sig.resize( RSA_size(my->rsa) ); + + uint32_t slen = 0; + if( 1 != RSA_sign( NID_sha1, (uint8_t*)digest.data(), + 20, (unsigned char*)sig.data(), &slen, my->rsa ) ) + { + FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + return sig; + } + signature private_key::sign( const sha256& digest )const + { + if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" ); + signature sig; + sig.resize( RSA_size(my->rsa) ); + + uint32_t slen = 0; + if( 1 != RSA_sign( NID_sha256, (uint8_t*)digest.data(), + 32, (unsigned char*)sig.data(), &slen, my->rsa ) ) + { + FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + return sig; + } + + + bytes private_key::encrypt( const bytes& in )const + { + if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" ); + bytes out; + out.resize( RSA_size(my->rsa) ); + int rtn = RSA_private_encrypt( in.size(), + (unsigned char*)in.data(), + (unsigned char*)out.data(), + my->rsa, RSA_PKCS1_OAEP_PADDING ); + if( rtn >= 0 ) { + out.resize(rtn); + return out; + } + + FC_THROW_EXCEPTION( exception, "encrypt failed" ); + } + + bytes private_key::decrypt( const char* in, size_t l )const + { + if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" ); + bytes out; + out.resize( RSA_size(my->rsa) ); + int rtn = RSA_private_decrypt( l, + (unsigned char*)in, + (unsigned char*)out.data(), + my->rsa, RSA_PKCS1_OAEP_PADDING ); + if( rtn >= 0 ) { + out.resize(rtn); + return out; + } + FC_THROW_EXCEPTION( exception, "decrypt failed" ); + } + bytes private_key::decrypt( const bytes& in )const + { + if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" ); + bytes out; + out.resize( RSA_size(my->rsa) ); + int rtn = RSA_private_decrypt( in.size(), + (unsigned char*)in.data(), + (unsigned char*)out.data(), + my->rsa, RSA_PKCS1_OAEP_PADDING ); + if( rtn >= 0 ) { + out.resize(rtn); + return out; + } + FC_THROW_EXCEPTION( exception, "decrypt failed" ); + } + + bytes private_key::serialize()const + { + bytes ba; + if( !my ) { return ba; } + + BIO *mem = BIO_new(BIO_s_mem()); + int e = PEM_write_bio_RSAPrivateKey( mem, my->rsa, NULL, NULL, 0, NULL, NULL ); + if( e != 1 ) + { + BIO_free(mem); + FC_THROW_EXCEPTION( exception, "Error writing private key, ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) ); + } + char* dat; + uint32_t l = BIO_get_mem_data( mem, &dat ); + // return bytes( dat, dat + l ); + + stringstream ss( string( dat, l ) ); + stringstream key; + string tmp; + fc::getline( ss, tmp ); + fc::getline( ss, tmp ); + + while( tmp.size() && tmp[0] != '-' ) + { + key << tmp; + fc::getline( ss, tmp ); + } + auto str = key.str(); + str = fc::base64_decode( str ); + ba = bytes( str.begin(), str.end() ); + // ba = bytes( dat, dat + l ); + BIO_free(mem); + return ba; + } + + void generate_key_pair( public_key& pub, private_key& priv ) + { + static bool init = true; + if( init ) { ERR_load_crypto_strings(); init = false; } + + pub.my = std::make_shared(); + priv.my = pub.my; + pub.my->rsa = RSA_generate_key( 2048, 65537, NULL, NULL ); + } + + /** encodes the big int as base64 string, or a number */ + void to_variant( const public_key& bi, variant& v ) + { + v = bi.serialize(); + } + + /** decodes the big int as base64 string, or a number */ + void from_variant( const variant& v, public_key& bi ) + { + bi = public_key( v.as >() ); + } + + + /** encodes the big int as base64 string, or a number */ + void to_variant( const private_key& bi, variant& v ) + { + v = bi.serialize(); + } + + /** decodes the big int as base64 string, or a number */ + void from_variant( const variant& v, private_key& bi ) + { + bi = private_key( v.as >() ); + } + +} // fc diff --git a/src/crypto/rand.cpp b/src/crypto/rand.cpp new file mode 100644 index 000000000..7235ab689 --- /dev/null +++ b/src/crypto/rand.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + + +namespace fc { + +void rand_bytes(char* buf, int count) +{ + static int init = init_openssl(); + + int result = RAND_bytes((unsigned char*)buf, count); + if (result != 1) + FC_THROW("Error calling OpenSSL's RAND_bytes(): ${code}", ("code", (uint32_t)ERR_get_error())); +} + +void rand_pseudo_bytes(char* buf, int count) +{ + static int init = init_openssl(); + + int result = RAND_pseudo_bytes((unsigned char*)buf, count); + if (result == -1) + FC_THROW("Error calling OpenSSL's RAND_pseudo_bytes(): ${code}", ("code", (uint32_t)ERR_get_error())); +} + +} // namespace fc diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp new file mode 100644 index 000000000..e336b3915 --- /dev/null +++ b/src/crypto/ripemd160.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "_digest_common.hpp" + +namespace fc +{ + +ripemd160::ripemd160() { memset( _hash, 0, sizeof(_hash) ); } +ripemd160::ripemd160( const string& hex_str ) { + fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); +} + +string ripemd160::str()const { + return fc::to_hex( (char*)_hash, sizeof(_hash) ); +} +ripemd160::operator string()const { return str(); } + +char* ripemd160::data()const { return (char*)&_hash[0]; } + + +struct ripemd160::encoder::impl { + impl() + { + memset( (char*)&ctx, 0, sizeof(ctx) ); + } + RIPEMD160_CTX ctx; +}; + +ripemd160::encoder::~encoder() {} +ripemd160::encoder::encoder() { + reset(); +} + +ripemd160 ripemd160::hash( const fc::sha512& h ) +{ + return hash( (const char*)&h, sizeof(h) ); +} +ripemd160 ripemd160::hash( const fc::sha256& h ) +{ + return hash( (const char*)&h, sizeof(h) ); +} +ripemd160 ripemd160::hash( const char* d, uint32_t dlen ) { + encoder e; + e.write(d,dlen); + return e.result(); +} +ripemd160 ripemd160::hash( const string& s ) { + return hash( s.c_str(), s.size() ); +} + +void ripemd160::encoder::write( const char* d, uint32_t dlen ) { + RIPEMD160_Update( &my->ctx, d, dlen); +} +ripemd160 ripemd160::encoder::result() { + ripemd160 h; + RIPEMD160_Final((uint8_t*)h.data(), &my->ctx ); + return h; +} +void ripemd160::encoder::reset() { + RIPEMD160_Init( &my->ctx); +} + +ripemd160 operator << ( const ripemd160& h1, uint32_t i ) { + ripemd160 result; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); + return result; +} +ripemd160 operator ^ ( const ripemd160& h1, const ripemd160& h2 ) { + ripemd160 result; + result._hash[0] = h1._hash[0] ^ h2._hash[0]; + result._hash[1] = h1._hash[1] ^ h2._hash[1]; + result._hash[2] = h1._hash[2] ^ h2._hash[2]; + result._hash[3] = h1._hash[3] ^ h2._hash[3]; + result._hash[4] = h1._hash[4] ^ h2._hash[4]; + return result; +} +bool operator >= ( const ripemd160& h1, const ripemd160& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0; +} +bool operator > ( const ripemd160& h1, const ripemd160& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0; +} +bool operator < ( const ripemd160& h1, const ripemd160& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0; +} +bool operator != ( const ripemd160& h1, const ripemd160& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0; +} +bool operator == ( const ripemd160& h1, const ripemd160& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0; +} + + void to_variant( const ripemd160& bi, variant& v ) + { + v = std::vector( (const char*)&bi, ((const char*)&bi) + sizeof(bi) ); + } + void from_variant( const variant& v, ripemd160& bi ) + { + std::vector ve = v.as< std::vector >(); + if( ve.size() ) + { + memcpy(&bi, ve.data(), fc::min(ve.size(),sizeof(bi)) ); + } + else + memset( &bi, char(0), sizeof(bi) ); + } + +} // fc diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp new file mode 100644 index 000000000..88107db9d --- /dev/null +++ b/src/crypto/sha1.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include "_digest_common.hpp" + +namespace fc +{ + +sha1::sha1() { memset( _hash, 0, sizeof(_hash) ); } +sha1::sha1( const string& hex_str ) { + fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); +} + +string sha1::str()const { + return fc::to_hex( (char*)_hash, sizeof(_hash) ); +} +sha1::operator string()const { return str(); } + +char* sha1::data()const { return (char*)&_hash[0]; } + + +struct sha1::encoder::impl { + SHA_CTX ctx; +}; + +sha1::encoder::~encoder() {} +sha1::encoder::encoder() { + reset(); +} + +sha1 sha1::hash( const char* d, uint32_t dlen ) { + encoder e; + e.write(d,dlen); + return e.result(); +} +sha1 sha1::hash( const string& s ) { + return hash( s.c_str(), s.size() ); +} + +void sha1::encoder::write( const char* d, uint32_t dlen ) { + SHA1_Update( &my->ctx, d, dlen); +} +sha1 sha1::encoder::result() { + sha1 h; + SHA1_Final((uint8_t*)h.data(), &my->ctx ); + return h; +} +void sha1::encoder::reset() { + SHA1_Init( &my->ctx); +} + +sha1 operator << ( const sha1& h1, uint32_t i ) { + sha1 result; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); + return result; +} +sha1 operator ^ ( const sha1& h1, const sha1& h2 ) { + sha1 result; + result._hash[0] = h1._hash[0] ^ h2._hash[0]; + result._hash[1] = h1._hash[1] ^ h2._hash[1]; + result._hash[2] = h1._hash[2] ^ h2._hash[2]; + result._hash[3] = h1._hash[3] ^ h2._hash[3]; + result._hash[4] = h1._hash[4] ^ h2._hash[4]; + return result; +} +bool operator >= ( const sha1& h1, const sha1& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0; +} +bool operator > ( const sha1& h1, const sha1& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0; +} +bool operator < ( const sha1& h1, const sha1& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0; +} +bool operator != ( const sha1& h1, const sha1& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0; +} +bool operator == ( const sha1& h1, const sha1& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0; +} + + void to_variant( const sha1& bi, variant& v ) + { + v = std::vector( (const char*)&bi, ((const char*)&bi) + sizeof(bi) ); + } + void from_variant( const variant& v, sha1& bi ) + { + std::vector ve = v.as< std::vector >(); + if( ve.size() ) + { + memcpy(&bi, ve.data(), fc::min(ve.size(),sizeof(bi)) ); + } + else + memset( &bi, char(0), sizeof(bi) ); + } + +} // fc diff --git a/src/crypto/sha224.cpp b/src/crypto/sha224.cpp new file mode 100644 index 000000000..0ae170712 --- /dev/null +++ b/src/crypto/sha224.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include "_digest_common.hpp" + +namespace fc { + + sha224::sha224() { memset( _hash, 0, sizeof(_hash) ); } + sha224::sha224( const string& hex_str ) { + fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); + } + + string sha224::str()const { + return fc::to_hex( (char*)_hash, sizeof(_hash) ); + } + sha224::operator string()const { return str(); } + + char* sha224::data()const { return (char*)&_hash[0]; } + + + struct sha224::encoder::impl { + SHA256_CTX ctx; + }; + + sha224::encoder::~encoder() {} + sha224::encoder::encoder() { + reset(); + } + + sha224 sha224::hash( const char* d, uint32_t dlen ) { + encoder e; + e.write(d,dlen); + return e.result(); + } + sha224 sha224::hash( const string& s ) { + return hash( s.c_str(), s.size() ); + } + + void sha224::encoder::write( const char* d, uint32_t dlen ) { + SHA224_Update( &my->ctx, d, dlen); + } + sha224 sha224::encoder::result() { + sha224 h; + SHA224_Final((uint8_t*)h.data(), &my->ctx ); + return h; + } + void sha224::encoder::reset() { + SHA224_Init( &my->ctx); + } + + sha224 operator << ( const sha224& h1, uint32_t i ) { + sha224 result; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); + return result; + } + sha224 operator ^ ( const sha224& h1, const sha224& h2 ) { + sha224 result; + for( uint32_t i = 0; i < 7; ++i ) + result._hash[i] = h1._hash[i] ^ h2._hash[i]; + return result; + } + bool operator >= ( const sha224& h1, const sha224& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(sha224) ) >= 0; + } + bool operator > ( const sha224& h1, const sha224& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(sha224) ) > 0; + } + bool operator < ( const sha224& h1, const sha224& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(sha224) ) < 0; + } + bool operator != ( const sha224& h1, const sha224& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(sha224) ) != 0; + } + bool operator == ( const sha224& h1, const sha224& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(sha224) ) == 0; + } + + void to_variant( const sha224& bi, variant& v ) + { + v = std::vector( (const char*)&bi, ((const char*)&bi) + sizeof(bi) ); + } + void from_variant( const variant& v, sha224& bi ) + { + std::vector ve = v.as< std::vector >(); + if( ve.size() ) + { + memcpy(&bi, ve.data(), fc::min(ve.size(),sizeof(bi)) ); + } + else + memset( &bi, char(0), sizeof(bi) ); + } + + template<> + unsigned int hmac::internal_block_size() const { return 64; } +} diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp new file mode 100644 index 000000000..9c3b9e6fc --- /dev/null +++ b/src/crypto/sha256.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "_digest_common.hpp" + +namespace fc { + + sha256::sha256() { memset( _hash, 0, sizeof(_hash) ); } + sha256::sha256( const char *data, size_t size ) { + if (size != sizeof(_hash)) + FC_THROW_EXCEPTION( exception, "sha256: size mismatch" ); + memcpy(_hash, data, size ); + } + sha256::sha256( const string& hex_str ) { + fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); + } + + string sha256::str()const { + return fc::to_hex( (char*)_hash, sizeof(_hash) ); + } + sha256::operator string()const { return str(); } + + char* sha256::data()const { return (char*)&_hash[0]; } + + + struct sha256::encoder::impl { + SHA256_CTX ctx; + }; + + sha256::encoder::~encoder() {} + sha256::encoder::encoder() { + reset(); + } + + sha256 sha256::hash( const char* d, uint32_t dlen ) { + encoder e; + e.write(d,dlen); + return e.result(); + } + + sha256 sha256::hash( const string& s ) { + return hash( s.c_str(), s.size() ); + } + + sha256 sha256::hash( const sha256& s ) + { + return hash( s.data(), sizeof( s._hash ) ); + } + + void sha256::encoder::write( const char* d, uint32_t dlen ) { + SHA256_Update( &my->ctx, d, dlen); + } + sha256 sha256::encoder::result() { + sha256 h; + SHA256_Final((uint8_t*)h.data(), &my->ctx ); + return h; + } + void sha256::encoder::reset() { + SHA256_Init( &my->ctx); + } + + sha256 operator << ( const sha256& h1, uint32_t i ) { + sha256 result; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); + return result; + } + sha256 operator >> ( const sha256& h1, uint32_t i ) { + sha256 result; + fc::detail::shift_r( h1.data(), result.data(), result.data_size(), i ); + return result; + } + sha256 operator ^ ( const sha256& h1, const sha256& h2 ) { + sha256 result; + result._hash[0] = h1._hash[0] ^ h2._hash[0]; + result._hash[1] = h1._hash[1] ^ h2._hash[1]; + result._hash[2] = h1._hash[2] ^ h2._hash[2]; + result._hash[3] = h1._hash[3] ^ h2._hash[3]; + return result; + } + bool operator >= ( const sha256& h1, const sha256& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0; + } + bool operator > ( const sha256& h1, const sha256& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0; + } + bool operator < ( const sha256& h1, const sha256& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0; + } + bool operator != ( const sha256& h1, const sha256& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0; + } + bool operator == ( const sha256& h1, const sha256& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0; + } + + void to_variant( const sha256& bi, variant& v ) + { + v = std::vector( (const char*)&bi, ((const char*)&bi) + sizeof(bi) ); + } + void from_variant( const variant& v, sha256& bi ) + { + std::vector ve = v.as< std::vector >(); + if( ve.size() ) + { + memcpy(&bi, ve.data(), fc::min(ve.size(),sizeof(bi)) ); + } + else + memset( &bi, char(0), sizeof(bi) ); + } + + uint64_t hash64(const char* buf, size_t len) + { + sha256 sha_value = sha256::hash(buf,len); + return sha_value._hash[0]; + } + + template<> + unsigned int hmac::internal_block_size() const { return 64; } +} //end namespace fc diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp new file mode 100644 index 000000000..7e0315ccf --- /dev/null +++ b/src/crypto/sha512.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include "_digest_common.hpp" + +namespace fc { + + sha512::sha512() { memset( _hash, 0, sizeof(_hash) ); } + sha512::sha512( const string& hex_str ) { + fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); + } + + string sha512::str()const { + return fc::to_hex( (char*)_hash, sizeof(_hash) ); + } + sha512::operator string()const { return str(); } + + char* sha512::data()const { return (char*)&_hash[0]; } + + + struct sha512::encoder::impl { + SHA512_CTX ctx; + }; + + sha512::encoder::~encoder() {} + sha512::encoder::encoder() { + reset(); + } + + sha512 sha512::hash( const char* d, uint32_t dlen ) { + encoder e; + e.write(d,dlen); + return e.result(); + } + sha512 sha512::hash( const string& s ) { + return hash( s.c_str(), s.size() ); + } + + void sha512::encoder::write( const char* d, uint32_t dlen ) { + SHA512_Update( &my->ctx, d, dlen); + } + sha512 sha512::encoder::result() { + sha512 h; + SHA512_Final((uint8_t*)h.data(), &my->ctx ); + return h; + } + void sha512::encoder::reset() { + SHA512_Init( &my->ctx); + } + + sha512 operator << ( const sha512& h1, uint32_t i ) { + sha512 result; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); + return result; + } + sha512 operator ^ ( const sha512& h1, const sha512& h2 ) { + sha512 result; + result._hash[0] = h1._hash[0] ^ h2._hash[0]; + result._hash[1] = h1._hash[1] ^ h2._hash[1]; + result._hash[2] = h1._hash[2] ^ h2._hash[2]; + result._hash[3] = h1._hash[3] ^ h2._hash[3]; + result._hash[4] = h1._hash[4] ^ h2._hash[4]; + result._hash[5] = h1._hash[5] ^ h2._hash[5]; + result._hash[6] = h1._hash[6] ^ h2._hash[6]; + result._hash[7] = h1._hash[7] ^ h2._hash[7]; + return result; + } + bool operator >= ( const sha512& h1, const sha512& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0; + } + bool operator > ( const sha512& h1, const sha512& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0; + } + bool operator < ( const sha512& h1, const sha512& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0; + } + bool operator != ( const sha512& h1, const sha512& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0; + } + bool operator == ( const sha512& h1, const sha512& h2 ) { + return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0; + } + + void to_variant( const sha512& bi, variant& v ) + { + v = std::vector( (const char*)&bi, ((const char*)&bi) + sizeof(bi) ); + } + void from_variant( const variant& v, sha512& bi ) + { + std::vector ve = v.as< std::vector >(); + if( ve.size() ) + { + memcpy(&bi, ve.data(), fc::min(ve.size(),sizeof(bi)) ); + } + else + memset( &bi, char(0), sizeof(bi) ); + } + + template<> + unsigned int hmac::internal_block_size() const { return 128; } +} diff --git a/src/error_report.cpp b/src/error_report.cpp deleted file mode 100644 index a6ac843ae..000000000 --- a/src/error_report.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -namespace fc { - -error_frame::error_frame( const fc::string& f, uint64_t l, const fc::string& m, const fc::string& d, fc::value met ) -:desc(d),file(fc::path(f).filename().generic_string()),line(l),method(m),meta(fc::move(met)),time(fc::time_point::now()),detail(false){} - -error_frame::error_frame( bool is_detail, const fc::string& f, uint64_t l, const fc::string& m, const fc::string& d, fc::value met ) -:desc(d),file(fc::path(f).filename().generic_string()),line(l),method(m),meta(fc::move(met)),time(fc::time_point::now()),detail(is_detail){} -error_report::error_report() -{ -} -error_frame::error_frame(const fc::error_frame& e) -:desc(e.desc),file(e.file),line(e.line),method(e.method),meta(e.meta),time(e.time),detail(e.detail){} - -error_frame::error_frame(fc::error_frame&& e) -:desc(fc::move(e.desc)), - file(fc::move(e.file)), - line(e.line), - method(fc::move(e.method)), - meta(fc::move(e.meta)), - time(e.time), - detail(e.detail) - {} - -fc::error_frame& fc::error_frame::operator=(const fc::error_frame& f ) { - auto tmp = f; - fc_swap( tmp, *this ); - return *this; -} -fc::error_frame& fc::error_frame::operator=(fc::error_frame&& e ) -{ - desc=fc::move(e.desc); - file=fc::move(e.file); - line=fc::move(e.line); - method=fc::move(e.method); - time=e.time; - meta=fc::move(e.meta); - detail = e.detail; - return *this; -} - -error_report::error_report( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta ) -{ - push_frame( file, line, method, desc, meta ); -} - - -fc::error_frame& error_report::current() -{ - if( !stack.size() ) stack.resize(1); - return stack.back(); -} - -fc::error_report& error_report::pop_frame() -{ - stack.pop_back(); - return *this; -} - -fc::error_report& error_report::push_frame( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta ) -{ - stack.push_back( fc::error_frame( file, line, method, desc, meta ) ); - return *this; -} -fc::error_report& error_report::push_frame( bool detail, const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta ) -{ - stack.push_back( fc::error_frame( detail, file, line, method, desc, meta ) ); - return *this; -} - -fc::error_report& error_report::append( const error_report& e ) -{ - // TODO: what to do about the 'failure...?' - stack.reserve( stack.size()+e.stack.size()); - for( uint32_t i = 0; i < e.stack.size(); ++i ) { - stack.push_back( e.stack[i] ); - } - return *this; -} - -fc::string error_frame::to_detail_string()const { - fc::stringstream ss; - ss << to_string() << "\n\t"; - ss << file << ":" << line << "\t"<val.is_string() ) { - ss<val.cast(); - } else { - ss << fc::json::to_string( itr->val ); - } - } else { - ss << "${"<(), keys ); - } - else if( in.is_object() ) { - for( auto itr = in.begin(); itr != in.end(); ++itr ) { - out[fc::substitute(itr->key, keys)] = recursive_substitute( itr->val, keys ); - } - return out; - } - else if( in.is_array() ) { - for( size_t i = 0; i < in.size(); ++i ) { - out.push_back( recursive_substitute( in[i], keys ) ); - } - } - return in; -} - -fc::string error_report::to_string()const { - fc::stringstream ss; - for( uint32_t i = 0; i < stack.size(); ++i ) { - ss << stack[i].to_string() << "\n"; - } - return ss.str(); -} -fc::string error_report::to_detail_string()const { - fc::stringstream ss; - for( uint32_t i = 0; i < stack.size(); ++i ) { - ss << stack[i].to_detail_string() << "\n"; - } - return ss.str(); -} -fc::exception_ptr error_report::copy_exception() { - return boost::copy_exception(*this); -} - -} // namespace fc diff --git a/src/exception.cpp b/src/exception.cpp index 9b1f64ddd..fef482af6 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -1,109 +1,267 @@ -#include -#include +#include #include -#include -#include - -namespace fc { - #define bexcept void* e = &my[0]; (*((boost::exception_ptr*)e)) - #define cbexcept const void* e = &my[0]; (*((const boost::exception_ptr*)e)) - - exception_ptr::exception_ptr() { - new (&my[0]) boost::exception_ptr(); - } - exception_ptr::exception_ptr( const boost::exception_ptr& c ){ - static_assert( sizeof(my) >= sizeof(c), "boost::exception_ptr is larger than space reserved for it" ); - new (&my[0]) boost::exception_ptr(c); - } - exception_ptr::exception_ptr( boost::exception_ptr&& c ){ - new (&my[0]) boost::exception_ptr(fc::move(c)); - } - exception_ptr::exception_ptr( const exception_ptr& c ){ - new (&my[0]) boost::exception_ptr(c); - } - exception_ptr::exception_ptr( exception_ptr&& c ){ - new (&my[0]) boost::exception_ptr(fc::move(c)); - } - exception_ptr::~exception_ptr(){ - bexcept.~exception_ptr(); - } - exception_ptr& exception_ptr::operator=(const boost::exception_ptr& c){ - bexcept = c; - return *this; - } - exception_ptr& exception_ptr::operator=(boost::exception_ptr&& c){ - bexcept = fc::move(c); - return *this; - } - - exception_ptr& exception_ptr::operator=(const exception_ptr& c){ - bexcept = c; - return *this; - } - exception_ptr& exception_ptr::operator=(exception_ptr&& c){ - bexcept = fc::move(c); - return *this; - } - - fc::string exception_ptr::diagnostic_information()const{ - const void* e = &my[0]; - return boost::diagnostic_information( *((const boost::exception_ptr*)e) ).c_str(); - } - - exception_ptr::operator const boost::exception_ptr& ()const{ - const void* e = &my[0]; - return (*((const boost::exception_ptr*)e)); - } - exception_ptr::operator boost::exception_ptr& (){ - void* e = &my[0]; - return (*((boost::exception_ptr*)e)); - } - - exception_ptr current_exception() { - return boost::current_exception(); - } - void rethrow_exception( const exception_ptr& e ) { - boost::rethrow_exception( static_cast(e) ); - } - exception_ptr::operator bool()const { - const void* e = &my[0]; - return (*((boost::exception_ptr*)e)); - } - - - fc::string to_string( char v ) { return fc::string(&v,1); } - fc::string to_string( uint64_t v ) { return fc::lexical_cast(v).c_str(); } - fc::string to_string( int64_t v ){ return fc::lexical_cast(v).c_str(); } - fc::string to_string( double v ){ return fc::lexical_cast(v).c_str(); } - fc::string to_string( float v ){ return fc::lexical_cast(v).c_str(); } - fc::string to_string( int32_t v ){ return fc::lexical_cast(v).c_str(); } - fc::string to_string( uint32_t v ){ return fc::lexical_cast(v).c_str(); } - fc::string to_string( int16_t v ){ return fc::lexical_cast(v).c_str(); } - fc::string to_string( uint16_t v ){ return fc::lexical_cast(v).c_str(); } -// fc::string to_string( size_t v ){ return fc::lexical_cast(v).c_str(); } - //fc::string to_string( long int v ){ return fc::lexical_cast(v).c_str(); } - - void throw_exception( const char* func, const char* file, int line, const char* msg ) { - ::boost::exception_detail::throw_exception_(fc::generic_exception(msg),func, file, line ); - } - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1 ) { - ::boost::exception_detail::throw_exception_(fc::generic_exception( (boost::format(msg) % a1.c_str() ).str().c_str()) ,func, file, line ); - } - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1, const fc::string& a2 ) { - ::boost::exception_detail::throw_exception_(fc::generic_exception( (boost::format(msg) % a1.c_str() %a2.c_str()).str().c_str()),func, file, line ); - } - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1, const fc::string& a2, const fc::string& a3 ) { - ::boost::exception_detail::throw_exception_(fc::generic_exception(msg),func, file, line ); - } - void throw_exception_( const char* func, const char* file, int line, const char* msg, - const fc::string& a1, const fc::string& a2, const fc::string& a3, const fc::string& a4 ) { - ::boost::exception_detail::throw_exception_(fc::generic_exception(msg),func, file, line ); - } - fc::string except_str() { return boost::current_exception_diagnostic_information(); }//boost::current_exception_diagonstic_information(); } - //fc::current_exception().diagnostic_information(); } - -} +#include +#include +#include +#include + +namespace fc +{ + FC_REGISTER_EXCEPTIONS( (timeout_exception) + (file_not_found_exception) + (parse_error_exception) + (invalid_arg_exception) + (invalid_operation_exception) + (key_not_found_exception) + (bad_cast_exception) + (out_of_range_exception) + (canceled_exception) + (assert_exception) + (eof_exception) + (unknown_host_exception) + (null_optional) + (udt_exception) + (aes_exception) + (overflow_exception) + (underflow_exception) + (divide_by_zero_exception) + ) + + namespace detail + { + class exception_impl + { + public: + std::string _name; + std::string _what; + int64_t _code; + log_messages _elog; + }; + } + exception::exception( log_messages&& msgs, int64_t code, + const std::string& name_value, + const std::string& what_value ) + :my( new detail::exception_impl() ) + { + my->_code = code; + my->_what = what_value; + my->_name = name_value; + my->_elog = fc::move(msgs); + } + + exception::exception( + const log_messages& msgs, + int64_t code, + const std::string& name_value, + const std::string& what_value ) + :my( new detail::exception_impl() ) + { + my->_code = code; + my->_what = what_value; + my->_name = name_value; + my->_elog = msgs; + } + + unhandled_exception::unhandled_exception( log_message&& m, std::exception_ptr e ) + :exception( fc::move(m) ) + { + _inner = e; + } + unhandled_exception::unhandled_exception( const exception& r ) + :exception(r) + { + } + unhandled_exception::unhandled_exception( log_messages m ) + :exception() + { my->_elog = fc::move(m); } + + std::exception_ptr unhandled_exception::get_inner_exception()const { return _inner; } + + NO_RETURN void unhandled_exception::dynamic_rethrow_exception()const + { + if( !(_inner == std::exception_ptr()) ) std::rethrow_exception( _inner ); + else { fc::exception::dynamic_rethrow_exception(); } + } + + std::shared_ptr unhandled_exception::dynamic_copy_exception()const + { + auto e = std::make_shared( *this ); + e->_inner = _inner; + return e; + } + + exception::exception( int64_t code, + const std::string& name_value, + const std::string& what_value ) + :my( new detail::exception_impl() ) + { + my->_code = code; + my->_what = what_value; + my->_name = name_value; + } + + exception::exception( log_message&& msg, + int64_t code, + const std::string& name_value, + const std::string& what_value ) + :my( new detail::exception_impl() ) + { + my->_code = code; + my->_what = what_value; + my->_name = name_value; + my->_elog.push_back( fc::move( msg ) ); + } + exception::exception( const exception& c ) + :my( new detail::exception_impl(*c.my) ) + { } + exception::exception( exception&& c ) + :my( fc::move(c.my) ){} + + const char* exception::name()const throw() { return my->_name.c_str(); } + const char* exception::what()const throw() { return my->_what.c_str(); } + int64_t exception::code()const throw() { return my->_code; } + + exception::~exception(){} + + void to_variant( const exception& e, variant& v ) + { + v = mutable_variant_object( "code", e.code() ) + ( "name", e.name() ) + ( "message", e.what() ) + ( "stack", e.get_log() ); + + } + void from_variant( const variant& v, exception& ll ) + { + auto obj = v.get_object(); + if( obj.contains( "stack" ) ) + ll.my->_elog = obj["stack"].as(); + if( obj.contains( "code" ) ) + ll.my->_code = obj["code"].as_int64(); + if( obj.contains( "name" ) ) + ll.my->_name = obj["name"].as_string(); + if( obj.contains( "message" ) ) + ll.my->_what = obj["message"].as_string(); + } + + const log_messages& exception::get_log()const { return my->_elog; } + void exception::append_log( log_message m ) + { + my->_elog.emplace_back( fc::move(m) ); + } + + /** + * Generates a detailed string including file, line, method, + * and other information that is generally only useful for + * developers. + */ + string exception::to_detail_string( log_level ll )const + { + fc::stringstream ss; + ss << variant(my->_code).as_string() <<" " << my->_name << ": " <_what<<"\n"; + for( auto itr = my->_elog.begin(); itr != my->_elog.end(); ) + { + ss << itr->get_message() <<"\n"; //fc::format_string( itr->get_format(), itr->get_data() ) <<"\n"; + ss << " " << json::to_string( itr->get_data() )<<"\n"; + ss << " " << itr->get_context().to_string(); + ++itr; + if( itr != my->_elog.end() ) ss<<"\n"; + } + return ss.str(); + } + + /** + * Generates a user-friendly error report. + */ + string exception::to_string( log_level ll )const + { + fc::stringstream ss; + ss << what() << " (" << variant(my->_code).as_string() <<")\n"; + for( auto itr = my->_elog.begin(); itr != my->_elog.end(); ++itr ) + { + ss << fc::format_string( itr->get_format(), itr->get_data() ) <<"\n"; + // ss << " " << itr->get_context().to_string() <<"\n"; + } + return ss.str(); + } + + void NO_RETURN exception_factory::rethrow( const exception& e )const + { + auto itr = _registered_exceptions.find( e.code() ); + if( itr != _registered_exceptions.end() ) + itr->second->rethrow( e ); + throw e; + } + /** + * Rethrows the exception restoring the proper type based upon + * the error code. This is used to propagate exception types + * across conversions to/from JSON + */ + NO_RETURN void exception::dynamic_rethrow_exception()const + { + exception_factory::instance().rethrow( *this ); + } + + exception_ptr exception::dynamic_copy_exception()const + { + return std::make_shared(*this); + } + + fc::string except_str() + { + return boost::current_exception_diagnostic_information(); + } + + void throw_bad_enum_cast( int64_t i, const char* e ) + { + FC_THROW_EXCEPTION( bad_cast_exception, + "invalid index '${key}' in enum '${enum}'", + ("key",i)("enum",e) ); + } + void throw_bad_enum_cast( const char* k, const char* e ) + { + FC_THROW_EXCEPTION( bad_cast_exception, + "invalid name '${key}' in enum '${enum}'", + ("key",k)("enum",e) ); + } + + bool assert_optional(bool is_valid ) + { + if( !is_valid ) + throw null_optional(); + return true; + } + exception& exception::operator=( const exception& copy ) + { + *my = *copy.my; + return *this; + } + + exception& exception::operator=( exception&& copy ) + { + my = std::move(copy.my); + return *this; + } + + void record_assert_trip( + const char* filename, + uint32_t lineno, + const char* expr + ) + { + fc::mutable_variant_object assert_trip_info = + fc::mutable_variant_object() + ("source_file", filename) + ("source_lineno", lineno) + ("expr", expr) + ; + std::cout + << "FC_ASSERT triggered: " + << fc::json::to_string( assert_trip_info ) << "\n"; + return; + } + + bool enable_record_assert_trip = false; + +} // fc diff --git a/src/file_appender.cpp b/src/file_appender.cpp deleted file mode 100644 index 7e9b6ad96..000000000 --- a/src/file_appender.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - class file_appender::impl : public fc::retainable { - public: - config cfg; - ofstream out; - boost::mutex slock; - }; - file_appender::config::config( const fc::path& p ) - :format( "${when} ${thread} ${context} ${file}:${line} ${method} ${level}] ${message}" ), - filename(p),flush(true),truncate(true){} - - file_appender::file_appender( const value& args ) - :my( new impl() ) - { - try { - my->cfg = fc::value_cast(args); - my->out.open( my->cfg.filename.string().c_str() ); - } catch ( ... ) { - elog( "%s", fc::except_str().c_str() ); - } - } - file_appender::~file_appender(){} - - void file_appender::log( const log_message& m ) - { - fc::string message = fc::substitute( m.format, m.args ); - fc::value lmsg(m); - - fc::string fmt_str = fc::substitute( my->cfg.format, value(m).set( "message", message) ); - { - fc::scoped_lock lock(my->slock); - my->out << fmt_str << "\n"; - } - if( my->cfg.flush ) my->out.flush(); - } -} diff --git a/src/file_mapping.cpp b/src/file_mapping.cpp deleted file mode 100644 index 0d68fb85a..000000000 --- a/src/file_mapping.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include - -namespace fc { - file_mapping::file_mapping( const char* file, mode_t m ) - :my(file, m == read_only ? boost::interprocess::read_only : boost::interprocess::read_write ){} - file_mapping::~file_mapping(){} - - - - mapped_region::mapped_region( const file_mapping& fm, mode_t m, size_t start, size_t size ) - :my( *fm.my, m == read_only ? boost::interprocess::read_only : boost::interprocess::read_write ,start, size) { } - mapped_region::mapped_region( const file_mapping& fm, mode_t m ) - :my( *fm.my, m == read_only ? boost::interprocess::read_only : boost::interprocess::read_write) { } - mapped_region::~mapped_region(){} - void* mapped_region::get_address()const { return my->get_address(); } - void mapped_region::flush(){ my->flush(); } - size_t mapped_region::get_size()const { return my->get_size(); } -} diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 6980572c0..a590065f1 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -1,20 +1,54 @@ //#define BOOST_NO_SCOPED_ENUMS #include +#include #include #include +#include + +#include +#include + #include #include -#include -#include + +#ifdef WIN32 +# include +# include +# include +#else + #include + #include + #include +# ifdef FC_HAS_SIMPLE_FILE_LOCK + #include + #include +# endif +#endif namespace fc { - void pack( fc::value& v, const fc::path& s ) { - v = s.generic_string(); + // when converting to and from a variant, store utf-8 in the variant + void to_variant( const fc::path& path_to_convert, variant& variant_output ) + { + std::wstring wide_string = path_to_convert.generic_wstring(); + std::string utf8_string; + fc::encodeUtf8(wide_string, &utf8_string); + variant_output = utf8_string; + + //std::string path = t.to_native_ansi_path(); + //std::replace(path.begin(), path.end(), '\\', '/'); + //v = path; } - void unpack( const fc::value& v, fc::path& s ) { - s = path(fc::value_cast(v)); + + void from_variant( const fc::variant& variant_to_convert, fc::path& path_output ) + { + std::wstring wide_string; + fc::decodeUtf8(variant_to_convert.as_string(), &wide_string); + path_output = path(wide_string); } + // Note: we can do this cast because the separator should be an ASCII character + char path::separator_char = static_cast(boost::filesystem::path("/").make_preferred().native()[0]); + path::path(){} path::~path(){}; path::path( const boost::filesystem::path& p ) @@ -25,6 +59,9 @@ namespace fc { path::path( const fc::string& p ) :_p(p.c_str()){} + path::path(const std::wstring& p) + :_p(p) {} + path::path( const path& p ) :_p(p){} @@ -63,16 +100,54 @@ namespace fc { fc::string path::generic_string()const { return _p->generic_string(); } + + fc::string path::preferred_string() const + { + return boost::filesystem::path(*_p).make_preferred().string(); + } + + std::wstring path::wstring() const + { + return _p->wstring(); + } + + std::wstring path::generic_wstring() const + { + return _p->generic_wstring(); + } + + std::wstring path::preferred_wstring() const + { + return boost::filesystem::path(*_p).make_preferred().wstring(); + } + + std::string path::to_native_ansi_path() const + { + std::wstring path = generic_wstring(); + +#ifdef WIN32 + const size_t maxPath = 32*1024; + std::vector short_path; + short_path.resize(maxPath + 1); + + wchar_t* buffer = short_path.data(); + DWORD res = GetShortPathNameW(path.c_str(), buffer, maxPath); + if(res != 0) + path = buffer; +#endif + std::string filePath; + fc::encodeUtf8(path, &filePath); + return filePath; + } + /** * @todo use iterators instead of indexes for * faster performance */ fc::string path::windows_string()const { - auto gs = _p->generic_string(); - for( int i =0 ; i < gs.size(); ++i ) { - if( gs[i] == '/' ) gs[i] = '\\'; - } - return gs; + std::string result = _p->generic_string(); + std::replace(result.begin(), result.end(), '/', '\\'); + return result; } fc::string path::string()const { @@ -103,6 +178,7 @@ namespace fc { directory_iterator::~directory_iterator(){} fc::path directory_iterator::operator*()const { return boost::filesystem::path(*(*_p)); } + detail::path_wrapper directory_iterator::operator->() const { return detail::path_wrapper(boost::filesystem::path(*(*_p))); } directory_iterator& directory_iterator::operator++(int) { (*_p)++; return *this; } directory_iterator& directory_iterator::operator++() { (*_p)++; return *this; } @@ -134,65 +210,390 @@ namespace fc { return *r._p != *l._p; } - + bool exists( const path& p ) { return boost::filesystem::exists(p); } void create_directories( const path& p ) { try { boost::filesystem::create_directories(p); } catch ( ... ) { - FC_THROW_REPORT( "Unable to create directories ${path}", fc::value().set("path", p ).set("inner", fc::except_str() ) ); + FC_THROW( "Unable to create directories ${path}", ("path", p )("inner", fc::except_str() ) ); } } bool is_directory( const path& p ) { return boost::filesystem::is_directory(p); } bool is_regular_file( const path& p ) { return boost::filesystem::is_regular_file(p); } uint64_t file_size( const path& p ) { return boost::filesystem::file_size(p); } + + uint64_t directory_size(const path& p) + { + try { + FC_ASSERT( is_directory( p ) ); + + recursive_directory_iterator end; + uint64_t size = 0; + for( recursive_directory_iterator itr( p ); itr != end; ++itr ) + { + if( is_regular_file( *itr ) ) + size += file_size( *itr ); + } + + return size; + } catch ( ... ) { + FC_THROW( "Unable to calculate size of directory ${path}", ("path", p )("inner", fc::except_str() ) ); + } + } + void remove_all( const path& p ) { boost::filesystem::remove_all(p); } - void rename( const path& f, const path& t ) { + void copy( const path& f, const path& t ) { try { - boost::filesystem::rename( boost::filesystem::path(f), boost::filesystem::path(t) ); + boost::filesystem::copy( boost::filesystem::path(f), boost::filesystem::path(t) ); } catch ( boost::system::system_error& e ) { - FC_THROW_REPORT( "Rename from ${srcfile} to ${dstfile} failed because ${reason}", - fc::value().set("srcfile",f).set("dstfile",t).set("reason",e.what() ) ); + FC_THROW( "Copy from ${srcfile} to ${dstfile} failed because ${reason}", + ("srcfile",f)("dstfile",t)("reason",e.what() ) ); } catch ( ... ) { - FC_THROW_REPORT( "Rename from ${srcfile} to ${dstfile} failed", - fc::value().set("srcfile",f).set("dstfile",t).set("inner", fc::except_str() ) ); + FC_THROW( "Copy from ${srcfile} to ${dstfile} failed", + ("srcfile",f)("dstfile",t)("inner", fc::except_str() ) ); } } - void copy( const path& f, const path& t ) { + void resize_file( const path& f, size_t t ) + { + try { + boost::filesystem::resize_file( f, t ); + } + catch ( boost::system::system_error& e ) + { + FC_THROW( "Resize file '${f}' to size ${s} failed: ${reason}", + ("f",f)("s",t)( "reason", e.what() ) ); + } + catch ( ... ) + { + FC_THROW( "Resize file '${f}' to size ${s} failed: ${reason}", + ("f",f)("s",t)( "reason", fc::except_str() ) ); + } + } + + // setuid, setgid not implemented. + // translates octal permission like 0755 to S_ stuff defined in sys/stat.h + // no-op on Windows. + void chmod( const path& p, int perm ) + { +#ifndef WIN32 + mode_t actual_perm = + ((perm & 0400) ? S_IRUSR : 0) + | ((perm & 0200) ? S_IWUSR : 0) + | ((perm & 0100) ? S_IXUSR : 0) + + | ((perm & 0040) ? S_IRGRP : 0) + | ((perm & 0020) ? S_IWGRP : 0) + | ((perm & 0010) ? S_IXGRP : 0) + + | ((perm & 0004) ? S_IROTH : 0) + | ((perm & 0002) ? S_IWOTH : 0) + | ((perm & 0001) ? S_IXOTH : 0) + ; + + int result = ::chmod( p.string().c_str(), actual_perm ); + if( result != 0 ) + FC_THROW( "chmod operation failed on ${p}", ("p",p) ); +#endif + return; + } + + void rename( const path& f, const path& t ) { try { - boost::filesystem::copy( boost::filesystem::path(f), boost::filesystem::path(t) ); - } catch ( boost::system::system_error& e ) { - FC_THROW_REPORT( "Copy from ${srcfile} to ${dstfile} failed because ${reason}", - fc::value().set("srcfile",f).set("dstfile",t).set("reason",e.what() ) ); + boost::filesystem::rename( boost::filesystem::path(f), boost::filesystem::path(t) ); + } catch ( boost::system::system_error& ) { + try{ + boost::filesystem::copy( boost::filesystem::path(f), boost::filesystem::path(t) ); + boost::filesystem::remove( boost::filesystem::path(f)); + } catch ( boost::system::system_error& e ) { + FC_THROW( "Rename from ${srcfile} to ${dstfile} failed because ${reason}", + ("srcfile",f)("dstfile",t)("reason",e.what() ) ); + } } catch ( ... ) { - FC_THROW_REPORT( "Copy from ${srcfile} to ${dstfile} failed", - fc::value().set("srcfile",f).set("dstfile",t).set("inner", fc::except_str() ) ); + FC_THROW( "Rename from ${srcfile} to ${dstfile} failed", + ("srcfile",f)("dstfile",t)("inner", fc::except_str() ) ); } } void create_hard_link( const path& f, const path& t ) { try { boost::filesystem::create_hard_link( f, t ); } catch ( ... ) { - FC_THROW_REPORT( "Unable to create hard link from '${from}' to '${to}'", - fc::value().set( "from", f ) - .set("to",t).set("exception", fc::except_str() ) ); + FC_THROW( "Unable to create hard link from '${from}' to '${to}'", + ( "from", f )("to",t)("exception", fc::except_str() ) ); } } bool remove( const path& f ) { try { return boost::filesystem::remove( f ); } catch ( ... ) { - FC_THROW_REPORT( "Unable to remove '${path}'", fc::value().set( "path", f ).set("exception", fc::except_str() ) ); + FC_THROW( "Unable to remove '${path}'", ( "path", f )("exception", fc::except_str() ) ); } } fc::path canonical( const fc::path& p ) { try { return boost::filesystem::canonical(p); } catch ( ... ) { - FC_THROW_REPORT( "Unable to resolve path '${path}'", fc::value().set( "path", p ).set("exception", fc::except_str() ) ); + FC_THROW( "Unable to resolve path '${path}'", ( "path", p )("exception", fc::except_str() ) ); } } fc::path absolute( const fc::path& p ) { return boost::filesystem::absolute(p); } path unique_path() { return boost::filesystem::unique_path(); } path temp_directory_path() { return boost::filesystem::temp_directory_path(); } + + // Return path when appended to a_From will resolve to same as a_To + fc::path make_relative(const fc::path& from, const fc::path& to) { + boost::filesystem::path a_From = boost::filesystem::absolute(from); + boost::filesystem::path a_To = boost::filesystem::absolute(to); + boost::filesystem::path ret; + boost::filesystem::path::const_iterator itrFrom(a_From.begin()), itrTo(a_To.begin()); + // Find common base + for( boost::filesystem::path::const_iterator toEnd( a_To.end() ), fromEnd( a_From.end() ) ; itrFrom != fromEnd && itrTo != toEnd && *itrFrom == *itrTo; ++itrFrom, ++itrTo ); + // Navigate backwards in directory to reach previously found base + for( boost::filesystem::path::const_iterator fromEnd( a_From.end() ); itrFrom != fromEnd; ++itrFrom ) { + if( (*itrFrom) != "." ) + ret /= ".."; + } + // Now navigate down the directory branch + for (; itrTo != a_To.end(); ++itrTo) + ret /= *itrTo; + return ret; + } + + temp_file::temp_file(const fc::path& p, bool create) + : temp_file_base(p / fc::unique_path()) + { + if (fc::exists(*_path)) + { + FC_THROW( "Name collision: ${path}", ("path", _path->string()) ); + } + if (create) + { + fc::ofstream ofs(*_path, fc::ofstream::out | fc::ofstream::binary); + ofs.close(); + } + } + + temp_file::temp_file(temp_file&& other) + : temp_file_base(std::move(other._path)) + { + } + + temp_file& temp_file::operator=(temp_file&& other) + { + if (this != &other) + { + remove(); + _path = std::move(other._path); + } + return *this; + } + + temp_directory::temp_directory(const fc::path& p) + : temp_file_base(p / fc::unique_path()) + { + if (fc::exists(*_path)) + { + FC_THROW( "Name collision: ${path}", ("path", _path->string()) ); + } + fc::create_directories(*_path); + } + + temp_directory::temp_directory(temp_directory&& other) + : temp_file_base(std::move(other._path)) + { + } + + temp_directory& temp_directory::operator=(temp_directory&& other) + { + if (this != &other) + { + remove(); + _path = std::move(other._path); + } + return *this; + } + + const fc::path& temp_file_base::path() const + { + if (!_path) + { + FC_THROW( "Temporary directory has been released." ); + } + return *_path; + } + + void temp_file_base::remove() + { + if (_path.valid()) + { + try + { + fc::remove_all(*_path); + } + catch (...) + { + // eat errors on cleanup + } + release(); + } + } + + void temp_file_base::release() + { + _path = fc::optional(); + } + + const fc::path& home_path() + { + static fc::path p = []() + { +#ifdef WIN32 + HANDLE access_token; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &access_token)) + FC_ASSERT(false, "Unable to open an access token for the current process"); + wchar_t user_profile_dir[MAX_PATH]; + DWORD user_profile_dir_len = sizeof(user_profile_dir); + BOOL success = GetUserProfileDirectoryW(access_token, user_profile_dir, &user_profile_dir_len); + CloseHandle(access_token); + if (!success) + FC_ASSERT(false, "Unable to get the user profile directory"); + return fc::path(std::wstring(user_profile_dir)); +#else + char* home = getenv( "HOME" ); + if( nullptr == home ) + { + struct passwd* pwd = getpwuid(getuid()); + if( pwd ) + { + return fc::path( std::string( pwd->pw_dir ) ); + } + FC_ASSERT( home != nullptr, "The HOME environment variable is not set" ); + } + return fc::path( std::string(home) ); +#endif + }(); + return p; + } + + const fc::path& app_path() + { +#ifdef __APPLE__ + static fc::path appdir = [](){ return home_path() / "Library" / "Application Support"; }(); +#elif defined( WIN32 ) + static fc::path appdir = [](){ + wchar_t app_data_dir[MAX_PATH]; + + if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, app_data_dir))) + FC_ASSERT(false, "Unable to get the current AppData directory"); + return fc::path(std::wstring(app_data_dir)); + }(); +#else + static fc::path appdir = home_path(); +#endif + return appdir; + } + + const fc::path& current_path() + { + static fc::path appCurrentPath = boost::filesystem::current_path(); + return appCurrentPath; + } + + +#ifdef FC_HAS_SIMPLE_FILE_LOCK + class simple_lock_file::impl + { + public: +#ifdef _WIN32 + HANDLE file_handle; +#else + int file_handle; +#endif + bool is_locked; + path lock_file_path; + + impl(const path& lock_file_path); + ~impl(); + + bool try_lock(); + void unlock(); + }; + + simple_lock_file::impl::impl(const path& lock_file_path) : +#ifdef _WIN32 + file_handle(INVALID_HANDLE_VALUE), +#else + file_handle(-1), +#endif + is_locked(false), + lock_file_path(lock_file_path) + {} + + simple_lock_file::impl::~impl() + { + unlock(); + } + + bool simple_lock_file::impl::try_lock() + { +#ifdef _WIN32 + HANDLE fh = CreateFileA(lock_file_path.to_native_ansi_path().c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, 0, + OPEN_ALWAYS, 0, NULL); + if (fh == INVALID_HANDLE_VALUE) + return false; + is_locked = true; + file_handle = fh; + return true; +#else + int fd = open(lock_file_path.string().c_str(), O_RDWR|O_CREAT, 0644); + if (fd < 0) + return false; + if (flock(fd, LOCK_EX|LOCK_NB) == -1) + { + close(fd); + return false; + } + is_locked = true; + file_handle = fd; + return true; +#endif + } + + void simple_lock_file::impl::unlock() + { +#ifdef WIN32 + CloseHandle(file_handle); + file_handle = INVALID_HANDLE_VALUE; + is_locked = false; +#else + flock(file_handle, LOCK_UN); + close(file_handle); + file_handle = -1; + is_locked = false; +#endif + } + + + simple_lock_file::simple_lock_file(const path& lock_file_path) : + my(new impl(lock_file_path)) + { + } + + simple_lock_file::~simple_lock_file() + { + } + + bool simple_lock_file::try_lock() + { + return my->try_lock(); + } + + void simple_lock_file::unlock() + { + my->unlock(); + } +#endif // FC_HAS_SIMPLE_FILE_LOCK + } diff --git a/src/fstream.cpp b/src/fstream.cpp deleted file mode 100644 index e927eed9c..000000000 --- a/src/fstream.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include - -namespace fc { - class ofstream::impl : public fc::retainable { - public: - std::ofstream ofs; - }; - class ifstream::impl : public fc::retainable { - public: - std::ifstream ifs; - }; - - ofstream::ofstream() - :my( new impl() ){} - - ofstream::ofstream( const fc::path& file, int m ) - :my( new impl() ) { this->open( file, m ); } - ofstream::~ofstream(){} - - void ofstream::open( const fc::path& file, int m ) { - my->ofs.open( file.string().c_str(), std::ios::binary ); - } - ofstream& ofstream::write( const char* buf, size_t len ) { - my->ofs.write(buf,len); - return *this; - } - void ofstream::put( char c ) { - my->ofs.put(c); - } - void ofstream::close() { - my->ofs.close(); - } - void ofstream::flush() { - my->ofs.flush(); - } - - ifstream::ifstream() - :my(new impl()){} - ifstream::ifstream( const fc::path& file, int m ) - :my(new impl()) - { - this->open( file, m ); - } - ifstream::~ifstream(){} - - void ifstream::open( const fc::path& file, int m ) { - my->ifs.open( file.string().c_str(), std::ios::binary ); - } - size_t ifstream::readsome( char* buf, size_t len ) { - return size_t(my->ifs.readsome( buf, len )); - } - ifstream& ifstream::read( char* buf, size_t len ) { - my->ifs.read(buf,len); - return *this; - } - ifstream& ifstream::seekg( size_t p, seekdir d ) { - switch( d ) { - case beg: my->ifs.seekg( p, std::ios_base::beg ); return *this; - case cur: my->ifs.seekg( p, std::ios_base::cur ); return *this; - case end: my->ifs.seekg( p, std::ios_base::end ); return *this; - } - return *this; - } - void ifstream::close() { return my->ifs.close(); } - - bool ifstream::eof()const { return my->ifs.eof(); } - - -} // namespace fc diff --git a/src/future.cpp b/src/future.cpp deleted file mode 100644 index b02264649..000000000 --- a/src/future.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - - -namespace fc { - - promise_base::promise_base( const char* desc ) - :_ready(false), - _blocked_thread(nullptr), - _timeout(time_point::max()), - _canceled(false), - _desc(desc), - _compl(nullptr) - { } - - const char* promise_base::get_desc()const{ - return _desc; - } - - void promise_base::cancel(){ - _canceled = true; - } - bool promise_base::ready()const { - return _ready; - } - bool promise_base::error()const { - { synchronized(_spin_yield) - return _exceptp; - } - } - - void promise_base::set_exception( const fc::exception_ptr& e ){ - _exceptp = e; - _set_value(nullptr); - } - - void promise_base::_wait( const microseconds& timeout_us ){ - if( timeout_us == microseconds::max() ) _wait_until( time_point::max() ); - else _wait_until( time_point::now() + timeout_us ); - } - void promise_base::_wait_until( const time_point& timeout_us ){ - { synchronized(_spin_yield) - if( _ready ) { - if( _exceptp ) fc::rethrow_exception( _exceptp ); - return; - } - _enqueue_thread(); - } - thread::current().wait_until( ptr(this,true), timeout_us ); - if( _ready ) { - if( _exceptp ) fc::rethrow_exception( _exceptp ); - return; - } - FC_THROW( future_wait_timeout() ); - } - void promise_base::_enqueue_thread(){ - _blocked_thread =&thread::current(); - } - void promise_base::_notify(){ - if( _blocked_thread != nullptr ) - _blocked_thread->notify(ptr(this,true)); - } - promise_base::~promise_base() { } - void promise_base::_set_timeout(){ - if( _ready ) - return; - set_exception( fc::copy_exception( future_wait_timeout() ) ); - } - void promise_base::_set_value(const void* s){ - // slog( "%p == %d", &_ready, int(_ready)); -// BOOST_ASSERT( !_ready ); - { synchronized(_spin_yield) - _ready = true; - } - _notify(); - if( nullptr != _compl ) { - _compl->on_complete(s,_exceptp); - } - } - void promise_base::_on_complete( detail::completion_handler* c ) { - { synchronized(_spin_yield) - delete _compl; - _compl = c; - } - } -} - diff --git a/src/git_revision.cpp.in b/src/git_revision.cpp.in new file mode 100644 index 000000000..40977b76f --- /dev/null +++ b/src/git_revision.cpp.in @@ -0,0 +1,11 @@ +#include + +#define FC_GIT_REVISION_SHA "@FC_GIT_REVISION_SHA@" +#define FC_GIT_REVISION_UNIX_TIMESTAMP @FC_GIT_REVISION_UNIX_TIMESTAMP@ + +namespace fc { + +const char* const git_revision_sha = FC_GIT_REVISION_SHA; +const uint32_t git_revision_unix_timestamp = FC_GIT_REVISION_UNIX_TIMESTAMP; + +} // end namespace fc diff --git a/src/http_server.cpp b/src/http_server.cpp deleted file mode 100644 index 6349f0665..000000000 --- a/src/http_server.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include - - -namespace fc { namespace http { - - class server::response::impl : public fc::retainable { - public: - impl( const fc::http::connection& c, const std::function& cont = std::function() ) - :body_bytes_sent(0),body_length(0),con(c),handle_next_req(cont) - {} - - void send_header() { - fc::stringstream ss; - ss << "HTTP/1.1 " << rep.status << " "; - switch( rep.status ) { - case fc::http::reply::OK: ss << "OK\n\r"; break; - case fc::http::reply::RecordCreated: ss << "Record Created\n\r"; break; - case fc::http::reply::NotFound: ss << "Not Found\n\r"; break; - case fc::http::reply::Found: ss << "Found\n\r"; break; - case fc::http::reply::InternalServerError: ss << "Internal Server Error\r\n"; break; - } - for( uint32_t i = 0; i < rep.headers.size(); ++i ) { - ss << rep.headers[i].key <<": "< handle_next_req; - }; - - - class server::impl : public fc::retainable { - public: - impl(){} - impl(uint16_t p ) { - tcp_serv.listen(p); - accept_complete = fc::async([this](){ this->accept_loop(); }); - } - fc::future accept_complete; - ~impl() { - try { - tcp_serv.close(); - accept_complete.wait(); - }catch(...){} - } - void accept_loop() { - try { - http::connection con; - while( tcp_serv.accept( con.get_socket() ) ) { - slog( "Accept Connection" ); - fc::async( [=](){ handle_connection( con, on_req ); } ); - con = http::connection(); - } - } catch ( ... ) { - wlog( "tcp listen failed...%s", fc::except_str().c_str() ); - } - } - - void handle_connection( const http::connection& c, - std::function do_on_req ) { - wlog( "reading request.." ); - try { - http::server::response rep( fc::shared_ptr( new response::impl(c, [=](){ this->handle_connection(c,do_on_req); } ) ) ); - auto req = c.read_request(); - if( do_on_req ) do_on_req( req, rep ); - } catch ( ... ) { - wlog( "unable to read request %s", fc::except_str().c_str()); - } - } - std::function on_req; - fc::tcp_server tcp_serv; - }; - - - - server::server(){} - server::server( uint16_t port ) :my( new impl(port) ){} - server::server( const server& s ):my(s.my){} - server::server( server&& s ):my(fc::move(s.my)){} - - server& server::operator=(const server& s) { my = s.my; return *this; } - server& server::operator=(server&& s) { fc_swap(my,s.my); return *this; } - - server::~server(){} - - void server::listen( uint16_t p ) { - my.reset( new impl(p) ); - } - - - - server::response::response(){} - server::response::response( const server::response& s ):my(s.my){} - server::response::response( server::response&& s ):my(fc::move(s.my)){} - server::response::response( const fc::shared_ptr& m ):my(m){} - - server::response& server::response::operator=(const server::response& s) { my = s.my; return *this; } - server::response& server::response::operator=(server::response&& s) { fc_swap(my,s.my); return *this; } - - void server::response::add_header( const fc::string& key, const fc::string& val )const { - wlog( "Attempt to add header after sending headers" ); - my->rep.headers.push_back( fc::http::header( key, val ) ); - } - void server::response::set_status( const http::reply::status_code& s )const { - if( my->body_bytes_sent != 0 ) { - wlog( "Attempt to set status after sending headers" ); - } - my->rep.status = s; - } - void server::response::set_length( uint64_t s )const { - if( my->body_bytes_sent != 0 ) { - wlog( "Attempt to set length after sending headers" ); - } - my->body_length = s; - } - void server::response::write( const char* data, uint64_t len )const { - if( my->body_bytes_sent + len > my->body_length ) { - wlog( "Attempt to send to many bytes.." ); - len = my->body_bytes_sent + len - my->body_length; - } - if( my->body_bytes_sent == 0 ) { - my->send_header(); - } - my->body_bytes_sent += len; - my->con.get_socket().write( data, len ); - if( my->body_bytes_sent == int64_t(my->body_length) ) { - if( my->handle_next_req ) { - slog( "handle next request..." ); - //fc::async( std::function(my->handle_next_req) ); - fc::async( my->handle_next_req ); - } - } - } - - server::response::~response(){} - void server::on_request( const std::function& cb ) - { my->on_req = cb; } - - - - -} } diff --git a/src/interprocess/file_mapping.cpp b/src/interprocess/file_mapping.cpp new file mode 100644 index 000000000..91c9297b8 --- /dev/null +++ b/src/interprocess/file_mapping.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +namespace fc { + + + file_mapping::file_mapping( const char* file, mode_t m ) : + my(file, m == read_only ? boost::interprocess::read_only : boost::interprocess::read_write ) + {} + + file_mapping::~file_mapping() {} + + + + mapped_region::mapped_region( const file_mapping& fm, mode_t m, uint64_t start, size_t size ) : + my( *fm.my, m == read_only ? boost::interprocess::read_only : boost::interprocess::read_write ,start, size) + {} + + mapped_region::mapped_region( const file_mapping& fm, mode_t m ) : + my( *fm.my, m == read_only ? boost::interprocess::read_only : boost::interprocess::read_write) + {} + + mapped_region::~mapped_region(){} + + void* mapped_region::get_address() const + { + return my->get_address(); + } + + void mapped_region::flush() + { + my->flush(); + } + + size_t mapped_region::get_size() const + { + return my->get_size(); + } +} diff --git a/src/interprocess/mmap_struct.cpp b/src/interprocess/mmap_struct.cpp new file mode 100644 index 000000000..8e9206c27 --- /dev/null +++ b/src/interprocess/mmap_struct.cpp @@ -0,0 +1,44 @@ +#include + +#include + +#include + +#include +#include + +namespace fc +{ + namespace detail + { + size_t mmap_struct_base::size()const { return _mapped_region->get_size(); } + void mmap_struct_base::flush() + { + _mapped_region->flush(); + } + + void mmap_struct_base::open( const fc::path& file, size_t s, bool create ) + { + if( !fc::exists( file ) || fc::file_size(file) != s ) + { + fc::ofstream out( file ); + char buffer[1024]; + memset( buffer, 0, sizeof(buffer) ); + + size_t bytes_left = s; + while( bytes_left > 0 ) + { + size_t to_write = std::min(bytes_left, sizeof(buffer) ); + out.write( buffer, to_write ); + bytes_left -= to_write; + } + } + + std::string filePath = file.to_native_ansi_path(); + + _file_mapping.reset( new fc::file_mapping( filePath.c_str(), fc::read_write ) ); + _mapped_region.reset( new fc::mapped_region( *_file_mapping, fc::read_write, 0, s ) ); + } + } // namespace fc + +} // namespace fc diff --git a/src/interprocess/process.cpp b/src/interprocess/process.cpp new file mode 100644 index 000000000..fcafc5c70 --- /dev/null +++ b/src/interprocess/process.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc { + namespace bp = boost::process; + namespace io = boost::iostreams; + +fc::path find_executable_in_path( const fc::string name ) { + try { + return fc::string(bp::find_executable_in_path( std::string(name), "" )); + } catch (...) { + const char* p = std::getenv("PATH"); + FC_THROW( "Unable to find executable ${exe} in path.", + ("exe", name) + ("inner", fc::except_str() ) + ("PATH", fc::string(p!=nullptr?p:"") ) ); + } + return fc::path(); +} + + +class process::impl +{ + public: + impl() + :stat( fc::asio::default_io_service() ){} + + ~impl() + { + try + { + if( _in ) + { + _in->close(); + } + if( _exited.valid() && !_exited.ready()) + { + //child->terminate(); + _exited.wait(); + } + } + catch(...) + { + wlog( "caught exception cleaning up process: ${except_str}", ("except_str",fc::except_str()) ); + } + } + + std::shared_ptr child; + + std::shared_ptr _inp; + std::shared_ptr _outp; + std::shared_ptr _errp; + + buffered_istream_ptr _out; + buffered_istream_ptr _err; + buffered_ostream_ptr _in; + + bp::status stat; + bp::context pctx; + + fc::future _exited; +}; + +process::process() +:my( new process::impl() ){} +process::~process(){} + +iprocess& process::exec( const fc::path& exe, + std::vector args, + const fc::path& work_dir, int opt ) +{ + + my->pctx.work_dir = work_dir.string(); + my->pctx.suppress_console = (opt & suppress_console) != 0; + + if( opt&open_stdout) + my->pctx.streams[boost::process::stdout_id] = bp::behavior::async_pipe(); + else + my->pctx.streams[boost::process::stdout_id] = bp::behavior::null(); + + + if( opt& open_stderr ) + my->pctx.streams[boost::process::stderr_id] = bp::behavior::async_pipe(); + else + my->pctx.streams[boost::process::stderr_id] = bp::behavior::null(); + + if( opt& open_stdin ) + my->pctx.streams[boost::process::stdin_id] = bp::behavior::async_pipe(); + else + my->pctx.streams[boost::process::stdin_id] = bp::behavior::close(); + + /* + std::vector a; + a.reserve(size_t(args.size())); + for( uint32_t i = 0; i < args.size(); ++i ) { + a.push_back( fc::move(args[i]) ); + } + */ + my->child.reset( new bp::child( bp::create_child( exe.string(), fc::move(args), my->pctx ) ) ); + + if( opt & open_stdout ) { + bp::handle outh = my->child->get_handle( bp::stdout_id ); + my->_outp.reset( new bp::pipe( fc::asio::default_io_service(), outh.release() ) ); + } + if( opt & open_stderr ) { + bp::handle errh = my->child->get_handle( bp::stderr_id ); + my->_errp.reset( new bp::pipe( fc::asio::default_io_service(), errh.release() ) ); + } + if( opt & open_stdin ) { + bp::handle inh = my->child->get_handle( bp::stdin_id ); + my->_inp.reset( new bp::pipe( fc::asio::default_io_service(), inh.release() ) ); + } + + + promise::ptr p(new promise("process")); + my->stat.async_wait( my->child->get_id(), [=]( const boost::system::error_code& ec, int exit_code ) + { + //slog( "process::result %d", exit_code ); + if( !ec ) { + #ifdef BOOST_POSIX_API + if( WIFEXITED(exit_code) ) p->set_value( WEXITSTATUS(exit_code) ); + else + { + p->set_exception( + fc::exception_ptr( new fc::exception( + FC_LOG_MESSAGE( error, "process exited with: ${message} ", + ("message", strsignal(WTERMSIG(exit_code))) ) ) ) ); + } + #else + p->set_value(exit_code); + #endif + } + else + { + p->set_exception( + fc::exception_ptr( new fc::exception( + FC_LOG_MESSAGE( error, "process exited with: ${message} ", + ("message", boost::system::system_error(ec).what())) ) ) ); + } + }); + if( opt & open_stdin ) + my->_in = std::make_shared(std::make_shared>(my->_inp)); + if( opt & open_stdout ) + my->_out = std::make_shared(std::make_shared>(my->_outp)); + if( opt & open_stderr ) + my->_err = std::make_shared(std::make_shared>(my->_errp)); + my->_exited = p; + return *this; +} + +/** + * Forcefully kills the process. + */ +void process::kill() { + my->child->terminate(); +} + +/** + * @brief returns a stream that writes to the process' stdin + */ +fc::buffered_ostream_ptr process::in_stream() { + return my->_in; +} + +/** + * @brief returns a stream that reads from the process' stdout + */ +fc::buffered_istream_ptr process::out_stream() { + return my->_out; +} +/** + * @brief returns a stream that reads from the process' stderr + */ +fc::buffered_istream_ptr process::err_stream() { + return my->_err; +} + +int process::result(const microseconds& timeout /* = microseconds::maximum() */) +{ + return my->_exited.wait(timeout); +} + +} diff --git a/src/interprocess/signals.cpp b/src/interprocess/signals.cpp new file mode 100644 index 000000000..f65f1228f --- /dev/null +++ b/src/interprocess/signals.cpp @@ -0,0 +1,17 @@ +#include +#include + +namespace fc +{ + void set_signal_handler( std::function handler, int signal_num ) + { + std::shared_ptr sig_set(new boost::asio::signal_set(fc::asio::default_io_service(), signal_num)); + sig_set->async_wait( + [sig_set,handler]( const boost::system::error_code& err, int num ) + { + handler( num ); + sig_set->cancel(); + // set_signal_handler( handler, signal_num ); + } ); + } +} diff --git a/src/io/buffered_iostream.cpp b/src/io/buffered_iostream.cpp new file mode 100644 index 000000000..076062129 --- /dev/null +++ b/src/io/buffered_iostream.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +#include + +namespace fc +{ + namespace detail + { + class buffered_istream_impl + { + public: + buffered_istream_impl( istream_ptr is ) : + _istr(fc::move(is)) +#ifndef NDEBUG + ,_shared_read_buffer_in_use(false) +#endif + {} + + istream_ptr _istr; + boost::asio::streambuf _rdbuf; + std::shared_ptr _shared_read_buffer; +#ifndef NDEBUG + bool _shared_read_buffer_in_use; +#endif + }; + static const size_t minimum_read_size = 1024; + } + + buffered_istream::buffered_istream( istream_ptr is ) + :my( new detail::buffered_istream_impl( fc::move(is) ) ) + { + FC_ASSERT( my->_istr != nullptr, " this shouldn't be null" ); + } + + buffered_istream::buffered_istream( buffered_istream&& o ) + :my( fc::move(o.my) ){} + + buffered_istream& buffered_istream::operator=( buffered_istream&& i ) + { + my = fc::move(i.my); + return *this; + } + + buffered_istream::~buffered_istream(){} + + size_t buffered_istream::readsome( char* buf, size_t len ) + { + size_t bytes_from_rdbuf = static_cast(my->_rdbuf.sgetn(buf, len)); + if (bytes_from_rdbuf) + return bytes_from_rdbuf; + + + if( len > detail::minimum_read_size ) + return my->_istr->readsome(buf,len); + + char tmp[detail::minimum_read_size]; + size_t bytes_read = my->_istr->readsome( tmp, detail::minimum_read_size ); + + size_t bytes_to_deliver_immediately = std::min(bytes_read,len); + + memcpy( buf, tmp, bytes_to_deliver_immediately ); + + + if( bytes_read > len ) + { + my->_rdbuf.sputn( tmp + len, bytes_read - len ); + } + + return bytes_to_deliver_immediately; + } + + size_t buffered_istream::readsome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + size_t bytes_from_rdbuf = static_cast(my->_rdbuf.sgetn(buf.get() + offset, len)); + if (bytes_from_rdbuf) + return bytes_from_rdbuf; + + + if( len > detail::minimum_read_size ) + return my->_istr->readsome(buf.get() + offset, len); + +#ifndef NDEBUG + // This code was written with the assumption that you'd only be making one call to readsome + // at a time so it reuses _shared_read_buffer. If you really need to make concurrent calls to + // readsome(), you'll need to prevent reusing _shared_read_buffer here + struct check_buffer_in_use { + bool& _buffer_in_use; + check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; } + ~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; } + } buffer_in_use_checker(my->_shared_read_buffer_in_use); +#endif + + if (!my->_shared_read_buffer) + my->_shared_read_buffer.reset(new char[detail::minimum_read_size], [](char* p){ delete[] p; }); + size_t bytes_read = my->_istr->readsome( my->_shared_read_buffer, detail::minimum_read_size, 0 ); + + size_t bytes_to_deliver_immediately = std::min(bytes_read,len); + + memcpy( buf.get() + offset, my->_shared_read_buffer.get(), bytes_to_deliver_immediately ); + + if( bytes_read > len ) + { + my->_rdbuf.sputn( my->_shared_read_buffer.get() + len, bytes_read - len ); + } + + return bytes_to_deliver_immediately; + } + + char buffered_istream::peek()const + { + if( my->_rdbuf.size() ) + { + return my->_rdbuf.sgetc(); + } + + char tmp[detail::minimum_read_size]; + size_t bytes_read = my->_istr->readsome( tmp, detail::minimum_read_size ); + my->_rdbuf.sputn( tmp, bytes_read ); + + if( my->_rdbuf.size() ) + { + return my->_rdbuf.sgetc(); + } + FC_THROW_EXCEPTION( assert_exception, + "at least one byte should be available, or eof should have been thrown" ); + } + + + namespace detail + { + class buffered_ostream_impl + { + public: + buffered_ostream_impl( ostream_ptr os ) : + _ostr(fc::move(os)) +#ifndef NDEBUG + ,_shared_write_buffer_in_use(false) +#endif + {} + + ostream_ptr _ostr; + boost::asio::streambuf _rdbuf; + std::shared_ptr _shared_write_buffer; +#ifndef NDEBUG + bool _shared_write_buffer_in_use; +#endif + }; + } + + buffered_ostream::buffered_ostream( ostream_ptr os, size_t bufsize ) + :my( new detail::buffered_ostream_impl( fc::move(os) ) ) + { + } + + buffered_ostream::buffered_ostream( buffered_ostream&& o ) + :my( fc::move(o.my) ){} + + buffered_ostream& buffered_ostream::operator=( buffered_ostream&& i ) + { + my = fc::move(i.my); + return *this; + } + + buffered_ostream::~buffered_ostream(){} + + size_t buffered_ostream::writesome( const char* buf, size_t len ) + { + size_t written = static_cast(my->_rdbuf.sputn( buf, len )); + if( written < len ) { flush(); } + return written + static_cast(my->_rdbuf.sputn( buf+written, len-written )); + } + + size_t buffered_ostream::writesome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return writesome(buf.get() + offset, len); + } + + void buffered_ostream::flush() + { +#ifndef NDEBUG + // This code was written with the assumption that you'd only be making one call to flush + // at a time so it reuses _shared_write_buffer. If you really need to make concurrent calls to + // flush(), you'll need to prevent reusing _shared_write_buffer here + struct check_buffer_in_use { + bool& _buffer_in_use; + check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; } + ~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; } + } buffer_in_use_checker(my->_shared_write_buffer_in_use); +#endif + const size_t write_buffer_size = 2048; + if (!my->_shared_write_buffer) + my->_shared_write_buffer.reset(new char[write_buffer_size], [](char* p){ delete[] p; }); + + while( size_t bytes_from_rdbuf = static_cast(my->_rdbuf.sgetn(my->_shared_write_buffer.get(), write_buffer_size)) ) + my->_ostr->write( my->_shared_write_buffer, bytes_from_rdbuf ); + my->_ostr->flush(); + } + + void buffered_ostream::close() + { + flush(); + my->_ostr->close(); + } + + +} diff --git a/src/io/console.cpp b/src/io/console.cpp new file mode 100644 index 000000000..c0cda1809 --- /dev/null +++ b/src/io/console.cpp @@ -0,0 +1,46 @@ +#include +#include + +namespace fc { + +#ifdef WIN32 +#include + +void set_console_echo( bool enable_echo ) +{ + auto stdin_handle = GetStdHandle( STD_INPUT_HANDLE ); + DWORD mode = 0; + GetConsoleMode( stdin_handle, &mode ); + if( enable_echo ) + { + SetConsoleMode( stdin_handle, mode | ENABLE_ECHO_INPUT ); + } + else + { + SetConsoleMode( stdin_handle, mode & (~ENABLE_ECHO_INPUT) ); + } +} + +#else // NOT WIN32 +#include +#include + +void set_console_echo( bool enable_echo ) +{ + termios oldt; + tcgetattr(STDIN_FILENO, &oldt); + termios newt = oldt; + if( enable_echo ) + { + newt.c_lflag |= ECHO; + } + else + { + newt.c_lflag &= ~ECHO; + } + tcsetattr(STDIN_FILENO, TCSANOW, &newt); +} + +#endif // WIN32 + +} // namespace fc diff --git a/src/io/datastream.cpp b/src/io/datastream.cpp new file mode 100644 index 000000000..cfe1104e4 --- /dev/null +++ b/src/io/datastream.cpp @@ -0,0 +1,7 @@ +#include +#include + +NO_RETURN void fc::detail::throw_datastream_range_error(char const* method, size_t len, int64_t over) +{ + FC_THROW_EXCEPTION( out_of_range_exception, "${method} datastream of length ${len} over by ${over}", ("method",fc::string(method))("len",len)("over",over) ); +} diff --git a/src/io/fstream.cpp b/src/io/fstream.cpp new file mode 100644 index 000000000..a40bbeb42 --- /dev/null +++ b/src/io/fstream.cpp @@ -0,0 +1,109 @@ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace fc { + class ofstream::impl : public fc::retainable { + public: + boost::filesystem::ofstream ofs; + }; + class ifstream::impl : public fc::retainable { + public: + boost::filesystem::ifstream ifs; + }; + + ofstream::ofstream() + :my( new impl() ){} + + ofstream::ofstream( const fc::path& file, int m ) + :my( new impl() ) { this->open( file, m ); } + ofstream::~ofstream(){} + + void ofstream::open( const fc::path& file, int m ) { + const boost::filesystem::path& bfp = file; + my->ofs.open( bfp, std::ios::binary ); + } + size_t ofstream::writesome( const char* buf, size_t len ) { + my->ofs.write(buf,len); + return len; + } + size_t ofstream::writesome(const std::shared_ptr& buffer, size_t len, size_t offset) + { + return writesome(buffer.get() + offset, len); + } + + void ofstream::put( char c ) { + my->ofs.put(c); + } + void ofstream::close() { + my->ofs.close(); + } + void ofstream::flush() { + my->ofs.flush(); + } + + ifstream::ifstream() + :my(new impl()){} + ifstream::ifstream( const fc::path& file, int m ) + :my(new impl()) + { + this->open( file, m ); + } + ifstream::~ifstream(){} + + void ifstream::open( const fc::path& file, int m ) { + const boost::filesystem::path& bfp = file; + my->ifs.open( bfp, std::ios::binary ); + } + size_t ifstream::readsome( char* buf, size_t len ) { + auto s = size_t(my->ifs.readsome( buf, len )); + if( s <= 0 ) { + read( buf, 1 ); + s = 1; + } + return s; + } + size_t ifstream::readsome(const std::shared_ptr& buffer, size_t max, size_t offset) + { + return readsome(buffer.get() + offset, max); + } + + ifstream& ifstream::read( char* buf, size_t len ) { + if (eof()) + FC_THROW_EXCEPTION( eof_exception , ""); + my->ifs.read(buf,len); + if (my->ifs.gcount() < int64_t(len)) + FC_THROW_EXCEPTION( eof_exception , ""); + return *this; + } + ifstream& ifstream::seekg( size_t p, seekdir d ) { + switch( d ) { + case beg: my->ifs.seekg( p, std::ios_base::beg ); return *this; + case cur: my->ifs.seekg( p, std::ios_base::cur ); return *this; + case end: my->ifs.seekg( p, std::ios_base::end ); return *this; + } + return *this; + } + void ifstream::close() { return my->ifs.close(); } + + bool ifstream::eof()const { return !my->ifs.good(); } + + void read_file_contents( const fc::path& filename, std::string& result ) + { + const boost::filesystem::path& bfp = filename; + boost::filesystem::ifstream f( bfp, std::ios::in | std::ios::binary ); + // don't use fc::stringstream here as we need something with override for << rdbuf() + std::stringstream ss; + ss << f.rdbuf(); + result = ss.str(); + } + +} // namespace fc diff --git a/src/io/iostream.cpp b/src/io/iostream.cpp new file mode 100644 index 000000000..3a16027f4 --- /dev/null +++ b/src/io/iostream.cpp @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc { + + struct cin_buffer { + cin_buffer():eof(false),write_pos(0),read_pos(0),cinthread("cin"){ + + cinthread.async( [=](){read();}, "cin_buffer::read" ); + } + + void read() { + char c; + std::cin.read(&c,1); + while( !std::cin.eof() ) { + while( write_pos - read_pos > 0xfffff ) { + fc::promise::ptr wr( new fc::promise("cin_buffer::write_ready") ); + write_ready = wr; + if( write_pos - read_pos <= 0xfffff ) { + wr->wait(); + } + write_ready.reset(); + } + buf[write_pos&0xfffff] = c; + ++write_pos; + + fc::promise::ptr tmp; + { // copy read_ready because it is accessed from multiple threads + fc::scoped_lock lock( read_ready_mutex ); + tmp = read_ready; + } + + if( tmp && !tmp->ready() ) { + tmp->set_value(); + } + std::cin.read(&c,1); + } + eof = true; + fc::promise::ptr tmp; + { // copy read_ready because it is accessed from multiple threads + fc::scoped_lock lock( read_ready_mutex ); + tmp = read_ready; + } + if( tmp && !tmp->ready() ) { + tmp->set_exception( exception_ptr( new eof_exception() )); + } + } + boost::mutex read_ready_mutex; + fc::promise::ptr read_ready; + fc::promise::ptr write_ready; + + volatile bool eof; + + volatile uint64_t write_pos; + char buf[0xfffff+1]; // 1 mb buffer + volatile uint64_t read_pos; + fc::thread cinthread; + }; + + cin_buffer& get_cin_buffer() { + static cin_buffer* b = new cin_buffer(); + return *b; + } + + + fc::thread& cin_thread() { static fc::thread i("cin"); return i; } + + fc::istream& getline( fc::istream& i, fc::string& s, char delim ) { + fc::stringstream ss; + char c; + i.read( &c, 1 ); + while( true ) { + if( c == delim ) { s = ss.str(); return i; } + if( c != '\r' ) ss.write(&c,1); + i.read( &c, 1 ); + } + s = ss.str(); + return i; + } + + + size_t cout_t::writesome( const char* buf, size_t len ) { std::cout.write(buf,len); return len; } + size_t cout_t::writesome( const std::shared_ptr& buf, size_t len, size_t offset ) { return writesome(buf.get() + offset, len); } + void cout_t::close() {} + void cout_t::flush() { std::cout.flush(); } + + size_t cerr_t::writesome( const char* buf, size_t len ) { std::cerr.write(buf,len); return len; } + size_t cerr_t::writesome( const std::shared_ptr& buf, size_t len, size_t offset ) { return writesome(buf.get() + offset, len); } + void cerr_t::close() {}; + void cerr_t::flush() { std::cerr.flush(); } + + + size_t cin_t::readsome( char* buf, size_t len ) { + cin_buffer& b = get_cin_buffer(); + int64_t avail = b.write_pos - b.read_pos; + avail = (fc::min)(int64_t(len),avail); + int64_t u = 0; + + if( !((avail>0) && (len>0)) ) { + read( buf, 1 ); + ++buf; + ++u; + --len; + } + + while( (avail>0) && (len>0) ) { + *buf = b.buf[b.read_pos&0xfffff]; + ++b.read_pos; + ++buf; + --avail; + --len; + ++u; + } + return size_t(u); + } + size_t cin_t::readsome( const std::shared_ptr& buf, size_t len, size_t offset ) { return readsome(buf.get() + offset, len); } + + cin_t::~cin_t() { + /* + cin_buffer& b = get_cin_buffer(); + if( b.read_ready ) { + b.read_ready->wait(); + } + */ + } + istream& cin_t::read( char* buf, size_t len ) { + cin_buffer& b = get_cin_buffer(); + do { + while( !b.eof && (b.write_pos - b.read_pos)==0 ){ + // wait for more... + fc::promise::ptr rr( new fc::promise("cin_buffer::read_ready") ); + { // copy read_ready because it is accessed from multiple threads + fc::scoped_lock lock( b.read_ready_mutex ); + b.read_ready = rr; + } + if( b.write_pos - b.read_pos == 0 ) { + rr->wait(); + } + // b.read_ready.reset(); + { // copy read_ready because it is accessed from multiple threads + fc::scoped_lock lock( b.read_ready_mutex ); + b.read_ready.reset(); + } + } + if( b.eof ) FC_THROW_EXCEPTION( eof_exception, "cin" ); + size_t r = readsome( buf, len ); + buf += r; + len -= r; + + auto tmp = b.write_ready; // copy write_writey because it is accessed from multiple thwrites + if( tmp && !tmp->ready() ) { + tmp->set_value(); + } + } while( len > 0 && !b.eof ); + if( b.eof ) FC_THROW_EXCEPTION( eof_exception, "cin" ); + return *this; + } + + bool cin_t::eof()const { return get_cin_buffer().eof; } + + + std::shared_ptr cin_ptr = std::make_shared(); + std::shared_ptr cout_ptr = std::make_shared(); + std::shared_ptr cerr_ptr = std::make_shared(); + cout_t& cout = *cout_ptr; + cerr_t& cerr = *cerr_ptr; + cin_t& cin = *cin_ptr; + + + ostream& operator<<( ostream& o, const char v ) + { + o.write( &v, 1 ); + return o; + } + ostream& operator<<( ostream& o, const char* v ) + { + o.write( v, strlen(v) ); + return o; + } + + ostream& operator<<( ostream& o, const std::string& v ) + { + o.write( v.c_str(), v.size() ); + return o; + } +#ifdef USE_FC_STRING + ostream& operator<<( ostream& o, const fc::string& v ) + { + o.write( v.c_str(), v.size() ); + return o; + } +#endif + + ostream& operator<<( ostream& o, const double& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const float& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const int64_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const uint64_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const int32_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const uint32_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const int16_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const uint16_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const int8_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + + ostream& operator<<( ostream& o, const uint8_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + +#ifdef __APPLE__ + ostream& operator<<( ostream& o, const size_t& v ) + { + return o << boost::lexical_cast(v).c_str(); + } + +#endif + + istream& operator>>( istream& o, std::string& v ) + { + assert(false && "not implemented"); + return o; + } + +#ifdef USE_FC_STRING + istream& operator>>( istream& o, fc::string& v ) + { + assert(false && "not implemented"); + return o; + } +#endif + + istream& operator>>( istream& o, char& v ) + { + o.read(&v,1); + return o; + } + + istream& operator>>( istream& o, double& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, float& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, int64_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, uint64_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, int32_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, uint32_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, int16_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, uint16_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, int8_t& v ) + { + assert(false && "not implemented"); + return o; + } + + istream& operator>>( istream& o, uint8_t& v ) + { + assert(false && "not implemented"); + return o; + } + + + char istream::get() + { + char tmp; + read(&tmp,1); + return tmp; + } + + istream& istream::read( char* buf, size_t len ) + { + char* pos = buf; + while( size_t(pos-buf) < len ) + pos += readsome( pos, len - (pos - buf) ); + return *this; + } + + istream& istream::read( const std::shared_ptr& buf, size_t len, size_t offset ) + { + size_t bytes_read = 0; + while( bytes_read < len ) + bytes_read += readsome(buf, len - bytes_read, bytes_read + offset); + return *this; + } + + ostream& ostream::write( const char* buf, size_t len ) + { + const char* pos = buf; + while( size_t(pos-buf) < len ) + pos += writesome( pos, len - (pos - buf) ); + return *this; + } + + ostream& ostream::write( const std::shared_ptr& buf, size_t len, size_t offset ) + { + size_t bytes_written = 0; + while( bytes_written < len ) + bytes_written += writesome(buf, len - bytes_written, bytes_written + offset); + return *this; + } + +} // namespace fc diff --git a/src/io/json.cpp b/src/io/json.cpp new file mode 100644 index 000000000..b3f57cc24 --- /dev/null +++ b/src/io/json.cpp @@ -0,0 +1,839 @@ +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include + +namespace fc +{ + // forward declarations of provided functions + template variant variant_from_stream( T& in ); + template char parseEscape( T& in ); + template fc::string stringFromStream( T& in ); + template bool skip_white_space( T& in ); + template fc::string stringFromToken( T& in ); + template variant_object objectFromStream( T& in ); + template variants arrayFromStream( T& in ); + template variant number_from_stream( T& in ); + template variant token_from_stream( T& in ); + void escape_string( const string& str, ostream& os ); + template void to_stream( T& os, const variants& a, json::output_formatting format ); + template void to_stream( T& os, const variant_object& o, json::output_formatting format ); + template void to_stream( T& os, const variant& v, json::output_formatting format ); + fc::string pretty_print( const fc::string& v, uint8_t indent ); +} + +#include + +namespace fc +{ + template + char parseEscape( T& in ) + { + if( in.peek() == '\\' ) + { + try { + in.get(); + switch( in.peek() ) + { + case 't': + in.get(); + return '\t'; + case 'n': + in.get(); + return '\n'; + case 'r': + in.get(); + return '\r'; + case '\\': + in.get(); + return '\\'; + default: + return in.get(); + } + } FC_RETHROW_EXCEPTIONS( info, "Stream ended with '\\'" ); + } + FC_THROW_EXCEPTION( parse_error_exception, "Expected '\\'" ); + } + + template + bool skip_white_space( T& in ) + { + bool skipped = false; + while( true ) + { + switch( in.peek() ) + { + case ' ': + case '\t': + case '\n': + case '\r': + skipped = true; + in.get(); + break; + default: + return skipped; + } + } + } + + template + fc::string stringFromStream( T& in ) + { + fc::stringstream token; + try + { + char c = in.peek(); + + if( c != '"' ) + FC_THROW_EXCEPTION( parse_error_exception, + "Expected '\"' but read '${char}'", + ("char", string(&c, (&c) + 1) ) ); + in.get(); + while( true ) + { + + switch( c = in.peek() ) + { + case '\\': + token << parseEscape( in ); + break; + case 0x04: + FC_THROW_EXCEPTION( parse_error_exception, "EOF before closing '\"' in string '${token}'", + ("token", token.str() ) ); + case '"': + in.get(); + return token.str(); + default: + token << c; + in.get(); + } + } + FC_THROW_EXCEPTION( parse_error_exception, "EOF before closing '\"' in string '${token}'", + ("token", token.str() ) ); + } FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'", + ("token", token.str() ) ); + } + template + fc::string stringFromToken( T& in ) + { + fc::stringstream token; + try + { + char c = in.peek(); + + while( true ) + { + switch( c = in.peek() ) + { + case '\\': + token << parseEscape( in ); + break; + case '\t': + case ' ': + case '\0': + case '\n': + in.get(); + return token.str(); + default: + if( isalnum( c ) || c == '_' || c == '-' || c == '.' || c == ':' || c == '/' ) + { + token << c; + in.get(); + } + else return token.str(); + } + } + return token.str(); + } + catch( const fc::eof_exception& eof ) + { + return token.str(); + } + catch (const std::ios_base::failure&) + { + return token.str(); + } + + FC_RETHROW_EXCEPTIONS( warn, "while parsing token '${token}'", + ("token", token.str() ) ); + } + + template + variant_object objectFromStream( T& in ) + { + mutable_variant_object obj; + try + { + char c = in.peek(); + if( c != '{' ) + FC_THROW_EXCEPTION( parse_error_exception, + "Expected '{', but read '${char}'", + ("char",string(&c, &c + 1)) ); + in.get(); + skip_white_space(in); + while( in.peek() != '}' ) + { + if( in.peek() == ',' ) + { + in.get(); + continue; + } + if( skip_white_space(in) ) continue; + string key = stringFromStream( in ); + skip_white_space(in); + if( in.peek() != ':' ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Expected ':' after key \"${key}\"", + ("key", key) ); + } + in.get(); + auto val = variant_from_stream( in ); + + obj(std::move(key),std::move(val)); + skip_white_space(in); + } + if( in.peek() == '}' ) + { + in.get(); + return obj; + } + FC_THROW_EXCEPTION( parse_error_exception, "Expected '}' after ${variant}", ("variant", obj ) ); + } + catch( const fc::eof_exception& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected EOF: ${e}", ("e", e.to_detail_string() ) ); + } + catch( const std::ios_base::failure& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected EOF: ${e}", ("e", e.what() ) ); + } FC_RETHROW_EXCEPTIONS( warn, "Error parsing object" ); + } + + template + variants arrayFromStream( T& in ) + { + variants ar; + try + { + if( in.peek() != '[' ) + FC_THROW_EXCEPTION( parse_error_exception, "Expected '['" ); + in.get(); + skip_white_space(in); + + while( in.peek() != ']' ) + { + if( in.peek() == ',' ) + { + in.get(); + continue; + } + if( skip_white_space(in) ) continue; + ar.push_back( variant_from_stream(in) ); + skip_white_space(in); + } + if( in.peek() != ']' ) + FC_THROW_EXCEPTION( parse_error_exception, "Expected ']' after parsing ${variant}", + ("variant", ar) ); + + in.get(); + } FC_RETHROW_EXCEPTIONS( warn, "Attempting to parse array ${array}", + ("array", ar ) ); + return ar; + } + + template + variant number_from_stream( T& in ) + { + fc::stringstream ss; + + bool dot = false; + bool neg = false; + if( in.peek() == '-') + { + neg = true; + ss.put( in.get() ); + } + bool done = false; + + try + { + char c; + while((c = in.peek()) && !done) + { + + switch( c ) + { + case '.': + if (dot) + FC_THROW_EXCEPTION(parse_error_exception, "Can't parse a number with two decimal places"); + dot = true; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + ss.put( in.get() ); + break; + default: + if( isalnum( c ) ) + { + return ss.str() + stringFromToken( in ); + } + done = true; + break; + } + } + } + catch (fc::eof_exception&) + { + } + catch (const std::ios_base::failure&) + { + } + fc::string str = ss.str(); + if (str == "-." || str == ".") // check the obviously wrong things we could have encountered + FC_THROW_EXCEPTION(parse_error_exception, "Can't parse token \"${token}\" as a JSON numeric constant", ("token", str)); + if( dot ) + return parser_type == json::legacy_parser_with_string_doubles ? variant(str) : variant(to_double(str)); + if( neg ) + return to_int64(str); + return to_uint64(str); + } + template + variant token_from_stream( T& in ) + { + std::stringstream ss; + ss.exceptions( std::ifstream::badbit ); + bool received_eof = false; + bool done = false; + + try + { + char c; + while((c = in.peek()) && !done) + { + switch( c ) + { + case 'n': + case 'u': + case 'l': + case 't': + case 'r': + case 'e': + case 'f': + case 'a': + case 's': + ss.put( in.get() ); + break; + default: + done = true; + break; + } + } + } + catch (fc::eof_exception&) + { + received_eof = true; + } + catch (const std::ios_base::failure&) + { + received_eof = true; + } + + // we can get here either by processing a delimiter as in "null," + // an EOF like "null", or an invalid token like "nullZ" + fc::string str = ss.str(); + if( str == "null" ) + return variant(); + if( str == "true" ) + return true; + if( str == "false" ) + return false; + else + { + if (received_eof) + { + if (str.empty()) + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected EOF" ); + else + return str; + } + else + { + // if we've reached this point, we've either seen a partial + // token ("tru") or something our simple parser couldn't + // make out ("falfe") + // A strict JSON parser would signal this as an error, but we + // will just treat the malformed token as an un-quoted string. + return str + stringFromToken(in);; + } + } + } + + + template + variant variant_from_stream( T& in ) + { + skip_white_space(in); + variant var; + while( signed char c = in.peek() ) + { + switch( c ) + { + case ' ': + case '\t': + case '\n': + case '\r': + in.get(); + continue; + case '"': + return stringFromStream( in ); + case '{': + return objectFromStream( in ); + case '[': + return arrayFromStream( in ); + case '-': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return number_from_stream( in ); + // null, true, false, or 'warning' / string + case 'n': + case 't': + case 'f': + return token_from_stream( in ); + case 0x04: // ^D end of transmission + case EOF: + case 0: + FC_THROW_EXCEPTION( eof_exception, "unexpected end of file" ); + default: + FC_THROW_EXCEPTION( parse_error_exception, "Unexpected char '${c}' in \"${s}\"", + ("c", c)("s", stringFromToken(in)) ); + } + } + return variant(); + } + + + /** the purpose of this check is to verify that we will not get a stack overflow in the recursive descent parser */ + void check_string_depth( const string& utf8_str ) + { + int32_t open_object = 0; + int32_t open_array = 0; + for( auto c : utf8_str ) + { + switch( c ) + { + case '{': open_object++; break; + case '}': open_object--; break; + case '[': open_array++; break; + case ']': open_array--; break; + default: break; + } + FC_ASSERT( open_object < 100 && open_array < 100, "object graph too deep", ("object depth",open_object)("array depth", open_array) ); + } + } + + variant json::from_string( const std::string& utf8_str, parse_type ptype ) + { try { + check_string_depth( utf8_str ); + + fc::stringstream in( utf8_str ); + //in.exceptions( std::ifstream::eofbit ); + switch( ptype ) + { + case legacy_parser: + return variant_from_stream( in ); + case legacy_parser_with_string_doubles: + return variant_from_stream( in ); + case strict_parser: + return json_relaxed::variant_from_stream( in ); + case relaxed_parser: + return json_relaxed::variant_from_stream( in ); + default: + FC_ASSERT( false, "Unknown JSON parser type {ptype}", ("ptype", ptype) ); + } + } FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) ) } + + variants json::variants_from_string( const std::string& utf8_str, parse_type ptype ) + { try { + check_string_depth( utf8_str ); + variants result; + fc::stringstream in( utf8_str ); + //in.exceptions( std::ifstream::eofbit ); + try { + while( true ) + { + // result.push_back( variant_from_stream( in )); + result.push_back(json_relaxed::variant_from_stream( in )); + } + } catch ( const fc::eof_exception& ){} + return result; + } FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) ) } + /* + void toUTF8( const char str, ostream& os ) + { + // validate str == valid utf8 + utf8::replace_invalid( &str, &str + 1, ostream_iterator(os) ); + } + + void toUTF8( const wchar_t c, ostream& os ) + { + utf8::utf16to8( &c, (&c)+1, ostream_iterator(os) ); + } + */ + + /** + * Convert '\t', '\a', '\n', '\\' and '"' to "\t\a\n\\\"" + * + * All other characters are printed as UTF8. + */ + void escape_string( const string& str, ostream& os ) + { + os << '"'; + for( auto itr = str.begin(); itr != str.end(); ++itr ) + { + switch( *itr ) + { + case '\t': + os << "\\t"; + break; + case '\n': + os << "\\n"; + break; + case '\\': + os << "\\\\"; + break; + case '\r': + os << "\\r"; + break; + case '\a': + os << "\\a"; + break; + case '\"': + os << "\\\""; + break; + default: + os << *itr; + //toUTF8( *itr, os ); + } + } + os << '"'; + } + ostream& json::to_stream( ostream& out, const fc::string& str ) + { + escape_string( str, out ); + return out; + } + + template + void to_stream( T& os, const variants& a, json::output_formatting format ) + { + os << '['; + auto itr = a.begin(); + + while( itr != a.end() ) + { + to_stream( os, *itr, format ); + ++itr; + if( itr != a.end() ) + os << ','; + } + os << ']'; + } + template + void to_stream( T& os, const variant_object& o, json::output_formatting format ) + { + os << '{'; + auto itr = o.begin(); + + while( itr != o.end() ) + { + escape_string( itr->key(), os ); + os << ':'; + to_stream( os, itr->value(), format ); + ++itr; + if( itr != o.end() ) + os << ','; + } + os << '}'; + } + + template + void to_stream( T& os, const variant& v, json::output_formatting format ) + { + switch( v.get_type() ) + { + case variant::null_type: + os << "null"; + return; + case variant::int64_type: + { + int64_t i = v.as_int64(); + if( format == json::stringify_large_ints_and_doubles && + i > 0xffffffff ) + os << '"'< 0xffffffff ) + os << '"'<( p, ifstream::binary ); + //auto tmp = std::make_shared( p.generic_string().c_str(), std::ios::binary ); + //buffered_istream bi( tmp ); + boost::filesystem::ifstream bi( p, std::ios::binary ); + switch( ptype ) + { + case legacy_parser: + return variant_from_stream( bi ); + case legacy_parser_with_string_doubles: + return variant_from_stream( bi ); + case strict_parser: + return json_relaxed::variant_from_stream( bi ); + case relaxed_parser: + return json_relaxed::variant_from_stream( bi ); + default: + FC_ASSERT( false, "Unknown JSON parser type {ptype}", ("ptype", ptype) ); + } + } + variant json::from_stream( buffered_istream& in, parse_type ptype ) + { + switch( ptype ) + { + case legacy_parser: + return variant_from_stream( in ); + case legacy_parser_with_string_doubles: + return variant_from_stream( in ); + case strict_parser: + return json_relaxed::variant_from_stream( in ); + case relaxed_parser: + return json_relaxed::variant_from_stream( in ); + default: + FC_ASSERT( false, "Unknown JSON parser type {ptype}", ("ptype", ptype) ); + } + } + + ostream& json::to_stream( ostream& out, const variant& v, output_formatting format /* = stringify_large_ints_and_doubles */ ) + { + fc::to_stream( out, v, format ); + return out; + } + ostream& json::to_stream( ostream& out, const variants& v, output_formatting format /* = stringify_large_ints_and_doubles */ ) + { + fc::to_stream( out, v, format ); + return out; + } + ostream& json::to_stream( ostream& out, const variant_object& v, output_formatting format /* = stringify_large_ints_and_doubles */ ) + { + fc::to_stream( out, v, format ); + return out; + } + + bool json::is_valid( const std::string& utf8_str, parse_type ptype ) + { + if( utf8_str.size() == 0 ) return false; + fc::stringstream in( utf8_str ); + switch( ptype ) + { + case legacy_parser: + variant_from_stream( in ); + break; + case legacy_parser_with_string_doubles: + variant_from_stream( in ); + break; + case strict_parser: + json_relaxed::variant_from_stream( in ); + break; + case relaxed_parser: + json_relaxed::variant_from_stream( in ); + break; + default: + FC_ASSERT( false, "Unknown JSON parser type {ptype}", ("ptype", ptype) ); + } + try { in.peek(); } catch ( const eof_exception& e ) { return true; } + return false; + } + +} // fc diff --git a/src/sstream.cpp b/src/io/sstream.cpp similarity index 62% rename from src/sstream.cpp rename to src/io/sstream.cpp index 7767479f9..9c4739e3b 100644 --- a/src/sstream.cpp +++ b/src/io/sstream.cpp @@ -1,6 +1,7 @@ -#include +#include #include -#include +#include +#include #include namespace fc { @@ -8,11 +9,13 @@ namespace fc { public: impl( fc::string&s ) :ss( s ) - { } + { ss.exceptions( std::stringstream::badbit ); } + impl( const fc::string&s ) :ss( s ) - { } - impl(){} + { ss.exceptions( std::stringstream::badbit ); } + + impl(){ss.exceptions( std::stringstream::badbit ); } std::stringstream ss; }; @@ -31,23 +34,53 @@ namespace fc { return my->ss.str();//.c_str();//*reinterpret_cast(&st); } + void stringstream::str(const fc::string& s) { + my->ss.str(s); + } + + void stringstream::clear() { + my->ss.clear(); + } + + bool stringstream::eof()const { return my->ss.eof(); } - ostream& stringstream::write( const char* buf, size_t len ) { + size_t stringstream::writesome( const char* buf, size_t len ) { my->ss.write(buf,len); - return *this; + if( my->ss.eof() ) + { + FC_THROW_EXCEPTION( eof_exception, "stringstream" ); + } + return len; } + size_t stringstream::writesome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return writesome(buf.get() + offset, len); + } + size_t stringstream::readsome( char* buf, size_t len ) { - return static_cast(my->ss.readsome(buf,len)); + size_t r = static_cast(my->ss.readsome(buf,len)); + if( my->ss.eof() || r == 0 ) + { + FC_THROW_EXCEPTION( eof_exception, "stringstream" ); + } + return r; } - istream& stringstream::read( char* buf, size_t len ) { - my->ss.read(buf,len); - return *this; + size_t stringstream::readsome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return readsome(buf.get() + offset, len); } + + void stringstream::close(){ my->ss.flush(); }; void stringstream::flush(){ my->ss.flush(); }; + /* + istream& stringstream::read( char* buf, size_t len ) { + my->ss.read(buf,len); + return *this; + } istream& stringstream::read( int64_t& v ) { my->ss >> v; return *this; } istream& stringstream::read( uint64_t& v ) { my->ss >> v; return *this; } istream& stringstream::read( int32_t& v ) { my->ss >> v; return *this; } @@ -66,7 +99,17 @@ namespace fc { my->ss.write( s.c_str(), s.size() ); return *this; } + */ + char stringstream::peek() + { + char c = my->ss.peek(); + if( my->ss.eof() ) + { + FC_THROW_EXCEPTION( eof_exception, "stringstream" ); + } + return c; + } } diff --git a/src/io/varint.cpp b/src/io/varint.cpp new file mode 100644 index 000000000..a3eaac22a --- /dev/null +++ b/src/io/varint.cpp @@ -0,0 +1,10 @@ +#include +#include + +namespace fc +{ +void to_variant( const signed_int& var, variant& vo ) { vo = var.value; } +void from_variant( const variant& var, signed_int& vo ) { vo.value = static_cast(var.as_int64()); } +void to_variant( const unsigned_int& var, variant& vo ) { vo = var.value; } +void from_variant( const variant& var, unsigned_int& vo ) { vo.value = static_cast(var.as_uint64()); } +} diff --git a/src/iostream.cpp b/src/iostream.cpp deleted file mode 100644 index c53e050c8..000000000 --- a/src/iostream.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - ostream& operator<<( ostream& o, const char* v ) { - o.write( v, strlen(v) ); - return o; - } - - struct cin_buffer { - cin_buffer():eof(false),write_pos(0),read_pos(0),cinthread("cin"){ - - cinthread.async( [=](){read();} ); - } - - void read() { - char c; - std::cin.read(&c,1); - while( !std::cin.eof() ) { - while( write_pos - read_pos > 0xfffff ) { - fc::promise::ptr wr( new fc::promise() ); - write_ready = wr; - if( write_pos - read_pos <= 0xfffff ) { - wr->wait(); - } - write_ready.reset(); - } - buf[write_pos&0xfffff] = c; - ++write_pos; - - fc::promise::ptr tmp; - { // copy read_ready because it is accessed from multiple threads - fc::scoped_lock lock( read_ready_mutex ); - tmp = read_ready; - } - if( tmp && !tmp->ready() ) { - tmp->set_value(); - } - std::cin.read(&c,1); - } - eof = true; - fc::promise::ptr tmp; - { // copy read_ready because it is accessed from multiple threads - fc::scoped_lock lock( read_ready_mutex ); - tmp = read_ready; - } - if( tmp && !tmp->ready() ) { - tmp->set_value(); - } - } - boost::mutex read_ready_mutex; - fc::promise::ptr read_ready; - fc::promise::ptr write_ready; - - volatile bool eof; - - volatile uint64_t write_pos; - char buf[0xfffff+1]; // 1 mb buffer - volatile uint64_t read_pos; - fc::thread cinthread; - }; - - cin_buffer& get_cin_buffer() { - static cin_buffer* b = new cin_buffer(); - return *b; - } - - - fc::thread& cin_thread() { static fc::thread i("cin"); return i; } - - fc::istream& getline( fc::istream& i, fc::string& s, char delim ) { - fc::stringstream ss; - char c; - i.read( &c, 1 ); - while( !i.eof() ) { - if( c == delim ) { s = ss.str(); return i; } - if( c != '\r' ) ss.write(&c,1); - i.read( &c, 1 ); - } - s = ss.str(); - return i; - } - - - ostream& cout_t::write( const char* buf, size_t len ) { std::cout.write(buf,len); return *this; } - void cout_t::close() {} - void cout_t::flush() { std::cout.flush(); } - - ostream& cout_t::write( const fc::string& s ) { std::cout.write(s.c_str(),s.size()); return *this; } - - ostream& cerr_t::write( const char* buf, size_t len ) { std::cerr.write(buf,len); return *this; } - void cerr_t::close() {}; - void cerr_t::flush() { std::cerr.flush(); } - - ostream& cerr_t::write( const fc::string& s ) { std::cerr<< static_cast(s); return *this; } - - size_t cin_t::readsome( char* buf, size_t len ) { - cin_buffer& b = get_cin_buffer(); - int64_t avail = b.write_pos - b.read_pos; - avail = (fc::min)(int64_t(len),avail); - int64_t u = 0; - while( (avail>0) && (len>0) ) { - *buf = b.buf[b.read_pos&0xfffff]; - ++b.read_pos; - ++buf; - --avail; - --len; - ++u; - } - return size_t(u); - } - - cin_t::~cin_t() { - /* - cin_buffer& b = get_cin_buffer(); - if( b.read_ready ) { - b.read_ready->wait(); - } - */ - } - istream& cin_t::read( char* buf, size_t len ) { - cin_buffer& b = get_cin_buffer(); - do { - while( !b.eof && (b.write_pos - b.read_pos)==0 ){ - // wait for more... - fc::promise::ptr rr( new fc::promise() ); - { // copy read_ready because it is accessed from multiple threads - fc::scoped_lock lock( b.read_ready_mutex ); - b.read_ready = rr; - } - if( b.write_pos - b.read_pos == 0 ) { - rr->wait(); - } - // b.read_ready.reset(); - { // copy read_ready because it is accessed from multiple threads - fc::scoped_lock lock( b.read_ready_mutex ); - b.read_ready.reset(); - } - } - if( b.eof ) return *this; - size_t r = readsome( buf, len ); - buf += r; - len -= r; - - auto tmp = b.write_ready; // copy write_writey because it is accessed from multiple thwrites - if( tmp && !tmp->ready() ) { - tmp->set_value(); - } - - - } while( len > 0 && !b.eof ); - return *this; - } - - bool cin_t::eof()const { return get_cin_buffer().eof; } - - cout_t cout; - cerr_t cerr; - cin_t cin; -} diff --git a/src/ip.cpp b/src/ip.cpp deleted file mode 100644 index f7f884074..000000000 --- a/src/ip.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include -#include -#include - -namespace fc { namespace ip { - - address::address( uint32_t ip ) - :_ip(ip){} - - address::address( const fc::string& s ) { - _ip = boost::asio::ip::address_v4::from_string(s.c_str()).to_ulong(); - } - - bool operator==( const address& a, const address& b ) { - return uint32_t(a) == uint32_t(b); - } - - address& address::operator=( const fc::string& s ) { - _ip = boost::asio::ip::address_v4::from_string(s.c_str()).to_ulong(); - return *this; - } - - address::operator fc::string()const { - return boost::asio::ip::address_v4(_ip).to_string().c_str(); - } - address::operator uint32_t()const { - return _ip; - } - - - endpoint::endpoint() - :_port(0){ } - endpoint::endpoint(const address& a, uint16_t p) - :_port(p),_ip(a){} - - bool operator==( const endpoint& a, const endpoint& b ) { - return a._port == b._port && a._ip == b._ip; - } - uint16_t endpoint::port()const { return _port; } - const address& endpoint::get_address()const { return _ip; } - - endpoint endpoint::from_string( const string& s ) { - endpoint ep; - const std::string& st = reinterpret_cast(s); - auto pos = st.find(':'); - ep._ip = boost::asio::ip::address_v4::from_string(st.substr( 0, pos ) ).to_ulong(); - ep._port = boost::lexical_cast( st.substr( pos+1, s.size() ) ); - return ep; - } - - endpoint::operator string()const { - return string(_ip) + ':' + fc::string(boost::lexical_cast(_port).c_str()); - } - -} - void pack( fc::value& v, const fc::ip::endpoint& s ) { - v = fc::string(s); - } - void unpack( const fc::value& v, fc::ip::endpoint& s ) { - s = fc::ip::endpoint::from_string(fc::value_cast(v)); - } - -} diff --git a/src/json.cpp b/src/json.cpp deleted file mode 100644 index c187bb90d..000000000 --- a/src/json.cpp +++ /dev/null @@ -1,1055 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace fc { namespace json { - /** - * Placeholder for unparsed json data. - */ - class string { - public: - template - string( T&& v ):json_data( fc::forward(v) ){} - string( const string& s ):json_data(s.json_data){} - string( string&& s ):json_data(fc::move(s.json_data)){} - string(){} - - string& operator=( const fc::string& s ) { - json_data = std::vector(s.begin(),s.end()); - return *this; - } - template - string& operator=( T&& t ) { - json_data = fc::forward(t); - return *this; - } - string& operator=( string&& s ) { - json_data = fc::move(s.json_data); - return *this; - } - string& operator=( const string& s ) - { - json_data = s.json_data; - return *this; - } - std::vector json_data; - }; - -} } - -typedef std::map jmap; - - - namespace errors { - enum error_type { - unknown_error = 0x0001, // Other errors not specified below - warning = 0x0002, // Other warnigns not specified below - sytnax_error = 0x0004, // fatal syntax errors unclosed brace - sytnax_warning = 0x0008, // recoverable syntax error (missing, missing ':', unquoted string) - missing_comma = 0x0010, // if the error was related to json syntax and not semantic - string_to_int = 0x0020, // any time lexical cast from string to int is required - double_to_int = 0x0040, // any time a double is received for an int - int_overflow = 0x0080, // any time int value is greater than underlying type - signed_to_unsigned = 0x0100, // any time a negative value is read for an unsigned field - int_to_bool = 0x0200, // any time an int is read for a bool - string_to_bool = 0x0400, // any time a string is read for a bool - bad_array_index = 0x0800, // atempt to read a vector field beyond end of sequence - unexpected_key = 0x1000, // fields in object - missing_key = 0x2000, // check required fields - type_mismatch = 0x4000, // expected a fundamental, got object, expected object, got array, etc. - type_conversion = 0x8000, // also set any time a conversion occurs - all = 0xffff, - none = 0x0000 - }; - } // namespace errors - - /** - * Stores information about errors that occurred durring the parse. - * - * By default extra fields are 'ignored' as warning - * Loss of presision errors are warning. - * String to Int conversion warnings - * Double to Int - * Int to bool - * - */ - struct parse_error { - parse_error( int32_t ec, fc::string msg, char* s = 0, char* e = 0 ) - :message(fc::move(msg)),type(ec),start(s),end(e){} - - parse_error( parse_error&& m ) - :message(fc::move(m.message)),type(m.type),start(m.start),end(m.end){} - - fc::string message; - int32_t type; - char* start; - char* end; - }; - - /** - * Collects errors and manages how they are responded to. - */ - class error_collector { - public: - error_collector( error_collector&& e ) - :m_errors(fc::move(e.m_errors)){ - memcpy((char*)m_eclass,(char*)e.m_eclass, sizeof(m_eclass) ); - } - /* - error_collector( const error_collector&& e ) - :m_errors(e.m_errors){ - memcpy((char*)m_eclass,(char*)e.m_eclass, sizeof(m_eclass) ); - } - */ - ~error_collector() throw() { - try { - m_errors.clear(); - }catch(...){} - } - - enum error_defaults { - default_report = errors::all, - default_recover = errors::all, - default_throw = errors::none, - default_ignore = ~(default_report|default_recover|default_throw) - }; - - error_collector(){ - m_eclass[report_error_t] = default_report; - m_eclass[recover_error_t] = default_recover; - m_eclass[throw_error_t] = default_throw; - m_eclass[ignore_error_t] = default_ignore; - } - - inline bool report( int32_t e )const { - return 0 != (m_eclass[report_error_t] & e); - } - inline bool recover( int32_t e )const { - return 0 != (m_eclass[recover_error_t] & e); - } - inline bool ignore( int32_t e )const { - return 0 != (m_eclass[ignore_error_t] & e); - } - - void report_error( int32_t e ) { - m_eclass[report_error_t] |= e; - m_eclass[ignore_error_t] &= ~e; - } - void recover_error( int32_t e ) { - m_eclass[recover_error_t] |= e; - m_eclass[ignore_error_t] &= ~e; - } - void throw_error( int32_t e ) { - m_eclass[throw_error_t] |= e; - m_eclass[ignore_error_t] &= ~e; - } - void ignore_error( int32_t e ) { - m_eclass[ignore_error_t] |= e; - m_eclass[report_error_t] &= ~m_eclass[ignore_error_t]; - m_eclass[recover_error_t] &= ~m_eclass[ignore_error_t]; - m_eclass[throw_error_t] &= ~m_eclass[ignore_error_t]; - } - - void post_error( int32_t ec, fc::string msg, char* s = 0, char* e = 0 ) { - m_errors.push_back( parse_error( ec, fc::move(msg), s, e ) ); - if( ec & m_eclass[throw_error_t] ) { - throw fc::move(*this); - } - } - const fc::vector& get_errors()const { - return m_errors; - } - - private: - enum error_class { - ignore_error_t, - report_error_t, - recover_error_t, - throw_error_t, - num_error_classes - }; - uint32_t m_eclass[num_error_classes]; - fc::vector m_errors; - }; - fc::value to_value( char* start, char* end, error_collector& ec ); - - - - - - - - - - -namespace fc { namespace json { - fc::string escape_string( const fc::string& s ) { - // calculate escape string size. - uint32_t ecount = 0; - for( auto i = s.begin(); i != s.end(); ++i ) { - if( ' '<= *i && *i <= '~' && *i !='\\' && *i != '"' ) { - ecount+=1; - } else { - switch( *i ) { - case '\t' : - case '\n' : - case '\r' : - case '\\' : - case '"' : - ecount += 2; break; - default: - ecount += 4; - } - } - } - // unless the size changed, just return it. - if( ecount == s.size() ) { return s; } - - // reserve the bytes - fc::string out; out.reserve(ecount); - // print it out. - for( auto i = s.begin(); i != s.end(); ++i ) { - if( ' '<= *i && *i <= '~' && *i !='\\' && *i != '"' ) { - out += *i; - } else { - out += '\\'; - switch( *i ) { - case '\t' : out += 't'; break; - case '\n' : out += 'n'; break; - case '\r' : out += 'r'; break; - case '\\' : out += '\\'; break; - case '"' : out += '"'; break; - default: - out += "x"; - const char* const hexdig = "0123456789abcdef"; - out += hexdig[*i >> 4]; - out += hexdig[*i & 0xF]; - } - } - } - return out; - } - - fc::string unescape_string( const fc::string& s ) { - fc::string out; out.reserve(s.size()); - for( auto i = s.begin(); i != s.end(); ++i ) { - if( *i != '\\' ) { - if( *i != '"' ) out += *i; - } - else { - ++i; - if( i == out.end() ) return out; - switch( *i ) { - case 't' : out += '\t'; break; - case 'n' : out += '\n'; break; - case 'r' : out += '\r'; break; - case '\\' : out += '\\'; break; - case '"' : out += '"'; break; - case 'x' : { - ++i; if( i == out.end() ) return out; - char c = fc::from_hex(*i); - ++i; if( i == out.end() ) { out += c; return out; } - c = c<<4 | fc::from_hex(*i); - out += c; - break; - } - default: - out += '\\'; - out += *i; - } - } - } - return out; - } - - /** - * Any unescaped quotes are dropped. - * Because unescaped strings are always shorter, we can simply reuse - * the memory of s. - * - * @param s a null terminated string that contains one or more escape chars - */ - char* inplace_unescape_string( char* s ) { - while( *s == '"' ) ++s; - char* out = s; - - for( auto i = s; *i != '\0'; ++i ) { - if( *i != '\\' ) { - if( *i != '"' ) { - *out = *i; - ++out; - } - } - else { - ++i; - if( *i == '\0' ) { *out = '\0'; return s; } - switch( *i ) { - case 't' : *out = '\t'; ++out; break; - case 'n' : *out = '\n'; ++out; break; - case 'r' : *out = '\r'; ++out; break; - case '\\': *out = '\\'; ++out; break; - case '"' : *out = '"'; ++out; break; - case 'x' : { - ++i; if( *i == '\0' ){ *out = '\0'; return s; } - char c = fc::from_hex(*i); - ++i; if( *i == '\0' ){ *out = c; ++out; *out = '\0'; return s; } - c = c<<4 | fc::from_hex(*i); - *out = c; - ++out; - break; - } - default: - *out = '\\'; - ++out; - *out = *i; - ++out; - } - } - } - *out = '\0'; - return s; - } - } }// fc::json - -/** - * Ignores leading white space. - * If it starts with [,", or { reads until matching ],", or } - * If it starts with something else it reads until [{",}]: or whitespace only - * allowing a starting - or a single . - * - * @note internal json syntax errors are not caught, only bracket errors - * are caught by this method. This makes it easy for error recovery - * when values are read recursivley. - * - * @param in start of input - * @param end end of input - * @param oend output parameter to the end of the value - * - * @return a pointer to the start of the value - */ -char* read_value( char* in, char* end, char*& oend ) { - char* start = in; - char* oin = in; - // ignore leading whitespace - while( (in < end) && ((*in == ' ') || (*in == '\t') || (*in == '\n') || (*in == '\r')) ) { - ++in; - } - start = in; - if( start == end ) { - oend = end; - return start; - } - - bool found_dot = false; - // check for literal vs object, array or string - switch( *in ) { - case ':': - case ',': - case '=': - oend = in+1; - return start; - case '[': - case '{': - case '"': - break; - default: { // literal - // read until non-literal character - // allow it to start with - - // allow only one '.' - while( in != end ) { - switch( *in ) { - case '[': case ']': - case '{': case '}': - case ':': case '=': - case ',': case '"': - case ' ': case '\t': case '\n': case '\r': { - oend = in; - return start; - } - case '.': - if( found_dot ) { - oend = in; - return start; - } - found_dot = true; - break; - case '-': - if( in-start ){ oend = in; return start; } - } - ++in; - } - oend = in; - return start; - } - } // end literal check - - int depth = 0; - bool in_quote = false; - bool in_escape = false; - // read until closing ]} or " ignoring escaped " - while( in != end ) { - if( !in_quote ) { - switch( *in ) { - case '[': - case '{': ++depth; break; - case ']': - case '}': --depth; break; - case '"': - ++depth; - in_quote = true; - break; - default: // do nothing; - break; - } - } else { // in quote - switch( *in ) { - case '"': if( !in_escape ) { - --depth; - in_quote = false; - break; - } - case '\\': - in_escape = !in_escape; - break; - default: - in_escape = false; - } - } - ++in; - if( !depth ) { oend = in; return start; } - } - if( depth != 0 ) { - // TODO: Throw Parse Error! - elog("Parse Error!! '%s' size: %d", fc::string( oin, end ).c_str(), (oin-start)); - } - oend = in; return start; -} - -struct temp_set { - temp_set( char* v, char t ) - :old(*v),val(v) { *val = t; } - ~temp_set() { *val = old; } - char old; - char* val; -}; - -/** - * A,B,C - * Warn on extra ',' or missing ',' - */ -void read_values( fc::value::array& a, char* in, char* end, error_collector& ec ) { - if( in >= end ) return; - char* ve = 0; - char* v = read_value( in, end, ve ); - while( *v == ',' ) { - wlog( "unexpected ','"); - v = read_value( ve, end, ve ); - } - if( v == ve ) return; // no values - - { temp_set temp(ve,'\0'); a.push_back( to_value( v, ve, ec ) ); } - - char* c; - char* ce = 0; - do { // expect comma + value | '' - - // expect comma or '' - c = read_value( ve, end, ce ); - - // '' means we are done, no errors - if( c == ce ) return; - - if( *c != ',' ) // we got a value when expecting ',' - { - wlog( "missing ," ); - temp_set temp(ce,'\0'); a.push_back( to_value(c, ce, ec) ); - ve = ce; - continue; // start back at start - } - - // expect value - v = read_value( ce, end, ve ); - while ( *v == ',' ) { // but got comma - // expect value - wlog( "extra comma at c->ce" ); - c = v; ce = ve; - v = read_value( ve, end, ve ); - } - if( v == ve ) { - wlog( "trailing comma at c->ce" ); - } else { // got value - temp_set temp(ve,'\0'); - a.push_back( to_value( v, ve, ec) ); - } - } while( ve < end );// expect comma + value | '' -} - - -/** - * Reads one level deep, does not recruse into sub objects. - */ -char* read_key_val( std::map& obj, bool sc, char* in, char* end, error_collector& ec ) { - char* name_end = 0; - char* name = in; - do { - // read first char - name = read_value( name, end, name_end ); - if( sc ) { // if we expect a , - if( *name != ',' ) { // but didn't get one - wlog( "expected ',' but got %1%", name ); // warn and accept name - } else { // we got the exepcted , read the expected name - name = read_value( name_end, end, name_end ); - } - } else { // don't start with ',' - while( *name == ',' ) { // while we don't have a name, keep looking - wlog( "unexpected ',' " ); - name = read_value( name_end, end, name_end ); - } - } - } while( *name == ',' ); - - - // now we should have a name. - if( name_end >= end -1 ) { - temp_set ntemp(name_end,'\0'); - elog( "early end after reading name %1%", name ); - return name_end; - } - if( *name != '"' ) { - temp_set ntemp(name_end,'\0'); - wlog( "unquoted name '%1%'", name ); - } else { - temp_set ntemp(name_end,'\0'); - name = fc::json::inplace_unescape_string(name); - } - - char* col_end = 0; - char* col = read_value( name_end, end, col_end ); - - char* val_end = 0; - char* val = 0; - - bool sep_error = false; - if( col_end-col == 1 ) { - switch( *col ) { - case ':': break; - case ';': - case '=': - wlog( "found %1% instead of ':'", *col ); - break; - default: - sep_error = true; - } - } else { - sep_error = true; - } - - if( sep_error ) { - temp_set ntemp(name_end,'\0'); - temp_set vtemp(col_end,'\0'); - wlog( "expected ':' but got %1%", col ); - wlog( "assuming this is the value... " ); - val = col; - val_end = col_end; - } else { - if( name_end >= end -1 ) { - temp_set ntemp(name_end,'\0'); - temp_set vtemp(col_end,'\0'); - elog( "early end after reading name '%1%' and col '%2%'", name, col ); - return name_end; - } - val = read_value( col_end, end, val_end ); - if( val == end ) { - wlog( "no value specified" ); - } - } - temp_set ntemp(name_end,'\0'); - temp_set vtemp(val_end,'\0'); - //slog( "name: '%1%'", fc::string(name,name_end) ); - obj[name] = std::vector(val,val_end); -// obj.fields.push_back( key_val( name, to_value( val, val_end, ec ) ) ); - return val_end; -} - - - - - -/** - * Reads an optional ',' followed by key : value, returning the next input position - * @param sc - start with ',' - */ -char* read_key_val( fc::value::object& obj, bool sc, char* in, char* end, error_collector& ec ) { - char* name_end = 0; - char* name = in; - do { - // read first char - name = read_value( name, end, name_end ); - if( name == name_end ) - return name; - if( sc ) { // if we expect a , - if( *name != ',' ) { // but didn't get one - if( *name != '}' ) - wlog( "expected ',' or '}' but got '%s'", name ); // warn and accept name - } else { // we got the exepcted , read the expected name - name = read_value( name_end, end, name_end ); - } - } else { // don't start with ',' - while( *name == ',' ) { // while we don't have a name, keep looking - wlog( "unexpected ',' " ); - name = read_value( name_end, end, name_end ); - } - } - } while( *name == ',' ); - - - // now we should have a name. - if( name_end >= end -1 ) { - temp_set ntemp(name_end,'\0'); - elog( "early end after reading name '%s'", name ); - return name_end; - } - if( *name != '"' ) { - temp_set ntemp(name_end,'\0'); - wlog( "unquoted name '%1%'", name ); - } else { - temp_set ntemp(name_end,'\0'); - name = fc::json::inplace_unescape_string(name); - } - - char* col_end = 0; - char* col = read_value( name_end, end, col_end ); - - char* val_end = 0; - char* val = 0; - - bool sep_error = false; - if( col_end-col == 1 ) { - switch( *col ) { - case ':': break; - case ';': - case '=': - wlog( "found %1% instead of ':'", *col ); - break; - default: - sep_error = true; - } - } else { - sep_error = true; - } - - if( sep_error ) { - temp_set ntemp(name_end,'\0'); - temp_set vtemp(col_end,'\0'); - wlog( "expected ':' but got %1%", col ); - wlog( "assuming this is the value... " ); - val = col; - val_end = col_end; - } else { - if( name_end >= end -1 ) { - temp_set ntemp(name_end,'\0'); - temp_set vtemp(col_end,'\0'); - elog( "early end after reading name '%1%' and col '%2%'", name, col ); - return name_end; - } - val = read_value( col_end, end, val_end ); - if( val == end ) { - wlog( "no value specified" ); - } - } - temp_set ntemp(name_end,'\0'); - temp_set vtemp(val_end,'\0'); - obj.fields.push_back( fc::value::key_val( name, to_value( val, val_end, ec ) ) ); - return val_end; -} - -// first_key =:: '' | "name" : VALUE *list_key -// list_key '' | ',' "name" : VALUE -void read_key_vals( fc::value::object& obj, char* in, char* end, error_collector& ec ) { - bool ex_c = false; - char* kv_end = in; - do { - //slog( "%1% bytes to read", (end-kv_end) ); - kv_end = read_key_val( obj, ex_c, kv_end, end, ec ); - ex_c = true; - } while( kv_end < end ); -} -// first_key =:: '' | "name" : VALUE *list_key -// list_key '' | ',' "name" : VALUE -std::map read_key_vals( char* in, char* end, error_collector& ec ) { - std::map m; - bool ex_c = false; - char* kv_end = in; - do { - //slog( "%1% bytes to read", (end-kv_end) ); - kv_end = read_key_val( m, ex_c, kv_end, end, ec ); - ex_c = true; - } while( kv_end < end ); - return m; -} - - - -/** - * @brief adaptor for to_value( char*, char*, error_collector& ) - */ -fc::value to_value( fc::vector&& v, error_collector& ec ) { - if( v.size() == 0 ) return fc::value(); - return to_value( v.data(), v.data() + v.size(), ec ); -} - -/** - * Returns a fc::value containing from the json string. - * - * @param ec - determines how to respond to parse errors and logs - * any errors that occur while parsing the string. - */ -fc::value to_value( char* start, char* end, error_collector& ec ) { - if( start == end ) return fc::value(); - - char* ve = 0; - char* s = read_value( start, end, ve ); - //slog( "'%1%'", fc::string(start,ve) ); - switch( s[0] ) { - case '[': { - fc::value::array a; - read_values( a, s+1, ve -1, ec ); - return a; - } - case '{': { - fc::value::object o; - read_key_vals( o, s+1, ve -1, ec ); - fc::value v = fc::move(o); - return v; - } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - temp_set move_end(ve,'\0'); - for( char* n = s+1; n != ve; ++n ) { - if( *n == '.' ) { - return fc::lexical_cast(s); - } - } - return fc::lexical_cast(s); - } - case '-': { - temp_set move_end(ve,'\0'); - for( char* n = s+1; n != ve; ++n ) { - if( *n == '.' ) { - return fc::lexical_cast(s); - } - } - return fc::lexical_cast(s); - } - case '.': { - temp_set move_end(ve,'\0'); - return fc::lexical_cast(s); - } - case '\"': { - temp_set move_end(ve,'\0'); - return fc::json::inplace_unescape_string( s ); - } - case 'n': { - temp_set move_end(ve,'\0'); - if( strcmp(s,"null" ) == 0) return fc::value(); - } - case 't': { - temp_set move_end(ve,'\0'); - if( strcmp(s,"true" ) == 0) return true; - } - case 'f': { - temp_set move_end(ve,'\0'); - if( strcmp(s,"false" ) == 0) return false; - } - - default: - wlog( "return unable to parse... return as string '%s'", fc::string(s,ve).c_str() ); - return fc::value( fc::string( s, ve) ); - } -} -namespace fc { namespace json { - -fc::string pretty_print( const fc::string& v, uint8_t indent ) { - int level = 0; - fc::stringstream ss; - bool first = false; - bool quote = false; - bool escape = false; - for( uint32_t i = 0; i < v.size(); ++i ) { - switch( v[i] ) { - case '\\': - if( !escape ) { - if( quote ) - escape = true; - } else { escape = false; } - ss<&& v, uint8_t indent ) { - int level = 0; - fc::stringstream ss; - bool first = false; - bool quote = false; - bool escape = false; - for( uint32_t i = 0; i < v.size(); ++i ) { - switch( v[i] ) { - case '\\': - if( !escape ) { - if( quote ) - escape = true; - } else { escape = false; } - ss< - struct value_visitor : value::const_visitor { - value_visitor( Stream& s ):os(s){} - Stream& os; - virtual void operator()( const int8_t& v ){ os << v; } - virtual void operator()( const int16_t& v ){ os << v; } - virtual void operator()( const int32_t& v ){ os << v; } - virtual void operator()( const int64_t& v ){ os << v; } - virtual void operator()( const uint8_t& v ){ os << v; } - virtual void operator()( const uint16_t& v ){ os << v; } - virtual void operator()( const uint32_t& v ){ os << v; } - virtual void operator()( const uint64_t& v ){ os << v; } - virtual void operator()( const float& v ){ os << v; } - virtual void operator()( const double& v ){ os << v; } - virtual void operator()( const bool& v ){ os << (v ? "true" : "false"); } - virtual void operator()( const fc::string& v ){ os << '"' << fc::json::escape_string(v) <<'"'; } - virtual void operator()( const fc::value::object& o ){ - os << '{'; - for( uint32_t i = 0; i < o.fields.size(); ++i ) { - if( i ) os <<','; - (*this)( o.fields[i].key ); - os<<':'; - o.fields[i].val.visit( value_visitor(*this) ); - } - os << '}'; - } - virtual void operator()( const value::array& o ){ - os << '['; - for( auto i = o.begin(); i != o.end(); ++i ) { - if( i != o.begin() ) os <<','; - i->visit( value_visitor(*this) ); - } - os << ']'; - } - virtual void operator()( ){ os << "null"; } - }; - - template - void to_json( const fc::value& v, Stream& os ) { - v.visit( value_visitor(os) ); - } - - void write( ostream& out, const value& val ) { - to_json( val, out ); - } - - fc::string to_string( const fc::value& v ) { - fc::stringstream ss; - to_json( v, ss ); - return ss.str(); - } - - fc::string to_pretty_string( const fc::value& v ) { - return pretty_print( to_string(v), 4 ); - } - - value from_file( const fc::path& local_path ) { - if( !exists(local_path) ) { - FC_THROW_REPORT( "Source file ${filename} does not exist", value().set("filename",local_path.string()) ); - } - if( is_directory( local_path ) ) { - FC_THROW_REPORT( "Source path ${path} is a directory; a file was expected", - value().set("path",local_path.string()) ); - } - try { - // memory map the file - size_t fsize = static_cast(file_size(local_path)); - if( fsize == 0 ) { return value(); } - file_mapping fmap( local_path.string().c_str(), read_only ); - - - mapped_region mr( fmap, fc::read_only, 0, fsize ); - - const char* pos = reinterpret_cast(mr.get_address()); - const char* end = pos + fsize; - - // TODO: implement a const version of to_value - fc::vector tmp(pos,end); - - error_collector ec; - return to_value(tmp.data(), tmp.data()+fsize,ec); - } catch ( ... ) { - FC_THROW_REPORT( "Error loading JSON object from file '${path}'", - fc::value().set( "file", local_path ) - .set( "exception", fc::except_str() ) - ); - } - } - - value from_string( const fc::string& s ) { - std::vector v(s.begin(),s.end()); - //slog( "from_string( '%s' )", s.c_str() ); - return from_string( v.data(), v.data()+v.size() ); - } - value from_string( fc::vector&& v ) { - error_collector ec; - return to_value( v.data(), v.data() + v.size(), ec ); - } - - value from_string( const char* s, const char* e ) { - return from_string( fc::vector(s,e) ); - } - -} } diff --git a/src/json_rpc_connection.cpp b/src/json_rpc_connection.cpp deleted file mode 100644 index 4f96cbaed..000000000 --- a/src/json_rpc_connection.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { namespace json { - - namespace detail { - - void pending_result::handle_error( const fc::string& e ) { - try { - FC_THROW_MSG( "%s", e ); - } catch ( ... ) { - set_exception( fc::current_exception() ); - } - } - - } - - class rpc_connection::impl : public fc::retainable { - public: - impl():_next_req_id(0){ } - ~impl(){ cancel_pending_requests(); } - int64_t _next_req_id; - std::unordered_map _methods; - - detail::pending_result::ptr _pr_head; - detail::pending_result::ptr _pr_tail; - - void cancel_pending_requests() { - auto cur = _pr_head; - while( cur ) { - cur->set_exception( fc::copy_exception( fc::generic_exception("canceled") ) ); - cur = cur->next; - } - _pr_head.reset(); - _pr_tail.reset(); - } - }; - - - void rpc_connection::add_method( const fc::string& name, fc::shared_ptr&& p ) { - my->_methods[name] = fc::move(p); - } - void rpc_connection::handle_message( const value& v ) { - auto id_itr = v.find( "id" ); - auto m_itr = v.find( "method" ); - auto end = v.end(); - - if( m_itr != end ) { - fc::string mname = value_cast(m_itr->val); - - auto smeth = my->_methods.find( mname ); - if( smeth == my->_methods.end() ) { - if( id_itr != end ) { - // TODO: send invalid method reply - auto id = value_cast(id_itr->val); - send_error( id, fc::json::error_object( "Unknown method '"+mname+"'" )); - } - // nothing to do, unknown method - } else { // known method, attempt to call it and send reply; - auto p_itr = v.find( "params" ); - - value nul; - const value& params = (p_itr != end) ? p_itr->val : nul; - // slog( "params '%s'", to_string( params ).c_str() ); - - if( id_itr != end ) { // capture reply - auto id = value_cast(id_itr->val); - try { - send_result( id, smeth->second->call(params) ); - } catch ( fc::error_report& eo ) { - if( eo.stack.size() ) { - send_error( id, error_object( eo.current().desc, fc::value(eo) ) ); - } else { - send_error( id, error_object( "error report", fc::value(eo) ) ); - } - } catch ( const fc::json::error_object& eo ) { - wlog( "%s", eo.message.c_str() ); - send_error( id, eo ); - } catch ( ... ) { - wlog( "%s", fc::except_str().c_str() ); - send_error( id, error_object( fc::except_str(), fc::value() ) ); - } - } else { // ignore exception + result - try { smeth->second->call( params ); }catch(...){} - } - } - return; - } else if( id_itr != end ) { // we id but no method, therefore potential reply - int id = value_cast(id_itr->val); - auto cur = my->_pr_head; - decltype(cur) prev; - while( cur ) { - if( cur->id == id ) { - if( prev ) prev->next = cur->next; - else my->_pr_head = cur->next; - if( !cur->next ) my->_pr_tail = cur->next; - - try { - auto r_itr = v.find( "result" ); - if( r_itr != end ) { - cur->handle_result( r_itr->val ); - } else { - auto e_itr = v.find( "error" ); - if( e_itr != end ) { - cur->set_exception( fc::value_cast(e_itr->val["data"]).copy_exception() ); - } else { - elog( "no result nor error field" ); - } - } - } catch( ... ) { - cur->set_exception( fc::current_exception() ); - } - return; - } - cur = cur->next; - } - FC_THROW_REPORT( "Unexpected reply with id ${id}", id_itr->val ); - } - FC_THROW_REPORT( "Method with no 'id' or 'method' field" ); - } - - - rpc_connection::rpc_connection() - :my( new impl() ){ } - - rpc_connection::rpc_connection( const rpc_connection& c ) - :my(c.my){ } - rpc_connection::rpc_connection( rpc_connection&& c ) { - fc_swap(my,c.my); - } - rpc_connection::~rpc_connection() { - } - - rpc_connection& rpc_connection::operator=(const rpc_connection& m) { - my= m.my; - return *this; - } - rpc_connection& rpc_connection::operator=(rpc_connection&& m) { - fc_swap(m.my,my); - return *this; - } - - void rpc_connection::cancel_pending_requests() { - my->cancel_pending_requests(); - } - - void rpc_connection::invoke( detail::pending_result::ptr&& p, - const fc::string& m, value&& param ) { - p->id = my->_next_req_id; - if( my->_pr_tail ) { - my->_pr_tail->next = p; - my->_pr_tail = my->_pr_tail->next; - } else { - my->_pr_tail = p; - my->_pr_head = my->_pr_tail; - } - send_invoke( my->_next_req_id++, m, fc::move(param) ); - } - - -} } // fc::json diff --git a/src/json_rpc_error_object.cpp b/src/json_rpc_error_object.cpp deleted file mode 100644 index 0c6a18965..000000000 --- a/src/json_rpc_error_object.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - - -namespace fc { namespace json { - error_object::error_object( const fc::string& m, const fc::value& v, int64_t c ) - :code(c),message(m),data(v){ } - error_object::error_object(const fc::string& m, int64_t c) - :code(c),message(m){ } - error_object::error_object(const error_object& e) - :code(e.code),message(e.message),data(e.data){} - error_object::~error_object(){} -}} diff --git a/src/json_rpc_stream_connection.cpp b/src/json_rpc_stream_connection.cpp deleted file mode 100644 index e00d11ac4..000000000 --- a/src/json_rpc_stream_connection.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -namespace fc { namespace json { - - class rpc_stream_connection::impl : public fc::retainable { - public: - fc::istream& in; - fc::ostream& out; - rpc_stream_connection& self; - std::function on_close; - - fc::logger log; - - impl( fc::istream& i, fc::ostream& o, rpc_stream_connection& s ) - :in(i),out(o),self(s){ - _read_loop_complete = fc::async( [=](){ read_loop(); } ); - log = logger::get( "fc::json::rpc_stream_connection" ); - } - - ~impl() { - try { - self.cancel_pending_requests(); - _read_loop_complete.cancel(); - _read_loop_complete.wait(); - } catch ( ... ) {} - } - - fc::future _read_loop_complete; - void read_loop() { - try { - fc::string line; - fc::getline( in, line ); - while( !in.eof() ) { - fc::async( [=]() { - try { - fc_dlog( log, "Received: '${line}'", ("line",line) ); - fc::value v= fc::json::from_string( line ); - self.handle_message(v); - } catch ( fc::error_report& er ) { - fc_wlog( log, "Error handling request '${line}'\n ${report}", ("line",line)("report",er.to_detail_string() ) ); - } catch (...) { - fc_wlog( log, "Error handling request '${exception}'",("exception",fc::except_str()) ); - return; - } - }); - fc::getline( in, line ); - } - } catch ( ... ) { - wlog( "%s", fc::except_str().c_str() ); - } - // slog( "cancel..."); - self.cancel_pending_requests(); - // slog( "close!" ); - if( !!on_close ) on_close(); - } - }; - - rpc_stream_connection::rpc_stream_connection( fc::istream& i, fc::ostream& o ) - :my( new impl(i,o,*this) ){ - } - rpc_stream_connection::rpc_stream_connection(){ } - rpc_stream_connection::rpc_stream_connection(const rpc_stream_connection& c):my(c.my){ } - rpc_stream_connection::~rpc_stream_connection(){ - close(); - } - - // the life of the streams must exceed the life of all copies - // of this rpc_stream_connection - void rpc_stream_connection::open( fc::istream& i, fc::ostream& o) { - my.reset( new impl(i,o,*this) ); - } - - // cancels all pending requests, closes the ostream - // results on_close() being called if the stream is not already closed. - void rpc_stream_connection::close() { - if( my ) my->out.close(); - my.reset(nullptr); - } - - /** - * When the connection is closed, call the given method - */ - void rpc_stream_connection::on_close( const std::function& oc ) { - my->on_close = oc; - } - - void rpc_stream_connection::send_invoke( uint64_t id, const fc::string& m, value&& param ) { - fc::stringstream ss; - ss<<"{\"id\":"<log, "Sent: '${line}'", ("line",o) ); - my->out.write( o.c_str(), o.size() ); - my->out.flush(); - } - void rpc_stream_connection::send_notice( const fc::string& m, value&& param ) { - fc::stringstream ss; - ss<<"{\"method\":\""<log, "Sent: '${line}'", ("line",o) ); - my->out.write( o.c_str(), o.size() ); - my->out.flush(); - } - void rpc_stream_connection::send_error( uint64_t id, const json::error_object& eo ) { - fc::stringstream ss; - ss<<"{\"id\":"<log, "Sent: '${line}'", ("line",o) ); - my->out.write( o.c_str(), o.size() ); - my->out.flush(); - } - void rpc_stream_connection::send_result( uint64_t id, value&& r ) { - fc::stringstream ss; - ss<<"{\"id\":"<log, "Sent: '${line}'", ("line",o) ); - my->out.write( o.c_str(), o.size() ); - my->out.flush(); - } - -} } // fc::json - - - diff --git a/src/json_rpc_tcp_connection.cpp b/src/json_rpc_tcp_connection.cpp deleted file mode 100644 index 52dfee96b..000000000 --- a/src/json_rpc_tcp_connection.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -namespace fc { - - namespace json { - class rpc_tcp_connection::impl : public fc::retainable { - public: - tcp_socket sock; - ~impl(){ } - }; - - rpc_tcp_connection::rpc_tcp_connection() - :my( new impl() ){ - } - rpc_tcp_connection::rpc_tcp_connection( const rpc_tcp_connection& c ) - :rpc_stream_connection(c),my(c.my){} - - rpc_tcp_connection::~rpc_tcp_connection(){ - close(); - } - - void rpc_tcp_connection::connect_to( const fc::ip::endpoint& ep ) { - my->sock.connect_to(ep); - open( my->sock, my->sock ); - } - void rpc_tcp_connection::start() { - open( my->sock, my->sock ); - } - void rpc_tcp_connection::close() { - rpc_stream_connection::close(); - //my->sock.close(); - } - - tcp_socket& rpc_tcp_connection::get_socket()const { return my->sock; } - - - } - -} diff --git a/src/json_rpc_tcp_server.cpp b/src/json_rpc_tcp_server.cpp deleted file mode 100644 index f5e6d398a..000000000 --- a/src/json_rpc_tcp_server.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -namespace fc { - namespace json { - class rpc_tcp_server::impl { - public: - std::function on_con; - fc::tcp_server tcp_serv; - fc::vector cons; - }; - rpc_tcp_server::rpc_tcp_server() - :my( new impl() ){} - rpc_tcp_server::~rpc_tcp_server() { - delete my; - } - - void rpc_tcp_server::on_new_connection( const std::function& c ) { - my->on_con = c; - } - - void rpc_tcp_server::listen( uint16_t port ) { - - my->tcp_serv.listen(port); - fc::async([this](){ - try { - rpc_tcp_connection::ptr con(new rpc_tcp_connection() ); - while( my->tcp_serv.accept( con->get_socket() ) ) { - slog( "new connection!" ); - my->on_con( *con ); - con->start(); - rpc_tcp_connection* tcpc = con.get(); - my->cons.push_back(con); - con->on_close( [=]() { - for( int i = 0; i < my->cons.size(); ++i ) { - if( my->cons[i].get() == tcpc ) { - fc_swap( my->cons[i], my->cons.back() ); - auto tmp = my->cons.back(); - my->cons.pop_back(); - fc::async([tmp](){slog("free con");}); - // TODO: swap to end, pop back - return; - } - } - }); - con.reset(new rpc_tcp_connection() ); - } - } catch ( ... ) { - wlog( "tcp listen failed..." ); - } - }); - } - - - } -} diff --git a/src/lexical_cast.cpp b/src/lexical_cast.cpp deleted file mode 100644 index 142e200c3..000000000 --- a/src/lexical_cast.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -namespace fc { - - namespace detail { - double to_double( const fc::string& s ) { - return boost::lexical_cast(s.c_str()); - } - int64_t to_int64( const fc::string& s ) { - return boost::lexical_cast(s.c_str()); - } - uint64_t to_uint64( const fc::string& s ) { - return boost::lexical_cast(s.c_str()); - } - fc::string to_string( double d ){ return boost::lexical_cast(d); } - #ifdef __APPLE__ - fc::string to_string( size_t d ){ return boost::lexical_cast(d); } - #endif - fc::string to_string( uint64_t d ){ return boost::lexical_cast(d); } - fc::string to_string( uint32_t d ){ return boost::lexical_cast(d); } - fc::string to_string( uint16_t d ){ return boost::lexical_cast(d); } - fc::string to_string( uint8_t d ){ return boost::lexical_cast(d); } - fc::string to_string( int64_t d ){ return boost::lexical_cast(d); } - fc::string to_string( int32_t d ){ return boost::lexical_cast(d); } - fc::string to_string( int16_t d ){ return boost::lexical_cast(d); } - fc::string to_string( int8_t d ){ return boost::lexical_cast(d); } - fc::string to_string( char d ){ return boost::lexical_cast(d); } - } -} diff --git a/src/log.cpp b/src/log.cpp deleted file mode 100644 index fa1002548..000000000 --- a/src/log.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif - -#include -#include - -namespace fc { - const char* thread_name(); - void* thread_ptr(); - - const char* short_name( const char* file_name ) { - const char* end = file_name + strlen(file_name); - --end; - while( end >= file_name ) { - if( *end == '/' || *end == '\\' ) { - return end + 1; - } - --end; - } - return file_name; - } - boost::mutex& log_mutex() { - static boost::mutex* m = new boost::mutex(); return *m; - } - - #ifdef WIN32 - #define isatty _isatty - #define fileno _fileno - #endif // WIN32 - - void log( const char* color, const char* file_name, size_t line_num, - const char* method_name, const char* format, ... ) { - #ifndef WIN32 - if(isatty(fileno(stderr))) - fprintf( stderr, "\r%s",color); - #endif - fc::unique_lock lock(log_mutex()); - // fc::string sname = fc::path(file_name).filename().generic_string(); - fprintf( stderr, "%-15s %-15s %-5d %-15s ", thread_name(), short_name(file_name), int(line_num), method_name ); - va_list args; - va_start(args,format); - vfprintf( stderr, format, args ); - va_end(args); - #ifndef WIN32 - if (isatty(fileno(stderr))) - fprintf( stderr, "%s", CONSOLE_DEFAULT ); - #endif - fprintf( stderr, "\n" ); - fflush( stderr ); - return; - } - - /** used to add extra fields to be printed (thread,fiber,time,etc) */ - void add_log_field( void (*f)( ) ) { - } - - void remove_log_field( void (*f)( ) ) { - } -} diff --git a/src/appender.cpp b/src/log/appender.cpp similarity index 55% rename from src/appender.cpp rename to src/log/appender.cpp index 61404791f..c4d9c9e2f 100644 --- a/src/appender.cpp +++ b/src/log/appender.cpp @@ -1,14 +1,15 @@ -#include -#include -#include -#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include "console_defines.h" namespace fc { @@ -22,6 +23,8 @@ namespace fc { return lm; } appender::ptr appender::get( const fc::string& s ) { + static fc::spin_lock appender_spinlock; + scoped_lock lock(appender_spinlock); return get_appender_map()[s]; } bool appender::register_appender( const fc::string& type, const appender_factory::ptr& f ) @@ -29,11 +32,11 @@ namespace fc { get_appender_factory_map()[type] = f; return true; } - appender::ptr appender::create( const fc::string& name, const fc::string& type, const value& args ) + appender::ptr appender::create( const fc::string& name, const fc::string& type, const variant& args ) { auto fact_itr = get_appender_factory_map().find(type); if( fact_itr == get_appender_factory_map().end() ) { - wlog( "Unknown appender type '%s'", type.c_str() ); + //wlog( "Unknown appender type '%s'", type.c_str() ); return appender::ptr(); } auto ap = fact_itr->second->create( args ); @@ -41,4 +44,8 @@ namespace fc { return ap; } + static bool reg_console_appender = appender::register_appender( "console" ); + static bool reg_file_appender = appender::register_appender( "file" ); + static bool reg_gelf_appender = appender::register_appender( "gelf" ); + } // namespace fc diff --git a/src/log/console_appender.cpp b/src/log/console_appender.cpp new file mode 100644 index 000000000..c012743f8 --- /dev/null +++ b/src/log/console_appender.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#define COLOR_CONSOLE 1 +#include "console_defines.h" +#include +#include +#include +#include + + +namespace fc { + + class console_appender::impl { + public: + config cfg; + color::type lc[log_level::off+1]; +#ifdef WIN32 + HANDLE console_handle; +#endif + }; + + console_appender::console_appender( const variant& args ) + :my(new impl) + { + configure( args.as() ); + } + + console_appender::console_appender( const config& cfg ) + :my(new impl) + { + configure( cfg ); + } + console_appender::console_appender() + :my(new impl){} + + + void console_appender::configure( const config& console_appender_config ) + { try { +#ifdef WIN32 + my->console_handle = INVALID_HANDLE_VALUE; +#endif + my->cfg = console_appender_config; +#ifdef WIN32 + if (my->cfg.stream = stream::std_error) + my->console_handle = GetStdHandle(STD_ERROR_HANDLE); + else if (my->cfg.stream = stream::std_out) + my->console_handle = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + + for( int i = 0; i < log_level::off+1; ++i ) + my->lc[i] = color::console_default; + for( auto itr = my->cfg.level_colors.begin(); itr != my->cfg.level_colors.end(); ++itr ) + my->lc[itr->level] = itr->color; + } FC_CAPTURE_AND_RETHROW( (console_appender_config) ) } + + console_appender::~console_appender() {} + + #ifdef WIN32 + static WORD + #else + static const char* + #endif + get_console_color(console_appender::color::type t ) { + switch( t ) { + case console_appender::color::red: return CONSOLE_RED; + case console_appender::color::green: return CONSOLE_GREEN; + case console_appender::color::brown: return CONSOLE_BROWN; + case console_appender::color::blue: return CONSOLE_BLUE; + case console_appender::color::magenta: return CONSOLE_MAGENTA; + case console_appender::color::cyan: return CONSOLE_CYAN; + case console_appender::color::white: return CONSOLE_WHITE; + case console_appender::color::console_default: + default: + return CONSOLE_DEFAULT; + } + } + + boost::mutex& log_mutex() { + static boost::mutex m; return m; + } + + void console_appender::log( const log_message& m ) { + //fc::string message = fc::format_string( m.get_format(), m.get_data() ); + //fc::variant lmsg(m); + + FILE* out = stream::std_error ? stderr : stdout; + + //fc::string fmt_str = fc::format_string( cfg.format, mutable_variant_object(m.get_context())( "message", message) ); + std::stringstream file_line; + file_line << m.get_context().get_file() <<":"< lock(log_mutex()); + + print( line.str(), my->lc[m.get_context().get_log_level()] ); + + fprintf( out, "\n" ); + + if( my->cfg.flush ) fflush( out ); + } + + void console_appender::print( const std::string& text, color::type text_color ) + { + FILE* out = stream::std_error ? stderr : stdout; + + #ifdef WIN32 + if (my->console_handle != INVALID_HANDLE_VALUE) + SetConsoleTextAttribute(my->console_handle, get_console_color(text_color)); + #else + if(isatty(fileno(out))) fprintf( out, "\r%s", get_console_color( text_color ) ); + #endif + + if( text.size() ) + fprintf( out, "%s", text.c_str() ); //fmt_str.c_str() ); + + #ifdef WIN32 + if (my->console_handle != INVALID_HANDLE_VALUE) + SetConsoleTextAttribute(my->console_handle, CONSOLE_DEFAULT); + #else + if(isatty(fileno(out))) fprintf( out, "\r%s", CONSOLE_DEFAULT ); + #endif + + if( my->cfg.flush ) fflush( out ); + } + +} diff --git a/include/fc/console_defines.h b/src/log/console_defines.h similarity index 100% rename from include/fc/console_defines.h rename to src/log/console_defines.h diff --git a/src/log/file_appender.cpp b/src/log/file_appender.cpp new file mode 100644 index 000000000..85b69c61b --- /dev/null +++ b/src/log/file_appender.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc { + + class file_appender::impl : public fc::retainable + { + public: + config cfg; + ofstream out; + boost::mutex slock; + + private: + future _rotation_task; + time_point_sec _current_file_start_time; + + time_point_sec get_file_start_time( const time_point_sec& timestamp, const microseconds& interval ) + { + int64_t interval_seconds = interval.to_seconds(); + int64_t file_number = timestamp.sec_since_epoch() / interval_seconds; + return time_point_sec( (uint32_t)(file_number * interval_seconds) ); + } + + public: + impl( const config& c) : cfg( c ) + { + if( cfg.rotate ) + { + FC_ASSERT( cfg.rotation_interval >= seconds( 1 ) ); + FC_ASSERT( cfg.rotation_limit >= cfg.rotation_interval ); + + + + + _rotation_task = async( [this]() { rotate_files( true ); }, "rotate_files(1)" ); + } + } + + ~impl() + { + try + { + _rotation_task.cancel_and_wait("file_appender is destructing"); + } + catch( ... ) + { + } + } + + void rotate_files( bool initializing = false ) + { + FC_ASSERT( cfg.rotate ); + fc::time_point now = time_point::now(); + fc::time_point_sec start_time = get_file_start_time( now, cfg.rotation_interval ); + string timestamp_string = start_time.to_non_delimited_iso_string(); + fc::path link_filename = cfg.filename; + fc::path log_filename = link_filename.parent_path() / (link_filename.filename().string() + "." + timestamp_string); + + { + fc::scoped_lock lock( slock ); + + if( !initializing ) + { + if( start_time <= _current_file_start_time ) + { + _rotation_task = schedule( [this]() { rotate_files(); }, + _current_file_start_time + cfg.rotation_interval.to_seconds(), + "rotate_files(2)" ); + return; + } + + out.flush(); + out.close(); + } + remove_all(link_filename); // on windows, you can't delete the link while the underlying file is opened for writing + if( fc::exists( log_filename ) ) + out.open( log_filename, std::ios_base::out | std::ios_base::app ); + else + out.open( log_filename, std::ios_base::out | std::ios_base::app); + + create_hard_link(log_filename, link_filename); + } + + /* Delete old log files */ + fc::time_point limit_time = now - cfg.rotation_limit; + string link_filename_string = link_filename.filename().string(); + directory_iterator itr(link_filename.parent_path()); + for( ; itr != directory_iterator(); itr++ ) + { + try + { + string current_filename = itr->filename().string(); + if (current_filename.compare(0, link_filename_string.size(), link_filename_string) != 0 || + current_filename.size() <= link_filename_string.size() + 1) + continue; + string current_timestamp_str = current_filename.substr(link_filename_string.size() + 1, + timestamp_string.size()); + fc::time_point_sec current_timestamp = fc::time_point_sec::from_iso_string( current_timestamp_str ); + if( current_timestamp < start_time ) + { + if( current_timestamp < limit_time || file_size( current_filename ) <= 0 ) + { + remove_all( *itr ); + continue; + } + } + } + catch (const fc::canceled_exception&) + { + throw; + } + catch( ... ) + { + } + } + + _current_file_start_time = start_time; + _rotation_task = schedule( [this]() { rotate_files(); }, + _current_file_start_time + cfg.rotation_interval.to_seconds(), + "rotate_files(3)" ); + } + }; + + file_appender::config::config(const fc::path& p) : + format( "${timestamp} ${thread_name} ${context} ${file}:${line} ${method} ${level}] ${message}" ), + filename(p), + flush(true), + rotate(false) + {} + + file_appender::file_appender( const variant& args ) : + my( new impl( args.as() ) ) + { + try + { + fc::create_directories(my->cfg.filename.parent_path()); + + if(!my->cfg.rotate) + my->out.open( my->cfg.filename, std::ios_base::out | std::ios_base::app); + + } + catch( ... ) + { + std::cerr << "error opening log file: " << my->cfg.filename.preferred_string() << "\n"; + } + } + + file_appender::~file_appender(){} + + // MS THREAD METHOD MESSAGE \t\t\t File:Line + void file_appender::log( const log_message& m ) + { + std::stringstream line; + //line << (m.get_context().get_timestamp().time_since_epoch().count() % (1000ll*1000ll*60ll*60))/1000 <<"ms "; + line << string(m.get_context().get_timestamp()) << " "; + line << std::setw( 21 ) << (m.get_context().get_thread_name().substr(0,9) + string(":") + m.get_context().get_task_name()).c_str() << " "; + + string method_name = m.get_context().get_method(); + // strip all leading scopes... + if( method_name.size() ) + { + uint32_t p = 0; + for( uint32_t i = 0;i < method_name.size(); ++i ) + { + if( method_name[i] == ':' ) p = i; + } + + if( method_name[p] == ':' ) + ++p; + line << std::setw( 20 ) << m.get_context().get_method().substr(p,20).c_str() <<" "; + } + + line << "] "; + fc::string message = fc::format_string( m.get_format(), m.get_data() ); + line << message.c_str(); + + //fc::variant lmsg(m); + + // fc::string fmt_str = fc::format_string( my->cfg.format, mutable_variant_object(m.get_context())( "message", message) ); + + { + fc::scoped_lock lock( my->slock ); + my->out << line.str() << "\t\t\t" << m.get_context().get_file() << ":" << m.get_context().get_line_number() << "\n"; + if( my->cfg.flush ) + my->out.flush(); + } + } + +} // fc diff --git a/src/log/gelf_appender.cpp b/src/log/gelf_appender.cpp new file mode 100644 index 000000000..b88387c69 --- /dev/null +++ b/src/log/gelf_appender.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace fc +{ + + class gelf_appender::impl : public retainable + { + public: + config cfg; + optional gelf_endpoint; + udp_socket gelf_socket; + + impl(const config& c) : + cfg(c) + { + } + + ~impl() + { + } + }; + + gelf_appender::gelf_appender(const variant& args) : + my(new impl(args.as())) + { + try + { + try + { + // if it's a numeric address:port, this will parse it + my->gelf_endpoint = ip::endpoint::from_string(my->cfg.endpoint); + } + catch (...) + { + } + if (!my->gelf_endpoint) + { + // couldn't parse as a numeric ip address, try resolving as a DNS name. + // This can yield, so don't do it in the catch block above + string::size_type colon_pos = my->cfg.endpoint.find(':'); + try + { + uint16_t port = boost::lexical_cast(my->cfg.endpoint.substr(colon_pos + 1, my->cfg.endpoint.size())); + + string hostname = my->cfg.endpoint.substr( 0, colon_pos ); + std::vector endpoints = resolve(hostname, port); + if (endpoints.empty()) + FC_THROW_EXCEPTION(unknown_host_exception, "The host name can not be resolved: ${hostname}", + ("hostname", hostname)); + my->gelf_endpoint = endpoints.back(); + } + catch (const boost::bad_lexical_cast&) + { + FC_THROW("Bad port: ${port}", ("port", my->cfg.endpoint.substr(colon_pos + 1, my->cfg.endpoint.size()))); + } + } + + if (my->gelf_endpoint) + my->gelf_socket.open(); + } + catch (...) + { + std::cerr << "error opening GELF socket to endpoint ${endpoint}" << my->cfg.endpoint << "\n"; + } + } + + gelf_appender::~gelf_appender() + {} + + void gelf_appender::log(const log_message& message) + { + if (!my->gelf_endpoint) + return; + + log_context context = message.get_context(); + + mutable_variant_object gelf_message; + gelf_message["version"] = "1.1"; + gelf_message["host"] = my->cfg.host; + gelf_message["short_message"] = format_string(message.get_format(), message.get_data()); + + gelf_message["timestamp"] = context.get_timestamp().time_since_epoch().count() / 1000000.; + + switch (context.get_log_level()) + { + case log_level::debug: + gelf_message["level"] = 7; // debug + break; + case log_level::info: + gelf_message["level"] = 6; // info + break; + case log_level::warn: + gelf_message["level"] = 4; // warning + break; + case log_level::error: + gelf_message["level"] = 3; // error + break; + case log_level::all: + case log_level::off: + // these shouldn't be used in log messages, but do something deterministic just in case + gelf_message["level"] = 6; // info + break; + } + + if (!context.get_context().empty()) + gelf_message["context"] = context.get_context(); + gelf_message["_line"] = context.get_line_number(); + gelf_message["_file"] = context.get_file(); + gelf_message["_method_name"] = context.get_method(); + gelf_message["_thread_name"] = context.get_thread_name(); + if (!context.get_task_name().empty()) + gelf_message["_task_name"] = context.get_task_name(); + + string gelf_message_as_string = json::to_string(gelf_message); + //unsigned uncompressed_size = gelf_message_as_string.size(); + gelf_message_as_string = zlib_compress(gelf_message_as_string); + + // graylog2 expects the zlib header to be 0x78 0x9c + // but miniz.c generates 0x78 0x01 (indicating + // low compression instead of default compression) + // so change that here + assert(gelf_message_as_string[0] == (char)0x78); + if (gelf_message_as_string[1] == (char)0x01 || + gelf_message_as_string[1] == (char)0xda) + gelf_message_as_string[1] = (char)0x9c; + assert(gelf_message_as_string[1] == (char)0x9c); + + // packets are sent by UDP, and they tend to disappear if they + // get too large. It's hard to find any solid numbers on how + // large they can be before they get dropped -- datagrams can + // be up to 64k, but anything over 512 is not guaranteed. + // You can play with this number, intermediate values like + // 1400 and 8100 are likely to work on most intranets. + const unsigned max_payload_size = 512; + + if (gelf_message_as_string.size() <= max_payload_size) + { + // no need to split + std::shared_ptr send_buffer(new char[gelf_message_as_string.size()], + [](char* p){ delete[] p; }); + memcpy(send_buffer.get(), gelf_message_as_string.c_str(), + gelf_message_as_string.size()); + + my->gelf_socket.send_to(send_buffer, gelf_message_as_string.size(), + *my->gelf_endpoint); + } + else + { + // split the message + // we need to generate an 8-byte ID for this message. + // city hash should do + uint64_t message_id = city_hash64(gelf_message_as_string.c_str(), gelf_message_as_string.size()); + const unsigned header_length = 2 /* magic */ + 8 /* msg id */ + 1 /* seq */ + 1 /* count */; + const unsigned body_length = max_payload_size - header_length; + unsigned total_number_of_packets = (gelf_message_as_string.size() + body_length - 1) / body_length; + unsigned bytes_sent = 0; + unsigned number_of_packets_sent = 0; + while (bytes_sent < gelf_message_as_string.size()) + { + unsigned bytes_to_send = std::min((unsigned)gelf_message_as_string.size() - bytes_sent, + body_length); + + std::shared_ptr send_buffer(new char[max_payload_size], + [](char* p){ delete[] p; }); + char* ptr = send_buffer.get(); + // magic number for chunked message + *(unsigned char*)ptr++ = 0x1e; + *(unsigned char*)ptr++ = 0x0f; + + // message id + memcpy(ptr, (char*)&message_id, sizeof(message_id)); + ptr += sizeof(message_id); + + *(unsigned char*)(ptr++) = number_of_packets_sent; + *(unsigned char*)(ptr++) = total_number_of_packets; + memcpy(ptr, gelf_message_as_string.c_str() + bytes_sent, + bytes_to_send); + my->gelf_socket.send_to(send_buffer, header_length + bytes_to_send, + *my->gelf_endpoint); + ++number_of_packets_sent; + bytes_sent += bytes_to_send; + } + assert(number_of_packets_sent == total_number_of_packets); + } + } +} // fc diff --git a/src/log/log_message.cpp b/src/log/log_message.cpp new file mode 100644 index 000000000..09b68158a --- /dev/null +++ b/src/log/log_message.cpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc +{ + namespace detail + { + class log_context_impl + { + public: + log_level level; + string file; + uint64_t line; + string method; + string thread_name; + string task_name; + string hostname; + string context; + time_point timestamp; + }; + + class log_message_impl + { + public: + log_message_impl( log_context&& ctx ) + :context( std::move(ctx) ){} + log_message_impl(){} + + log_context context; + string format; + variant_object args; + }; + } + + + + log_context::log_context() + :my( std::make_shared() ){} + + log_context::log_context( log_level ll, const char* file, uint64_t line, + const char* method ) + :my( std::make_shared() ) + { + my->level = ll; + my->file = fc::path(file).filename().generic_string(); // TODO truncate filename + my->line = line; + my->method = method; + my->timestamp = time_point::now(); + my->thread_name = fc::thread::current().name(); + const char* current_task_desc = fc::thread::current().current_task_desc(); + my->task_name = current_task_desc ? current_task_desc : "?unnamed?"; + } + + log_context::log_context( const variant& v ) + :my( std::make_shared() ) + { + auto obj = v.get_object(); + my->level = obj["level"].as(); + my->file = obj["file"].as_string(); + my->line = obj["line"].as_uint64(); + my->method = obj["method"].as_string(); + my->hostname = obj["hostname"].as_string(); + my->thread_name = obj["thread_name"].as_string(); + if (obj.contains("task_name")) + my->task_name = obj["task_name"].as_string(); + my->timestamp = obj["timestamp"].as(); + if( obj.contains( "context" ) ) + my->context = obj["context"].as(); + } + + fc::string log_context::to_string()const + { + return my->thread_name + " " + my->file + ":" + fc::to_string(my->line) + " " + my->method; + + } + + void log_context::append_context( const fc::string& s ) + { + if (!my->context.empty()) + my->context += " -> "; + my->context += s; + } + + log_context::~log_context(){} + + + void to_variant( const log_context& l, variant& v ) + { + v = l.to_variant(); + } + + void from_variant( const variant& l, log_context& c ) + { + c = log_context(l); + } + + void from_variant( const variant& l, log_message& c ) + { + c = log_message(l); + } + void to_variant( const log_message& m, variant& v ) + { + v = m.to_variant(); + } + + void to_variant( log_level e, variant& v ) + { + switch( e ) + { + case log_level::all: + v = "all"; + return; + case log_level::debug: + v = "debug"; + return; + case log_level::info: + v = "info"; + return; + case log_level::warn: + v = "warn"; + return; + case log_level::error: + v = "error"; + return; + case log_level::off: + v = "off"; + return; + } + } + void from_variant( const variant& v, log_level& e ) + { + try + { + if( v.as_string() == "all" ) e = log_level::all; + else if( v.as_string() == "debug" ) e = log_level::debug; + else if( v.as_string() == "info" ) e = log_level::info; + else if( v.as_string() == "warn" ) e = log_level::warn; + else if( v.as_string() == "error" ) e = log_level::error; + else if( v.as_string() == "off" ) e = log_level::off; + else FC_THROW_EXCEPTION( bad_cast_exception, "Failed to cast from Variant to log_level" ); + } FC_RETHROW_EXCEPTIONS( error, + "Expected 'all|debug|info|warn|error|off', but got '${variant}'", + ("variant",v) ); + } + + + + string log_context::get_file()const { return my->file; } + uint64_t log_context::get_line_number()const { return my->line; } + string log_context::get_method()const { return my->method; } + string log_context::get_thread_name()const { return my->thread_name; } + string log_context::get_task_name()const { return my->task_name; } + string log_context::get_host_name()const { return my->hostname; } + time_point log_context::get_timestamp()const { return my->timestamp; } + log_level log_context::get_log_level()const{ return my->level; } + string log_context::get_context()const { return my->context; } + + + variant log_context::to_variant()const + { + mutable_variant_object o; + o( "level", variant(my->level) ) + ( "file", my->file ) + ( "line", my->line ) + ( "method", my->method ) + ( "hostname", my->hostname ) + ( "thread_name", my->thread_name ) + ( "timestamp", variant(my->timestamp) ); + + if( my->context.size() ) + o( "context", my->context ); + + return o; + } + + log_message::~log_message(){} + log_message::log_message() + :my( std::make_shared() ){} + + log_message::log_message( log_context ctx, std::string format, variant_object args ) + :my( std::make_shared(std::move(ctx)) ) + { + my->format = std::move(format); + my->args = std::move(args); + } + + log_message::log_message( const variant& v ) + :my( std::make_shared( log_context( v.get_object()["context"] ) ) ) + { + my->format = v.get_object()["format"].as_string(); + my->args = v.get_object()["data"].get_object(); + } + + variant log_message::to_variant()const + { + return mutable_variant_object( "context", my->context ) + ( "format", my->format ) + ( "data", my->args ); + } + + log_context log_message::get_context()const { return my->context; } + string log_message::get_format()const { return my->format; } + variant_object log_message::get_data()const { return my->args; } + + string log_message::get_message()const + { + return format_string( my->format, my->args ); + } + + +} // fc + diff --git a/src/logger.cpp b/src/log/logger.cpp similarity index 53% rename from src/logger.cpp rename to src/log/logger.cpp index f1615fdfb..8c4f149c5 100644 --- a/src/logger.cpp +++ b/src/log/logger.cpp @@ -1,55 +1,42 @@ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include - -// tmp... -#include +#include namespace fc { - log_message::log_message(){} - log_message::log_message(log_level::type ll, const string& f, int l, const string& fun, const string& fmt ) - :when( fc::time_point::now() ), level(ll), thread( fc::thread::current().name() ),file(fc::path(f).filename().generic_string()),line(l),method(fun),format(fmt){} - - log_message& log_message::operator()( const fc::string& k, fc::value&& v ) { - args[k] = fc::move(v); - return *this; - } - log_message& log_message::operator()( fc::value&& v ) { - args.push_back( fc::move(v) ); - return *this; - } - log_message& log_message::operator()( const fc::string& k, const fc::value& v ) { - args[k] = v; - return *this; - } - log_message& log_message::operator()( const fc::value& v ) { - args.push_back( v ); - return *this; - } class logger::impl : public fc::retainable { public: impl() - :_parent(nullptr),_enabled(true),_level(log_level::warn){} + :_parent(nullptr),_enabled(true),_additivity(false),_level(log_level::warn){} fc::string _name; logger _parent; bool _enabled; bool _additivity; - log_level::type _level; + log_level _level; - fc::vector _appenders; + std::vector _appenders; }; logger::logger() :my( new impl() ){} - logger::logger(std::nullptr_t){} + logger::logger(nullptr_t){} + + logger::logger( const string& name, const logger& parent ) + :my( new impl() ) + { + my->_name = name; + my->_parent = parent; + } + logger::logger( const logger& l ) :my(l.my){} @@ -70,13 +57,13 @@ namespace fc { bool operator==( const logger& l, std::nullptr_t ) { return !l.my; } bool operator!=( const logger& l, std::nullptr_t ) { return l.my; } - bool logger::is_enabled( log_level::type e )const { + bool logger::is_enabled( log_level e )const { return e >= my->_level; } void logger::log( log_message m ) { - if( !m.context ) m.context = my->_name; - else m.context = *m.context + "->" + my->_name; + m.get_context().append_context( my->_name ); + for( auto itr = my->_appenders.begin(); itr != my->_appenders.end(); ++itr ) (*itr)->log( m ); @@ -87,9 +74,14 @@ namespace fc { void logger::set_name( const fc::string& n ) { my->_name = n; } const fc::string& logger::name()const { return my->_name; } + extern bool do_default_config; + std::unordered_map& get_logger_map() { - static std::unordered_map lm; - return lm; + static bool force_link_default_config = fc::do_default_config; + //TODO: Atomic compare/swap set + static std::unordered_map* lm = new std::unordered_map(); + (void)force_link_default_config; // hide warning; + return *lm; } logger logger::get( const fc::string& s ) { @@ -101,11 +93,21 @@ namespace fc { logger logger::get_parent()const { return my->_parent; } logger& logger::set_parent(const logger& p) { my->_parent = p; return *this; } - log_level::type logger::get_log_level()const { return my->_level; } - logger& logger::set_log_level(log_level::type ll) { my->_level = ll; return *this; } + log_level logger::get_log_level()const { return my->_level; } + logger& logger::set_log_level(log_level ll) { my->_level = ll; return *this; } void logger::add_appender( const fc::shared_ptr& a ) { my->_appenders.push_back(a); } +// void logger::remove_appender( const fc::shared_ptr& a ) + // { my->_appenders.erase(a); } + + std::vector > logger::get_appenders()const + { + return my->_appenders; + } + + bool configure_logging( const logging_config& cfg ); + bool do_default_config = configure_logging( logging_config::default_config() ); } // namespace fc diff --git a/src/logger_config.cpp b/src/log/logger_config.cpp similarity index 54% rename from src/logger_config.cpp rename to src/log/logger_config.cpp index 95619ba52..92dde24fc 100644 --- a/src/logger_config.cpp +++ b/src/log/logger_config.cpp @@ -1,15 +1,18 @@ -#include -#include -#include +#include +#include +#include #include #include #include -#include -#include +#include +#include +#include +#include +#include namespace fc { - std::unordered_map& get_logger_map(); - std::unordered_map& get_appender_map(); + extern std::unordered_map& get_logger_map(); + extern std::unordered_map& get_appender_map(); logger_config& logger_config::add_appender( const string& s ) { appenders.push_back(s); return *this; } void configure_logging( const fc::path& lc ) @@ -18,6 +21,7 @@ namespace fc { } bool configure_logging( const logging_config& cfg ) { + try { static bool reg_console_appender = appender::register_appender( "console" ); static bool reg_file_appender = appender::register_appender( "file" ); get_logger_map().clear(); @@ -32,11 +36,11 @@ namespace fc { auto lgr = logger::get( cfg.loggers[i].name ); // TODO: finish configure logger here... - if( cfg.loggers[i].parent ) { + if( cfg.loggers[i].parent.valid() ) { lgr.set_parent( logger::get( *cfg.loggers[i].parent ) ); } lgr.set_name(cfg.loggers[i].name); - lgr.set_log_level( *cfg.loggers[i].level ); + if( cfg.loggers[i].level.valid() ) lgr.set_log_level( *cfg.loggers[i].level ); for( auto a = cfg.loggers[i].appenders.begin(); a != cfg.loggers[i].appenders.end(); ++a ){ @@ -44,33 +48,34 @@ namespace fc { if( ap ) { lgr.add_appender(ap); } } } - return true; + return reg_console_appender || reg_file_appender; + } catch ( exception& e ) + { + fc::cerr< -#include -#include -#include -#include "context.hpp" -#include "thread_d.hpp" - -namespace fc { - - mutex::mutex() - :m_blist(0){} - - mutex::~mutex() { - if( m_blist ) { - auto c = m_blist; - fc::thread::current().debug("~mutex"); - while( c ) { - elog( "still blocking on context %p (%s)", m_blist, (m_blist->cur_task ? m_blist->cur_task->get_desc() : "no current task") ); - c = c->next_blocked_mutex; - } - } - BOOST_ASSERT( !m_blist && "Attempt to free mutex while others are blocking on lock." ); - } - - /** - * @param next - is set to the next context to get the lock. - * @return the last context (the one with the lock) - */ - static fc::context* get_tail( fc::context* h, fc::context*& next ) { - next = 0; - fc::context* n = h; - if( !n ) return n; - while( n->next_blocked_mutex ) { - next = n; - n=n->next_blocked_mutex; - } - return n; - } - static fc::context* remove( fc::context* head, fc::context* target ) { - fc::context* c = head; - fc::context* p = 0; - while( c ) { - if( c == target ) { - if( p ) { - p->next_blocked_mutex = c->next_blocked_mutex; - return head; - } - return c->next_blocked_mutex; - } - p = c; - c = c->next_blocked_mutex; - } - return head; - } - static void cleanup( fc::mutex& m, fc::spin_yield_lock& syl, fc::context*& bl, fc::context* cc ) { - { - fc::unique_lock lock(syl); - if( cc->next_blocked_mutex ) { - bl = remove(bl, cc ); - return; - } - } - m.unlock(); - } - - /** - * A mutex is considered to hold the lock when - * the current context is the tail in the wait queue. - */ - bool mutex::try_lock() { - fc::thread* ct = &fc::thread::current(); - fc::context* cc = ct->my->current; - fc::context* n = 0; - - fc::unique_lock lock(m_blist_lock, fc::try_to_lock_t()); - if( !lock ) - return false; - - if( !m_blist ) { - m_blist = cc; - return true; - } - // allow recursive locks. - return ( get_tail( m_blist, n ) == cc ); - } - - bool mutex::try_lock_until( const fc::time_point& abs_time ) { - fc::context* n = 0; - fc::context* cc = fc::thread::current().my->current; - - { // lock scope - fc::unique_lock lock(m_blist_lock,abs_time); - if( !lock ) return false; - - if( !m_blist ) { - m_blist = cc; - return true; - } - - // allow recusive locks - if ( get_tail( m_blist, n ) == cc ) - return true; - - cc->next_blocked_mutex = m_blist; - m_blist = cc; - } // end lock scope - try { - fc::thread::current().my->yield_until( abs_time, false ); - return( 0 == cc->next_blocked_mutex ); - } catch (...) { - cleanup( *this, m_blist_lock, m_blist, cc); - throw; - } - } - - void mutex::lock() { - fc::context* n = 0; - fc::context* cc = fc::thread::current().my->current; - { - fc::unique_lock lock(m_blist_lock); - if( !m_blist ) { - m_blist = cc; - return; - } - - // allow recusive locks - if ( get_tail( m_blist, n ) == cc ) { - return; - } - cc->next_blocked_mutex = m_blist; - m_blist = cc; - - int cnt = 0; - auto i = m_blist; - while( i ) { - i = i->next_blocked_mutex; - ++cnt; - } - wlog( "wait queue len %1%", cnt ); - } - - try { - fc::thread::current().yield(false); - BOOST_ASSERT( cc->next_blocked_mutex == 0 ); - } catch ( ... ) { - wlog( "lock with throw %p %s",this, fc::current_exception().diagnostic_information().c_str() ); - cleanup( *this, m_blist_lock, m_blist, cc); - throw; - } - } - - void mutex::unlock() { - fc::context* next = 0; - { fc::unique_lock lock(m_blist_lock); - get_tail(m_blist, next); - if( next ) { - next->next_blocked_mutex = 0; - next->ctx_thread->my->unblock( next ); - } else { - m_blist = 0; - } - } - } - -} // fc - - diff --git a/src/network/gntp.cpp b/src/network/gntp.cpp new file mode 100644 index 000000000..89c17354f --- /dev/null +++ b/src/network/gntp.cpp @@ -0,0 +1,291 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace fc +{ + namespace detail + { + static std::string calc_sha1_base32_of_buffer(const std::string& buffer) + { + sha1::encoder sha1_encoder; + sha1_encoder.write(buffer.c_str(), buffer.size()); + sha1 sha1_result = sha1_encoder.result(); + string sha1_result_base32 = to_base32((char*)&sha1_result, sizeof(sha1_result)); + return sha1_result_base32.c_str(); + } + + + class gntp_icon_impl + { + public: + std::string _icon_bytes; + std::string _sha1_hash; + + gntp_icon_impl(const char* buffer, size_t length) : + _icon_bytes(buffer, length), + _sha1_hash(calc_sha1_base32_of_buffer(_icon_bytes)) + { + } + }; + + class gntp_notifier_impl + { + public: + gntp_notifier_impl(const std::string& host_to_notify = "127.0.0.1", uint16_t port = 23053, const optional& password = optional()); + + // there's no API to change these right now, it will always notify localhost at the default GNTP port + std::string hostname; + uint16_t port; + optional password; + + std::string application_name; + gntp_icon_ptr application_icon; + + gntp_notification_type_list notification_types; // list of all notification types we're registered to send + + optional endpoint; // cache the last endpoint we've connected to + + bool connection_failed; // true after we've tried to connect and failed + bool is_registered; // true after we've registered + + void send_gntp_message(const std::string& message); + }; + + gntp_notifier_impl::gntp_notifier_impl(const std::string& host_to_notify /* = "127.0.0.1" */, uint16_t port /* = 23053 */, + const optional& password /* = optional() */) : + hostname(host_to_notify), + port(port), + password(password), + connection_failed(false), + is_registered(false) + { + } + + void gntp_notifier_impl::send_gntp_message(const std::string& message) + { + std::shared_ptr sock(new boost::asio::ip::tcp::socket(asio::default_io_service())); + + bool connected = false; + if (endpoint) + { + // we've successfully connected before, connect to the same endpoint that worked last time + try + { + asio::tcp::connect(*sock, *endpoint); + connected = true; + } + catch (exception& er) + { + ilog("Failed to connect to GNTP service using an endpoint that previously worked: ${error_report}", + ("error_report", er.to_detail_string())); + sock->close(); + // clear the cached endpoint and fall through to the full connection procedure + endpoint = optional(); + } + catch (...) + { + ilog("Failed to connect to GNTP service using an endpoint that previously worked"); + sock->close(); + // clear the cached endpoint and fall through to the full connection procedure + endpoint = optional(); + } + } + if (!connected) + { + // do the full connection procedure + auto eps = asio::tcp::resolve(hostname, boost::lexical_cast(port)); + if (eps.size() == 0) + FC_THROW("Unable to resolve host '${host}'", ("host", hostname)); + + for (uint32_t i = 0; i < eps.size(); ++i) + { + try + { + boost::system::error_code ec; + ilog("Attempting to connect to GNTP srvice"); + asio::tcp::connect(*sock, eps[i]); + endpoint = eps[i]; + connected = true; + break; + } + catch (const exception& er) + { + ilog("Failed to connect to GNTP service: ${error_reprot}", + ("error_report", er.to_detail_string()) ); + sock->close(); + } + catch (...) + { + ilog("Failed to connect to GNTP service"); + sock->close(); + } + } + } + if (!connected) + FC_THROW("Unable to connect to any resolved endpoint for ${host}:${port}", + ("host", hostname)("port", port)); + try + { + asio::ostream write_stream(sock); + write_stream.write(message.c_str(), message.size()); + write_stream.flush(); + write_stream.close(); + } + catch (exception& er) + { + FC_RETHROW_EXCEPTION(er, warn, "Caught an exception while sending data to GNTP service"); + } + catch (...) + { + FC_THROW("Caught an exception while sending data to GNTP service"); + } + } + } // end namespace detail + + gntp_icon::gntp_icon(const char* buffer, size_t length) : + my(new detail::gntp_icon_impl(buffer, length)) + { + } + gntp_icon::~gntp_icon() + { + } + + gntp_notifier::gntp_notifier(const std::string& host_to_notify /* = "127.0.0.1" */, uint16_t port /* = 23053 */, + const optional& password /* = optional() */) : + my(new detail::gntp_notifier_impl(host_to_notify, port, password)) + { + } + + gntp_notifier::~gntp_notifier() + { + } + + void gntp_notifier::set_application_name(std::string appName) + { + my->application_name = appName; + } + void gntp_notifier::set_application_icon(const gntp_icon_ptr& icon) + { + my->application_icon = icon; + } + void gntp_notifier::add_notification_type(const gntp_notification_type& notification_type) + { + my->notification_types.push_back(notification_type); + } + + void gntp_notifier::register_notifications() + { + // this call will reset any errors + my->connection_failed = false; + my->is_registered = false; + + std::ostringstream message; + std::set icons_used; + + message << "GNTP/1.0 REGISTER NONE\r\n"; + message << "Application-Name: " << my->application_name << "\r\n"; + if (my->application_icon) + { + message << "Application-Icon: x-growl-resource://" << my->application_icon->my->_sha1_hash << "\r\n"; + icons_used.insert(my->application_icon); + } + + message << "Notifications-Count: " << my->notification_types.size() << "\r\n"; + for (const gntp_notification_type& notification_type : my->notification_types) + { + message << "\r\n"; + message << "Notification-Name: " << notification_type.name << "\r\n"; + if (!notification_type.display_name.empty()) + message << "Notification-Display-Name: " << notification_type.display_name << "\r\n"; + if (notification_type.icon) + { + message << "Notification-Icon: x-growl-resource://" << notification_type.icon->my->_sha1_hash << "\r\n"; + icons_used.insert(notification_type.icon); + } + message << "Notification-Enabled: " << (notification_type.enabled ? "True" : "False") << "\r\n"; + } + if (!icons_used.empty()) + { + message << "\r\n"; + for (const gntp_icon_ptr& icon : icons_used) + { + message << "Identifier: " << icon->my->_sha1_hash << "\r\n"; + message << "Length: " << icon->my->_icon_bytes.size() << "\r\n"; + message << "\r\n"; + message << icon->my->_icon_bytes; + message << "\r\n"; + } + } + + message << "\r\n\r\n"; + try + { + my->send_gntp_message(message.str()); + my->is_registered = true; + } + catch (const exception&) + { + my->connection_failed = true; + } + } + gntp_guid gntp_notifier::send_notification(std::string name, std::string title, std::string text, + const gntp_icon_ptr& icon, optional coalescingId /* = optional() */) + { + if (my->connection_failed) + return gntp_guid(); + if (!my->is_registered) + return gntp_guid(); + + gntp_guid notification_id; + rand_pseudo_bytes(notification_id.data(), 20); + + std::ostringstream message; + message << "GNTP/1.0 NOTIFY NONE"; + if (my->password) + { + char salt[16]; + rand_pseudo_bytes(salt, sizeof(salt)); + std::string salted_password = *my->password + std::string(salt, 16); + sha256 key = sha256::hash(salted_password); + sha256 keyhash = sha256::hash(key.data(), 32); + message << " SHA256:" << boost::to_upper_copy(to_hex(keyhash.data(), 32)) << "." << boost::to_upper_copy(to_hex(salt, sizeof(salt))); + } + message << "\r\n"; + message << "Application-Name: " << my->application_name << "\r\n"; + message << "Notification-Name: " << name << "\r\n"; + message << "Notification-ID: " << notification_id.str() << "\r\n"; + message << "Notification-Coalescing-ID: " << (coalescingId ? coalescingId->str() : notification_id.str()) << "\r\n"; + message << "Notification-Title: " << title << "\r\n"; + message << "Notification-Text: " << text << "\r\n"; + if (icon) + message << "Notification-Icon: x-growl-resource://" << icon->my->_sha1_hash << "\r\n"; + + if (icon) + { + message << "\r\n"; + message << "Identifier: " << icon->my->_sha1_hash << "\r\n"; + message << "Length: " << icon->my->_icon_bytes.size() << "\r\n"; + message << "\r\n"; + message << icon->my->_icon_bytes; + message << "\r\n"; + } + message << "\r\n\r\n"; + my->send_gntp_message(message.str()); + return notification_id; + } + +} // namespace fc \ No newline at end of file diff --git a/src/http_connection.cpp b/src/network/http/http_connection.cpp similarity index 69% rename from src/http_connection.cpp rename to src/network/http/http_connection.cpp index 8ba10353d..309866d9c 100644 --- a/src/http_connection.cpp +++ b/src/network/http/http_connection.cpp @@ -1,14 +1,19 @@ -#include -#include -#include -#include -#include -#include -#include -#include - - -FC_START_SHARED_IMPL(fc::http::connection) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class fc::http::connection::impl +{ + public: fc::tcp_socket sock; fc::ip::endpoint ep; impl() { @@ -17,7 +22,7 @@ FC_START_SHARED_IMPL(fc::http::connection) int read_until( char* buffer, char* end, char c = '\n' ) { char* p = buffer; // try { - while( p < end && !sock.read(p,1).eof() ) { + while( p < end && 1 == sock.readsome(p,1) ) { if( *p == c ) { *p = '\0'; return (p - buffer)-1; @@ -34,10 +39,10 @@ FC_START_SHARED_IMPL(fc::http::connection) fc::http::reply parse_reply() { fc::http::reply rep; try { - fc::vector line(1024*8); + std::vector line(1024*8); int s = read_until( line.data(), line.data()+line.size(), ' ' ); // HTTP/1.1 s = read_until( line.data(), line.data()+line.size(), ' ' ); // CODE - rep.status = fc::lexical_cast(fc::string(line.data())); + rep.status = static_cast(to_int64(fc::string(line.data()))); s = read_until( line.data(), line.data()+line.size(), '\n' ); // DESCRIPTION while( (s = read_until( line.data(), line.data()+line.size(), '\n' )) > 1 ) { @@ -51,32 +56,30 @@ FC_START_SHARED_IMPL(fc::http::connection) while( *end != '\r' ) ++end; h.val = fc::string(skey,end); rep.headers.push_back(h); - if( h.key == "Content-Length" ) { - rep.body.resize( fc::lexical_cast( fc::string(h.val) ) ); + if( boost::iequals(h.key, "Content-Length") ) { + rep.body.resize( static_cast(to_uint64( fc::string(h.val) ) )); } } if( rep.body.size() ) { - //slog( "Reading body size %d", rep.body.size() ); sock.read( rep.body.data(), rep.body.size() ); } return rep; - } catch ( ... ) { - elog( "%s", fc::except_str().c_str() ); + } catch ( fc::exception& e ) { + elog( "${exception}", ("exception",e.to_detail_string() ) ); sock.close(); - FC_THROW_REPORT( "Error parsing reply" ); rep.status = http::reply::InternalServerError; return rep; } } - -FC_END_SHARED_IMPL -#include +}; namespace fc { namespace http { -FC_REFERENCE_TYPE_IMPL( connection ) + connection::connection() + :my( new connection::impl() ){} + connection::~connection(){} // used for clients @@ -87,17 +90,22 @@ void connection::connect_to( const fc::ip::endpoint& ep ) { http::reply connection::request( const fc::string& method, const fc::string& url, - const fc::string& body ) { + const fc::string& body, const headers& he ) { + fc::url parsed_url(url); if( !my->sock.is_open() ) { wlog( "Re-open socket!" ); my->sock.connect_to( my->ep ); } try { fc::stringstream req; - req << method <<" "<generic_string()<<" HTTP/1.1\r\n"; + req << "Host: "<<*parsed_url.host()<<"\r\n"; req << "Content-Type: application/json\r\n"; + for( auto i = he.begin(); i != he.end(); ++i ) + { + req << i->key <<": " << i->val<<"\r\n"; + } if( body.size() ) req << "Content-Length: "<< body.size() << "\r\n"; req << "\r\n"; fc::string head = req.str(); @@ -114,7 +122,7 @@ http::reply connection::request( const fc::string& method, return my->parse_reply(); } catch ( ... ) { my->sock.close(); - FC_THROW_REPORT( "Error Sending HTTP Request" ); // TODO: provide more info + FC_THROW_EXCEPTION( exception, "Error Sending HTTP Request" ); // TODO: provide more info // return http::reply( http::reply::InternalServerError ); // TODO: replace with connection error } } @@ -126,7 +134,7 @@ fc::tcp_socket& connection::get_socket()const { http::request connection::read_request()const { http::request req; - fc::vector line(1024*8); + std::vector line(1024*8); int s = my->read_until( line.data(), line.data()+line.size(), ' ' ); // METHOD req.method = line.data(); s = my->read_until( line.data(), line.data()+line.size(), ' ' ); // PATH @@ -144,15 +152,19 @@ http::request connection::read_request()const { while( *end != '\r' ) ++end; h.val = fc::string(skey,end); req.headers.push_back(h); - if( h.key == "Content-Length" ) { - req.body.resize( fc::lexical_cast( fc::string(h.val) ) ); + if( boost::iequals(h.key, "Content-Length")) { + auto s = static_cast(to_uint64( fc::string(h.val) ) ); + FC_ASSERT( s < 1024*1024 ); + req.body.resize( static_cast(to_uint64( fc::string(h.val) ) )); } - if( h.key == "Host" ) { + if( boost::iequals(h.key, "Host") ) { req.domain = h.val; } } + // TODO: some common servers won't give a Content-Length, they'll use + // Transfer-Encoding: chunked. handle that here. + if( req.body.size() ) { - slog( "Reading body size %d", req.body.size() ); my->sock.read( req.body.data(), req.body.size() ); } return req; @@ -160,16 +172,16 @@ http::request connection::read_request()const { fc::string request::get_header( const fc::string& key )const { for( auto itr = headers.begin(); itr != headers.end(); ++itr ) { - if( itr->key == key ) { return itr->val; } + if( boost::iequals(itr->key, key) ) { return itr->val; } } return fc::string(); } -fc::vector
parse_urlencoded_params( const fc::string& f ) { +std::vector
parse_urlencoded_params( const fc::string& f ) { int num_args = 0; for( size_t i = 0; i < f.size(); ++i ) { if( f[i] == '=' ) ++num_args; } - fc::vector
h(num_args); + std::vector
h(num_args); int arg = 0; for( size_t i = 0; i < f.size(); ++i ) { while( f[i] != '=' && i < f.size() ) { diff --git a/src/network/http/http_server.cpp b/src/network/http/http_server.cpp new file mode 100644 index 000000000..648fc548e --- /dev/null +++ b/src/network/http/http_server.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include + + +namespace fc { namespace http { + + class server::response::impl : public fc::retainable + { + public: + impl( const fc::http::connection_ptr& c, const std::function& cont = std::function() ) + :body_bytes_sent(0),body_length(0),con(c),handle_next_req(cont) + {} + + void send_header() { + //ilog( "sending header..." ); + fc::stringstream ss; + ss << "HTTP/1.1 " << rep.status << " "; + switch( rep.status ) { + case fc::http::reply::OK: ss << "OK\r\n"; break; + case fc::http::reply::RecordCreated: ss << "Record Created\r\n"; break; + case fc::http::reply::NotFound: ss << "Not Found\r\n"; break; + case fc::http::reply::Found: ss << "Found\r\n"; break; + default: ss << "Internal Server Error\r\n"; break; + } + for( uint32_t i = 0; i < rep.headers.size(); ++i ) { + ss << rep.headers[i].key <<": "<get_socket().write( s.c_str(), s.size() ); + } + + http::reply rep; + int64_t body_bytes_sent; + uint64_t body_length; + http::connection_ptr con; + std::function handle_next_req; + }; + + + class server::impl + { + public: + impl(){} + + impl(const fc::ip::endpoint& p ) + { + tcp_serv.set_reuse_address(); + tcp_serv.listen(p); + accept_complete = fc::async([this](){ this->accept_loop(); }, "http_server accept_loop"); + } + + ~impl() + { + try + { + tcp_serv.close(); + if (accept_complete.valid()) + accept_complete.wait(); + } + catch (...) + { + } + + for (fc::future& request_in_progress : requests_in_progress) + { + try + { + request_in_progress.cancel_and_wait(); + } + catch (const fc::exception& e) + { + wlog("Caught exception while canceling http request task: ${error}", ("error", e)); + } + catch (const std::exception& e) + { + wlog("Caught exception while canceling http request task: ${error}", ("error", e.what())); + } + catch (...) + { + wlog("Caught unknown exception while canceling http request task"); + } + } + requests_in_progress.clear(); + } + + void accept_loop() + { + while( !accept_complete.canceled() ) + { + http::connection_ptr con = std::make_shared(); + tcp_serv.accept( con->get_socket() ); + //ilog( "Accept Connection" ); + // clean up futures for any completed requests + for (auto iter = requests_in_progress.begin(); iter != requests_in_progress.end();) + if (!iter->valid() || iter->ready()) + iter = requests_in_progress.erase(iter); + else + ++iter; + requests_in_progress.emplace_back(fc::async([=](){ handle_connection(con, on_req); }, "http_server handle_connection")); + } + } + + void handle_connection( const http::connection_ptr& c, + std::function do_on_req ) + { + try + { + http::server::response rep( fc::shared_ptr( new response::impl(c) ) ); + request req = c->read_request(); + if( do_on_req ) + do_on_req( req, rep ); + c->get_socket().close(); + } + catch ( fc::exception& e ) + { + wlog( "unable to read request ${1}", ("1", e.to_detail_string() ) );//fc::except_str().c_str()); + } + //wlog( "done handle connection" ); + } + + fc::future accept_complete; + std::function on_req; + std::vector > requests_in_progress; + fc::tcp_server tcp_serv; + }; + + + + server::server():my( new impl() ){} + server::server( uint16_t port ) :my( new impl(fc::ip::endpoint( fc::ip::address(),port)) ){} + server::server( server&& s ):my(fc::move(s.my)){} + + server& server::operator=(server&& s) { fc_swap(my,s.my); return *this; } + + server::~server(){} + + void server::listen( const fc::ip::endpoint& p ) + { + my.reset( new impl(p) ); + } + + fc::ip::endpoint server::get_local_endpoint() const + { + return my->tcp_serv.get_local_endpoint(); + } + + + server::response::response(){} + server::response::response( const server::response& s ):my(s.my){} + server::response::response( server::response&& s ):my(fc::move(s.my)){} + server::response::response( const fc::shared_ptr& m ):my(m){} + + server::response& server::response::operator=(const server::response& s) { my = s.my; return *this; } + server::response& server::response::operator=(server::response&& s) { fc_swap(my,s.my); return *this; } + + void server::response::add_header( const fc::string& key, const fc::string& val )const { + my->rep.headers.push_back( fc::http::header( key, val ) ); + } + void server::response::set_status( const http::reply::status_code& s )const { + if( my->body_bytes_sent != 0 ) { + wlog( "Attempt to set status after sending headers" ); + } + my->rep.status = s; + } + void server::response::set_length( uint64_t s )const { + if( my->body_bytes_sent != 0 ) { + wlog( "Attempt to set length after sending headers" ); + } + my->body_length = s; + } + void server::response::write( const char* data, uint64_t len )const { + if( my->body_bytes_sent + len > my->body_length ) { + wlog( "Attempt to send to many bytes.." ); + len = my->body_bytes_sent + len - my->body_length; + } + if( my->body_bytes_sent == 0 ) { + my->send_header(); + } + my->body_bytes_sent += len; + my->con->get_socket().write( data, static_cast(len) ); + if( my->body_bytes_sent == int64_t(my->body_length) ) { + if( false || my->handle_next_req ) { + ilog( "handle next request..." ); + //fc::async( std::function(my->handle_next_req) ); + fc::async( my->handle_next_req, "http_server handle_next_req" ); + } + } + } + + server::response::~response(){} + + void server::on_request( const std::function& cb ) + { + my->on_req = cb; + } + + + + +} } diff --git a/src/network/http/websocket.cpp b/src/network/http/websocket.cpp new file mode 100644 index 000000000..b8a3d3cb7 --- /dev/null +++ b/src/network/http/websocket.cpp @@ -0,0 +1,681 @@ +#include + +#ifndef WIN32 +// websocket++ currently does not build correctly with permessage deflate enabled +// since chrome does not work with websocketpp's implementation of permessage-deflate +// yet, I'm just disabling it on windows instead of trying to fix the build error. +# define ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE +#endif + +#include +#include +#include +#include +#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE +# include +#endif +#include +#include + +#include +#include +#include +#include + +#ifdef DEFAULT_LOGGER +# undef DEFAULT_LOGGER +#endif +#define DEFAULT_LOGGER "rpc" + +namespace fc { namespace http { + + namespace detail { + struct asio_with_stub_log : public websocketpp::config::asio { + typedef asio_with_stub_log type; + typedef asio base; + + //// All boilerplate copying the base class's config, except as noted + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + /// Custom Logging policies, use do-nothing log::stub instead of log::basic + typedef websocketpp::log::stub elog_type; + typedef websocketpp::log::stub alog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; + + // override default value of 5 sec timeout + static const long timeout_open_handshake = 0; + }; + +#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + struct asio_with_stub_log_and_deflate : public websocketpp::config::asio { + typedef asio_with_stub_log_and_deflate type; + typedef asio base; + + //// All boilerplate copying the base class's config, except as noted + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + /// Custom Logging policies, use do-nothing log::stub instead of log::basic + typedef websocketpp::log::stub elog_type; + typedef websocketpp::log::stub alog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; + + /// enable the permessage_compress extension + struct permessage_deflate_config {}; + typedef websocketpp::extensions::permessage_deflate::enabled + permessage_deflate_type; + + // override default value of 5 sec timeout + static const long timeout_open_handshake = 0; + }; +#endif ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + + struct asio_tls_stub_log : public websocketpp::config::asio_tls { + typedef asio_tls_stub_log type; + typedef asio_tls base; + + //// All boilerplate copying the base class's config, except as noted + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + /// Custom Logging policies, use do-nothing log::stub instead of log::basic + typedef websocketpp::log::stub elog_type; + typedef websocketpp::log::stub alog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; + }; + +#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + struct asio_tls_stub_log_and_deflate : public websocketpp::config::asio_tls { + typedef asio_tls_stub_log_and_deflate type; + typedef asio_tls base; + + //// All boilerplate copying the base class's config, except as noted + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + /// Custom Logging policies, use do-nothing log::stub instead of log::basic + typedef websocketpp::log::stub elog_type; + typedef websocketpp::log::stub alog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; + + /// enable the permessage_compress extension + struct permessage_deflate_config {}; + typedef websocketpp::extensions::permessage_deflate::enabled + permessage_deflate_type; + }; +#endif + + using websocketpp::connection_hdl; + + template + class websocket_connection_impl : public websocket_connection + { + public: + websocket_connection_impl( T con ) + :_ws_connection(con){ + } + + ~websocket_connection_impl() + { + } + + virtual void send_message( const std::string& message )override + { + idump((message)); + //std::cerr<<"send: "<send( message ); + FC_ASSERT( !ec, "websocket send failed: ${msg}", ("msg",ec.message() ) ); + } + virtual void close( int64_t code, const std::string& reason )override + { + _ws_connection->close(code,reason); + } + + T _ws_connection; + }; + + typedef websocketpp::lib::shared_ptr context_ptr; + + class abstract_websocket_server + { + public: + virtual ~abstract_websocket_server() {} + + virtual void on_connection( const on_connection_handler& handler) = 0; + virtual void listen( uint16_t port ) = 0; + virtual void listen( const fc::ip::endpoint& ep ) = 0; + virtual void start_accept() = 0; + }; + + template + class websocket_server_impl : public abstract_websocket_server + { + public: + websocket_server_impl() + :_server_thread( fc::thread::current() ) + { + + _server.clear_access_channels( websocketpp::log::alevel::all ); + _server.init_asio(&fc::asio::default_io_service()); + _server.set_reuse_addr(true); + _server.set_open_handler( [&]( connection_hdl hdl ){ + _server_thread.async( [&](){ + websocket_connection_ptr new_con = std::make_shared::connection_ptr>>( _server.get_con_from_hdl(hdl) ); + _on_connection( _connections[hdl] = new_con ); + }).wait(); + }); + _server.set_message_handler( [&]( connection_hdl hdl, typename websocketpp::server::message_ptr msg ){ + _server_thread.async( [&](){ + auto current_con = _connections.find(hdl); + assert( current_con != _connections.end() ); + //wdump(("server")(msg->get_payload())); + //std::cerr<<"recv: "<get_payload()<<"\n"; + auto payload = msg->get_payload(); + std::shared_ptr con = current_con->second; + ++_pending_messages; + auto f = fc::async([this,con,payload](){ if( _pending_messages ) --_pending_messages; con->on_message( payload ); }); + if( _pending_messages > 100 ) + f.wait(); + }).wait(); + }); + + _server.set_http_handler( [&]( connection_hdl hdl ){ + _server_thread.async( [&](){ + auto current_con = std::make_shared::connection_ptr>>( _server.get_con_from_hdl(hdl) ); + _on_connection( current_con ); + + auto con = _server.get_con_from_hdl(hdl); + con->defer_http_response(); + std::string request_body = con->get_request_body(); + //wdump(("server")(request_body)); + + fc::async([current_con, request_body, con] { + std::string response = current_con->on_http(request_body); + con->set_body( response ); + con->set_status( websocketpp::http::status_code::ok ); + con->send_http_response(); + current_con->closed(); + }, "call on_http"); + }).wait(); + }); + + _server.set_close_handler( [&]( connection_hdl hdl ){ + _server_thread.async( [&](){ + if( _connections.find(hdl) != _connections.end() ) + { + _connections[hdl]->closed(); + _connections.erase( hdl ); + } + else + { + wlog( "unknown connection closed" ); + } + if( _connections.empty() && _closed ) + _closed->set_value(); + }).wait(); + }); + + _server.set_fail_handler( [&]( connection_hdl hdl ){ + if( _server.is_listening() ) + { + _server_thread.async( [&](){ + if( _connections.find(hdl) != _connections.end() ) + { + _connections[hdl]->closed(); + _connections.erase( hdl ); + } + else + { + wlog( "unknown connection failed" ); + } + if( _connections.empty() && _closed ) + _closed->set_value(); + }).wait(); + } + }); + } + ~websocket_server_impl() + { + if( _server.is_listening() ) + _server.stop_listening(); + + if( _connections.size() ) + _closed = new fc::promise(); + + auto cpy_con = _connections; + for( auto item : cpy_con ) + _server.close( item.first, 0, "server exit" ); + + if( _closed ) _closed->wait(); + } + + void on_connection( const on_connection_handler& handler ) override + { + _on_connection = handler; + } + + void listen( uint16_t port ) override + { + _server.listen(port); + } + + void listen( const fc::ip::endpoint& ep ) override + { + _server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) ); + } + + void start_accept() override + { + _server.start_accept(); + } + + typedef std::map > con_map; + + con_map _connections; + fc::thread& _server_thread; + websocketpp::server _server; + on_connection_handler _on_connection; + fc::promise::ptr _closed; + uint32_t _pending_messages = 0; + }; + + template + class websocket_tls_server_impl : public websocket_server_impl + { + public: + websocket_tls_server_impl( const string& server_pem, const string& ssl_password ) + { + this->_server.set_tls_init_handler( [=]( websocketpp::connection_hdl hdl ) -> context_ptr { + context_ptr ctx = websocketpp::lib::make_shared(boost::asio::ssl::context::tlsv1); + try { + ctx->set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::single_dh_use); + ctx->set_password_callback([=](std::size_t max_length, boost::asio::ssl::context::password_purpose){ return ssl_password;}); + ctx->use_certificate_chain_file(server_pem); + ctx->use_private_key_file(server_pem, boost::asio::ssl::context::pem); + } catch (std::exception& e) { + std::cout << e.what() << std::endl; + } + return ctx; + }); + } + }; + + + + typedef websocketpp::client websocket_client_type; + typedef websocketpp::client websocket_tls_client_type; + + typedef websocket_client_type::connection_ptr websocket_client_connection_type; + typedef websocket_tls_client_type::connection_ptr websocket_tls_client_connection_type; + + class websocket_client_impl + { + public: + typedef websocket_client_type::message_ptr message_ptr; + + websocket_client_impl() + :_client_thread( fc::thread::current() ) + { + _client.clear_access_channels( websocketpp::log::alevel::all ); + _client.set_message_handler( [&]( connection_hdl hdl, message_ptr msg ){ + _client_thread.async( [&](){ + wdump((msg->get_payload())); + //std::cerr<<"recv: "<get_payload()<<"\n"; + auto received = msg->get_payload(); + fc::async( [=](){ + if( _connection ) + _connection->on_message(received); + }); + }).wait(); + }); + _client.set_close_handler( [=]( connection_hdl hdl ){ + _client_thread.async( [&](){ if( _connection ) {_connection->closed(); _connection.reset();} } ).wait(); + if( _closed ) _closed->set_value(); + }); + _client.set_fail_handler( [=]( connection_hdl hdl ){ + auto con = _client.get_con_from_hdl(hdl); + auto message = con->get_ec().message(); + if( _connection ) + _client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait(); + if( _connected && !_connected->ready() ) + _connected->set_exception( exception_ptr( new FC_EXCEPTION( exception, "${message}", ("message",message)) ) ); + if( _closed ) + _closed->set_value(); + }); + + _client.init_asio( &fc::asio::default_io_service() ); + } + ~websocket_client_impl() + { + if(_connection ) + { + _connection->close(0, "client closed"); + _connection.reset(); + _closed->wait(); + } + } + fc::promise::ptr _connected; + fc::promise::ptr _closed; + fc::thread& _client_thread; + websocket_client_type _client; + websocket_connection_ptr _connection; + }; + + + + class websocket_tls_client_impl + { + public: + typedef websocket_tls_client_type::message_ptr message_ptr; + + websocket_tls_client_impl() + :_client_thread( fc::thread::current() ) + { + _client.clear_access_channels( websocketpp::log::alevel::all ); + _client.set_message_handler( [&]( connection_hdl hdl, message_ptr msg ){ + _client_thread.async( [&](){ + wdump((msg->get_payload())); + _connection->on_message( msg->get_payload() ); + }).wait(); + }); + _client.set_close_handler( [=]( connection_hdl hdl ){ + if( _connection ) + { + try { + _client_thread.async( [&](){ + wlog(". ${p}", ("p",uint64_t(_connection.get()))); + if( !_shutting_down && !_closed && _connection ) + _connection->closed(); + _connection.reset(); + } ).wait(); + } catch ( const fc::exception& e ) + { + if( _closed ) _closed->set_exception( e.dynamic_copy_exception() ); + } + if( _closed ) _closed->set_value(); + } + }); + _client.set_fail_handler( [=]( connection_hdl hdl ){ + elog( "." ); + auto con = _client.get_con_from_hdl(hdl); + auto message = con->get_ec().message(); + if( _connection ) + _client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait(); + if( _connected && !_connected->ready() ) + _connected->set_exception( exception_ptr( new FC_EXCEPTION( exception, "${message}", ("message",message)) ) ); + if( _closed ) + _closed->set_value(); + }); + + _client.set_tls_init_handler( [=](websocketpp::connection_hdl) { + context_ptr ctx = websocketpp::lib::make_shared(boost::asio::ssl::context::tlsv1); + try { + ctx->set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::single_dh_use); + } catch (std::exception& e) { + edump((e.what())); + std::cout << e.what() << std::endl; + } + return ctx; + }); + + _client.init_asio( &fc::asio::default_io_service() ); + } + ~websocket_tls_client_impl() + { + if(_connection ) + { + wlog("."); + _shutting_down = true; + _connection->close(0, "client closed"); + _closed->wait(); + } + } + bool _shutting_down = false; + fc::promise::ptr _connected; + fc::promise::ptr _closed; + fc::thread& _client_thread; + websocket_tls_client_type _client; + websocket_connection_ptr _connection; + }; + + + } // namespace detail + + websocket_server::websocket_server(bool enable_permessage_deflate /* = true */) : + my( +#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + enable_permessage_deflate ? + (detail::abstract_websocket_server*)new detail::websocket_server_impl : +#endif + (detail::abstract_websocket_server*)new detail::websocket_server_impl ) + { +#ifndef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + if (enable_permessage_deflate) + elog("Websocket permessage-deflate requested but not enabled during compile"); +#endif + } + websocket_server::~websocket_server(){} + + void websocket_server::on_connection( const on_connection_handler& handler ) + { + my->on_connection(handler); + } + + void websocket_server::listen( uint16_t port ) + { + my->listen(port); + } + void websocket_server::listen( const fc::ip::endpoint& ep ) + { + my->listen(ep); + } + + void websocket_server::start_accept() { + my->start_accept(); + } + + + + + websocket_tls_server::websocket_tls_server(const string& server_pem, + const string& ssl_password, + bool enable_permessage_deflate /* = true */) : + my( +#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + enable_permessage_deflate ? + (detail::abstract_websocket_server*)new detail::websocket_tls_server_impl(server_pem, ssl_password) : +#endif + (detail::abstract_websocket_server*)new detail::websocket_tls_server_impl(server_pem, ssl_password) ) + { +#ifndef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE + if (enable_permessage_deflate) + elog("Websocket permessage-deflate requested but not enabled during compile"); +#endif + } + websocket_tls_server::~websocket_tls_server(){} + + void websocket_tls_server::on_connection( const on_connection_handler& handler ) + { + my->on_connection(handler); + } + + void websocket_tls_server::listen( uint16_t port ) + { + my->listen(port); + } + void websocket_tls_server::listen( const fc::ip::endpoint& ep ) + { + my->listen(ep); + } + + void websocket_tls_server::start_accept() + { + my->start_accept(); + } + + + websocket_client::websocket_client():my( new detail::websocket_client_impl() ),smy(new detail::websocket_tls_client_impl()) {} + websocket_client::~websocket_client(){ } + + websocket_connection_ptr websocket_client::connect( const std::string& uri ) + { try { + if( uri.substr(0,4) == "wss:" ) + return secure_connect(uri); + FC_ASSERT( uri.substr(0,3) == "ws:" ); + + // wlog( "connecting to ${uri}", ("uri",uri)); + websocketpp::lib::error_code ec; + + my->_connected = fc::promise::ptr( new fc::promise("websocket::connect") ); + + my->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){ + auto con = my->_client.get_con_from_hdl(hdl); + my->_connection = std::make_shared>( con ); + my->_closed = fc::promise::ptr( new fc::promise("websocket::closed") ); + my->_connected->set_value(); + }); + + auto con = my->_client.get_connection( uri, ec ); + + if( ec ) FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) ); + + my->_client.connect(con); + my->_connected->wait(); + return my->_connection; + } FC_CAPTURE_AND_RETHROW( (uri) ) } + + websocket_connection_ptr websocket_client::secure_connect( const std::string& uri ) + { try { + if( uri.substr(0,3) == "ws:" ) + return connect(uri); + FC_ASSERT( uri.substr(0,4) == "wss:" ); + // wlog( "connecting to ${uri}", ("uri",uri)); + websocketpp::lib::error_code ec; + + smy->_connected = fc::promise::ptr( new fc::promise("websocket::connect") ); + + smy->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){ + auto con = smy->_client.get_con_from_hdl(hdl); + smy->_connection = std::make_shared>( con ); + smy->_closed = fc::promise::ptr( new fc::promise("websocket::closed") ); + smy->_connected->set_value(); + }); + + auto con = smy->_client.get_connection( uri, ec ); + if( ec ) + FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) ); + smy->_client.connect(con); + smy->_connected->wait(); + return smy->_connection; + } FC_CAPTURE_AND_RETHROW( (uri) ) } + + websocket_connection_ptr websocket_tls_client::connect( const std::string& uri ) + { try { + // wlog( "connecting to ${uri}", ("uri",uri)); + websocketpp::lib::error_code ec; + + my->_connected = fc::promise::ptr( new fc::promise("websocket::connect") ); + + my->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){ + auto con = my->_client.get_con_from_hdl(hdl); + my->_connection = std::make_shared>( con ); + my->_closed = fc::promise::ptr( new fc::promise("websocket::closed") ); + my->_connected->set_value(); + }); + + auto con = my->_client.get_connection( uri, ec ); + if( ec ) + { + FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) ); + } + my->_client.connect(con); + my->_connected->wait(); + return my->_connection; + } FC_CAPTURE_AND_RETHROW( (uri) ) } + +} } // fc::http diff --git a/src/network/ip.cpp b/src/network/ip.cpp new file mode 100644 index 000000000..1961659e4 --- /dev/null +++ b/src/network/ip.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include + +namespace fc { namespace ip { + + address::address( uint32_t ip ) + :_ip(ip){} + + address::address( const fc::string& s ) + { + try + { + _ip = boost::asio::ip::address_v4::from_string(s.c_str()).to_ulong(); + } + FC_RETHROW_EXCEPTIONS(error, "Error parsing IP address ${address}", ("address", s)) + } + + bool operator==( const address& a, const address& b ) { + return uint32_t(a) == uint32_t(b); + } + bool operator!=( const address& a, const address& b ) { + return uint32_t(a) != uint32_t(b); + } + + address& address::operator=( const fc::string& s ) + { + try + { + _ip = boost::asio::ip::address_v4::from_string(s.c_str()).to_ulong(); + } + FC_RETHROW_EXCEPTIONS(error, "Error parsing IP address ${address}", ("address", s)) + return *this; + } + + address::operator fc::string()const + { + try + { + return boost::asio::ip::address_v4(_ip).to_string().c_str(); + } + FC_RETHROW_EXCEPTIONS(error, "Error parsing IP address to string") + } + address::operator uint32_t()const { + return _ip; + } + + + endpoint::endpoint() + :_port(0){ } + endpoint::endpoint(const address& a, uint16_t p) + :_port(p),_ip(a){} + + bool operator==( const endpoint& a, const endpoint& b ) { + return a._port == b._port && a._ip == b._ip; + } + bool operator!=( const endpoint& a, const endpoint& b ) { + return a._port != b._port || a._ip != b._ip; + } + + bool operator< ( const endpoint& a, const endpoint& b ) + { + return uint32_t(a.get_address()) < uint32_t(b.get_address()) || + (uint32_t(a.get_address()) == uint32_t(b.get_address()) && + uint32_t(a.port()) < uint32_t(b.port())); + } + + uint16_t endpoint::port()const { return _port; } + const address& endpoint::get_address()const { return _ip; } + + endpoint endpoint::from_string( const string& endpoint_string ) + { + try + { + endpoint ep; + auto pos = endpoint_string.find(':'); + ep._ip = boost::asio::ip::address_v4::from_string(endpoint_string.substr( 0, pos ) ).to_ulong(); + ep._port = boost::lexical_cast( endpoint_string.substr( pos+1, endpoint_string.size() ) ); + return ep; + } + FC_RETHROW_EXCEPTIONS(warn, "error converting string to IP endpoint") + } + + endpoint::operator string()const + { + try + { + return string(_ip) + ':' + fc::string(boost::lexical_cast(_port).c_str()); + } + FC_RETHROW_EXCEPTIONS(warn, "error converting IP endpoint to string") + } + + /** + * @return true if the ip is in the following ranges: + * + * 10.0.0.0 to 10.255.255.255 + * 172.16.0.0 to 172.31.255.255 + * 192.168.0.0 to 192.168.255.255 + * 169.254.0.0 to 169.254.255.255 + * + */ + bool address::is_private_address()const + { + static address min10_ip("10.0.0.0"); + static address max10_ip("10.255.255.255"); + static address min172_ip("172.16.0.0"); + static address max172_ip("172.31.255.255"); + static address min192_ip("192.168.0.0"); + static address max192_ip("192.168.255.255"); + static address min169_ip("169.254.0.0"); + static address max169_ip("169.254.255.255"); + if( _ip >= min10_ip._ip && _ip <= max10_ip._ip ) return true; + if( _ip >= min172_ip._ip && _ip <= max172_ip._ip ) return true; + if( _ip >= min192_ip._ip && _ip <= max192_ip._ip ) return true; + if( _ip >= min169_ip._ip && _ip <= max169_ip._ip ) return true; + return false; + } + + /** + * 224.0.0.0 to 239.255.255.255 + */ + bool address::is_multicast_address()const + { + static address min_ip("224.0.0.0"); + static address max_ip("239.255.255.255"); + return _ip >= min_ip._ip && _ip <= max_ip._ip; + } + + /** !private & !multicast */ + bool address::is_public_address()const + { + return !( is_private_address() || is_multicast_address() ); + } + +} // namespace ip + + void to_variant( const ip::endpoint& var, variant& vo ) + { + vo = fc::string(var); + } + void from_variant( const variant& var, ip::endpoint& vo ) + { + vo = ip::endpoint::from_string(var.as_string()); + } + + void to_variant( const ip::address& var, variant& vo ) + { + vo = fc::string(var); + } + void from_variant( const variant& var, ip::address& vo ) + { + vo = ip::address(var.as_string()); + } + +} diff --git a/src/network/ntp.cpp b/src/network/ntp.cpp new file mode 100644 index 000000000..5c0a085be --- /dev/null +++ b/src/network/ntp.cpp @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include + +#include +#include "../byteswap.hpp" + +#include +#include + +namespace fc +{ + namespace detail { + + class ntp_impl + { + public: + /** vector < host, port > */ + fc::thread _ntp_thread; + std::vector< std::pair< std::string, uint16_t> > _ntp_hosts; + fc::future _read_loop_done; + udp_socket _sock; + uint32_t _request_interval_sec; + uint32_t _retry_failed_request_interval_sec; + fc::time_point _last_valid_ntp_reply_received_time; + + std::atomic_bool _last_ntp_delta_initialized; + std::atomic _last_ntp_delta_microseconds; + + + fc::future _request_time_task_done; + + ntp_impl() : + _ntp_thread("ntp"), + _request_interval_sec( 60*60 /* 1 hr */), + _retry_failed_request_interval_sec(60 * 5), + _last_ntp_delta_microseconds(0) + { + _last_ntp_delta_initialized = false; + _ntp_hosts.push_back( std::make_pair( "pool.ntp.org",123 ) ); + } + + ~ntp_impl() + { + } + + fc::time_point ntp_timestamp_to_fc_time_point(uint64_t ntp_timestamp_net_order) + { + uint64_t ntp_timestamp_host = bswap_64(ntp_timestamp_net_order); + uint32_t fractional_seconds = ntp_timestamp_host & 0xffffffff; + uint32_t microseconds = (uint32_t)((((uint64_t)fractional_seconds * 1000000) + (uint64_t(1) << 31)) >> 32); + uint32_t seconds_since_1900 = ntp_timestamp_host >> 32; + uint32_t seconds_since_epoch = seconds_since_1900 - 2208988800; + return fc::time_point() + fc::seconds(seconds_since_epoch) + fc::microseconds(microseconds); + } + + uint64_t fc_time_point_to_ntp_timestamp(const fc::time_point& fc_timestamp) + { + uint64_t microseconds_since_epoch = (uint64_t)fc_timestamp.time_since_epoch().count(); + uint32_t seconds_since_epoch = (uint32_t)(microseconds_since_epoch / 1000000); + uint32_t seconds_since_1900 = seconds_since_epoch + 2208988800; + uint32_t microseconds = microseconds_since_epoch % 1000000; + uint32_t fractional_seconds = (uint32_t)((((uint64_t)microseconds << 32) + (uint64_t(1) << 31)) / 1000000); + uint64_t ntp_timestamp_net_order = ((uint64_t)seconds_since_1900 << 32) + fractional_seconds; + return bswap_64(ntp_timestamp_net_order); + } + + void request_now() + { + assert(_ntp_thread.is_current()); + for( auto item : _ntp_hosts ) + { + try + { + //wlog( "resolving... ${r}", ("r", item) ); + auto eps = resolve( item.first, item.second ); + for( auto ep : eps ) + { + // wlog( "sending request to ${ep}", ("ep",ep) ); + std::shared_ptr send_buffer(new char[48], [](char* p){ delete[] p; }); + std::array packet_to_send { {010,0,0,0,0,0,0,0,0} }; + memcpy(send_buffer.get(), packet_to_send.data(), packet_to_send.size()); + uint64_t* send_buf_as_64_array = (uint64_t*)send_buffer.get(); + send_buf_as_64_array[5] = fc_time_point_to_ntp_timestamp(fc::time_point::now()); // 5 = Transmit Timestamp + _sock.send_to(send_buffer, packet_to_send.size(), ep); + break; + } + } + catch (const fc::canceled_exception&) + { + throw; + } + // this could fail to resolve but we want to go on to other hosts.. + catch ( const fc::exception& e ) + { + elog( "${e}", ("e",e.to_detail_string() ) ); + } + } + } // request_now + + // started for first time in ntp() constructor, canceled in ~ntp() destructor + // this task gets invoked every _retry_failed_request_interval_sec (currently 5 min), and if + // _request_interval_sec (currently 1 hour) has passed since the last successful update, + // it sends a new request + void request_time_task() + { + assert(_ntp_thread.is_current()); + if (_last_valid_ntp_reply_received_time <= fc::time_point::now() - fc::seconds(_request_interval_sec - 5)) + request_now(); + if (!_request_time_task_done.valid() || !_request_time_task_done.canceled()) + _request_time_task_done = schedule( [=](){ request_time_task(); }, + fc::time_point::now() + fc::seconds(_retry_failed_request_interval_sec), + "request_time_task" ); + } // request_loop + + void start_read_loop() + { + _read_loop_done = _ntp_thread.async( [this](){ read_loop(); }, "ntp_read_loop" ); + } + + void read_loop() + { + assert(_ntp_thread.is_current()); + + uint32_t receive_buffer_size = sizeof(uint64_t) * 1024; + std::shared_ptr receive_buffer(new char[receive_buffer_size], [](char* p){ delete[] p; }); + uint64_t* recv_buf = (uint64_t*)receive_buffer.get(); + + //outer while to restart read-loop if exception is thrown while waiting to receive on socket. + while( !_read_loop_done.canceled() ) + { + // if you start the read while loop here, the recieve_from call will throw "invalid argument" on win32, + // so instead we start the loop after making our first request + try + { + _sock.open(); + request_time_task(); //this will re-send a time request + + while( !_read_loop_done.canceled() ) + { + fc::ip::endpoint from; + try + { + _sock.receive_from( receive_buffer, receive_buffer_size, from ); + // wlog("received ntp reply from ${from}",("from",from) ); + } FC_RETHROW_EXCEPTIONS(error, "Error reading from NTP socket"); + + fc::time_point receive_time = fc::time_point::now(); + fc::time_point origin_time = ntp_timestamp_to_fc_time_point(recv_buf[3]); + fc::time_point server_receive_time = ntp_timestamp_to_fc_time_point(recv_buf[4]); + fc::time_point server_transmit_time = ntp_timestamp_to_fc_time_point(recv_buf[5]); + + fc::microseconds offset(((server_receive_time - origin_time) + + (server_transmit_time - receive_time)).count() / 2); + fc::microseconds round_trip_delay((receive_time - origin_time) - + (server_transmit_time - server_receive_time)); + //wlog("origin_time = ${origin_time}, server_receive_time = ${server_receive_time}, server_transmit_time = ${server_transmit_time}, receive_time = ${receive_time}", + // ("origin_time", origin_time)("server_receive_time", server_receive_time)("server_transmit_time", server_transmit_time)("receive_time", receive_time)); + // wlog("ntp offset: ${offset}, round_trip_delay ${delay}", ("offset", offset)("delay", round_trip_delay)); + + //if the reply we just received has occurred more than a second after our last time request (it was more than a second ago since our last request) + if( round_trip_delay > fc::microseconds(300000) ) + { + wlog("received stale ntp reply requested at ${request_time}, send a new time request", ("request_time", origin_time)); + request_now(); //request another reply and ignore this one + } + else //we think we have a timely reply, process it + { + if( offset < fc::seconds(60*60*24) && offset > fc::seconds(-60*60*24) ) + { + _last_ntp_delta_microseconds = offset.count(); + _last_ntp_delta_initialized = true; + fc::microseconds ntp_delta_time = fc::microseconds(_last_ntp_delta_microseconds); + _last_valid_ntp_reply_received_time = receive_time; + wlog("ntp_delta_time updated to ${delta_time} us", ("delta_time",ntp_delta_time) ); + } + else + elog( "NTP time and local time vary by more than a day! ntp:${ntp_time} local:${local}", + ("ntp_time", receive_time + offset)("local", fc::time_point::now()) ); + } + } + } // try + catch (fc::canceled_exception) + { + throw; + } + catch (const fc::exception& e) + { + //swallow any other exception and restart loop + elog("exception in read_loop, going to restart it. ${e}",("e",e)); + } + catch (...) + { + //swallow any other exception and restart loop + elog("unknown exception in read_loop, going to restart it."); + } + _sock.close(); + fc::usleep(fc::seconds(_retry_failed_request_interval_sec)); + } //outer while loop + wlog("exiting ntp read_loop"); + } //end read_loop() + }; //ntp_impl + + } // namespace detail + + + + ntp::ntp() + :my( new detail::ntp_impl() ) + { + my->start_read_loop(); + } + + ntp::~ntp() + { + my->_ntp_thread.async([=](){ + try + { + my->_request_time_task_done.cancel_and_wait("ntp object is destructing"); + } + catch ( const fc::exception& e ) + { + wlog( "Exception thrown while shutting down NTP's request_time_task, ignoring: ${e}", ("e",e) ); + } + catch (...) + { + wlog( "Exception thrown while shutting down NTP's request_time_task, ignoring" ); + } + + try + { + my->_read_loop_done.cancel_and_wait("ntp object is destructing"); + } + catch ( const fc::exception& e ) + { + wlog( "Exception thrown while shutting down NTP's read_loop, ignoring: ${e}", ("e",e) ); + } + catch (...) + { + wlog( "Exception thrown while shutting down NTP's read_loop, ignoring" ); + } + + }, "ntp_shutdown_task").wait(); + } + + + void ntp::add_server( const std::string& hostname, uint16_t port) + { + my->_ntp_thread.async( [=](){ my->_ntp_hosts.push_back( std::make_pair(hostname,port) ); }, "add_server" ).wait(); + } + + void ntp::set_request_interval( uint32_t interval_sec ) + { + my->_request_interval_sec = interval_sec; + my->_retry_failed_request_interval_sec = std::min(my->_retry_failed_request_interval_sec, interval_sec); + } + + void ntp::request_now() + { + my->_ntp_thread.async( [=](){ my->request_now(); }, "request_now" ).wait(); + } + + optional ntp::get_time()const + { + if( my->_last_ntp_delta_initialized ) + return fc::time_point::now() + fc::microseconds(my->_last_ntp_delta_microseconds); + return optional(); + } + +} //namespace fc diff --git a/src/network/rate_limiting.cpp b/src/network/rate_limiting.cpp new file mode 100644 index 000000000..ab7464e6b --- /dev/null +++ b/src/network/rate_limiting.cpp @@ -0,0 +1,543 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc +{ + + namespace detail + { + // data about a read or write we're managing + class rate_limited_operation + { + public: + size_t length; + size_t offset; + size_t permitted_length; + promise::ptr completion_promise; + + rate_limited_operation(size_t length, + size_t offset, + promise::ptr&& completion_promise) : + length(length), + offset(offset), + permitted_length(0), + completion_promise(completion_promise) + {} + + virtual void perform_operation() = 0; + }; + + class rate_limited_tcp_write_operation : public rate_limited_operation + { + public: + boost::asio::ip::tcp::socket& socket; + const char* raw_buffer; + std::shared_ptr shared_buffer; + + rate_limited_tcp_write_operation(boost::asio::ip::tcp::socket& socket, + const char* buffer, + size_t length, + size_t offset, + promise::ptr completion_promise) : + rate_limited_operation(length, offset, std::move(completion_promise)), + socket(socket), + raw_buffer(buffer) + { + assert(false); + } + rate_limited_tcp_write_operation(boost::asio::ip::tcp::socket& socket, + const std::shared_ptr& buffer, + size_t length, + size_t offset, + promise::ptr completion_promise) : + rate_limited_operation(length, offset, std::move(completion_promise)), + socket(socket), + raw_buffer(nullptr), + shared_buffer(buffer) + {} + virtual void perform_operation() override + { + if (raw_buffer) + asio::async_write_some(socket, + raw_buffer, permitted_length, + completion_promise); + else + asio::async_write_some(socket, + shared_buffer, permitted_length, offset, + completion_promise); + } + }; + + class rate_limited_tcp_read_operation : public rate_limited_operation + { + public: + boost::asio::ip::tcp::socket& socket; + char* raw_buffer; + std::shared_ptr shared_buffer; + + rate_limited_tcp_read_operation(boost::asio::ip::tcp::socket& socket, + char* buffer, + size_t length, + size_t offset, + promise::ptr completion_promise) : + rate_limited_operation(length, offset, std::move(completion_promise)), + socket(socket), + raw_buffer(buffer) + {} + rate_limited_tcp_read_operation(boost::asio::ip::tcp::socket& socket, + const std::shared_ptr& buffer, + size_t length, + size_t offset, + promise::ptr completion_promise) : + rate_limited_operation(length, offset, std::move(completion_promise)), + socket(socket), + raw_buffer(nullptr), + shared_buffer(buffer) + {} + virtual void perform_operation() override + { + if (raw_buffer) + asio::async_read_some(socket, + raw_buffer, permitted_length, + completion_promise); + else + asio::async_read_some(socket, + shared_buffer, permitted_length, offset, + completion_promise); + + } + }; + + struct is_operation_shorter + { + // less than operator designed to bring the shortest operations to the end + bool operator()(const rate_limited_operation* lhs, const rate_limited_operation* rhs) + { + return lhs->length > rhs->length; + } + }; + + class average_rate_meter + { + private: + mutable double _average_rate; + mutable uint32_t _unaccounted_bytes; + mutable time_point _last_update_time; + microseconds _time_constant; + + void update_const(uint32_t bytes_transferred = 0) const; + public: + average_rate_meter(const microseconds& time_constant = seconds(10)); + void set_time_constant(const microseconds& time_constant); + void update(uint32_t bytes_transferred = 0); + uint32_t get_average_rate() const; + }; + average_rate_meter::average_rate_meter(const microseconds& time_constant) : + _average_rate(0.), + _unaccounted_bytes(0), + _last_update_time(time_point_sec::min()), + _time_constant(time_constant) + {} + void average_rate_meter::set_time_constant(const microseconds& time_constant) + { + _time_constant = time_constant; + } + void average_rate_meter::update(uint32_t bytes_transferred /* = 0 */) + { + update_const(bytes_transferred); + } + void average_rate_meter::update_const(uint32_t bytes_transferred /* = 0 */) const + { + time_point now = time_point::now(); + if (now <= _last_update_time) + _unaccounted_bytes += bytes_transferred; + else + { + microseconds time_since_last_update = now - _last_update_time; + if (time_since_last_update > _time_constant) + _average_rate = bytes_transferred / (_time_constant.count() / (double)seconds(1).count()); + else + { + bytes_transferred += _unaccounted_bytes; + double seconds_since_last_update = time_since_last_update.count() / (double)seconds(1).count(); + double rate_since_last_update = bytes_transferred / seconds_since_last_update; + double alpha = time_since_last_update.count() / (double)_time_constant.count(); + _average_rate = rate_since_last_update * alpha + _average_rate * (1.0 - alpha); + } + _last_update_time = now; + _unaccounted_bytes = 0; + } + } + uint32_t average_rate_meter::get_average_rate() const + { + update_const(); + return (uint32_t)_average_rate; + } + + class rate_limiting_group_impl : public tcp_socket_io_hooks + { + public: + uint32_t _upload_bytes_per_second; + uint32_t _download_bytes_per_second; + uint32_t _burstiness_in_seconds; + + microseconds _granularity; // how often to add tokens to the bucket + uint32_t _read_tokens; + uint32_t _unused_read_tokens; // gets filled with tokens for unused bytes (if I'm allowed to read 200 bytes and I try to read 200 bytes, but can only read 50, tokens for the other 150 get returned here) + uint32_t _write_tokens; + uint32_t _unused_write_tokens; + + typedef std::list rate_limited_operation_list; + rate_limited_operation_list _read_operations_in_progress; + rate_limited_operation_list _read_operations_for_next_iteration; + rate_limited_operation_list _write_operations_in_progress; + rate_limited_operation_list _write_operations_for_next_iteration; + + time_point _last_read_iteration_time; + time_point _last_write_iteration_time; + + future _process_pending_reads_loop_complete; + promise::ptr _new_read_operation_available_promise; + future _process_pending_writes_loop_complete; + promise::ptr _new_write_operation_available_promise; + + average_rate_meter _actual_upload_rate; + average_rate_meter _actual_download_rate; + + rate_limiting_group_impl(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second, + uint32_t burstiness_in_seconds = 1); + ~rate_limiting_group_impl(); + + virtual size_t readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length) override; + virtual size_t readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) override; + template + size_t readsome_impl(boost::asio::ip::tcp::socket& socket, const BufferType& buffer, size_t length, size_t offset); + virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length) override; + virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) override; + template + size_t writesome_impl(boost::asio::ip::tcp::socket& socket, const BufferType& buffer, size_t length, size_t offset); + + void process_pending_reads(); + void process_pending_writes(); + void process_pending_operations(time_point& last_iteration_start_time, + uint32_t& limit_bytes_per_second, + rate_limited_operation_list& operations_in_progress, + rate_limited_operation_list& operations_for_next_iteration, + uint32_t& tokens, + uint32_t& unused_tokens); + }; + + rate_limiting_group_impl::rate_limiting_group_impl(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second, + uint32_t burstiness_in_seconds) : + _upload_bytes_per_second(upload_bytes_per_second), + _download_bytes_per_second(download_bytes_per_second), + _burstiness_in_seconds(burstiness_in_seconds), + _granularity(milliseconds(50)), + _read_tokens(_download_bytes_per_second), + _unused_read_tokens(0), + _write_tokens(_upload_bytes_per_second), + _unused_write_tokens(0) + { + } + + rate_limiting_group_impl::~rate_limiting_group_impl() + { + try + { + _process_pending_reads_loop_complete.cancel_and_wait(); + } + catch (...) + { + } + try + { + _process_pending_writes_loop_complete.cancel_and_wait(); + } + catch (...) + { + } + } + + size_t rate_limiting_group_impl::readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) + { + return readsome_impl(socket, buffer, length, offset); + } + + size_t rate_limiting_group_impl::readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length) + { + return readsome_impl(socket, buffer, length, 0); + } + + template + size_t rate_limiting_group_impl::readsome_impl(boost::asio::ip::tcp::socket& socket, const BufferType& buffer, size_t length, size_t offset) + { + size_t bytes_read; + if (_download_bytes_per_second) + { + promise::ptr completion_promise(new promise("rate_limiting_group_impl::readsome")); + rate_limited_tcp_read_operation read_operation(socket, buffer, length, offset, completion_promise); + _read_operations_for_next_iteration.push_back(&read_operation); + + // launch the read processing loop it if isn't running, or signal it to resume if it's paused. + if (!_process_pending_reads_loop_complete.valid() || _process_pending_reads_loop_complete.ready()) + _process_pending_reads_loop_complete = async([=](){ process_pending_reads(); }, "process_pending_reads" ); + else if (_new_read_operation_available_promise) + _new_read_operation_available_promise->set_value(); + + try + { + bytes_read = completion_promise->wait(); + } + catch (...) + { + _read_operations_for_next_iteration.remove(&read_operation); + _read_operations_in_progress.remove(&read_operation); + throw; + } + _unused_read_tokens += read_operation.permitted_length - bytes_read; + } + else + bytes_read = asio::read_some(socket, buffer, length, offset); + + _actual_download_rate.update(bytes_read); + + return bytes_read; + } + + size_t rate_limiting_group_impl::writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length) + { + return writesome_impl(socket, buffer, length, 0); + } + + size_t rate_limiting_group_impl::writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) + { + return writesome_impl(socket, buffer, length, offset); + } + + template + size_t rate_limiting_group_impl::writesome_impl(boost::asio::ip::tcp::socket& socket, const BufferType& buffer, size_t length, size_t offset) + { + size_t bytes_written; + if (_upload_bytes_per_second) + { + promise::ptr completion_promise(new promise("rate_limiting_group_impl::writesome")); + rate_limited_tcp_write_operation write_operation(socket, buffer, length, offset, completion_promise); + _write_operations_for_next_iteration.push_back(&write_operation); + + // launch the write processing loop it if isn't running, or signal it to resume if it's paused. + if (!_process_pending_writes_loop_complete.valid() || _process_pending_writes_loop_complete.ready()) + _process_pending_writes_loop_complete = async([=](){ process_pending_writes(); }, "process_pending_writes"); + else if (_new_write_operation_available_promise) + _new_write_operation_available_promise->set_value(); + + try + { + bytes_written = completion_promise->wait(); + } + catch (...) + { + _write_operations_for_next_iteration.remove(&write_operation); + _write_operations_in_progress.remove(&write_operation); + throw; + } + _unused_write_tokens += write_operation.permitted_length - bytes_written; + } + else + bytes_written = asio::write_some(socket, buffer, length, offset); + + _actual_upload_rate.update(bytes_written); + + return bytes_written; + } + + void rate_limiting_group_impl::process_pending_reads() + { + for (;;) + { + process_pending_operations(_last_read_iteration_time, _download_bytes_per_second, + _read_operations_in_progress, _read_operations_for_next_iteration, _read_tokens, _unused_read_tokens); + + _new_read_operation_available_promise = new promise("rate_limiting_group_impl::process_pending_reads"); + try + { + if (_read_operations_in_progress.empty()) + _new_read_operation_available_promise->wait(); + else + _new_read_operation_available_promise->wait(_granularity); + } + catch (const timeout_exception&) + { + } + _new_read_operation_available_promise.reset(); + } + } + void rate_limiting_group_impl::process_pending_writes() + { + for (;;) + { + process_pending_operations(_last_write_iteration_time, _upload_bytes_per_second, + _write_operations_in_progress, _write_operations_for_next_iteration, _write_tokens, _unused_write_tokens); + + _new_write_operation_available_promise = new promise("rate_limiting_group_impl::process_pending_writes"); + try + { + if (_write_operations_in_progress.empty()) + _new_write_operation_available_promise->wait(); + else + _new_write_operation_available_promise->wait(_granularity); + } + catch (const timeout_exception&) + { + } + _new_write_operation_available_promise.reset(); + } + } + void rate_limiting_group_impl::process_pending_operations(time_point& last_iteration_start_time, + uint32_t& limit_bytes_per_second, + rate_limited_operation_list& operations_in_progress, + rate_limited_operation_list& operations_for_next_iteration, + uint32_t& tokens, + uint32_t& unused_tokens) + { + // lock here for multithreaded + std::copy(operations_for_next_iteration.begin(), + operations_for_next_iteration.end(), + std::back_inserter(operations_in_progress)); + operations_for_next_iteration.clear(); + + // find out how much time since our last read/write + time_point this_iteration_start_time = time_point::now(); + if (limit_bytes_per_second) // the we are limiting up/download speed + { + microseconds time_since_last_iteration = this_iteration_start_time - last_iteration_start_time; + if (time_since_last_iteration > seconds(1)) + time_since_last_iteration = seconds(1); + else if (time_since_last_iteration < microseconds(0)) + time_since_last_iteration = microseconds(0); + + tokens += (uint32_t)((limit_bytes_per_second * time_since_last_iteration.count()) / 1000000); + tokens += unused_tokens; + unused_tokens = 0; + tokens = std::min(tokens, limit_bytes_per_second * _burstiness_in_seconds); + + if (tokens) + { + // sort the pending reads/writes in order of the number of bytes they need to write, smallest first + std::vector operations_sorted_by_length; + operations_sorted_by_length.reserve(operations_in_progress.size()); + for (rate_limited_operation* operation_data : operations_in_progress) + operations_sorted_by_length.push_back(operation_data); + std::sort(operations_sorted_by_length.begin(), operations_sorted_by_length.end(), is_operation_shorter()); + + // figure out how many bytes each reader/writer is allowed to read/write + uint32_t bytes_remaining_to_allocate = tokens; + while (!operations_sorted_by_length.empty()) + { + uint32_t bytes_permitted_for_this_operation = bytes_remaining_to_allocate / operations_sorted_by_length.size(); + uint32_t bytes_allocated_for_this_operation = std::min(operations_sorted_by_length.back()->length, bytes_permitted_for_this_operation); + operations_sorted_by_length.back()->permitted_length = bytes_allocated_for_this_operation; + bytes_remaining_to_allocate -= bytes_allocated_for_this_operation; + operations_sorted_by_length.pop_back(); + } + tokens = bytes_remaining_to_allocate; + + // kick off the reads/writes in first-come order + for (auto iter = operations_in_progress.begin(); iter != operations_in_progress.end();) + { + if ((*iter)->permitted_length > 0) + { + rate_limited_operation* operation_to_perform = *iter; + iter = operations_in_progress.erase(iter); + operation_to_perform->perform_operation(); + } + else + ++iter; + } + } + } + else // down/upload speed is unlimited + { + // we shouldn't end up here often. If the rate is unlimited, we should just execute + // the operation immediately without being queued up. This should only be hit if + // we change from a limited rate to unlimited + for (auto iter = operations_in_progress.begin(); + iter != operations_in_progress.end();) + { + rate_limited_operation* operation_to_perform = *iter; + iter = operations_in_progress.erase(iter); + operation_to_perform->permitted_length = operation_to_perform->length; + operation_to_perform->perform_operation(); + } + } + last_iteration_start_time = this_iteration_start_time; + } + + } + + rate_limiting_group::rate_limiting_group(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second, uint32_t burstiness_in_seconds /* = 1 */) : + my(new detail::rate_limiting_group_impl(upload_bytes_per_second, download_bytes_per_second, burstiness_in_seconds)) + { + } + + rate_limiting_group::~rate_limiting_group() + { + } + + uint32_t rate_limiting_group::get_actual_upload_rate() const + { + return my->_actual_upload_rate.get_average_rate(); + } + + uint32_t rate_limiting_group::get_actual_download_rate() const + { + return my->_actual_download_rate.get_average_rate(); + } + + void rate_limiting_group::set_actual_rate_time_constant(microseconds time_constant) + { + my->_actual_upload_rate.set_time_constant(time_constant); + my->_actual_download_rate.set_time_constant(time_constant); + } + + void rate_limiting_group::set_upload_limit(uint32_t upload_bytes_per_second) + { + my->_upload_bytes_per_second = upload_bytes_per_second; + } + + uint32_t rate_limiting_group::get_upload_limit() const + { + return my->_upload_bytes_per_second; + } + + void rate_limiting_group::set_download_limit(uint32_t download_bytes_per_second) + { + my->_download_bytes_per_second = download_bytes_per_second; + } + + uint32_t rate_limiting_group::get_download_limit() const + { + return my->_download_bytes_per_second; + } + + void rate_limiting_group::add_tcp_socket(tcp_socket* tcp_socket_to_limit) + { + tcp_socket_to_limit->set_io_hooks(my.get()); + } + + void rate_limiting_group::remove_tcp_socket(tcp_socket* tcp_socket_to_stop_limiting) + { + tcp_socket_to_stop_limiting->set_io_hooks(NULL); + } + + +} // namespace fc diff --git a/src/network/resolve.cpp b/src/network/resolve.cpp new file mode 100644 index 000000000..3e9ec2855 --- /dev/null +++ b/src/network/resolve.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +namespace fc +{ + std::vector resolve( const fc::string& host, uint16_t port ) + { + auto ep = fc::asio::tcp::resolve( host, std::to_string(uint64_t(port)) ); + std::vector eps; + eps.reserve(ep.size()); + for( auto itr = ep.begin(); itr != ep.end(); ++itr ) + { + if( itr->address().is_v4() ) + { + eps.push_back( fc::ip::endpoint(itr->address().to_v4().to_ulong(), itr->port()) ); + } + // TODO: add support for v6 + } + return eps; + } +} diff --git a/src/network/tcp_socket.cpp b/src/network/tcp_socket.cpp new file mode 100644 index 000000000..cb96bbbfc --- /dev/null +++ b/src/network/tcp_socket.cpp @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined _WIN32 || defined WIN32 || defined OS_WIN64 || defined _WIN64 || defined WIN64 || defined WINNT +# include +#endif + +namespace fc { + + namespace detail + { + bool have_so_reuseport = true; + } + + class tcp_socket::impl : public tcp_socket_io_hooks { + public: + impl() : + _sock(fc::asio::default_io_service()), + _io_hooks(this) + {} + ~impl() + { + if( _sock.is_open() ) + try + { + _sock.close(); + } + catch( ... ) + {} + if( _read_in_progress.valid() ) + try + { + _read_in_progress.wait(); + } + catch ( ... ) + { + } + if( _write_in_progress.valid() ) + try + { + _write_in_progress.wait(); + } + catch ( ... ) + { + } + } + virtual size_t readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length) override; + virtual size_t readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) override; + virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length) override; + virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) override; + + fc::future _write_in_progress; + fc::future _read_in_progress; + boost::asio::ip::tcp::socket _sock; + tcp_socket_io_hooks* _io_hooks; + }; + + size_t tcp_socket::impl::readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length) + { + return (_read_in_progress = fc::asio::read_some(socket, buffer, length)).wait(); + } + size_t tcp_socket::impl::readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) + { + return (_read_in_progress = fc::asio::read_some(socket, buffer, length, offset)).wait(); + } + size_t tcp_socket::impl::writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length) + { + return (_write_in_progress = fc::asio::write_some(socket, buffer, length)).wait(); + } + size_t tcp_socket::impl::writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr& buffer, size_t length, size_t offset) + { + return (_write_in_progress = fc::asio::write_some(socket, buffer, length, offset)).wait(); + } + + + void tcp_socket::open() + { + my->_sock.open(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0).protocol()); + } + + bool tcp_socket::is_open()const { + return my->_sock.is_open(); + } + + tcp_socket::tcp_socket(){}; + + tcp_socket::~tcp_socket(){}; + + void tcp_socket::flush() {} + void tcp_socket::close() { + try { + if( is_open() ) + { + my->_sock.close(); + } + } FC_RETHROW_EXCEPTIONS( warn, "error closing tcp socket" ); + } + + bool tcp_socket::eof()const { + return !my->_sock.is_open(); + } + + size_t tcp_socket::writesome(const char* buf, size_t len) + { + return my->_io_hooks->writesome(my->_sock, buf, len); + } + + size_t tcp_socket::writesome(const std::shared_ptr& buf, size_t len, size_t offset) + { + return my->_io_hooks->writesome(my->_sock, buf, len, offset); + } + + fc::ip::endpoint tcp_socket::remote_endpoint()const + { + try + { + auto rep = my->_sock.remote_endpoint(); + return fc::ip::endpoint(rep.address().to_v4().to_ulong(), rep.port() ); + } + FC_RETHROW_EXCEPTIONS( warn, "error getting socket's remote endpoint" ); + } + + + fc::ip::endpoint tcp_socket::local_endpoint() const + { + try + { + auto boost_local_endpoint = my->_sock.local_endpoint(); + return fc::ip::endpoint(boost_local_endpoint.address().to_v4().to_ulong(), boost_local_endpoint.port() ); + } + FC_RETHROW_EXCEPTIONS( warn, "error getting socket's local endpoint" ); + } + + size_t tcp_socket::readsome( char* buf, size_t len ) + { + return my->_io_hooks->readsome(my->_sock, buf, len); + } + + size_t tcp_socket::readsome( const std::shared_ptr& buf, size_t len, size_t offset ) { + return my->_io_hooks->readsome(my->_sock, buf, len, offset); + } + + void tcp_socket::connect_to( const fc::ip::endpoint& remote_endpoint ) { + fc::asio::tcp::connect(my->_sock, fc::asio::tcp::endpoint( boost::asio::ip::address_v4(remote_endpoint.get_address()), remote_endpoint.port() ) ); + } + + void tcp_socket::bind(const fc::ip::endpoint& local_endpoint) + { + try + { + my->_sock.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4(local_endpoint.get_address()), + local_endpoint.port())); + } + catch (const std::exception& except) + { + elog("Exception binding outgoing connection to desired local endpoint: ${what}", ("what", except.what())); + FC_THROW("error binding to ${endpoint}: ${what}", ("endpoint", local_endpoint)("what", except.what())); + } + } + + void tcp_socket::enable_keep_alives(const fc::microseconds& interval) + { + if (interval.count()) + { + boost::asio::socket_base::keep_alive option(true); + my->_sock.set_option(option); +#if defined _WIN32 || defined WIN32 || defined OS_WIN64 || defined _WIN64 || defined WIN64 || defined WINNT + struct tcp_keepalive keepalive_settings; + keepalive_settings.onoff = 1; + keepalive_settings.keepalivetime = (ULONG)(interval.count() / fc::milliseconds(1).count()); + keepalive_settings.keepaliveinterval = (ULONG)(interval.count() / fc::milliseconds(1).count()); + + DWORD dwBytesRet = 0; + if (WSAIoctl(my->_sock.native(), SIO_KEEPALIVE_VALS, &keepalive_settings, sizeof(keepalive_settings), + NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) + wlog("Error setting TCP keepalive values"); +#elif !defined(__clang__) || (__clang_major__ >= 6) + // This should work for modern Linuxes and for OSX >= Mountain Lion + int timeout_sec = interval.count() / fc::seconds(1).count(); + if (setsockopt(my->_sock.native(), IPPROTO_TCP, + #if defined( __APPLE__ ) + TCP_KEEPALIVE, + #else + TCP_KEEPIDLE, + #endif + (char*)&timeout_sec, sizeof(timeout_sec)) < 0) + wlog("Error setting TCP keepalive idle time"); +# if !defined(__APPLE__) || defined(TCP_KEEPINTVL) // TCP_KEEPINTVL not defined before 10.9 + if (setsockopt(my->_sock.native(), IPPROTO_TCP, TCP_KEEPINTVL, + (char*)&timeout_sec, sizeof(timeout_sec)) < 0) + wlog("Error setting TCP keepalive interval"); +# endif // !__APPLE__ || TCP_KEEPINTVL +#endif // !WIN32 + } + else + { + boost::asio::socket_base::keep_alive option(false); + my->_sock.set_option(option); + } + } + + void tcp_socket::set_io_hooks(tcp_socket_io_hooks* new_hooks) + { + my->_io_hooks = new_hooks ? new_hooks : &*my; + } + + void tcp_socket::set_reuse_address(bool enable /* = true */) + { + FC_ASSERT(my->_sock.is_open()); + boost::asio::socket_base::reuse_address option(enable); + my->_sock.set_option(option); +#if defined(__APPLE__) || defined(__linux__) +# ifndef SO_REUSEPORT +# define SO_REUSEPORT 15 +# endif + // OSX needs SO_REUSEPORT in addition to SO_REUSEADDR. + // This probably needs to be set for any BSD + if (detail::have_so_reuseport) + { + int reuseport_value = 1; + if (setsockopt(my->_sock.native(), SOL_SOCKET, SO_REUSEPORT, + (char*)&reuseport_value, sizeof(reuseport_value)) < 0) + { + if (errno == ENOPROTOOPT) + detail::have_so_reuseport = false; + else + wlog("Error setting SO_REUSEPORT"); + } + } +#endif // __APPLE__ + } + + + class tcp_server::impl { + public: + impl() + :_accept( fc::asio::default_io_service() ) + { + _accept.open(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0).protocol()); + } + + ~impl(){ + try { + _accept.close(); + } + catch ( boost::system::system_error& ) + { + wlog( "unexpected exception ${e}", ("e", fc::except_str()) ); + } + } + + boost::asio::ip::tcp::acceptor _accept; + }; + void tcp_server::close() { + if( my && my->_accept.is_open() ) + my->_accept.close(); + delete my; + my = nullptr; + } + tcp_server::tcp_server() + :my(nullptr) { + } + tcp_server::~tcp_server() { + delete my; + } + + + void tcp_server::accept( tcp_socket& s ) + { + try + { + FC_ASSERT( my != nullptr ); + fc::asio::tcp::accept( my->_accept, s.my->_sock ); + } FC_RETHROW_EXCEPTIONS( warn, "Unable to accept connection on socket." ); + } + void tcp_server::set_reuse_address(bool enable /* = true */) + { + if( !my ) + my = new impl; + boost::asio::ip::tcp::acceptor::reuse_address option(enable); + my->_accept.set_option(option); +#if defined(__APPLE__) || (defined(__linux__) && defined(SO_REUSEPORT)) + // OSX needs SO_REUSEPORT in addition to SO_REUSEADDR. + // This probably needs to be set for any BSD + if (detail::have_so_reuseport) + { + int reuseport_value = 1; + if (setsockopt(my->_accept.native(), SOL_SOCKET, SO_REUSEPORT, + (char*)&reuseport_value, sizeof(reuseport_value)) < 0) + { + if (errno == ENOPROTOOPT) + detail::have_so_reuseport = false; + else + wlog("Error setting SO_REUSEPORT"); + } + } +#endif // __APPLE__ + } + void tcp_server::listen( uint16_t port ) + { + if( !my ) + my = new impl; + try + { + my->_accept.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4(), port)); + my->_accept.listen(); + } + FC_RETHROW_EXCEPTIONS(warn, "error listening on socket"); + } + void tcp_server::listen( const fc::ip::endpoint& ep ) + { + if( !my ) + my = new impl; + try + { + my->_accept.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string((string)ep.get_address()), ep.port())); + my->_accept.listen(); + } + FC_RETHROW_EXCEPTIONS(warn, "error listening on socket"); + } + + fc::ip::endpoint tcp_server::get_local_endpoint() const + { + FC_ASSERT( my != nullptr ); + return fc::ip::endpoint(my->_accept.local_endpoint().address().to_v4().to_ulong(), + my->_accept.local_endpoint().port() ); + } + + uint16_t tcp_server::get_port()const + { + FC_ASSERT( my != nullptr ); + return my->_accept.local_endpoint().port(); + } + + + +} // namespace fc diff --git a/src/network/udp_socket.cpp b/src/network/udp_socket.cpp new file mode 100644 index 000000000..514e148a2 --- /dev/null +++ b/src/network/udp_socket.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include + + +namespace fc { + + class udp_socket::impl : public fc::retainable { + public: + impl():_sock( fc::asio::default_io_service() ){} + ~impl(){ + // _sock.cancel(); + } + + boost::asio::ip::udp::socket _sock; + }; + + boost::asio::ip::udp::endpoint to_asio_ep( const fc::ip::endpoint& e ) { + return boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4(e.get_address()), e.port() ); + } + fc::ip::endpoint to_fc_ep( const boost::asio::ip::udp::endpoint& e ) { + return fc::ip::endpoint( e.address().to_v4().to_ulong(), e.port() ); + } + + udp_socket::udp_socket() + :my( new impl() ) + { + } + + udp_socket::udp_socket( const udp_socket& s ) + :my(s.my) + { + } + + udp_socket::~udp_socket() + { + try + { + my->_sock.close(); //close boost socket to make any pending reads run their completion handler + } + catch (...) //avoid destructor throw and likely this is just happening because socket wasn't open. + { + } + } + + size_t udp_socket::send_to( const char* buffer, size_t length, const ip::endpoint& to ) + { + try + { + return my->_sock.send_to( boost::asio::buffer(buffer, length), to_asio_ep(to) ); + } + catch( const boost::system::system_error& e ) + { + if( e.code() != boost::asio::error::would_block ) + throw; + } + + promise::ptr completion_promise(new promise("udp_socket::send_to")); + my->_sock.async_send_to( boost::asio::buffer(buffer, length), to_asio_ep(to), + asio::detail::read_write_handler(completion_promise) ); + + return completion_promise->wait(); + } + + size_t udp_socket::send_to( const std::shared_ptr& buffer, size_t length, + const fc::ip::endpoint& to ) + { + try + { + return my->_sock.send_to( boost::asio::buffer(buffer.get(), length), to_asio_ep(to) ); + } + catch( const boost::system::system_error& e ) + { + if( e.code() != boost::asio::error::would_block ) + throw; + } + + promise::ptr completion_promise(new promise("udp_socket::send_to")); + my->_sock.async_send_to( boost::asio::buffer(buffer.get(), length), to_asio_ep(to), + asio::detail::read_write_handler_with_buffer(completion_promise, buffer) ); + + return completion_promise->wait(); + } + + void udp_socket::open() { + my->_sock.open( boost::asio::ip::udp::v4() ); + my->_sock.non_blocking(true); + } + void udp_socket::set_receive_buffer_size( size_t s ) { + my->_sock.set_option(boost::asio::socket_base::receive_buffer_size(s) ); + } + void udp_socket::bind( const fc::ip::endpoint& e ) { + my->_sock.bind( to_asio_ep(e) ); + } + + size_t udp_socket::receive_from( const std::shared_ptr& receive_buffer, size_t receive_buffer_length, fc::ip::endpoint& from ) + { + try + { + boost::asio::ip::udp::endpoint boost_from_endpoint; + size_t bytes_read = my->_sock.receive_from( boost::asio::buffer(receive_buffer.get(), receive_buffer_length), + boost_from_endpoint ); + from = to_fc_ep(boost_from_endpoint); + return bytes_read; + } + catch( const boost::system::system_error& e ) + { + if( e.code() != boost::asio::error::would_block ) + throw; + } + + boost::asio::ip::udp::endpoint boost_from_endpoint; + promise::ptr completion_promise(new promise("udp_socket::receive_from")); + my->_sock.async_receive_from( boost::asio::buffer(receive_buffer.get(), receive_buffer_length), + boost_from_endpoint, + asio::detail::read_write_handler_with_buffer(completion_promise, receive_buffer) ); + size_t bytes_read = completion_promise->wait(); + from = to_fc_ep(boost_from_endpoint); + return bytes_read; + } + + size_t udp_socket::receive_from( char* receive_buffer, size_t receive_buffer_length, fc::ip::endpoint& from ) + { + try + { + boost::asio::ip::udp::endpoint boost_from_endpoint; + size_t bytes_read = my->_sock.receive_from( boost::asio::buffer(receive_buffer, receive_buffer_length), + boost_from_endpoint ); + from = to_fc_ep(boost_from_endpoint); + return bytes_read; + } + catch( const boost::system::system_error& e ) + { + if( e.code() != boost::asio::error::would_block ) + throw; + } + + boost::asio::ip::udp::endpoint boost_from_endpoint; + promise::ptr completion_promise(new promise("udp_socket::receive_from")); + my->_sock.async_receive_from( boost::asio::buffer(receive_buffer, receive_buffer_length), boost_from_endpoint, + asio::detail::read_write_handler(completion_promise) ); + size_t bytes_read = completion_promise->wait(); + from = to_fc_ep(boost_from_endpoint); + return bytes_read; + } + + void udp_socket::close() { + //my->_sock.cancel(); + my->_sock.close(); + } + + fc::ip::endpoint udp_socket::local_endpoint()const { + return to_fc_ep( my->_sock.local_endpoint() ); + } + void udp_socket::connect( const fc::ip::endpoint& e ) { + my->_sock.connect( to_asio_ep(e) ); + } + + void udp_socket::set_multicast_enable_loopback( bool s ) + { + my->_sock.set_option( boost::asio::ip::multicast::enable_loopback(s) ); + } + void udp_socket::set_reuse_address( bool s ) + { + my->_sock.set_option( boost::asio::ip::udp::socket::reuse_address(s) ); + } + void udp_socket::join_multicast_group( const fc::ip::address& a ) + { + my->_sock.set_option( boost::asio::ip::multicast::join_group( boost::asio::ip::address_v4(a) ) ); + } + +} diff --git a/src/network/udt_socket.cpp b/src/network/udt_socket.cpp new file mode 100644 index 000000000..655e16c53 --- /dev/null +++ b/src/network/udt_socket.cpp @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +# include +#endif + +namespace fc { + + void check_udt_errors() + { + UDT::ERRORINFO& error_info = UDT::getlasterror(); + if( error_info.getErrorCode() ) + { + std::string error_message = error_info.getErrorMessage(); + error_info.clear(); + FC_CAPTURE_AND_THROW( udt_exception, (error_message) ); + } + } + + class udt_epoll_service + { + public: + udt_epoll_service() + :_epoll_thread("udt_epoll") + { + UDT::startup(); + check_udt_errors(); + _epoll_id = UDT::epoll_create(); + _epoll_loop = _epoll_thread.async( [=](){ poll_loop(); }, "udt_poll_loop" ); + } + + ~udt_epoll_service() + { + _epoll_loop.cancel("udt_epoll_service is destructing"); + _epoll_loop.wait(); + UDT::cleanup(); + } + + void poll_loop() + { + std::set read_ready; + std::set write_ready; + while( !_epoll_loop.canceled() ) + { + UDT::epoll_wait( _epoll_id, + &read_ready, + &write_ready, 100000000 ); + + { synchronized(_read_promises_mutex) + for( auto sock : read_ready ) + { + auto itr = _read_promises.find( sock ); + if( itr != _read_promises.end() ) + { + itr->second->set_value(); + _read_promises.erase(itr); + } + } + } // synchronized read promise mutex + + { synchronized(_write_promises_mutex) + for( auto sock : write_ready ) + { + auto itr = _write_promises.find( sock ); + if( itr != _write_promises.end() ) + { + itr->second->set_value(); + _write_promises.erase(itr); + } + } + } // synchronized write promise mutex + } // while not canceled + } // poll_loop + + + void notify_read( int udt_socket_id, + const promise::ptr& p ) + { + int events = UDT_EPOLL_IN | UDT_EPOLL_ERR; + if( 0 != UDT::epoll_add_usock( _epoll_id, + udt_socket_id, + &events ) ) + { + check_udt_errors(); + } + { synchronized(_read_promises_mutex) + + _read_promises[udt_socket_id] = p; + } + } + + void notify_write( int udt_socket_id, + const promise::ptr& p ) + { + int events = UDT_EPOLL_OUT | UDT_EPOLL_ERR; + if( 0 != UDT::epoll_add_usock( _epoll_id, + udt_socket_id, + &events ) ) + { + check_udt_errors(); + } + + { synchronized(_write_promises_mutex) + _write_promises[udt_socket_id] = p; + } + } + void remove( int udt_socket_id ) + { + { synchronized(_read_promises_mutex) + auto read_itr = _read_promises.find( udt_socket_id ); + if( read_itr != _read_promises.end() ) + { + read_itr->second->set_exception( fc::copy_exception( fc::exception() ) ); + _read_promises.erase(read_itr); + } + } + { synchronized(_write_promises_mutex) + auto write_itr = _write_promises.find( udt_socket_id ); + if( write_itr != _write_promises.end() ) + { + write_itr->second->set_exception( fc::copy_exception( fc::exception() ) ); + _write_promises.erase(write_itr); + } + } + UDT::epoll_remove_usock( _epoll_id, udt_socket_id ); + } + + private: + fc::mutex _read_promises_mutex; + fc::mutex _write_promises_mutex; + std::unordered_map::ptr > _read_promises; + std::unordered_map::ptr > _write_promises; + + fc::future _epoll_loop; + fc::thread _epoll_thread; + int _epoll_id; + }; + + + udt_epoll_service& default_epool_service() + { + static udt_epoll_service* default_service = new udt_epoll_service(); + return *default_service; + } + + + + udt_socket::udt_socket() + :_udt_socket_id( UDT::INVALID_SOCK ) + { + } + + udt_socket::~udt_socket() + { + try { + close(); + } catch ( const fc::exception& e ) + { + wlog( "${e}", ("e", e.to_detail_string() ) ); + } + } + + void udt_socket::bind( const fc::ip::endpoint& local_endpoint ) + { try { + if( !is_open() ) + open(); + + sockaddr_in local_addr; + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(local_endpoint.port()); + local_addr.sin_addr.s_addr = htonl(local_endpoint.get_address()); + + if( UDT::ERROR == UDT::bind(_udt_socket_id, (sockaddr*)&local_addr, sizeof(local_addr)) ) + check_udt_errors(); + } FC_CAPTURE_AND_RETHROW() } + + void udt_socket::connect_to( const ip::endpoint& remote_endpoint ) + { try { + if( !is_open() ) + open(); + + sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(remote_endpoint.port()); + serv_addr.sin_addr.s_addr = htonl(remote_endpoint.get_address()); + + // UDT doesn't allow now blocking connects... + fc::thread connect_thread("connect_thread"); + connect_thread.async( [&](){ + if( UDT::ERROR == UDT::connect(_udt_socket_id, (sockaddr*)&serv_addr, sizeof(serv_addr)) ) + check_udt_errors(); + }, "udt_socket::connect_to").wait(); + + bool block = false; + UDT::setsockopt(_udt_socket_id, 0, UDT_SNDSYN, &block, sizeof(bool)); + UDT::setsockopt(_udt_socket_id, 0, UDT_RCVSYN, &block, sizeof(bool)); + check_udt_errors(); + + } FC_CAPTURE_AND_RETHROW( (remote_endpoint) ) } + + ip::endpoint udt_socket::remote_endpoint() const + { try { + sockaddr_in peer_addr; + int peer_addr_size = sizeof(peer_addr); + int error_code = UDT::getpeername( _udt_socket_id, (struct sockaddr*)&peer_addr, &peer_addr_size ); + if( error_code == UDT::ERROR ) + check_udt_errors(); + return ip::endpoint( ip::address( htonl( peer_addr.sin_addr.s_addr ) ), htons(peer_addr.sin_port) ); + } FC_CAPTURE_AND_RETHROW() } + + ip::endpoint udt_socket::local_endpoint() const + { try { + sockaddr_in sock_addr; + int addr_size = sizeof(sock_addr); + int error_code = UDT::getsockname( _udt_socket_id, (struct sockaddr*)&sock_addr, &addr_size ); + if( error_code == UDT::ERROR ) + check_udt_errors(); + return ip::endpoint( ip::address( htonl( sock_addr.sin_addr.s_addr ) ), htons(sock_addr.sin_port) ); + } FC_CAPTURE_AND_RETHROW() } + + + /// @{ + size_t udt_socket::readsome( char* buffer, size_t max ) + { try { + auto bytes_read = UDT::recv( _udt_socket_id, buffer, max, 0 ); + while( bytes_read == UDT::ERROR ) + { + if( UDT::getlasterror().getErrorCode() == CUDTException::EASYNCRCV ) + { + UDT::getlasterror().clear(); + promise::ptr p(new promise("udt_socket::readsome")); + default_epool_service().notify_read( _udt_socket_id, p ); + p->wait(); + bytes_read = UDT::recv( _udt_socket_id, buffer, max, 0 ); + } + else + check_udt_errors(); + } + return bytes_read; + } FC_CAPTURE_AND_RETHROW( (max) ) } + + size_t udt_socket::readsome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return readsome(buf.get() + offset, len); + } + + bool udt_socket::eof()const + { + // TODO... + return false; + } + /// @} + + /// ostream interface + /// @{ + size_t udt_socket::writesome( const char* buffer, size_t len ) + { try { + auto bytes_sent = UDT::send(_udt_socket_id, buffer, len, 0); + + while( UDT::ERROR == bytes_sent ) + { + if( UDT::getlasterror().getErrorCode() == CUDTException::EASYNCSND ) + { + UDT::getlasterror().clear(); + promise::ptr p(new promise("udt_socket::writesome")); + default_epool_service().notify_write( _udt_socket_id, p ); + p->wait(); + bytes_sent = UDT::send(_udt_socket_id, buffer, len, 0); + continue; + } + else + check_udt_errors(); + } + return bytes_sent; + } FC_CAPTURE_AND_RETHROW( (len) ) } + + size_t udt_socket::writesome( const std::shared_ptr& buf, size_t len, size_t offset ) + { + return writesome(buf.get() + offset, len); + } + + void udt_socket::flush(){} + + void udt_socket::close() + { try { + if( is_open() ) + { + default_epool_service().remove( _udt_socket_id ); + UDT::close( _udt_socket_id ); + check_udt_errors(); + _udt_socket_id = UDT::INVALID_SOCK; + } + else + { + wlog( "already closed" ); + } + } FC_CAPTURE_AND_RETHROW() } + /// @} + + void udt_socket::open() + { + _udt_socket_id = UDT::socket(AF_INET, SOCK_STREAM, 0); + if( _udt_socket_id == UDT::INVALID_SOCK ) + check_udt_errors(); + } + + bool udt_socket::is_open()const + { + return _udt_socket_id != UDT::INVALID_SOCK; + } + + + + + + + udt_server::udt_server() + :_udt_socket_id( UDT::INVALID_SOCK ) + { + _udt_socket_id = UDT::socket(AF_INET, SOCK_STREAM, 0); + if( _udt_socket_id == UDT::INVALID_SOCK ) + check_udt_errors(); + + bool block = false; + UDT::setsockopt(_udt_socket_id, 0, UDT_SNDSYN, &block, sizeof(bool)); + check_udt_errors(); + UDT::setsockopt(_udt_socket_id, 0, UDT_RCVSYN, &block, sizeof(bool)); + check_udt_errors(); + } + + udt_server::~udt_server() + { + try { + close(); + } catch ( const fc::exception& e ) + { + wlog( "${e}", ("e", e.to_detail_string() ) ); + } + } + + void udt_server::close() + { try { + if( _udt_socket_id != UDT::INVALID_SOCK ) + { + UDT::close( _udt_socket_id ); + check_udt_errors(); + default_epool_service().remove( _udt_socket_id ); + _udt_socket_id = UDT::INVALID_SOCK; + } + } FC_CAPTURE_AND_RETHROW() } + + void udt_server::accept( udt_socket& s ) + { try { + FC_ASSERT( !s.is_open() ); + int namelen; + sockaddr_in their_addr; + + + while( s._udt_socket_id == UDT::INVALID_SOCK ) + { + s._udt_socket_id = UDT::accept( _udt_socket_id, (sockaddr*)&their_addr, &namelen ); + if( UDT::getlasterror().getErrorCode() == CUDTException::EASYNCRCV ) + { + UDT::getlasterror().clear(); + promise::ptr p(new promise("udt_server::accept")); + default_epool_service().notify_read( _udt_socket_id, p ); + p->wait(); + s._udt_socket_id = UDT::accept( _udt_socket_id, (sockaddr*)&their_addr, &namelen ); + } + else + check_udt_errors(); + } + } FC_CAPTURE_AND_RETHROW() } + + void udt_server::listen( const ip::endpoint& ep ) + { try { + sockaddr_in my_addr; + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(ep.port()); + my_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(my_addr.sin_zero), '\0', 8); + + if( UDT::ERROR == UDT::bind(_udt_socket_id, (sockaddr*)&my_addr, sizeof(my_addr)) ) + check_udt_errors(); + + UDT::listen(_udt_socket_id, 10); + check_udt_errors(); + } FC_CAPTURE_AND_RETHROW( (ep) ) } + + fc::ip::endpoint udt_server::local_endpoint() const + { try { + sockaddr_in sock_addr; + int addr_size = sizeof(sock_addr); + int error_code = UDT::getsockname( _udt_socket_id, (struct sockaddr*)&sock_addr, &addr_size ); + if( error_code == UDT::ERROR ) + check_udt_errors(); + return ip::endpoint( ip::address( htonl( sock_addr.sin_addr.s_addr ) ), htons(sock_addr.sin_port) ); + } FC_CAPTURE_AND_RETHROW() } + +} diff --git a/src/network/url.cpp b/src/network/url.cpp new file mode 100644 index 000000000..635dd2d68 --- /dev/null +++ b/src/network/url.cpp @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include + +namespace fc +{ + namespace detail + { + class url_impl + { + public: + void parse( const fc::string& s ) + { + std::stringstream ss(s); + std::string skip,_lpath,_largs,luser,lpass; + std::getline( ss, _proto, ':' ); + std::getline( ss, skip, '/' ); + std::getline( ss, skip, '/' ); + + if( s.find('@') != size_t(fc::string::npos) ) { + fc::string user_pass; + std::getline( ss, user_pass, '@' ); + std::stringstream upss(user_pass); + if( user_pass.find( ':' ) != size_t(fc::string::npos) ) { + std::getline( upss, luser, ':' ); + std::getline( upss, lpass, ':' ); + _user = fc::move(luser); + _pass = fc::move(lpass); + } else { + _user = fc::move(user_pass); + } + } + fc::string host_port; + std::getline( ss, host_port, '/' ); + auto pos = host_port.find( ':' ); + if( pos != fc::string::npos ) { + try { + _port = static_cast(to_uint64( host_port.substr( pos+1 ) )); + } catch ( ... ) { + FC_THROW_EXCEPTION( parse_error_exception, "Unable to parse port field in url",( "url", s ) ); + } + _host = host_port.substr(0,pos); + } else { + _host = fc::move(host_port); + } + std::getline( ss, _lpath, '?' ); +#ifdef WIN32 + // On windows, a URL like file:///c:/autoexec.bat would result in _lpath = c:/autoexec.bat + // which is what we really want (it's already an absolute path) + if (!stricmp(_proto.c_str(), "file")) + _path = _lpath; + else + _path = fc::path( "/" ) / _lpath; // let other schemes behave like unix +#else + // On unix, a URL like file:///etc/rc.local would result in _lpath = etc/rc.local + // but we really want to make it the absolute path /etc/rc.local + _path = fc::path( "/" ) / _lpath; +#endif + std::getline( ss, _largs ); + if( _args.valid() && _args->size() ) + { + // TODO: args = fc::move(_args); + } + } + + string _proto; + ostring _host; + ostring _user; + ostring _pass; + opath _path; + ovariant_object _args; + fc::optional _port; + }; + } + + void to_variant( const url& u, fc::variant& v ) + { + v = fc::string(u); + } + void from_variant( const fc::variant& v, url& u ) + { + u = url( v.as_string() ); + } + + url::operator string()const + { + std::stringstream ss; + ss<_proto<<"://"; + if( my->_user.valid() ) { + ss << *my->_user; + if( my->_pass.valid() ) { + ss<<":"<<*my->_pass; + } + ss<<"@"; + } + if( my->_host.valid() ) ss<<*my->_host; + if( my->_port.valid() ) ss<<":"<<*my->_port; + if( my->_path.valid() ) ss<_path->generic_string(); + // if( my->_args ) ss<<"?"<<*my->_args; + return ss.str(); + } + + url::url( const fc::string& u ) + :my( std::make_shared() ) + { + my->parse(u); + } + + std::shared_ptr get_null_url() + { + static auto u = std::make_shared(); + return u; + } + + url::url() + :my( get_null_url() ) + { } + + url::url( const url& u ) + :my(u.my){} + + url::url( url&& u ) + :my( fc::move(u.my) ) + { + u.my = get_null_url(); + } + + url::url( const mutable_url& mu ) + :my( std::make_shared(*mu.my) ) + { + + } + url::url( mutable_url&& mu ) + :my( fc::move( mu.my ) ) + { } + + url::~url(){} + + url& url::operator=(const url& u ) + { + my = u.my; + return *this; + } + + url& url::operator=(url&& u ) + { + if( this != &u ) + { + my = fc::move(u.my); + u.my= get_null_url(); + } + return *this; + } + url& url::operator=(const mutable_url& u ) + { + my = std::make_shared(*u.my); + return *this; + } + url& url::operator=(mutable_url&& u ) + { + my = fc::move(u.my); + return *this; + } + + string url::proto()const + { + return my->_proto; + } + ostring url::host()const + { + return my->_host; + } + ostring url::user()const + { + return my->_user; + } + ostring url::pass()const + { + return my->_pass; + } + opath url::path()const + { + return my->_path; + } + ovariant_object url::args()const + { + return my->_args; + } + fc::optional url::port()const + { + return my->_port; + } + + + +} + diff --git a/src/pke.cpp b/src/pke.cpp deleted file mode 100644 index ad0e7d786..000000000 --- a/src/pke.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - void pack( fc::value& v, const fc::public_key_t& s ) { - fc::vector ve = fc::raw::pack( s ); - v = to_base58( ve.data(), size_t(ve.size()) ); - } - void unpack( const fc::value& v, fc::public_key_t& s ) { - try { - auto ve = from_base58(fc::value_cast(v)); - s = fc::raw::unpack(ve); - } catch ( ... ) { - wlog( "error unpacking signature" ); - } - } - - void pack( fc::value& v, const fc::private_key_t& s ) { - fc::vector ve = fc::raw::pack( s ); - v = to_base58( ve.data(), ve.size() ); - } - void unpack( const fc::value& v, fc::private_key_t& s ) { - try { - auto ve = from_base58(fc::value_cast(v)); - s = fc::raw::unpack(ve); - } catch ( ... ) { - wlog( "error unpacking private_key" ); - } - } - void pack( fc::value& v, const fc::signature_t& s ) { - fc::vector ve = fc::raw::pack( s ); - v = to_base58( ve.data(), ve.size() ); - } - void unpack( const fc::value& v, fc::signature_t& s ) { - try { - auto ve = from_base58(fc::value_cast(v)); - s = fc::raw::unpack(ve); - } catch ( ... ) { - wlog( "error unpacking signature" ); - } - } - - RSA* get_pub( const char* key, uint32_t key_size, uint32_t pe ) - { - RSA* rsa = RSA_new(); - rsa->n = BN_bin2bn( (unsigned char*)key, key_size, NULL ); - rsa->e = BN_new(); - BN_set_word(rsa->e, pe ); - return rsa; - } - RSA* get_priv( const fc::vector& d, uint32_t /*key_size*/, uint32_t /*pe*/ ) - { - BIO* mem = (BIO*)BIO_new_mem_buf( (void*)&d.front(), d.size() ); - RSA* rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL ); - BIO_free(mem); - return rsa; - } - - bool verify_data( const char* key, uint32_t key_size, uint32_t pe, const sha1& digest, const char* sig ) - { - RSA* pub = get_pub( key,key_size,pe); - auto v = RSA_verify( NID_sha1, (const uint8_t*)digest.data(), 20, (uint8_t*)sig, key_size, pub ); - RSA_free(pub); - return 0 != v; - } - bool sign_data( const fc::vector& key, uint32_t key_size, uint32_t pe, const sha1& digest, char* sig ) - { - RSA* priv = get_priv( key,key_size,pe); - if( !priv ) { - generic_exception g(fc::generic_exception("Error loading private key: " + fc::string(ERR_error_string( ERR_get_error(),NULL))) ); - FC_THROW(g); - } - uint32_t slen = 0; - if( 1 != RSA_sign( NID_sha1, (uint8_t*)digest.data(), sizeof(digest), (unsigned char*)sig, &slen, priv ) ) - { - RSA_free(priv); - generic_exception g(fc::generic_exception("Error signing data: " + fc::string(ERR_error_string( ERR_get_error(),NULL))) ); - FC_THROW(g); - - } - RSA_free(priv); - return true; - } - - bool public_encrypt( const char* key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ) - { - RSA* pub = get_pub( key,key_size/8,pe); - out.resize(RSA_size(pub)); - int rtn = RSA_public_encrypt( in.size(), (unsigned char*)&in.front(), (unsigned char*)&out.front(), pub, RSA_PKCS1_OAEP_PADDING ); - RSA_free(pub); - if( rtn >= 0 ) - { - out.resize(rtn); - return true; - } - out.resize(0); - FC_THROW( fc::generic_exception( ERR_error_string( ERR_get_error(), NULL ) ) ); - return false; - } - bool public_decrypt( const char* key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ) - { - RSA* pub = get_pub( key,key_size/8,pe); - out.resize(RSA_size(pub)); - int rtn = RSA_public_decrypt( RSA_size(pub), (unsigned char*)&in.front(), (unsigned char*)&out.front(), pub, RSA_PKCS1_OAEP_PADDING ); - RSA_free(pub); - if( rtn >= 0 ) - { - out.resize(rtn); - return true; - } - out.resize(0); - FC_THROW( fc::generic_exception( ERR_error_string( ERR_get_error(), NULL ) ) ); - return false;; - } - bool private_encrypt( const fc::vector& key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ) - { - RSA* priv = get_priv( key,key_size/8,pe); - int rtn = RSA_private_encrypt( in.size(), (unsigned char*)&in.front(), (unsigned char*)&out.front(), priv, RSA_PKCS1_OAEP_PADDING ); - RSA_free(priv); - if( rtn >= 0 ) - { - out.resize(rtn); - return true; - } - out.resize(0); - return false;; - } - bool private_decrypt( const fc::vector& key, uint32_t key_size, uint32_t pe, const fc::vector& in, fc::vector& out ) - { - - RSA* priv = get_priv( key,key_size/8,pe); - out.resize(RSA_size(priv)); - int rtn = RSA_private_decrypt( in.size(), (unsigned char*)&in.front(), (unsigned char*)&out.front(), priv, RSA_PKCS1_OAEP_PADDING ); - RSA_free(priv); - if( rtn >= 0 ) - { - out.resize(rtn); - return true; - } - out.resize(0); - FC_THROW( fc::generic_exception( ERR_error_string( ERR_get_error(), NULL ) ) ); - return false; - } - - bool generate_keys( char* pubkey, fc::vector& privkey, uint32_t key_size, uint32_t pe ) - { - static bool init = true; - if( init ) { ERR_load_crypto_strings(); init = false; } - - RSA* rsa = RSA_generate_key( key_size, pe, NULL, NULL ); - BN_bn2bin( rsa->n, (unsigned char*)pubkey ); - - BIO *mem = BIO_new(BIO_s_mem()); - int e = PEM_write_bio_RSAPrivateKey(mem, rsa, NULL, NULL, 0, NULL, NULL ); - if( e != 1 ) - { - BIO_free(mem); - RSA_free(rsa); - FC_THROW(generic_exception("Error writing PrivateKey") ); - } - - char* dat; - uint32_t l = BIO_get_mem_data( mem, &dat ); - privkey.resize(l); - memcpy( &privkey.front(), dat, l ); - - BIO_free(mem); - RSA_free(rsa); - return true; - } -} diff --git a/src/process.cpp b/src/process.cpp deleted file mode 100644 index 4a793117f..000000000 --- a/src/process.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - namespace bp = boost::process; - namespace io = boost::iostreams; - - fc::path find_executable_in_path( const fc::string name ) { - try { - return fc::string(bp::find_executable_in_path( std::string(name), "" )); - } catch (...) { - const char* p = std::getenv("PATH"); - FC_THROW_REPORT( "Unable to find executable ${exe} in path.", - fc::value().set("exe", name) - .set("inner", fc::except_str() ) - .set("PATH", fc::string(p!=nullptr?p:"") ) ); - } - return fc::path(); - } - - class process_sink : public io::sink { - public: - struct category : io::sink::category, io::flushable_tag {}; - typedef char type; - - process_sink( std::shared_ptr& p ):m_in(p){} - - std::streamsize write( const char* s, std::streamsize n ) { - if( !m_in ) return -1; - return static_cast(fc::asio::write( *m_in, - boost::asio::const_buffers_1( s, static_cast(n) ) )); - } - void close() { if(m_in) m_in->close(); } - bool flush() { return true; } - - private: - std::shared_ptr& m_in; - }; - - class process_source : public io::source { - public: - typedef char type; - - process_source( std::shared_ptr& pi ) - :m_pi(pi){} - - std::streamsize read( char* s, std::streamsize n ) { - if( !m_pi ) return -1; - try { - return static_cast(fc::asio::read_some( *m_pi, boost::asio::buffer( s, static_cast(n) ) )); - } catch ( const boost::system::system_error& e ) { - // wlog( "%s", fc::except_str().c_str() ); - if( e.code() == boost::asio::error::eof ) - return -1; - wlog( "%s", fc::except_str().c_str() ); - throw; - } catch ( ... ) { - //wlog( "%s", fc::except_str().c_str() ); - return -1; - } - } - private: - std::shared_ptr& m_pi; - }; -} // namespace fc - -FC_START_SHARED_IMPL( fc::process ) - public: - impl() - :stat( fc::asio::default_io_service() ), - std_out(process_source(outp)), - std_err(process_source(errp)), - std_in(process_sink(inp)), - _ins(std_in), - _outs(std_out), - _errs(std_err){} - - ~impl() { - try { - if( inp ) { - inp->close(); - } - if( _exited.valid() && !_exited.ready()) { - //child->terminate(); - _exited.wait(); - } - }catch(...) { - wlog( "caught exception cleaning up process: %s", fc::except_str().c_str() ); - } - } - - std::shared_ptr child; - std::shared_ptr outp; - std::shared_ptr errp; - std::shared_ptr inp; - - bp::status stat; - bp::context pctx; - - // provide useful buffering - io::stream std_out; - io::stream std_err; - io::stream std_in; - - fc::future _exited; - - // adapt to ostream and istream interfaces - fc::ostream_wrapper _ins; - fc::istream_wrapper _outs; - fc::istream_wrapper _errs; -FC_END_SHARED_IMPL -#include - -namespace fc { - -FC_REFERENCE_TYPE_IMPL( process ) - - -fc::future process::exec( const fc::path& exe, fc::vector&& args, - const fc::path& work_dir, int opt ) { - - my->pctx.work_dir = work_dir.string(); - - if( opt&open_stdout) - my->pctx.streams[boost::process::stdout_id] = bp::behavior::async_pipe(); - else - my->pctx.streams[boost::process::stdout_id] = bp::behavior::null(); - - - if( opt& open_stderr ) - my->pctx.streams[boost::process::stderr_id] = bp::behavior::async_pipe(); - else - my->pctx.streams[boost::process::stderr_id] = bp::behavior::null(); - - if( opt& open_stdout ) - my->pctx.streams[boost::process::stdin_id] = bp::behavior::async_pipe(); - else - my->pctx.streams[boost::process::stdin_id] = bp::behavior::close(); - - std::vector a; - a.reserve(size_t(args.size())); - for( uint32_t i = 0; i < args.size(); ++i ) { - a.push_back( args[i] ); - } - my->child.reset( new bp::child( bp::create_child( exe.string(), fc::move(a), my->pctx ) ) ); - - if( opt & open_stdout ) { - bp::handle outh = my->child->get_handle( bp::stdout_id ); - my->outp.reset( new bp::pipe( fc::asio::default_io_service(), outh.release() ) ); - } - if( opt & open_stderr ) { - bp::handle errh = my->child->get_handle( bp::stderr_id ); - my->errp.reset( new bp::pipe( fc::asio::default_io_service(), errh.release() ) ); - } - if( opt & open_stdin ) { - bp::handle inh = my->child->get_handle( bp::stdin_id ); - my->inp.reset( new bp::pipe( fc::asio::default_io_service(), inh.release() ) ); - } - - - promise::ptr p(new promise("process")); - my->stat.async_wait( my->child->get_id(), [=]( const boost::system::error_code& ec, int exit_code ) - { - //slog( "process::result %d", exit_code ); - if( !ec ) { - #ifdef BOOST_POSIX_API - try { - if( WIFEXITED(exit_code) ) - p->set_value( WEXITSTATUS(exit_code) ); - else { - FC_THROW_MSG( "process exited with: %s ", strsignal(WTERMSIG(exit_code)) ); - } - } catch ( ... ) { - p->set_exception( fc::current_exception() ); - } - #else - p->set_value(exit_code); - #endif - } - else p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); - }); - return my->_exited = p; -} - -/** - * Forcefully kills the process. - */ -void process::kill() { - my->child->terminate(); -} - -/** - * @brief returns a stream that writes to the process' stdin - */ -fc::ostream& process::in_stream() { - return my->_ins; -} - -/** - * @brief returns a stream that reads from the process' stdout - */ -fc::istream& process::out_stream() { - return my->_outs; -} -/** - * @brief returns a stream that reads from the process' stderr - */ -fc::istream& process::err_stream() { - return my->_errs; -} - -} diff --git a/src/program_options.cpp b/src/program_options.cpp deleted file mode 100644 index eebc192c0..000000000 --- a/src/program_options.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include -#include - -namespace fc { namespace program_options { - - class options_description::impl { - public: - impl( const char* c ) - :opts(c){} - - boost::program_options::options_description opts; - }; - - options_description::options_description( const char* c ) - :my(c) - { } - - options_description::~options_description(){ - - } - - - options_description& options_description::add_options(){ - return *this; - } - - options_description& options_description::operator()( const char* o, const char* desc ){ - my->opts.add_options()( o, desc ); - return *this; - } - - options_description& options_description::operator()( const char* o, const value& v, const char* desc ){ - my->opts.add_options()( o, boost::program_options::value(reinterpret_cast(v.get())), desc ); - return *this; - } - - options_description& options_description::operator()( const char* o, const value& v, const char* desc ){ - my->opts.add_options()( o, boost::program_options::value(v.get()), desc ); - return *this; - } - - options_description& options_description::operator()( const char* o, const value >& v, const char* desc ){ - my->opts.add_options()( o, boost::program_options::value >(v.get()), desc ); - //my->opts.add_options()( o, desc ); - return *this; - } - - class variables_map::impl { - public: - boost::program_options::variables_map vm; - }; - - variables_map::variables_map(){} - variables_map::~variables_map(){} - - void variables_map::parse_command_line( int argc, char** argv, const options_description& d ) { - boost::program_options::store( boost::program_options::parse_command_line( argc, argv, d.my->opts ), my->vm ); - } - int variables_map::count( const char* opt ) { - return my->vm.count(opt); - } - - fc::ostream& operator<<( fc::ostream& o, const options_description& od ) { - std::stringstream ss; ss << od.my->opts; - fc::cout << ss.str().c_str(); - return o; - } - -} } diff --git a/src/real128.cpp b/src/real128.cpp new file mode 100644 index 000000000..c83336e94 --- /dev/null +++ b/src/real128.cpp @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include + +namespace fc +{ + uint64_t real128::to_uint64()const + { + return (fixed/ FC_REAL128_PRECISION).to_uint64(); + } + + real128::real128( uint64_t integer ) + { + fixed = uint128(integer) * FC_REAL128_PRECISION; + } + real128& real128::operator += ( const real128& o ) + { + fixed += o.fixed; + return *this; + } + real128& real128::operator -= ( const real128& o ) + { + fixed -= o.fixed; + return *this; + } + + real128& real128::operator /= ( const real128& o ) + { try { + FC_ASSERT( o.fixed > uint128(0), "Divide by Zero" ); + + fc::bigint self(fixed); + fc::bigint other(o.fixed); + self *= FC_REAL128_PRECISION; + self /= other; + fixed = self; + + return *this; + } FC_CAPTURE_AND_RETHROW( (*this)(o) ) } + + real128& real128::operator *= ( const real128& o ) + { try { + fc::bigint self(fixed); + fc::bigint other(o.fixed); + self *= other; + self /= FC_REAL128_PRECISION; + fixed = self; + return *this; + } FC_CAPTURE_AND_RETHROW( (*this)(o) ) } + + + real128::real128( const std::string& ratio_str ) + { + const char* c = ratio_str.c_str(); + int digit = *c - '0'; + if (digit >= 0 && digit <= 9) + { + uint64_t int_part = digit; + ++c; + digit = *c - '0'; + while (digit >= 0 && digit <= 9) + { + int_part = int_part * 10 + digit; + ++c; + digit = *c - '0'; + } + *this = real128(int_part); + } + else + { + // if the string doesn't look like "123.45" or ".45", this code isn't designed to parse it correctly + // in particular, we don't try to handle leading whitespace or '+'/'-' indicators at the beginning + assert(*c == '.'); + fixed = fc::uint128(); + } + + + if (*c == '.') + { + c++; + digit = *c - '0'; + if (digit >= 0 && digit <= 9) + { + int64_t frac_part = digit; + int64_t frac_magnitude = 10; + ++c; + digit = *c - '0'; + while (digit >= 0 && digit <= 9) + { + frac_part = frac_part * 10 + digit; + frac_magnitude *= 10; + ++c; + digit = *c - '0'; + } + *this += real128( frac_part ) / real128( frac_magnitude ); + } + } + } + real128::operator std::string()const + { + std::stringstream ss; + ss << std::string(fixed / FC_REAL128_PRECISION); + ss << '.'; + auto frac = (fixed % FC_REAL128_PRECISION) + FC_REAL128_PRECISION; + ss << std::string( frac ).substr(1); + + auto number = ss.str(); + while( number.back() == '0' ) number.pop_back(); + + return number; + } + + real128 real128::from_fixed( const uint128& fixed ) + { + real128 result; + result.fixed = fixed; + return result; + } + + void to_variant( const real128& var, variant& vo ) + { + vo = std::string(var); + } + void from_variant( const variant& var, real128& vo ) + { + vo = real128(var.as_string()); + } + +} // namespace fc diff --git a/src/reflect.cpp b/src/reflect.cpp deleted file mode 100644 index b5897fefb..000000000 --- a/src/reflect.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -namespace fc { - void throw_bad_enum_cast( int64_t i, const char* e ) { - FC_THROW_REPORT( "Unknown field ${field} not in enum ${enum}", - fc::value().set("field",i).set("enum",e) ); - } - void throw_bad_enum_cast( const char* k, const char* e ){ - FC_THROW_REPORT( "Field '${field}' not in enum ${enum}", - fc::value().set("field",k).set("enum",e) ); - } -} diff --git a/src/rpc/cli.cpp b/src/rpc/cli.cpp new file mode 100644 index 000000000..d3070fb8b --- /dev/null +++ b/src/rpc/cli.cpp @@ -0,0 +1,221 @@ +#include +#include + +#include + +#ifndef WIN32 +#include +#endif + +#ifdef HAVE_READLINE +# include +# include +// I don't know exactly what version of readline we need. I know the 4.2 version that ships on some macs is +// missing some functions we require. We're developing against 6.3, but probably anything in the 6.x +// series is fine +# if RL_VERSION_MAJOR < 6 +# ifdef _MSC_VER +# pragma message("You have an old version of readline installed that might not support some of the features we need") +# pragma message("Readline support will not be compiled in") +# else +# warning "You have an old version of readline installed that might not support some of the features we need" +# warning "Readline support will not be compiled in" +# endif +# undef HAVE_READLINE +# endif +# ifdef WIN32 +# include +# endif +#endif + +namespace fc { namespace rpc { + +static std::vector& cli_commands() +{ + static std::vector* cmds = new std::vector(); + return *cmds; +} + +cli::~cli() +{ + if( _run_complete.valid() ) + { + stop(); + } +} + +variant cli::send_call( api_id_type api_id, string method_name, variants args /* = variants() */ ) +{ + FC_ASSERT(false); +} + +variant cli::send_callback( uint64_t callback_id, variants args /* = variants() */ ) +{ + FC_ASSERT(false); +} + +void cli::send_notice( uint64_t callback_id, variants args /* = variants() */ ) +{ + FC_ASSERT(false); +} + +void cli::start() +{ + cli_commands() = get_method_names(0); + _run_complete = fc::async( [&](){ run(); } ); +} + +void cli::stop() +{ + _run_complete.cancel(); + _run_complete.wait(); +} + +void cli::wait() +{ + _run_complete.wait(); +} + +void cli::format_result( const string& method, std::function formatter) +{ + _result_formatters[method] = formatter; +} + +void cli::set_prompt( const string& prompt ) +{ + _prompt = prompt; +} + +void cli::run() +{ + while( !_run_complete.canceled() ) + { + try + { + std::string line; + try + { + getline( _prompt.c_str(), line ); + } + catch ( const fc::eof_exception& e ) + { + break; + } + std::cout << line << "\n"; + line += char(EOF); + fc::variants args = fc::json::variants_from_string(line);; + if( args.size() == 0 ) + continue; + + const string& method = args[0].get_string(); + + auto result = receive_call( 0, method, variants( args.begin()+1,args.end() ) ); + auto itr = _result_formatters.find( method ); + if( itr == _result_formatters.end() ) + { + std::cout << fc::json::to_pretty_string( result ) << "\n"; + } + else + std::cout << itr->second( result, args ) << "\n"; + } + catch ( const fc::exception& e ) + { + std::cout << e.to_detail_string() << "\n"; + } + } +} + + +char * dupstr (const char* s) { + char *r; + + r = (char*) malloc ((strlen (s) + 1)); + strcpy (r, s); + return (r); +} + +char* my_generator(const char* text, int state) +{ + static int list_index, len; + const char *name; + + if (!state) { + list_index = 0; + len = strlen (text); + } + + auto& cmd = cli_commands(); + + while( list_index < cmd.size() ) + { + name = cmd[list_index].c_str(); + list_index++; + + if (strncmp (name, text, len) == 0) + return (dupstr(name)); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + + +static char** cli_completion( const char * text , int start, int end) +{ + char **matches; + matches = (char **)NULL; + +#ifdef HAVE_READLINE + if (start == 0) + matches = rl_completion_matches ((char*)text, &my_generator); + else + rl_bind_key('\t',rl_abort); +#endif + + return (matches); +} + + +void cli::getline( const fc::string& prompt, fc::string& line) +{ + // getting file descriptor for C++ streams is near impossible + // so we just assume it's the same as the C stream... +#ifdef HAVE_READLINE +#ifndef WIN32 + if( isatty( fileno( stdin ) ) ) +#else + // it's implied by + // https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx + // that this is the proper way to do this on Windows, but I have + // no access to a Windows compiler and thus, + // no idea if this actually works + if( _isatty( _fileno( stdin ) ) ) +#endif + { + rl_attempted_completion_function = cli_completion; + + static fc::thread getline_thread("getline"); + getline_thread.async( [&](){ + char* line_read = nullptr; + std::cout.flush(); //readline doesn't use cin, so we must manually flush _out + line_read = readline(prompt.c_str()); + if( line_read == nullptr ) + FC_THROW_EXCEPTION( fc::eof_exception, "" ); + rl_bind_key( '\t', rl_complete ); + if( *line_read ) + add_history(line_read); + line = line_read; + free(line_read); + }).wait(); + } + else +#endif + { + std::cout << prompt; + // sync_call( cin_thread, [&](){ std::getline( *input_stream, line ); }, "getline"); + fc::getline( fc::cin, line ); + return; + } +} + +} } // namespace fc::rpc diff --git a/src/rpc/http_api.cpp b/src/rpc/http_api.cpp new file mode 100644 index 000000000..c842369ad --- /dev/null +++ b/src/rpc/http_api.cpp @@ -0,0 +1,123 @@ + +#include + +namespace fc { namespace rpc { + +http_api_connection::~http_api_connection() +{ +} + +http_api_connection::http_api_connection() +{ + _rpc_state.add_method( "call", [this]( const variants& args ) -> variant + { + FC_ASSERT( args.size() == 3 && args[2].is_array() ); + return this->receive_call( + args[0].as_uint64(), + args[1].as_string(), + args[2].get_array() ); + } ); + + _rpc_state.add_method( "notice", [this]( const variants& args ) -> variant + { + FC_ASSERT( args.size() == 2 && args[1].is_array() ); + this->receive_notice( + args[0].as_uint64(), + args[1].get_array() ); + return variant(); + } ); + + _rpc_state.add_method( "callback", [this]( const variants& args ) -> variant + { + FC_ASSERT( args.size() == 2 && args[1].is_array() ); + this->receive_callback( + args[0].as_uint64(), + args[1].get_array() ); + return variant(); + } ); + + _rpc_state.on_unhandled( [&]( const std::string& method_name, const variants& args ) + { + return this->receive_call( 0, method_name, args ); + } ); +} + +variant http_api_connection::send_call( + api_id_type api_id, + string method_name, + variants args /* = variants() */ ) +{ + // HTTP has no way to do this, so do nothing + return variant(); +} + +variant http_api_connection::send_callback( + uint64_t callback_id, + variants args /* = variants() */ ) +{ + // HTTP has no way to do this, so do nothing + return variant(); +} + +void http_api_connection::send_notice( + uint64_t callback_id, + variants args /* = variants() */ ) +{ + // HTTP has no way to do this, so do nothing + return; +} + +void http_api_connection::on_request( const fc::http::request& req, const fc::http::server::response& resp ) +{ + // this must be called by outside HTTP server's on_request method + std::string resp_body; + http::reply::status_code resp_status; + + try + { + resp.add_header( "Content-Type", "application/json" ); + std::string req_body( req.body.begin(), req.body.end() ); + auto var = fc::json::from_string( req_body ); + const auto& var_obj = var.get_object(); + + if( var_obj.contains( "method" ) ) + { + auto call = var.as(); + try + { + auto result = _rpc_state.local_call( call.method, call.params ); + resp_body = fc::json::to_string( fc::rpc::response( *call.id, result ) ); + resp_status = http::reply::OK; + } + catch ( const fc::exception& e ) + { + resp_body = fc::json::to_string( fc::rpc::response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) ); + resp_status = http::reply::InternalServerError; + } + } + else + { + resp_status = http::reply::BadRequest; + resp_body = ""; + } + } + catch ( const fc::exception& e ) + { + resp_status = http::reply::InternalServerError; + resp_body = ""; + wdump((e.to_detail_string())); + } + try + { + resp.set_status( resp_status ); + resp.set_length( resp_body.length() ); + resp.write( resp_body.c_str(), resp_body.length() ); + } + catch( const fc::exception& e ) + { + wdump((e.to_detail_string())); + } + return; +} + +} } // namespace fc::rpc diff --git a/src/rpc/json_connection.cpp b/src/rpc/json_connection.cpp new file mode 100644 index 000000000..bde046a15 --- /dev/null +++ b/src/rpc/json_connection.cpp @@ -0,0 +1,719 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc { namespace rpc { + + namespace detail + { + class json_connection_impl + { + public: + json_connection_impl( fc::buffered_istream_ptr&& in, fc::buffered_ostream_ptr&& out ) + :_in(fc::move(in)),_out(fc::move(out)),_eof(false),_next_id(0),_logger("json_connection"){} + + fc::buffered_istream_ptr _in; + fc::buffered_ostream_ptr _out; + + fc::future _done; + fc::future _handle_message_future; + bool _eof; + + uint64_t _next_id; + boost::unordered_map::ptr> _awaiting; + boost::unordered_map _methods; + boost::unordered_map _named_param_methods; + + fc::mutex _write_mutex; + std::function _on_close; + + logger _logger; + + void send_result( variant id, variant result ) + { + ilog( "send: {\"id\": ${i}, \"result\": ${r}}", ("i",id)("r",result) ); + { + fc::scoped_lock lock(_write_mutex); + *_out << "{\"id\":"; + json::to_stream( *_out, id ); + *_out << ",\"result\":"; + json::to_stream( *_out, result); + *_out << "}\n"; + _out->flush(); + } + } + void send_error( variant id, fc::exception& e ) + { + ilog( "send: {\"id\": ${i}, \"error\":{\"message\": ${what},\"code\":0,\"data\":${data}}}", + ("i",id)("what",e.what())("data", e) ); + { + fc::scoped_lock lock(_write_mutex); + *_out << "{\"id\":"; + json::to_stream( *_out, id ); + *_out << ",\"error\":{\"message\":"; + json::to_stream( *_out, fc::string(e.what()) ); + *_out <<",\"code\":0,\"data\":"; + json::to_stream( *_out, variant(e)); + *_out << "}}\n"; + _out->flush(); + } + //wlog( "exception: ${except}", ("except", variant(e)) ); + } + + void handle_message( const variant_object& obj ) + { + wlog( "recv: ${msg}", ("msg", obj) ); + fc::exception_ptr eptr; + try + { + auto m = obj.find("method"); + auto i = obj.find("id"); + if( m != obj.end() ) + { + fc::exception except; + bool exception_caught = false; + try + { + auto p = obj.find("params"); + variant result; + if( p == obj.end() ) + { + auto pmi = _methods.find(m->value().as_string()); + auto nmi = _named_param_methods.find(m->value().as_string()); + if( pmi != _methods.end() ) + { + result = pmi->second( variants() ); + } + else if( nmi != _named_param_methods.end() ) + { + result = nmi->second( variant_object() ); + } + else // invalid method + { + FC_THROW_EXCEPTION( exception, "Invalid Method '${method}'", ("method",m->value().as_string())); + } + } + else if( p->value().is_array() ) + { + auto pmi = _methods.find(m->value().as_string()); + if( pmi != _methods.end() ) + { + result = pmi->second( p->value().get_array() ); + } + else // invalid method / param combo + { + FC_THROW_EXCEPTION( exception, "Invalid method or params '${method}'", + ("method",m->value().as_string())); + } + + } + else if( p->value().is_object() ) + { + auto nmi = _named_param_methods.find(m->value().as_string()); + if( nmi != _named_param_methods.end() ) + { + result = nmi->second( p->value().get_object() ); + } + else // invalid method / param combo? + { + FC_THROW_EXCEPTION( exception, "Invalid method or params '${method}'", + ("method",m->value().as_string())); + } + } + else // invalid params + { + FC_THROW_EXCEPTION( exception, "Invalid Params for method ${method}", + ("method",m->value().as_string())); + } + if( i != obj.end() ) + { + send_result( i->value(), result ); + } + } + catch ( fc::exception& e ) + { + exception_caught = true; + except = e; + } + if( exception_caught && i != obj.end() ) + send_error( i->value(), except ); + else + fc_wlog( _logger, "json rpc exception: ${exception}", ("exception",except) ); + } + else if( i != obj.end() ) //handle any received JSON response + { + uint64_t id = i->value().as_int64(); + auto await = _awaiting.find(id); + if( await != _awaiting.end() ) + { + auto r = obj.find("result"); + auto e = obj.find("error"); + if( r != obj.end() ) //if regular result response + { + await->second->set_value( r->value() ); + } + else if( e != obj.end() ) //if error response + { + fc::exception_ptr eptr; + try + { + auto err = e->value().get_object(); + auto data = err.find( "data" ); + if( data != err.end() ) + { + //wlog( "exception: ${except}", ("except", data->value() ) ); + await->second->set_exception( data->value().as().dynamic_copy_exception() ); + } + else + await->second->set_exception( exception_ptr(new FC_EXCEPTION( exception, "${error}", ("error",e->value()) ) ) ); + } + catch ( fc::exception& e ) + { + elog( "Error parsing exception: ${e}", ("e", e.to_detail_string() ) ); + eptr = e.dynamic_copy_exception(); + } + if( eptr ) await->second->set_exception( eptr ); + } + else // id found without error, result, nor method field + { + fc_wlog( _logger, "no error or result specified in '${message}'", ("message",obj) ); + } + } + } + else // no method nor request id... invalid message + { + + } + } + catch ( fc::exception& e ) // catch all other errors... + { + fc_elog( _logger, "json rpc exception: ${exception}", ("exception",e )); + elog( "json rpc exception: ${exception}", ("exception",e )); + eptr = e.dynamic_copy_exception(); + } + if( eptr ) { close(eptr); } + } + + void read_loop() + { + fc::exception_ptr eptr; + try + { + fc::string line; + while( !_done.canceled() ) + { + variant v = json::from_stream(*_in); + ///ilog( "input: ${in}", ("in", v ) ); + //wlog( "recv: ${line}", ("line", line) ); + _handle_message_future = fc::async([=](){ handle_message(v.get_object()); }, "json_connection handle_message"); + } + } + catch ( eof_exception& eof ) + { + _eof = true; + eptr = eof.dynamic_copy_exception(); + } + catch ( exception& e ) + { + eptr = e.dynamic_copy_exception(); + } + catch ( ... ) + { + eptr = fc::exception_ptr(new FC_EXCEPTION( unhandled_exception, "json connection read error" )); + } + if( eptr ) close( eptr ); + } + + void close( fc::exception_ptr e ) + { + wlog( "close ${reason}", ("reason", e->to_detail_string() ) ); + if( _on_close ) + _on_close(e); + for( auto itr = _awaiting.begin(); itr != _awaiting.end(); ++itr ) + { + itr->second->set_exception( e->dynamic_copy_exception() ); + } + } + }; + }//namespace detail + + json_connection::json_connection( fc::buffered_istream_ptr in, fc::buffered_ostream_ptr out ) + :my( new detail::json_connection_impl(fc::move(in),fc::move(out)) ) + {} + + json_connection::~json_connection() + { + close(); + } + + fc::future json_connection::exec() + { + if( my->_done.valid() ) + { + FC_THROW_EXCEPTION( assert_exception, "start should only be called once" ); + } + return my->_done = fc::async( [=](){ my->read_loop(); }, "json_connection read_loop" ); + } + + void json_connection::close() + { + try + { + if( my->_handle_message_future.valid() && !my->_handle_message_future.ready() ) + my->_handle_message_future.cancel_and_wait(__FUNCTION__); + if( my->_done.valid() && !my->_done.ready() ) + { + my->_done.cancel("json_connection is destructing"); + my->_out->close(); + my->_done.wait(); + } + } + catch ( fc::canceled_exception& ){} // expected exception + catch ( fc::eof_exception& ){} // expected exception + catch ( fc::exception& e ) + { + // unhandled, unexpected exception cannot throw from destructor, so log it. + wlog( "${exception}", ("exception",e.to_detail_string()) ); + } + } + + void json_connection::set_on_disconnected_callback(std::function callback) + { + my->_on_close = callback; + } + + void json_connection::add_method( const fc::string& name, method m ) + { + ilog( "add method ${name}", ("name",name) ); + my->_methods.emplace(std::pair(name,fc::move(m))); + } + void json_connection::add_named_param_method( const fc::string& name, named_param_method m ) + { + ilog( "add named param method ${name}", ("name",name) ); + my->_named_param_methods.emplace(std::pair(name,fc::move(m))); + } + void json_connection::remove_method( const fc::string& name ) + { + my->_methods.erase(name); + my->_named_param_methods.erase(name); + } + void json_connection::notice( const fc::string& method, const variants& args ) + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"method\":"; + json::to_stream( *my->_out, method ); + if( args.size() ) + { + *my->_out << ",\"params\":"; + fc::json::to_stream( *my->_out, args ); + *my->_out << "}\n"; + } + else + { + *my->_out << ",\"params\":[]}\n"; + } + } + void json_connection::notice( const fc::string& method, const variant_object& named_args ) + { + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":"; + fc::json::to_stream( *my->_out, named_args ); + *my->_out << "}\n"; + my->_out->flush(); + } + } + void json_connection::notice( const fc::string& method ) + { + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << "}\n"; + my->_out->flush(); + } + } + + + future json_connection::async_call( const fc::string& method, const variants& args ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + if( args.size() ) + { + *my->_out << ",\"params\":"; + fc::json::to_stream( *my->_out, args ); + *my->_out << "}\n"; + } + else + { + *my->_out << ",\"params\":[]}\n"; + } + my->_out->flush(); + } + return my->_awaiting[id]; + } + + future json_connection::async_call( const fc::string& method, const variant& a1 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method, const variant& a1, const variant& a2 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method, const variant& a1, const variant& a2, const variant& a3 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + + future json_connection::async_call( const fc::string& method, const variant& a1, const variant& a2, const variant& a3, const variant& a4 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + + future json_connection::async_call( const fc::string& method, const variant& a1, const variant& a2, const variant& a3, const variant& a4, const variant& a5 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a5 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + + future json_connection::async_call( const fc::string& method, const variant& a1, const variant& a2, const variant& a3, const variant& a4, const variant& a5, const variant& a6 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a5 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a6 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method, const variant& a1, const variant& a2, const variant& a3, const variant& a4, const variant& a5, const variant& a6, const variant& a7 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a5 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a6 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a7 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a5 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a6 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a7 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a8 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + const variant& a9 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a5 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a6 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a7 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a8 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a9 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method, + const variant& a1, + const variant& a2, + const variant& a3, + const variant& a4, + const variant& a5, + const variant& a6, + const variant& a7, + const variant& a8, + const variant& a9, + const variant& a10 ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + + { + fc::scoped_lock lock(my->_write_mutex); + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":["; + fc::json::to_stream( *my->_out, a1 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a2 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a3 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a4 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a5 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a6 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a7 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a8 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a9 ); + *my->_out << ","; + fc::json::to_stream( *my->_out, a10 ); + *my->_out << "]}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + + future json_connection::async_call( const fc::string& method, mutable_variant_object named_args ) + { + return async_call( method, variant_object( fc::move(named_args) ) ); + } + future json_connection::async_call( const fc::string& method, const variant_object& named_args ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + fc::scoped_lock lock(my->_write_mutex); + { + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << ",\"params\":"; + fc::json::to_stream( *my->_out, named_args ); + *my->_out << "}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + future json_connection::async_call( const fc::string& method ) + { + auto id = my->_next_id++; + my->_awaiting[id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + fc::scoped_lock lock(my->_write_mutex); + { + *my->_out << "{\"id\":"; + *my->_out << id; + *my->_out << ",\"method\":"; + json::to_stream( *my->_out, method ); + *my->_out << "}\n"; + my->_out->flush(); + } + return my->_awaiting[id]; + } + + logger json_connection::get_logger()const + { + return my->_logger; + } + + void json_connection::set_logger( const logger& l ) + { + my->_logger = l; + } + +}} diff --git a/src/rpc/state.cpp b/src/rpc/state.cpp new file mode 100644 index 000000000..7d4063d2f --- /dev/null +++ b/src/rpc/state.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +namespace fc { namespace rpc { +state::~state() +{ + close(); +} + +void state::add_method( const fc::string& name, method m ) +{ + _methods.emplace(std::pair(name,fc::move(m))); +} + +void state::remove_method( const fc::string& name ) +{ + _methods.erase(name); +} + +variant state::local_call( const string& method_name, const variants& args ) +{ + auto method_itr = _methods.find(method_name); + if( method_itr == _methods.end() && _unhandled ) + return _unhandled( method_name, args ); + FC_ASSERT( method_itr != _methods.end(), "Unknown Method: ${name}", ("name",method_name) ); + return method_itr->second(args); +} + +void state::handle_reply( const response& response ) +{ + auto await = _awaiting.find( response.id ); + FC_ASSERT( await != _awaiting.end(), "Unknown Response ID: ${id}", ("id",response.id)("response",response) ); + if( response.result ) + await->second->set_value( *response.result ); + else if( response.error ) + { + await->second->set_exception( exception_ptr(new FC_EXCEPTION( exception, "${error}", ("error",response.error->message)("data",response) ) ) ); + } + else + await->second->set_value( fc::variant() ); + _awaiting.erase(await); +} + +request state::start_remote_call( const string& method_name, variants args ) +{ + request request{ _next_id++, method_name, std::move(args) }; + _awaiting[*request.id] = fc::promise::ptr( new fc::promise("json_connection::async_call") ); + return request; +} +variant state::wait_for_response( uint64_t request_id ) +{ + auto itr = _awaiting.find(request_id); + FC_ASSERT( itr != _awaiting.end() ); + return fc::future( itr->second ).wait(); +} +void state::close() +{ + for( auto item : _awaiting ) + item.second->set_exception( fc::exception_ptr(new FC_EXCEPTION( eof_exception, "connection closed" )) ); + _awaiting.clear(); +} +void state::on_unhandled( const std::function& unhandled ) +{ + _unhandled = unhandled; +} + +} } // namespace fc::rpc diff --git a/src/rpc/websocket_api.cpp b/src/rpc/websocket_api.cpp new file mode 100644 index 000000000..39381abeb --- /dev/null +++ b/src/rpc/websocket_api.cpp @@ -0,0 +1,126 @@ + +#include + +namespace fc { namespace rpc { + +websocket_api_connection::~websocket_api_connection() +{ +} + +websocket_api_connection::websocket_api_connection( fc::http::websocket_connection& c ) + : _connection(c) +{ + _rpc_state.add_method( "call", [this]( const variants& args ) -> variant + { + FC_ASSERT( args.size() == 3 && args[2].is_array() ); + return this->receive_call( + args[0].as_uint64(), + args[1].as_string(), + args[2].get_array() ); + } ); + + _rpc_state.add_method( "notice", [this]( const variants& args ) -> variant + { + FC_ASSERT( args.size() == 2 && args[1].is_array() ); + this->receive_notice( args[0].as_uint64(), args[1].get_array() ); + return variant(); + } ); + + _rpc_state.add_method( "callback", [this]( const variants& args ) -> variant + { + FC_ASSERT( args.size() == 2 && args[1].is_array() ); + this->receive_callback( args[0].as_uint64(), args[1].get_array() ); + return variant(); + } ); + + _rpc_state.on_unhandled( [&]( const std::string& method_name, const variants& args ) + { + return this->receive_call( 0, method_name, args ); + } ); + + _connection.on_message_handler( [&]( const std::string& msg ){ on_message(msg,true); } ); + _connection.on_http_handler( [&]( const std::string& msg ){ return on_message(msg,false); } ); + _connection.closed.connect( [this](){ closed(); } ); +} + +variant websocket_api_connection::send_call( + api_id_type api_id, + string method_name, + variants args /* = variants() */ ) +{ + auto request = _rpc_state.start_remote_call( "call", {api_id, std::move(method_name), std::move(args) } ); + _connection.send_message( fc::json::to_string(request) ); + return _rpc_state.wait_for_response( *request.id ); +} + +variant websocket_api_connection::send_callback( + uint64_t callback_id, + variants args /* = variants() */ ) +{ + auto request = _rpc_state.start_remote_call( "callback", {callback_id, std::move(args) } ); + _connection.send_message( fc::json::to_string(request) ); + return _rpc_state.wait_for_response( *request.id ); +} + +void websocket_api_connection::send_notice( + uint64_t callback_id, + variants args /* = variants() */ ) +{ + fc::rpc::request req{ optional(), "notice", {callback_id, std::move(args)}}; + _connection.send_message( fc::json::to_string(req) ); +} + +std::string websocket_api_connection::on_message( + const std::string& message, + bool send_message /* = true */ ) +{ + try + { + auto var = fc::json::from_string(message); + const auto& var_obj = var.get_object(); + if( var_obj.contains( "method" ) ) + { + auto call = var.as(); + exception_ptr optexcept; + try + { + auto result = _rpc_state.local_call( call.method, call.params ); + if( call.id ) + { + auto reply = fc::json::to_string( response( *call.id, result ) ); + if( send_message ) + _connection.send_message( reply ); + return reply; + } + } + catch ( const fc::exception& e ) + { + if( call.id ) + { + optexcept = e.dynamic_copy_exception(); + } + } + if( optexcept ) { + + auto reply = fc::json::to_string( response( *call.id, error_object{ 1, optexcept->to_detail_string(), fc::variant(*optexcept)} ) ); + if( send_message ) + _connection.send_message( reply ); + + return reply; + } + } + else + { + auto reply = var.as(); + _rpc_state.handle_reply( reply ); + } + } + catch ( const fc::exception& e ) + { + wdump((e.to_detail_string())); + return e.to_detail_string(); + } + return string(); +} + +} } // namespace fc::rpc diff --git a/src/sha1.cpp b/src/sha1.cpp deleted file mode 100644 index 363a5c27e..000000000 --- a/src/sha1.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - - sha1::sha1() { memset( _hash, 0, sizeof(_hash) ); } - sha1::sha1( const fc::string& hex_str ) { - fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); - } - - fc::string sha1::str()const { - return to_hex( (char*)_hash, sizeof(_hash) ); - } - sha1::operator fc::string()const { return str(); } - - char* sha1::data()const { return (char*)&_hash[0]; } - - - struct sha1::encoder::impl { - SHA_CTX ctx; - }; - - sha1::encoder::~encoder() {} - sha1::encoder::encoder() { - reset(); - } - - sha1 sha1::hash( const char* d, uint32_t dlen ) { - encoder e; - e.write(d,dlen); - return e.result(); - } - sha1 sha1::hash( const fc::string& s ) { - return hash( s.c_str(), s.size() ); - } - sha1 sha1::hash( const fc::path& s ) { - file_mapping fmap( s.string().c_str(), read_only ); - size_t fsize = file_size(s); - mapped_region mr( fmap, fc::read_only, 0, fsize ); - - const char* pos = reinterpret_cast(mr.get_address()); - return hash( pos, fsize ); - } - - void sha1::encoder::write( const char* d, uint32_t dlen ) { - SHA1_Update( &my->ctx, d, dlen); - } - sha1 sha1::encoder::result() { - sha1 h; - SHA1_Final((uint8_t*)h.data(), &my->ctx ); - return h; - } - void sha1::encoder::reset() { - SHA1_Init( &my->ctx); - } - - fc::sha1 operator << ( const fc::sha1& h1, uint32_t i ) { - fc::sha1 result; - uint8_t* r = (uint8_t*)result._hash; - uint8_t* s = (uint8_t*)h1._hash; - for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[19] = s[19] << i; - return result; - } - fc::sha1 operator ^ ( const fc::sha1& h1, const fc::sha1& h2 ) { - fc::sha1 result; - result._hash[0] = h1._hash[0] ^ h2._hash[0]; - result._hash[1] = h1._hash[1] ^ h2._hash[1]; - result._hash[2] = h1._hash[2] ^ h2._hash[2]; - result._hash[3] = h1._hash[3] ^ h2._hash[3]; - result._hash[4] = h1._hash[4] ^ h2._hash[4]; - return result; - } - bool operator >= ( const fc::sha1& h1, const fc::sha1& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0; - } - bool operator > ( const fc::sha1& h1, const fc::sha1& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0; - } - bool operator < ( const fc::sha1& h1, const fc::sha1& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0; - } - bool operator != ( const fc::sha1& h1, const fc::sha1& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0; - } - bool operator == ( const fc::sha1& h1, const fc::sha1& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0; - } - - void pack( fc::value& v, const fc::sha1& s ) { - v = fc::string(s); - } - void unpack( const fc::value& v, fc::sha1& s ) { - s = sha1(fc::value_cast(v)); - } - -} // namespace fc - diff --git a/src/sha256.cpp b/src/sha256.cpp deleted file mode 100644 index 7473839f7..000000000 --- a/src/sha256.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - - sha256::sha256() { memset( _hash, 0, sizeof(_hash) ); } - sha256::sha256( const fc::string& hex_str ) { - fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) ); - } - - fc::string sha256::str()const { - return to_hex( (char*)_hash, sizeof(_hash) ); - } - sha256::operator fc::string()const { return str(); } - - char* sha256::data()const { return (char*)&_hash[0]; } - - - struct sha256::encoder::impl { - SHA_CTX ctx; - }; - - sha256::encoder::~encoder() {} - sha256::encoder::encoder() { - reset(); - } - - sha256 sha256::hash( const char* d, uint32_t dlen ) { - encoder e; - e.write(d,dlen); - return e.result(); - } - sha256 sha256::hash( const fc::string& s ) { - return hash( s.c_str(), s.size() ); - } - sha256 sha256::hash( const fc::path& s ) { - file_mapping fmap( s.string().c_str(), read_only ); - size_t fsize = file_size(s); - mapped_region mr( fmap, fc::read_only, 0, fsize ); - - const char* pos = reinterpret_cast(mr.get_address()); - return hash( pos, fsize ); - } - - void sha256::encoder::write( const char* d, uint32_t dlen ) { - SHA1_Update( &my->ctx, d, dlen); - } - sha256 sha256::encoder::result() { - sha256 h; - SHA1_Final((uint8_t*)h.data(), &my->ctx ); - return h; - } - void sha256::encoder::reset() { - SHA1_Init( &my->ctx); - } - - fc::sha256 operator << ( const fc::sha256& h1, uint32_t i ) { - fc::sha256 result; - uint8_t* r = (uint8_t*)result._hash; - uint8_t* s = (uint8_t*)h1._hash; - for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[31] = s[31] << i; - return result; - } - fc::sha256 operator ^ ( const fc::sha256& h1, const fc::sha256& h2 ) { - fc::sha256 result; - result._hash[0] = h1._hash[0] ^ h2._hash[0]; - result._hash[1] = h1._hash[1] ^ h2._hash[1]; - result._hash[2] = h1._hash[2] ^ h2._hash[2]; - result._hash[3] = h1._hash[3] ^ h2._hash[3]; - return result; - } - bool operator >= ( const fc::sha256& h1, const fc::sha256& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0; - } - bool operator > ( const fc::sha256& h1, const fc::sha256& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0; - } - bool operator < ( const fc::sha256& h1, const fc::sha256& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0; - } - bool operator != ( const fc::sha256& h1, const fc::sha256& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0; - } - bool operator == ( const fc::sha256& h1, const fc::sha256& h2 ) { - return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0; - } - - void pack( fc::value& v, const fc::sha256& s ) { - v = fc::string(s); - } - void unpack( const fc::value& v, fc::sha256& s ) { - s = sha256(fc::value_cast(v)); - } - -} // namespace fc - diff --git a/src/ssh.cpp b/src/ssh.cpp deleted file mode 100644 index 04375cf02..000000000 --- a/src/ssh.cpp +++ /dev/null @@ -1,1060 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fc { namespace ssh { - - namespace detail { - static int ssh_init = libssh2_init(0); - - class process_impl; - class process_istream : public fc::istream { - public: - process_istream( process_impl& p, int c ) - :proc(p),chan(c){} - - virtual size_t readsome( char* buf, size_t len ); - virtual istream& read( char* buf, size_t len ); - - virtual bool eof()const; - - process_impl& proc; - int chan; - }; - - class process_ostream : public fc::ostream { - public: - process_ostream( process_impl& p ) - :proc(p){} - - virtual ostream& write( const char* buf, size_t len ); - virtual void close(); - virtual void flush(); - - process_impl& proc; - }; - - class process_impl : public fc::retainable { - public: - process_impl( const client& c, const fc::string& cmd, const fc::string& pty_type ); - fc::string command; - fc::promise::ptr result; - LIBSSH2_CHANNEL* chan; - - int read_some( char* data, size_t len, int stream_id ); - int write_some( const char* data, size_t len, int stream_id ); - void flush(); - void send_eof(); - - client sshc; - process_istream std_err; - process_istream std_out; - process_ostream std_in; - - }; - - - class client_impl : public fc::retainable { - public: - client_impl() { - sftp = nullptr; - session = nullptr; - knownhosts = nullptr; - _trace_level = LIBSSH2_TRACE_ERROR; - logr = fc::logger::get( "fc::ssh::client" ); - logr.set_parent( fc::logger::get( "default" ) ); - } - LIBSSH2_SESSION* session; - LIBSSH2_KNOWNHOSTS* knownhosts; - LIBSSH2_SFTP* sftp; - - std::unique_ptr sock; - boost::asio::ip::tcp::endpoint endpt; - - fc::mutex scp_send_mutex; - fc::string uname; - fc::string upass; - fc::string pubkey; - fc::string privkey; - fc::string passphrase; - fc::string hostname; - uint16_t port; - bool session_connected; - fc::promise::ptr read_prom; - fc::promise::ptr write_prom; - fc::spin_lock _spin_lock; - - LIBSSH2_CHANNEL* open_channel( const fc::string& pty_type ); - static void kbd_callback(const char *name, int name_len, - const char *instruction, int instruction_len, int num_prompts, - const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, - LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, - void **abstract) - { - int i; - size_t n; - char buf[1024]; - client_impl* self = (client_impl*)*abstract; - - //slog( "Keyboard-interactive authentication" ); - // printf("Performing keyboard-interactive authentication.\n"); - - // printf("Authentication name: '"); - // fwrite(name, 1, name_len, stdout); - // printf("'\n"); - - // printf("Authentication instruction: '"); - // fwrite(instruction, 1, instruction_len, stdout); - // printf("'\n"); - - // printf("Number of prompts: %d\n\n", num_prompts); - - for (i = 0; i < num_prompts; i++) { - // printf("Prompt %d from server: '", i); - fwrite(prompts[i].text, 1, prompts[i].length, stdout); - // printf("'\n"); - - // printf("Please type response: "); - - if( self->upass.size() == 0 ) { - fgets(buf, sizeof(buf), stdin); - n = strlen(buf); - while (n > 0 && strchr("\r\n", buf[n - 1])) - n--; - buf[n] = 0; - - #ifdef WIN32 // fix warning - #define strdup _strdup - #endif - responses[i].text = strdup(buf); - responses[i].length = n; - } else { - responses[i].text = strdup(self->upass.c_str()); - responses[i].length = self->upass.size(); - } - - // printf("Response %d from user is '", i); - // fwrite(responses[i].text, 1, responses[i].length, stdout); - // printf("'\n\n"); - } - - // printf("Done. Sending keyboard-interactive responses to server now.\n"); - } // kbd_callback - - void connect() { - try { - if( libssh2_init(0) < 0 ) { FC_THROW_REPORT( "Unable to init libssh2" ); } - - auto eps = fc::asio::tcp::resolve( hostname, fc::lexical_cast(port) ); - if( eps.size() == 0 ) { - FC_THROW_REPORT( "Unable to resolve host '${host}'", fc::value().set("host",hostname) ); - } - sock.reset( new boost::asio::ip::tcp::socket( fc::asio::default_io_service() ) ); - - bool resolved = false; - for( uint32_t i = 0; i < eps.size(); ++i ) { - std::stringstream ss; ss << eps[i]; - try { - boost::system::error_code ec; - fc_ilog( logr, "Attempting to connect to ${endpoint}", ("endpoint",ss.str().c_str()) ); - fc::asio::tcp::connect( *sock, eps[i] ); - endpt = eps[i]; - resolved = true; - break; - } catch ( fc::error_report& er ) { - fc_ilog( logr, "Failed to connect to ${endpoint}\n${error_reprot}", - ("endpoint",ss.str().c_str())("error_report", er.to_detail_string()) ); - sock->close(); - } - } - if( !resolved ) { - FC_THROW_REPORT( "Unable to connect to any resolved endpoint for ${host}:${port}", - fc::value().set("host", hostname).set("port",port) ); - } - session = libssh2_session_init(); - libssh2_trace( session, _trace_level ); - libssh2_trace_sethandler( session, this, client_impl::handle_trace ); - - *libssh2_session_abstract(session) = this; - - libssh2_session_set_blocking( session, 0 ); - int ec = libssh2_session_handshake( session, sock->native() ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_session_handshake( session, sock->native() ); - } - if( ec < 0 ) { - char* msg; - libssh2_session_last_error( session, &msg, 0, 0 ); - FC_THROW_REPORT( "SSH Handshake error: ${code} - ${message}", - fc::value().set("code",ec).set("message", msg) ); - } - const char* fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); - //slog( "fingerprint: %s", fingerprint ); - - // try to authenticate, throw on error. - authenticate(); - //slog("."); - } catch ( error_report& er ) { - elog( "%s", er.to_detail_string().c_str() ); - close(); - throw FC_REPORT_PUSH( er, "Unable to connect to ssh server" );; - } catch ( ... ) { - close(); - FC_THROW_REPORT( "Unable to connect to ssh server", fc::value().set("exception", fc::except_str() ) ); - } - } - - static void handle_trace( LIBSSH2_SESSION* session, void* context, const char* data, size_t length ) { - client_impl* my = (client_impl*)context; - fc::string str(data,length); - fc_wlog( my->logr, "${message}", ("message",str) ); - } - - - void close() { - if( session ) { - if( sftp ) { - int ec = libssh2_sftp_shutdown(sftp); - try { - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_sftp_shutdown(sftp); - } - }catch(...){ - fc_wlog( logr, "caught closing sftp session" ); - } - sftp = 0; - } - try { - int ec = libssh2_session_disconnect(session, "exit cleanly" ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_session_disconnect(session, "exit cleanly" ); - } - ec = libssh2_session_free(session); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_session_free(session ); - } - session = 0; - } catch ( ... ){ - fc_wlog( logr, "caught freeing session" ); - session = 0; - } - try { - if( sock ) { - sock->close(); - } - } catch ( ... ){ - fc_wlog( logr, "caught error closing socket" ); - } - sock.reset(0); - try { - if( read_prom ) read_prom->wait(); - } catch ( ... ){ - fc_wlog( logr, "caught error waiting on read" ); - } - try { - if( write_prom ) write_prom->wait(); - } catch ( ... ){ - fc_wlog( logr, "caught error waiting on write" ); - } - } - } - - void authenticate() { - try { - char * alist = libssh2_userauth_list(session, uname.c_str(),uname.size()); - char * msg = 0; - int ec = 0; - - if(alist==NULL){ - if(libssh2_userauth_authenticated(session)){ - return; // CONNECTED! - } - ec = libssh2_session_last_error(session,&msg,NULL,0); - - while( !alist && (ec == LIBSSH2_ERROR_EAGAIN) ) { - wait_on_socket(); - alist = libssh2_userauth_list(session, uname.c_str(), uname.size()); - ec = libssh2_session_last_error(session,&msg,NULL,0); - } - if( !alist ) { - FC_THROW_REPORT( "Error getting authorization list: ${code} - ${message}", - fc::value().set("code",ec).set("message",msg)); - } - } - - std::vector split_alist; - bool pubkey = false; - bool pass = false; - bool keybd = false; - boost::split( split_alist, alist, boost::is_any_of(",") ); - std::for_each( split_alist.begin(), split_alist.end(), [&](const std::string& s){ - if( s == "publickey" ) { - pubkey = true; - } - else if( s == "password" ) { - pass = true; - } - else if( s == "keyboard-interactive" ) { - keybd = true; - } - else { - fc_wlog( logr, "Unknown/unsupported authentication type '${auth_type}'", ("auth_type",s.c_str())); - } - }); - - if( pubkey ) { - if( try_pub_key() ) - return; - } - if( pass ) { - if( try_pass() ) - return; - } - if( keybd ) { - if( try_keyboard() ) - return; - } - } catch ( error_report er ) { - throw FC_REPORT_PUSH( er, "Unable to authenticate ssh connection" ); - } - FC_THROW_REPORT( "Unable to authenticate ssh connection" ); - } // authenticate() - - bool try_pass() { - int ec = libssh2_userauth_password(session, uname.c_str(), upass.c_str() ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_userauth_password(session, uname.c_str(), upass.c_str() ); - } - - return !ec; - } - bool try_keyboard() { - int ec = libssh2_userauth_keyboard_interactive(session, uname.c_str(), - &client_impl::kbd_callback); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_userauth_keyboard_interactive(session, uname.c_str(), - &client_impl::kbd_callback); - } - return !ec; - } - - bool try_pub_key() { - int ec = libssh2_userauth_publickey_fromfile(session, - uname.c_str(), - pubkey.c_str(), - privkey.c_str(), - passphrase.c_str() ); - - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_userauth_publickey_fromfile(session, - uname.c_str(), - pubkey.c_str(), - privkey.c_str(), - passphrase.c_str() ); - } - return !ec; - } - - /** - * @todo figure out why this method results in deadlocks... - */ - void wait_on_socket() { - fc::usleep(fc::microseconds(5000)); - return; - - auto dir = libssh2_session_block_directions(session); - if( !dir ) return; - - fc::promise::ptr rprom, wprom; - if( dir & LIBSSH2_SESSION_BLOCK_INBOUND ) { - fc::scoped_lock lock(this->_spin_lock); - if( !read_prom ) { - read_prom.reset( new fc::promise("read_prom") ); - sock->async_read_some( boost::asio::null_buffers(), - [=]( const boost::system::error_code& e, size_t ) { - fc::scoped_lock lock(this->_spin_lock); - this->read_prom->set_value(e); - this->read_prom.reset(nullptr); - } ); - } - rprom = read_prom; - } - - if( dir & LIBSSH2_SESSION_BLOCK_OUTBOUND ) { - fc::scoped_lock lock(this->_spin_lock); - if( !write_prom ) { - write_prom.reset( new fc::promise("write_prom") ); - sock->async_write_some( boost::asio::null_buffers(), - [=]( const boost::system::error_code& e, size_t ) { - this->write_prom->set_value(e); - this->write_prom.reset(0); - } ); - } - wprom = write_prom; - } - - - boost::system::error_code ec; - if( rprom.get() && wprom.get() ) { - wlog( "wait both dir" ); - typedef fc::future fprom; - fprom fw(wprom); - fprom fr(rprom); - int r = fc::wait_any( fw, fr, fc::seconds(1) ); - switch( r ) { - case 0: - if( wprom->wait() ) { - FC_THROW_REPORT( "Socket Error ${message}", - fc::value().set( "message", boost::system::system_error(rprom->wait() ).what() ) ); - } - break; - case 1: - if( rprom->wait() ) { - FC_THROW_REPORT( "Socket Error ${message}", - fc::value().set( "message", boost::system::system_error(rprom->wait() ).what() ) ); - } - break; - } - } else if( rprom ) { - if( rprom->wait() ) { - FC_THROW_REPORT( "Socket Error ${message}", - fc::value().set( "message", boost::system::system_error(rprom->wait() ).what() ) ); - } - } else if( wprom ) { - if( wprom->wait() ) { - FC_THROW_REPORT( "Socket Error ${message}", - fc::value().set( "message", boost::system::system_error(wprom->wait() ).what() ) ); - } - } - } - - void init_sftp() { - if( !sftp ) { - sftp = libssh2_sftp_init(session); - while( !sftp ) { - char* msg = 0; - int ec = libssh2_session_last_error(session,&msg,NULL,0); - if( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - sftp = libssh2_sftp_init(session); - } else { - FC_THROW_REPORT( "init sftp error ${code} - ${message}", fc::value().set("code",ec).set("message",msg) ); - } - } - } - } - - int _trace_level; - logger logr; - }; - - } - - client::client():my( new detail::client_impl() ){} - client::~client(){} - - void client::set_trace_level( int bitmask ) { my->_trace_level = bitmask; } - int client::get_trace_level()const { return my->_trace_level; } - const logger& client::get_logger()const { return my->logr; } - void client::set_logger( const logger& l ) { my->logr = l; } - - void client::connect( const fc::string& user, const fc::string& host, uint16_t port ) { - my->hostname = host; - my->uname = user; - my->port = port; - my->connect(); - } - void client::connect( const fc::string& user, const fc::string& pass, - const fc::string& host, uint16_t port ) { - my->hostname = host; - my->uname = user; - my->upass = pass; - my->port = port; - - my->connect(); - } - - - ssh::process client::exec( const fc::string& cmd, const fc::string& pty_type ) { - return ssh::process( *this, cmd, pty_type ); - } - - /** - * @todo implement progress reporting. - */ - void client::scp_send_dir( const fc::path& local_dir, const fc::path& remote_path, - std::function progress ) - { - fc::path remote_dir = remote_path; - if( remote_dir.filename() == fc::path(".") ) - remote_dir /= local_dir.filename(); - - fc_dlog( my->logr, "scp -r ${local} ${remote}", ("local",local_dir)("remote",remote_dir) ); - create_directories( remote_dir ); - - directory_iterator ditr(local_dir); - directory_iterator dend; - - while( ditr != dend ) { - if( (*ditr).filename() == "." || - (*ditr).filename() == ".." ) - { } - else if( fc::is_directory(*ditr) ) - { - scp_send_dir( (*ditr), remote_dir / (*ditr).filename() ); - } else if( fc::is_regular_file(*ditr) ) { - scp_send( *ditr, remote_dir / (*ditr).filename() ); - } else { - fc_wlog( my->logr, "Skipping '${path}", ("path",fc::canonical(*ditr)) ); - } - ++ditr; - } - } - - void client::scp_send( const fc::path& local_path, const fc::path& remote_path, - std::function progress ) { - fc_ilog( my->logr, "scp ${local} ${remote}", ("local",local_path)("remote",remote_path ) ); - /** - * Tests have shown that if one scp is 'blocked' by a need to read (presumably to - * ack recv for the trx window), and then a second transfer begins that the first - * transfer will never be acked. Placing this mutex limits the transfer of - * one file at a time via SCP which is just as well because there is a fixed - * amount of bandwidth. - */ - fc::unique_lock lock(my->scp_send_mutex); - - -// using namespace boost::filesystem; - if( !fc::exists(local_path) ) { - FC_THROW_REPORT( "Source file '${file}' does not exist", fc::value().set("file",local_path) ) ; - } - if( is_directory( local_path ) ) { - FC_THROW_REPORT( "Source file '${file}' is a directory, expected a file", fc::value().set("file",local_path) ) ; - } - - // memory map the file - size_t fsize = file_size(local_path); - if( fsize == 0 ) { - elog( "file size %d", fsize ); - // TODO: handle empty file case - if( progress ) progress(0,0); - return; - } - file_mapping fmap( local_path.string().c_str(), read_only ); - mapped_region mr( fmap, fc::read_only, 0, fsize ); - - LIBSSH2_CHANNEL* chan = 0; - time_t now; - memset( &now, 0, sizeof(now) ); - - // TODO: preserve creation / modification date - // TODO: perserve permissions / exec bit? - chan = libssh2_scp_send64( my->session, remote_path.generic_string().c_str(), 0700, fsize, now, now ); - while( chan == 0 ) { - char* msg; - int ec = libssh2_session_last_error( my->session, &msg, 0, 0 ); - if( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - chan = libssh2_scp_send64( my->session, local_path.generic_string().c_str(), 0700, fsize, now, now ); - } else { - FC_THROW_REPORT( "scp ${local_file} to ${remote_file} failed ${code} - ${message}", - fc::value().set("local_file", local_path).set("remote_file",remote_path).set("code",ec).set("message",msg) ); - } - } - try { - size_t wrote = 0; - char* pos = reinterpret_cast(mr.get_address()); - while( progress( wrote, fsize ) && wrote < fsize ) { - int r = libssh2_channel_write( chan, pos, fsize - wrote ); - while( r == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - r = libssh2_channel_write( chan, pos, fsize - wrote ); - } - if( r < 0 ) { - char* msg = 0; - int ec = libssh2_session_last_error( my->session, &msg, 0, 0 ); - FC_THROW_REPORT( "scp failed ${code} - ${message}", - fc::value().set("code",ec).set("message",msg) ); - } - wrote += r; - pos += r; - } - - auto ec = libssh2_channel_send_eof( chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_channel_send_eof( chan ); - } - ec = libssh2_channel_wait_eof( chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_channel_wait_eof( chan ); - } - - ec = libssh2_channel_close( chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_channel_close( chan ); - } - } catch ( error_report& er ) { - // clean up chan - int ec = libssh2_channel_free(chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_channel_free( chan ); - } - throw er; - } - int ec = libssh2_channel_free( chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_channel_free( chan ); - } - if( ec < 0 ) { - char* msg = 0; - int ec = libssh2_session_last_error( my->session, &msg, 0, 0 ); - FC_THROW_REPORT( "scp failed ${code} - ${message}", - fc::value().set("code",ec).set("message",msg) ); - } - - } - - - void client::rm( const fc::path& remote_path ) { - try { - auto s = stat(remote_path); - if( s.is_directory() ) { - FC_THROW_REPORT( "sftp cannot remove directory ${path}", fc::value().set("path",remote_path) ); - } - else if( !s.exists() ) { - return; // nothing to do - } - - int rc = libssh2_sftp_unlink(my->sftp, remote_path.generic_string().c_str() ); - while( rc == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - rc = libssh2_sftp_unlink(my->sftp, remote_path.generic_string().c_str() ); - } - if( 0 != rc ) { - rc = libssh2_sftp_last_error(my->sftp); - FC_THROW_REPORT( "sftp rm failed ${code}", fc::value().set("code",rc) ); - } - } catch ( error_report& er ) { - throw FC_REPORT_PUSH( er, "sftp remove '${remote_path}' failed", fc::value().set("remote_path",remote_path) ); - } - } - - file_attrib client::stat( const fc::path& remote_path ){ - my->init_sftp(); - LIBSSH2_SFTP_ATTRIBUTES att; - int ec = libssh2_sftp_stat( my->sftp, remote_path.generic_string().c_str(), &att ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_sftp_stat( my->sftp, remote_path.generic_string().c_str(), &att ); - } - if( ec ) { - return file_attrib(); - } - file_attrib ft; - ft.size = att.filesize; - ft.permissions = att.permissions; - return ft; - } - void client::create_directories( const fc::path& rdir, int mode ) { - boost::filesystem::path dir = rdir; - boost::filesystem::path p; - auto pitr = dir.begin(); - while( pitr != dir.end() ) { - p /= *pitr; - if( !stat( p ).exists() ) { - mkdir(p,mode); - } - ++pitr; - } - } - - void client::mkdir( const fc::path& rdir, int mode ) { - try { - auto s = stat(rdir); - if( s.is_directory() ) return; - else if( s.exists() ) { - FC_THROW_REPORT( "File already exists at path ${path}", fc::value().set("path",rdir) ); - } - - int rc = libssh2_sftp_mkdir(my->sftp, rdir.generic_string().c_str(), mode ); - while( rc == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - rc = libssh2_sftp_mkdir(my->sftp, rdir.generic_string().c_str(), mode ); - } - if( 0 != rc ) { - rc = libssh2_sftp_last_error(my->sftp); - FC_THROW_REPORT( "sftp mkdir error", fc::value().set("code",rc) ); - } - } catch ( error_report& er ) { - throw FC_REPORT_PUSH( er, "sftp failed to create directory '${directory}'", fc::value().set( "directory", rdir ) ); - } - } - - void client::close() { - if( my->session ) { - if( my->sftp ) { - int ec = libssh2_sftp_shutdown(my->sftp); - try { - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_sftp_shutdown(my->sftp); - } - }catch(...){ - fc_wlog( my->logr, "caught closing sftp sessionn" ); - } - my->sftp = 0; - } - try { - int ec = libssh2_session_disconnect(my->session, "exit cleanly" ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_session_disconnect(my->session, "exit cleanly" ); - } - ec = libssh2_session_free(my->session); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->wait_on_socket(); - ec = libssh2_session_free(my->session ); - } - my->session = 0; - } catch ( ... ){ - fc_wlog( my->logr, "caught error freeing session" ); - my->session = 0; - } - try { - if( my->sock ) { - my->sock->close(); - } - } catch ( ... ){ - fc_wlog( my->logr, "caught error closing socket" ); - } - my->sock.reset(0); - try { - if( my->read_prom ) my->read_prom->wait(); - } catch ( ... ){ - fc_wlog( my->logr,"caught error waiting on read prom" ); - } - try { - if( my->write_prom ) my->write_prom->wait(); - } catch ( ... ){ - fc_wlog( my->logr, "caught error waiting on write prom" ); - } - } - } - - file_attrib::file_attrib() - :size(0),uid(0),gid(0),permissions(0),atime(0),mtime(0) - { } - - bool file_attrib::is_directory() { - return LIBSSH2_SFTP_S_ISDIR(permissions); - } - bool file_attrib::is_file() { - return LIBSSH2_SFTP_S_ISREG(permissions); - } - bool file_attrib::exists() { - return 0 != permissions; - } - - - - - process::process() - {} - process::~process() - {} - bool process::valid()const { - return !!my; - } - - /** - * Blocks until the result code of the process has been returned. - */ - int process::result() { - char* msg = 0; - - int ec = libssh2_channel_wait_eof( my->chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->sshc.my->wait_on_socket(); - ec = libssh2_channel_wait_eof( my->chan ); - } - - ec = libssh2_channel_wait_closed( my->chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - my->sshc.my->wait_on_socket(); - ec = libssh2_channel_wait_closed( my->chan ); - } - ec = libssh2_session_last_error( my->sshc.my->session, &msg, 0, 0 ); - if( !ec ) { - FC_THROW_REPORT( "Error waiting on socket to close: ${message}", fc::value().set("message",msg) );//%ec %msg ); - } - return libssh2_channel_get_exit_status( my->chan ); - } - /** - * @brief returns a stream that writes to the procss' stdin - */ - fc::ostream& process::in_stream()const { - return my->std_in; - } - /** - * @brief returns a stream that reads from the process' stdout - */ - fc::istream& process::out_stream()const { - return my->std_out; - } - /** - * @brief returns a stream that reads from the process' stderr - */ - fc::istream& process::err_stream()const { - return my->std_err; - } - - process::process( const process& p ) - :my(p.my){ } - process::process( process&& p ) - :my(fc::move(p.my)){ } - - process::process( client& c, const fc::string& cmd, const fc::string& pty_type) - :my( new detail::process_impl( c, cmd, pty_type ) ) - { - } - - - void detail::process_impl::flush() { - if( !chan ) return; - /* channel_flush deleates input buffer, and does not ensure writes go out - * - int ec = libssh2_channel_flush_ex( chan, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - sshc.my->wait_on_socket(); - ec = libssh2_channel_flush_ex( chan, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA ); - } - ec = libssh2_channel_flush( chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - sshc.my->wait_on_socket(); - ec = libssh2_channel_flush( chan ); - } - if( ec < 0 ) { - FC_THROW_REPORT( "ssh flush failed", fc::value().set( "channel_error", ec) ); - } - */ - } - int detail::process_impl::read_some( char* data, size_t len, int stream_id ){ - if( !sshc.my->session ) { FC_THROW_REPORT( "Session closed" ); } - - int rc; - char* buf = data; - size_t buflen = len; - do { - rc = libssh2_channel_read_ex( chan, stream_id, buf, buflen ); - if( rc > 0 ) { - buf += rc; - buflen -= rc; - return buf-data; - } else if( rc == 0 ) { - if( libssh2_channel_eof( chan ) ) { - return -1; // eof - } - sshc.my->wait_on_socket(); - } else { - if( rc == LIBSSH2_ERROR_EAGAIN ) { - if( 0 < (buf-data) ) { - return buf-data; - } - else { - sshc.my->wait_on_socket(); - rc = 0; - continue; - } - } else { - char* msg; - if( !sshc.my || !sshc.my->session ) { FC_THROW_REPORT( "Session closed" ); } - rc = libssh2_session_last_error( sshc.my->session, &msg, 0, 0 ); - FC_THROW_REPORT( "read failed: ${message}", fc::value().set("message", msg).set("code", rc) ); - } - } - } while( rc >= 0 && buflen); - return buf-data; - } - int detail::process_impl::write_some( const char* data, size_t len, int stream_id ) { - if( !sshc.my->session ) { FC_THROW_REPORT( "Session closed" ); } - - int rc; - const char* buf = data; - size_t buflen = len; - do { - rc = libssh2_channel_write_ex( chan, stream_id, buf, buflen ); - if( rc > 0 ) { - buf += rc; - buflen -= rc; - return buf-data; - } else if( rc == 0 ) { - if( libssh2_channel_eof( chan ) ) { - FC_THROW_REPORT( "EOF" ); - //return -1; // eof - } - } else { - - if( rc == LIBSSH2_ERROR_EAGAIN ) { - if( 0 < (buf-data) ) { - return buf-data; - } - else { - sshc.my->wait_on_socket(); - rc = 0; - continue; - } - } else { - char* msg; - rc = libssh2_session_last_error( sshc.my->session, &msg, 0, 0 ); - FC_THROW_REPORT( "write failed: ${message}", fc::value().set("message",msg).set("code",rc) ); - return buf-data; - } - } - } while( rc >= 0 && buflen); - return buf-data; - } - void detail::process_impl::send_eof() { - if( sshc.my->session ) { - int ec = libssh2_channel_send_eof( chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - sshc.my->wait_on_socket(); - ec = libssh2_channel_send_eof( chan ); - } - if( ec ) { - char* msg = 0; - ec = libssh2_session_last_error( sshc.my->session, &msg, 0, 0 ); - FC_THROW_REPORT( "send eof failed: ${message}", fc::value().set("message",msg).set("code",ec) ); - } - } - } - - size_t detail::process_istream::readsome( char* buf, size_t len ) { - return proc.read_some( buf, len, chan ); - } - istream& detail::process_istream::read( char* buf, size_t len ) { - size_t r = 0; - do { - r += proc.read_some( buf + r, len - r, chan ); - } while( r < len ); - - return *this; - } - - bool detail::process_istream::eof()const { - return 0 != libssh2_channel_eof( proc.chan ); - } - - ostream& detail::process_ostream::write( const char* buf, size_t len ) { - size_t wrote = 0; - do { - wrote += proc.write_some( buf+wrote, len-wrote, 0 ); - } while( wrote < len ); - return *this; - } - void detail::process_ostream::close(){ - proc.send_eof(); - } - void detail::process_ostream::flush(){ - proc.flush(); - } - detail::process_impl::process_impl( const client& c, const fc::string& cmd, const fc::string& pty_type ) - :sshc(c),std_err(*this,SSH_EXTENDED_DATA_STDERR),std_out(*this,0),std_in(*this) - { - chan = c.my->open_channel(pty_type); - - int ec = libssh2_channel_handle_extended_data2(chan, LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - sshc.my->wait_on_socket(); - ec = libssh2_channel_handle_extended_data2(chan, LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL ); - } - - if( cmd.size() == 0 ) { - ec = libssh2_channel_shell(chan ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - sshc.my->wait_on_socket(); - ec = libssh2_channel_shell(chan); - } - } else { - ec = libssh2_channel_exec( chan, cmd.c_str() ); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - sshc.my->wait_on_socket(); - ec = libssh2_channel_exec( chan, cmd.c_str() ); - } - } - if( ec ) { - elog( "error starting process" ); - char* msg = 0; - ec = libssh2_session_last_error( sshc.my->session, &msg, 0, 0 ); - FC_THROW_REPORT( "exec failed: ${message}", fc::value().set("message",msg).set("code",ec) ); - } - } - LIBSSH2_CHANNEL* detail::client_impl::open_channel( const fc::string& pty_type ) { - LIBSSH2_CHANNEL* chan = 0; - chan = libssh2_channel_open_session(session); - if( !chan ) { - char* msg; - int ec = libssh2_session_last_error( session, &msg, 0, 0 ); - while( !chan && ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - chan = libssh2_channel_open_session(session); - ec = libssh2_session_last_error( session, &msg, 0, 0 ); - } - if( !chan ) { - FC_THROW_REPORT( "libssh2_channel_open_session failed: ${message}", fc::value().set("message",msg).set("code",ec) ); - } - } - - if( pty_type.size() ) { - int ec = libssh2_channel_request_pty(chan,pty_type.c_str()); - while( ec == LIBSSH2_ERROR_EAGAIN ) { - wait_on_socket(); - ec = libssh2_channel_request_pty(chan,pty_type.c_str()); - } - if( 0 != ec ) { - char* msg; - ec = libssh2_session_last_error( session, &msg, 0, 0 ); - FC_THROW_REPORT( "libssh2_channel_req_pty failed: ${message}", fc::value().set("message",msg).set("code",ec) ); - } - } - return chan; - } - - process& process::operator=( const process& p ) { - my = p.my; - return *this; - } -} } diff --git a/src/ssh/client.cpp b/src/ssh/client.cpp new file mode 100644 index 000000000..80d878807 --- /dev/null +++ b/src/ssh/client.cpp @@ -0,0 +1,717 @@ +#define NOMINMAX // prevent windows from defining min and max macros +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client_impl.hpp" + +namespace fc { namespace ssh { + + namespace detail { + static int ssh_init = libssh2_init(0); + } + + client::client():my( new detail::client_impl() ){ (void)detail::ssh_init; /* fix unused warning...*/ } + client::~client() { my->close(); } + + void client::set_trace_level( int bitmask ) { my->_trace_level = bitmask; } + int client::get_trace_level()const { return my->_trace_level; } + const logger& client::get_logger()const { return my->logr; } + void client::set_logger( const logger& l ) { my->logr = l; } + + void client::connect( const fc::string& user, const fc::string& host, uint16_t port ) { + my->hostname = host; + my->uname = user; + my->port = port; + my->connect(); + } + void client::connect( const fc::string& user, const fc::string& pass, + const fc::string& host, uint16_t port ) { + my->hostname = host; + my->uname = user; + my->upass = pass; + my->port = port; + + my->connect(); + } + + void client::close() { my->close(); } + + +// ssh::process client::exec( const fc::string& cmd, const fc::string& pty_type ) { +// return ssh::process( *this, cmd, pty_type ); +// } + + /** + * @todo implement progress reporting. + */ + void client::scp_send_dir( const fc::path& local_dir, const fc::path& remote_path, + std::function progress ) + { + fc::path remote_dir = remote_path; + if( remote_dir.filename() == fc::path(".") ) + remote_dir /= local_dir.filename(); + + fc_dlog( my->logr, "scp -r ${local} ${remote}", ("local",local_dir)("remote",remote_dir) ); + create_directories( remote_dir ); + + directory_iterator ditr(local_dir); + directory_iterator dend; + + while( ditr != dend ) { + if( (*ditr).filename() == "." || + (*ditr).filename() == ".." ) + { } + else if( fc::is_directory(*ditr) ) + { + scp_send_dir( (*ditr), remote_dir / (*ditr).filename() ); + } else if( fc::is_regular_file(*ditr) ) { + scp_send( *ditr, remote_dir / (*ditr).filename() ); + } else { + fc_wlog( my->logr, "Skipping '${path}", ("path",fc::canonical(*ditr)) ); + } + ++ditr; + } + } + + void client::scp_send( const fc::path& local_path, const fc::path& remote_path, + std::function progress ) { + fc_wlog( my->logr, "scp ${local} ${remote}", ("local",local_path)("remote",remote_path ) ); + if( !fc::exists(local_path) ) { + FC_THROW( "Source file '${file}' does not exist", ("file",local_path) ) ; + } + if( is_directory( local_path ) ) { + FC_THROW( "Source file '${file}' is a directory, expected a file", ("file",local_path) ) ; + } + + // memory map the file + uint64_t fsize = file_size(local_path); + if( fsize == 0 ) { + elog( "file size ${file_size}", ("file_size", fsize) ); + // TODO: handle empty file case + if( progress ) progress(0,0); + return; + } + file_mapping fmap( local_path.string().c_str(), read_only ); + + LIBSSH2_CHANNEL* chan = 0; + time_t now; + memset( &now, 0, sizeof(now) ); + + // TODO: preserve creation / modification date + // TODO: perserve permissions / exec bit? + try { + // libssh2_scp_send64 stores state data in the session object, and it calls channel_open which + // stores its own state data, so lock both. + fc::scoped_lock channel_open_lock(my->channel_open_mutex); + fc::scoped_lock scp_send_lock(my->scp_send_mutex); + chan = my->call_ssh2_ptr_function_throw(boost::bind(libssh2_scp_send64, my->session, remote_path.generic_string().c_str(), 0700, fsize, now, now )); + } catch (fc::exception& er) { + FC_RETHROW_EXCEPTION(er, error, "scp ${local_file} to ${remote_file} failed", ("local_file", local_path)("remote_file",remote_path)); + } + uint64_t total_bytes_written = 0; + try { + const size_t max_mapping_size = 1024*1024*1024; // 1GB + for (uint64_t current_offset = 0; current_offset < fsize; current_offset += max_mapping_size) { + uint64_t total_bytes_left_to_send = fsize - current_offset; + size_t bytes_to_send_this_iteration = (size_t)std::min(total_bytes_left_to_send, max_mapping_size); + mapped_region mr( fmap, fc::read_only, current_offset, bytes_to_send_this_iteration); + size_t bytes_written_this_iteration = 0; + char* pos = reinterpret_cast(mr.get_address()); + while( progress(total_bytes_written, fsize) && bytes_written_this_iteration < bytes_to_send_this_iteration) { + int r = my->call_ssh2_function_throw(boost::bind(libssh2_channel_write_ex, chan, 0, pos, + bytes_to_send_this_iteration - bytes_written_this_iteration), + "scp failed ${code} - ${message}"); + bytes_written_this_iteration += r; + total_bytes_written += r; + pos += r; + // fc_wlog( my->logr, "wrote ${bytes} bytes", ("bytes",r) ); + } + } + my->call_ssh2_function(boost::bind(libssh2_channel_send_eof, chan)); + my->call_ssh2_function(boost::bind(libssh2_channel_wait_eof, chan)); + my->call_ssh2_function(boost::bind(libssh2_channel_close, chan)); + } catch ( fc::exception& er ) { + // clean up chan + my->call_ssh2_function(boost::bind(libssh2_channel_free, chan)); + throw er; + } + my->call_ssh2_function_throw(boost::bind(libssh2_channel_free, chan), + "scp failed ${code} - ${message}"); + } + + + void client::rm( const fc::path& remote_path ) { + try { + auto s = stat(remote_path); + if( s.is_directory() ) { + FC_THROW( "sftp cannot remove directory ${path}", ("path",remote_path) ); + } + else if( !s.exists() ) { + return; // nothing to do + } + + { + fc::scoped_lock scp_unlink_lock(my->scp_unlink_mutex); + my->call_ssh2_function_throw(boost::bind(libssh2_sftp_unlink_ex, my->sftp, remote_path.generic_string().c_str(), remote_path.generic_string().size()), + "sftp rm failed ${code}"); + } + } catch ( fc::exception& er ) { + FC_RETHROW_EXCEPTION( er, error, "sftp remove '${remote_path}' failed", ("remote_path",remote_path) ); + } + } + + void client::rmdir( const fc::path& remote_path ) { + try { + auto s = stat(remote_path); + if( !s.is_directory() ) + FC_THROW( "sftp cannot rmdir non-directory ${path}", ("path",remote_path) ); + else if( !s.exists() ) + return; // nothing to do + + { + fc::scoped_lock scp_rmdir_lock(my->scp_rmdir_mutex); + my->call_ssh2_function_throw(boost::bind(libssh2_sftp_rmdir_ex, my->sftp, remote_path.generic_string().c_str(), remote_path.generic_string().size()), + "sftp rmdir failed ${code}"); + } + } catch ( fc::exception& er ) { + FC_RETHROW_EXCEPTION( er, error, "sftp rmdir '${remote_path}' failed", ("remote_path",remote_path) ); + } + } + + void client::rmdir_recursive( const fc::path& remote_path ) { + try { + auto s = stat(remote_path); + if( !s.is_directory() ) + FC_THROW( "sftp cannot rmdir non-directory ${path}", ("path",remote_path) ); + else if( !s.exists() ) + return; // nothing to do + + LIBSSH2_SFTP_HANDLE *dir_handle; + { + fc::scoped_lock scp_open_lock(my->scp_open_mutex); + dir_handle = + my->call_ssh2_ptr_function_throw(boost::bind(libssh2_sftp_open_ex, my->sftp, remote_path.generic_string().c_str(), remote_path.generic_string().size(), 0, 0, LIBSSH2_SFTP_OPENDIR), + "sftp libssh2_sftp_opendir failed ${code}"); + } + do { + char mem[512]; + LIBSSH2_SFTP_ATTRIBUTES attrs; + + int rc; + { + fc::scoped_lock scp_readdir_lock(my->scp_readdir_mutex); + rc = my->call_ssh2_function_throw(boost::bind(libssh2_sftp_readdir_ex, dir_handle, mem, sizeof(mem), (char*)NULL, 0, &attrs), + "sftp readdir failed ${code}"); + } + if (rc > 0) { + fc::string file_or_dir_name(mem, rc); + if (file_or_dir_name == "." || file_or_dir_name == "..") + continue; + fc::path full_remote_path = remote_path / file_or_dir_name; + if (LIBSSH2_SFTP_S_ISDIR(attrs.permissions)) + rmdir_recursive(full_remote_path); + else if (LIBSSH2_SFTP_S_ISREG(attrs.permissions)) { + fc::scoped_lock scp_unlink_lock(my->scp_unlink_mutex); + my->call_ssh2_function_throw(boost::bind(libssh2_sftp_unlink_ex, my->sftp, full_remote_path.generic_string().c_str(), full_remote_path.generic_string().size()), + "sftp rm failed ${code}"); + } + } else + break; + } while (1); + + { + fc::scoped_lock scp_close_lock(my->scp_close_mutex); + my->call_ssh2_function_throw(boost::bind(libssh2_sftp_close_handle, dir_handle), "sftp libssh2_sftp_closedir failed ${code}"); + } + { + fc::scoped_lock scp_rmdir_lock(my->scp_rmdir_mutex); + my->call_ssh2_function_throw(boost::bind(libssh2_sftp_rmdir_ex, my->sftp, remote_path.generic_string().c_str(), remote_path.generic_string().size()), + "sftp rmdir failed ${code}"); + } + } catch ( fc::exception& er ) { + FC_RETHROW_EXCEPTION( er, error, "sftp rmdir recursive '${remote_path}' failed", ("remote_path",remote_path) ); + } + } + + file_attrib client::stat( const fc::path& remote_path ){ + my->init_sftp(); + LIBSSH2_SFTP_ATTRIBUTES att; + int ec; + { + fc::scoped_lock scp_stat_lock(my->scp_stat_mutex); + ec = my->call_ssh2_function(boost::bind(libssh2_sftp_stat_ex, my->sftp, + remote_path.generic_string().c_str(), remote_path.generic_string().size(), + LIBSSH2_SFTP_STAT, &att)); + } + if( ec ) + return file_attrib(); + file_attrib ft; + ft.size = att.filesize; + ft.permissions = att.permissions; + return ft; + } + void client::create_directories( const fc::path& rdir, int mode ) { + boost::filesystem::path dir = rdir; + boost::filesystem::path p; + auto pitr = dir.begin(); + while( pitr != dir.end() ) { + p /= *pitr; + if( !stat( p ).exists() ) { + mkdir(p,mode); + } + ++pitr; + } + } + + void client::mkdir( const fc::path& rdir, int mode ) { + try { + auto s = stat(rdir); + if( s.is_directory() ) + return; + else if( s.exists() ) + FC_THROW( "File already exists at path ${path}", ("path",rdir) ); + + { + fc::scoped_lock scp_mkdir_lock(my->scp_mkdir_mutex); + my->call_ssh2_function_throw(boost::bind(libssh2_sftp_mkdir_ex, my->sftp, + rdir.generic_string().c_str(), rdir.generic_string().size(), mode), + "sftp mkdir error"); + } + } catch ( fc::exception& er ) { + FC_RETHROW_EXCEPTION( er, error, "sftp failed to create directory '${directory}'", ( "directory", rdir ) ); + } + } + + void client::set_remote_system_is_windows(bool is_windows /* = true */) { + my->remote_system_is_windows = is_windows; + } + + + file_attrib::file_attrib() + :size(0),uid(0),gid(0),permissions(0),atime(0),mtime(0) + { } + + bool file_attrib::is_directory() { + return LIBSSH2_SFTP_S_ISDIR(permissions); + } + bool file_attrib::is_file() { + return LIBSSH2_SFTP_S_ISREG(permissions); + } + bool file_attrib::exists() { + return 0 != permissions; + } + + detail::client_impl::client_impl() : + session(nullptr), + knownhosts(nullptr), + sftp(nullptr), + agent(nullptr), + _trace_level(0), // was LIBSSH2_TRACE_ERROR + logr(fc::logger::get( "fc::ssh::client" )), + remote_system_is_windows(false) { + logr.set_parent( fc::logger::get( "default" ) ); + } + + detail::client_impl::~client_impl() { + close(); + } + + /* static */ + void detail::client_impl::kbd_callback(const char *name, int name_len, + const char *instruction, int instruction_len, int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract) { + detail::client_impl* self = (client_impl*)*abstract; + + + for (int i = 0; i < num_prompts; i++) { + fwrite(prompts[i].text, 1, prompts[i].length, stdout); + + if( self->upass.size() == 0 ) { + /** TODO: add keyboard callback here... + fgets(buf, sizeof(buf), stdin); + n = strlen(buf); + while (n > 0 && strchr("\r\n", buf[n - 1])) + n--; + buf[n] = 0; + +#ifdef WIN32 // fix warning +# define strdup _strdup +#endif + responses[i].text = strdup(buf); + responses[i].length = n; + */ + responses[i].text = nullptr; + responses[i].length = 0; + } else { + responses[i].text = strdup(self->upass.c_str()); + responses[i].length = self->upass.size(); + } + } + } + + void detail::client_impl::connect() { + try { + if( libssh2_init(0) < 0 ) + FC_THROW( "Unable to init libssh2" ); + + auto eps = fc::asio::tcp::resolve( hostname, boost::lexical_cast(port) ); + if( eps.size() == 0 ) + FC_THROW( "Unable to resolve host '${host}'", ("host",hostname) ); + + sock.reset( new boost::asio::ip::tcp::socket( fc::asio::default_io_service() ) ); + + bool resolved = false; + for( uint32_t i = 0; i < eps.size(); ++i ) { + std::stringstream ss; ss << eps[i]; + try { + boost::system::error_code ec; + fc_ilog( logr, "Attempting to connect to ${endpoint}", ("endpoint",ss.str().c_str()) ); + fc::asio::tcp::connect( *sock, eps[i] ); + endpt = eps[i]; + resolved = true; + break; + } catch ( fc::exception& er ) { + fc_ilog( logr, "Failed to connect to ${endpoint}\n${error_reprot}", + ("endpoint",ss.str().c_str())("error_report", er.to_detail_string()) ); + sock->close(); + } + } + if( !resolved ) + FC_THROW( "Unable to connect to any resolved endpoint for ${host}:${port}", + ("host", hostname).set("port",port) ); + + session = libssh2_session_init(); + libssh2_trace( session, _trace_level ); + libssh2_trace_sethandler( session, this, client_impl::handle_trace ); + + *libssh2_session_abstract(session) = this; + + libssh2_session_set_blocking( session, 0 ); + try { + call_ssh2_function_throw(boost::bind(libssh2_session_handshake, session, sock->native()), + "SSH Handshake error: ${code} - ${message}"); + } catch (fc::exception& er) { + FC_RETHROW_EXCEPTION( er, error, "Error during SSH handshake" );; + } + //const char* fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); + //slog( "fingerprint: %s", fingerprint ); + + // try to authenticate, throw on error. + try { + authenticate(); + } catch (fc::exception& er) { + FC_RETHROW_EXCEPTION( er, error, "Error during SSH authentication" );; + } + //slog("."); + } catch ( fc::exception& er ) { + elog( "Unable to connect to ssh server: ${detail}", ("detail", er.to_detail_string().c_str()) ); + close(); + FC_RETHROW_EXCEPTION( er, error, "Unable to connect to ssh server" );; + } catch ( ... ) { + close(); + FC_THROW( "Unable to connect to ssh server", ("exception", fc::except_str() ) ); + } + } + + /* static */ + void detail::client_impl::handle_trace( LIBSSH2_SESSION* session, void* context, const char* data, size_t length ) { + client_impl* my = (client_impl*)context; + fc::string str(data,length); + fc_wlog( my->logr, "${message}", ("message",str) ); + } + + void detail::client_impl::close() { + if( session ) { + if( sftp ) { + try { + call_ssh2_function(boost::bind(libssh2_sftp_shutdown, sftp)); + }catch(...){ + fc_wlog( logr, "caught closing sftp session" ); + } + sftp = 0; + } + + if (agent) { + libssh2_agent_disconnect(agent); + libssh2_agent_free(agent); + agent = nullptr; + } + + try { + call_ssh2_function(boost::bind(libssh2_session_disconnect_ex, session, SSH_DISCONNECT_BY_APPLICATION, "exit cleanly", "")); + call_ssh2_function(boost::bind(libssh2_session_free, session), false); + } catch ( ... ){ + fc_wlog( logr, "caught freeing session" ); + } + session = 0; + try { + if( sock ) + sock->close(); + } catch ( ... ){ + fc_wlog( logr, "caught error closing socket" ); + } + sock.reset(0); + try { + if( read_prom ) + read_prom->wait(); + } catch ( ... ){ + fc_wlog( logr, "caught error waiting on read" ); + } + try { + if( write_prom ) + write_prom->wait(); + } catch ( ... ){ + fc_wlog( logr, "caught error waiting on write" ); + } + } + } + + void detail::client_impl::authenticate() { + try { + char * alist = NULL; + // libssh2_userauth_list has strange enough behavior that we can't use the + // call_blocking_libssh2_function-type functions to wait and retry, so we must + // explicitly lock around the libssh2_userauth_list calls. + // hence, this anonymous scope: + { + fc::unique_lock lock(ssh_session_mutex); + + alist = libssh2_userauth_list(session, uname.c_str(),uname.size()); + + if(alist==NULL) { + char * msg = 0; + int ec = 0; + if(libssh2_userauth_authenticated(session)) + return; // CONNECTED! + ec = libssh2_session_last_error(session,&msg,NULL,0); + + while( !alist && (ec == LIBSSH2_ERROR_EAGAIN) ) { + wait_on_socket(); + alist = libssh2_userauth_list(session, uname.c_str(), uname.size()); + ec = libssh2_session_last_error(session,&msg,NULL,0); + } + if( !alist ) { + FC_THROW( "Error getting authorization list: ${code} - ${message}", + ("code",ec).set("message",msg)); + } + } + } // end anonymous scope + + std::vector split_alist; + bool pubkey = false; + bool pass = false; + bool keybd = false; + boost::split( split_alist, alist, boost::is_any_of(",") ); + std::for_each( split_alist.begin(), split_alist.end(), [&](const std::string& s){ + if( s == "publickey" ) + pubkey = true; + else if( s == "password" ) + pass = true; + else if( s == "keyboard-interactive" ) + keybd = true; + else + fc_dlog( logr, "Unknown/unsupported authentication type '${auth_type}'", ("auth_type",s.c_str())); + }); + + if( pubkey && try_pub_key() ) + return; + if( pass && try_pass() ) + return; + if( keybd && try_keyboard() ) + return; + } catch ( fc::exception& er ) { + FC_RETHROW_EXCEPTION( er, error, "Unable to authenticate ssh connection" ); + } + FC_THROW( "Unable to authenticate ssh connection" ); + } // authenticate() + + bool detail::client_impl::try_pass() { + return !call_ssh2_function(boost::bind(libssh2_userauth_password_ex, session, uname.c_str(), uname.size(), + upass.c_str(), upass.size(), (LIBSSH2_PASSWD_CHANGEREQ_FUNC((*)))NULL)); + } + bool detail::client_impl::try_keyboard() { + return !call_ssh2_function(boost::bind(libssh2_userauth_keyboard_interactive_ex, session, + uname.c_str(), uname.size(), &client_impl::kbd_callback)); + } + bool detail::client_impl::try_pub_key() { + if (privkey.size()) { + if (!call_ssh2_function(boost::bind(libssh2_userauth_publickey_fromfile_ex, + session, + uname.c_str(), uname.size(), + pubkey.c_str(), + privkey.c_str(), + passphrase.c_str()))) + return true; // successful authentication from file + fc_ilog( logr, "failed to authenticate with private key from file '${privkey_filename}'", ("privkey_filename",privkey)); + } else + fc_ilog( logr, "no private key file set, skiping pubkey authorization from file"); + + agent = libssh2_agent_init(session); + if (!agent) { + fc_wlog( logr, "failed to initialize ssh-agent support"); + return false; + } + + if (call_ssh2_function(boost::bind(libssh2_agent_connect, agent))) { + fc_ilog( logr, "failed to connect to ssh-agent"); + return false; + } + + if (call_ssh2_function(boost::bind(libssh2_agent_list_identities, agent))) { + fc_ilog( logr, "failed requesting identities from ssh-agent"); + return false; + } + + struct libssh2_agent_publickey *prev_identity = NULL; + while (1) { + struct libssh2_agent_publickey *identity; + int ec = call_ssh2_function(boost::bind(libssh2_agent_get_identity, agent, &identity, prev_identity)); + if (ec == 1) + break; // done iterating over keys + if (ec < 0) { + fc_ilog( logr, "failed obtaining identity from ssh-agent"); + return false; + } + + if (call_ssh2_function(boost::bind(libssh2_agent_userauth, agent, uname.c_str(), identity))) + fc_ilog( logr, "unable to authenticate with public key '${key_comment}'", ("key_comment",identity->comment)); + else { + fc_ilog( logr, "authenticated with public key '${key_comment}'", ("key_comment",identity->comment)); + return true; + } + prev_identity = identity; + } + return false; + } + + void detail::client_impl::wait_on_socket(int additionalDirections /* = 0 */) { + int dir = libssh2_session_block_directions(session); + dir |= additionalDirections; + if( !dir ) + return; + + fc::promise::ptr rprom, wprom; + if( dir & LIBSSH2_SESSION_BLOCK_INBOUND ) { + fc::scoped_lock lock(this->_spin_lock); + if( !read_prom ) { + read_prom.reset( new fc::promise("read_prom") ); + sock->async_read_some( boost::asio::null_buffers(), + [=]( const boost::system::error_code& e, size_t ) { + fc::scoped_lock lock(this->_spin_lock); + this->read_prom->set_value(e); + this->read_prom.reset(nullptr); + } ); + } + rprom = read_prom; + } + + if( dir & LIBSSH2_SESSION_BLOCK_OUTBOUND ) { + fc::scoped_lock lock(this->_spin_lock); + if( !write_prom ) { + write_prom.reset( new fc::promise("write_prom") ); + sock->async_write_some( boost::asio::null_buffers(), + [=]( const boost::system::error_code& e, size_t ) { + fc::scoped_lock lock(this->_spin_lock); + this->write_prom->set_value(e); + this->write_prom.reset(0); + } ); + } + wprom = write_prom; + } + + boost::system::error_code ec; + if( rprom.get() && wprom.get() ) { + typedef fc::future fprom; + fprom fw(wprom); + fprom fr(rprom); +#if 0 + // EMF: at present there are known bugs in fc::wait_any, and it will fail to wake up + // when one of the futures is ready. + int r = fc::wait_any( fw, fr, fc::seconds(1) ); +#else + int r; + while (1) { + if (fw.ready()) { + r = 0; break; + } + if (fr.ready()) { + r = 1; break; + } + fc::usleep(fc::microseconds(5000)); + } +#endif + switch( r ) { + case 0: + if( wprom->wait() ) { + FC_THROW( "Socket Error ${message}", + ( "message", boost::system::system_error(rprom->wait() ).what() ) ); + } + break; + case 1: + if( rprom->wait() ) { + FC_THROW( "Socket Error ${message}", + ( "message", boost::system::system_error(rprom->wait() ).what() ) ); + } + break; + } + } else if( rprom ) { + if( rprom->wait() ) { + FC_THROW( "Socket Error ${message}", + ( "message", boost::system::system_error(rprom->wait() ).what() ) ); + } + } else if( wprom ) { + if( wprom->wait() ) { + FC_THROW( "Socket Error ${message}", + ( "message", boost::system::system_error(wprom->wait() ).what() ) ); + } + } + } + void detail::client_impl::init_sftp() { + if( !sftp ) + sftp = call_ssh2_ptr_function_throw(boost::bind(libssh2_sftp_init,session), + "init sftp error ${code} - ${message}"); + } + + + LIBSSH2_CHANNEL* detail::client_impl::open_channel( const fc::string& pty_type ) { + LIBSSH2_CHANNEL* chan = 0; + /* anonymous scope */ { + fc::scoped_lock channel_open_lock(channel_open_mutex); + + chan = call_ssh2_ptr_function_throw(boost::bind(libssh2_channel_open_ex, session, + "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, + (const char*)NULL, 0), + "libssh2_channel_open_session failed: ${message}"); + } + + if( pty_type.size() ) + call_ssh2_function_throw(boost::bind(libssh2_channel_request_pty_ex, chan, pty_type.c_str(), pty_type.size(), + (char *)NULL, 0, LIBSSH2_TERM_WIDTH, LIBSSH2_TERM_HEIGHT, + LIBSSH2_TERM_WIDTH_PX, LIBSSH2_TERM_HEIGHT_PX), + "libssh2_channel_req_pty failed: ${message}"); + return chan; + } + +} } diff --git a/src/ssh/client_impl.hpp b/src/ssh/client_impl.hpp new file mode 100644 index 000000000..606bd3648 --- /dev/null +++ b/src/ssh/client_impl.hpp @@ -0,0 +1,280 @@ +#define NOMINMAX +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +// include this to get acess to the details of the LIBSSH2_SESSION structure, so +// we can verify that all data has really been sent when libssh2 says it has. +#include <../src/libssh2_priv.h> + +namespace fc { namespace ssh { + + namespace detail { + + class client_impl { + public: + client_impl(); + ~client_impl(); + + LIBSSH2_SESSION* session; + LIBSSH2_KNOWNHOSTS* knownhosts; + LIBSSH2_SFTP* sftp; + LIBSSH2_AGENT* agent; + + std::unique_ptr sock; + boost::asio::ip::tcp::endpoint endpt; + + fc::mutex ssh_session_mutex; + fc::mutex channel_open_mutex; + fc::mutex process_startup_mutex; + fc::mutex scp_send_mutex; + fc::mutex scp_stat_mutex; + fc::mutex scp_mkdir_mutex; + fc::mutex scp_rmdir_mutex; + fc::mutex scp_unlink_mutex; + fc::mutex scp_close_mutex; + fc::mutex scp_readdir_mutex; + fc::mutex scp_open_mutex; + + fc::string uname; + fc::string upass; + fc::string pubkey; + fc::string privkey; + fc::string passphrase; + fc::string hostname; + uint16_t port; + bool session_connected; + fc::promise::ptr read_prom; + fc::promise::ptr write_prom; + fc::spin_lock _spin_lock; + int _trace_level; + logger logr; + + bool remote_system_is_windows; // true if windows, false if unix, used for command-line quoting and maybe filename translation + + LIBSSH2_CHANNEL* open_channel( const fc::string& pty_type ); + static void kbd_callback(const char *name, int name_len, + const char *instruction, int instruction_len, int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract); + + void connect(); + + static void handle_trace( LIBSSH2_SESSION* session, void* context, const char* data, size_t length ); + + void close(); + void authenticate(); + + bool try_pass(); + bool try_keyboard(); + bool try_pub_key(); + + // don't call this "unlocked" version directly + template + int call_ssh2_function_unlocked(const T& lambda, bool check_for_errors = true); + + // calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN + template + int call_ssh2_function(const T& lambda, bool check_for_errors = true); + + // calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN + // if libssh2 returns an error, get extended info and throw a message with ${code} and ${message} + // set appropriately. + template + int call_ssh2_function_throw(const T& lambda, const char* message = "libssh2 call failed ${code} - ${message}", bool check_for_errors = true); + + // this version is a little different, it handles functions like libssh2_sftp_init which return + // a pointer instead of an int. These retry automatically if the result is NULL and the error + // is LIBSSH2_ERROR_EAGAIN + template + return_type call_ssh2_ptr_function_throw(std::function lambda, const char* message = "libssh2 call failed ${code} - ${message}", bool check_for_errors = true); + + void wait_on_socket(int additionalDirections = 0); + + void init_sftp(); + }; + + + // #define OLD_BLOCKING, + // the OLD_BLOCKING version of these functions will ensure that if a libssh2 function returns + // LIBSSH2_ERROR_EAGAIN, no other libssh2 functions will be called until that function has been + // called again and returned some other value. + // + // if you don't define this and use the new version of this, we will release the lock and let + // other libssh2 functions be called *unless* it appears that there was unwritten data. + // + // the OLD_BLOCKING version is too conservative -- if you try to read on a channel that doesn't + // have any data, you're likely to deadlock. The new version is not heavily tested and may be + // too lax, time will tell. +#ifdef OLD_BLOCKING + // don't call this "unlocked" version directly + template + int client_impl::call_ssh2_function_unlocked(const T& lambda, bool check_for_errors /* = true */) { + int ec = lambda(); + while (ec == LIBSSH2_ERROR_EAGAIN ) { + wait_on_socket(); + ec = lambda(); + } + + // this assert catches bugs in libssh2 if libssh2 returns ec != LIBSSH2_ERROR_EAGAIN + // but the internal session data indicates a data write is still in progress + // set check_for_errors to false when closing the connection + assert(!check_for_errors || !session->packet.olen); + + return ec; + } + + // calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN + template + int client_impl::call_ssh2_function(const T& lambda, bool check_for_errors /* = true */) { + fc::scoped_lock lock(ssh_session_mutex); + return call_ssh2_function_unlocked(lambda, check_for_errors); + } +#else + // calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN + template + int client_impl::call_ssh2_function(const T& lambda, bool check_for_errors /* = true */) { + fc::unique_lock lock(ssh_session_mutex); + int ec = lambda(); + while (ec == LIBSSH2_ERROR_EAGAIN) { + bool unlock_to_wait = !session->packet.olen; + if (unlock_to_wait) + lock.unlock(); + wait_on_socket(); + if (unlock_to_wait) + lock.lock(); + ec = lambda(); + } + // this assert catches bugs in libssh2 if libssh2 returns ec != LIBSSH2_ERROR_EAGAIN + // but the internal session data indicates a data write is still in progress + // set check_for_errors to false when closing the connection + assert(!check_for_errors || !session->packet.olen); + return ec; + } +#endif + +#ifdef OLD_BLOCKING + // calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN + // if libssh2 returns an error, get extended info and throw a message with ${code} and ${message} + // set appropriately. + template + int client_impl::call_ssh2_function_throw(const T& lambda, const char* message /* = "libssh2 call failed ${code} - ${message}" */, bool check_for_errors /* = true */) { + fc::scoped_lock lock(ssh_session_mutex); + int ec = call_ssh2_function_unlocked(lambda, check_for_errors); + + if (ec == LIBSSH2_ERROR_SFTP_PROTOCOL && sftp) { + ec = libssh2_sftp_last_error(sftp); + FC_THROW(message, ("code", ec).set("message", "SFTP protocol error")); + } else if( ec < 0 ) { + char* msg = 0; + ec = libssh2_session_last_error( session, &msg, 0, 0 ); + FC_THROW(message, ("code",ec).set("message",msg)); + } + return ec; + } +#else + // calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN + // if libssh2 returns an error, get extended info and throw a message with ${code} and ${message} + // set appropriately. + template + int client_impl::call_ssh2_function_throw(const T& lambda, const char* message /* = "libssh2 call failed ${code} - ${message}" */, bool check_for_errors /* = true */) { + fc::unique_lock lock(ssh_session_mutex); + int ec = lambda(); + while (ec == LIBSSH2_ERROR_EAGAIN) { + bool unlock_to_wait = !session->packet.olen; + if (unlock_to_wait) + lock.unlock(); + wait_on_socket(); + if (unlock_to_wait) + lock.lock(); + ec = lambda(); + } + // this assert catches bugs in libssh2 if libssh2 returns ec != LIBSSH2_ERROR_EAGAIN + // but the internal session data indicates a data write is still in progress + // set check_for_errors to false when closing the connection + assert(!check_for_errors || !session->packet.olen); + + if (ec == LIBSSH2_ERROR_SFTP_PROTOCOL && sftp) { + ec = libssh2_sftp_last_error(sftp); + FC_THROW(message, ("code", ec).set("message", "SFTP protocol error")); + } else if( ec < 0 ) { + char* msg = 0; + ec = libssh2_session_last_error( session, &msg, 0, 0 ); + FC_THROW(message, ("code",ec).set("message",msg)); + } + return ec; + } +#endif + +#ifdef OLD_BLOCKING + // this version is a little different, it handles functions like libssh2_sftp_init which return + // a pointer instead of an int. These retry automatically if the result is NULL and the error + // is LIBSSH2_ERROR_EAGAIN + template + return_type client_impl::call_ssh2_ptr_function_throw(std::function lambda, const char* message /* = "libssh2 call failed ${code} - ${message}" */, bool check_for_errors /* = true */) { + fc::scoped_lock lock(ssh_session_mutex); + return_type ret = lambda(); + while (!ret) { + char* msg = 0; + int ec = libssh2_session_last_error(session,&msg,NULL,0); + if ( ec == LIBSSH2_ERROR_EAGAIN ) { + wait_on_socket(); + ret = lambda(); + } else if (ec == LIBSSH2_ERROR_SFTP_PROTOCOL && sftp) { + ec = libssh2_sftp_last_error(sftp); + FC_THROW(message, ("code", ec).set("message", "SFTP protocol error")); + } else { + ec = libssh2_session_last_error( session, &msg, 0, 0 ); + FC_THROW(message, ("code",ec).set("message",msg)); + } + } + assert(!check_for_errors || !session->packet.olen); + + return ret; + } +#else + // this version is a little different, it handles functions like libssh2_sftp_init which return + // a pointer instead of an int. These retry automatically if the result is NULL and the error + // is LIBSSH2_ERROR_EAGAIN + template + return_type client_impl::call_ssh2_ptr_function_throw(std::function lambda, const char* message /* = "libssh2 call failed ${code} - ${message}" */, bool check_for_errors /* = true */) { + fc::unique_lock lock(ssh_session_mutex); + return_type ret = lambda(); + while (!ret) { + char* msg = 0; + int ec = libssh2_session_last_error(session,&msg,NULL,0); + if ( ec == LIBSSH2_ERROR_EAGAIN ) { + bool unlock_to_wait = !session->packet.olen; + if (unlock_to_wait) + lock.unlock(); + wait_on_socket(); + if (unlock_to_wait) + lock.lock(); + ret = lambda(); + } else if (ec == LIBSSH2_ERROR_SFTP_PROTOCOL && sftp) { + ec = libssh2_sftp_last_error(sftp); + FC_THROW(message, ("code", ec).set("message", "SFTP protocol error")); + } else { + ec = libssh2_session_last_error( session, &msg, 0, 0 ); + FC_THROW(message, ("code",ec).set("message",msg)); + } + } + assert(!check_for_errors || !session->packet.olen); + + return ret; + } +#endif + } + +} } diff --git a/src/ssh/process.cpp b/src/ssh/process.cpp new file mode 100644 index 000000000..8794c119a --- /dev/null +++ b/src/ssh/process.cpp @@ -0,0 +1,334 @@ +#define NOMINMAX // prevent windows from defining min and max macros +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "client_impl.hpp" + +#if defined (_MSC_VER) +#pragma warning (disable : 4355) +#endif + +namespace fc { namespace ssh { + + namespace detail { + class process_impl; + class process_istream : public fc::istream { + public: + process_istream( process_impl& p, int c ) + :proc(p),chan(c){} + + virtual size_t readsome( char* buf, size_t len ); + + virtual bool eof() const; + + process_impl& proc; + int chan; + }; + + class process_ostream : public fc::ostream { + public: + process_ostream( process_impl& p ) + :proc(p){} + + virtual size_t writesome( const char* buf, size_t len ); + virtual void close(); + virtual void flush(); + + process_impl& proc; + }; + + class process_impl { + public: + process_impl( client_ptr c ); + ~process_impl(); + //process_impl( const client& c, const fc::string& cmd, const fc::string& pty_type ); + void exec(const fc::path& exe, vector args, + const fc::path& work_dir /* = fc::path() */, fc::iprocess::exec_opts opts /* = open_all */); + + + int read_some( char* data, size_t len, int stream_id ); + int write_some( const char* data, size_t len, int stream_id ); + void flush(); + void send_eof(); + + LIBSSH2_CHANNEL* chan; + client_ptr sshc; + buffered_ostream_ptr buffered_std_in; + buffered_istream_ptr buffered_std_out; + buffered_istream_ptr buffered_std_err; + + fc::string command; + fc::promise::ptr result; + + fc::optional return_code; + fc::ostring return_signal; + fc::ostring return_signal_message; + private: + static fc::string windows_shell_escape(const fc::string& str); + static fc::string unix_shell_escape(const fc::string& str); + static fc::string windows_shell_escape_command(const fc::path& exe, const vector& args); + static fc::string unix_shell_escape_command(const fc::path& exe, const vector& args); + }; + + } // end namespace detail + + + process::process(client_ptr c) : + my(new detail::process_impl(c)) + {} + + process::~process() + {} + + iprocess& process::exec( const fc::path& exe, vector args, + const fc::path& work_dir /* = fc::path() */, exec_opts opts /* = open_all */ ) { + my->exec(exe, args, work_dir, opts); + return *this; + } + + /** + * Blocks until the result code of the process has been returned. + */ + int process::result() { + if (!my->return_code && !my->return_signal) { + // we don't have any cached exit status, so wait and obtain the values now + my->sshc->my->call_ssh2_function(boost::bind(libssh2_channel_wait_eof, my->chan)); + my->sshc->my->call_ssh2_function_throw(boost::bind(libssh2_channel_wait_closed, my->chan), + "Error waiting on socket to close: ${message}"); + + char* exit_signal; + char* error_message; + libssh2_channel_get_exit_signal(my->chan, &exit_signal, NULL, &error_message, NULL, NULL, NULL); + if (exit_signal) { + // process terminated with a signal + my->return_signal = exit_signal; + libssh2_free(my->chan->session, exit_signal); + if (error_message) { + my->return_signal_message = error_message; + libssh2_free(my->chan->session, error_message); + } + } else + my->return_code = libssh2_channel_get_exit_status(my->chan); + } + if (my->return_signal) + FC_THROW("process terminated with signal ${signal}: ${signal_message}", ("signal", *my->return_signal) + ("signal_message", my->return_signal_message ? *my->return_signal_message : "")); + else + return *my->return_code; + } + + void process::kill() { + elog("error: fc::ssh::process::kill() not supported"); + } + + + /** + * @brief returns a stream that writes to the procss' stdin + */ + fc::buffered_ostream_ptr process::in_stream() { + return my->buffered_std_in; + } + + /** + * @brief returns a stream that reads from the process' stdout + */ + fc::buffered_istream_ptr process::out_stream() { + return my->buffered_std_out; + } + + /** + * @brief returns a stream that reads from the process' stderr + */ + fc::buffered_istream_ptr process::err_stream() { + return my->buffered_std_err; + } + + void detail::process_impl::flush() { + if( !chan ) return; + /* channel_flush deleates input buffer, and does not ensure writes go out + * + int ec = libssh2_channel_flush_ex( chan, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA); + while( ec == LIBSSH2_ERROR_EAGAIN ) { + sshc.my->wait_on_socket(); + ec = libssh2_channel_flush_ex( chan, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA ); + } + ec = libssh2_channel_flush( chan ); + while( ec == LIBSSH2_ERROR_EAGAIN ) { + sshc.my->wait_on_socket(); + ec = libssh2_channel_flush( chan ); + } + if( ec < 0 ) { + FC_THROW( "ssh flush failed", ( "channel_error", ec) ); + } + */ + } + + int detail::process_impl::read_some( char* data, size_t len, int stream_id ){ + if( !sshc->my->session ) { FC_THROW( "Session closed" ); } + int rc; + char* buf = data; + size_t buflen = len; + do { + rc = sshc->my->call_ssh2_function_throw(boost::bind(libssh2_channel_read_ex, chan, stream_id, buf, buflen), + "read failed: ${message}"); + if( rc > 0 ) { + buf += rc; + buflen -= rc; + return buf-data; + } else if( rc == 0 ) { + if( libssh2_channel_eof( chan ) ) + return -1; // eof + sshc->my->wait_on_socket(); + } + } while( rc >= 0 && buflen); + return buf-data; + } + + int detail::process_impl::write_some( const char* data, size_t len, int stream_id ) { + if( !sshc->my->session ) { FC_THROW( "Session closed" ); } + + int rc; + const char* buf = data; + size_t buflen = len; + do { + rc = sshc->my->call_ssh2_function_throw(boost::bind(libssh2_channel_write_ex, chan, stream_id, buf, buflen), + "write failed: ${message}"); + if( rc > 0 ) { + buf += rc; + buflen -= rc; + return buf-data; + } else if( rc == 0 ) { + if( libssh2_channel_eof( chan ) ) { + FC_THROW( "EOF" ); + //return -1; // eof + } + } + } while( rc >= 0 && buflen); + return buf-data; + } + + void detail::process_impl::send_eof() { + if( sshc->my->session ) + sshc->my->call_ssh2_function_throw(boost::bind(libssh2_channel_send_eof, chan), + "send eof failed: ${message}"); + } + + size_t detail::process_istream::readsome( char* buf, size_t len ) { + int bytesRead = proc.read_some(buf, len, chan); + if (bytesRead < 0) + FC_THROW("EOF"); + else + return bytesRead; + } + + bool detail::process_istream::eof()const { + return 0 != libssh2_channel_eof( proc.chan ); + } + + size_t detail::process_ostream::writesome( const char* buf, size_t len ) { + return proc.write_some(buf, len, 0); + } + + void detail::process_ostream::close(){ + proc.send_eof(); + } + + void detail::process_ostream::flush(){ + proc.flush(); + } + + detail::process_impl::process_impl( client_ptr c ) + :chan(nullptr), + sshc(c), + buffered_std_in(new buffered_ostream(ostream_ptr(new process_ostream(*this)))), + buffered_std_out(new buffered_istream(istream_ptr(new process_istream(*this, 0)))), + buffered_std_err(new buffered_istream(istream_ptr(new process_istream(*this, SSH_EXTENDED_DATA_STDERR)))) + { + } + + detail::process_impl::~process_impl() { + if (chan) { + sshc->my->call_ssh2_function(boost::bind(libssh2_channel_free, chan)); + chan = NULL; + } + } + + // these rules work pretty well for a standard bash shell on unix + fc::string detail::process_impl::unix_shell_escape(const fc::string& str) { + if (str.find_first_of(" ;&|><*?`$(){}[]!#'\"") == fc::string::npos) + return str; + fc::string escaped_quotes(str); + for (size_t start = escaped_quotes.find("'"); + start != fc::string::npos; + start = escaped_quotes.find("'", start + 5)) + escaped_quotes.replace(start, 1, "'\"'\"'"); + fc::string escaped_str("\'"); + escaped_str += escaped_quotes; + escaped_str += "\'"; + return escaped_str; + } + fc::string detail::process_impl::unix_shell_escape_command(const fc::path& exe, const vector& args) { + fc::stringstream command_line; + command_line << unix_shell_escape(exe.string()); + for (unsigned i = 0; i < args.size(); ++i) + command_line << " " << unix_shell_escape(args[i]); + return command_line.str(); + } + + // windows command-line escaping rules are a disaster, partly because how the command-line is + // parsed depends on what program you're running. In windows, the command line is passed in + // as a single string, and the process is left to interpret it as it sees fit. The standard + // C runtime uses one set of rules, the function CommandLineToArgvW usually used by + // GUI-mode programs uses a different set. + // Here we try to find a common denominator that works well for simple cases + // it's only minimally tested right now due to time constraints. + fc::string detail::process_impl::windows_shell_escape(const fc::string& str) { + if (str.find_first_of(" \"") == fc::string::npos) + return str; + fc::string escaped_quotes(str); + for (size_t start = escaped_quotes.find("\""); + start != fc::string::npos; + start = escaped_quotes.find("\"", start + 2)) + escaped_quotes.replace(start, 1, "\\\""); + fc::string escaped_str("\""); + escaped_str += escaped_quotes; + escaped_str += "\""; + return escaped_str; + } + fc::string detail::process_impl::windows_shell_escape_command(const fc::path& exe, const vector& args) { + fc::stringstream command_line; + command_line << windows_shell_escape(exe.string()); + for (unsigned i = 0; i < args.size(); ++i) + command_line << " " << windows_shell_escape(args[i]); + return command_line.str(); + } + + void detail::process_impl::exec(const fc::path& exe, vector args, + const fc::path& work_dir /* = fc::path() */, + fc::iprocess::exec_opts opts /* = open_all */) { + chan = sshc->my->open_channel(""); + + sshc->my->call_ssh2_function(boost::bind(libssh2_channel_handle_extended_data2, chan, LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL)); + + try { + fc::scoped_lock process_startup_lock(sshc->my->process_startup_mutex); + fc::string command_line = sshc->my->remote_system_is_windows ? windows_shell_escape_command(exe, args) : unix_shell_escape_command(exe, args); + sshc->my->call_ssh2_function_throw(boost::bind(libssh2_channel_process_startup, chan, "exec", sizeof("exec") - 1, command_line.c_str(), command_line.size()), + "exec failed: ${message}"); // equiv to libssh2_channel_exec(chan, cmd) macro + } catch (fc::exception& er) { + elog( "error starting process" ); + FC_RETHROW_EXCEPTION(er, error, "error starting process"); + } + } + +} } diff --git a/src/string.cpp b/src/string.cpp index 0f164f861..0b7cb9f47 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -1,15 +1,37 @@ #include #include #include +#include +#include +#include #include +#include +#include +#include +#include /** * Implemented with std::string for now. */ namespace fc { + class comma_numpunct : public std::numpunct + { + protected: + virtual char do_thousands_sep() const { return ','; } + virtual std::string do_grouping() const { return "\03"; } + }; + std::string to_pretty_string( int64_t value ) + { + std::stringstream ss; + ss.imbue( {std::locale(), new comma_numpunct} ); + ss << std::fixed << value; + return ss.str(); + } + +#ifdef USE_FC_STRING string::string(const char* s, int l) :my(s,l){ } string::string(){} string::string( const fc::string& c ):my(*c.my) { } @@ -21,6 +43,7 @@ namespace fc { string::~string() { } string::operator std::string&() { return *my; } string::operator const std::string&()const { return *my; } + char* string::data() { return &my->front(); } string::iterator string::begin() { return &(*this)[0]; } string::iterator string::end() { return &(*this)[size()]; } @@ -33,9 +56,17 @@ namespace fc { void string::reserve(size_t r) { my->reserve(r); } size_t string::size()const { return my->size(); } size_t string::find(char c, size_t p)const { return my->find(c,p); } + size_t string::find(const fc::string& str, size_t pos /* = 0 */) const { return my->find(str, pos); } + size_t string::find(const char* s, size_t pos /* = 0 */) const { return my->find(s,pos); } size_t string::rfind(char c, size_t p)const { return my->rfind(c,p); } size_t string::rfind(const char* c, size_t p)const { return my->rfind(c,p); } size_t string::rfind(const fc::string& c, size_t p)const { return my->rfind(c,p); } + size_t string::find_first_of(const fc::string& str, size_t pos /* = 0 */) const { return my->find_first_of(str, pos); } + size_t string::find_first_of(const char* s, size_t pos /* = 0 */) const { return my->find_first_of(s, pos); } + + fc::string& string::replace(size_t pos, size_t len, const string& str) { my->replace(pos, len, str); return *this; } + fc::string& string::replace(size_t pos, size_t len, const char* s) { my->replace(pos, len, s); return *this; } + void string::clear() { my->clear(); } void string::resize( size_t s ) { my->resize(s); } @@ -55,6 +86,91 @@ namespace fc { bool operator < ( const string& a, const string& b ) { return *a.my < *b.my; } string operator + ( const string& s, const string& c ) { return string(s) += c; } string operator + ( const string& s, char c ) { return string(s) += c; } +#endif // USE_FC_STRING + + + int64_t to_int64( const fc::string& i ) + { + try + { + return boost::lexical_cast(i.c_str()); + } + catch( const boost::bad_lexical_cast& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Couldn't parse int64_t" ); + } + FC_RETHROW_EXCEPTIONS( warn, "${i} => int64_t", ("i",i) ) + } + + uint64_t to_uint64( const fc::string& i ) + { try { + try + { + return boost::lexical_cast(i.c_str()); + } + catch( const boost::bad_lexical_cast& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Couldn't parse uint64_t" ); + } + FC_RETHROW_EXCEPTIONS( warn, "${i} => uint64_t", ("i",i) ) + } FC_CAPTURE_AND_RETHROW( (i) ) } + + double to_double( const fc::string& i) + { + try + { + return boost::lexical_cast(i.c_str()); + } + catch( const boost::bad_lexical_cast& e ) + { + FC_THROW_EXCEPTION( parse_error_exception, "Couldn't parse double" ); + } + FC_RETHROW_EXCEPTIONS( warn, "${i} => double", ("i",i) ) + } + + fc::string to_string(double d) + { + // +2 is required to ensure that the double is rounded correctly when read back in. http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html + std::stringstream ss; + ss << std::setprecision(std::numeric_limits::digits10 + 2) << std::fixed << d; + return ss.str(); + } + + fc::string to_string( uint64_t d) + { + return boost::lexical_cast(d); + } + + fc::string to_string( int64_t d) + { + return boost::lexical_cast(d); + } + fc::string to_string( uint16_t d) + { + return boost::lexical_cast(d); + } + std::string trim( const std::string& s ) + { + return boost::algorithm::trim_copy(s); + /* + std::string cpy(s); + boost::algorithm::trim(cpy); + return cpy; + */ + } + std::string to_lower( const std::string& s ) + { + auto tmp = s; + boost::algorithm::to_lower(tmp); + return tmp; + } + string trim_and_normalize_spaces( const string& s ) + { + string result = boost::algorithm::trim_copy( s ); + while( result.find( " " ) != result.npos ) + boost::algorithm::replace_all( result, " ", " " ); + return result; + } } // namespace fc diff --git a/src/super_fast_hash.cpp b/src/super_fast_hash.cpp deleted file mode 100644 index ac1bbca1d..000000000 --- a/src/super_fast_hash.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/** - Initial Publish Date: Thu, 16 Oct 2008 - Initial Author: Paul Hsieh - @file SuperFastHash.c - @version $Revision$ - @date $LastChangedDate$ - @author $LastChangeAuthor$ - @author Paul Hsieh - - The code below appears to be the fastest hashing algorithm available and is licensed for - anyone to use. An article describing the has function can be found on the link below. This - function is used by Safari and some other big-name programs (so it must be good right?) - - http://www.azillionmonkeys.com/qed/hash.html -*/ -#include -#include - -namespace fc { - - #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 - - uint32_t super_fast_hash (const fc::string& str ) { - return super_fast_hash( str.c_str(), str.size() ); - } - - uint32_t super_fast_hash (const char * data, int len) - { - uint32_t hash = len, tmp; - int rem; - - if (len <= 0 || data == 0) return 0; - - rem = len & 3; - len >>= 2; - - /* Main loop */ - for (;len > 0; len--) - { - hash += get16bits (data); - tmp = (get16bits (data+2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2*sizeof (uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) - { - case 3: hash += get16bits (data); - hash ^= hash << 16; - hash ^= data[sizeof (uint16_t)] << 18; - hash += hash >> 11; - break; - case 2: hash += get16bits (data); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; - } -} // namespace fc - diff --git a/src/task.cpp b/src/task.cpp deleted file mode 100644 index d17f17381..000000000 --- a/src/task.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include -#include -#include - - -namespace fc { - task_base::task_base(void* func) - :_functor(func){ - } - - void task_base::run() { - try { - _run_functor( _functor, _promise_impl ); - } catch ( ... ) { - set_exception( current_exception() ); - } - } - task_base::~task_base() { - _destroy_functor( _functor ); - } - - void task_base::_set_active_context(context* c) { - { synchronized( *_spinlock ) - _active_context = c; - } - } -} diff --git a/src/tcp_socket.cpp b/src/tcp_socket.cpp deleted file mode 100644 index 6b0a44077..000000000 --- a/src/tcp_socket.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace fc { - - class tcp_socket::impl { - public: - impl():_sock( fc::asio::default_io_service() ){ } - ~impl(){ - if( _sock.is_open() ) _sock.close(); - } - - boost::asio::ip::tcp::socket _sock; - }; - bool tcp_socket::is_open()const { - return my->_sock.is_open(); - } - - tcp_socket::tcp_socket(){}; - - tcp_socket::~tcp_socket(){}; - - void tcp_socket::flush() {} - void tcp_socket::close() { - if( is_open() ) my->_sock.close(); - } - - bool tcp_socket::eof()const { - return !my->_sock.is_open(); - } - - fc::ostream& tcp_socket::write( const char* buf, size_t len ) { - boost::system::error_code ec; - size_t w = my->_sock.write_some( boost::asio::buffer( buf, len ), ec ); - - if( w < len ) { - buf += w; - len -= w; - } - - if( ec == boost::asio::error::would_block ) { - promise::ptr p(new promise("tcp_socket::write")); - boost::asio::async_write( my->_sock, boost::asio::buffer(buf, len), - [=]( const boost::system::error_code& ec, size_t bt ) { - if( !ec ) p->set_value(bt); - else p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); - }); - p->wait(); - } else if( ec ) { - elog( "throw... write data failed %s", boost::system::system_error(ec).what() ); - FC_THROW_REPORT( "Failed to write data to tcp socket", - fc::value().set("reason", fc::string(boost::system::system_error(ec).what())) - .set("data", fc::to_hex(buf,len) ) ); - } - return *this; - } - size_t tcp_socket::readsome( char* buf, size_t len ) { - boost::system::error_code ec; - size_t w = my->_sock.read_some( boost::asio::buffer( buf, len ), ec ); - if( ec == boost::asio::error::would_block ) { - promise::ptr p(new promise("tcp_socket::readsome")); - my->_sock.async_read_some( boost::asio::buffer(buf, len), - [=]( const boost::system::error_code& ec, size_t bt ) { - slog( "%d ec: %s", bt, boost::system::system_error(ec).what() ); - if( !ec ) p->set_value(bt); - else p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); - }); - return p->wait(); - } else if (ec ) { - slog( "throw!" ); - throw boost::system::system_error(ec); - } - return w; - } - fc::istream& tcp_socket::read( char* buffer, size_t s ) { - size_t r = readsome( buffer, s ); - while( r < s ) { - r += readsome( buffer + r, s - r ); - } - return *this; - } - void tcp_socket::connect_to( const fc::ip::endpoint& e ) { - fc::asio::tcp::connect(my->_sock, fc::asio::tcp::endpoint( boost::asio::ip::address_v4(e.get_address()), e.port() ) ); - } - - class tcp_server::impl { - public: - impl(uint16_t port): - _accept( fc::asio::default_io_service(), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port) ){ - } - ~impl(){ - _accept.close(); - } - - boost::asio::ip::tcp::acceptor _accept; - }; - void tcp_server::close() { - if( my && my->_accept.is_open() ) my->_accept.close(); - delete my; my = nullptr; - } - tcp_server::tcp_server() - :my(nullptr) { - } - tcp_server::~tcp_server() { - delete my; - } - - - bool tcp_server::accept( tcp_socket& s ) { - if( !my ) return false; - fc::promise::ptr p( new promise("tcp::accept") ); - my->_accept.async_accept( s.my->_sock, [=]( const boost::system::error_code& e ) { - p->set_value(e); - } ); - auto ec = p->wait(); - if( !ec ) s.my->_sock.non_blocking(true); - if( ec ) BOOST_THROW_EXCEPTION( boost::system::system_error(ec) ); - return true; - } - void tcp_server::listen( uint16_t port ) { - if( my ) delete my; - my = new impl(port); - } - - - -} // namespace fc diff --git a/src/thread.cpp b/src/thread.cpp deleted file mode 100644 index 4aec97886..000000000 --- a/src/thread.cpp +++ /dev/null @@ -1,356 +0,0 @@ -#include -#include -#include -#include "thread_d.hpp" - -namespace fc { - const char* thread_name() { - return thread::current().name().c_str(); - } - void* thread_ptr() { - return &thread::current(); - } - - thread*& current_thread() { - #ifdef _MSC_VER - static __declspec(thread) thread* t = NULL; - #else - static __thread thread* t = NULL; - #endif - return t; - } - - thread::thread( const char* name ) { - promise::ptr p(new promise()); - boost::thread* t = new boost::thread( [this,p,name]() { - try { - this->my = new thread_d(*this); - current_thread() = this; - p->set_value(); - exec(); - } catch ( ... ) { - elog( "Caught unhandled exception %s", boost::current_exception_diagnostic_information().c_str() ); - } - delete this->my; - this->my = 0; - } ); - p->wait(); - my->boost_thread = t; - set_name(name); - } - thread::thread( thread_d* ) { - my = new thread_d(*this); - } - - thread::thread( thread&& m ) { - my = m.my; - m.my = 0; - } - - thread& thread::operator=(thread&& t ) { - fc_swap(t.my,my); - return *this; - } - - thread::~thread() { - if( is_current() ) { - //slog( "my %p", my ); - delete my; - my = 0; - } - } - - thread& thread::current() { - if( !current_thread() ) current_thread() = new thread((thread_d*)0); - return *current_thread(); - } - const string& thread::name()const { return my->name; } - void thread::set_name( const fc::string& n ) { my->name = n; } - void thread::debug( const fc::string& d ) { my->debug(d); } - - void thread::quit() { - if( ¤t() != this ) { - async( [=](){quit();} );//.wait(); - if( my->boost_thread ) { - auto n = name(); - slog( "joining... %s", n.c_str() ); - my->boost_thread->join(); - slog( "done joining... %s", n.c_str() ); - } - return; - } - wlog( "%s", my->name.c_str() ); - - // break all promises, thread quit! - while( my->blocked ) { - fc::context* cur = my->blocked; - while( cur ) { - fc::context* n = cur->next; - // this will move the context into the ready list. - //cur->prom->set_exception( boost::copy_exception( error::thread_quit() ) ); - cur->except_blocking_promises( thread_quit() ); - cur = n; - } - if( my->blocked ) { - wlog( "still blocking... whats up with that?"); - debug( "on quit" ); - } - } - BOOST_ASSERT( my->blocked == 0 ); - //my->blocked = 0; - - - // move all sleep tasks to ready - for( uint32_t i = 0; i < my->sleep_pqueue.size(); ++i ) { - my->ready_push_front( my->sleep_pqueue[i] ); - } - my->sleep_pqueue.clear(); - - // move all idle tasks to ready - fc::context* cur = my->pt_head; - while( cur ) { - fc::context* n = cur->next; - cur->next = 0; - my->ready_push_front( cur ); - cur = n; - } - - // mark all ready tasks (should be everyone)... as canceled - cur = my->ready_head; - while( cur ) { - cur->canceled = true; - cur = cur->next; - } - - my->done = true; - - // now that we have poked all fibers... switch to the next one and - // let them all quit. - while( my->ready_head ) { - my->start_next_fiber(true); - my->check_for_timeouts(); - } - my->clear_free_list(); - } - - void thread::exec() { - if( !my->current ) my->current = new fc::context(&fc::thread::current()); - my->process_tasks(); - delete my->current; - my->current = 0; - } - - bool thread::is_running()const { - return !my->done; - } - - priority thread::current_priority()const { - BOOST_ASSERT(my); - if( my->current ) return my->current->prio; - return priority(); - } - - void thread::yield(bool reschedule ) { - my->check_fiber_exceptions(); - my->start_next_fiber(reschedule); - my->check_fiber_exceptions(); - } - void thread::sleep_until( const time_point& tp ) { - my->check_fiber_exceptions(); - - BOOST_ASSERT( ¤t() == this ); - if( !my->current ) { - my->current = new fc::context(&fc::thread::current()); - } - - my->current->resume_time = tp; - my->current->clear_blocking_promises(); - - my->sleep_pqueue.push_back(my->current); - std::push_heap( my->sleep_pqueue.begin(), - my->sleep_pqueue.end(), sleep_priority_less() ); - - my->start_next_fiber(); - my->current->resume_time = time_point::max(); - - my->check_fiber_exceptions(); - } - int thread::wait_any_until( fc::vector&& p, const time_point& timeout) { - for( size_t i = 0; i < p.size(); ++i ) { - if( p[i]->ready() ) return i; - } - - if( timeout < time_point::now() ) { - fc::stringstream ss; - for( auto i = p.begin(); i != p.end(); ++i ) { - ss << (*i)->get_desc() <<", "; - } - BOOST_THROW_EXCEPTION( future_wait_timeout( ss.str() ) ); - } - - if( !my->current ) { - my->current = new fc::context(&fc::thread::current()); - } - - for( uint32_t i = 0; i < p.size(); ++i ) { - my->current->add_blocking_promise(p[i].get(),false); - }; - - // if not max timeout, added to sleep pqueue - if( timeout != time_point::max() ) { - my->current->resume_time = timeout; - my->sleep_pqueue.push_back(my->current); - std::push_heap( my->sleep_pqueue.begin(), - my->sleep_pqueue.end(), - sleep_priority_less() ); - } - my->add_to_blocked( my->current ); - my->start_next_fiber(); - - for( auto i = p.begin(); i != p.end(); ++i ) { - my->current->remove_blocking_promise(i->get()); - } - - my->check_fiber_exceptions(); - - for( uint32_t i = 0; i < p.size(); ++i ) { - if( p[i]->ready() ) return i; - } - BOOST_THROW_EXCEPTION( wait_any_error() ); - return -1; - } - - void thread::async_task( task_base* t, const priority& p, const char* desc ) { - async_task( t, p, time_point::min(), desc ); - } - - void thread::poke() { - boost::unique_lock lock(my->task_ready_mutex); - my->task_ready.notify_one(); - } - - void thread::async_task( task_base* t, const priority& p, const time_point& tp, const char* desc ) { - assert(my); - t->_when = tp; - // slog( "when %lld", t->_when.time_since_epoch().count() ); - // slog( "delay %lld", (tp - fc::time_point::now()).count() ); - task_base* stale_head = my->task_in_queue.load(boost::memory_order_relaxed); - do { t->_next = stale_head; - }while( !my->task_in_queue.compare_exchange_weak( stale_head, t, boost::memory_order_release ) ); - - // Because only one thread can post the 'first task', only that thread will attempt - // to aquire the lock and therefore there should be no contention on this lock except - // when *this thread is about to block on a wait condition. - if( this != ¤t() && !stale_head ) { - boost::unique_lock lock(my->task_ready_mutex); - my->task_ready.notify_one(); - } - } - - void yield() { - thread::current().yield(); - } - void usleep( const microseconds& u ) { - thread::current().sleep_until( time_point::now() + u); - } - void sleep_until( const time_point& tp ) { - thread::current().sleep_until(tp); - } - - void exec() { - return thread::current().exec(); - } - - int wait_any( fc::vector&& v, const microseconds& timeout_us ) { - return thread::current().wait_any_until( fc::move(v), time_point::now() + timeout_us ); - } - int wait_any_until( fc::vector&& v, const time_point& tp ) { - return thread::current().wait_any_until( fc::move(v), tp ); - } - void thread::wait_until( promise_base::ptr&& p, const time_point& timeout ) { - if( p->ready() ) return; - if( timeout < time_point::now() ) - BOOST_THROW_EXCEPTION( future_wait_timeout( p->get_desc() ) ); - - if( !my->current ) { - my->current = new fc::context(&fc::thread::current()); - } - - //slog( " %1% blocking on %2%", my->current, p.get() ); - my->current->add_blocking_promise(p.get(),true); - - // if not max timeout, added to sleep pqueue - if( timeout != time_point::max() ) { - my->current->resume_time = timeout; - my->sleep_pqueue.push_back(my->current); - std::push_heap( my->sleep_pqueue.begin(), - my->sleep_pqueue.end(), - sleep_priority_less() ); - } - - // elog( "blocking %1%", my->current ); - my->add_to_blocked( my->current ); - // my->debug("swtiching fibers..." ); - - - my->start_next_fiber(); - // slog( "resuming %1%", my->current ); - - //slog( " %1% unblocking blocking on %2%", my->current, p.get() ); - my->current->remove_blocking_promise(p.get()); - - my->check_fiber_exceptions(); - } - - void thread::notify( const promise_base::ptr& p ) { - //slog( "this %p my %p", this, my ); - BOOST_ASSERT(p->ready()); - if( !is_current() ) { - this->async( [=](){ notify(p); }, "notify", priority::max() ); - return; - } - // TODO: store a list of blocked contexts with the promise - // to accelerate the lookup.... unless it introduces contention... - - // iterate over all blocked contexts - - - fc::context* cur_blocked = my->blocked; - fc::context* prev_blocked = 0; - while( cur_blocked ) { - // if the blocked context is waiting on this promise - if( cur_blocked->try_unblock( p.get() ) ) { - // remove it from the blocked list. - - // remove this context from the sleep queue... - for( uint32_t i = 0; i < my->sleep_pqueue.size(); ++i ) { - if( my->sleep_pqueue[i] == cur_blocked ) { - my->sleep_pqueue[i]->blocking_prom.clear(); - my->sleep_pqueue[i] = my->sleep_pqueue.back(); - my->sleep_pqueue.pop_back(); - std::make_heap( my->sleep_pqueue.begin(),my->sleep_pqueue.end(), sleep_priority_less() ); - break; - } - } - auto cur = cur_blocked; - if( prev_blocked ) { - prev_blocked->next_blocked = cur_blocked->next_blocked; - cur_blocked = prev_blocked->next_blocked; - } else { - my->blocked = cur_blocked->next_blocked; - cur_blocked = my->blocked; - } - cur->next_blocked = 0; - my->ready_push_front( cur ); - } else { // goto the next blocked task - prev_blocked = cur_blocked; - cur_blocked = cur_blocked->next_blocked; - } - } - } - bool thread::is_current()const { - return this == ¤t(); - } - - -} diff --git a/src/thread/context.hpp b/src/thread/context.hpp new file mode 100644 index 000000000..f6e8a7749 --- /dev/null +++ b/src/thread/context.hpp @@ -0,0 +1,238 @@ +#pragma once +#include +#include +#include +#include + +#include + +#if BOOST_VERSION >= 105400 +# include + namespace bc = boost::context; + namespace bco = boost::coroutines; +# if BOOST_VERSION >= 105600 && !defined(NDEBUG) +# include +# include + typedef bco::protected_stack_allocator stack_allocator; +# else +# include + typedef bco::stack_allocator stack_allocator; +# endif + +#elif BOOST_VERSION >= 105300 + #include + namespace bc = boost::context; + namespace bco = boost::coroutines; +#elif BOOST_VERSION >= 105200 + namespace bc = boost::context; +#else + namespace bc = boost::ctx; + namespace bco = boost::coroutine; +#endif + +namespace fc { + class thread; + class promise_base; + class task_base; + + /** + * maintains information associated with each context such as + * where it is blocked, what time it should resume, priority, + * etc. + */ + struct context { + typedef fc::context* ptr; + +#if BOOST_VERSION >= 105400 + bco::stack_context stack_ctx; +#endif + + + context( void (*sf)(intptr_t), stack_allocator& alloc, fc::thread* t ) + : caller_context(0), + stack_alloc(&alloc), + next_blocked(0), + next_blocked_mutex(0), + next(0), + ctx_thread(t), + canceled(false), +#ifndef NDEBUG + cancellation_reason(nullptr), +#endif + complete(false), + cur_task(0), + context_posted_num(0) + { +#if BOOST_VERSION >= 105600 + size_t stack_size = FC_CONTEXT_STACK_SIZE; + alloc.allocate(stack_ctx, stack_size); + my_context = bc::make_fcontext( stack_ctx.sp, stack_ctx.size, sf); +#elif BOOST_VERSION >= 105400 + size_t stack_size = FC_CONTEXT_STACK_SIZE; + alloc.allocate(stack_ctx, stack_size); + my_context = bc::make_fcontext( stack_ctx.sp, stack_ctx.size, sf); +#elif BOOST_VERSION >= 105300 + size_t stack_size = FC_CONTEXT_STACK_SIZE; + void* stackptr = alloc.allocate(stack_size); + my_context = bc::make_fcontext( stackptr, stack_size, sf); +#else + size_t stack_size = FC_CONTEXT_STACK_SIZE; + my_context.fc_stack.base = alloc.allocate( stack_size ); + my_context.fc_stack.limit = static_cast( my_context.fc_stack.base) - stack_size; + make_fcontext( &my_context, sf ); +#endif + } + + context( fc::thread* t) : +#if BOOST_VERSION >= 105600 + my_context(nullptr), +#elif BOOST_VERSION >= 105300 + my_context(new bc::fcontext_t), +#endif + caller_context(0), + stack_alloc(0), + next_blocked(0), + next_blocked_mutex(0), + next(0), + ctx_thread(t), + canceled(false), +#ifndef NDEBUG + cancellation_reason(nullptr), +#endif + complete(false), + cur_task(0), + context_posted_num(0) + {} + + ~context() { +#if BOOST_VERSION >= 105600 + if(stack_alloc) + stack_alloc->deallocate( stack_ctx ); +#elif BOOST_VERSION >= 105400 + if(stack_alloc) + stack_alloc->deallocate( stack_ctx ); + else + delete my_context; +#elif BOOST_VERSION >= 105300 + if(stack_alloc) + stack_alloc->deallocate( my_context->fc_stack.sp, FC_CONTEXT_STACK_SIZE); + else + delete my_context; +#else + if(stack_alloc) + stack_alloc->deallocate( my_context.fc_stack.base, FC_CONTEXT_STACK_SIZE ); +#endif + } + + void reinitialize() + { + canceled = false; +#ifndef NDEBUG + cancellation_reason = nullptr; +#endif + blocking_prom.clear(); + caller_context = nullptr; + resume_time = fc::time_point(); + next_blocked = nullptr; + next_blocked_mutex = nullptr; + next = nullptr; + complete = false; + } + + struct blocked_promise { + blocked_promise( promise_base* p=0, bool r=true ) + :prom(p),required(r){} + + promise_base* prom; + bool required; + }; + + /** + * @todo Have a list of promises so that we can wait for + * P1 or P2 and either will unblock instead of requiring both + * @param req - require this promise to 'unblock', otherwise try_unblock + * will allow it to be one of many that could 'unblock' + */ + void add_blocking_promise( promise_base* p, bool req = true ) { + for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { + if( i->prom == p ) { + i->required = req; + return; + } + } + blocking_prom.push_back( blocked_promise(p,req) ); + } + /** + * If all of the required promises and any optional promises then + * return true, else false. + * @todo check list + */ + bool try_unblock( promise_base* p ) { + if( blocking_prom.size() == 0 ) { + return true; + } + bool req = false; + for( uint32_t i = 0; i < blocking_prom.size(); ++i ) { + if( blocking_prom[i].prom == p ) { + blocking_prom[i].required = false; + return true; + } + req = req || blocking_prom[i].required; + } + return !req; + } + + void remove_blocking_promise( promise_base* p ) { + for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { + if( i->prom == p ) { + blocking_prom.erase(i); + return; + } + } + } + + void timeout_blocking_promises() { + for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { + i->prom->set_exception( std::make_shared() ); + } + } + void set_exception_on_blocking_promises( const exception_ptr& e ) { + for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) { + i->prom->set_exception( e ); + } + } + void clear_blocking_promises() { + blocking_prom.clear(); + } + + bool is_complete()const { return complete; } + + + +#if BOOST_VERSION >= 105300 && BOOST_VERSION < 105600 + bc::fcontext_t* my_context; +#else + bc::fcontext_t my_context; +#endif + fc::context* caller_context; + stack_allocator* stack_alloc; + priority prio; + //promise_base* prom; + std::vector blocking_prom; + time_point resume_time; + // time_point ready_time; // time that this context was put on ready queue + fc::context* next_blocked; + fc::context* next_blocked_mutex; + fc::context* next; + fc::thread* ctx_thread; + bool canceled; +#ifndef NDEBUG + const char* cancellation_reason; +#endif + bool complete; + task_base* cur_task; + uint64_t context_posted_num; // serial number set each tiem the context is added to the ready list + }; + +} // naemspace fc + diff --git a/src/thread/future.cpp b/src/thread/future.cpp new file mode 100644 index 000000000..2111584e7 --- /dev/null +++ b/src/thread/future.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include + +#include + + +namespace fc { + + promise_base::promise_base( const char* desc ) + :_ready(false), + _blocked_thread(nullptr), + _blocked_fiber_count(0), + _timeout(time_point::maximum()), + _canceled(false), +#ifndef NDEBUG + _cancellation_reason(nullptr), +#endif + _desc(desc), + _compl(nullptr) + { } + + const char* promise_base::get_desc()const{ + return _desc; + } + + void promise_base::cancel(const char* reason /* = nullptr */){ +// wlog("${desc} canceled!", ("desc", _desc? _desc : "")); + _canceled = true; +#ifndef NDEBUG + _cancellation_reason = reason; +#endif + } + bool promise_base::ready()const { + return _ready; + } + bool promise_base::error()const { + { synchronized(_spin_yield) + return _exceptp != nullptr; + } + } + + void promise_base::set_exception( const fc::exception_ptr& e ){ + _exceptp = e; + _set_value(nullptr); + } + + void promise_base::_wait( const microseconds& timeout_us ){ + if( timeout_us == microseconds::maximum() ) + _wait_until( time_point::maximum() ); + else + _wait_until( time_point::now() + timeout_us ); + } + void promise_base::_wait_until( const time_point& timeout_us ){ + { synchronized(_spin_yield) + if( _ready ) { + if( _exceptp ) + _exceptp->dynamic_rethrow_exception(); + return; + } + _enqueue_thread(); + } + std::exception_ptr e; + + // + // Create shared_ptr to take ownership of this; i.e. this will + // be deleted when p_this goes out of scope. Consequently, + // it would be Very Bad to let p_this go out of scope + // before we're done reading/writing instance variables! + // See https://github.com/cryptonomex/graphene/issues/597 + // + + ptr p_this = ptr( this, true ); + + try + { + // + // We clone p_this here because the wait_until() API requires us + // to use std::move(). I.e. wait_until() takes ownership of any + // pointer passed to it. Since we want to keep ownership ourselves, + // we need to have two shared_ptr's to this: + // + // - p_this to keep this alive until the end of the current function + // - p_this2 to be owned by wait_until() as the wait_until() API requires + // + ptr p_this2 = p_this; + thread::current().wait_until( std::move( p_this2 ), timeout_us ); + } + catch (...) { e = std::current_exception(); } + + _dequeue_thread(); + + if( e ) std::rethrow_exception(e); + + if( _ready ) + { + if( _exceptp ) + _exceptp->dynamic_rethrow_exception(); + return; + } + FC_THROW_EXCEPTION( timeout_exception, "" ); + } + void promise_base::_enqueue_thread(){ + ++_blocked_fiber_count; + // only one thread can wait on a promise at any given time + assert(!_blocked_thread || + _blocked_thread == &thread::current()); + _blocked_thread = &thread::current(); + } + void promise_base::_dequeue_thread(){ + synchronized(_spin_yield) + if (!--_blocked_fiber_count) + _blocked_thread = nullptr; + } + void promise_base::_notify(){ + // copy _blocked_thread into a local so that if the thread unblocks (e.g., + // because of a timeout) before we get a chance to notify it, we won't be + // calling notify on a null pointer + thread* blocked_thread; + { synchronized(_spin_yield) + blocked_thread = _blocked_thread; + } + if( blocked_thread ) + blocked_thread->notify(ptr(this,true)); + } + promise_base::~promise_base() { } + void promise_base::_set_timeout(){ + if( _ready ) + return; + set_exception( std::make_shared() ); + } + void promise_base::_set_value(const void* s){ + // slog( "%p == %d", &_ready, int(_ready)); +// BOOST_ASSERT( !_ready ); + { synchronized(_spin_yield) + if (_ready) //don't allow promise to be set more than once + return; + _ready = true; + } + _notify(); + if( nullptr != _compl ) { + _compl->on_complete(s,_exceptp); + } + } + void promise_base::_on_complete( detail::completion_handler* c ) { + { synchronized(_spin_yield) + delete _compl; + _compl = c; + } + } +} + diff --git a/src/thread/mutex.cpp b/src/thread/mutex.cpp new file mode 100644 index 000000000..9f3f9baa2 --- /dev/null +++ b/src/thread/mutex.cpp @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include "context.hpp" +#include "thread_d.hpp" + +namespace fc { + + mutex::mutex() : + m_blist(0), + recursive_lock_count(0) + {} + + mutex::~mutex() { + if( m_blist ) + { + context* c = m_blist; + fc::thread::current().debug("~mutex"); +#if 0 + while( c ) { + // elog( "still blocking on context %p (%s)", m_blist, (m_blist->cur_task ? m_blist->cur_task->get_desc() : "no current task") ); + c = c->next_blocked_mutex; + } +#endif + BOOST_ASSERT( false && "Attempt to free mutex while others are blocking on lock." ); + } + } + + /** + * @param last_context - is set to the next context to get the lock (the next-to-last element of the list) + * @return the last context (the one with the lock) + */ + static fc::context* get_tail( fc::context* list_head, fc::context*& context_to_unblock ) { + context_to_unblock = 0; + fc::context* list_context_iter = list_head; + if( !list_context_iter ) + return list_context_iter; + while( list_context_iter->next_blocked_mutex ) + { + context_to_unblock = list_context_iter; + list_context_iter = list_context_iter->next_blocked_mutex; + } + return list_context_iter; + } + + static fc::context* remove( fc::context* head, fc::context* target ) { + fc::context* context_iter = head; + fc::context* previous = 0; + while( context_iter ) + { + if( context_iter == target ) + { + if( previous ) + { + previous->next_blocked_mutex = context_iter->next_blocked_mutex; + return head; + } + return context_iter->next_blocked_mutex; + } + previous = context_iter; + context_iter = context_iter->next_blocked_mutex; + } + return head; + } + static void cleanup( fc::mutex& m, fc::spin_yield_lock& syl, fc::context*& bl, fc::context* cc ) { + { + fc::unique_lock lock(syl); + if( cc->next_blocked_mutex ) { + bl = remove(bl, cc); + cc->next_blocked_mutex = nullptr; + return; + } + } + m.unlock(); + } + + /** + * A mutex is considered to hold the lock when + * the current context is the tail in the wait queue. + */ + bool mutex::try_lock() { + assert(false); // this is currently broken re: recursive mutexes + fc::thread* ct = &fc::thread::current(); + fc::context* cc = ct->my->current; + fc::context* n = 0; + + fc::unique_lock lock(m_blist_lock, fc::try_to_lock_t()); + if( !lock ) + return false; + + if( !m_blist ) { + m_blist = cc; + return true; + } + // allow recursive locks. + return ( get_tail( m_blist, n ) == cc ); + } + + bool mutex::try_lock_until( const fc::time_point& abs_time ) { + assert(false); // this is currently broken re: recursive mutexes + fc::context* n = 0; + fc::context* cc = fc::thread::current().my->current; + + { // lock scope + fc::unique_lock lock(m_blist_lock,abs_time); + if( !lock ) return false; + + if( !m_blist ) { + m_blist = cc; + return true; + } + + // allow recusive locks + if ( get_tail( m_blist, n ) == cc ) + return true; + + cc->next_blocked_mutex = m_blist; + m_blist = cc; + } // end lock scope + + + std::exception_ptr e; + try { + fc::thread::current().my->yield_until( abs_time, false ); + return( 0 == cc->next_blocked_mutex ); + } catch (...) { + e = std::current_exception(); + } + assert(e); + cleanup( *this, m_blist_lock, m_blist, cc); + std::rethrow_exception(e); + } + + void mutex::lock() { + fc::context* current_context = fc::thread::current().my->current; + if( !current_context ) + current_context = fc::thread::current().my->current = new fc::context( &fc::thread::current() ); + + { + fc::unique_lock lock(m_blist_lock); + if( !m_blist ) + { + // nobody else owns the mutex, so we get it; add our context as the last and only element on the mutex's list + m_blist = current_context; + assert(recursive_lock_count == 0); + recursive_lock_count = 1; + assert(!current_context->next_blocked_mutex); + return; + } + + // allow recusive locks + fc::context* dummy_context_to_unblock = 0; + if ( get_tail( m_blist, dummy_context_to_unblock ) == current_context ) { + assert(recursive_lock_count > 0); + ++recursive_lock_count; + return; + } + // add ourselves to the head of the list + current_context->next_blocked_mutex = m_blist; + m_blist = current_context; + +#if 0 + int cnt = 0; + auto i = m_blist; + while( i ) { + i = i->next_blocked_mutex; + ++cnt; + } + //wlog( "wait queue len %1%", cnt ); +#endif + } + + std::exception_ptr e; // cleanup calls yield so we need to move the exception outside of the catch block + try + { + fc::thread::current().yield(false); + // if yield() returned normally, we should now own the lock (we should be at the tail of the list) + BOOST_ASSERT( current_context->next_blocked_mutex == 0 ); + assert(recursive_lock_count == 0); + recursive_lock_count = 1; + } + catch ( ... ) + { + e = std::current_exception(); + } + if( e ) { + cleanup( *this, m_blist_lock, m_blist, current_context); + std::rethrow_exception(e); + } + } + + void mutex::unlock() + { + fc::context* context_to_unblock = 0; + + fc::unique_lock lock(m_blist_lock); + assert(recursive_lock_count > 0); + --recursive_lock_count; + if (recursive_lock_count != 0) + return; + + get_tail(m_blist, context_to_unblock); + if( context_to_unblock ) + { + context_to_unblock->next_blocked_mutex = 0; + context_to_unblock->ctx_thread->my->unblock( context_to_unblock ); + } + else + m_blist = 0; + } + +} // fc + + diff --git a/src/thread/non_preemptable_scope_check.cpp b/src/thread/non_preemptable_scope_check.cpp new file mode 100644 index 000000000..574bdd43c --- /dev/null +++ b/src/thread/non_preemptable_scope_check.cpp @@ -0,0 +1,20 @@ +#ifndef NDEBUG +#include +#include +#include "thread_d.hpp" + + +namespace fc +{ + non_preemptable_scope_check::non_preemptable_scope_check() + { + ++thread::current().my->non_preemptable_scope_count; + } + + non_preemptable_scope_check::~non_preemptable_scope_check() + { + assert(thread::current().my->non_preemptable_scope_count > 0); + --thread::current().my->non_preemptable_scope_count; + } +} // fc +#endif \ No newline at end of file diff --git a/src/spin_lock.cpp b/src/thread/spin_lock.cpp similarity index 96% rename from src/spin_lock.cpp rename to src/thread/spin_lock.cpp index 8bf72c689..97989c5aa 100644 --- a/src/spin_lock.cpp +++ b/src/thread/spin_lock.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/src/spin_yield_lock.cpp b/src/thread/spin_yield_lock.cpp similarity index 96% rename from src/spin_yield_lock.cpp rename to src/thread/spin_yield_lock.cpp index 07d25601f..17206da73 100644 --- a/src/spin_yield_lock.cpp +++ b/src/thread/spin_yield_lock.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/src/thread/task.cpp b/src/thread/task.cpp new file mode 100644 index 000000000..b081872ee --- /dev/null +++ b/src/thread/task.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include "context.hpp" + +#include +#include + +#ifdef _MSC_VER +# include +# include +#endif + +namespace fc { + task_base::task_base(void* func) + : + promise_base("task_base"), + _posted_num(0), + _active_context(nullptr), + _next(nullptr), + _task_specific_data(nullptr), + _promise_impl(nullptr), + _functor(func){ + } + + void task_base::run() { +#ifdef _MSC_VER + __try { +#endif + run_impl(); +#ifdef _MSC_VER + } __except (get_unhandled_structured_exception_filter() ? get_unhandled_structured_exception_filter()(GetExceptionCode(), GetExceptionInformation()) : EXCEPTION_CONTINUE_SEARCH) { + ExitProcess(1); + } +#endif + } + + void task_base::run_impl() { + try { + if( !canceled() ) + _run_functor( _functor, _promise_impl ); + else +#ifdef NDEBUG + FC_THROW_EXCEPTION( canceled_exception, "task ${description} canceled before starting", ("description", get_desc())); +#else + FC_THROW_EXCEPTION( canceled_exception, "task ${description} canceled before starting, reason ${reason}", + ("description", get_desc()) + ("reason", _cancellation_reason ? _cancellation_reason : "[none given]")); +#endif + } + catch ( const exception& e ) + { + set_exception( e.dynamic_copy_exception() ); + } + catch ( ... ) + { + set_exception( std::make_shared( FC_LOG_MESSAGE( warn, "unhandled exception: ${diagnostic}", ("diagnostic",boost::current_exception_diagnostic_information()) ) ) ); + } + } + + void task_base::cancel(const char* reason /* = nullptr */) + { + promise_base::cancel(reason); + if (_active_context) + { + if (_active_context->next_blocked_mutex) + { + // this task is blocked on a mutex, we probably don't handle this correctly + _active_context->ctx_thread->unblock(_active_context); + } + _active_context->canceled = true; +#ifndef NDEBUG + _active_context->cancellation_reason = reason; +#endif + _active_context->ctx_thread->notify_task_has_been_canceled(); + } + } + + task_base::~task_base() { + cleanup_task_specific_data(); + _destroy_functor( _functor ); + } + + void task_base::_set_active_context(context* c) { + { synchronized( *_spinlock ) + _active_context = c; + } + } + + void task_base::cleanup_task_specific_data() + { + if (_task_specific_data) + { + for (auto iter = _task_specific_data->begin(); iter != _task_specific_data->end(); ++iter) + if (iter->cleanup) + iter->cleanup(iter->value); + delete _task_specific_data; + _task_specific_data = nullptr; + } + } + +} diff --git a/src/thread/thread.cpp b/src/thread/thread.cpp new file mode 100644 index 000000000..eaa8e4082 --- /dev/null +++ b/src/thread/thread.cpp @@ -0,0 +1,504 @@ +#include +#include +#include +#include +#include "thread_d.hpp" + +#if defined(_MSC_VER) && !defined(NDEBUG) +# include +const DWORD MS_VC_EXCEPTION=0x406D1388; + +#pragma pack(push,8) +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; +#pragma pack(pop) + +static void set_thread_name(const char* threadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = -1; + info.dwFlags = 0; + + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +} +#elif defined(__linux__) && !defined(NDEBUG) +# include +static void set_thread_name(const char* threadName) +{ + pthread_setname_np(pthread_self(), threadName); +} +#elif defined(__APPLE__) && !defined(NDEBUG) +# include +static void set_thread_name(const char* threadName) +{ + pthread_setname_np(threadName); +} +#else +static void set_thread_name(const char* threadName) +{ + // do nothing in release mode +} +#endif + +namespace fc { + const char* thread_name() { + return thread::current().name().c_str(); + } + void* thread_ptr() { + return &thread::current(); + } + + thread*& current_thread() { + #ifdef _MSC_VER + static __declspec(thread) thread* t = NULL; + #else + static __thread thread* t = NULL; + #endif + return t; + } + + thread::thread( const std::string& name ) { + promise::ptr p(new promise("thread start")); + boost::thread* t = new boost::thread( [this,p,name]() { + try { + set_thread_name(name.c_str()); // set thread's name for the debugger to display + this->my = new thread_d(*this); + current_thread() = this; + p->set_value(); + exec(); + } catch ( fc::exception& e ) { + wlog( "unhandled exception" ); + p->set_exception( e.dynamic_copy_exception() ); + } catch ( ... ) { + wlog( "unhandled exception" ); + p->set_exception( std::make_shared( FC_LOG_MESSAGE( warn, "unhandled exception: ${diagnostic}", ("diagnostic",boost::current_exception_diagnostic_information()) ) ) ); + //assert( !"unhandled exception" ); + //elog( "Caught unhandled exception %s", boost::current_exception_diagnostic_information().c_str() ); + } + } ); + p->wait(); + my->boost_thread = t; + my->name = name; + wlog("name:${n} tid:${tid}", ("n", name)("tid", (uintptr_t)my->boost_thread->native_handle()) ); + } + thread::thread( thread_d* ) { + my = new thread_d(*this); + } + + thread::thread( thread&& m ) { + my = m.my; + m.my = 0; + } + + thread& thread::operator=(thread&& t ) { + fc_swap(t.my,my); + return *this; + } + + thread::~thread() { + //wlog( "my ${n}", ("n",name()) ); + if( my ) + { + // wlog( "calling quit() on ${n}",("n",my->name) ); + quit(); // deletes `my` + } + } + + thread& thread::current() { + if( !current_thread() ) + current_thread() = new thread((thread_d*)0); + return *current_thread(); + } + + const string& thread::name()const + { + return my->name; + } + + void thread::set_name( const fc::string& n ) + { + if (!is_current()) + { + async([=](){ set_name(n); }, "set_name").wait(); + return; + } + my->name = n; + set_thread_name(my->name.c_str()); // set thread's name for the debugger to display + } + + const char* thread::current_task_desc() const + { + if (my->current && my->current->cur_task) + return my->current->cur_task->get_desc(); + return NULL; + } + + void thread::debug( const fc::string& d ) { /*my->debug(d);*/ } + + void thread::quit() + { + //if quitting from a different thread, start quit task on thread. + //If we have and know our attached boost thread, wait for it to finish, then return. + if( ¤t() != this ) + { + async( [=](){quit();}, "thread::quit" );//.wait(); + if( my->boost_thread ) + { + //wlog("destroying boost thread ${tid}",("tid",(uintptr_t)my->boost_thread->native_handle())); + my->boost_thread->join(); + delete my; + my = nullptr; + } + return; + } + + my->done = true; + // wlog( "${s}", ("s",name()) ); + // We are quiting from our own thread... + + // break all promises, thread quit! + while( my->blocked ) + { + fc::context* cur = my->blocked; + while( cur ) + { + fc::context* n = cur->next; + // this will move the context into the ready list. + //cur->prom->set_exception( boost::copy_exception( error::thread_quit() ) ); + //cur->set_exception_on_blocking_promises( thread_quit() ); + cur->set_exception_on_blocking_promises( std::make_shared(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting")) ); + + cur = n; + } + if( my->blocked ) + { + //wlog( "still blocking... whats up with that?"); + debug( "on quit" ); + } + } + BOOST_ASSERT( my->blocked == 0 ); + //my->blocked = 0; + + for (task_base* unstarted_task : my->task_pqueue) + unstarted_task->set_exception(std::make_shared(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting"))); + my->task_pqueue.clear(); + + for (task_base* scheduled_task : my->task_sch_queue) + scheduled_task->set_exception(std::make_shared(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting"))); + my->task_sch_queue.clear(); + + + + // move all sleep tasks to ready + for( uint32_t i = 0; i < my->sleep_pqueue.size(); ++i ) + my->add_context_to_ready_list( my->sleep_pqueue[i] ); + my->sleep_pqueue.clear(); + + // move all idle tasks to ready + fc::context* cur = my->pt_head; + while( cur ) + { + fc::context* n = cur->next; + cur->next = 0; + my->add_context_to_ready_list( cur ); + cur = n; + } + + // mark all ready tasks (should be everyone)... as canceled + for (fc::context* ready_context : my->ready_heap) + ready_context->canceled = true; + + // now that we have poked all fibers... switch to the next one and + // let them all quit. + while (!my->ready_heap.empty()) + { + my->start_next_fiber(true); + my->check_for_timeouts(); + } + my->clear_free_list(); + my->cleanup_thread_specific_data(); + } + + void thread::exec() + { + if( !my->current ) + my->current = new fc::context(&fc::thread::current()); + + try + { + my->process_tasks(); + } + catch( canceled_exception& e ) + { + dlog( "thread canceled: ${e}", ("e", e.to_detail_string()) ); + } + delete my->current; + my->current = 0; + } + + bool thread::is_running()const + { + return !my->done; + } + + priority thread::current_priority()const + { + BOOST_ASSERT(my); + if( my->current ) + return my->current->prio; + return priority(); + } + + void thread::yield(bool reschedule) + { + my->check_fiber_exceptions(); + my->start_next_fiber(reschedule); + my->check_fiber_exceptions(); + } + + void thread::sleep_until( const time_point& tp ) + { + if( tp <= (time_point::now()+fc::microseconds(10000)) ) + yield(true); + my->yield_until( tp, false ); + } + + int thread::wait_any_until( std::vector&& p, const time_point& timeout) { + for( size_t i = 0; i < p.size(); ++i ) + if( p[i]->ready() ) + return i; + + if( timeout < time_point::now() ) + { + fc::stringstream ss; + for( auto i = p.begin(); i != p.end(); ++i ) + ss << (*i)->get_desc() << ", "; + + FC_THROW_EXCEPTION( timeout_exception, "${task}", ("task",ss.str()) ); + } + + if( !my->current ) + my->current = new fc::context(&fc::thread::current()); + + for( uint32_t i = 0; i < p.size(); ++i ) + my->current->add_blocking_promise(p[i].get(),false); + + // if not max timeout, added to sleep pqueue + if( timeout != time_point::maximum() ) + { + my->current->resume_time = timeout; + my->sleep_pqueue.push_back(my->current); + std::push_heap( my->sleep_pqueue.begin(), + my->sleep_pqueue.end(), + sleep_priority_less() ); + } + + my->add_to_blocked( my->current ); + my->start_next_fiber(); + + for( auto i = p.begin(); i != p.end(); ++i ) + my->current->remove_blocking_promise(i->get()); + + my->check_fiber_exceptions(); + + for( uint32_t i = 0; i < p.size(); ++i ) + if( p[i]->ready() ) + return i; + + //BOOST_THROW_EXCEPTION( wait_any_error() ); + return -1; + } + + void thread::async_task( task_base* t, const priority& p ) { + async_task( t, p, time_point::min() ); + } + + void thread::poke() { + boost::unique_lock lock(my->task_ready_mutex); + my->task_ready.notify_one(); + } + + void thread::async_task( task_base* t, const priority& p, const time_point& tp ) { + assert(my); + t->_when = tp; + // slog( "when %lld", t->_when.time_since_epoch().count() ); + // slog( "delay %lld", (tp - fc::time_point::now()).count() ); + task_base* stale_head = my->task_in_queue.load(boost::memory_order_relaxed); + do { t->_next = stale_head; + }while( !my->task_in_queue.compare_exchange_weak( stale_head, t, boost::memory_order_release ) ); + + // Because only one thread can post the 'first task', only that thread will attempt + // to aquire the lock and therefore there should be no contention on this lock except + // when *this thread is about to block on a wait condition. + if( this != ¤t() && !stale_head ) { + boost::unique_lock lock(my->task_ready_mutex); + my->task_ready.notify_one(); + } + } + + void yield() { + thread::current().yield(); + } + void usleep( const microseconds& u ) { + thread::current().sleep_until( time_point::now() + u); + } + void sleep_until( const time_point& tp ) { + thread::current().sleep_until(tp); + } + + void exec() + { + return thread::current().exec(); + } + + int wait_any( std::vector&& v, const microseconds& timeout_us ) + { + return thread::current().wait_any_until( fc::move(v), time_point::now() + timeout_us ); + } + + int wait_any_until( std::vector&& v, const time_point& tp ) + { + return thread::current().wait_any_until( fc::move(v), tp ); + } + + void thread::wait_until( promise_base::ptr&& p, const time_point& timeout ) + { + if( p->ready() ) + return; + + if( timeout < time_point::now() ) + FC_THROW_EXCEPTION( timeout_exception, "${task}", ("task", p->get_desc()) ); + + if( !my->current ) + my->current = new fc::context(&fc::thread::current()); + + //slog( " %1% blocking on %2%", my->current, p.get() ); + my->current->add_blocking_promise(p.get(), true); + + // if not max timeout, added to sleep pqueue + if( timeout != time_point::maximum() ) + { + my->current->resume_time = timeout; + my->sleep_pqueue.push_back(my->current); + std::push_heap( my->sleep_pqueue.begin(), + my->sleep_pqueue.end(), + sleep_priority_less() ); + } + + // elog( "blocking %1%", my->current ); + my->add_to_blocked( my->current ); + // my->debug("swtiching fibers..." ); + + + my->start_next_fiber(); + // slog( "resuming %1%", my->current ); + + //slog( " %1% unblocking blocking on %2%", my->current, p.get() ); + my->current->remove_blocking_promise(p.get()); + + my->check_fiber_exceptions(); + } + + void thread::notify( const promise_base::ptr& p ) + { + //slog( "this %p my %p", this, my ); + BOOST_ASSERT(p->ready()); + if( !is_current() ) + { + this->async( [=](){ notify(p); }, "notify", priority::max() ); + return; + } + // TODO: store a list of blocked contexts with the promise + // to accelerate the lookup.... unless it introduces contention... + + // iterate over all blocked contexts + + + fc::context* cur_blocked = my->blocked; + fc::context* prev_blocked = 0; + while( cur_blocked ) + { + // if the blocked context is waiting on this promise + if( cur_blocked->try_unblock( p.get() ) ) + { + // remove it from the blocked list. + + // remove this context from the sleep queue... + for( uint32_t i = 0; i < my->sleep_pqueue.size(); ++i ) + { + if( my->sleep_pqueue[i] == cur_blocked ) + { + my->sleep_pqueue[i]->blocking_prom.clear(); + my->sleep_pqueue[i] = my->sleep_pqueue.back(); + my->sleep_pqueue.pop_back(); + std::make_heap( my->sleep_pqueue.begin(),my->sleep_pqueue.end(), sleep_priority_less() ); + break; + } + } + auto cur = cur_blocked; + if( prev_blocked ) + { + prev_blocked->next_blocked = cur_blocked->next_blocked; + cur_blocked = prev_blocked->next_blocked; + } + else + { + my->blocked = cur_blocked->next_blocked; + cur_blocked = my->blocked; + } + cur->next_blocked = 0; + my->add_context_to_ready_list( cur ); + } + else + { // goto the next blocked task + prev_blocked = cur_blocked; + cur_blocked = cur_blocked->next_blocked; + } + } + } + + bool thread::is_current()const + { + return this == ¤t(); + } + + void thread::notify_task_has_been_canceled() + { + async( [=](){ my->notify_task_has_been_canceled(); }, "notify_task_has_been_canceled", priority::max() ); + } + + void thread::unblock(fc::context* c) + { + my->unblock(c); + } + + +#ifdef _MSC_VER + /* support for providing a structured exception handler for async tasks */ + namespace detail + { + unhandled_exception_filter_type unhandled_structured_exception_filter = nullptr; + } + void set_unhandled_structured_exception_filter(unhandled_exception_filter_type new_filter) + { + detail::unhandled_structured_exception_filter = new_filter; + } + unhandled_exception_filter_type get_unhandled_structured_exception_filter() + { + return detail::unhandled_structured_exception_filter; + } +#endif // _MSC_VER +} // end namespace fc diff --git a/src/thread/thread_d.hpp b/src/thread/thread_d.hpp new file mode 100644 index 000000000..941b2fa01 --- /dev/null +++ b/src/thread/thread_d.hpp @@ -0,0 +1,782 @@ +#include +#include +#include +#include +#include "context.hpp" +#include +#include +#include +#include +//#include + +namespace fc { + struct sleep_priority_less { + bool operator()( const context::ptr& a, const context::ptr& b ) { + return a->resume_time > b->resume_time; + } + }; + class thread_d { + + public: + thread_d(fc::thread& s) + :self(s), boost_thread(0), + task_in_queue(0), + next_posted_num(1), + done(false), + current(0), + pt_head(0), + blocked(0), + next_unused_task_storage_slot(0) +#ifndef NDEBUG + ,non_preemptable_scope_count(0) +#endif + { + static boost::atomic cnt(0); + name = fc::string("th_") + char('a'+cnt++); +// printf("thread=%p\n",this); + } + + ~thread_d() + { + delete current; + fc::context* temp; + for (fc::context* ready_context : ready_heap) + delete ready_context; + ready_heap.clear(); + while (blocked) + { + temp = blocked->next; + delete blocked; + blocked = temp; + } + /* + while (pt_head) + { + temp = pt_head->next; + delete pt_head; + pt_head = temp; + } + */ + //ilog(""); + if (boost_thread) + { + boost_thread->detach(); + delete boost_thread; + } + } + + fc::thread& self; + boost::thread* boost_thread; + stack_allocator stack_alloc; + boost::condition_variable task_ready; + boost::mutex task_ready_mutex; + + boost::atomic task_in_queue; + std::vector task_pqueue; // heap of tasks that have never started, ordered by proirity & scheduling time + uint64_t next_posted_num; // each task or context gets assigned a number in the order it is ready to execute, tracked here + std::vector task_sch_queue; // heap of tasks that have never started but are scheduled for a time in the future, ordered by the time they should be run + std::vector sleep_pqueue; // heap of running tasks that have sleeped, ordered by the time they should resume + std::vector free_list; // list of unused contexts that are ready for deletion + + bool done; + fc::string name; + fc::context* current; // the currently-executing task in this thread + + fc::context* pt_head; // list of contexts that can be reused for new tasks + + std::vector ready_heap; // priority heap of contexts that are ready to run + + fc::context* blocked; // linked list of contexts (using 'next_blocked') blocked on promises via wait() + + // values for thread specific data objects for this thread + std::vector thread_specific_data; + // values for task_specific data for code executing on a thread that's + // not a task launched by async (usually the default task on the main + // thread in a process) + std::vector non_task_specific_data; + unsigned next_unused_task_storage_slot; + +#ifndef NDEBUG + unsigned non_preemptable_scope_count; +#endif + +#if 0 + void debug( const fc::string& s ) { + return; + //boost::unique_lock lock(log_mutex()); + + fc::cerr<<"--------------------- "<cur_task ) fc::cerr<<'('<cur_task->get_desc()<<')'; + fc::cerr<<" ---------------------------\n"; + fc::cerr<<" Ready\n"; + fc::context* c = ready_head; + while( c ) { + fc::cerr<<" "<cur_task ) fc::cerr<<'('<cur_task->get_desc()<<')'; + fc::context* p = c->caller_context; + while( p ) { + fc::cerr<<" -> "<caller_context; + } + fc::cerr<<"\n"; + c = c->next; + } + fc::cerr<<" Blocked\n"; + c = blocked; + while( c ) { + fc::cerr<<" ctx: "<< c; + if( c->cur_task ) fc::cerr<<'('<cur_task->get_desc()<<')'; + fc::cerr << " blocked on prom: "; + for( uint32_t i = 0; i < c->blocking_prom.size(); ++i ) { + fc::cerr<blocking_prom[i].prom<<'('<blocking_prom[i].prom->get_desc()<<')'; + if( i + 1 < c->blocking_prom.size() ) { + fc::cerr<<","; + } + } + + fc::context* p = c->caller_context; + while( p ) { + fc::cerr<<" -> "<caller_context; + } + fc::cerr<<"\n"; + c = c->next_blocked; + } + fc::cerr<<"-------------------------------------------------\n"; + } +#endif + // insert at from of blocked linked list + inline void add_to_blocked( fc::context* c ) + { + c->next_blocked = blocked; + blocked = c; + } + + void pt_push_back(fc::context* c) + { + c->next = pt_head; + pt_head = c; + /* + fc::context* n = pt_head; + int i = 0; + while( n ) { + ++i; + n = n->next; + } + wlog( "idle context...%2% %1%", c, i ); + */ + } + + fc::context::ptr ready_pop_front() + { + fc::context* highest_priority_context = ready_heap.front(); + std::pop_heap(ready_heap.begin(), ready_heap.end(), task_priority_less()); + ready_heap.pop_back(); + return highest_priority_context; + } + + void add_context_to_ready_list(context* context_to_add, bool at_end = false) + { + + context_to_add->context_posted_num = next_posted_num++; + ready_heap.push_back(context_to_add); + std::push_heap(ready_heap.begin(), ready_heap.end(), task_priority_less()); + } + + struct task_priority_less + { + bool operator()(const task_base* a, const task_base* b) const + { + return a->_prio.value < b->_prio.value ? true : + (a->_prio.value > b->_prio.value ? false : + a->_posted_num > b->_posted_num); + } + bool operator()(const task_base* a, const context* b) const + { + return a->_prio.value < b->prio.value ? true : + (a->_prio.value > b->prio.value ? false : + a->_posted_num > b->context_posted_num); + } + bool operator()(const context* a, const task_base* b) const + { + return a->prio.value < b->_prio.value ? true : + (a->prio.value > b->_prio.value ? false : + a->context_posted_num > b->_posted_num); + } + bool operator()(const context* a, const context* b) const + { + return a->prio.value < b->prio.value ? true : + (a->prio.value > b->prio.value ? false : + a->context_posted_num > b->context_posted_num); + } + }; + + struct task_when_less + { + bool operator()( task_base* a, task_base* b ) + { + return a->_when > b->_when; + } + }; + + void enqueue( task_base* t ) + { + time_point now = time_point::now(); + task_base* cur = t; + + // the linked list of tasks passed to enqueue is in the reverse order of + // what you'd expect -- the first task to be scheduled is at the end of + // the list. We'll rectify the ordering by assigning the _posted_num + // in reverse order + unsigned num_ready_tasks = 0; + while (cur) + { + if (cur->_when <= now) + ++num_ready_tasks; + cur = cur->_next; + } + + cur = t; + next_posted_num += num_ready_tasks; + unsigned tasks_posted = 0; + while (cur) + { + if (cur->_when > now) + { + task_sch_queue.push_back(cur); + std::push_heap(task_sch_queue.begin(), + task_sch_queue.end(), task_when_less()); + } + else + { + cur->_posted_num = next_posted_num - (++tasks_posted); + task_pqueue.push_back(cur); + std::push_heap(task_pqueue.begin(), + task_pqueue.end(), task_priority_less()); + BOOST_ASSERT(this == thread::current().my); + } + cur = cur->_next; + } + } + + void move_newly_scheduled_tasks_to_task_pqueue() + { + BOOST_ASSERT(this == thread::current().my); + + // first, if there are any new tasks on 'task_in_queue', which is tasks that + // have been just been async or scheduled, but we haven't processed them. + // move them into the task_sch_queue or task_pqueue, as appropriate + + //DLN: changed from memory_order_consume for boost 1.55. + //This appears to be safest replacement for now, maybe + //can be changed to relaxed later, but needs analysis. + task_base* pending_list = task_in_queue.exchange(0, boost::memory_order_seq_cst); + if (pending_list) + enqueue(pending_list); + + // second, walk through task_sch_queue and move any scheduled tasks that are now + // able to run (because their scheduled time has arrived) to task_pqueue + + while (!task_sch_queue.empty() && + task_sch_queue.front()->_when <= time_point::now()) + { + task_base* ready_task = task_sch_queue.front(); + std::pop_heap(task_sch_queue.begin(), task_sch_queue.end(), task_when_less()); + task_sch_queue.pop_back(); + + ready_task->_posted_num = next_posted_num++; + task_pqueue.push_back(ready_task); + std::push_heap(task_pqueue.begin(), task_pqueue.end(), task_priority_less()); + } + } + + task_base* dequeue() + { + // get a new task + BOOST_ASSERT( this == thread::current().my ); + + assert(!task_pqueue.empty()); + task_base* p = task_pqueue.front(); + std::pop_heap(task_pqueue.begin(), task_pqueue.end(), task_priority_less() ); + task_pqueue.pop_back(); + return p; + } + + bool process_canceled_tasks() + { + bool canceled_task = false; + for( auto task_itr = task_sch_queue.begin(); + task_itr != task_sch_queue.end(); + ) + { + if( (*task_itr)->canceled() ) + { + (*task_itr)->run(); + (*task_itr)->release(); + task_itr = task_sch_queue.erase(task_itr); + canceled_task = true; + continue; + } + ++task_itr; + } + + if( canceled_task ) + std::make_heap( task_sch_queue.begin(), task_sch_queue.end(), task_when_less() ); + + return canceled_task; + } + + /** + * This should be before or after a context switch to + * detect quit/cancel operations and throw an exception. + */ + void check_fiber_exceptions() + { + if( current && current->canceled ) + { +#ifdef NDEBUG + FC_THROW_EXCEPTION( canceled_exception, "" ); +#else + FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: ${reason}", ("reason", current->cancellation_reason ? current->cancellation_reason : "[none given]")); +#endif + } + else if( done ) + { + ilog( "throwing canceled exception" ); + FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: thread quitting" ); + // BOOST_THROW_EXCEPTION( thread_quit() ); + } + } + + /** + * Find the next available context and switch to it. + * If none are available then create a new context and + * have it wait for something to do. + */ + bool start_next_fiber( bool reschedule = false ) + { + /* If this assert fires, it means you are executing an operation that is causing + * the current task to yield, but there is a ASSERT_TASK_NOT_PREEMPTED() in effect + * (somewhere up the stack) */ + assert(non_preemptable_scope_count == 0); + + /* If this assert fires, it means you are causing the current task to yield while + * in the middle of handling an exception. The boost::context library's behavior + * is not well-defined in this case, and this has the potential to corrupt the + * exception stack, often resulting in a crash very soon after this */ + /* NB: At least on Win64, this only catches a yield while in the body of + * a catch block; it fails to catch a yield while unwinding the stack, which + * is probably just as likely to cause crashes */ + assert(std::current_exception() == std::exception_ptr()); + + check_for_timeouts(); + if( !current ) + current = new fc::context( &fc::thread::current() ); + + priority original_priority = current->prio; + + // check to see if any other contexts are ready + if (!ready_heap.empty()) + { + fc::context* next = ready_pop_front(); + if (next == current) + { + // elog( "next == current... something went wrong" ); + assert(next != current); + return false; + } + BOOST_ASSERT(next != current); + + // jump to next context, saving current context + fc::context* prev = current; + current = next; + if (reschedule) + { + current->prio = priority::_internal__priority_for_short_sleeps(); + add_context_to_ready_list(prev, true); + } + // slog( "jump to %p from %p", next, prev ); + // fc_dlog( logger::get("fc_context"), "from ${from} to ${to}", ( "from", int64_t(prev) )( "to", int64_t(next) ) ); +#if BOOST_VERSION >= 105600 + bc::jump_fcontext( &prev->my_context, next->my_context, 0 ); +#elif BOOST_VERSION >= 105300 + bc::jump_fcontext( prev->my_context, next->my_context, 0 ); +#else + bc::jump_fcontext( &prev->my_context, &next->my_context, 0 ); +#endif + BOOST_ASSERT( current ); + BOOST_ASSERT( current == prev ); + //current = prev; + } + else + { + // all contexts are blocked, create a new context + // that will process posted tasks... + fc::context* prev = current; + + fc::context* next = nullptr; + if( pt_head ) + { + // grab cached context + next = pt_head; + pt_head = pt_head->next; + next->next = 0; + next->reinitialize(); + } + else + { + // create new context. + next = new fc::context( &thread_d::start_process_tasks, stack_alloc, + &fc::thread::current() ); + } + + current = next; + if( reschedule ) + { + current->prio = priority::_internal__priority_for_short_sleeps(); + add_context_to_ready_list(prev, true); + } + + // slog( "jump to %p from %p", next, prev ); + // fc_dlog( logger::get("fc_context"), "from ${from} to ${to}", ( "from", int64_t(prev) )( "to", int64_t(next) ) ); +#if BOOST_VERSION >= 105600 + bc::jump_fcontext( &prev->my_context, next->my_context, (intptr_t)this ); +#elif BOOST_VERSION >= 105300 + bc::jump_fcontext( prev->my_context, next->my_context, (intptr_t)this ); +#else + bc::jump_fcontext( &prev->my_context, &next->my_context, (intptr_t)this ); +#endif + BOOST_ASSERT( current ); + BOOST_ASSERT( current == prev ); + //current = prev; + } + + if (reschedule) + current->prio = original_priority; + + if( current->canceled ) + { + //current->canceled = false; +#ifdef NDEBUG + FC_THROW_EXCEPTION( canceled_exception, "" ); +#else + FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: ${reason}", ("reason", current->cancellation_reason ? current->cancellation_reason : "[none given]")); +#endif + } + + return true; + } + + static void start_process_tasks( intptr_t my ) + { + thread_d* self = (thread_d*)my; + try + { + self->process_tasks(); + } + catch ( canceled_exception& ) { /* allowed exception */ } + catch ( ... ) + { + elog( "fiber ${name} exited with uncaught exception: ${e}", ("e",fc::except_str())("name", self->name) ); + // assert( !"fiber exited with uncaught exception" ); + //TODO replace errror fc::cerr<<"fiber exited with uncaught exception:\n "<< + // boost::current_exception_diagnostic_information() <free_list.push_back(self->current); + self->start_next_fiber( false ); + } + + void run_next_task() + { + task_base* next = dequeue(); + + next->_set_active_context( current ); + current->cur_task = next; + next->run(); + current->cur_task = 0; + next->_set_active_context(0); + next->release(); + current->reinitialize(); + } + + bool has_next_task() + { + if( task_pqueue.size() || + (task_sch_queue.size() && task_sch_queue.front()->_when <= time_point::now()) || + task_in_queue.load( boost::memory_order_relaxed ) ) + return true; + return false; + } + + void clear_free_list() + { + for( uint32_t i = 0; i < free_list.size(); ++i ) + delete free_list[i]; + free_list.clear(); + } + + void process_tasks() + { + while( !done || blocked ) + { + // move all new tasks to the task_pqueue + move_newly_scheduled_tasks_to_task_pqueue(); + + // move all now-ready sleeping tasks to the ready list + check_for_timeouts(); + + if (!task_pqueue.empty()) + { + if (!ready_heap.empty()) + { + // a new task and an existing task are both ready to go + if (task_priority_less()(task_pqueue.front(), ready_heap.front())) + { + // run the existing task first + pt_push_back(current); + start_next_fiber(false); + continue; + } + } + + // if we made it here, either there's no ready context, or the ready context is + // scheduled after the ready task, so we should run the task first + run_next_task(); + continue; + } + + // if I have something else to do other than + // process tasks... do it. + if (!ready_heap.empty()) + { + pt_push_back( current ); + start_next_fiber(false); + continue; + } + + if( process_canceled_tasks() ) + continue; + + clear_free_list(); + + { // lock scope + boost::unique_lock lock(task_ready_mutex); + if( has_next_task() ) + continue; + time_point timeout_time = check_for_timeouts(); + + if( done ) + return; + if( timeout_time == time_point::maximum() ) + task_ready.wait( lock ); + else if( timeout_time != time_point::min() ) + { + // there may be tasks that have been canceled we should filter them out now + // rather than waiting... + + + /* This bit is kind of sloppy -- this wait was originally implemented as a wait + * with respect to boost::chrono::system_clock. This behaved rather comically + * if you were to do a: + * fc::usleep(fc::seconds(60)); + * and then set your system's clock back a month, it would sleep for a month + * plus a minute before waking back up (this happened on Linux, it seems + * Windows' behavior in this case was less unexpected). + * + * Boost Chrono's steady_clock will always increase monotonically so it will + * avoid this behavior. + * + * Right now we don't really have a way to distinguish when a timeout_time is coming + * from a function that takes a relative time like fc::usleep() vs something + * that takes an absolute time like fc::promise::wait_until(), so we can't always + * do the right thing here. + */ + task_ready.wait_until( lock, boost::chrono::steady_clock::now() + + boost::chrono::microseconds(timeout_time.time_since_epoch().count() - time_point::now().time_since_epoch().count()) ); + } + } + } + } + /** + * Return system_clock::time_point::min() if tasks have timed out + * Retunn system_clock::time_point::max() if there are no scheduled tasks + * Return the time the next task needs to be run if there is anything scheduled. + */ + time_point check_for_timeouts() + { + if( !sleep_pqueue.size() && !task_sch_queue.size() ) + { + // ilog( "no timeouts ready" ); + return time_point::maximum(); + } + + time_point next = time_point::maximum(); + if( !sleep_pqueue.empty() && next > sleep_pqueue.front()->resume_time ) + next = sleep_pqueue.front()->resume_time; + if( !task_sch_queue.empty() && next > task_sch_queue.front()->_when ) + next = task_sch_queue.front()->_when; + + time_point now = time_point::now(); + if( now < next ) + return next; + + // move all expired sleeping tasks to the ready queue + while( sleep_pqueue.size() && sleep_pqueue.front()->resume_time < now ) + { + fc::context::ptr c = sleep_pqueue.front(); + std::pop_heap(sleep_pqueue.begin(), sleep_pqueue.end(), sleep_priority_less() ); + // ilog( "sleep pop back..." ); + sleep_pqueue.pop_back(); + + if( c->blocking_prom.size() ) + { + // ilog( "timeout blocking prom" ); + c->timeout_blocking_promises(); + } + else + { + // ilog( "..." ); + // ilog( "ready_push_front" ); + if (c != current) + add_context_to_ready_list(c); + } + } + return time_point::min(); + } + + void unblock( fc::context* c ) + { + if( fc::thread::current().my != this ) + { + self.async( [=](){ unblock(c); }, "thread_d::unblock" ); + return; + } + + if (c != current) + add_context_to_ready_list(c); + } + + void yield_until( const time_point& tp, bool reschedule ) { + check_fiber_exceptions(); + + if( tp <= (time_point::now()+fc::microseconds(10000)) ) + return; + + FC_ASSERT(std::current_exception() == std::exception_ptr(), + "Attempting to yield while processing an exception"); + + if( !current ) + current = new fc::context(&fc::thread::current()); + + current->resume_time = tp; + current->clear_blocking_promises(); + + sleep_pqueue.push_back(current); + std::push_heap( sleep_pqueue.begin(), + sleep_pqueue.end(), sleep_priority_less() ); + + start_next_fiber(reschedule); + + // clear current context from sleep queue... + for( uint32_t i = 0; i < sleep_pqueue.size(); ++i ) + { + if( sleep_pqueue[i] == current ) + { + sleep_pqueue[i] = sleep_pqueue.back(); + sleep_pqueue.pop_back(); + std::make_heap( sleep_pqueue.begin(), + sleep_pqueue.end(), sleep_priority_less() ); + break; + } + } + + current->resume_time = time_point::maximum(); + check_fiber_exceptions(); + } + + void wait( const promise_base::ptr& p, const time_point& timeout ) { + if( p->ready() ) + return; + + FC_ASSERT(std::current_exception() == std::exception_ptr(), + "Attempting to yield while processing an exception"); + + if( timeout < time_point::now() ) + FC_THROW_EXCEPTION( timeout_exception, "" ); + + if( !current ) + current = new fc::context(&fc::thread::current()); + + // slog( " %1% blocking on %2%", current, p.get() ); + current->add_blocking_promise(p.get(),true); + + // if not max timeout, added to sleep pqueue + if( timeout != time_point::maximum() ) + { + current->resume_time = timeout; + sleep_pqueue.push_back(current); + std::push_heap( sleep_pqueue.begin(), + sleep_pqueue.end(), + sleep_priority_less() ); + } + + // elog( "blocking %1%", current ); + add_to_blocked( current ); + // debug("swtiching fibers..." ); + + + start_next_fiber(); + // slog( "resuming %1%", current ); + + // slog( " %1% unblocking blocking on %2%", current, p.get() ); + current->remove_blocking_promise(p.get()); + + check_fiber_exceptions(); + } + + void cleanup_thread_specific_data() + { + for (auto iter = non_task_specific_data.begin(); iter != non_task_specific_data.end(); ++iter) + if (iter->cleanup) + iter->cleanup(iter->value); + + for (auto iter = thread_specific_data.begin(); iter != thread_specific_data.end(); ++iter) + if (iter->cleanup) + iter->cleanup(iter->value); + } + + void notify_task_has_been_canceled() + { + for (fc::context** iter = &blocked; *iter;) + { + if ((*iter)->canceled) + { + fc::context* next_blocked = (*iter)->next_blocked; + (*iter)->next_blocked = nullptr; + add_context_to_ready_list(*iter); + *iter = next_blocked; + continue; + } + iter = &(*iter)->next_blocked; + } + + bool task_removed_from_sleep_pqueue = false; + for (auto sleep_iter = sleep_pqueue.begin(); sleep_iter != sleep_pqueue.end();) + { + if ((*sleep_iter)->canceled) + { + bool already_on_ready_list = std::find(ready_heap.begin(), ready_heap.end(), + *sleep_iter) != ready_heap.end(); + if (!already_on_ready_list) + add_context_to_ready_list(*sleep_iter); + sleep_iter = sleep_pqueue.erase(sleep_iter); + task_removed_from_sleep_pqueue = true; + } + else + ++sleep_iter; + } + if (task_removed_from_sleep_pqueue) + std::make_heap(sleep_pqueue.begin(), sleep_pqueue.end(), sleep_priority_less()); + } + }; +} // namespace fc diff --git a/src/thread/thread_specific.cpp b/src/thread/thread_specific.cpp new file mode 100644 index 000000000..a91236d1a --- /dev/null +++ b/src/thread/thread_specific.cpp @@ -0,0 +1,65 @@ +#include +#include +#include "thread_d.hpp" +#include + +namespace fc +{ + namespace detail + { + boost::atomic thread_specific_slot_counter; + unsigned get_next_unused_thread_storage_slot() + { + return thread_specific_slot_counter.fetch_add(1); + } + + void* get_specific_data(std::vector *specific_data, unsigned slot) + { + return slot < specific_data->size() ? + (*specific_data)[slot].value : nullptr; + } + void set_specific_data(std::vector *specific_data, unsigned slot, void* new_value, void(*cleanup)(void*)) + { + if (slot + 1 > specific_data->size()) + specific_data->resize(slot + 1); + (*specific_data)[slot] = std::move(detail::specific_data_info(new_value, cleanup)); + } + + void* get_thread_specific_data(unsigned slot) + { + return get_specific_data(&thread::current().my->thread_specific_data, slot); + } + void set_thread_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)) + { + return set_specific_data(&thread::current().my->thread_specific_data, slot, new_value, cleanup); + } + + unsigned get_next_unused_task_storage_slot() + { + return thread::current().my->next_unused_task_storage_slot++; + } + void* get_task_specific_data(unsigned slot) + { + context* current_context = thread::current().my->current; + if (!current_context || + !current_context->cur_task) + return get_specific_data(&thread::current().my->non_task_specific_data, slot); + if (current_context->cur_task->_task_specific_data) + return get_specific_data(current_context->cur_task->_task_specific_data, slot); + return nullptr; + } + void set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*)) + { + context* current_context = thread::current().my->current; + if (!current_context || + !current_context->cur_task) + set_specific_data(&thread::current().my->non_task_specific_data, slot, new_value, cleanup); + else + { + if (!current_context->cur_task->_task_specific_data) + current_context->cur_task->_task_specific_data = new std::vector; + set_specific_data(current_context->cur_task->_task_specific_data, slot, new_value, cleanup); + } + } + } +} // end namespace fc \ No newline at end of file diff --git a/src/thread_d.hpp b/src/thread_d.hpp deleted file mode 100644 index 5c4d8522f..000000000 --- a/src/thread_d.hpp +++ /dev/null @@ -1,465 +0,0 @@ -#include -#include -#include - -#include -#include "context.hpp" -#include -#include -#include -#include -#include -#include - -namespace fc { - struct sleep_priority_less { - bool operator()( const context::ptr& a, const context::ptr& b ) { - return a->resume_time > b->resume_time; - } - }; - class thread_d { - public: - thread_d(fc::thread& s) - :self(s), boost_thread(0), - task_in_queue(0), - done(false), - current(0), - pt_head(0), - ready_head(0), - ready_tail(0), - blocked(0) - { - static char cnt = 0; - name = fc::string("th_") + char('a'+cnt); - cnt++; - } - ~thread_d(){ - fc_ilog( logger::get("fc_context"), "thread ${name} exited}", ( "name", name) ); - } - fc::thread& self; - boost::thread* boost_thread; - bc::stack_allocator stack_alloc; - boost::condition_variable task_ready; - boost::mutex task_ready_mutex; - - boost::atomic task_in_queue; - std::vector task_pqueue; - std::vector task_sch_queue; - std::vector sleep_pqueue; - std::vector free_list; - - bool done; - fc::string name; - fc::context* current; - - fc::context* pt_head; - - fc::context* ready_head; - fc::context* ready_tail; - - fc::context* blocked; - - - - void debug( const fc::string& s ) { - return; - boost::unique_lock lock(log_mutex()); - - std::cerr<<"--------------------- "<cur_task ) std::cerr<<'('<cur_task->get_desc()<<')'; - std::cerr<<" ---------------------------\n"; - std::cerr<<" Ready\n"; - fc::context* c = ready_head; - while( c ) { - std::cerr<<" "<cur_task ) std::cerr<<'('<cur_task->get_desc()<<')'; - fc::context* p = c->caller_context; - while( p ) { - std::cerr<<" -> "<caller_context; - } - std::cerr<<"\n"; - c = c->next; - } - std::cerr<<" Blocked\n"; - c = blocked; - while( c ) { - std::cerr<<" ctx: "<< c; - if( c->cur_task ) std::cerr<<'('<cur_task->get_desc()<<')'; - std::cerr << " blocked on prom: "; - for( uint32_t i = 0; i < c->blocking_prom.size(); ++i ) { - std::cerr<blocking_prom[i].prom<<'('<blocking_prom[i].prom->get_desc()<<')'; - if( i + 1 < c->blocking_prom.size() ) { - std::cerr<<","; - } - } - - fc::context* p = c->caller_context; - while( p ) { - std::cerr<<" -> "<caller_context; - } - std::cerr<<"\n"; - c = c->next_blocked; - } - std::cerr<<"-------------------------------------------------\n"; - } - - // insert at from of blocked linked list - inline void add_to_blocked( fc::context* c ) { - c->next_blocked = blocked; - blocked = c; - } - - void pt_push_back(fc::context* c) { - c->next = pt_head; - pt_head = c; - /* - fc::context* n = pt_head; - int i = 0; - while( n ) { - ++i; - n = n->next; - } - wlog( "idle context...%2% %1%", c, i ); - */ - } - fc::context::ptr ready_pop_front() { - fc::context::ptr tmp = nullptr; - if( ready_head ) { - tmp = ready_head; - ready_head = tmp->next; - if( !ready_head ) - ready_tail = nullptr; - tmp->next = nullptr; - } - return tmp; - } - void ready_push_front( const fc::context::ptr& c ) { - BOOST_ASSERT( c->next == nullptr ); - BOOST_ASSERT( c != current ); - //if( c == current ) wlog( "pushing current to ready??" ); - c->next = ready_head; - ready_head = c; - if( !ready_tail ) - ready_tail = c; - } - void ready_push_back( const fc::context::ptr& c ) { - BOOST_ASSERT( c->next == nullptr ); - BOOST_ASSERT( c != current ); - //if( c == current ) wlog( "pushing current to ready??" ); - c->next = 0; - if( ready_tail ) { - ready_tail->next = c; - } else { - assert( !ready_head ); - ready_head = c; - } - ready_tail = c; - } - struct task_priority_less { - bool operator()( task_base* a, task_base* b ) { - return a->_prio.value < b->_prio.value ? true : (a->_prio.value > b->_prio.value ? false : a->_posted_num > b->_posted_num ); - } - }; - struct task_when_less { - bool operator()( task_base* a, task_base* b ) { - return a->_when > b->_when; - } - }; - - void enqueue( task_base* t ) { - time_point now = time_point::now(); - task_base* cur = t; - while( cur ) { - if( cur->_when > now ) { - task_sch_queue.push_back(cur); - std::push_heap( task_sch_queue.begin(), - task_sch_queue.end(), task_when_less() ); - } else { - task_pqueue.push_back(cur); - BOOST_ASSERT( this == thread::current().my ); - std::push_heap( task_pqueue.begin(), - task_pqueue.end(), task_priority_less() ); - } - cur = cur->_next; - } - } - task_base* dequeue() { - // get a new task - BOOST_ASSERT( this == thread::current().my ); - - task_base* pending = 0; - - pending = task_in_queue.exchange(0,boost::memory_order_consume); - if( pending ) { enqueue( pending ); } - - task_base* p(0); - if( task_sch_queue.size() ) { - if( task_sch_queue.front()->_when <= time_point::now() ) { - p = task_sch_queue.front(); - std::pop_heap(task_sch_queue.begin(), task_sch_queue.end(), task_when_less() ); - task_sch_queue.pop_back(); - return p; - } - } - if( task_pqueue.size() ) { - p = task_pqueue.front(); - std::pop_heap(task_pqueue.begin(), task_pqueue.end(), task_priority_less() ); - task_pqueue.pop_back(); - } - return p; - } - - /** - * This should be before or after a context switch to - * detect quit/cancel operations and throw an exception. - */ - void check_fiber_exceptions() { - if( current && current->canceled ) { - BOOST_THROW_EXCEPTION( task_canceled() ); - } else if( done ) { - BOOST_THROW_EXCEPTION( thread_quit() ); - } - } - - /** - * Find the next available context and switch to it. - * If none are available then create a new context and - * have it wait for something to do. - */ - bool start_next_fiber( bool reschedule = false ) { - check_for_timeouts(); - if( !current ) current = new fc::context( &fc::thread::current() ); - - // check to see if any other contexts are ready - if( ready_head ) { - fc::context* next = ready_pop_front(); - if( next == current ) { - elog( "next == current... something went wrong" ); - return false; - } - BOOST_ASSERT( next != current ); - - // jump to next context, saving current context - fc::context* prev = current; - current = next; - if( reschedule ) ready_push_back(prev); - // slog( "jump to %p from %p", next, prev ); - fc_dlog( logger::get("fc_context"), "from ${from} to ${to}", ( "from", int64_t(prev) )( "to", int64_t(next) ) ); - bc::jump_fcontext( &prev->my_context, &next->my_context, 0 ); - BOOST_ASSERT( current ); - BOOST_ASSERT( current == prev ); - //current = prev; - } else { // all contexts are blocked, create a new context - // that will process posted tasks... - fc::context* prev = current; - - fc::context* next = nullptr; - if( pt_head ) { // grab cached context - next = pt_head; - pt_head = pt_head->next; - next->next = 0; - } else { // create new context. - next = new fc::context( &thread_d::start_process_tasks, stack_alloc, - &fc::thread::current() ); - } - - current = next; - if( reschedule ) ready_push_back(prev); - - // slog( "jump to %p from %p", next, prev ); - fc_dlog( logger::get("fc_context"), "from ${from} to ${to}", ( "from", int64_t(prev) )( "to", int64_t(next) ) ); - bc::jump_fcontext( &prev->my_context, &next->my_context, (intptr_t)this ); - BOOST_ASSERT( current ); - BOOST_ASSERT( current == prev ); - //current = prev; - } - - if( current->canceled ) - BOOST_THROW_EXCEPTION( task_canceled() ); - - return true; - } - - static void start_process_tasks( intptr_t my ) { - thread_d* self = (thread_d*)my; - try { - self->process_tasks(); - } catch ( ... ) { - std::cerr<<"fiber exited with uncaught exception:\n "<< - boost::current_exception_diagnostic_information() <free_list.push_back(self->current); - self->start_next_fiber( false ); - } - - bool run_next_task() { - check_for_timeouts(); - task_base* next = dequeue(); - if( next ) { - next->_set_active_context( current ); - current->cur_task = next; - next->run(); - current->cur_task = 0; - next->_set_active_context(0); - next->release(); - return true; - } - return false; - } - bool has_next_task() { - if( task_pqueue.size() || - (task_sch_queue.size() && task_sch_queue.front()->_when <= time_point::now()) || - task_in_queue.load( boost::memory_order_relaxed ) ) - return true; - return false; - } - void clear_free_list() { - for( uint32_t i = 0; i < free_list.size(); ++i ) { - delete free_list[i]; - } - free_list.clear(); - } - void process_tasks() { - while( !done || blocked ) { - if( run_next_task() ) continue; - - // if I have something else to do other than - // process tasks... do it. - if( ready_head ) { - pt_push_back( current ); - start_next_fiber(false); - continue; - } - - clear_free_list(); - - { // lock scope - boost::unique_lock lock(task_ready_mutex); - if( has_next_task() ) continue; - time_point timeout_time = check_for_timeouts(); - - if( done ) return; - if( timeout_time == time_point::max() ) { - task_ready.wait( lock ); - } else if( timeout_time != time_point::min() ) { - task_ready.wait_until( lock, boost::chrono::system_clock::time_point() + - boost::chrono::microseconds(timeout_time.time_since_epoch().count()) ); - } - } - } - } - /** - * Return system_clock::time_point::min() if tasks have timed out - * Retunn system_clock::time_point::max() if there are no scheduled tasks - * Return the time the next task needs to be run if there is anything scheduled. - */ - time_point check_for_timeouts() { - if( !sleep_pqueue.size() && !task_sch_queue.size() ) { - return time_point::max(); - } - - - time_point next = time_point::max(); - if( task_sch_queue.size() && next > task_sch_queue.front()->_when ) - next = task_sch_queue.front()->_when; - if( sleep_pqueue.size() && next > sleep_pqueue.front()->resume_time ) - next = sleep_pqueue.front()->resume_time; - - time_point now = time_point::now(); - if( now < next ) { return next; } - - // move all expired sleeping tasks to the ready queue - while( sleep_pqueue.size() && sleep_pqueue.front()->resume_time < now ) { - fc::context::ptr c = sleep_pqueue.front(); - std::pop_heap(sleep_pqueue.begin(), sleep_pqueue.end(), sleep_priority_less() ); - sleep_pqueue.pop_back(); - - if( c->blocking_prom.size() ) { - c->timeout_blocking_promises(); - } - else { - if( c != current ) ready_push_front( c ); - } - } - return time_point::min(); - } - - void unblock( fc::context* c ) { - if( fc::thread::current().my != this ) { - async( [=](){ unblock(c); } ); - return; - } - if( c != current ) ready_push_front(c); - } - void yield_until( const time_point& tp, bool reschedule ) { - check_fiber_exceptions(); - - if( tp <= time_point::now() ) - return; - - if( !current ) { - current = new fc::context(&fc::thread::current()); - } - - current->resume_time = tp; - current->clear_blocking_promises(); - - sleep_pqueue.push_back(current); - std::push_heap( sleep_pqueue.begin(), - sleep_pqueue.end(), sleep_priority_less() ); - - start_next_fiber(reschedule); - - // clear current context from sleep queue... - for( uint32_t i = 0; i < sleep_pqueue.size(); ++i ) { - if( sleep_pqueue[i] == current ) { - sleep_pqueue[i] = sleep_pqueue.back(); - sleep_pqueue.pop_back(); - std::make_heap( sleep_pqueue.begin(), - sleep_pqueue.end(), sleep_priority_less() ); - break; - } - } - - current->resume_time = time_point::max(); - check_fiber_exceptions(); - } - - void wait( const promise_base::ptr& p, const time_point& timeout ) { - if( p->ready() ) return; - if( timeout < time_point::now() ) - BOOST_THROW_EXCEPTION( future_wait_timeout() ); - - if( !current ) { - current = new fc::context(&fc::thread::current()); - } - - //slog( " %1% blocking on %2%", current, p.get() ); - current->add_blocking_promise(p.get(),true); - - // if not max timeout, added to sleep pqueue - if( timeout != time_point::max() ) { - current->resume_time = timeout; - sleep_pqueue.push_back(current); - std::push_heap( sleep_pqueue.begin(), - sleep_pqueue.end(), - sleep_priority_less() ); - } - - // elog( "blocking %1%", current ); - add_to_blocked( current ); - // debug("swtiching fibers..." ); - - - start_next_fiber(); - // slog( "resuming %1%", current ); - - //slog( " %1% unblocking blocking on %2%", current, p.get() ); - current->remove_blocking_promise(p.get()); - - check_fiber_exceptions(); - } - }; -} // namespace fc diff --git a/src/time.cpp b/src/time.cpp index 93aa57ff2..27dd84812 100644 --- a/src/time.cpp +++ b/src/time.cpp @@ -1,30 +1,146 @@ #include -#include +#include #include #include #include +#include +#include +#include namespace fc { + namespace bch = boost::chrono; - time_point time_point::now() { - return time_point(microseconds(bch::duration_cast(bch::system_clock::now().time_since_epoch()).count())); + + time_point time_point::now() + { + return time_point( microseconds( bch::duration_cast( bch::system_clock::now().time_since_epoch() ).count() ) ); + } + + fc::string time_point_sec::to_non_delimited_iso_string()const + { + const auto ptime = boost::posix_time::from_time_t( time_t( sec_since_epoch() ) ); + return boost::posix_time::to_iso_string( ptime ); + } + + fc::string time_point_sec::to_iso_string()const + { + const auto ptime = boost::posix_time::from_time_t( time_t( sec_since_epoch() ) ); + return boost::posix_time::to_iso_extended_string( ptime ); + } + + time_point_sec::operator fc::string()const + { + return this->to_iso_string(); + } + + time_point_sec time_point_sec::from_iso_string( const fc::string& s ) + { try { + static boost::posix_time::ptime epoch = boost::posix_time::from_time_t( 0 ); + boost::posix_time::ptime pt; + if( s.size() >= 5 && s.at( 4 ) == '-' ) // http://en.wikipedia.org/wiki/ISO_8601 + pt = boost::date_time::parse_delimited_time( s, 'T' ); + else + pt = boost::posix_time::from_iso_string( s ); + return fc::time_point_sec( (pt - epoch).total_seconds() ); + } FC_RETHROW_EXCEPTIONS( warn, "unable to convert ISO-formatted string to fc::time_point_sec" ) } + + time_point::operator fc::string()const + { + return fc::string( time_point_sec( *this ) ); } - time_point::operator fc::string()const { - bch::system_clock::time_point tp; - tp += bch::microseconds( elapsed._count); - time_t tt = bch::system_clock::to_time_t(tp); - - return boost::posix_time::to_iso_string( boost::posix_time::from_time_t(tt) + boost::posix_time::microseconds( elapsed._count % 1000000 ) ); + + time_point time_point::from_iso_string( const fc::string& s ) + { try { + return time_point( time_point_sec::from_iso_string( s ) ); + } FC_RETHROW_EXCEPTIONS( warn, "unable to convert ISO-formatted string to fc::time_point" ) } + + void to_variant( const fc::time_point& t, variant& v ) { + v = fc::string( t ); + } + void from_variant( const fc::variant& v, fc::time_point& t ) { + t = fc::time_point::from_iso_string( v.as_string() ); } - time_point time_point::from_iso_string( const fc::string& s ) { - auto pt = boost::posix_time::from_iso_string(s); - return fc::time_point(fc::seconds( (pt - boost::posix_time::from_time_t(0)).total_seconds() )); + void to_variant( const fc::time_point_sec& t, variant& v ) { + v = fc::string( t ); } - class value; - void pack( fc::value& v, const fc::time_point& t ) { - v = fc::string(t); + void from_variant( const fc::variant& v, fc::time_point_sec& t ) { + t = fc::time_point_sec::from_iso_string( v.as_string() ); } - void unpack( const fc::value& v, fc::time_point& t ) { - t = fc::time_point::from_iso_string(v.cast()); + + // inspired by show_date_relative() in git's date.c + string get_approximate_relative_time_string(const time_point_sec& event_time, + const time_point_sec& relative_to_time /* = fc::time_point::now() */, + const std::string& default_ago /* = " ago" */) { + + + string ago = default_ago; + int32_t seconds_ago = relative_to_time.sec_since_epoch() - event_time.sec_since_epoch(); + if (seconds_ago < 0) + { + ago = " in the future"; + seconds_ago = -seconds_ago; + } + stringstream result; + if (seconds_ago < 90) + { + result << seconds_ago << " second" << (seconds_ago > 1 ? "s" : "") << ago; + return result.str(); + } + uint32_t minutes_ago = (seconds_ago + 30) / 60; + if (minutes_ago < 90) + { + result << minutes_ago << " minute" << (minutes_ago > 1 ? "s" : "") << ago; + return result.str(); + } + uint32_t hours_ago = (minutes_ago + 30) / 60; + if (hours_ago < 90) + { + result << hours_ago << " hour" << (hours_ago > 1 ? "s" : "") << ago; + return result.str(); + } + uint32_t days_ago = (hours_ago + 12) / 24; + if (days_ago < 90) + { + result << days_ago << " day" << (days_ago > 1 ? "s" : "") << ago; + return result.str(); + } + uint32_t weeks_ago = (days_ago + 3) / 7; + if (weeks_ago < 70) + { + result << weeks_ago << " week" << (weeks_ago > 1 ? "s" : "") << ago; + return result.str(); + } + uint32_t months_ago = (days_ago + 15) / 30; + if (months_ago < 12) + { + result << months_ago << " month" << (months_ago > 1 ? "s" : "") << ago; + return result.str(); + } + uint32_t years_ago = days_ago / 365; + result << years_ago << " year" << (months_ago > 1 ? "s" : ""); + if (months_ago < 12 * 5) + { + uint32_t leftover_days = days_ago - (years_ago * 365); + uint32_t leftover_months = (leftover_days + 15) / 30; + if (leftover_months) + result << leftover_months << " month" << (months_ago > 1 ? "s" : ""); + } + result << ago; + return result.str(); } -} + string get_approximate_relative_time_string(const time_point& event_time, + const time_point& relative_to_time /* = fc::time_point::now() */, + const std::string& ago /* = " ago" */) { + return get_approximate_relative_time_string(time_point_sec(event_time), time_point_sec(relative_to_time), ago); + } + + void to_variant( const microseconds& input_microseconds, variant& output_variant ) + { + output_variant = input_microseconds.count(); + } + void from_variant( const variant& input_variant, microseconds& output_microseconds ) + { + output_microseconds = microseconds(input_variant.as_int64()); + } + +} //namespace fc diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp deleted file mode 100644 index 334570ea0..000000000 --- a/src/udp_socket.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#include -#include - - -namespace fc { - - class udp_socket::impl : public fc::retainable { - public: - impl():_sock( fc::asio::default_io_service() ){} - ~impl(){ - // _sock.cancel(); - } - - boost::asio::ip::udp::socket _sock; - }; - - boost::asio::ip::udp::endpoint to_asio_ep( const fc::ip::endpoint& e ) { - return boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4(e.get_address()), e.port() ); - } - fc::ip::endpoint to_fc_ep( const boost::asio::ip::udp::endpoint& e ) { - return fc::ip::endpoint( e.address().to_v4().to_ulong(), e.port() ); - } - - udp_socket::udp_socket() - :my( new impl() ) { - } - udp_socket::udp_socket( const udp_socket& s ) - :my(s.my){} - udp_socket::~udp_socket() { - } - - size_t udp_socket::send_to( const char* b, size_t l, const ip::endpoint& to ) { - try { - return my->_sock.send_to( boost::asio::buffer(b, l), to_asio_ep(to) ); - } catch( const boost::system::system_error& e ) { - if( e.code() == boost::asio::error::would_block ) { - promise::ptr p(new promise("udp_socket::send_to")); - my->_sock.async_send_to( boost::asio::buffer(b,l), to_asio_ep(to), - [=]( const boost::system::error_code& ec, size_t bt ) { - if( !ec ) p->set_value(bt); - else p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); - }); - return p->wait(); - } - throw; - } - } - void udp_socket::open() { - my->_sock.open( boost::asio::ip::udp::v4() ); - my->_sock.non_blocking(true); - } - void udp_socket::set_receive_buffer_size( size_t s ) { - my->_sock.set_option(boost::asio::socket_base::receive_buffer_size(s) ); - } - void udp_socket::bind( const fc::ip::endpoint& e ) { - my->_sock.bind( to_asio_ep(e) ); - } - size_t udp_socket::receive_from( char* b, size_t l, fc::ip::endpoint& _from ) { - try { - boost::asio::ip::udp::endpoint from; - size_t r = my->_sock.receive_from( boost::asio::buffer(b, l), from ); - _from = to_fc_ep(from); - return r; - } catch( const boost::system::system_error& e ) { - if( e.code() == boost::asio::error::would_block ) { - boost::asio::ip::udp::endpoint from; - promise::ptr p(new promise("udp_socket::send_to")); - my->_sock.async_receive_from( boost::asio::buffer(b,l), from, - [=]( const boost::system::error_code& ec, size_t bt ) { - if( !ec ) p->set_value(bt); - else p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) ); - }); - auto r = p->wait(); - _from = to_fc_ep(from); - return r; - } - throw; - } - } - void udp_socket::close() { - //my->_sock.cancel(); - my->_sock.close(); - } - - fc::ip::endpoint udp_socket::local_endpoint()const { - return to_fc_ep( my->_sock.local_endpoint() ); - } - void udp_socket::connect( const fc::ip::endpoint& e ) { - my->_sock.connect( to_asio_ep(e) ); - } - - void udp_socket::set_multicast_enable_loopback( bool s ) - { - my->_sock.set_option( boost::asio::ip::multicast::enable_loopback(s) ); - } - void udp_socket::set_reuse_address( bool s ) - { - my->_sock.set_option( boost::asio::ip::udp::socket::reuse_address(s) ); - } - void udp_socket::join_multicast_group( const fc::ip::address& a ) - { - my->_sock.set_option( boost::asio::ip::multicast::join_group( boost::asio::ip::address_v4(a) ) ); - } - -} diff --git a/src/uint128.cpp b/src/uint128.cpp new file mode 100644 index 000000000..dae2aeffa --- /dev/null +++ b/src/uint128.cpp @@ -0,0 +1,377 @@ +#include +#include +#include +#include +#include "byteswap.hpp" + +namespace fc +{ + template + static void divide(const T &numerator, const T &denominator, T "ient, T &remainder) + { + + static const int bits = sizeof(T) * 8;//CHAR_BIT; + + if(denominator == 0) { + throw std::domain_error("divide by zero"); + } else { + T n = numerator; + T d = denominator; + T x = 1; + T answer = 0; + + + while((n >= d) && (((d >> (bits - 1)) & 1) == 0)) { + x <<= 1; + d <<= 1; + } + + while(x != 0) { + if(n >= d) { + n -= d; + answer |= x; + } + + x >>= 1; + d >>= 1; + } + + quotient = answer; + remainder = n; + } + } + + uint128::uint128(const std::string &sz) + :hi(0), lo(0) + { + // do we have at least one character? + if(!sz.empty()) { + // make some reasonable assumptions + int radix = 10; + bool minus = false; + + std::string::const_iterator i = sz.begin(); + + // check for minus sign, i suppose technically this should only apply + // to base 10, but who says that -0x1 should be invalid? + if(*i == '-') { + ++i; + minus = true; + } + + // check if there is radix changing prefix (0 or 0x) + if(i != sz.end()) { + if(*i == '0') { + radix = 8; + ++i; + if(i != sz.end()) { + if(*i == 'x') { + radix = 16; + ++i; + } + } + } + + while(i != sz.end()) { + unsigned int n = 0; + const char ch = *i; + + if(ch >= 'A' && ch <= 'Z') { + if(((ch - 'A') + 10) < radix) { + n = (ch - 'A') + 10; + } else { + break; + } + } else if(ch >= 'a' && ch <= 'z') { + if(((ch - 'a') + 10) < radix) { + n = (ch - 'a') + 10; + } else { + break; + } + } else if(ch >= '0' && ch <= '9') { + if((ch - '0') < radix) { + n = (ch - '0'); + } else { + break; + } + } else { + /* completely invalid character */ + break; + } + + (*this) *= radix; + (*this) += n; + + ++i; + } + } + + // if this was a negative number, do that two's compliment madness :-P + if(minus) { + *this = -*this; + } + } + } + + + uint128::operator bigint()const + { + auto tmp = uint128( bswap_64( hi ), bswap_64( lo ) ); + bigint bi( (char*)&tmp, sizeof(tmp) ); + return bi; + } + uint128::uint128( const fc::bigint& bi ) + { + *this = uint128( std::string(bi) ); // TODO: optimize this... + } + + uint128::operator std::string ()const + { + if(*this == 0) { return "0"; } + + // at worst it will be size digits (base 2) so make our buffer + // that plus room for null terminator + static char sz [128 + 1]; + sz[sizeof(sz) - 1] = '\0'; + + uint128 ii(*this); + int i = 128 - 1; + + while (ii != 0 && i) { + + uint128 remainder; + divide(ii, uint128(10), ii, remainder); + sz [--i] = "0123456789abcdefghijklmnopqrstuvwxyz"[remainder.to_integer()]; + } + + return &sz[i]; + } + + + uint128& uint128::operator<<=(const uint128& rhs) + { + if(rhs >= 128) + { + hi = 0; + lo = 0; + } + else + { + unsigned int n = rhs.to_integer(); + const unsigned int halfsize = 128 / 2; + + if(n >= halfsize){ + n -= halfsize; + hi = lo; + lo = 0; + } + + if(n != 0) { + // shift high half + hi <<= n; + + const uint64_t mask(~(uint64_t(-1) >> n)); + + // and add them to high half + hi |= (lo & mask) >> (halfsize - n); + + // and finally shift also low half + lo <<= n; + } + } + + return *this; + } + + uint128 & uint128::operator>>=(const uint128& rhs) + { + if(rhs >= 128) + { + hi = 0; + lo = 0; + } + else + { + unsigned int n = rhs.to_integer(); + const unsigned int halfsize = 128 / 2; + + if(n >= halfsize) { + n -= halfsize; + lo = hi; + hi = 0; + } + + if(n != 0) { + // shift low half + lo >>= n; + + // get lower N bits of high half + const uint64_t mask(~(uint64_t(-1) << n)); + + // and add them to low qword + lo |= (hi & mask) << (halfsize - n); + + // and finally shift also high half + hi >>= n; + } + } + return *this; + } + + uint128& uint128::operator/=(const uint128 &b) + { + uint128 remainder; + divide(*this, b, *this, remainder); + return *this; + } + + uint128& uint128::operator%=(const uint128 &b) + { + uint128 quotient; + divide(*this, b, quotient, *this); + return *this; + } + + uint128& uint128::operator*=(const uint128 &b) + { + uint64_t a0 = (uint32_t) (this->lo ); + uint64_t a1 = (uint32_t) (this->lo >> 0x20); + uint64_t a2 = (uint32_t) (this->hi ); + uint64_t a3 = (uint32_t) (this->hi >> 0x20); + + uint64_t b0 = (uint32_t) (b.lo ); + uint64_t b1 = (uint32_t) (b.lo >> 0x20); + uint64_t b2 = (uint32_t) (b.hi ); + uint64_t b3 = (uint32_t) (b.hi >> 0x20); + + // (a0 + (a1 << 0x20) + (a2 << 0x40) + (a3 << 0x60)) * + // (b0 + (b1 << 0x20) + (b2 << 0x40) + (b3 << 0x60)) = + // a0 * b0 + // + // (a1 * b0 + a0 * b1) << 0x20 + // (a2 * b0 + a1 * b1 + a0 * b2) << 0x40 + // (a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3) << 0x60 + // + // all other cross terms are << 0x80 or higher, thus do not appear in result + + this->hi = 0; + this->lo = a3*b0; + (*this) += a2*b1; + (*this) += a1*b2; + (*this) += a0*b3; + (*this) <<= 0x20; + (*this) += a2*b0; + (*this) += a1*b1; + (*this) += a0*b2; + (*this) <<= 0x20; + (*this) += a1*b0; + (*this) += a0*b1; + (*this) <<= 0x20; + (*this) += a0*b0; + + return *this; + } + + void uint128::full_product( const uint128& a, const uint128& b, uint128& result_hi, uint128& result_lo ) + { + // (ah * 2**64 + al) * (bh * 2**64 + bl) + // = (ah * bh * 2**128 + al * bh * 2**64 + ah * bl * 2**64 + al * bl + // = P * 2**128 + (Q + R) * 2**64 + S + // = Ph * 2**192 + Pl * 2**128 + // + Qh * 2**128 + Ql * 2**64 + // + Rh * 2**128 + Rl * 2**64 + // + Sh * 2**64 + Sl + // + + uint64_t ah = a.hi; + uint64_t al = a.lo; + uint64_t bh = b.hi; + uint64_t bl = b.lo; + + uint128 s = al; + s *= bl; + uint128 r = ah; + r *= bl; + uint128 q = al; + q *= bh; + uint128 p = ah; + p *= bh; + + uint64_t sl = s.lo; + uint64_t sh = s.hi; + uint64_t rl = r.lo; + uint64_t rh = r.hi; + uint64_t ql = q.lo; + uint64_t qh = q.hi; + uint64_t pl = p.lo; + uint64_t ph = p.hi; + + uint64_t y[4]; // final result + y[0] = sl; + + uint128_t acc = sh; + acc += ql; + acc += rl; + y[1] = acc.lo; + acc = acc.hi; + acc += qh; + acc += rh; + acc += pl; + y[2] = acc.lo; + y[3] = acc.hi + ph; + + result_hi = uint128( y[3], y[2] ); + result_lo = uint128( y[1], y[0] ); + + return; + } + + static uint8_t _popcount_64( uint64_t x ) + { + static const uint64_t m[] = { + 0x5555555555555555ULL, + 0x3333333333333333ULL, + 0x0F0F0F0F0F0F0F0FULL, + 0x00FF00FF00FF00FFULL, + 0x0000FFFF0000FFFFULL, + 0x00000000FFFFFFFFULL + }; + // TODO future optimization: replace slow, portable version + // with fast, non-portable __builtin_popcountll intrinsic + // (when available) + + for( int i=0, w=1; i<6; i++, w+=w ) + { + x = (x & m[i]) + ((x >> w) & m[i]); + } + return uint8_t(x); + } + + uint8_t uint128::popcount()const + { + return _popcount_64( lo ) + _popcount_64( hi ); + } + + void to_variant( const uint128& var, variant& vo ) { vo = std::string(var); } + void from_variant( const variant& var, uint128& vo ){ vo = uint128(var.as_string()); } + +} // namespace fc + + +/* + * Portions of the above code were adapted from the work of Evan Teran. + * + * Copyright (c) 2008 + * Evan Teran + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appears in all copies and that both the + * copyright notice and this permission notice appear in supporting + * documentation, and that the same name not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. We make no representations about the + * suitability this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + diff --git a/src/url.cpp b/src/url.cpp deleted file mode 100644 index 54ebf6e5c..000000000 --- a/src/url.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace fc { - -// url::url( const url& u ); -#if 0 - url::url( url&& u ) - :proto(fc::move(u.proto)), - host(fc::move(u.host)), - user(fc::move(u.user)), - pass(fc::move(u.pass)), - path(fc::move(u.path)), - args(fc::move(u.args)), - port(u.port){} - - url& url::operator=(url&& c) - { - fc::swap(*this,c); - return *this; - } - - // url::url& operator=(const url& c) { - // } -#endif - url::url( const string& s ) { - from_string(s); - } - string url::to_string()const { - fc::stringstream ss; - ss<( host_port.substr( pos+1 ) ); - } catch ( ... ) { - FC_THROW_REPORT( "Unable to parse port field in url", value().set( "url", s ) ); - } - host = host_port.substr(0,pos); - } else { - host = fc::move(host_port); - } - fc::getline( ss, _path, '?' ); - fc::getline( ss, _args ); - path = fc::move(_path); - if( _args.size() ) args = fc::move(_args); - - return *this; - } - - bool url::operator==( const url& cmp )const { - return cmp.proto == proto && - cmp.host == host && - cmp.user == user && - cmp.pass == pass && - cmp.path == path && - cmp.args == args && - cmp.port == port; - } - -} // namespace fc diff --git a/src/utf8.cpp b/src/utf8.cpp new file mode 100644 index 000000000..0bab5e005 --- /dev/null +++ b/src/utf8.cpp @@ -0,0 +1,32 @@ +#include "fc/utf8.hpp" + +#include "utf8/checked.h" +#include "utf8/core.h" +#include "utf8/unchecked.h" + +#include + +namespace fc { + + bool is_utf8( const std::string& str ) + { + return utf8::is_valid( str.begin(), str.end() ); + } + + void decodeUtf8(const std::string& input, std::wstring* storage) + { + assert(storage != nullptr); + + utf8::utf8to32(input.begin(), input.end(), std::back_inserter(*storage)); + } + + void encodeUtf8(const std::wstring& input, std::string* storage) + { + assert(storage != nullptr); + + utf8::utf32to8(input.begin(), input.end(), std::back_inserter(*storage)); + } + +} ///namespace fc + + diff --git a/src/utf8/ReleaseNotes b/src/utf8/ReleaseNotes new file mode 100644 index 000000000..364411a23 --- /dev/null +++ b/src/utf8/ReleaseNotes @@ -0,0 +1,12 @@ +utf8 cpp library +Release 2.3.4 + +A minor bug fix release. Thanks to all who reported bugs. + +Note: Version 2.3.3 contained a regression, and therefore was removed. + +Changes from version 2.3.2 +- Bug fix [39]: checked.h Line 273 and unchecked.h Line 182 have an extra ';' +- Bug fix [36]: replace_invalid() only works with back_inserter + +Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes diff --git a/src/utf8/checked.h b/src/utf8/checked.h new file mode 100644 index 000000000..133115513 --- /dev/null +++ b/src/utf8/checked.h @@ -0,0 +1,327 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" +#include + +namespace utf8 +{ + // Base for the exceptions that may be thrown from the library + class exception : public ::std::exception { + }; + + // Exceptions that may be thrown from the library functions. + class invalid_code_point : public exception { + uint32_t cp; + public: + invalid_code_point(uint32_t cp) : cp(cp) {} + virtual const char* what() const throw() { return "Invalid code point"; } + uint32_t code_point() const {return cp;} + }; + + class invalid_utf8 : public exception { + uint8_t u8; + public: + invalid_utf8 (uint8_t u) : u8(u) {} + virtual const char* what() const throw() { return "Invalid UTF-8"; } + uint8_t utf8_octet() const {return u8;} + }; + + class invalid_utf16 : public exception { + uint16_t u16; + public: + invalid_utf16 (uint16_t u) : u16(u) {} + virtual const char* what() const throw() { return "Invalid UTF-16"; } + uint16_t utf16_word() const {return u16;} + }; + + class not_enough_room : public exception { + public: + virtual const char* what() const throw() { return "Not enough space"; } + }; + + /// The library API - functions intended to be called by the users + + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (!utf8::internal::is_code_point_valid(cp)) + throw invalid_code_point(cp); + + if (cp < 0x80) // one octet + *(result++) = static_cast(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast((cp >> 6) | 0xc0); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast((cp >> 12) | 0xe0); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast((cp >> 18) | 0xf0); + *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + return result; + } + + template + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) { + case internal::UTF8_OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: + out = utf8::append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + out = utf8::append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (start != end && utf8::internal::is_trail(*start)) + ++start; + break; + } + } + return out; + } + + template + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); + return utf8::replace_invalid(start, end, out, replacement_marker); + } + + template + uint32_t next(octet_iterator& it, octet_iterator end) + { + uint32_t cp = 0; + internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); + switch (err_code) { + case internal::UTF8_OK : + break; + case internal::NOT_ENOUGH_ROOM : + throw not_enough_room(); + case internal::INVALID_LEAD : + case internal::INCOMPLETE_SEQUENCE : + case internal::OVERLONG_SEQUENCE : + throw invalid_utf8(*it); + case internal::INVALID_CODE_POINT : + throw invalid_code_point(cp); + } + return cp; + } + + template + uint32_t peek_next(octet_iterator it, octet_iterator end) + { + return utf8::next(it, end); + } + + template + uint32_t prior(octet_iterator& it, octet_iterator start) + { + // can't do much if it == start + if (it == start) + throw not_enough_room(); + + octet_iterator end = it; + // Go back until we hit either a lead octet or start + while (utf8::internal::is_trail(*(--it))) + if (it == start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + return utf8::peek_next(it, end); + } + + /// Deprecated in versions that include "prior" + template + uint32_t previous(octet_iterator& it, octet_iterator pass_start) + { + octet_iterator end = it; + while (utf8::internal::is_trail(*(--it))) + if (it == pass_start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return utf8::next(temp, end); + } + + template + void advance (octet_iterator& it, distance_type n, octet_iterator end) + { + for (distance_type i = 0; i < n; ++i) + utf8::next(it, end); + } + + template + typename std::iterator_traits::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::next(first, last); + return dist; + } + + template + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + if (start != end) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + if (utf8::internal::is_trail_surrogate(trail_surrogate)) + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + else + throw invalid_utf16(static_cast(trail_surrogate)); + } + else + throw invalid_utf16(static_cast(cp)); + + } + // Lone trail surrogate + else if (utf8::internal::is_trail_surrogate(cp)) + throw invalid_utf16(static_cast(cp)); + + result = utf8::append(cp, result); + } + return result; + } + + template + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = utf8::next(start, end); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast(cp); + } + return result; + } + + template + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::append(*(start++), result); + + return result; + } + + template + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start != end) + (*result++) = utf8::next(start, end); + + return result; + } + + // The iterator class + template + class iterator : public std::iterator { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end) : + it(octet_it), range_start(range_start), range_end(range_end) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::next(temp, range_end); + } + bool operator == (const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + utf8::next(it, range_end); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + utf8::next(it, range_end); + return temp; + } + iterator& operator -- () + { + utf8::prior(it, range_start); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::prior(it, range_start); + return temp; + } + }; // class iterator + +} // namespace utf8 + +#endif //header guard + + diff --git a/src/utf8/core.h b/src/utf8/core.h new file mode 100644 index 000000000..693d388c0 --- /dev/null +++ b/src/utf8/core.h @@ -0,0 +1,329 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include + +namespace utf8 +{ + // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers + // You may need to change them to match your system. + // These typedefs have the same names as ones from cstdint, or boost/cstdint + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + +// Helper code - not intended to be directly called by the library users. May be changed at any time +namespace internal +{ + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); + const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template + inline uint8_t mask8(octet_type oc) + { + return static_cast(0xff & oc); + } + template + inline uint16_t mask16(u16_type oc) + { + return static_cast(0xffff & oc); + } + template + inline bool is_trail(octet_type oc) + { + return ((utf8::internal::mask8(oc) >> 6) == 0x2); + } + + template + inline bool is_lead_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); + } + + template + inline bool is_trail_surrogate(u16 cp) + { + return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); + } + + template + inline typename std::iterator_traits::difference_type + sequence_length(octet_iterator lead_it) + { + uint8_t lead = utf8::internal::mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; + } + + template + inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) + { + if (cp < 0x80) { + if (length != 1) + return true; + } + else if (cp < 0x800) { + if (length != 2) + return true; + } + else if (cp < 0x10000) { + if (length != 3) + return true; + } + + return false; + } + + enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + + /// Helper for get_sequence_x + template + utf_error increase_safely(octet_iterator& it, octet_iterator end) + { + if (++it == end) + return NOT_ENOUGH_ROOM; + + if (!utf8::internal::is_trail(*it)) + return INCOMPLETE_SEQUENCE; + + return UTF8_OK; + } + + #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} + + /// get_sequence_x functions decode utf-8 sequences of the length x + template + utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + return UTF8_OK; + } + + template + utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); + + return UTF8_OK; + } + + template + utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + template + utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR + + template + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + // Save the original value of it so we can go back in case of failure + // Of course, it does not make much sense with i.e. stream iterators + octet_iterator original_it = it; + + uint32_t cp = 0; + // Determine the sequence length based on the lead octet + typedef typename std::iterator_traits::difference_type octet_difference_type; + const octet_difference_type length = utf8::internal::sequence_length(it); + + // Get trail octets and calculate the code point + utf_error err = UTF8_OK; + switch (length) { + case 0: + return INVALID_LEAD; + case 1: + err = utf8::internal::get_sequence_1(it, end, cp); + break; + case 2: + err = utf8::internal::get_sequence_2(it, end, cp); + break; + case 3: + err = utf8::internal::get_sequence_3(it, end, cp); + break; + case 4: + err = utf8::internal::get_sequence_4(it, end, cp); + break; + } + + if (err == UTF8_OK) { + // Decoding succeeded. Now, security checks... + if (utf8::internal::is_code_point_valid(cp)) { + if (!utf8::internal::is_overlong_sequence(cp, length)){ + // Passed! Return here. + code_point = cp; + ++it; + return UTF8_OK; + } + else + err = OVERLONG_SEQUENCE; + } + else + err = INVALID_CODE_POINT; + } + + // Failure branch - restore the original value of the iterator + it = original_it; + return err; + } + + template + inline utf_error validate_next(octet_iterator& it, octet_iterator end) { + uint32_t ignored; + return utf8::internal::validate_next(it, end, ignored); + } + +} // namespace internal + + /// The library API - functions intended to be called by the users + + // Byte order mark + const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + + template + octet_iterator find_invalid(octet_iterator start, octet_iterator end) + { + octet_iterator result = start; + while (result != end) { + utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); + if (err_code != internal::UTF8_OK) + return result; + } + return result; + } + + template + inline bool is_valid(octet_iterator start, octet_iterator end) + { + return (utf8::find_invalid(start, end) == end); + } + + template + inline bool starts_with_bom (octet_iterator it, octet_iterator end) + { + return ( + ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && + ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && + ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) + ); + } + + //Deprecated in release 2.3 + template + inline bool is_bom (octet_iterator it) + { + return ( + (utf8::internal::mask8(*it++)) == bom[0] && + (utf8::internal::mask8(*it++)) == bom[1] && + (utf8::internal::mask8(*it)) == bom[2] + ); + } +} // namespace utf8 + +#endif // header guard + + diff --git a/src/utf8/unchecked.h b/src/utf8/unchecked.h new file mode 100644 index 000000000..cb2427166 --- /dev/null +++ b/src/utf8/unchecked.h @@ -0,0 +1,228 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" + +namespace utf8 +{ + namespace unchecked + { + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (cp < 0x80) // one octet + *(result++) = static_cast(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast((cp >> 6) | 0xc0); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast((cp >> 12) | 0xe0); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast((cp >> 18) | 0xf0); + *(result++) = static_cast(((cp >> 12) & 0x3f)| 0x80); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + return result; + } + + template + uint32_t next(octet_iterator& it) + { + uint32_t cp = utf8::internal::mask8(*it); + typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); + switch (length) { + case 1: + break; + case 2: + it++; + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + break; + case 3: + ++it; + cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + ++it; + cp += (*it) & 0x3f; + break; + case 4: + ++it; + cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + ++it; + cp += (utf8::internal::mask8(*it) << 6) & 0xfff; + ++it; + cp += (*it) & 0x3f; + break; + } + ++it; + return cp; + } + + template + uint32_t peek_next(octet_iterator it) + { + return utf8::unchecked::next(it); + } + + template + uint32_t prior(octet_iterator& it) + { + while (utf8::internal::is_trail(*(--it))) ; + octet_iterator temp = it; + return utf8::unchecked::next(temp); + } + + // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) + template + inline uint32_t previous(octet_iterator& it) + { + return utf8::unchecked::prior(it); + } + + template + void advance (octet_iterator& it, distance_type n) + { + for (distance_type i = 0; i < n; ++i) + utf8::unchecked::next(it); + } + + template + typename std::iterator_traits::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::unchecked::next(first); + return dist; + } + + template + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + } + result = utf8::unchecked::append(cp, result); + } + return result; + } + + template + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start < end) { + uint32_t cp = utf8::unchecked::next(start); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast(cp); + } + return result; + } + + template + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::unchecked::append(*(start++), result); + + return result; + } + + template + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = utf8::unchecked::next(start); + + return result; + } + + // The iterator class + template + class iterator : public std::iterator { + octet_iterator it; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it): it(octet_it) {} + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::unchecked::next(temp); + } + bool operator == (const iterator& rhs) const + { + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + ::std::advance(it, utf8::internal::sequence_length(it)); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + ::std::advance(it, utf8::internal::sequence_length(it)); + return temp; + } + iterator& operator -- () + { + utf8::unchecked::prior(it); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::unchecked::prior(it); + return temp; + } + }; // class iterator + + } // namespace utf8::unchecked +} // namespace utf8 + + +#endif // header guard + diff --git a/src/utf8/utf8cpp.html b/src/utf8/utf8cpp.html new file mode 100644 index 000000000..6f2aacbe7 --- /dev/null +++ b/src/utf8/utf8cpp.html @@ -0,0 +1,1789 @@ + + + + + + + + + UTF8-CPP: UTF-8 with C++ in a Portable Way + + + + +

+ UTF8-CPP: UTF-8 with C++ in a Portable Way +

+

+ The Sourceforge project page +

+ +

+ Introduction +

+

+ Many C++ developers miss an easy and portable way of handling Unicode encoded + strings. The original C++ Standard (known as C++98 or C++03) is Unicode agnostic. + C++11 provides some support for Unicode on core language and library level: + u8, u, and U character and string literals, char16_t and char32_t character types, + u16string and u32string library classes, and codecvt support for conversions + between Unicode encoding forms. + In the meantime, developers use third party libraries like ICU, OS specific capabilities, or simply + roll out their own solutions. +

+

+ In order to easily handle UTF-8 encoded Unicode strings, I came up with a small + generic library. For anybody used to work with STL algorithms and iterators, it should be + easy and natural to use. The code is freely available for any purpose - check out + the license at the beginning of the utf8.h file. If you run into + bugs or performance issues, please let me know and I'll do my best to address them. +

+

+ The purpose of this article is not to offer an introduction to Unicode in general, + and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out + Unicode Home Page or some other source of + information for Unicode. Also, it is not my aim to advocate the use of UTF-8 + encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from + C++, I am sure you have good reasons for it. +

+

+ Examples of use +

+

+ Introductionary Sample +

+

+ To illustrate the use of the library, let's start with a small but complete program + that opens a file containing UTF-8 encoded text, reads it line by line, checks each line + for invalid UTF-8 byte sequences, and converts it to UTF-16 encoding and back to UTF-8: +

+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include "utf8.h"
+using namespace std;
+int main(int argc, char** argv)
+{
+    if (argc != 2) {
+        cout << "\nUsage: docsample filename\n";
+        return 0;
+    }
+
+    const char* test_file_path = argv[1];
+    // Open the test file (contains UTF-8 encoded text)
+    ifstream fs8(test_file_path);
+    if (!fs8.is_open()) {
+    cout << "Could not open " << test_file_path << endl;
+    return 0;
+    }
+
+    unsigned line_count = 1;
+    string line;
+    // Play with all the lines in the file
+    while (getline(fs8, line)) {
+       // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)
+        string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
+        if (end_it != line.end()) {
+            cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n";
+            cout << "This part is fine: " << string(line.begin(), end_it) << "\n";
+        }
+
+        // Get the line length (at least for the valid part)
+        int length = utf8::distance(line.begin(), end_it);
+        cout << "Length of line " << line_count << " is " << length <<  "\n";
+
+        // Convert it to utf-16
+        vector<unsigned short> utf16line;
+        utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
+
+        // And back to utf-8
+        string utf8line; 
+        utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
+
+        // Confirm that the conversion went OK:
+        if (utf8line != string(line.begin(), end_it))
+            cout << "Error in UTF-16 conversion at line: " << line_count << "\n";        
+
+        line_count++;
+    }
+    return 0;
+}
+
+

+ In the previous code sample, for each line we performed + a detection of invalid UTF-8 sequences with find_invalid; the number + of characters (more precisely - the number of Unicode code points, including the end + of line and even BOM if there is one) in each line was + determined with a use of utf8::distance; finally, we have converted + each line to UTF-16 encoding with utf8to16 and back to UTF-8 with + utf16to8. +

+

Checking if a file contains valid UTF-8 text

+

+Here is a function that checks whether the content of a file is valid UTF-8 encoded text without +reading the content into the memory: +

+
    
+bool valid_utf8_file(iconst char* file_name)
+{
+    ifstream ifs(file_name);
+    if (!ifs)
+        return false; // even better, throw here
+
+    istreambuf_iterator<char> it(ifs.rdbuf());
+    istreambuf_iterator<char> eos;
+
+    return utf8::is_valid(it, eos);
+}
+
+

+Because the function utf8::is_valid() works with input iterators, we were able +to pass an istreambuf_iterator to it and read the content of the file directly +without loading it to the memory first.

+

+Note that other functions that take input iterator arguments can be used in a similar way. For +instance, to read the content of a UTF-8 encoded text file and convert the text to UTF-16, just +do something like: +

+
+    utf8::utf8to16(it, eos, back_inserter(u16string));
+
+

Ensure that a string contains valid UTF-8 text

+

+If we have some text that "probably" contains UTF-8 encoded text and we want to +replace any invalid UTF-8 sequence with a replacement character, something like +the following function may be used: +

+
+void fix_utf8_string(std::string& str)
+{
+    std::string temp;
+    utf8::replace_invalid(str.begin(), str.end(), back_inserter(temp));
+    str = temp;
+}
+
+

The function will replace any invalid UTF-8 sequence with a Unicode replacement character. +There is an overloaded function that enables the caller to supply their own replacement character. +

+

+ Reference +

+

+ Functions From utf8 Namespace +

+

+ utf8::append +

+

+ Available in version 1.0 and later. +

+

+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. +

+
+template <typename octet_iterator>
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+
+

+ octet_iterator: an output iterator.
+ cp: a 32 bit integer representing a code point to append to the + sequence.
+ result: an output iterator to the place in the sequence where to + append the code point.
+ Return value: an iterator pointing to the place + after the newly appended sequence. +

+

+ Example of use: +

+
+unsigned char u[5] = {0,0,0,0,0};
+unsigned char* end = append(0x0448, u);
+assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0);
+
+

+ Note that append does not allocate any memory - it is the burden of + the caller to make sure there is enough memory allocated for the operation. To make + things more interesting, append can add anywhere between 1 and 4 + octets to the sequence. In practice, you would most often want to use + std::back_inserter to ensure that the necessary memory is allocated. +

+

+ In case of an invalid code point, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::next +

+

+ Available in version 1.0 and later. +

+

+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point and moves the iterator to the next position. +

+
+template <typename octet_iterator> 
+uint32_t next(octet_iterator& it, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = next(w, twochars + 6);
+assert (cp == 0x65e5);
+assert (w == twochars + 3);
+
+

+ This function is typically used to iterate through a UTF-8 encoded string. +

+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. +

+

+ utf8::peek_next +

+

+ Available in version 2.1 and later. +

+

+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point for the following sequence without changing the value of the iterator. +

+
+template <typename octet_iterator> 
+uint32_t peek_next(octet_iterator it, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ it: an iterator pointing to the beginning of an UTF-8 + encoded code point.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = peek_next(w, twochars + 6);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. +

+

+ utf8::prior +

+

+ Available in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 sequence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator> 
+uint32_t prior(octet_iterator& it, octet_iterator start);
+   
+
+

+ octet_iterator: a bidirectional iterator.
+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ start: an iterator to the beginning of the sequence where the search + for the beginning of a code point is performed. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars + 3;
+int cp = prior (w, twochars);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This function has two purposes: one is two iterate backwards through a UTF-8 + encoded string. Note that it is usually a better idea to iterate forward instead, + since utf8::next is faster. The second purpose is to find a beginning + of a UTF-8 sequence if we have a random position within a string. Note that in that + case utf8::prior may not detect an invalid UTF-8 sequence in some scenarios: + for instance if there are superfluous trail octets, it will just skip them. +

+

+ it will typically point to the beginning of + a code point, and start will point to the + beginning of the string to ensure we don't go backwards too far. it is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. +

+

+ In case start is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an invalid_utf8 + exception is thrown. +

+

In case start equals it, a not_enough_room + exception is thrown. +

+ utf8::previous +

+

+ Deprecated in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator> 
+uint32_t previous(octet_iterator& it, octet_iterator pass_start);
+   
+
+

+ octet_iterator: a random access iterator.
+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ pass_start: an iterator to the point in the sequence where the search + for the beginning of a code point is aborted if no result was reached. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars + 3;
+int cp = previous (w, twochars - 1);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ utf8::previous is deprecated, and utf8::prior should + be used instead, although the existing code can continue using this function. + The problem is the parameter pass_start that points to the position + just before the beginning of the sequence. Standard containers don't have the + concept of "pass start" and the function can not be used with their iterators. +

+

+ it will typically point to the beginning of + a code point, and pass_start will point to the octet just before the + beginning of the string to ensure we don't go backwards too far. it is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. +

+

+ In case pass_start is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an invalid_utf8 + exception is thrown +

+

+ utf8::advance +

+

+ Available in version 1.0 and later. +

+

+ Advances an iterator by the specified number of code points within an UTF-8 + sequence. +

+
+template <typename octet_iterator, typename distance_type> 
+void advance (octet_iterator& it, distance_type n, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ distance_type: an integral type convertible to octet_iterator's difference type.
+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.
+ n: a positive integer that shows how many code points we want to + advance.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars;
+advance (w, 2, twochars + 6);
+assert (w == twochars + 5);
+
+

+ This function works only "forward". In case of a negative n, there is + no effect. +

+

+ In case of an invalid code point, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::distance +

+

+ Available in version 1.0 and later. +

+

+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. +

+
+template <typename octet_iterator> 
+typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last);
+   
+
+

+ octet_iterator: an input iterator.
+ first: an iterator to a beginning of a UTF-8 encoded code point.
+ last: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.
+ Return value the distance between the iterators, + in code points. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+size_t dist = utf8::distance(twochars, twochars + 5);
+assert (dist == 2);
+
+

+ This function is used to find the length (in code points) of a UTF-8 encoded + string. The reason it is called distance, rather than, say, + length is mainly because developers are used that length is an + O(1) function. Computing the length of an UTF-8 string is a linear operation, and + it looked better to model it after std::distance algorithm. +

+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If last does not point to the past-of-end of a UTF-8 seqence, + a utf8::not_enough_room exception is thrown. +

+

+ utf8::utf16to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-16 encoded string to UTF-8. +

+
+template <typename u16bit_iterator, typename octet_iterator>
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+
+

+ u16bit_iterator: an input iterator.
+ octet_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e};
+vector<unsigned char> utf8result;
+utf16to8(utf16string, utf16string + 5, back_inserter(utf8result));
+assert (utf8result.size() == 10);    
+
+

+ In case of invalid UTF-16 sequence, a utf8::invalid_utf16 exception is + thrown. +

+

+ utf8::utf8to16 +

+

+ Available in version 1.0 and later. +

+

+ Converts an UTF-8 encoded string to UTF-16 +

+
+template <typename u16bit_iterator, typename octet_iterator>
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+
+

+ octet_iterator: an input iterator.
+ u16bit_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> end: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.
+ result: an output iterator to the place in the UTF-16 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-16 string. +

+

+ Example of use: +

+
+char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e";
+vector <unsigned short> utf16result;
+utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result));
+assert (utf16result.size() == 4);
+assert (utf16result[2] == 0xd834);
+assert (utf16result[3] == 0xdd1e);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If end does not point to the past-of-end of a UTF-8 seqence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::utf32to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-32 encoded string to UTF-8. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+
+

+ octet_iterator: an output iterator.
+ u32bit_iterator: an input iterator.
+ start: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+int utf32string[] = {0x448, 0x65E5, 0x10346, 0};
+vector<unsigned char> utf8result;
+utf32to8(utf32string, utf32string + 3, back_inserter(utf8result));
+assert (utf8result.size() == 9);
+
+

+ In case of invalid UTF-32 string, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::utf8to32 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-8 encoded string to UTF-32. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+
+

+ octet_iterator: an input iterator.
+ u32bit_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.
+ result: an output iterator to the place in the UTF-32 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-32 string. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+vector<int> utf32result;
+utf8to32(twochars, twochars + 5, back_inserter(utf32result));
+assert (utf32result.size() == 2);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If end does not point to the past-of-end of a UTF-8 seqence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::find_invalid +

+

+ Available in version 1.0 and later. +

+

+ Detects an invalid sequence within a UTF-8 string. +

+
+template <typename octet_iterator> 
+octet_iterator find_invalid(octet_iterator start, octet_iterator end);
+
+

+ octet_iterator: an input iterator.
+ start: an iterator pointing to the beginning of the UTF-8 string to + test for validity.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.
+ Return value: an iterator pointing to the first + invalid octet in the UTF-8 string. In case none were found, equals + end. +

+

+ Example of use: +

+
+char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa";
+char* invalid = find_invalid(utf_invalid, utf_invalid + 6);
+assert (invalid == utf_invalid + 5);
+
+

+ This function is typically used to make sure a UTF-8 string is valid before + processing it with other functions. It is especially important to call it if before + doing any of the unchecked operations on it. +

+

+ utf8::is_valid +

+

+ Available in version 1.0 and later. +

+

+ Checks whether a sequence of octets is a valid UTF-8 string. +

+
+template <typename octet_iterator> 
+bool is_valid(octet_iterator start, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ start: an iterator pointing to the beginning of the UTF-8 string to + test for validity.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.
+ Return value: true if the sequence + is a valid UTF-8 string; false if not. +

+ Example of use: +
+char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa";
+bool bvalid = is_valid(utf_invalid, utf_invalid + 6);
+assert (bvalid == false);
+
+

+ is_valid is a shorthand for find_invalid(start, end) == + end;. You may want to use it to make sure that a byte seqence is a valid + UTF-8 string without the need to know where it fails if it is not valid. +

+

+ utf8::replace_invalid +

+

+ Available in version 2.0 and later. +

+

+ Replaces all invalid UTF-8 sequences within a string with a replacement marker. +

+
+template <typename octet_iterator, typename output_iterator>
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement);
+template <typename octet_iterator, typename output_iterator>
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out);
+   
+
+

+ octet_iterator: an input iterator.
+ output_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-8 string to + look for invalid UTF-8 sequences.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to look + for invalid UTF-8 sequences.
+ out: An output iterator to the range where the result of replacement + is stored.
+ replacement: A Unicode code point for the replacement marker. The + version without this parameter assumes the value 0xfffd
+ Return value: An iterator pointing to the place + after the UTF-8 string with replaced invalid sequences. +

+

+ Example of use: +

+
+char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z";
+vector<char> replace_invalid_result;
+replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), '?');
+bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end());
+assert (bvalid);
+char* fixed_invalid_sequence = "a????z";
+assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence));
+
+

+ replace_invalid does not perform in-place replacement of invalid + sequences. Rather, it produces a copy of the original string with the invalid + sequences replaced with a replacement marker. Therefore, out must not + be in the [start, end] range. +

+

+ If end does not point to the past-of-end of a UTF-8 sequence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::starts_with_bom +

+

+ Available in version 2.3 and later. Relaces deprecated is_bom() function. +

+

+ Checks whether an octet sequence starts with a UTF-8 byte order mark (BOM) +

+
+template <typename octet_iterator> 
+bool starts_with_bom (octet_iterator it, octet_iterator end);
+
+

+ octet_iterator: an input iterator.
+ it: beginning of the octet sequence to check
+ end: pass-end of the sequence to check
+ Return value: true if the sequence + starts with a UTF-8 byte order mark; false if not. +

+

+ Example of use: +

+
+unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf};
+bool bbom = starts_with_bom(byte_order_mark, byte_order_mark + sizeof(byte_order_mark));
+assert (bbom == true);
+
+

+ The typical use of this function is to check the first three bytes of a file. If + they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 + encoded text. +

+

+ utf8::is_bom +

+

+ Available in version 1.0 and later. Deprecated in version 2.3. starts_with_bom() should be used + instead. +

+

+ Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM) +

+
+template <typename octet_iterator> 
+bool is_bom (octet_iterator it);  // Deprecated
+
+

+ octet_iterator: an input iterator.
+ it: beginning of the 3-octet sequence to check
+ Return value: true if the sequence + is UTF-8 byte order mark; false if not. +

+

+ Example of use: +

+
+unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf};
+bool bbom = is_bom(byte_order_mark);
+assert (bbom == true);
+
+

+ The typical use of this function is to check the first three bytes of a file. If + they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 + encoded text. +

+

+ If a sequence is + shorter than three bytes, an invalid iterator will be dereferenced. Therefore, this function is deprecated + in favor of starts_with_bom()that takes the end of sequence as an argument. +

+

+ Types From utf8 Namespace +

+

utf8::exception +

+

+ Available in version 2.3 and later. +

+

+ Base class for the exceptions thrown by UTF CPP library functions. +

+
+class exception : public std::exception {};
+
+

+ Example of use: +

+
+try {
+  code_that_uses_utf_cpp_library();
+}
+catch(const utf8::exception& utfcpp_ex) {
+  cerr << utfcpp_ex.what();
+}
+
+ +

utf8::invalid_code_point +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP functions such as advance and next if an UTF-8 sequence represents and invalid code point. +

+ +
+class invalid_code_point : public exception {
+public: 
+    uint32_t code_point() const;
+};
+
+
+

+ Member function code_point() can be used to determine the invalid code point that + caused the exception to be thrown. +

+

utf8::invalid_utf8 +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP functions such as next and prior if an invalid UTF-8 sequence + is detected during decoding. +

+ +
+class invalid_utf8 : public exception {
+public: 
+    uint8_t utf8_octet() const;
+};
+
+ +

+ Member function utf8_octet() can be used to determine the beginning of the byte + sequence that caused the exception to be thrown. +

+ +

utf8::invalid_utf16 +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP function utf16to8 if an invalid UTF-16 sequence + is detected during decoding. +

+ +
+class invalid_utf16 : public exception {
+public: 
+    uint16_t utf16_word() const;
+};
+
+ +

+ Member function utf16_word() can be used to determine the UTF-16 code unit + that caused the exception to be thrown. +

+

utf8::not_enough_room +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP functions such as next if the end of the decoded UTF-8 sequence + was reached before the code point was decoded. +

+ +
+class not_enough_room : public exception {};
+
+

+ utf8::iterator +

+

+ Available in version 2.0 and later. +

+

+ Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. +

+
+template <typename octet_iterator>
+class iterator;
+
+ +
Member functions
+
+
iterator();
the deafult constructor; the underlying octet_iterator is + constructed with its default constructor. +
explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end);
a constructor + that initializes the underlying octet_iterator with octet_it + and sets the range in which the iterator is considered valid. +
octet_iterator base () const;
returns the + underlying octet_iterator. +
uint32_t operator * () const;
decodes the utf-8 sequence + the underlying octet_iterator is pointing to and returns the code point. +
bool operator == (const iterator& rhs) + const;
returns true + if the two underlaying iterators are equal. +
bool operator != (const iterator& rhs) + const;
returns true + if the two underlaying iterators are not equal. +
iterator& operator ++ ();
the prefix increment - moves + the iterator to the next UTF-8 encoded code point. +
iterator operator ++ (int);
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. +
iterator& operator -- ();
the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. +
iterator operator -- (int);
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. +
+

+ Example of use: +

+
+char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88";
+utf8::iterator<char*> it(threechars, threechars, threechars + 9);
+utf8::iterator<char*> it2 = it;
+assert (it2 == it);
+assert (*it == 0x10346);
+assert (*(++it) == 0x65e5);
+assert ((*it++) == 0x65e5);
+assert (*it == 0x0448);
+assert (it != it2);
+utf8::iterator<char*> endit (threechars + 9, threechars, threechars + 9);  
+assert (++it == endit);
+assert (*(--it) == 0x0448);
+assert ((*it--) == 0x0448);
+assert (*it == 0x65e5);
+assert (--it == utf8::iterator<char*>(threechars, threechars, threechars + 9));
+assert (*it == 0x10346);
+
+

+ The purpose of utf8::iterator adapter is to enable easy iteration as well as the use of STL + algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of + utf8::next() and utf8::prior() functions. +

+

+ Note that utf8::iterator adapter is a checked iterator. It operates on the range specified in + the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators + require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically, + the range will be determined by sequence container functions begin and end, i.e.: +

+
+std::string s = "example";
+utf8::iterator i (s.begin(), s.begin(), s.end());
+
+

+ Functions From utf8::unchecked Namespace +

+

+ utf8::unchecked::append +

+

+ Available in version 1.0 and later. +

+

+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. +

+
+template <typename octet_iterator>
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+
+

+ cp: A 32 bit integer representing a code point to append to the + sequence.
+ result: An output iterator to the place in the sequence where to + append the code point.
+ Return value: An iterator pointing to the place + after the newly appended sequence. +

+

+ Example of use: +

+
+unsigned char u[5] = {0,0,0,0,0};
+unsigned char* end = unchecked::append(0x0448, u);
+assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0);
+
+

+ This is a faster but less safe version of utf8::append. It does not + check for validity of the supplied code point, and may produce an invalid UTF-8 + sequence. +

+

+ utf8::unchecked::next +

+

+ Available in version 1.0 and later. +

+

+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point + and moves the iterator to the next position. +

+
+template <typename octet_iterator>
+uint32_t next(octet_iterator& it);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = unchecked::next(w);
+assert (cp == 0x65e5);
+assert (w == twochars + 3);
+
+

+ This is a faster but less safe version of utf8::next. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::peek_next +

+

+ Available in version 2.1 and later. +

+

+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point. +

+
+template <typename octet_iterator>
+uint32_t peek_next(octet_iterator it);
+   
+
+

+ it: an iterator pointing to the beginning of an UTF-8 + encoded code point.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = unchecked::peek_next(w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This is a faster but less safe version of utf8::peek_next. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::prior +

+

+ Available in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator>
+uint32_t prior(octet_iterator& it);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars + 3;
+int cp = unchecked::prior (w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This is a faster but less safe version of utf8::prior. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::previous (deprecated, see utf8::unchecked::prior) +

+

+ Deprecated in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator>
+uint32_t previous(octet_iterator& it);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars + 3;
+int cp = unchecked::previous (w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ The reason this function is deprecated is just the consistency with the "checked" + versions, where prior should be used instead of previous. + In fact, unchecked::previous behaves exactly the same as + unchecked::prior +

+

+ This is a faster but less safe version of utf8::previous. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::advance +

+

+ Available in version 1.0 and later. +

+

+ Advances an iterator by the specified number of code points within an UTF-8 + sequence. +

+
+template <typename octet_iterator, typename distance_type>
+void advance (octet_iterator& it, distance_type n);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.
+ n: a positive integer that shows how many code points we want to + advance.
+

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+unchecked::advance (w, 2);
+assert (w == twochars + 5);
+
+

+ This function works only "forward". In case of a negative n, there is + no effect. +

+

+ This is a faster but less safe version of utf8::advance. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::distance +

+

+ Available in version 1.0 and later. +

+

+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. +

+
+template <typename octet_iterator>
+typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last);
+
+

+ first: an iterator to a beginning of a UTF-8 encoded code point.
+ last: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.
+ Return value the distance between the iterators, + in code points. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+size_t dist = utf8::unchecked::distance(twochars, twochars + 5);
+assert (dist == 2);
+
+

+ This is a faster but less safe version of utf8::distance. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::utf16to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-16 encoded string to UTF-8. +

+
+template <typename u16bit_iterator, typename octet_iterator>
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e};
+vector<unsigned char> utf8result;
+unchecked::utf16to8(utf16string, utf16string + 5, back_inserter(utf8result));
+assert (utf8result.size() == 10);    
+
+

+ This is a faster but less safe version of utf8::utf16to8. It does not + check for validity of the supplied UTF-16 sequence. +

+

+ utf8::unchecked::utf8to16 +

+

+ Available in version 1.0 and later. +

+

+ Converts an UTF-8 encoded string to UTF-16 +

+
+template <typename u16bit_iterator, typename octet_iterator>
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> end: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.
+ result: an output iterator to the place in the UTF-16 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-16 string. +

+

+ Example of use: +

+
+char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e";
+vector <unsigned short> utf16result;
+unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result));
+assert (utf16result.size() == 4);
+assert (utf16result[2] == 0xd834);
+assert (utf16result[3] == 0xdd1e);
+
+

+ This is a faster but less safe version of utf8::utf8to16. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::utf32to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-32 encoded string to UTF-8. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+int utf32string[] = {0x448, 0x65e5, 0x10346, 0};
+vector<unsigned char> utf8result;
+utf32to8(utf32string, utf32string + 3, back_inserter(utf8result));
+assert (utf8result.size() == 9);
+
+

+ This is a faster but less safe version of utf8::utf32to8. It does not + check for validity of the supplied UTF-32 sequence. +

+

+ utf8::unchecked::utf8to32 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-8 encoded string to UTF-32. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.
+ result: an output iterator to the place in the UTF-32 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-32 string. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+vector<int> utf32result;
+unchecked::utf8to32(twochars, twochars + 5, back_inserter(utf32result));
+assert (utf32result.size() == 2);
+
+

+ This is a faster but less safe version of utf8::utf8to32. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ Types From utf8::unchecked Namespace +

+

+ utf8::iterator +

+

+ Available in version 2.0 and later. +

+

+ Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. +

+
+template <typename octet_iterator>
+class iterator;
+
+ +
Member functions
+
+
iterator();
the deafult constructor; the underlying octet_iterator is + constructed with its default constructor. +
explicit iterator (const octet_iterator& octet_it); +
a constructor + that initializes the underlying octet_iterator with octet_it +
octet_iterator base () const;
returns the + underlying octet_iterator. +
uint32_t operator * () const;
decodes the utf-8 sequence + the underlying octet_iterator is pointing to and returns the code point. +
bool operator == (const iterator& rhs) + const;
returns true + if the two underlaying iterators are equal. +
bool operator != (const iterator& rhs) + const;
returns true + if the two underlaying iterators are not equal. +
iterator& operator ++ ();
the prefix increment - moves + the iterator to the next UTF-8 encoded code point. +
iterator operator ++ (int);
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. +
iterator& operator -- ();
the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. +
iterator operator -- (int);
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. +
+

+ Example of use: +

+
+char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88";
+utf8::unchecked::iterator<char*> un_it(threechars);
+utf8::unchecked::iterator<char*> un_it2 = un_it;
+assert (un_it2 == un_it);
+assert (*un_it == 0x10346);
+assert (*(++un_it) == 0x65e5);
+assert ((*un_it++) == 0x65e5);
+assert (*un_it == 0x0448);
+assert (un_it != un_it2);
+utf8::::unchecked::iterator<char*> un_endit (threechars + 9);  
+assert (++un_it == un_endit);
+assert (*(--un_it) == 0x0448);
+assert ((*un_it--) == 0x0448);
+assert (*un_it == 0x65e5);
+assert (--un_it == utf8::unchecked::iterator<char*>(threechars));
+assert (*un_it == 0x10346);
+
+

+ This is an unchecked version of utf8::iterator. It is faster in many cases, but offers + no validity or range checks. +

+

+ Points of interest +

+

+ Design goals and decisions +

+

+ The library was designed to be: +

+
    +
  1. + Generic: for better or worse, there are many C++ string classes out there, and + the library should work with as many of them as possible. +
  2. +
  3. + Portable: the library should be portable both accross different platforms and + compilers. The only non-portable code is a small section that declares unsigned + integers of different sizes: three typedefs. They can be changed by the users of + the library if they don't match their platform. The default setting should work + for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. +
  4. +
  5. + Lightweight: follow the "pay only for what you use" guideline. +
  6. +
  7. + Unintrusive: avoid forcing any particular design or even programming style on the + user. This is a library, not a framework. +
  8. +
+

+ Alternatives +

+

+ In case you want to look into other means of working with UTF-8 strings from C++, + here is the list of solutions I am aware of: +

+
    +
  1. + ICU Library. It is very powerful, + complete, feature-rich, mature, and widely used. Also big, intrusive, + non-generic, and doesn't play well with the Standard Library. I definitelly + recommend looking at ICU even if you don't plan to use it. +
  2. +
  3. + C++11 language and library features. Still far from complete, and not widely + supported by compiler vendors. +
  4. +
  5. + Glib::ustring. + A class specifically made to work with UTF-8 strings, and also feel like + std::string. If you prefer to have yet another string class in your + code, it may be worth a look. Be aware of the licensing issues, though. +
  6. +
  7. + Platform dependent solutions: Windows and POSIX have functions to convert strings + from one encoding to another. That is only a subset of what my library offers, + but if that is all you need it may be good enough. +
  8. +
+ +
    +
  1. + The Unicode Consortium. +
  2. +
  3. + ICU Library. +
  4. +
  5. + UTF-8 at Wikipedia +
  6. +
  7. + UTF-8 and Unicode FAQ for + Unix/Linux +
  8. +
+ + diff --git a/src/value.cpp b/src/value.cpp deleted file mode 100644 index 3a11de4ab..000000000 --- a/src/value.cpp +++ /dev/null @@ -1,734 +0,0 @@ -#include -#include -#include -#include -#include - - -namespace fc { - - namespace detail { - class value_visitor { - public: - virtual void operator()( int8_t& v ){}; - virtual void operator()( int16_t& v ){}; - virtual void operator()( int32_t& v ){}; - virtual void operator()( int64_t& v ){}; - virtual void operator()( uint8_t& v ){}; - virtual void operator()( uint16_t& v ){}; - virtual void operator()( uint32_t& v ){}; - virtual void operator()( uint64_t& v ){}; - virtual void operator()( float& v ){}; - virtual void operator()( double& v ){}; - virtual void operator()( bool& v ){}; - virtual void operator()( fc::string& v ){}; - virtual void operator()( value::object& ){}; - virtual void operator()( value::array& ){}; - virtual void operator()( ){}; - }; - template - struct cast_visitor : value::const_visitor { - cast_visitor( T& out ) - :m_out(out){} - virtual void operator()( const int8_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const int16_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const int32_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const int64_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const uint8_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const uint16_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const uint32_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const uint64_t& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const float& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const double& v ) { m_out = fc::numeric_cast(v); } - virtual void operator()( const bool& v ) { m_out = v; } - virtual void operator()( const fc::string& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const value::object& ) { FC_THROW_REPORT("bad cast to ${type} from object", - fc::value().set("type",fc::get_typename::name())); } - virtual void operator()( const value::array& ) { FC_THROW_REPORT("bad cast to ${type} from array", - fc::value().set("type",fc::get_typename::name())); } - virtual void operator()( ) { FC_THROW_REPORT("bad cast to ${type} from null", - fc::value().set("type",fc::get_typename::name())); } - private: - T& m_out; - }; - - template<> - struct cast_visitor : value::const_visitor { - cast_visitor( bool& out ) - :m_out(out){} - virtual void operator()( const int8_t& v ){ m_out = v != 0; } - virtual void operator()( const int16_t& v ){ m_out = v != 0; } - virtual void operator()( const int32_t& v ){ m_out = v != 0; } - virtual void operator()( const int64_t& v ){ m_out = v != 0; } - virtual void operator()( const uint8_t& v ){ m_out = v != 0; } - virtual void operator()( const uint16_t& v ){ m_out = v != 0; } - virtual void operator()( const uint32_t& v ){ m_out = v != 0; } - virtual void operator()( const uint64_t& v ){ m_out = v != 0; } - virtual void operator()( const float& v ){ m_out = v != 0; } - virtual void operator()( const double& v ){ m_out = v != 0; } - virtual void operator()( const bool& v ){ m_out = v; } - virtual void operator()( const fc::string& v ) { m_out = !(v != "true"); } - virtual void operator()( const value::object& ) { FC_THROW_REPORT("bad cast to bool from object"); } - virtual void operator()( const value::array& ) { FC_THROW_REPORT("bad cast to bool from array"); } - virtual void operator()( ) { FC_THROW_REPORT("bad cast to bool from null"); } - private: - bool& m_out; - }; - - template<> - struct cast_visitor : value::const_visitor { - cast_visitor( fc::string& out ) - :m_out(out){} - virtual void operator()( const int8_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const int16_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const int32_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const int64_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const uint8_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const uint16_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const uint32_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const uint64_t& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const float& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const double& v ) { m_out = fc::lexical_cast(v); } - virtual void operator()( const bool& v ) { m_out = v != 0 ? "true" : "false"; } - virtual void operator()( const fc::string& v ) { m_out = v; } - virtual void operator()( const value::object& ){ FC_THROW_REPORT("bad cast to string from object"); } - virtual void operator()( const value::array& ) { FC_THROW_REPORT("bad cast to string from array"); } - virtual void operator()( ) { m_out = fc::string(); } - - private: - fc::string& m_out; - }; - - template<> - struct cast_visitor : value::const_visitor { - cast_visitor( value::array& out ) - :m_out(out){} - virtual void operator()( const int8_t& v ) { FC_THROW_REPORT("bad cast to array from int8");} - virtual void operator()( const int16_t& v ) { FC_THROW_REPORT("bad cast to array from int16");} - virtual void operator()( const int32_t& v ) { FC_THROW_REPORT("bad cast to array from int32");} - virtual void operator()( const int64_t& v ) { FC_THROW_REPORT("bad cast to array from int32");} - virtual void operator()( const uint8_t& v ) { FC_THROW_REPORT("bad cast to array from uint8");} - virtual void operator()( const uint16_t& v ) { FC_THROW_REPORT("bad cast to array from uint16");} - virtual void operator()( const uint32_t& v ) { FC_THROW_REPORT("bad cast to array from uint32");} - virtual void operator()( const uint64_t& v ) { FC_THROW_REPORT("bad cast to array from uint64");} - virtual void operator()( const float& v ) { FC_THROW_REPORT("bad cast to array from float");} - virtual void operator()( const double& v ) { FC_THROW_REPORT("bad cast to array from double");} - virtual void operator()( const bool& v ) { FC_THROW_REPORT("bad cast to array from bool");} - virtual void operator()( const fc::string& v ) { FC_THROW_REPORT("bad cast to array from string");} - virtual void operator()( const value::object& ) { FC_THROW_REPORT("bad cast to array from object");} - virtual void operator()( const value::array& a ) { m_out = a; } - virtual void operator()( ) { m_out = value::array(); } - - private: - value::array& m_out; - }; - - template<> - struct cast_visitor : value::const_visitor { - cast_visitor( value::object& out ) - :m_out(out){} - virtual void operator()( const int8_t& v ){ FC_THROW_REPORT("bad cast to array from int8");} - virtual void operator()( const int16_t& v ){ FC_THROW_REPORT("bad cast to array from int16");} - virtual void operator()( const int32_t& v ){ FC_THROW_REPORT("bad cast to array from int32");} - virtual void operator()( const int64_t& v ){ FC_THROW_REPORT("bad cast to array from int32");} - virtual void operator()( const uint8_t& v ){ FC_THROW_REPORT("bad cast to array from uint8");} - virtual void operator()( const uint16_t& v ){ FC_THROW_REPORT("bad cast to array from uint16");} - virtual void operator()( const uint32_t& v ){ FC_THROW_REPORT("bad cast to array from uint32");} - virtual void operator()( const uint64_t& v ){ FC_THROW_REPORT("bad cast to array from uint64");} - virtual void operator()( const float& v ){ FC_THROW_REPORT("bad cast to array from float");} - virtual void operator()( const double& v ){ FC_THROW_REPORT("bad cast to array from double");} - virtual void operator()( const bool& v ){ FC_THROW_REPORT("bad cast to array from bool");} - virtual void operator()( const fc::string& v ) { FC_THROW_REPORT("bad cast to array from string");} - virtual void operator()( const value::object& a ) { m_out = a; } - virtual void operator()( const value::array& ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( ) { m_out = value::object(); } - - private: - value::object& m_out; - }; - template<> - struct cast_visitor : value::const_visitor { - cast_visitor( value& out ) - :m_out(out){} - virtual void operator()( const int8_t& v ) { m_out = v; } - virtual void operator()( const int16_t& v ) { m_out = v; } - virtual void operator()( const int32_t& v ) { m_out = v; } - virtual void operator()( const int64_t& v ) { m_out = v; } - virtual void operator()( const uint8_t& v ) { m_out = v; } - virtual void operator()( const uint16_t& v ) { m_out = v; } - virtual void operator()( const uint32_t& v ) { m_out = v; } - virtual void operator()( const uint64_t& v ) { m_out = v; } - virtual void operator()( const float& v ) { m_out = v; } - virtual void operator()( const double& v ) { m_out = v; } - virtual void operator()( const bool& v ) { m_out = v; } - virtual void operator()( const fc::string& v ) { m_out = v; } - virtual void operator()( const value::object& a ) { m_out = a; } - virtual void operator()( const value::array& a ) { m_out = a; } - virtual void operator()( ) { m_out = value(); } - - value& m_out; - }; - - template<> - struct cast_visitor : value::const_visitor { - virtual void operator()( const int8_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const int16_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const int32_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const int64_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const uint8_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const uint16_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const uint32_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const uint64_t& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const float& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const double& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const bool& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const fc::string& v ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const value::object& a ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( const value::array& ) { FC_THROW_REPORT("bad cast");} - virtual void operator()( ) { } - }; - - void cast_value( const value& v, int8_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, int16_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, int32_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, int64_t& out ){ - v.visit( cast_visitor(out) ); - slog( "cast_value( v, int64: %lld )", out ); - } - - void cast_value( const value& v, uint8_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, uint16_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, uint32_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, uint64_t& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, double& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, float& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, bool& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, fc::string& out ){ - v.visit( cast_visitor(out) ); - } - - void cast_value( const value& v, value& out ){ - out = v; - } - - - struct value_holder { - virtual ~value_holder(); - virtual value::value_type type()const; - const char* get_typename()const { return fc::reflector::to_string(type()); } - virtual void visit( value::const_visitor&& v )const; - virtual void visit( value_visitor&& v ); - - virtual void clear(); - virtual size_t size()const; - virtual void resize( size_t ); - virtual void reserve( size_t ); - virtual value& at( size_t ); - virtual const value& at( size_t )const; - virtual void push_back( value&& v ); - - virtual value_holder* move_helper( char* c ); - virtual value_holder* copy_helper( char* c )const; - }; - - void value_holder::visit( value::const_visitor&& v )const {v(); } - void value_holder::visit( value_visitor&& v ) {v(); } - - template - struct get_value_type{}; - template<> struct get_value_type { static value::value_type type(){ return value::null_type; } }; - template<> struct get_value_type { static value::value_type type(){ return value::int8_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::int16_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::int32_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::int64_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::uint8_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::uint16_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::uint32_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::uint64_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::double_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::float_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::string_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::bool_type; } }; - template<> struct get_value_type>{ static value::value_type type(){ return value::array_type; } }; - template<> struct get_value_type{ static value::value_type type(){ return value::object_type; } }; - - // fundamental values... - template - struct value_holder_impl : value_holder { - static_assert( !fc::is_class::value, "only fundamental types can be stored without specialization" ); - - value_holder_impl(){ - static_assert( sizeof(value_holder_impl) <= 40, "Validate size" ); - } - virtual value::value_type type()const { return get_value_type::type(); } - virtual void visit( value::const_visitor&& v )const{ v(val); } - virtual void visit( value_visitor&& v ) { v(val); } - virtual void clear() { val = T(); } - virtual size_t size()const { return 0; } - - virtual value_holder* move_helper( char* c ){ return new(c) value_holder_impl( fc::move(val) ); } - virtual value_holder* copy_helper( char* c )const{ return new(c) value_holder_impl(val); } - - template - value_holder_impl( V&& v ):val( fc::forward(v) ){} - - T val; - }; - - - template<> - struct value_holder_impl : value_holder { - value_holder_impl(){}; - virtual void visit( value::const_visitor&& v )const{ v(); } - virtual void visit( value_visitor&& v ) { v(); } - virtual value_holder* move_helper( char* c ) { return new(c) value_holder_impl(); } - virtual value_holder* copy_helper( char* c )const{ return new(c) value_holder_impl(); } - }; - - - template<> - struct value_holder_impl : value_holder { - template - value_holder_impl( V&& v ):val( fc::forward(v) ){ - static_assert( sizeof(value_holder_impl) <= 40, "Validate size" ); - } - - virtual value::value_type type()const { return value::string_type; } - virtual void visit( value::const_visitor&& v )const { v(val); } - virtual void visit( value_visitor&& v ) { v(val); } - - virtual value_holder* move_helper( char* c ){ return new(c) value_holder_impl( fc::move(val) ); } - virtual value_holder* copy_helper( char* c )const{ return new(c) value_holder_impl(val); } - - virtual void clear() { val = fc::string(); } - virtual size_t size()const { FC_THROW_REPORT( "Attempt to access string as array" ); } - - fc::string val; - }; - - template<> - struct value_holder_impl : value_holder { - virtual value::value_type type()const { return value::object_type; } - virtual void visit( value::const_visitor&& v )const; - virtual void visit( value_visitor&& v ); - virtual value_holder* move_helper( char* c ); - virtual value_holder* copy_helper( char* c )const; - virtual void reserve( size_t s ); - - virtual void clear(); - virtual size_t size()const; - - template - value_holder_impl( V&& v ):val( fc::forward(v) ){} - - value::object val; - }; - - template<> - struct value_holder_impl : value_holder { - virtual value::value_type type()const { return value::array_type; } - virtual void visit( value::const_visitor&& v )const; - virtual void visit( value_visitor&& v ); - virtual value_holder* move_helper( char* c ); - virtual value_holder* copy_helper( char* c )const; - - virtual void resize( size_t s ); - virtual void reserve( size_t s ); - virtual value& at( size_t i); - virtual const value& at( size_t i)const; - virtual void push_back( value&& v ); - - template - value_holder_impl( V&& v ):val( fc::forward(v) ){} - - virtual void clear(); - virtual size_t size()const; - - value::array val; - }; - static_assert( sizeof( value_holder_impl ) <= 40, "sanity check" ); - static_assert( sizeof( value_holder_impl ) <= 40, "sanity check" ); - - value_holder::~value_holder(){} - value::value_type value_holder::type()const { return value::null_type; } - value_holder* value_holder::move_helper( char* c ) { return new(c) value_holder_impl(); } - value_holder* value_holder::copy_helper( char* c )const { return new(c) value_holder_impl(); } - - void new_value_holder_void( value* v ) { - new (v) value_holder_impl(); - } - - void value_holder::clear() {} - size_t value_holder::size()const { return 0; } - void value_holder::resize( size_t ) { FC_THROW_MSG("value type '%s' not an array", get_typename()); } - void value_holder::reserve( size_t ) { FC_THROW_MSG("value type '%s' not an array or object", get_typename()); } - value& value_holder::at( size_t ) { FC_THROW_MSG("value type '%s' not an array", get_typename()); return *((value*)0); } - const value& value_holder::at( size_t )const { FC_THROW_MSG("value type '%s' not an array", get_typename()); return *((const value*)0); } - void value_holder::push_back( value&& v ) { FC_THROW_MSG("value type '%s' not an array", get_typename()); } - - - void value_holder_impl::resize( size_t s ) { val.resize(s); } - void value_holder_impl::reserve( size_t s ) { val.reserve(s); } - value& value_holder_impl::at( size_t i) { return val[i]; } - const value& value_holder_impl::at( size_t i)const { return val[i]; } - value_holder* value_holder_impl::move_helper( char* c ){ return new(c) value_holder_impl( fc::move(val) ); } - value_holder* value_holder_impl::copy_helper( char* c )const{ return new(c) value_holder_impl(val); } - - void value_holder_impl::clear() { val.clear(); } - size_t value_holder_impl::size()const { return static_cast(val.size()); } - void value_holder_impl::visit( value::const_visitor&& v )const { v(val); } - void value_holder_impl::visit( value_visitor&& v ) { v(val); } - void value_holder_impl::push_back( value&& v ) { val.push_back( fc::move(v) ); } - - - void value_holder_impl::visit( value::const_visitor&& v )const { v(val); } - void value_holder_impl::visit( value_visitor&& v ) { v(val); } - value_holder* value_holder_impl::move_helper( char* c ) { return new(c) value_holder_impl( fc::move(val) ); } - value_holder* value_holder_impl::copy_helper( char* c )const { return new(c) value_holder_impl(val); } - void value_holder_impl::reserve( size_t s ) { val.fields.reserve(s); } - - void value_holder_impl::clear() { val = value::object(); } - size_t value_holder_impl::size()const { return val.fields.size(); } - } // namespace detail - -static detail::value_holder* gh( aligned<40>& h ) { - return (detail::value_holder*)h._store._data; -} -static const detail::value_holder* gh( const aligned<40>& h ) { - return (const detail::value_holder*)h._store._data; -} - -value::value() { - new (holder) detail::value_holder_impl(); -} -value::value( value&& m ) { - gh(m.holder)->move_helper(holder._store._data); -} -value::value( const value& m ){ - gh(m.holder)->copy_helper(holder._store._data); -} -value::value( const char* c ) { - new (holder) detail::value_holder_impl( c ); -} -value::value( char* c ) { - new (holder) detail::value_holder_impl( c ); -} -value::~value() { - gh(holder)->~value_holder(); -} -value::value( int8_t v){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(v); -} -value::value( int16_t v){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(v); -} -value::value( int32_t v){ - new (holder) detail::value_holder_impl(v); -} -value::value( int64_t v){ - new (holder) detail::value_holder_impl(v); -} -value::value( uint8_t v){ - new (holder) detail::value_holder_impl(v); -} -value::value( uint16_t v){ - new (holder) detail::value_holder_impl(v); -} -value::value( uint32_t v){ - new (holder) detail::value_holder_impl(v); -} -value::value( uint64_t v){ - new (holder) detail::value_holder_impl(v); -} -value::value( double v){ - new (holder) detail::value_holder_impl(v); -} -value::value( float v){ - new (holder) detail::value_holder_impl(v); -} -value::value( bool v){ - new (holder) detail::value_holder_impl(v); -} -value::value( fc::string&& v){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(fc::move(v)); -} -value::value( fc::string& v){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(v); -} -value::value( const fc::string& v){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(v); -} -value::value( const fc::string& v, const value& val ) { - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(value::object()); - set( v, val ); -} -value::value( value::object&& o ){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(fc::move(o)); -} -value::value( value::array&& a ){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(fc::move(a)); -} -value::value( const value::array& a ){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(a); -} -value::value( value::array& a ){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(a); -} -value::value( const value::object& a ){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(a); -} -value::value( value::object& a ){ - static_assert( sizeof(holder) >= sizeof( detail::value_holder_impl ), "size check" ); - new (holder) detail::value_holder_impl(a); -} -bool operator == ( const value& v, std::nullptr_t ) { - return v.is_null(); -} -bool operator != ( const value& v, std::nullptr_t ) { - return v.is_null(); -} - -value& value::operator=( value&& v ){ - decltype(holder) tmp; - gh(holder)->move_helper(tmp); - gh(v.holder)->move_helper(holder); - gh(tmp)->move_helper(v.holder); - return *this; -} -value& value::operator=( const value& v ){ - if( this == &v ) return *this; - gh(holder)->~value_holder(); - gh(v.holder)->copy_helper(holder); - return *this; -} -bool value::is_null()const { - return gh(holder)->type() == null_type; -} -bool value::is_object()const { - return gh(holder)->type() == object_type; -} -bool value::is_array()const { - return gh(holder)->type() == array_type; -} -bool value::is_string()const { - return gh(holder)->type() == string_type; -} - -const fc::vector& value::as_array()const { - if( gh(holder)->type() != array_type ) { - FC_THROW_REPORT( "Attempt to dereference value of type ${type} as value array", value("type",gh(holder)->get_typename() ) ); - } - const detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val; -} -fc::vector& value::as_array(){ - if( gh(holder)->type() != array_type ) { - FC_THROW_REPORT( "Attempt to dereference value of type ${type} as value array", value("type",gh(holder)->get_typename() ) ); - } - detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val; -} -const value::object& value::as_object()const { - if( gh(holder)->type() != object_type ) { - FC_THROW_REPORT( "Attempt to dereference value of type ${type} as value object", value("type",gh(holder)->get_typename() ) ); - } - const detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val; -} -value::object& value::as_object(){ - if( gh(holder)->type() != object_type ) { - FC_THROW_REPORT( "Attempt to dereference value of type ${type} as value object", value("type",gh(holder)->get_typename() ) ); - } - detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val; -} -const fc::string& value::as_string()const { - if( gh(holder)->type() != string_type ) { - FC_THROW_REPORT( "Attempt to dereference value of type ${type} as value string", value("type",gh(holder)->get_typename() ) ); - } - const detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val; -} -fc::string& value::as_string(){ - if( gh(holder)->type() != string_type ) { - FC_THROW_REPORT( "Attempt to dereference value of type ${type} as value string", value("type",gh(holder)->get_typename() ) ); - } - detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val; -} - - -value::object::const_iterator value::find( const char* key )const { - if( gh(holder)->type() == object_type ) { - const detail::value_holder_impl* o = static_cast*>(gh(holder)); - for( auto i = o->val.fields.begin(); - i != o->val.fields.end(); ++i ) { - if( strcmp( i->key.c_str(), key ) == 0 ) - return i; - } - return o->val.fields.end(); - } - //FC_THROW_MSG( "Bad cast of %s to object", gh(holder)->type() ); - return value::object::const_iterator(); -} -value::object::const_iterator value::begin()const { - if( gh(holder)->type() == object_type ) { - const detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val.fields.begin(); - } - //// FC_THROW_MSG( "Bad cast of %s to object", gh(holder)->type() ); - return value::object::const_iterator(); - //return nullptr; -} -value::object::const_iterator value::end()const { - if( gh(holder)->type()== object_type ) { - const detail::value_holder_impl* o = static_cast*>(gh(holder)); - return o->val.fields.end(); - } - ////FC_THROW_MSG( "Bad cast of %s to object", gh(holder)->type() ); - return value::object::const_iterator(); - //return nullptr; -} -/** - * If this value is an object, remove key from the object - * - * @return *this; - */ -value& value::clear( const fc::string& key ) { - if( gh(holder)->type()== object_type ) { - detail::value_holder_impl* o = dynamic_cast*>(gh(holder)); - for( auto i = o->val.fields.begin(); - i != o->val.fields.end(); ++i ) { - if( strcmp( i->key.c_str(), key.c_str() ) == 0 ) { - o->val.fields.erase(i); - return *this; - } - } - } - return *this; -} -value& value::operator[]( const char* key ) { - if( gh(holder)->type()== object_type ) { - detail::value_holder_impl* o = dynamic_cast*>(gh(holder)); - for( auto i = o->val.fields.begin(); - i != o->val.fields.end(); ++i ) { - if( strcmp( i->key.c_str(), key ) == 0 ) - return i->val; - } - o->val.fields.reserve(o->val.fields.size()+1); - o->val.fields.push_back( key_val(key) ); - return o->val.fields.back().val; - } else if (gh(holder)->type() == null_type ) { - new (gh(holder)) detail::value_holder_impl(value::object()); - return (*this)[key]; - } - FC_THROW_REPORT( "Bad cast of ${type} to object", fc::value().set("type", gh(holder)->get_typename()) ); - return *((value*)0); -} -value& value::operator[]( const fc::string& key ) { return (*this)[key.c_str()]; } -const value& value::operator[]( const fc::string& key )const { return (*this)[key.c_str()]; } -const value& value::operator[]( const char* key )const { - auto i = find(key); - if( i == end() ) { - FC_THROW_MSG( "Key '%s' not found in object", key ); - } - return i->val; -} - -void value::clear() { - gh(holder)->clear(); -} -size_t value::size()const { - return gh(holder)->size(); -} -void value::resize( size_t s ) { - gh(holder)->resize(s); -} -void value::reserve( size_t s ) { - gh(holder)->reserve(s); -} -value& value::push_back( value&& v ) { - if (gh(holder)->type() == null_type ) { - new (gh(holder)) detail::value_holder_impl(value::array()); - return push_back( fc::move(v) ); - } - gh(holder)->push_back(fc::move(v)); - return *this; -} -value& value::push_back( const value& v ) { - if (gh(holder)->type() == null_type ) { - new (gh(holder)) detail::value_holder_impl(value::array()); - return push_back( v ); - } - gh(holder)->push_back(value(v)); - return *this; -} -value& value::operator[]( int32_t idx ) { - return gh(holder)->at(idx); -} -const value& value::operator[]( int32_t idx )const { - return gh(holder)->at(idx); -} - -value::value_type value::type()const { return gh(holder)->type(); } - -void value::visit( value::const_visitor&& v )const { - auto h = ((detail::value_holder*)&holder[0]); - h->visit( fc::move(v) ); -} -/* sets the subkey key with v and return *this */ -value& value::set( const char* key, fc::value v ) { - (*this)[key] = fc::move(v); - return *this; -} -value& value::operator()( const char* key, fc::value v ) { - (*this)[key] = fc::move(v); - return *this; -} -value& value::set( const fc::string& key, fc::value v ) { - (*this)[key.c_str()] = fc::move(v); - return *this; -} - -} // namepace fc diff --git a/src/value_cast.cpp b/src/value_cast.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/variant.cpp b/src/variant.cpp new file mode 100644 index 000000000..542ee66fa --- /dev/null +++ b/src/variant.cpp @@ -0,0 +1,892 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fc +{ +/** + * The TypeID is stored in the 'last byte' of the variant. + */ +void set_variant_type( variant* v, variant::type_id t) +{ + char* data = reinterpret_cast(v); + data[ sizeof(variant) -1 ] = t; +} + +variant::variant() +{ + set_variant_type( this, null_type ); +} + +variant::variant( fc::nullptr_t ) +{ + set_variant_type( this, null_type ); +} + +variant::variant( uint8_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, uint64_type ); +} + +variant::variant( int8_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, int64_type ); +} + +variant::variant( uint16_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, uint64_type ); +} + +variant::variant( int16_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, int64_type ); +} + +variant::variant( uint32_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, uint64_type ); +} + +variant::variant( int32_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, int64_type ); +} + +variant::variant( uint64_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, uint64_type ); +} + +variant::variant( int64_t val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, int64_type ); +} + +variant::variant( float val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, double_type ); +} + +variant::variant( double val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, double_type ); +} + +variant::variant( bool val ) +{ + *reinterpret_cast(this) = val; + set_variant_type( this, bool_type ); +} + +variant::variant( char* str ) +{ + *reinterpret_cast(this) = new string( str ); + set_variant_type( this, string_type ); +} + +variant::variant( const char* str ) +{ + *reinterpret_cast(this) = new string( str ); + set_variant_type( this, string_type ); +} + +// TODO: do a proper conversion to utf8 +variant::variant( wchar_t* str ) +{ + size_t len = wcslen(str); + boost::scoped_array buffer(new char[len]); + for (unsigned i = 0; i < len; ++i) + buffer[i] = (char)str[i]; + *reinterpret_cast(this) = new string(buffer.get(), len); + set_variant_type( this, string_type ); +} + +// TODO: do a proper conversion to utf8 +variant::variant( const wchar_t* str ) +{ + size_t len = wcslen(str); + boost::scoped_array buffer(new char[len]); + for (unsigned i = 0; i < len; ++i) + buffer[i] = (char)str[i]; + *reinterpret_cast(this) = new string(buffer.get(), len); + set_variant_type( this, string_type ); +} + +variant::variant( fc::string val ) +{ + *reinterpret_cast(this) = new string( fc::move(val) ); + set_variant_type( this, string_type ); +} +variant::variant( blob val ) +{ + *reinterpret_cast(this) = new blob( fc::move(val) ); + set_variant_type( this, blob_type ); +} + +variant::variant( variant_object obj) +{ + *reinterpret_cast(this) = new variant_object(fc::move(obj)); + set_variant_type(this, object_type ); +} +variant::variant( mutable_variant_object obj) +{ + *reinterpret_cast(this) = new variant_object(fc::move(obj)); + set_variant_type(this, object_type ); +} + +variant::variant( variants arr ) +{ + *reinterpret_cast(this) = new variants(fc::move(arr)); + set_variant_type(this, array_type ); +} + + +typedef const variant_object* const_variant_object_ptr; +typedef const variants* const_variants_ptr; +typedef const blob* const_blob_ptr; +typedef const string* const_string_ptr; + +void variant::clear() +{ + switch( get_type() ) + { + case object_type: + delete *reinterpret_cast(this); + break; + case array_type: + delete *reinterpret_cast(this); + break; + case string_type: + delete *reinterpret_cast(this); + break; + default: + break; + } + set_variant_type( this, null_type ); +} + +variant::variant( const variant& v ) +{ + switch( v.get_type() ) + { + case object_type: + *reinterpret_cast(this) = + new variant_object(**reinterpret_cast(&v)); + set_variant_type( this, object_type ); + return; + case array_type: + *reinterpret_cast(this) = + new variants(**reinterpret_cast(&v)); + set_variant_type( this, array_type ); + return; + case string_type: + *reinterpret_cast(this) = + new string(**reinterpret_cast(&v) ); + set_variant_type( this, string_type ); + return; + default: + memcpy( this, &v, sizeof(v) ); + } +} + +variant::variant( variant&& v ) +{ + memcpy( this, &v, sizeof(v) ); + set_variant_type( &v, null_type ); +} + +variant::~variant() +{ + clear(); +} + +variant& variant::operator=( variant&& v ) +{ + if( this == &v ) return *this; + clear(); + memcpy( (char*)this, (char*)&v, sizeof(v) ); + set_variant_type( &v, null_type ); + return *this; +} + +variant& variant::operator=( const variant& v ) +{ + if( this == &v ) + return *this; + + clear(); + switch( v.get_type() ) + { + case object_type: + *reinterpret_cast(this) = + new variant_object((**reinterpret_cast(&v))); + break; + case array_type: + *reinterpret_cast(this) = + new variants((**reinterpret_cast(&v))); + break; + case string_type: + *reinterpret_cast(this) = new string((**reinterpret_cast(&v)) ); + break; + + default: + memcpy( this, &v, sizeof(v) ); + } + set_variant_type( this, v.get_type() ); + return *this; +} + +void variant::visit( const visitor& v )const +{ + switch( get_type() ) + { + case null_type: + v.handle(); + return; + case int64_type: + v.handle( *reinterpret_cast(this) ); + return; + case uint64_type: + v.handle( *reinterpret_cast(this) ); + return; + case double_type: + v.handle( *reinterpret_cast(this) ); + return; + case bool_type: + v.handle( *reinterpret_cast(this) ); + return; + case string_type: + v.handle( **reinterpret_cast(this) ); + return; + case array_type: + v.handle( **reinterpret_cast(this) ); + return; + case object_type: + v.handle( **reinterpret_cast(this) ); + return; + default: + FC_THROW_EXCEPTION( assert_exception, "Invalid Type / Corrupted Memory" ); + } +} + +variant::type_id variant::get_type()const +{ + return (type_id)reinterpret_cast(this)[sizeof(*this)-1]; +} + +bool variant::is_null()const +{ + return get_type() == null_type; +} + +bool variant::is_string()const +{ + return get_type() == string_type; +} +bool variant::is_bool()const +{ + return get_type() == bool_type; +} +bool variant::is_double()const +{ + return get_type() == double_type; +} +bool variant::is_uint64()const +{ + return get_type() == uint64_type; +} +bool variant::is_int64()const +{ + return get_type() == int64_type; +} + +bool variant::is_integer()const +{ + switch( get_type() ) + { + case int64_type: + case uint64_type: + case bool_type: + return true; + default: + return false; + } + return false; +} +bool variant::is_numeric()const +{ + switch( get_type() ) + { + case int64_type: + case uint64_type: + case double_type: + case bool_type: + return true; + default: + return false; + } + return false; +} + +bool variant::is_object()const +{ + return get_type() == object_type; +} + +bool variant::is_array()const +{ + return get_type() == array_type; +} +bool variant::is_blob()const +{ + return get_type() == blob_type; +} + +int64_t variant::as_int64()const +{ + switch( get_type() ) + { + case string_type: + return to_int64(**reinterpret_cast(this)); + case double_type: + return int64_t(*reinterpret_cast(this)); + case int64_type: + return *reinterpret_cast(this); + case uint64_type: + return int64_t(*reinterpret_cast(this)); + case bool_type: + return *reinterpret_cast(this); + case null_type: + return 0; + default: + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to int64", ("type", get_type()) ); + } +} + +uint64_t variant::as_uint64()const +{ try { + switch( get_type() ) + { + case string_type: + return to_uint64(**reinterpret_cast(this)); + case double_type: + return static_cast(*reinterpret_cast(this)); + case int64_type: + return static_cast(*reinterpret_cast(this)); + case uint64_type: + return *reinterpret_cast(this); + case bool_type: + return static_cast(*reinterpret_cast(this)); + case null_type: + return 0; + default: + FC_THROW_EXCEPTION( bad_cast_exception,"Invalid cast from ${type} to uint64", ("type",get_type())); + } +} FC_CAPTURE_AND_RETHROW( (*this) ) } + + +double variant::as_double()const +{ + switch( get_type() ) + { + case string_type: + return to_double(**reinterpret_cast(this)); + case double_type: + return *reinterpret_cast(this); + case int64_type: + return static_cast(*reinterpret_cast(this)); + case uint64_type: + return static_cast(*reinterpret_cast(this)); + case bool_type: + return *reinterpret_cast(this); + case null_type: + return 0; + default: + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to double", ("type",get_type()) ); + } +} + +bool variant::as_bool()const +{ + switch( get_type() ) + { + case string_type: + { + const string& s = **reinterpret_cast(this); + if( s == "true" ) + return true; + if( s == "false" ) + return false; + FC_THROW_EXCEPTION( bad_cast_exception, "Cannot convert string to bool (only \"true\" or \"false\" can be converted)" ); + } + case double_type: + return *reinterpret_cast(this) != 0.0; + case int64_type: + return *reinterpret_cast(this) != 0; + case uint64_type: + return *reinterpret_cast(this) != 0; + case bool_type: + return *reinterpret_cast(this); + case null_type: + return false; + default: + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to bool" , ("type",get_type())); + } +} + +string variant::as_string()const +{ + switch( get_type() ) + { + case string_type: + return **reinterpret_cast(this); + case double_type: + return to_string(*reinterpret_cast(this)); + case int64_type: + return to_string(*reinterpret_cast(this)); + case uint64_type: + return to_string(*reinterpret_cast(this)); + case bool_type: + return *reinterpret_cast(this) ? "true" : "false"; + case blob_type: + if( get_blob().data.size() ) + return base64_encode( get_blob().data.data(), get_blob().data.size() ) + "="; + return string(); + case null_type: + return string(); + default: + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to string", ("type", get_type() ) ); + } +} + + +/// @throw if get_type() != array_type | null_type +variants& variant::get_array() +{ + if( get_type() == array_type ) + return **reinterpret_cast(this); + + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to Array", ("type",get_type()) ); +} +blob& variant::get_blob() +{ + if( get_type() == blob_type ) + return **reinterpret_cast(this); + + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to Blob", ("type",get_type()) ); +} +const blob& variant::get_blob()const +{ + if( get_type() == blob_type ) + return **reinterpret_cast(this); + + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to Blob", ("type",get_type()) ); +} + +blob variant::as_blob()const +{ + switch( get_type() ) + { + case null_type: return blob(); + case blob_type: return get_blob(); + case string_type: + { + const string& str = get_string(); + if( str.size() == 0 ) return blob(); + if( str.back() == '=' ) + { + std::string b64 = base64_decode( get_string() ); + return blob( { std::vector( b64.begin(), b64.end() ) } ); + } + return blob( { std::vector( str.begin(), str.end() ) } ); + } + case object_type: + case array_type: + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to Blob", ("type",get_type()) ); + default: + return blob( { std::vector( (char*)&_data, (char*)&_data + sizeof(_data) ) } ); + } +} + + +/// @throw if get_type() != array_type +const variants& variant::get_array()const +{ + if( get_type() == array_type ) + return **reinterpret_cast(this); + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to Array", ("type",get_type()) ); +} + + +/// @throw if get_type() != object_type | null_type +variant_object& variant::get_object() +{ + if( get_type() == object_type ) + return **reinterpret_cast(this); + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from ${type} to Object", ("type",get_type()) ); +} + +const variant& variant::operator[]( const char* key )const +{ + return get_object()[key]; +} +const variant& variant::operator[]( size_t pos )const +{ + return get_array()[pos]; +} + /// @pre is_array() +size_t variant::size()const +{ + return get_array().size(); +} + +const string& variant::get_string()const +{ + if( get_type() == string_type ) + return **reinterpret_cast(this); + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from type '${type}' to Object", ("type",get_type()) ); +} + +/// @throw if get_type() != object_type +const variant_object& variant::get_object()const +{ + if( get_type() == object_type ) + return **reinterpret_cast(this); + FC_THROW_EXCEPTION( bad_cast_exception, "Invalid cast from type '${type}' to Object", ("type",get_type()) ); +} + +void from_variant( const variant& var, variants& vo ) +{ + vo = var.get_array(); +} + +//void from_variant( const variant& var, variant_object& vo ) +//{ +// vo = var.get_object(); +//} + +void from_variant( const variant& var, variant& vo ) { vo = var; } + +void to_variant( const uint8_t& var, variant& vo ) { vo = uint64_t(var); } +// TODO: warn on overflow? +void from_variant( const variant& var, uint8_t& vo ){ vo = static_cast(var.as_uint64()); } + +void to_variant( const int8_t& var, variant& vo ) { vo = int64_t(var); } +// TODO: warn on overflow? +void from_variant( const variant& var, int8_t& vo ){ vo = static_cast(var.as_int64()); } + +void to_variant( const uint16_t& var, variant& vo ) { vo = uint64_t(var); } +// TODO: warn on overflow? +void from_variant( const variant& var, uint16_t& vo ){ vo = static_cast(var.as_uint64()); } + +void to_variant( const int16_t& var, variant& vo ) { vo = int64_t(var); } +// TODO: warn on overflow? +void from_variant( const variant& var, int16_t& vo ){ vo = static_cast(var.as_int64()); } + +void to_variant( const uint32_t& var, variant& vo ) { vo = uint64_t(var); } +void from_variant( const variant& var, uint32_t& vo ) +{ + vo = static_cast(var.as_uint64()); +} + +void to_variant( const int32_t& var, variant& vo ) { vo = int64_t(var); } +void from_variant( const variant& var, int32_t& vo ) +{ + vo = static_cast(var.as_int64()); +} + +void from_variant( const variant& var, int64_t& vo ) +{ + vo = var.as_int64(); +} + +void from_variant( const variant& var, uint64_t& vo ) +{ + vo = var.as_uint64(); +} + +void from_variant( const variant& var, bool& vo ) +{ + vo = var.as_bool(); +} + +void from_variant( const variant& var, double& vo ) +{ + vo = var.as_double(); +} + +void from_variant( const variant& var, float& vo ) +{ + vo = static_cast(var.as_double()); +} + +void to_variant( const std::string& s, variant& v ) +{ + v = variant( fc::string(s) ); +} + +void from_variant( const variant& var, string& vo ) +{ + vo = var.as_string(); +} + +void to_variant( const std::vector& var, variant& vo ) +{ + if( var.size() ) + vo = variant(to_hex(var.data(),var.size())); + else vo = ""; +} +void from_variant( const variant& var, std::vector& vo ) +{ + auto str = var.as_string(); + vo.resize( str.size() / 2 ); + if( vo.size() ) + { + size_t r = from_hex( str, vo.data(), vo.size() ); + FC_ASSERT( r = vo.size() ); + } +// std::string b64 = base64_decode( var.as_string() ); +// vo = std::vector( b64.c_str(), b64.c_str() + b64.size() ); +} + +string format_string( const string& format, const variant_object& args ) +{ + stringstream ss; + size_t prev = 0; + auto next = format.find( '$' ); + while( prev != size_t(string::npos) && prev < size_t(format.size()) ) + { + ss << format.substr( prev, size_t(next-prev) ); + + // if we got to the end, return it. + if( next == size_t(string::npos) ) + return ss.str(); + + // if we are not at the end, then update the start + prev = next + 1; + + if( format[prev] == '{' ) + { + // if the next char is a open, then find close + next = format.find( '}', prev ); + // if we found close... + if( next != size_t(string::npos) ) + { + // the key is between prev and next + string key = format.substr( prev+1, (next-prev-1) ); + + auto val = args.find( key ); + if( val != args.end() ) + { + if( val->value().is_object() || val->value().is_array() ) + { + ss << json::to_string( val->value() ); + } + else + { + ss << val->value().as_string(); + } + } + else + { + ss << "${"< ( const variant& a, const variant& b ) + { + if( a.is_string() || b.is_string() ) return a.as_string() > b.as_string(); + if( a.is_double() || b.is_double() ) return a.as_double() > b.as_double(); + if( a.is_int64() || b.is_int64() ) return a.as_int64() > b.as_int64(); + if( a.is_uint64() || b.is_uint64() ) return a.as_uint64() > b.as_uint64(); + FC_ASSERT( false, "Invalid operation" ); + } + + variant operator <= ( const variant& a, const variant& b ) + { + if( a.is_string() || b.is_string() ) return a.as_string() <= b.as_string(); + if( a.is_double() || b.is_double() ) return a.as_double() <= b.as_double(); + if( a.is_int64() || b.is_int64() ) return a.as_int64() <= b.as_int64(); + if( a.is_uint64() || b.is_uint64() ) return a.as_uint64() <= b.as_uint64(); + FC_ASSERT( false, "Invalid operation" ); + } + + + variant operator + ( const variant& a, const variant& b ) + { + if( a.is_array() && b.is_array() ) + { + const variants& aa = a.get_array(); + const variants& ba = b.get_array(); + variants result; + result.reserve( std::max(aa.size(),ba.size()) ); + auto num = std::max(aa.size(),ba.size()); + for( unsigned i = 0; i < num; ++i ) + { + if( aa.size() > i && ba.size() > i ) + result[i] = aa[i] + ba[i]; + else if( aa.size() > i ) + result[i] = aa[i]; + else + result[i] = ba[i]; + } + return result; + } + if( a.is_string() || b.is_string() ) return a.as_string() + b.as_string(); + if( a.is_double() || b.is_double() ) return a.as_double() + b.as_double(); + if( a.is_int64() || b.is_int64() ) return a.as_int64() + b.as_int64(); + if( a.is_uint64() || b.is_uint64() ) return a.as_uint64() + b.as_uint64(); + FC_ASSERT( false, "invalid operation ${a} + ${b}", ("a",a)("b",b) ); + } + + variant operator - ( const variant& a, const variant& b ) + { + if( a.is_array() && b.is_array() ) + { + const variants& aa = a.get_array(); + const variants& ba = b.get_array(); + variants result; + result.reserve( std::max(aa.size(),ba.size()) ); + auto num = std::max(aa.size(),ba.size()); + for( unsigned i = 0; i < num; --i ) + { + if( aa.size() > i && ba.size() > i ) + result[i] = aa[i] - ba[i]; + else if( aa.size() > i ) + result[i] = aa[i]; + else + result[i] = ba[i]; + } + return result; + } + if( a.is_string() || b.is_string() ) return a.as_string() - b.as_string(); + if( a.is_double() || b.is_double() ) return a.as_double() - b.as_double(); + if( a.is_int64() || b.is_int64() ) return a.as_int64() - b.as_int64(); + if( a.is_uint64() || b.is_uint64() ) return a.as_uint64() - b.as_uint64(); + FC_ASSERT( false, "invalid operation ${a} + ${b}", ("a",a)("b",b) ); + } + variant operator * ( const variant& a, const variant& b ) + { + if( a.is_double() || b.is_double() ) return a.as_double() * b.as_double(); + if( a.is_int64() || b.is_int64() ) return a.as_int64() * b.as_int64(); + if( a.is_uint64() || b.is_uint64() ) return a.as_uint64() * b.as_uint64(); + if( a.is_array() && b.is_array() ) + { + const variants& aa = a.get_array(); + const variants& ba = b.get_array(); + variants result; + result.reserve( std::max(aa.size(),ba.size()) ); + auto num = std::max(aa.size(),ba.size()); + for( unsigned i = 0; i < num; ++i ) + { + if( aa.size() > i && ba.size() > i ) + result[i] = aa[i] * ba[i]; + else if( aa.size() > i ) + result[i] = aa[i]; + else + result[i] = ba[i]; + } + return result; + } + FC_ASSERT( false, "invalid operation ${a} * ${b}", ("a",a)("b",b) ); + } + variant operator / ( const variant& a, const variant& b ) + { + if( a.is_double() || b.is_double() ) return a.as_double() / b.as_double(); + if( a.is_int64() || b.is_int64() ) return a.as_int64() / b.as_int64(); + if( a.is_uint64() || b.is_uint64() ) return a.as_uint64() / b.as_uint64(); + if( a.is_array() && b.is_array() ) + { + const variants& aa = a.get_array(); + const variants& ba = b.get_array(); + variants result; + result.reserve( std::max(aa.size(),ba.size()) ); + auto num = std::max(aa.size(),ba.size()); + for( unsigned i = 0; i < num; ++i ) + { + if( aa.size() > i && ba.size() > i ) + result[i] = aa[i] / ba[i]; + else if( aa.size() > i ) + result[i] = aa[i]; + else + result[i] = ba[i]; + } + return result; + } + FC_ASSERT( false, "invalid operation ${a} / ${b}", ("a",a)("b",b) ); + } +} // namespace fc diff --git a/src/variant_object.cpp b/src/variant_object.cpp new file mode 100644 index 000000000..6f3b1dcc1 --- /dev/null +++ b/src/variant_object.cpp @@ -0,0 +1,379 @@ +#include +#include +#include + + +namespace fc +{ + // --------------------------------------------------------------- + // entry + + variant_object::entry::entry() {} + variant_object::entry::entry( string k, variant v ) : _key(fc::move(k)),_value(fc::move(v)) {} + variant_object::entry::entry( entry&& e ) : _key(fc::move(e._key)),_value(fc::move(e._value)) {} + variant_object::entry::entry( const entry& e ) : _key(e._key),_value(e._value) {} + variant_object::entry& variant_object::entry::operator=( const variant_object::entry& e ) + { + if( this != &e ) + { + _key = e._key; + _value = e._value; + } + return *this; + } + variant_object::entry& variant_object::entry::operator=( variant_object::entry&& e ) + { + fc_swap( _key, e._key ); + fc_swap( _value, e._value ); + return *this; + } + + const string& variant_object::entry::key()const + { + return _key; + } + + const variant& variant_object::entry::value()const + { + return _value; + } + variant& variant_object::entry::value() + { + return _value; + } + + void variant_object::entry::set( variant v ) + { + fc_swap( _value, v ); + } + + // --------------------------------------------------------------- + // variant_object + + variant_object::iterator variant_object::begin() const + { + assert( _key_value != nullptr ); + return _key_value->begin(); + } + + variant_object::iterator variant_object::end() const + { + return _key_value->end(); + } + + variant_object::iterator variant_object::find( const string& key )const + { + return find( key.c_str() ); + } + + variant_object::iterator variant_object::find( const char* key )const + { + for( auto itr = begin(); itr != end(); ++itr ) + { + if( itr->key() == key ) + { + return itr; + } + } + return end(); + } + + const variant& variant_object::operator[]( const string& key )const + { + return (*this)[key.c_str()]; + } + + const variant& variant_object::operator[]( const char* key )const + { + auto itr = find( key ); + if( itr != end() ) return itr->value(); + FC_THROW_EXCEPTION( key_not_found_exception, "Key ${key}", ("key",key) ); + } + + size_t variant_object::size() const + { + return _key_value->size(); + } + + variant_object::variant_object() + :_key_value(std::make_shared>() ) + { + } + + variant_object::variant_object( string key, variant val ) + : _key_value(std::make_shared>()) + { + //_key_value->push_back(entry(fc::move(key), fc::move(val))); + _key_value->emplace_back(entry(fc::move(key), fc::move(val))); + } + + variant_object::variant_object( const variant_object& obj ) + :_key_value( obj._key_value ) + { + assert( _key_value != nullptr ); + } + + variant_object::variant_object( variant_object&& obj) + : _key_value( fc::move(obj._key_value) ) + { + obj._key_value = std::make_shared>(); + assert( _key_value != nullptr ); + } + + variant_object::variant_object( const mutable_variant_object& obj ) + : _key_value(std::make_shared>(*obj._key_value)) + { + } + + variant_object::variant_object( mutable_variant_object&& obj ) + : _key_value(fc::move(obj._key_value)) + { + assert( _key_value != nullptr ); + } + + variant_object& variant_object::operator=( variant_object&& obj ) + { + if (this != &obj) + { + fc_swap(_key_value, obj._key_value ); + assert( _key_value != nullptr ); + } + return *this; + } + + variant_object& variant_object::operator=( const variant_object& obj ) + { + if (this != &obj) + { + _key_value = obj._key_value; + } + return *this; + } + + variant_object& variant_object::operator=( mutable_variant_object&& obj ) + { + _key_value = fc::move(obj._key_value); + obj._key_value.reset( new std::vector() ); + return *this; + } + + variant_object& variant_object::operator=( const mutable_variant_object& obj ) + { + *_key_value = *obj._key_value; + return *this; + } + + void to_variant( const variant_object& var, variant& vo ) + { + vo = variant(var); + } + + void from_variant( const variant& var, variant_object& vo ) + { + vo = var.get_object(); + } + + // --------------------------------------------------------------- + // mutable_variant_object + + mutable_variant_object::iterator mutable_variant_object::begin() + { + return _key_value->begin(); + } + + mutable_variant_object::iterator mutable_variant_object::end() + { + return _key_value->end(); + } + + mutable_variant_object::iterator mutable_variant_object::begin() const + { + return _key_value->begin(); + } + + mutable_variant_object::iterator mutable_variant_object::end() const + { + return _key_value->end(); + } + + mutable_variant_object::iterator mutable_variant_object::find( const string& key )const + { + return find( key.c_str() ); + } + + mutable_variant_object::iterator mutable_variant_object::find( const char* key )const + { + for( auto itr = begin(); itr != end(); ++itr ) + { + if( itr->key() == key ) + { + return itr; + } + } + return end(); + } + + mutable_variant_object::iterator mutable_variant_object::find( const string& key ) + { + return find( key.c_str() ); + } + + mutable_variant_object::iterator mutable_variant_object::find( const char* key ) + { + for( auto itr = begin(); itr != end(); ++itr ) + { + if( itr->key() == key ) + { + return itr; + } + } + return end(); + } + + const variant& mutable_variant_object::operator[]( const string& key )const + { + return (*this)[key.c_str()]; + } + + const variant& mutable_variant_object::operator[]( const char* key )const + { + auto itr = find( key ); + if( itr != end() ) return itr->value(); + FC_THROW_EXCEPTION( key_not_found_exception, "Key ${key}", ("key",key) ); + } + variant& mutable_variant_object::operator[]( const string& key ) + { + return (*this)[key.c_str()]; + } + + variant& mutable_variant_object::operator[]( const char* key ) + { + auto itr = find( key ); + if( itr != end() ) return itr->value(); + _key_value->emplace_back(entry(key, variant())); + return _key_value->back().value(); + } + + size_t mutable_variant_object::size() const + { + return _key_value->size(); + } + + mutable_variant_object::mutable_variant_object() + :_key_value(new std::vector) + { + } + + mutable_variant_object::mutable_variant_object( string key, variant val ) + : _key_value(new std::vector()) + { + _key_value->push_back(entry(fc::move(key), fc::move(val))); + } + + mutable_variant_object::mutable_variant_object( const variant_object& obj ) + : _key_value( new std::vector(*obj._key_value) ) + { + } + + mutable_variant_object::mutable_variant_object( const mutable_variant_object& obj ) + : _key_value( new std::vector(*obj._key_value) ) + { + } + + mutable_variant_object::mutable_variant_object( mutable_variant_object&& obj ) + : _key_value(fc::move(obj._key_value)) + { + } + + mutable_variant_object& mutable_variant_object::operator=( const variant_object& obj ) + { + *_key_value = *obj._key_value; + return *this; + } + + mutable_variant_object& mutable_variant_object::operator=( mutable_variant_object&& obj ) + { + if (this != &obj) + { + _key_value = fc::move(obj._key_value); + } + return *this; + } + + mutable_variant_object& mutable_variant_object::operator=( const mutable_variant_object& obj ) + { + if (this != &obj) + { + *_key_value = *obj._key_value; + } + return *this; + } + + void mutable_variant_object::reserve( size_t s ) + { + _key_value->reserve(s); + } + + void mutable_variant_object::erase( const string& key ) + { + for( auto itr = begin(); itr != end(); ++itr ) + { + if( itr->key() == key ) + { + _key_value->erase(itr); + return; + } + } + } + + /** replaces the value at \a key with \a var or insert's \a key if not found */ + mutable_variant_object& mutable_variant_object::set( string key, variant var ) + { + auto itr = find( key.c_str() ); + if( itr != end() ) + { + itr->set( fc::move(var) ); + } + else + { + _key_value->push_back( entry( fc::move(key), fc::move(var) ) ); + } + return *this; + } + + /** Appends \a key and \a var without checking for duplicates, designed to + * simplify construction of dictionaries using (key,val)(key2,val2) syntax + */ + mutable_variant_object& mutable_variant_object::operator()( string key, variant var ) + { + _key_value->push_back( entry( fc::move(key), fc::move(var) ) ); + return *this; + } + + mutable_variant_object& mutable_variant_object::operator()( const variant_object& vo ) + { + for( const variant_object::entry& e : vo ) + set( e.key(), e.value() ); + return *this; + } + + mutable_variant_object& mutable_variant_object::operator()( const mutable_variant_object& mvo ) + { + if( &mvo == this ) // mvo(mvo) is no-op + return *this; + for( const mutable_variant_object::entry& e : mvo ) + set( e.key(), e.value() ); + return *this; + } + + void to_variant( const mutable_variant_object& var, variant& vo ) + { + vo = variant(var); + } + + void from_variant( const variant& var, mutable_variant_object& vo ) + { + vo = var.get_object(); + } + +} // namesapce fc diff --git a/src/vector.cpp b/src/vector.cpp deleted file mode 100644 index 48b88e88f..000000000 --- a/src/vector.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include - -namespace fc { - struct vector_impl_d { - abstract_value_type& _vtbl; - char* _data; - size_t _size; - size_t _capacity; - - vector_impl_d( abstract_value_type& r, size_t size, size_t cap= 0 ) - :_vtbl(r),_data(0),_size(size),_capacity(cap) { - if( _size > _capacity ) _capacity = _size; - if( _capacity ) { - const unsigned int so = _vtbl.size_of(); - _data = new char[_capacity*so]; - char* end = _data + _size *so; - for( char* idx = _data; idx < end; idx += so ) { - _vtbl.construct( idx ); - } - } - } - - vector_impl_d( const vector_impl_d& cpy ) - :_vtbl(cpy._vtbl),_data(0),_size(cpy._size),_capacity(cpy._size) { - if( _size ) { - _data = new char[_size*_vtbl.size_of()]; - copy_from( cpy._data, cpy._size ); - } - } - - void copy_from( char* src, int cnt ) { - const unsigned int so = _vtbl.size_of(); - char* end = _data + cnt * so; - char* cpy_idx = src; - for( char* idx = _data; idx < end; idx += so ) { - _vtbl.copy_construct( idx, cpy_idx ); - cpy_idx += so; - } - } - void move_from( char* src, int cnt ) { - const unsigned int so = _vtbl.size_of(); - char* end = _data + cnt * so; - char* cpy_idx = src; - for( char* idx = _data; idx < end; idx += so ) { - _vtbl.move_construct( idx, cpy_idx ); - cpy_idx += so; - } - } - void destruct( char* src, int cnt ) { - const unsigned int so = _vtbl.size_of(); - char* end = src + cnt * so; - for( char* idx = src; idx < end; idx += so ) { - _vtbl.destructor( idx ); - } - } - - ~vector_impl_d() { - clear(); - delete[] _data; - } - - void clear() { - const unsigned int so = _vtbl.size_of(); - char* end = _data + _size * so; - for( char* idx = _data; idx < end; idx += so ) { - _vtbl.destructor( idx ); - } - _size = 0; - } - }; - - vector_impl::vector_impl( abstract_value_type& r, size_t s ) { - my = new vector_impl_d(r,s); - } - - vector_impl::vector_impl( const vector_impl& cpy ) { - my = new vector_impl_d(*cpy.my); - } - - vector_impl::vector_impl( vector_impl&& cpy ){ - my = cpy.my; - cpy.my = 0; - } - - vector_impl::~vector_impl() { - delete my; - } - - vector_impl& vector_impl::operator=( const vector_impl& v ) { - clear(); - reserve(v.size()); - size_t s = v.size(); - for( size_t i = 0; i < s; ++i ) { - _push_back( v._at(i) ); - } - return *this; - } - vector_impl& vector_impl::operator=( vector_impl&& v ) { - fc::swap(my,v.my); - return *this; - } - - void vector_impl::_push_back( const void* v ) { - reserve( my->_size + 1 ); - my->_vtbl.copy_construct( _back(), v ); - my->_size++; - } - void vector_impl::_push_back_m( void* v ) { - reserve( my->_size + 1 ); - my->_vtbl.move_construct( _back(), v ); - my->_size++; - } - - void* vector_impl::_back() { - return my->_data + my->_vtbl.size_of() * (my->_size-1); - } - const void* vector_impl::_back()const { - return my->_data + my->_vtbl.size_of() * (my->_size-1); - } - - void* vector_impl::_at(size_t p) { - return my->_data + my->_vtbl.size_of() * p; - } - const void* vector_impl::_at(size_t p)const { - return my->_data + my->_vtbl.size_of() * p; - } - - void vector_impl::pop_back() { - my->_vtbl.destructor( _back() ); - my->_size--; - } - void vector_impl::clear() { - my->clear(); - } - - size_t vector_impl::size()const { - return my->_size; - } - - void vector_impl::reserve( size_t s ) { - if( s < my->_capacity ) { - return; - } - char* new_data = new char[s*my->_vtbl.size_of()]; - fc::swap(new_data,my->_data); - my->move_from(new_data,my->_size); - my->destruct(new_data,my->_size); - delete[] new_data; - } - void vector_impl::resize( size_t s ) { - if( s <= my->_size ) { - for( size_t i = s; i < my->_size; ++i ) { - my->_vtbl.destructor( _at(i) ); - } - my->_size = s; - return; - } - if( s <= my->_capacity ) { - - return; - } - const unsigned int so = my->_vtbl.size_of(); - char* new_data = new char[s*so]; - fc::swap(new_data,my->_data); - my->_capacity = s; - // move from old to new location - my->move_from(new_data,my->_size); - // destroy old location - my->destruct(new_data,my->_size); - delete[] new_data; - - // default construct any left overs. - char* cur = (char*)_back(); - char* end = (char*)my->_data + s * so; - while( cur < end ) { - my->_vtbl.construct(cur); - cur += so; - my->_size++; - } - } - - size_t vector_impl::capacity()const { - return my->_capacity; - } - -} // namespace fc diff --git a/tests/all_tests.cpp b/tests/all_tests.cpp new file mode 100644 index 000000000..8056cccea --- /dev/null +++ b/tests/all_tests.cpp @@ -0,0 +1,3 @@ +#define BOOST_TEST_MODULE AllTests +#include + diff --git a/tests/api.cpp b/tests/api.cpp new file mode 100644 index 000000000..4fcd8b5ed --- /dev/null +++ b/tests/api.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include + +class calculator +{ + public: + int32_t add( int32_t a, int32_t b ); // not implemented + int32_t sub( int32_t a, int32_t b ); // not implemented + void on_result( const std::function& cb ); + void on_result2( const std::function& cb, int test ); +}; + +FC_API( calculator, (add)(sub)(on_result)(on_result2) ) + + +class login_api +{ + public: + fc::api get_calc()const + { + FC_ASSERT( calc ); + return *calc; + } + fc::optional> calc; + std::set test( const std::string&, const std::string& ) { return std::set(); } +}; +FC_API( login_api, (get_calc)(test) ); + +using namespace fc; + +class some_calculator +{ + public: + int32_t add( int32_t a, int32_t b ) { wlog("."); if( _cb ) _cb(a+b); return a+b; } + int32_t sub( int32_t a, int32_t b ) { wlog(".");if( _cb ) _cb(a-b); return a-b; } + void on_result( const std::function& cb ) { wlog( "set callback" ); _cb = cb; return ; } + void on_result2( const std::function& cb, int test ){} + std::function _cb; +}; +class variant_calculator +{ + public: + double add( fc::variant a, fc::variant b ) { return a.as_double()+b.as_double(); } + double sub( fc::variant a, fc::variant b ) { return a.as_double()-b.as_double(); } + void on_result( const std::function& cb ) { wlog("set callback"); _cb = cb; return ; } + void on_result2( const std::function& cb, int test ){} + std::function _cb; +}; + +using namespace fc::http; +using namespace fc::rpc; + +int main( int argc, char** argv ) +{ + try { + fc::api calc_api( std::make_shared() ); + + fc::http::websocket_server server; + server.on_connection([&]( const websocket_connection_ptr& c ){ + auto wsc = std::make_shared(*c); + auto login = std::make_shared(); + login->calc = calc_api; + wsc->register_api(fc::api(login)); + c->set_session_data( wsc ); + }); + + server.listen( 8090 ); + server.start_accept(); + + for( uint32_t i = 0; i < 5000; ++i ) + { + try { + fc::http::websocket_client client; + auto con = client.connect( "ws://localhost:8090" ); + auto apic = std::make_shared(*con); + auto remote_login_api = apic->get_remote_api(); + auto remote_calc = remote_login_api->get_calc(); + remote_calc->on_result( []( uint32_t r ) { elog( "callback result ${r}", ("r",r) ); } ); + wdump((remote_calc->add( 4, 5 ))); + } catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + } + } + wlog( "exit scope" ); + } + catch( const fc::exception& e ) + { + edump((e.to_detail_string())); + } + wlog( "returning now..." ); + + return 0; + + some_calculator calc; + variant_calculator vcalc; + + fc::api api_calc( &calc ); + fc::api api_vcalc( &vcalc ); + fc::api api_nested_calc( api_calc ); + + wdump( (api_calc->add(5,4)) ); + wdump( (api_calc->sub(5,4)) ); + wdump( (api_vcalc->add(5,4)) ); + wdump( (api_vcalc->sub(5,4)) ); + wdump( (api_nested_calc->sub(5,4)) ); + wdump( (api_nested_calc->sub(5,4)) ); + + /* + variants v = { 4, 5 }; + auto g = to_generic( api_calc->add ); + auto r = call_generic( api_calc->add, v.begin(), v.end() ); + wdump((r)); + wdump( (g(v)) ); + */ + + /* + try { + fc::api_server server; + auto api_id = server.register_api( api_calc ); + wdump( (api_id) ); + auto result = server.call( api_id, "add", {4, 5} ); + wdump( (result) ); + } catch ( const fc::exception& e ) + { + elog( "${e}", ("e",e.to_detail_string() ) ); + } + + ilog( "------------------ NESTED TEST --------------" ); + try { + login_api napi_impl; + napi_impl.calc = api_calc; + fc::api napi(&napi_impl); + + fc::api_server server; + auto api_id = server.register_api( napi ); + wdump( (api_id) ); + auto result = server.call( api_id, "get_calc" ); + wdump( (result) ); + result = server.call( result.as_uint64(), "add", {4,5} ); + wdump( (result) ); + + + fc::api serv( &server ); + + fc::api_client apic( serv ); + + fc::api remote_api = apic; + + + auto remote_calc = remote_api->get_calc(); + int r = remote_calc->add( 4, 5 ); + idump( (r) ); + + } catch ( const fc::exception& e ) + { + elog( "${e}", ("e",e.to_detail_string() ) ); + } + */ + + ilog( "------------------ NESTED TEST --------------" ); + try { + login_api napi_impl; + napi_impl.calc = api_calc; + fc::api napi(&napi_impl); + + + auto client_side = std::make_shared(); + auto server_side = std::make_shared(); + server_side->set_remote_connection( client_side ); + client_side->set_remote_connection( server_side ); + + server_side->register_api( napi ); + + fc::api remote_api = client_side->get_remote_api(); + + auto remote_calc = remote_api->get_calc(); + int r = remote_calc->add( 4, 5 ); + idump( (r) ); + + } catch ( const fc::exception& e ) + { + elog( "${e}", ("e",e.to_detail_string() ) ); + } + + return 0; +} diff --git a/tests/blinding_test.cpp b/tests/blinding_test.cpp new file mode 100644 index 000000000..318f16169 --- /dev/null +++ b/tests/blinding_test.cpp @@ -0,0 +1,304 @@ +#define BOOST_TEST_MODULE BlindingTest +#include +#include +#include +#include +#include +#include +#include + +// See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors + +static fc::string TEST1_SEED = "000102030405060708090a0b0c0d0e0f"; +static fc::string TEST1_M_PUB = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"; +static fc::string TEST1_M_PRIV = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"; +static fc::string TEST1_M_0H_PUB = "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"; +static fc::string TEST1_M_0H_PRIV = "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"; +static fc::string TEST1_M_0H_1_PUB = "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"; +static fc::string TEST1_M_0H_1_PRIV = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"; +static fc::string TEST1_M_0H_1_2H_PUB = "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"; +static fc::string TEST1_M_0H_1_2H_PRIV = "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"; +static fc::string TEST1_M_0H_1_2H_2_PUB = "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"; +static fc::string TEST1_M_0H_1_2H_2_PRIV = "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"; +static fc::string TEST1_M_0H_1_2H_2_1g_PUB = "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"; +static fc::string TEST1_M_0H_1_2H_2_1g_PRIV = "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"; + +static fc::string TEST2_SEED = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"; +static fc::string TEST2_M_PUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"; +static fc::string TEST2_M_PRIV = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"; +static fc::string TEST2_M_0_PUB = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"; +static fc::string TEST2_M_0_PRIV = "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"; +static fc::string TEST2_M_0_m1_PUB = "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"; +static fc::string TEST2_M_0_m1_PRIV = "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"; +static fc::string TEST2_M_0_m1_1_PUB = "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"; +static fc::string TEST2_M_0_m1_1_PRIV = "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"; +static fc::string TEST2_M_0_m1_1_m2_PUB = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; +static fc::string TEST2_M_0_m1_1_m2_PRIV = "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"; +static fc::string TEST2_M_0_m1_1_m2_2_PUB = "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"; +static fc::string TEST2_M_0_m1_1_m2_2_PRIV = "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; + +static fc::string BLIND_K_X = "08be5c5076f8cbd1283cf98e74bff873032b9bc79a0769962bf3900f33c2df6e"; +static fc::string BLIND_T_X = "80deff382af8a8e4a5f297588e44d5bf858f30a524f74b13efcefeb54f4b3f47"; +static fc::string BLINDED_HASH = "7196e80cdafdfdfb7496323ad24bf47dda8447febd7426e444facc04940c7309"; +static fc::string BLIND_SIG = "40d6a477d849cc860df8ad159481f2ffc5b4dc3131b86a799d7d10460824dd53"; +static fc::string UNBLINDED = "700092a72a05e33509f9b068aa1d7c5336d8b5692b4157da199d7ec1e10fd7c0"; + +BOOST_AUTO_TEST_CASE(test_extended_keys_1) +{ + char seed[16]; + fc::from_hex( TEST1_SEED, seed, sizeof(seed) ); + fc::ecc::extended_private_key master = fc::ecc::extended_private_key::generate_master( seed, sizeof(seed) ); + BOOST_CHECK_EQUAL( master.str(), TEST1_M_PRIV ); + BOOST_CHECK_EQUAL( master.get_extended_public_key().str(), TEST1_M_PUB ); + + BOOST_CHECK_EQUAL( fc::ecc::extended_private_key::from_base58(TEST1_M_PRIV).str(), TEST1_M_PRIV ); + BOOST_CHECK_EQUAL( fc::ecc::extended_public_key::from_base58(TEST1_M_PUB).str(), TEST1_M_PUB ); + BOOST_CHECK_EQUAL( fc::ecc::extended_private_key::from_base58(TEST1_M_0H_PRIV).str(), TEST1_M_0H_PRIV ); + BOOST_CHECK_EQUAL( fc::ecc::extended_public_key::from_base58(TEST1_M_0H_PUB).str(), TEST1_M_0H_PUB ); + + fc::ecc::extended_private_key m_0 = master.derive_child(0x80000000); + BOOST_CHECK_EQUAL( m_0.str(), TEST1_M_0H_PRIV ); + BOOST_CHECK_EQUAL( m_0.get_extended_public_key().str(), TEST1_M_0H_PUB ); + + fc::ecc::extended_private_key m_0_1 = m_0.derive_child(1); + BOOST_CHECK_EQUAL( m_0_1.str(), TEST1_M_0H_1_PRIV ); + BOOST_CHECK_EQUAL( m_0_1.get_extended_public_key().str(), TEST1_M_0H_1_PUB ); + BOOST_CHECK_EQUAL( m_0.get_extended_public_key().derive_child(1).str(), TEST1_M_0H_1_PUB ); + + fc::ecc::extended_private_key m_0_1_2 = m_0_1.derive_child(0x80000002); + BOOST_CHECK_EQUAL( m_0_1_2.str(), TEST1_M_0H_1_2H_PRIV ); + BOOST_CHECK_EQUAL( m_0_1_2.get_extended_public_key().str(), TEST1_M_0H_1_2H_PUB ); + + fc::ecc::extended_private_key m_0_1_2_2 = m_0_1_2.derive_child(2); + BOOST_CHECK_EQUAL( m_0_1_2_2.str(), TEST1_M_0H_1_2H_2_PRIV ); + BOOST_CHECK_EQUAL( m_0_1_2_2.get_extended_public_key().str(), TEST1_M_0H_1_2H_2_PUB ); + BOOST_CHECK_EQUAL( m_0_1_2.get_extended_public_key().derive_child(2).str(), TEST1_M_0H_1_2H_2_PUB ); + + fc::ecc::extended_private_key m_0_1_2_2_1g = m_0_1_2_2.derive_child(1000000000); + BOOST_CHECK_EQUAL( m_0_1_2_2_1g.str(), TEST1_M_0H_1_2H_2_1g_PRIV ); + BOOST_CHECK_EQUAL( m_0_1_2_2_1g.get_extended_public_key().str(), TEST1_M_0H_1_2H_2_1g_PUB ); + BOOST_CHECK_EQUAL( m_0_1_2_2.get_extended_public_key().derive_child(1000000000).str(), TEST1_M_0H_1_2H_2_1g_PUB ); +} + +BOOST_AUTO_TEST_CASE(test_extended_keys_2) +{ + char seed[64]; + fc::from_hex( TEST2_SEED, seed, sizeof(seed) ); + fc::ecc::extended_private_key master = fc::ecc::extended_private_key::generate_master( seed, sizeof(seed) ); + BOOST_CHECK_EQUAL( master.str(), TEST2_M_PRIV ); + BOOST_CHECK_EQUAL( master.get_extended_public_key().str(), TEST2_M_PUB ); + + fc::ecc::extended_private_key m_0 = master.derive_child(0); + BOOST_CHECK_EQUAL( m_0.str(), TEST2_M_0_PRIV ); + BOOST_CHECK_EQUAL( m_0.get_extended_public_key().str(), TEST2_M_0_PUB ); + BOOST_CHECK_EQUAL( master.get_extended_public_key().derive_child(0).str(), TEST2_M_0_PUB ); + + fc::ecc::extended_private_key m_0_m1 = m_0.derive_child(-1); + BOOST_CHECK_EQUAL( m_0_m1.str(), TEST2_M_0_m1_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1.get_extended_public_key().str(), TEST2_M_0_m1_PUB ); + + fc::ecc::extended_private_key m_0_m1_1 = m_0_m1.derive_child(1); + BOOST_CHECK_EQUAL( m_0_m1_1.str(), TEST2_M_0_m1_1_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1_1.get_extended_public_key().str(), TEST2_M_0_m1_1_PUB ); + BOOST_CHECK_EQUAL( m_0_m1.get_extended_public_key().derive_child(1).str(), TEST2_M_0_m1_1_PUB ); + + fc::ecc::extended_private_key m_0_m1_1_m2 = m_0_m1_1.derive_child(-2); + BOOST_CHECK_EQUAL( m_0_m1_1_m2.str(), TEST2_M_0_m1_1_m2_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1_1_m2.get_extended_public_key().str(), TEST2_M_0_m1_1_m2_PUB ); + + fc::ecc::extended_private_key m_0_m1_1_m2_2 = m_0_m1_1_m2.derive_child(2); + BOOST_CHECK_EQUAL( m_0_m1_1_m2_2.str(), TEST2_M_0_m1_1_m2_2_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1_1_m2_2.get_extended_public_key().str(), TEST2_M_0_m1_1_m2_2_PUB ); + BOOST_CHECK_EQUAL( m_0_m1_1_m2.get_extended_public_key().derive_child(2).str(), TEST2_M_0_m1_1_m2_2_PUB ); +} + +//static void print(const unsigned char* data, int len) { +// for (int i = 0; i < len; i++) { +// printf("%02x", *data++); +// } +//} + +BOOST_AUTO_TEST_CASE(test_blinding_1) +{ + char buffer[7] = "test_"; + fc::ecc::extended_private_key alice = fc::ecc::extended_private_key::generate_master( "master" ); + fc::ecc::extended_private_key bob = fc::ecc::extended_private_key::generate_master( "puppet" ); + + for ( int i = 0; i < 30; i++ ) + { + buffer[5] = '0' + i; + fc::ecc::extended_public_key bob_pub = bob.get_extended_public_key(); + fc::sha256 hash = fc::sha256::hash( buffer, sizeof(buffer) ); + fc::ecc::public_key t = alice.blind_public_key( bob_pub, i ); + fc::ecc::blinded_hash blinded = alice.blind_hash( hash, i ); + fc::ecc::blind_signature blind_sig = bob.blind_sign( blinded, i ); + try { + fc::ecc::compact_signature sig = alice.unblind_signature( bob_pub, blind_sig, hash, i ); + fc::ecc::public_key validate( sig, hash ); +// printf("Validated: "); print((unsigned char*) validate.serialize().begin(), 33); +// printf("\nT: "); print((unsigned char*) t.serialize().begin(), 33); printf("\n"); + BOOST_CHECK( validate.serialize() == t.serialize() ); + } catch (const fc::exception& e) { + printf( "Test %d: %s\n", i, e.to_string().c_str() ); + } + alice = alice.derive_child( i ); + bob = bob.derive_child( i | 0x80000000 ); + } +} + +BOOST_AUTO_TEST_CASE(test_blinding_2) +{ + char message[7] = "test_0"; + fc::ecc::extended_private_key alice = fc::ecc::extended_private_key::generate_master( "master" ); + fc::ecc::extended_private_key bob = fc::ecc::extended_private_key::generate_master( "puppet" ); + fc::ecc::extended_public_key bob_pub = bob.get_extended_public_key(); + fc::sha256 hash = fc::sha256::hash( message, sizeof(message) ); + + fc::ecc::public_key t = alice.blind_public_key( bob_pub, 0 ); + fc::ecc::public_key_data pub = t.serialize(); + char buffer[32]; + fc::from_hex( BLIND_T_X, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( pub.begin() + 1, buffer, sizeof(buffer) ) ); + + fc::ecc::blinded_hash blinded = alice.blind_hash( hash, 0 ); + fc::from_hex( BLINDED_HASH, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( blinded.data(), buffer, sizeof(buffer) ) ); + + fc::ecc::blind_signature blind_sig = bob.blind_sign( blinded, 0 ); + fc::from_hex( BLIND_SIG, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( blind_sig.data(), buffer, sizeof(buffer) ) ); + + fc::ecc::compact_signature sig = alice.unblind_signature( bob_pub, blind_sig, hash, 0 ); + fc::from_hex( BLIND_K_X, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( sig.begin() + 1, buffer, sizeof(buffer) ) ); + fc::from_hex( UNBLINDED, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( sig.begin() + 33, buffer, sizeof(buffer) ) ); +} + +static void to_bignum(const char* data32, fc::ssl_bignum& out) { + unsigned char dummy[33]; dummy[0] = 0; + memcpy(dummy, data32, 32); + BN_bin2bn((unsigned char*) data32, 32, out); +} + +//static void print(const fc::sha256 hash) { +// print((unsigned char*) hash.data(), hash.data_size()); +//} +// +//static void print(const BIGNUM* bn) { +// unsigned char buffer[64]; +// int len = BN_num_bytes(bn); +// if (len > sizeof(buffer)) { +// printf("BN too long - %d bytes?!", len); +// return; +// } +// BN_bn2bin(bn, buffer); +// print(buffer, len); +//} +// +//static void print(const fc::ec_group& curve, const fc::ec_point& p, fc::bn_ctx& ctx) { +// fc::ssl_bignum x; +// fc::ssl_bignum y; +// EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, ctx); +// printf("("); +// print(x); +// printf(", "); +// print(y); +// printf(")"); +//} + +namespace fc { +SSL_TYPE(ec_key, EC_KEY, EC_KEY_free) +} + +BOOST_AUTO_TEST_CASE(openssl_blinding) +{ + // Needed this "test" for producing data for debugging my libsecp256k1 implementation + + char buffer[7] = "test_0"; + fc::ecc::extended_private_key alice = fc::ecc::extended_private_key::generate_master( "master" ); + fc::ecc::extended_private_key bob = fc::ecc::extended_private_key::generate_master( "puppet" ); + fc::ec_group curve(EC_GROUP_new_by_curve_name(NID_secp256k1)); + fc::bn_ctx ctx(BN_CTX_new()); + fc::ssl_bignum n; + EC_GROUP_get_order(curve, n, ctx); + fc::ssl_bignum n_half; + BN_rshift1(n_half, n); + fc::ssl_bignum zero; BN_zero(zero); + + fc::sha256 hash_ = fc::sha256::hash( buffer, sizeof(buffer) ); + fc::ssl_bignum hash; to_bignum(hash_.data(), hash); + fc::ssl_bignum a; to_bignum(alice.derive_hardened_child(0).get_secret().data(), a); + fc::ssl_bignum b; to_bignum(alice.derive_hardened_child(1).get_secret().data(), b); + fc::ssl_bignum c; to_bignum(alice.derive_hardened_child(2).get_secret().data(), c); + fc::ssl_bignum d; to_bignum(alice.derive_hardened_child(3).get_secret().data(), d); + + fc::ec_point P(EC_POINT_new(curve)); + fc::ecc::public_key_data Pd = bob.get_extended_public_key().derive_child(0).serialize(); + fc::ssl_bignum Px; to_bignum(Pd.begin() + 1, Px); + EC_POINT_set_compressed_coordinates_GFp(curve, P, Px, (*Pd.begin()) & 1, ctx); + + fc::ec_point Q(EC_POINT_new(curve)); + fc::ecc::public_key_data Qd = bob.get_extended_public_key().derive_child(1).serialize(); + fc::ssl_bignum Qx; to_bignum(Qd.begin() + 1, Qx); + EC_POINT_set_compressed_coordinates_GFp(curve, Q, Qx, (*Qd.begin()) & 1, ctx); + + // Alice computes K = (c·a)^-1·P and public key T = (a·Kx)^-1·(b·G + Q + d·c^-1·P). + fc::ec_point K(EC_POINT_new(curve)); + fc::ssl_bignum tmp; + BN_mod_mul(tmp, a, c, n, ctx); + BN_mod_inverse(tmp, tmp, n, ctx); + EC_POINT_mul(curve, K, zero, P, tmp, ctx); + + fc::ec_point T(EC_POINT_new(curve)); + BN_mod_inverse(tmp, c, n, ctx); + BN_mod_mul(tmp, d, tmp, n, ctx); + EC_POINT_mul(curve, T, b, P, tmp, ctx); + EC_POINT_add(curve, T, T, Q, ctx); + fc::ssl_bignum Kx; + fc::ssl_bignum Ky; + EC_POINT_get_affine_coordinates_GFp(curve, K, Kx, Ky, ctx); + BN_mod_mul(tmp, a, Kx, n, ctx); + BN_mod_inverse(tmp, tmp, n, ctx); + EC_POINT_mul(curve, T, zero, T, tmp, ctx); + + fc::ssl_bignum blinded; + BN_mod_mul(blinded, a, hash, n, ctx); + BN_mod_add(blinded, blinded, b, n, ctx); + + fc::ssl_bignum p; to_bignum(bob.derive_normal_child(0).get_secret().data(), p); + fc::ssl_bignum q; to_bignum(bob.derive_normal_child(1).get_secret().data(), q); + BN_mod_inverse(p, p, n, ctx); + BN_mod_mul(q, q, p, n, ctx); + fc::ssl_bignum blind_sig; + BN_mod_mul(blind_sig, p, blinded, n, ctx); + BN_mod_add(blind_sig, blind_sig, q, n, ctx); + + fc::ecdsa_sig sig(ECDSA_SIG_new()); + BN_copy(sig->r, Kx); + BN_mod_mul(sig->s, c, blind_sig, n, ctx); + BN_mod_add(sig->s, sig->s, d, n, ctx); + + if (BN_cmp(sig->s, n_half) > 0) { + BN_sub(sig->s, n, sig->s); + } + + fc::ec_key verify(EC_KEY_new()); + EC_KEY_set_public_key(verify, T); + BOOST_CHECK( ECDSA_do_verify( (unsigned char*) hash_.data(), hash_.data_size(), sig, verify ) ); +// printf("a: "); print(a); +// printf("\nb: "); print(b); +// printf("\nc: "); print(c); +// printf("\nd: "); print(d); +// printf("\nP: "); print(curve, P, ctx); +// printf("\nQ: "); print(curve, Q, ctx); +// printf("\nK: "); print(curve, K, ctx); +// printf("\nT: "); print(curve, T, ctx); +// printf("\np: "); print(p); +// printf("\nq: "); print(q); +// printf("\nhash: "); print(hash_); +// printf("\nblinded: "); print(blinded); +// printf("\nblind_sig: "); print(blind_sig); +// printf("\nunblinded: "); print(sig->s); +// printf("\n"); +} diff --git a/tests/bloom_test.cpp b/tests/bloom_test.cpp new file mode 100644 index 000000000..ca1779231 --- /dev/null +++ b/tests/bloom_test.cpp @@ -0,0 +1,136 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace fc; + +static bloom_parameters setup_parameters() +{ + bloom_parameters parameters; + + // How many elements roughly do we expect to insert? + parameters.projected_element_count = 100000; + + // Maximum tolerable false positive probability? (0,1) + parameters.false_positive_probability = 0.0001; // 1 in 10000 + + // Simple randomizer (optional) + parameters.random_seed = 0xA5A5A5A5; + + if (!parameters) + { + BOOST_FAIL( "Error - Invalid set of bloom filter parameters!" ); + } + + parameters.compute_optimal_parameters(); + + return parameters; +} + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(bloom_test_1) +{ + try { + + //Instantiate Bloom Filter + bloom_filter filter(setup_parameters()); + + uint32_t count = 0; + std::string line; + std::ifstream in("README.md"); + std::ofstream words("words.txt"); + while( !in.eof() && count < 100000 ) + { + std::getline(in, line); +// std::cout << "'"< -100; --i) + { + BOOST_CHECK( !filter.contains(i) ); + } + } + } + catch ( const fc::exception& e ) + { + edump((e.to_detail_string()) ); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/compress/compress.cpp b/tests/compress/compress.cpp new file mode 100644 index 000000000..a30255e98 --- /dev/null +++ b/tests/compress/compress.cpp @@ -0,0 +1,89 @@ +#include + +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(compress) + +BOOST_AUTO_TEST_CASE(smaz_test) +{ + std::ifstream testfile; + testfile.open("README.md"); + + std::stringstream buffer; + std::string line; + std::getline( testfile, line ); + while( testfile.good() ) + { + buffer << line << "\n"; + try { + std::string compressed = fc::smaz_compress( line ); + std::string decomp = fc::smaz_decompress( compressed ); + BOOST_CHECK_EQUAL( decomp, line ); + } + catch ( fc::exception& e ) + { + std::cout< + +#include +#include +#include +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(aes_test) +{ + std::ifstream testfile; + testfile.open("README.md"); + + auto key = fc::sha512::hash( "hello", 5 ); + std::stringstream buffer; + std::string line; + std::getline( testfile, line ); + while( testfile.good() ) + { +// std::cout << line << "\n"; + buffer << line << "\n"; + try { + std::vector data( line.c_str(),line.c_str()+line.size()+1 ); + std::vector crypt = fc::aes_encrypt( key, data ); + std::vector dcrypt = fc::aes_decrypt( key, crypt ); + BOOST_CHECK( data == dcrypt ); + +// memset( crypt.data(), 0, crypt.size() ); +// fc::aes_encoder enc; +// enc.init( fc::sha256::hash((char*)&key,sizeof(key) ), fc::city_hash_crc_128( (char*)&key, sizeof(key) ) ); +// auto len = enc.encode( dcrypt.data(), dcrypt.size(), crypt.data() ); +// BOOST_CHECK_EQUAL( dcrypt.size(), len ); +// +// fc::aes_decoder dec; +// dec.init( fc::sha256::hash((char*)&key,sizeof(key) ), fc::city_hash_crc_128( (char*)&key, sizeof(key) ) ); +// len = dec.decode( crypt.data(), len, dcrypt.data() ); +// BOOST_CHECK_EQUAL( dcrypt.size(), len ); +// BOOST_CHECK( !memcmp( dcrypt.data(), data.data(), len) ); + } + catch ( fc::exception& e ) + { + std::cout< data( line.c_str(),line.c_str()+line.size()+1 ); + std::vector crypt = fc::aes_encrypt( key, data ); + std::vector dcrypt = fc::aes_decrypt( key, crypt ); + BOOST_CHECK( data == dcrypt ); + +// memset( crypt.data(), 0, crypt.size() ); +// fc::aes_encoder enc; +// enc.init( fc::sha256::hash((char*)&key,sizeof(key) ), fc::city_hash_crc_128( (char*)&key, sizeof(key) ) ); +// auto len = enc.encode( dcrypt.data(), dcrypt.size(), crypt.data() ); +// BOOST_CHECK_EQUAL( dcrypt.size(), len ); +// +// fc::aes_decoder dec; +// dec.init( fc::sha256::hash((char*)&key,sizeof(key) ), fc::city_hash_crc_128( (char*)&key, sizeof(key) ) ); +// len = dec.decode( crypt.data(), len, dcrypt.data() ); +// BOOST_CHECK_EQUAL( dcrypt.size(), len ); +// BOOST_CHECK( !memcmp( dcrypt.data(), data.data(), len) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/base_n_tests.cpp b/tests/crypto/base_n_tests.cpp new file mode 100644 index 000000000..a501123fb --- /dev/null +++ b/tests/crypto/base_n_tests.cpp @@ -0,0 +1,150 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +static const std::string TEST1(""); +static const std::string TEST2("\0\00101", 4); +static const std::string TEST3("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +static const std::string TEST4("\377\376\000\375\001\374", 6); +static const std::string TEST5("\0\0\0", 3); + +static void test_16( const std::string& test, const std::string& expected ) +{ + std::vector vec( test.begin(), test.end() ); + fc::string enc1 = fc::to_hex( vec ); + fc::string enc2 = fc::to_hex( test.c_str(), test.size() ); + BOOST_CHECK_EQUAL( enc1, enc2 ); + BOOST_CHECK_EQUAL( expected, enc2 ); + + char out[32]; + int len = fc::from_hex( enc1, out, 32 ); + BOOST_CHECK_EQUAL( test.size(), len ); + BOOST_CHECK( !memcmp( test.c_str(), out, len ) ); + if (len > 10) { + BOOST_CHECK( fc::from_hex( enc1, out, 10 ) <= 10 ); + } +} + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(hex_test) +{ + test_16( TEST1, "" ); + test_16( TEST2, "00013031" ); + test_16( TEST3, "4142434445464748494a4b4c4d4e4f505152535455565758595a" ); + test_16( TEST4, "fffe00fd01fc" ); + test_16( TEST5, "000000" ); +} + + +static void test_32( const std::string& test, const std::string& expected ) +{ + std::vector vec( test.begin(), test.end() ); + fc::string enc1 = fc::to_base32( vec ); + fc::string enc2 = fc::to_base32( test.c_str(), test.size() ); + BOOST_CHECK_EQUAL( enc1, enc2 ); + BOOST_CHECK_EQUAL( expected, enc2 ); + + std::vector dec = fc::from_base32( enc1 ); + BOOST_CHECK_EQUAL( vec.size(), dec.size() ); + BOOST_CHECK( !memcmp( vec.data(), dec.data(), vec.size() ) ); +} + +BOOST_AUTO_TEST_CASE(base32_test) +{ + test_32( TEST1, "" ); + test_32( TEST2, "AAATAMI=" ); + test_32( TEST3, "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======" ); + test_32( TEST4, "777AB7IB7Q======" ); + test_32( TEST5, "AAAAA===" ); +} + + +static void test_36( const std::string& test, const std::string& expected ) +{ + std::vector vec( test.begin(), test.end() ); + fc::string enc1 = fc::to_base36( vec ); + fc::string enc2 = fc::to_base36( test.c_str(), test.size() ); + BOOST_CHECK_EQUAL( enc1, enc2 ); + BOOST_CHECK_EQUAL( expected, enc2 ); + + std::vector dec = fc::from_base36( enc1 ); + BOOST_CHECK_EQUAL( vec.size(), dec.size() ); + BOOST_CHECK( !memcmp( vec.data(), dec.data(), vec.size() ) ); +} + +BOOST_AUTO_TEST_CASE(base36_test) +{ + test_36( TEST1, "" ); + test_36( TEST2, "01o35" ); + test_36( TEST3, "l4ksdleyi5pnl0un5raue268ptj43dwjwmz15ie2" ); + test_36( TEST4, "2rrrvpb7y4" ); + test_36( TEST5, "000" ); +} + + +static void test_58( const std::string& test, const std::string& expected ) +{ + std::vector vec( test.begin(), test.end() ); + fc::string enc1 = fc::to_base58( vec ); + fc::string enc2 = fc::to_base58( test.c_str(), test.size() ); + BOOST_CHECK_EQUAL( enc1, enc2 ); + BOOST_CHECK_EQUAL( expected, enc2 ); + + std::vector dec = fc::from_base58( enc1 ); + BOOST_CHECK_EQUAL( vec.size(), dec.size() ); + BOOST_CHECK( !memcmp( vec.data(), dec.data(), vec.size() ) ); + + char buffer[64]; + size_t len = fc::from_base58( enc1, buffer, 64 ); + BOOST_CHECK( len <= 64 ); + BOOST_CHECK( !memcmp( vec.data(), buffer, len ) ); + if ( len > 10 ) { + try { + len = fc::from_base58( enc1, buffer, 10 ); + BOOST_CHECK( len <= 10 ); + } catch ( fc::exception expected ) {} + } + +} + +BOOST_AUTO_TEST_CASE(base58_test) +{ + test_58( TEST1, "" ); + test_58( TEST2, "1Q9e" ); + test_58( TEST3, "2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD" ); + test_58( TEST4, "3CUeREErf" ); + test_58( TEST5, "111" ); +} + + +static void test_64( const std::string& test, const std::string& expected ) +{ + fc::string enc1 = fc::base64_encode( test ); + fc::string enc2 = fc::base64_encode( test.c_str(), test.size() ); + BOOST_CHECK_EQUAL( enc1, enc2 ); + BOOST_CHECK_EQUAL( expected, enc2 ); + + std::string dec = fc::base64_decode( enc1 ); + BOOST_CHECK_EQUAL( test.size(), dec.size() ); + BOOST_CHECK_EQUAL( test, dec ); +} + +BOOST_AUTO_TEST_CASE(base64_test) +{ + test_64( TEST1, "" ); + test_64( TEST2, "AAEwMQ==" ); + test_64( TEST3, "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=" ); + test_64( TEST4, "//4A/QH8" ); + test_64( TEST5, "AAAA" ); +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/bigint_test.cpp b/tests/crypto/bigint_test.cpp new file mode 100644 index 000000000..5473b7a6a --- /dev/null +++ b/tests/crypto/bigint_test.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(bigint_test_1) +{ + int64_t counter = 0, accu = 0, c_sq; + fc::bigint bi_accu(accu); + do { + c_sq = counter * counter; + fc::bigint add(c_sq); + bi_accu += add; + accu += c_sq; + BOOST_CHECK( fc::bigint(accu) == bi_accu ); + + bi_accu = bi_accu + add; + accu = accu + c_sq; + BOOST_CHECK_EQUAL( accu, bi_accu.to_int64() ); + + bi_accu = fc::bigint( bi_accu.dup() ); + + counter++; + } while (c_sq < 1000000); + + fc::variant test; + fc::to_variant( bi_accu, test ); + fc::bigint other; + fc::from_variant( test, other ); + BOOST_CHECK( other == bi_accu ); +} + +BOOST_AUTO_TEST_CASE(bigint_test_2) +{ + const fc::bigint bi_1(1), bi_3(3), bi_17(17), bi_65537(65537); + fc::bigint bi_accu(bi_1); + do { + std::vector bytes = bi_accu; + fc::bigint a_1( bytes ); + a_1 = a_1 + bi_1; + BOOST_CHECK( bi_accu < a_1 ); + + bi_accu = a_1 * bi_accu; + BOOST_CHECK( bi_accu >= a_1 ); + } while ( bi_accu.log2() <= 128 ); + + bi_accu = bi_accu; + + BOOST_CHECK( bi_accu && !bi_accu.is_negative() && bi_accu != bi_1 ); + + BOOST_CHECK( bi_3.exp( bi_accu.log2() ) > bi_accu ); + + fc::bigint big(1); + big <<= 30; big += bi_17; big <<= 30; big++; + big <<= 30; big -= bi_17; big >>= 5; big--; + fc::bigint rest = bi_accu % big; + BOOST_CHECK( (bi_accu - rest) / big == bi_accu / big ); + + fc::bigint big2; big2 = big; + big2 *= bi_65537.exp(3); + big2 /= bi_65537.exp(3); + BOOST_CHECK( big2 == big ); + big--; + + BOOST_CHECK_EQUAL( (std::string) bi_1, "1" ); + BOOST_CHECK_EQUAL( (std::string) bi_3, "3" ); + BOOST_CHECK_EQUAL( (std::string) bi_17, "17" ); + BOOST_CHECK_EQUAL( (std::string) bi_65537, "65537" ); + BOOST_CHECK_EQUAL( (std::string) bi_65537.exp(3), "281487861809153" ); + BOOST_CHECK_EQUAL( (std::string) bi_accu, "12864938683278671740537145998360961546653259485195806" ); + BOOST_CHECK_EQUAL( (std::string) big, "38685626840157682946539517" ); +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/tests/crypto/blind.cpp b/tests/crypto/blind.cpp new file mode 100644 index 000000000..29f81f992 --- /dev/null +++ b/tests/crypto/blind.cpp @@ -0,0 +1,113 @@ +#include + +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(blind_test) +{ + try { + auto InB1 = fc::sha256::hash("InB1"); + auto InB2 = fc::sha256::hash("InB2"); + auto OutB1 = fc::sha256::hash("OutB1"); + + + auto InC1 = fc::ecc::blind(InB1,25); + auto InC2 = fc::ecc::blind(InB2,75); + + auto OutC1 = fc::ecc::blind(OutB1,40); + + auto OutB2 = fc::ecc::blind_sum( {InB1,InB2,OutB1}, 2 ); + auto OutC2 = fc::ecc::blind( OutB2, 60 ); + + /* + FC_ASSERT( fc::ecc::verify_sum( {},{InC1,InC2}, 100 ) ); + FC_ASSERT( fc::ecc::verify_sum( {InC1,InC2}, {}, -100 ) ); + */ + + //FC_ASSERT( fc::ecc::verify_sum( {InC1,InC2}, {OutC1}, -60 ) ); + + + BOOST_CHECK( fc::ecc::verify_sum( {InC1,InC2}, {OutC1,OutC2}, 0 ) ); + auto nonce = fc::sha256::hash("nonce"); + + auto proof = fc::ecc::range_proof_sign( 0, OutC1, OutB1, nonce, 0, 0, 40 ); +// wdump( (proof.size())); + + auto result = fc::ecc::range_get_info( proof ); +// wdump((result)); + BOOST_CHECK( result.max_value >= 60 ); + BOOST_CHECK( result.min_value >= 0 ); + + + auto B1 = fc::sha256::hash("B1"); + auto B2 = fc::sha256::hash("B2"); + auto b3 = fc::sha256::hash("b3"); + auto B4 = fc::sha256::hash("B4"); + auto C1 = fc::ecc::blind( B1, 1 ); + auto C2 = fc::ecc::blind( B2, 2 ); + auto c3 = fc::ecc::blind( b3, 3 ); + auto C4 = fc::ecc::blind( B4, -1 ); + + auto B3 = fc::ecc::blind_sum( {B1,B2}, 2 ); + auto C3 = fc::ecc::blind( B3, 3 ); + + + auto B2m1 = fc::ecc::blind_sum( {B2,B1}, 1 ); + auto C2m1 = fc::ecc::blind( B2m1, 1 ); + + BOOST_CHECK( fc::ecc::verify_sum( {C1,C2}, {C3}, 0 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C1,C2}, {C3}, 0 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C3}, {C1,C2}, 0 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C3}, {C1,C2}, 0 ) ); + + + { + auto B1 = fc::sha256::hash("B1"); + auto B2 = fc::sha256::hash("B2"); + auto B3 = fc::sha256::hash("B3"); + auto B4 = fc::sha256::hash("B4"); + + //secp256k1_scalar_get_b32((unsigned char*)&B1, (const secp256k1_scalar_t*)&B2); + //B1 = fc::variant("b2e5da56ef9f2a34d3e22fd12634bc99261e95c87b9960bf94ed3d27b30").as(); + + auto C1 = fc::ecc::blind( B1, INT64_MAX ); + auto C2 = fc::ecc::blind( B1, 0 ); + auto C3 = fc::ecc::blind( B1, 1 ); + auto C4 = fc::ecc::blind( B1, 2 ); + + BOOST_CHECK( fc::ecc::verify_sum( {C2}, {C3}, -1 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C1}, {C1}, 0 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C2}, {C2}, 0 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C3}, {C2}, 1 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C1}, {C2}, INT64_MAX ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C1}, {C2}, INT64_MAX ) ); + BOOST_CHECK( fc::ecc::verify_sum( {C2}, {C1}, -INT64_MAX ) ); + } + + + { + auto Out1 = fc::sha256::hash("B1"); + auto Out2 = fc::sha256::hash("B2"); + auto OutC1 = fc::ecc::blind( Out1, 250 ); + auto OutC2 = fc::ecc::blind( Out2, 750 ); + auto InBlind = fc::ecc::blind_sum( {Out1,Out2}, 2 ); + auto InC = fc::ecc::blind( InBlind, 1000 ); + auto In0 = fc::ecc::blind( InBlind, 0 ); + + BOOST_CHECK( fc::ecc::verify_sum( {InC}, {OutC1,OutC2}, 0 ) ); + BOOST_CHECK( fc::ecc::verify_sum( {InC}, {In0}, 1000 ) ); + + } + } + catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/blowfish_test.cpp b/tests/crypto/blowfish_test.cpp new file mode 100644 index 000000000..ec4ada7cc --- /dev/null +++ b/tests/crypto/blowfish_test.cpp @@ -0,0 +1,169 @@ +#include + +#include +#include + +// Test vectors from https://www.schneier.com/code/vectors.txt + +struct ecb_testdata { + char key[17], plain[17], cipher[17]; +} ecb_tests[] = { + { "0000000000000000", "0000000000000000", "4EF997456198DD78" }, + { "FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A" }, + { "3000000000000000", "1000000000000001", "7D856F9A613063F2" }, + { "1111111111111111", "1111111111111111", "2466DD878B963C9D" }, + { "0123456789ABCDEF", "1111111111111111", "61F9C3802281B096" }, + { "1111111111111111", "0123456789ABCDEF", "7D0CC630AFDA1EC7" }, + { "0000000000000000", "0000000000000000", "4EF997456198DD78" }, + { "FEDCBA9876543210", "0123456789ABCDEF", "0ACEAB0FC6A0A28D" }, + { "7CA110454A1A6E57", "01A1D6D039776742", "59C68245EB05282B" }, + { "0131D9619DC1376E", "5CD54CA83DEF57DA", "B1B8CC0B250F09A0" }, + { "07A1133E4A0B2686", "0248D43806F67172", "1730E5778BEA1DA4" }, + { "3849674C2602319E", "51454B582DDF440A", "A25E7856CF2651EB" }, + { "04B915BA43FEB5B6", "42FD443059577FA2", "353882B109CE8F1A" }, + { "0113B970FD34F2CE", "059B5E0851CF143A", "48F4D0884C379918" }, + { "0170F175468FB5E6", "0756D8E0774761D2", "432193B78951FC98" }, + { "43297FAD38E373FE", "762514B829BF486A", "13F04154D69D1AE5" }, + { "07A7137045DA2A16", "3BDD119049372802", "2EEDDA93FFD39C79" }, + { "04689104C2FD3B2F", "26955F6835AF609A", "D887E0393C2DA6E3" }, + { "37D06BB516CB7546", "164D5E404F275232", "5F99D04F5B163969" }, + { "1F08260D1AC2465E", "6B056E18759F5CCA", "4A057A3B24D3977B" }, + { "584023641ABA6176", "004BD6EF09176062", "452031C1E4FADA8E" }, + { "025816164629B007", "480D39006EE762F2", "7555AE39F59B87BD" }, + { "49793EBC79B3258F", "437540C8698F3CFA", "53C55F9CB49FC019" }, + { "4FB05E1515AB73A7", "072D43A077075292", "7A8E7BFA937E89A3" }, + { "49E95D6D4CA229BF", "02FE55778117F12A", "CF9C5D7A4986ADB5" }, + { "018310DC409B26D6", "1D9D5C5018F728C2", "D1ABB290658BC778" }, + { "1C587F1C13924FEF", "305532286D6F295A", "55CB3774D13EF201" }, + { "0101010101010101", "0123456789ABCDEF", "FA34EC4847B268B2" }, + { "1F1F1F1F0E0E0E0E", "0123456789ABCDEF", "A790795108EA3CAE" }, + { "E0FEE0FEF1FEF1FE", "0123456789ABCDEF", "C39E072D9FAC631D" }, + { "0000000000000000", "FFFFFFFFFFFFFFFF", "014933E0CDAFF6E4" }, + { "FFFFFFFFFFFFFFFF", "0000000000000000", "F21E9A77B71C49BC" }, + { "0123456789ABCDEF", "0000000000000000", "245946885754369A" }, + { "FEDCBA9876543210", "FFFFFFFFFFFFFFFF", "6B5C5A9C5D9E0A5A" } +}; + +const std::string key_test_key = "F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677"; +const std::string key_test_plain = "FEDCBA9876543210"; +const char key_test_ciphers[][17] = { + "F9AD597C49DB005E", + "E91D21C1D961A6D6", + "E9C2B70A1BC65CF3", + "BE1E639408640F05", + "B39E44481BDB1E6E", + "9457AA83B1928C0D", + "8BB77032F960629D", + "E87A244E2CC85E82", + "15750E7A4F4EC577", + "122BA70B3AB64AE0", + "3A833C9AFFC537F6", + "9409DA87A90F6BF2", + "884F80625060B8B4", + "1F85031C19E11968", + "79D9373A714CA34F", + "93142887EE3BE15C", + "03429E838CE2D14B", + "A4299E27469FF67B", + "AFD5AED1C1BC96A8", + "10851C0E3858DA9F", + "E6F51ED79B9DB21F", + "64A6E14AFD36B46F", + "80C7D7D45A5479AD", + "05044B62FA52D080", +}; + +const std::string chain_test_key = "0123456789ABCDEFF0E1D2C3B4A59687"; +const std::string chain_test_iv = "FEDCBA9876543210"; +const std::string chain_test_plain = "7654321 Now is the time for \0\0\0\0"; +const std::string chain_test_cbc = "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"; +const std::string chain_test_cfb = "E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3"; + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(blowfish_ecb_test) +{ + for ( int i = 0; i < 34; i++ ) { + unsigned char key[8], plain[8], cipher[8], out[8]; + BOOST_CHECK_EQUAL( 8, fc::from_hex( ecb_tests[i].key, (char*) key, sizeof(key) ) ); + BOOST_CHECK_EQUAL( 8, fc::from_hex( ecb_tests[i].plain, (char*) plain, sizeof(plain) ) ); + BOOST_CHECK_EQUAL( 8, fc::from_hex( ecb_tests[i].cipher, (char*) cipher, sizeof(cipher) ) ); + + fc::blowfish fish; + fish.start( key, 8 ); + fish.encrypt( plain, out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( cipher, out, 8) ); + fish.decrypt( out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( plain, out, 8) ); + fish.decrypt( cipher, out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( plain, out, 8) ); + fish.encrypt( out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( cipher, out, 8) ); + } +} + +BOOST_AUTO_TEST_CASE(blowfish_key_test) +{ + unsigned char key[24], plain[8], cipher[8], out[8]; + BOOST_CHECK_EQUAL( 24, fc::from_hex( key_test_key.c_str(), (char*) key, sizeof(key) ) ); + BOOST_CHECK_EQUAL( 8, fc::from_hex( key_test_plain.c_str(), (char*) plain, sizeof(plain) ) ); + + for ( unsigned int i = 0; i < sizeof(key); i++ ) { + BOOST_CHECK_EQUAL( 8, fc::from_hex( key_test_ciphers[i], (char*) cipher, sizeof(cipher) ) ); + fc::blowfish fish; + fish.start( key, i + 1 ); + fish.encrypt( plain, out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( cipher, out, 8) ); + fish.decrypt( out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( plain, out, 8) ); + fish.decrypt( cipher, out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( plain, out, 8) ); + fish.encrypt( out, 8, fc::blowfish::ECB ); + BOOST_CHECK( !memcmp( cipher, out, 8) ); + } +} + +static unsigned int from_bytes( const unsigned char* p ) { + return (((unsigned int) p[0]) << 24) + | (((unsigned int) p[1]) << 16) + | (((unsigned int) p[2]) << 8) + | ((unsigned int) p[3]); +} + +BOOST_AUTO_TEST_CASE(blowfish_chain_test) +{ + unsigned char key[16], iv[8], cipher[32], out[32]; + BOOST_CHECK_EQUAL( 16, fc::from_hex( chain_test_key.c_str(), (char*) key, sizeof(key) ) ); + BOOST_CHECK_EQUAL( 8, fc::from_hex( chain_test_iv.c_str(), (char*) iv, sizeof(iv) ) ); + + BOOST_CHECK_EQUAL( 32, fc::from_hex( chain_test_cbc.c_str(), (char*) cipher, sizeof(cipher) ) ); + fc::blowfish fish; + fish.start( key, sizeof(key), fc::sblock( from_bytes( iv ), from_bytes( iv + 4 ) ) ); + fish.encrypt( (unsigned char*) chain_test_plain.c_str(), out, sizeof(out), fc::blowfish::CBC ); + BOOST_CHECK( !memcmp( cipher, out, sizeof(cipher) ) ); + fish.reset_chain(); + fish.decrypt( out, sizeof(out), fc::blowfish::CBC ); + BOOST_CHECK( !memcmp( chain_test_plain.c_str(), out, 29 ) ); + fish.reset_chain(); + fish.encrypt( out, sizeof(out), fc::blowfish::CBC ); + BOOST_CHECK( !memcmp( cipher, out, sizeof(cipher) ) ); + fish.reset_chain(); + fish.decrypt( cipher, out, sizeof(cipher), fc::blowfish::CBC ); + BOOST_CHECK( !memcmp( chain_test_plain.c_str(), out, 29 ) ); + + BOOST_CHECK_EQUAL( 29, fc::from_hex( chain_test_cfb.c_str(), (char*) cipher, sizeof(cipher) ) ); + fish.reset_chain(); + fish.encrypt( (unsigned char*) chain_test_plain.c_str(), out, sizeof(out), fc::blowfish::CFB ); + BOOST_CHECK( !memcmp( cipher, out, 29 ) ); + fish.reset_chain(); memset( out + 29, 0, 3 ); + fish.decrypt( out, sizeof(out), fc::blowfish::CFB ); + BOOST_CHECK( !memcmp( chain_test_plain.c_str(), out, 29 ) ); + fish.reset_chain(); memset( out + 29, 0, 3 ); + fish.encrypt( out, sizeof(out), fc::blowfish::CFB ); + BOOST_CHECK( !memcmp( cipher, out, 29 ) ); + fish.reset_chain(); memset( out + 29, 0, 3 ); + fish.decrypt( cipher, out, sizeof(cipher), fc::blowfish::CFB ); + BOOST_CHECK( !memcmp( chain_test_plain.c_str(), out, 29 ) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/dh_test.cpp b/tests/crypto/dh_test.cpp new file mode 100644 index 000000000..e972b7845 --- /dev/null +++ b/tests/crypto/dh_test.cpp @@ -0,0 +1,69 @@ +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(dh_test) +{ + fc::diffie_hellman alice; + BOOST_CHECK( alice.generate_params( 128, 5 ) ); + BOOST_CHECK( alice.generate_pub_key() ); + + fc::diffie_hellman bob; + bob.p = alice.p; + BOOST_CHECK( bob.validate() ); + BOOST_CHECK( bob.generate_pub_key() ); + + fc::diffie_hellman charlie; + BOOST_CHECK( !charlie.validate() ); + BOOST_CHECK( !charlie.generate_pub_key() ); + charlie.p = alice.p; + BOOST_CHECK( charlie.validate() ); + BOOST_CHECK( charlie.generate_pub_key() ); + + BOOST_CHECK( alice.compute_shared_key( bob.pub_key ) ); + BOOST_CHECK( bob.compute_shared_key( alice.pub_key ) ); + BOOST_CHECK_EQUAL( alice.shared_key.size(), bob.shared_key.size() ); + BOOST_CHECK( !memcmp( alice.shared_key.data(), bob.shared_key.data(), alice.shared_key.size() ) ); + std::vector alice_bob = alice.shared_key; + + BOOST_CHECK( alice.compute_shared_key( charlie.pub_key ) ); + BOOST_CHECK( charlie.compute_shared_key( alice.pub_key ) ); + BOOST_CHECK_EQUAL( alice.shared_key.size(), charlie.shared_key.size() ); + BOOST_CHECK( !memcmp( alice.shared_key.data(), charlie.shared_key.data(), alice.shared_key.size() ) ); + std::vector alice_charlie = alice.shared_key; + + BOOST_CHECK( charlie.compute_shared_key( bob.pub_key ) ); + BOOST_CHECK( bob.compute_shared_key( charlie.pub_key ) ); + BOOST_CHECK_EQUAL( charlie.shared_key.size(), bob.shared_key.size() ); + BOOST_CHECK( !memcmp( charlie.shared_key.data(), bob.shared_key.data(), bob.shared_key.size() ) ); + std::vector bob_charlie = charlie.shared_key; + + BOOST_CHECK_EQUAL( alice_bob.size(), alice_charlie.size() ); + BOOST_CHECK( memcmp( alice_bob.data(), alice_charlie.data(), alice_bob.size() ) ); + + BOOST_CHECK_EQUAL( alice_bob.size(), bob_charlie.size() ); + BOOST_CHECK( memcmp( alice_bob.data(), bob_charlie.data(), alice_bob.size() ) ); + + BOOST_CHECK_EQUAL( alice_charlie.size(), bob_charlie.size() ); + BOOST_CHECK( memcmp( alice_charlie.data(), bob_charlie.data(), alice_charlie.size() ) ); + + alice.p.clear(); alice.p.push_back(100); alice.p.push_back(2); + BOOST_CHECK( !alice.validate() ); + alice.p = bob.p; + alice.g = 9; + BOOST_CHECK( !alice.validate() ); + +// It ain't easy... +// alice.g = 2; +// BOOST_CHECK( alice.validate() ); +// BOOST_CHECK( alice.generate_pub_key() ); +// BOOST_CHECK( alice.compute_shared_key( bob.pub_key ) ); +// BOOST_CHECK( bob.compute_shared_key( alice.pub_key ) ); +// BOOST_CHECK_EQUAL( alice.shared_key.size(), bob.shared_key.size() ); +// BOOST_CHECK( memcmp( alice.shared_key.data(), bob.shared_key.data(), alice.shared_key.size() ) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/ecc-interop.sh b/tests/crypto/ecc-interop.sh new file mode 100755 index 000000000..98d2b6929 --- /dev/null +++ b/tests/crypto/ecc-interop.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +#TIME=time + +cd "`dirname $0`"/.. + +echo Building ecc_test with openssl... +( +cmake -D ECC_IMPL=openssl . +make ecc_test +mv ecc_test ecc_test.openssl +) >/dev/null 2>&1 + +echo Building ecc_test with secp256k1... +( +cmake -D ECC_IMPL=secp256k1 . +make ecc_test +mv ecc_test ecc_test.secp256k1 +) >/dev/null 2>&1 + +echo Building ecc_test with mixed... +( +cmake -D ECC_IMPL=mixed . +make ecc_test +mv ecc_test ecc_test.mixed +) >/dev/null 2>&1 + +run () { + echo "Running ecc_test.$1 test ecc.interop.$2 ..." + $TIME "./ecc_test.$1" test "ecc.interop.$2" +} + +run openssl openssl +run openssl openssl +run secp256k1 secp256k1 +run secp256k1 secp256k1 +run mixed mixed +run mixed mixed +run openssl secp256k1 +run openssl mixed +run secp256k1 openssl +run secp256k1 mixed +run mixed openssl +run mixed secp256k1 + +echo Done. + +rm -f ecc_test.openssl ecc_test.secp256k1 ecc_test.mixed ecc.interop.openssl ecc.interop.secp256k1 ecc.interop.mixed diff --git a/tests/crypto/ecc_test.cpp b/tests/crypto/ecc_test.cpp new file mode 100644 index 000000000..df809ee09 --- /dev/null +++ b/tests/crypto/ecc_test.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +static std::fstream interop_data; +static bool write_mode = false; + +static void interop_do(const char * const data, size_t len) { + static char buffer[256]; + + if (!interop_data.is_open()) { return; } + + FC_ASSERT(len < sizeof(buffer)); + if (write_mode) { + interop_data.write(data, len); + return; + } + + interop_data.read(buffer, len); + FC_ASSERT(!interop_data.eof()); + FC_ASSERT(!memcmp(data, buffer, len)); +} + +static void interop_do(const fc::ecc::public_key_data &data) { + interop_do(data.begin(), data.size()); +} + +static void interop_do(const fc::ecc::private_key_secret &data) { + interop_do(data.data(), 32); +} + +static void interop_do(const fc::ecc::public_key_point_data &data) { + interop_do(data.begin(), data.size()); +} + +static void interop_do(const std::string &data) { + interop_do(data.c_str(), data.length()); +} + +static void interop_do(const fc::sha512 &data) { + interop_do(data.data(), 64); +} + +static void interop_do(fc::ecc::compact_signature &data) { + if (write_mode) { + interop_data.write((char*) data.begin(), data.size()); + return; + } + + interop_data.read((char*) data.begin(), data.size()); +} + +static void interop_file(const char * const name) { + interop_data.open(name, std::fstream::in | std::fstream::binary); + if (!interop_data.fail()) { return; } + + write_mode = true; + interop_data.open(name, std::fstream::out | std::fstream::binary); + if (!interop_data.fail()) { return; } + + std::cerr << "Can't read nor write " << name << "\n"; +} + +int main( int argc, char** argv ) +{ + if (argc > 2) { + interop_file(argv[2]); + } + + fc::ecc::private_key nullkey; + + for( uint32_t i = 0; i < 3000; ++ i ) + { + try { + FC_ASSERT( argc > 1 ); + + std::string pass(argv[1]); + fc::sha256 h = fc::sha256::hash( pass.c_str(), pass.size() ); + fc::ecc::private_key priv = fc::ecc::private_key::generate_from_seed(h); + FC_ASSERT( nullkey != priv ); + interop_do(priv.get_secret()); + fc::ecc::public_key pub = priv.get_public_key(); + interop_do(pub.serialize()); + interop_do(pub.serialize_ecc_point()); + + pass += "1"; + fc::sha256 h2 = fc::sha256::hash( pass.c_str(), pass.size() ); + fc::ecc::public_key pub1 = pub.add( h2 ); + interop_do(pub1.serialize()); + interop_do(pub1.serialize_ecc_point()); + fc::ecc::private_key priv1 = fc::ecc::private_key::generate_from_seed(h, h2); + interop_do(priv1.get_secret()); + + std::string b58 = pub1.to_base58(); + interop_do(b58); + fc::ecc::public_key pub2 = fc::ecc::public_key::from_base58(b58); + FC_ASSERT( pub1 == pub2 ); + + fc::sha512 shared = priv1.get_shared_secret( pub ); + interop_do(shared); + + auto sig = priv.sign_compact( h ); + interop_do(sig); + auto recover = fc::ecc::public_key( sig, h ); + interop_do(recover.serialize()); + interop_do(recover.serialize_ecc_point()); + FC_ASSERT( recover == pub ); + } + catch ( const fc::exception& e ) + { + edump( (e.to_detail_string()) ); + } + } + + if (interop_data.is_open()) { + interop_data.close(); + } + + return 0; +} diff --git a/tests/crypto/rand_test.cpp b/tests/crypto/rand_test.cpp new file mode 100644 index 000000000..09533be70 --- /dev/null +++ b/tests/crypto/rand_test.cpp @@ -0,0 +1,43 @@ +#include + +#include + +#include + +static void check_randomness( const char* buffer, size_t len ) { + if (len == 0) { return; } + // count bit runs and 0's / 1's + unsigned int zc = 0, oc = 0, rc = 0, last = 2; + for (size_t k = len; k; k--) { + char c = *buffer++; + for (int i = 0; i < 8; i++) { + unsigned int bit = c & 1; + c >>= 1; + if (bit) { oc++; } else { zc++; } + if (bit != last) { rc++; last = bit; } + } + } + BOOST_CHECK_EQUAL( 8*len, zc + oc ); + double E = 1 + (zc + oc) / 2.0; + double variance = (E - 1) * (E - 2) / (oc + zc - 1); + double sigma = sqrt(variance); + BOOST_CHECK( rc > E - sigma && rc < E + sigma); +} + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(rand_test) +{ + char buffer[128]; + fc::rand_bytes( buffer, sizeof(buffer) ); + check_randomness( buffer, sizeof(buffer) ); +} + +BOOST_AUTO_TEST_CASE(pseudo_rand_test) +{ + char buffer[10013]; + fc::rand_pseudo_bytes( buffer, sizeof(buffer) ); + check_randomness( buffer, sizeof(buffer) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/sha_tests.cpp b/tests/crypto/sha_tests.cpp new file mode 100644 index 000000000..850c2f86b --- /dev/null +++ b/tests/crypto/sha_tests.cpp @@ -0,0 +1,178 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +// SHA test vectors taken from http://www.di-mgt.com.au/sha_testvectors.html +static const std::string TEST1("abc"); +static const std::string TEST2(""); +static const std::string TEST3("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); +static const std::string TEST4("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); +static char TEST5[1000001]; +static const std::string TEST6("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"); + +static void init_5() { + memset( TEST5, 'a', sizeof(TEST5) - 1 ); + TEST5[1000000] = 0; +} + +template +void test( const char* to_hash, const std::string& expected ) { + H hash = H::hash( to_hash, strlen( to_hash ) ); + BOOST_CHECK_EQUAL( expected, (std::string) hash ); + H hash2( expected ); + BOOST_CHECK( hash == hash2 ); +} + +template +void test( const std::string& to_hash, const std::string& expected ) { + H hash = H::hash( to_hash ); + BOOST_CHECK_EQUAL( expected, (std::string) hash ); + test( to_hash.c_str(), expected ); +} + +template +void test_big( const std::string& expected ) { + typename H::encoder enc; + for (char c : TEST6) { enc.put(c); } + for (int i = 0; i < 16777215; i++) { + enc.write( TEST6.c_str(), TEST6.size() ); + } + H hash = enc.result(); + BOOST_CHECK_EQUAL( expected, (std::string) hash ); + + enc.reset(); + enc.write( TEST1.c_str(), TEST1.size() ); + hash = enc.result(); + BOOST_CHECK( hash >= H::hash( TEST1 ) ); + test( TEST1, (std::string) hash ); + + hash = hash ^ hash; + hash.data()[hash.data_size() - 1] = 1; + for (int i = hash.data_size() * 8 - 1; i > 0; i--) { + H other = hash << i; + BOOST_CHECK( other != hash ); + BOOST_CHECK( other > hash ); + BOOST_CHECK( hash < other ); + } + + H hash2( expected ); + fc::variant v; + to_variant( hash2, v ); + from_variant( v, hash ); + BOOST_CHECK( hash == hash2 ); + + H hash3( expected.substr(15) + "000000000000000" ); + BOOST_CHECK( hash3 == hash2 << 60 ); +} + +template +void test_stream( ) { + H hash( TEST1 ); + std::stringstream stream; + stream << hash; + + H other; + stream >> other; + BOOST_CHECK( hash == other ); +} + +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); + +template void test_stream(); +template void test_stream(); +template void test_stream(); + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(ripemd160_test) +{ + init_5(); + test( TEST1, "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc" ); + test( TEST2, "9c1185a5c5e9fc54612808977ee8f548b2258d31" ); + test( TEST3, "12a053384a9c0c88e405a06c27dcf49ada62eb2b" ); +// test( TEST4, "" ); + test( TEST5, "52783243c1697bdbe16d37f97f68f08325dc1528" ); + test_big( "29b6df855772aa9a95442bf83b282b495f9f6541" ); + test_stream(); +} + +BOOST_AUTO_TEST_CASE(sha1_test) +{ + init_5(); + test( TEST1, "a9993e364706816aba3e25717850c26c9cd0d89d" ); + test( TEST2, "da39a3ee5e6b4b0d3255bfef95601890afd80709" ); + test( TEST3, "84983e441c3bd26ebaae4aa1f95129e5e54670f1" ); + test( TEST4, "a49b2446a02c645bf419f995b67091253a04a259" ); + test( TEST5, "34aa973cd4c4daa4f61eeb2bdbad27316534016f" ); + test_big( "7789f0c9ef7bfc40d93311143dfbe69e2017f592" ); +} + +BOOST_AUTO_TEST_CASE(sha224_test) +{ + init_5(); + test( TEST1, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7" ); + test( TEST2, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ); + test( TEST3, "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525" ); + test( TEST4, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3" ); + test( TEST5, "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67" ); + test_big( "b5989713ca4fe47a009f8621980b34e6d63ed3063b2a0a2c867d8a85" ); +} + +BOOST_AUTO_TEST_CASE(sha256_test) +{ + init_5(); + test( TEST1, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" ); + test( TEST2, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ); + test( TEST3, "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" ); + test( TEST4, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1" ); + test( TEST5, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" ); + test_big( "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e" ); + test_stream(); + + std::vector test_object; + test_object.push_back( 42 ); + fc::sha256 digest = fc::digest( test_object ); + BOOST_CHECK( digest == fc::sha256::hash( test_object ) ); + fc::sha256 other( digest.data(), digest.data_size() ); + BOOST_CHECK( digest == other ); + fc::sha512 yet_another = fc::sha512::hash( TEST1 ); + try { + fc::sha256 fourth( yet_another.data(), yet_another.data_size() ); + BOOST_FAIL( "Expected exception!" ); + } catch ( fc::exception& expected ) {} + + fc::sha256 fourth( "445C7A8007A93D8733188288BB320A8FE2DEBD2AE1B47F0F50BC10BAE845C094" ); + BOOST_CHECK_EQUAL( "d61967f63c7dd183914a4ae452c9f6ad5d462ce3d277798075b107615c1a8a30", (std::string) fc::sha256::hash(fourth) ); +} + +BOOST_AUTO_TEST_CASE(sha512_test) +{ + init_5(); + test( TEST1, "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" ); + test( TEST2, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" + "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" ); + test( TEST3, "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c335" + "96fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" ); + test( TEST4, "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" + "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909" ); + test( TEST5, "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" + "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b" ); + test_big( "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d" + "77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086" ); + test_stream(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/hmac_test.cpp b/tests/hmac_test.cpp new file mode 100644 index 000000000..a8f21dd6d --- /dev/null +++ b/tests/hmac_test.cpp @@ -0,0 +1,118 @@ +#define BOOST_TEST_MODULE HmacTest +#include +#include +#include +#include +#include +#include +#include + +// See http://tools.ietf.org/html/rfc4231 + +static const fc::string TEST1_KEY = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; +static const fc::string TEST1_DATA = "4869205468657265"; +static const fc::string TEST1_224 = "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22"; +static const fc::string TEST1_256 = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"; +static const fc::string TEST1_512 = "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"; + +static const fc::string TEST2_KEY = "4a656665"; +static const fc::string TEST2_DATA = "7768617420646f2079612077616e7420666f72206e6f7468696e673f"; +static const fc::string TEST2_224 = "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44"; +static const fc::string TEST2_256 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"; +static const fc::string TEST2_512 = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"; + +static const fc::string TEST3_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +static const fc::string TEST3_DATA = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddddddd"; +static const fc::string TEST3_224 = "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea"; +static const fc::string TEST3_256 = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"; +static const fc::string TEST3_512 = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"; + +static const fc::string TEST4_KEY = "0102030405060708090a0b0c0d0e0f10111213141516171819"; +static const fc::string TEST4_DATA = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"; +static const fc::string TEST4_224 = "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a"; +static const fc::string TEST4_256 = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"; +static const fc::string TEST4_512 = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"; + +// test 5 skipped - truncated + +static const fc::string TEST6_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa"; +static const fc::string TEST6_DATA = "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" + "65204b6579202d2048617368204b6579204669727374"; +static const fc::string TEST6_224 = "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e"; +static const fc::string TEST6_256 = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"; +static const fc::string TEST6_512 = "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"; + +static const fc::string TEST7_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa"; +static const fc::string TEST7_DATA = "5468697320697320612074657374207573696e672061206c6172676572207468" + "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" + "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" + "647320746f20626520686173686564206265666f7265206265696e6720757365" + "642062792074686520484d414320616c676f726974686d2e"; +static const fc::string TEST7_224 = "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"; +static const fc::string TEST7_256 = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"; +static const fc::string TEST7_512 = "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"; + +static fc::hmac mac_224; +static fc::hmac mac_256; +static fc::hmac mac_512; + +template +static void run_test( const fc::string& key, const fc::string& data, const fc::string& expect_224, + const fc::string& expect_256, const fc::string& expect_512 ) +{ + + fc::array key_arr; + BOOST_CHECK_EQUAL( fc::from_hex( key, key_arr.begin(), key_arr.size() ), N ); + fc::array data_arr; + BOOST_CHECK_EQUAL( fc::from_hex( data, data_arr.begin(), data_arr.size() ), M ); + + BOOST_CHECK_EQUAL( mac_224.digest( key_arr.begin(), N, data_arr.begin(), M ).str(), expect_224 ); + BOOST_CHECK_EQUAL( mac_256.digest( key_arr.begin(), N, data_arr.begin(), M ).str(), expect_256 ); + BOOST_CHECK_EQUAL( mac_512.digest( key_arr.begin(), N, data_arr.begin(), M ).str(), expect_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_1) +{ + run_test<20,8>( TEST1_KEY, TEST1_DATA, TEST1_224, TEST1_256, TEST1_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_2) +{ + run_test<4,28>( TEST2_KEY, TEST2_DATA, TEST2_224, TEST2_256, TEST2_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_3) +{ + run_test<20,50>( TEST3_KEY, TEST3_DATA, TEST3_224, TEST3_256, TEST3_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_4) +{ + run_test<25,50>( TEST4_KEY, TEST4_DATA, TEST4_224, TEST4_256, TEST4_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_6) +{ + run_test<131,54>( TEST6_KEY, TEST6_DATA, TEST6_224, TEST6_256, TEST6_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_7) +{ + run_test<131,152>( TEST7_KEY, TEST7_DATA, TEST7_224, TEST7_256, TEST7_512 ); +} diff --git a/tests/json_rpc_test.cpp b/tests/json_rpc_test.cpp deleted file mode 100644 index 25b8d699d..000000000 --- a/tests/json_rpc_test.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -struct namep { - fc::string a; - double b; -}; - -FC_REFLECT( namep, (a)(b) ) -FC_JSON_NAMED_PARAMS( namep ) - -struct test { - int add(int x){ return x+1; } - int sub(int x){ return x-1; } - int sub1(int x){ return 3; } - int namep_test(namep x){ return 3; } - int sub2(float x){ return 3; } - int sub3(double x, int y){ return x-y; } - int sub4(uint16_t x){ return 3; } - int sub5(char x){ return 3; } - int sub6(uint64_t x){ return 3; } - int sub7(int x){ return 3; } - int sub8(int x){ return 3; } - int sub9(int x){ return 3; } -}; - -FC_STUB( test, (add)(namep_test)(sub)(sub1)(sub2)(sub3)(sub4)(sub5)(sub6)(sub7)(sub8)(sub9) ) - -int main( int argc, char** argv ) { - try { - slog( "Hello World\n" ); - fc::value v = fc::string("goodbye"); - slog("."); - fc::value v2; - slog(".."); - v2["a"]; - slog("........ v2[a] %p = v %p", &v2["a"], &v); - v2["a"] = v; - slog("..."); - fc::value& b = v2["b"]; - slog( "...."); - b = fc::string("hello"); - slog("....."); - return 0; - - fc::ptr t( new test() ); - fc::json::rpc_tcp_server serv; - serv.add_interface( t ); - serv.listen(8001); - slog( "%s", fc::json::to_string( fc::json::detail::named_param >::to_value(fc::tuple() ) ).c_str() ); - slog("..."); - { - wlog( "create new connection" ); - fc::json::rpc_tcp_connection::ptr con(new fc::json::rpc_tcp_connection()); - wlog( "connnect to..." ); - con->connect_to( fc::ip::endpoint::from_string("127.0.0.1:8001") ); - wlog( "connected, " ); - - fc::json::rpc_client rpcc( con ); - slog( "5+1=%d", rpcc->add(5).wait() ); - slog( "sub3 4-5=%d", rpcc->sub3(4,5).wait() ); - slog( "namep=%d", rpcc->namep_test(namep()).wait() ); - } - slog( "exit serv" ); - /* - fc::json::rpc_connection::ptr con( new fc::json::rpc_stream_connection( fc::cin, fc::cout ) ); - fc::json::rpc_client c( con ); - - slog( "%d", c->add( 5 ).wait() ); - slog( "%d", c->add( 6 ).wait() ); - */ - - slog( "Exiting" ); - } catch ( ... ) { - elog( "%s", fc::except_str().c_str() ); - } - - return 0; -} diff --git a/tests/logger.cpp b/tests/logger.cpp deleted file mode 100644 index 350cb851c..000000000 --- a/tests/logger.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include - -int main( int argc, char** argv ) { - auto lgr = fc::logger::get(); - auto dconfig = fc::logging_config::default_config(); - dconfig.appenders.push_back( fc::appender_config("logfile", "file", fc::value(fc::file_appender::config("test.log")) ) ); - dconfig.loggers.push_back( fc::logger_config("main").add_appender("stderr").add_appender("logfile") ); - fc::configure_logging( dconfig ); - fc_dlog( lgr, "Hello Debug" ); - fc_ilog( lgr, "Hello Info" ); - fc_wlog( lgr, "Hello Warn" ); - fc_elog( lgr, "Hello Error" ); - fc_flog( lgr, "Hello Fatal" ); - - - auto main_lgr = fc::logger::get( "main" ); - fc_dlog( main_lgr, "Hello Debug" ); - fc_ilog( main_lgr, "Hello Info" ); - fc_wlog( main_lgr, "Hello Warn" ); - fc_elog( main_lgr, "Hello Error" ); - fc_flog( main_lgr, "Hello Fatal" ); - return 0; -} diff --git a/tests/network/http/websocket_test.cpp b/tests/network/http/websocket_test.cpp new file mode 100644 index 000000000..03bfdd154 --- /dev/null +++ b/tests/network/http/websocket_test.cpp @@ -0,0 +1,70 @@ +#include + +#include + +#include + +BOOST_AUTO_TEST_SUITE(fc_network) + +BOOST_AUTO_TEST_CASE(websocket_test) +{ + fc::http::websocket_client client; + fc::http::websocket_connection_ptr s_conn, c_conn; + { + fc::http::websocket_server server; + server.on_connection([&]( const fc::http::websocket_connection_ptr& c ){ + s_conn = c; + c->on_message_handler([&](const std::string& s){ + c->send_message("echo: " + s); + }); + }); + + server.listen( 8090 ); + server.start_accept(); + + std::string echo; + c_conn = client.connect( "ws://localhost:8090" ); + c_conn->on_message_handler([&](const std::string& s){ + echo = s; + }); + c_conn->send_message( "hello world" ); + fc::usleep( fc::seconds(1) ); + BOOST_CHECK_EQUAL("echo: hello world", echo); + c_conn->send_message( "again" ); + fc::usleep( fc::seconds(1) ); + BOOST_CHECK_EQUAL("echo: again", echo); + + s_conn->close(0, "test"); + fc::usleep( fc::seconds(1) ); + try { + c_conn->send_message( "again" ); + BOOST_FAIL("expected assertion failure"); + } catch (const fc::assert_exception& e) { + //std::cerr << e.to_string() << "\n"; + } + + c_conn = client.connect( "ws://localhost:8090" ); + c_conn->on_message_handler([&](const std::string& s){ + echo = s; + }); + c_conn->send_message( "hello world" ); + fc::usleep( fc::seconds(1) ); + BOOST_CHECK_EQUAL("echo: hello world", echo); + } + + try { + c_conn->send_message( "again" ); + BOOST_FAIL("expected assertion failure"); + } catch (const fc::assert_exception& e) { + std::cerr << e.to_string() << "\n"; + } + + try { + c_conn = client.connect( "ws://localhost:8090" ); + BOOST_FAIL("expected assertion failure"); + } catch (const fc::assert_exception& e) { + std::cerr << e.to_string() << "\n"; + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/network/ntp_test.cpp b/tests/network/ntp_test.cpp new file mode 100644 index 000000000..f0c02d48e --- /dev/null +++ b/tests/network/ntp_test.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_network) + +BOOST_AUTO_TEST_CASE( ntp_test ) +{ + fc::ntp ntp_service; + ntp_service.set_request_interval(5); + fc::usleep(fc::seconds(4) ); + auto time = ntp_service.get_time(); + BOOST_CHECK( time ); + auto ntp_time = *time; + auto delta = ntp_time - fc::time_point::now(); +// auto minutes = delta.count() / 1000000 / 60; +// auto hours = delta.count() / 1000000 / 60 / 60; +// auto seconds = delta.count() / 1000000; + auto msec= delta.count() / 1000; + BOOST_CHECK( msec < 100 ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/rate_limiting.cpp b/tests/rate_limiting.cpp new file mode 100644 index 000000000..e33b2ff16 --- /dev/null +++ b/tests/rate_limiting.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +#include +fc::rate_limiting_group rate_limiter(1000000, 1000000); + +void download_url(const std::string& ip_address, const std::string& url) +{ + fc::http::connection http_connection; + rate_limiter.add_tcp_socket(&http_connection.get_socket()); + http_connection.connect_to(fc::ip::endpoint(fc::ip::address(ip_address.c_str()), 80)); + std::cout << "Starting download...\n"; + fc::time_point start_time(fc::time_point::now()); + fc::http::reply reply = http_connection.request("GET", "http://mirror.cs.vt.edu/pub/cygwin/glibc/releases/glibc-2.9.tar.gz"); + fc::time_point end_time(fc::time_point::now()); + + std::cout << "HTTP return code: " << reply.status << "\n"; + std::cout << "Retreived " << reply.body.size() << " bytes in " << ((end_time - start_time).count() / fc::milliseconds(1).count()) << "ms\n"; + std::cout << "Average speed " << ((1000 * (uint64_t)reply.body.size()) / ((end_time - start_time).count() / fc::milliseconds(1).count())) << " bytes per second"; +} + +int main( int argc, char** argv ) +{ + rate_limiter.set_actual_rate_time_constant(fc::seconds(1)); + + std::vector > download_futures; + download_futures.push_back(fc::async([](){ download_url("198.82.184.145", "http://mirror.cs.vt.edu/pub/cygwin/glibc/releases/glibc-2.9.tar.gz"); })); + download_futures.push_back(fc::async([](){ download_url("198.82.184.145", "http://mirror.cs.vt.edu/pub/cygwin/glibc/releases/glibc-2.7.tar.gz"); })); + + while (1) + { + bool all_done = true; + for (unsigned i = 0; i < download_futures.size(); ++i) + if (!download_futures[i].ready()) + all_done = false; + if (all_done) + break; + std::cout << "Current measurement of actual transfer rate: upload " << rate_limiter.get_actual_upload_rate() << ", download " << rate_limiter.get_actual_download_rate() << "\n"; + fc::usleep(fc::seconds(1)); + } + + for (unsigned i = 0; i < download_futures.size(); ++i) + download_futures[i].wait(); + return 0; +} diff --git a/tests/real128_test.cpp b/tests/real128_test.cpp new file mode 100644 index 000000000..beae9b7e4 --- /dev/null +++ b/tests/real128_test.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(fc) + +using fc::real128; +using std::string; + +BOOST_AUTO_TEST_CASE(real128_test) +{ + BOOST_CHECK_EQUAL(string(real128()), string("0.")); + BOOST_CHECK_EQUAL(string(real128(0)), string("0.")); + BOOST_CHECK_EQUAL(real128(8).to_uint64(), 8); + BOOST_CHECK_EQUAL(real128(6789).to_uint64(), 6789); + BOOST_CHECK_EQUAL(real128(10000).to_uint64(), 10000); + BOOST_CHECK_EQUAL(string(real128(1)), string("1.")); + BOOST_CHECK_EQUAL(string(real128(5)), string("5.")); + BOOST_CHECK_EQUAL(string(real128(12345)), string("12345.")); + BOOST_CHECK_EQUAL(string(real128(0)), string(real128("0"))); + + real128 ten(10); + real128 two(2); + real128 twenty(20); + real128 pi(31415926535); + pi /= 10000000000; + + BOOST_CHECK_EQUAL( string(ten), "10." ); + BOOST_CHECK_EQUAL( string(two), "2." ); + BOOST_CHECK_EQUAL( string(ten+two), "12." ); + BOOST_CHECK_EQUAL( string(ten-two), "8." ); + BOOST_CHECK_EQUAL( string(ten*two), "20." ); + BOOST_CHECK_EQUAL( string(ten/two), "5." ); + BOOST_CHECK_EQUAL( string(ten/two/two/two*two*two*two), "10." ); + BOOST_CHECK_EQUAL( string(ten/two/two/two*two*two*two), string(ten) ); + BOOST_CHECK_EQUAL( string(twenty/ten), string(two) ); + BOOST_CHECK_EQUAL( string(pi), "3.1415926535" ); + BOOST_CHECK_EQUAL( string(pi*10), "31.415926535" ); + BOOST_CHECK_EQUAL( string(pi*20), "62.83185307" ); + BOOST_CHECK_EQUAL( string(real128("62.83185307")/twenty), string(pi) ); + BOOST_CHECK_EQUAL( string(pi*1), "3.1415926535" ); + BOOST_CHECK_EQUAL( string(pi*0), "0." ); + + BOOST_CHECK_EQUAL(real128("12345.6789").to_uint64(), 12345); + BOOST_CHECK_EQUAL((real128("12345.6789")*10000).to_uint64(), 123456789); + BOOST_CHECK_EQUAL(string(real128("12345.6789")), string("12345.6789")); + + BOOST_CHECK_EQUAL( real128(uint64_t(-1)).to_uint64(), uint64_t(-1) ); + + wdump( (ten)(two)(twenty) ); + wdump((real128("12345.6789")) ); + wdump( (ten/3*3) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/rpc.cpp b/tests/rpc.cpp new file mode 100644 index 000000000..0fde4a3a6 --- /dev/null +++ b/tests/rpc.cpp @@ -0,0 +1,8 @@ + + + + + +int main( int argc, char** argv ) +{ +} diff --git a/tests/sleep.cpp b/tests/sleep.cpp new file mode 100644 index 000000000..84d80ea58 --- /dev/null +++ b/tests/sleep.cpp @@ -0,0 +1,17 @@ +#include +#include + +int main( int argc, char** argv ) +{ + fc::thread test("test"); + auto result = test.async( [=]() { + while( true ) + { + fc::usleep( fc::microseconds(1000) ); + } + }); + char c; + std::cin >> c; + test.quit(); + return 0; +} diff --git a/tests/ssh.cpp b/tests/ssh.cpp deleted file mode 100644 index d3c0e318f..000000000 --- a/tests/ssh.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include -//#include - -int main( int argc, char** argv ) { - try { - slog( "create ssh client" ); - fc::ssh::client c; - c.connect( "dlarimer", "rapture", "10.10.10.112" ); - slog( "connected" ); - fc::ssh::process proc = c.exec( "/bin/cat -u" ); - slog( "proc!"); - fc::string hello( "hello.............." ); - hello += hello; - hello += hello; - hello += hello; - hello += hello; - hello += hello; - hello += "\n"; - /* - hello += hello2; - */ - fc::string line; - proc.in_stream().write(hello.c_str(), hello.size() ); - fc::getline( proc.out_stream(), line ); - fc::cout< + +#include +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_thread) + +BOOST_AUTO_TEST_CASE( leave_mutex_locked ) +{ + { + fc::mutex test_mutex; + fc::future test_task = fc::async([&](){ + fc::scoped_lock test_lock(test_mutex); + for (int i = 0; i < 10; ++i) + fc::usleep(fc::seconds(1)); + }, "test_task"); + fc::usleep(fc::seconds(3)); + test_task.cancel_and_wait("cancel called directly by test"); + } + BOOST_TEST_PASSPOINT(); +} + +BOOST_AUTO_TEST_CASE( cancel_task_blocked_on_mutex) +{ + { + fc::mutex test_mutex; + fc::future test_task; + { + fc::scoped_lock test_lock(test_mutex); + test_task = fc::async([&test_mutex](){ + BOOST_TEST_MESSAGE("--- In test_task, locking mutex"); + fc::scoped_lock async_task_test_lock(test_mutex); + BOOST_TEST_MESSAGE("--- In test_task, mutex locked, commencing sleep"); + for (int i = 0; i < 10; ++i) + fc::usleep(fc::seconds(1)); + BOOST_TEST_MESSAGE("--- In test_task, sleeps done, exiting"); + }, "test_task"); + fc::usleep(fc::seconds(3)); + { + fc::scoped_lock test_lock2(test_mutex); + test_task.cancel(); + try + { + test_task.wait(fc::seconds(1)); + BOOST_ERROR("test should have been canceled, not exited cleanly"); + } + catch (const fc::canceled_exception&) + { + BOOST_TEST_PASSPOINT(); + } + catch (const fc::timeout_exception&) + { + BOOST_ERROR("unable to cancel task blocked on mutex"); + } + } + BOOST_TEST_MESSAGE("Unlocking mutex locked from the main task so the test task will have the opportunity to lock it and be canceled"); + } + fc::usleep(fc::seconds(3)); + + test_task.cancel_and_wait("cleaning up test"); + } +} + + +BOOST_AUTO_TEST_CASE( test_non_preemptable_assertion ) +{ + return; // this isn't a real test, because the thing it tries to test works by asserting, not by throwing + fc::usleep(fc::milliseconds(10)); // this should not assert + { + ASSERT_TASK_NOT_PREEMPTED(); + fc::usleep(fc::seconds(1)); // this should assert + } + + { + ASSERT_TASK_NOT_PREEMPTED(); + { + ASSERT_TASK_NOT_PREEMPTED(); + fc::usleep(fc::seconds(1)); // this should assert + } + } + + { + ASSERT_TASK_NOT_PREEMPTED(); + { + ASSERT_TASK_NOT_PREEMPTED(); + int i = 4; + i += 2; + } + fc::usleep(fc::seconds(1)); // this should assert + } + + fc::usleep(fc::seconds(1)); // this should not assert + BOOST_TEST_PASSPOINT(); +} + +BOOST_AUTO_TEST_CASE( cancel_an_sleeping_task ) +{ + enum task_result{sleep_completed, sleep_aborted}; + fc::future task = fc::async([]() { + BOOST_TEST_MESSAGE("Starting async task"); + try + { + fc::usleep(fc::seconds(5)); + return sleep_completed; + } + catch (const fc::exception&) + { + return sleep_aborted; + } + }, "test_task"); + + fc::time_point start_time = fc::time_point::now(); + + // wait a bit for the task to start running + fc::usleep(fc::milliseconds(100)); + + BOOST_TEST_MESSAGE("Canceling task"); + task.cancel("canceling to test if cancel works"); + try + { + task_result result = task.wait(); + BOOST_CHECK_MESSAGE(result != sleep_completed, "sleep should have been canceled"); + } + catch (fc::exception& e) + { + BOOST_TEST_MESSAGE("Caught exception from canceled task: " << e.what()); + BOOST_CHECK_MESSAGE(fc::time_point::now() - start_time < fc::seconds(4), "Task was not canceled quickly"); + } +} + +BOOST_AUTO_TEST_CASE( cancel_a_task_waiting_on_promise ) +{ + enum task_result{task_completed, task_aborted}; + + fc::promise::ptr promise_to_wait_on(new fc::promise()); + + fc::future task = fc::async([promise_to_wait_on]() { + BOOST_TEST_MESSAGE("Starting async task"); + try + { + promise_to_wait_on->wait_until(fc::time_point::now() + fc::seconds(5)); + return task_completed; + } + catch (const fc::canceled_exception&) + { + BOOST_TEST_MESSAGE("Caught canceled_exception inside task-to-be-canceled"); + throw; + } + catch (const fc::timeout_exception&) + { + BOOST_TEST_MESSAGE("Caught timeout_exception inside task-to-be-canceled"); + throw; + } + catch (const fc::exception& e) + { + BOOST_TEST_MESSAGE("Caught unexpected exception inside task-to-be-canceled: " << e.to_detail_string()); + return task_aborted; + } + }, "test_task"); + + fc::time_point start_time = fc::time_point::now(); + + // wait a bit for the task to start running + fc::usleep(fc::milliseconds(100)); + + BOOST_TEST_MESSAGE("Canceling task"); + task.cancel("canceling to test if cancel works"); + //promise_to_wait_on->set_value(); + try + { + task_result result = task.wait(); + BOOST_CHECK_MESSAGE(result != task_completed, "task should have been canceled"); + } + catch (fc::exception& e) + { + BOOST_TEST_MESSAGE("Caught exception from canceled task: " << e.what()); + BOOST_CHECK_MESSAGE(fc::time_point::now() - start_time < fc::seconds(4), "Task was not canceled quickly"); + } +} + +BOOST_AUTO_TEST_CASE( cleanup_cancelled_task ) +{ + std::shared_ptr some_string(std::make_shared("some string")); + fc::future task = fc::async([some_string]() { + BOOST_TEST_MESSAGE("Starting async task, bound string is " << *some_string); + try + { + fc::usleep(fc::seconds(5)); + BOOST_TEST_MESSAGE("Finsihed usleep in async task, leaving the task's functor"); + } + catch (...) + { + BOOST_TEST_MESSAGE("Caught exception in async task, leaving the task's functor"); + } + }, "test_task"); + std::weak_ptr weak_string_ptr(some_string); + some_string.reset(); + BOOST_CHECK_MESSAGE(!weak_string_ptr.expired(), "Weak pointer should still be valid because async task should be holding the strong pointer"); + fc::usleep(fc::milliseconds(100)); + BOOST_TEST_MESSAGE("Canceling task"); + task.cancel("canceling to test if cancel works"); + try + { + task.wait(); + } + catch (fc::exception& e) + { + BOOST_TEST_MESSAGE("Caught exception from canceled task: " << e.what()); + } +// BOOST_CHECK_MESSAGE(weak_string_ptr.expired(), "Weak pointer should now be invalid because async task should be done with it"); + task = fc::future(); + BOOST_CHECK_MESSAGE(weak_string_ptr.expired(), "Weak pointer should now be invalid because async task should have been destroyed"); +} + +int task_execute_count = 0; +fc::future simple_task_done; +void simple_task() +{ + task_execute_count++; + simple_task_done = fc::schedule([](){ simple_task(); }, + fc::time_point::now() + fc::seconds(3), + "simple_task"); +} + +BOOST_AUTO_TEST_CASE( cancel_scheduled_task ) +{ + //bool task_executed = false; + try + { +// simple_task(); + simple_task(); + fc::usleep(fc::seconds(4)); + simple_task_done.cancel("canceling scheduled task to test if cancel works"); + simple_task_done.wait(); + BOOST_CHECK_EQUAL(task_execute_count, 2); + fc::usleep(fc::seconds(3)); + BOOST_CHECK_EQUAL(task_execute_count, 2); + } + catch ( const fc::exception& e ) + { + wlog( "${e}", ("e",e.to_detail_string() ) ); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/udt_client.cpp b/tests/udt_client.cpp new file mode 100644 index 000000000..f575b28ec --- /dev/null +++ b/tests/udt_client.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +using namespace std; +using namespace UDT; + +int main() +{ + UDTSOCKET client = UDT::socket(AF_INET, SOCK_STREAM, 0); + + sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(9000); + inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr); + + memset(&(serv_addr.sin_zero), '\0', 8); + + // connect to the server, implict bind + if (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr))) + { + cout << "connect: " << UDT::getlasterror().getErrorMessage(); + return 0; + } + + char* hello = "hello world! 3\n"; + if (UDT::ERROR == UDT::send(client, hello, strlen(hello) + 1, 0)) + { + cout << "send: " << UDT::getlasterror().getErrorMessage(); + return 0; + } + + UDT::close(client); + + return 1; +} diff --git a/tests/udt_server.cpp b/tests/udt_server.cpp new file mode 100644 index 000000000..ff7e2fbcd --- /dev/null +++ b/tests/udt_server.cpp @@ -0,0 +1,83 @@ +#include +#include +#include + +using namespace std; + +int main( int argc, char** argv ) +{ + UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0); + bool block = false; + + sockaddr_in my_addr; + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(9000); + my_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(my_addr.sin_zero), '\0', 8); + + if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr))) + { + cout << "bind: " << UDT::getlasterror().getErrorMessage(); + return 0; + } + UDT::listen(serv, 10); + + int namelen; + sockaddr_in their_addr; + + + UDT::setsockopt(serv, 0, UDT_SNDSYN, &block, sizeof(bool)); + UDT::setsockopt(serv, 0, UDT_RCVSYN, &block, sizeof(bool)); + UDTSOCKET recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen); + if( recver == UDT::INVALID_SOCK ) + { + if( UDT::getlasterror_code() == CUDTException::EASYNCRCV ) + { + std::cout << "nothing yet... better luck next time\n"; + } + } + auto pollid = UDT::epoll_create(); + UDT::epoll_add_usock(pollid, serv, nullptr );// const int* events = NULL); + std::set readready; + std::set writeready; + std::cout << "waiting for 5 seconds\n"; + UDT::epoll_wait( pollid, &readready, &writeready, 10000 ); + + + recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen); + if( recver == UDT::INVALID_SOCK ) + { + if( UDT::getlasterror_code() == CUDTException::EASYNCRCV ) + { + std::cout << "nothing yet... better luck next time\n"; + } + return 0; + } + UDT::setsockopt(recver, 0, UDT_SNDSYN, &block, sizeof(bool)); + UDT::setsockopt(recver, 0, UDT_RCVSYN, &block, sizeof(bool)); + UDT::epoll_remove_usock(pollid, serv );// const int* events = NULL); + int events = UDT_EPOLL_IN; + + UDT::epoll_add_usock(pollid, recver, &events );// const int* events = NULL); + + readready.clear(); + UDT::epoll_wait( pollid, &readready, &writeready, 5000 ); + + char ip[16]; + cout << "new connection: " << inet_ntoa(their_addr.sin_addr) << ":" << ntohs(their_addr.sin_port) << endl; + + char data[100]; + + while (UDT::ERROR == UDT::recv(recver, data, 100, 0)) + { + cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl; + UDT::epoll_wait( pollid, &readready, &writeready, 5000 ); + } + + cout << data << endl; + + UDT::close(recver); + UDT::close(serv); + + return 1; +} diff --git a/tests/udtc.cpp b/tests/udtc.cpp new file mode 100644 index 000000000..aa48bb33e --- /dev/null +++ b/tests/udtc.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include + +using namespace fc; + +int main( int argc, char** argv ) +{ + try { + udt_socket sock; + sock.bind( fc::ip::endpoint::from_string( "127.0.0.1:6666" ) ); + ilog( "." ); + sock.connect_to( fc::ip::endpoint::from_string( "127.0.0.1:7777" ) ); + ilog( "after connect to..." ); + + std::cout << "local endpoint: " < response; + response.resize(1024); + int r = sock.readsome( response.data(), response.size() ); + while( r ) + { + std::cout.write( response.data(), r ); + r = sock.readsome( response.data(), response.size() ); + } + */ + // if we exit too quickly, UDT will not have a chance to + // send the graceful close message. + //fc::usleep( fc::seconds(1) ); + } catch ( const fc::exception& e ) + { + elog( "${e}", ("e",e.to_detail_string() ) ); + } + + return 0; +} diff --git a/tests/udts.cpp b/tests/udts.cpp new file mode 100644 index 000000000..8b8d5750f --- /dev/null +++ b/tests/udts.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +using namespace fc; + +int main( int argc, char** argv ) +{ + try { + udt_server serv; + serv.listen( fc::ip::endpoint::from_string( "127.0.0.1:7777" ) ); + + while( true ) + { + udt_socket sock; + serv.accept( sock ); + + std::vector response; + response.resize(1024); + int r = sock.readsome( response.data(), response.size() ); + while( r ) + { + std::cout.write( response.data(), r ); + r = sock.readsome( response.data(), response.size() ); + //sock.write( response.data(), response.size() ); + } + + std::string goodbye = "goodbye cruel world"; + sock.write( goodbye.c_str(), goodbye.size() ); + } + } catch ( const fc::exception& e ) + { + elog( "${e}", ("e",e.to_detail_string() ) ); + } + + return 0; +} diff --git a/tests/utf8_test.cpp b/tests/utf8_test.cpp new file mode 100644 index 000000000..833d26de1 --- /dev/null +++ b/tests/utf8_test.cpp @@ -0,0 +1,84 @@ +#include + +#include + +using namespace fc; + +static const std::string TEST_INVALID_1("\375\271\261\241\201\211\001"); +static const std::string TEST_INVALID_2("\371\261\241\201\211\001"); +static const std::string TEST_VALID_1("\361\241\201\211\001"); +static const std::string TEST_VALID_2("\361\241\201\211"); +static const std::string TEST_INVALID_3("\361\241\201"); +static const std::string TEST_INVALID_4("\361\241\201\001"); +static const std::string TEST_INVALID_5("\361\241"); +static const std::string TEST_INVALID_6("\361\241\001"); +static const std::string TEST_INVALID_7("\361"); +static const std::string TEST_INVALID_8("\361\001"); +static const std::string TEST_INVALID_9("\355\244\200"); +static const std::string TEST_INVALID_10("\355\244\200\001"); +static const std::string TEST_INVALID_11("\340\214\200"); +static const std::string TEST_INVALID_12("\340\214\200\001"); + +BOOST_AUTO_TEST_SUITE(fc) + +BOOST_AUTO_TEST_CASE(utf8_test) +{ + std::wstring test(L"\0\001\002"); + test.reserve(65536); + for (wchar_t c = 0xffff; c >= 0xe000; c--) { + test.push_back(c); + } + for (wchar_t c = 0xd7ff; c > 2; c--) { + test.push_back(c); + } + for (wchar_t c = 1; c < 16; c++) { + test.push_back((c << 16) | 0xffff); + } + + std::string storage; + storage.reserve(257*1024); + fc::encodeUtf8(test, &storage); + BOOST_CHECK(fc::is_utf8(storage)); + + std::wstring decoded; + decoded.reserve(65536); + fc::decodeUtf8(storage, &decoded); + BOOST_CHECK(test.compare(decoded) == 0); + + BOOST_CHECK(fc::is_utf8(TEST_VALID_1)); + BOOST_CHECK(fc::is_utf8(TEST_VALID_2)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_1)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_2)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_3)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_4)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_5)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_6)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_7)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_8)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_9)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_10)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_11)); + BOOST_CHECK(!fc::is_utf8(TEST_INVALID_12)); + + decoded.clear(); + try { + fc::decodeUtf8(TEST_INVALID_1, &decoded); + BOOST_FAIL("expected invalid utf8 exception"); + } catch (const std::exception& e) { + BOOST_CHECK(!strncmp("Invalid UTF-8", e.what(), 14)); + } + try { + fc::decodeUtf8(TEST_INVALID_9, &decoded); + BOOST_FAIL("expected invalid code point exception"); + } catch (const std::exception& e) { + BOOST_CHECK(!strncmp("Invalid code point", e.what(), 19)); + } + try { + fc::decodeUtf8(TEST_INVALID_11, &decoded); + BOOST_FAIL("expected invalid utf8 exception"); + } catch (const std::exception& e) { + BOOST_CHECK(!strncmp("Invalid UTF-8", e.what(), 14)); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests b/unit_tests deleted file mode 100755 index 02227c356..000000000 Binary files a/unit_tests and /dev/null differ diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt deleted file mode 100644 index 16ac56b5a..000000000 --- a/vendor/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory( libssh2-1.4.2 ) -#add_subdirectory( zlib-1.2.7) -#add_subdirectory( sigar ) diff --git a/include/boost/process.hpp b/vendor/boost_1.51/include/boost/process.hpp similarity index 100% rename from include/boost/process.hpp rename to vendor/boost_1.51/include/boost/process.hpp diff --git a/include/boost/process/all.hpp b/vendor/boost_1.51/include/boost/process/all.hpp similarity index 100% rename from include/boost/process/all.hpp rename to vendor/boost_1.51/include/boost/process/all.hpp diff --git a/include/boost/process/child.hpp b/vendor/boost_1.51/include/boost/process/child.hpp similarity index 90% rename from include/boost/process/child.hpp rename to vendor/boost_1.51/include/boost/process/child.hpp index f0a632a66..1441e56dd 100644 --- a/include/boost/process/child.hpp +++ b/vendor/boost_1.51/include/boost/process/child.hpp @@ -79,6 +79,14 @@ class child : public process return (it != handles_.end()) ? it->second : handle(); } +#if defined(BOOST_WINDOWS_API) + handle::native_type get_os_handle() const + { + return handle_.native(); + } +#endif + + private: /** * Handles providing access to streams attached to the child process. diff --git a/include/boost/process/config.hpp b/vendor/boost_1.51/include/boost/process/config.hpp similarity index 100% rename from include/boost/process/config.hpp rename to vendor/boost_1.51/include/boost/process/config.hpp diff --git a/include/boost/process/context.hpp b/vendor/boost_1.51/include/boost/process/context.hpp similarity index 92% rename from include/boost/process/context.hpp rename to vendor/boost_1.51/include/boost/process/context.hpp index 6d4a8d0e5..1228d9e54 100644 --- a/include/boost/process/context.hpp +++ b/vendor/boost_1.51/include/boost/process/context.hpp @@ -82,6 +82,11 @@ struct context */ environment env; + /** + * Suppress creation of a console window on win32 (nop on other platforms) + */ + bool suppress_console; + /** * Constructs a process context. * @@ -91,7 +96,8 @@ struct context */ context() : work_dir(self::get_work_dir()), - env(self::get_environment()) + env(self::get_environment()), + suppress_console(false) { #if 0 // this default behavior will throw in non-console apps #if defined(BOOST_POSIX_API) diff --git a/include/boost/process/detail/basic_status.hpp b/vendor/boost_1.51/include/boost/process/detail/basic_status.hpp similarity index 100% rename from include/boost/process/detail/basic_status.hpp rename to vendor/boost_1.51/include/boost/process/detail/basic_status.hpp diff --git a/include/boost/process/detail/basic_status_service.hpp b/vendor/boost_1.51/include/boost/process/detail/basic_status_service.hpp similarity index 94% rename from include/boost/process/detail/basic_status_service.hpp rename to vendor/boost_1.51/include/boost/process/detail/basic_status_service.hpp index 97c8162dd..2fd1a391f 100644 --- a/include/boost/process/detail/basic_status_service.hpp +++ b/vendor/boost_1.51/include/boost/process/detail/basic_status_service.hpp @@ -152,6 +152,11 @@ class basic_status_service boost::unique_lock lock(work_thread_mutex_); if (++pids_ == 1) { + // if there was a previous worker thread that exited because + // pids_ dropped to 0, we must join it now to free the thread's + // memory before launching a new worker thread + if (work_thread_.joinable()) + work_thread_.join(); work_.reset(new boost::asio::io_service::work( this->get_io_service())); work_thread_ = boost::thread( diff --git a/include/boost/process/detail/posix_helpers.hpp b/vendor/boost_1.51/include/boost/process/detail/posix_helpers.hpp similarity index 100% rename from include/boost/process/detail/posix_helpers.hpp rename to vendor/boost_1.51/include/boost/process/detail/posix_helpers.hpp diff --git a/include/boost/process/detail/status_impl.hpp b/vendor/boost_1.51/include/boost/process/detail/status_impl.hpp similarity index 100% rename from include/boost/process/detail/status_impl.hpp rename to vendor/boost_1.51/include/boost/process/detail/status_impl.hpp diff --git a/include/boost/process/detail/systembuf.hpp b/vendor/boost_1.51/include/boost/process/detail/systembuf.hpp similarity index 100% rename from include/boost/process/detail/systembuf.hpp rename to vendor/boost_1.51/include/boost/process/detail/systembuf.hpp diff --git a/include/boost/process/detail/windows_helpers.hpp b/vendor/boost_1.51/include/boost/process/detail/windows_helpers.hpp similarity index 100% rename from include/boost/process/detail/windows_helpers.hpp rename to vendor/boost_1.51/include/boost/process/detail/windows_helpers.hpp diff --git a/include/boost/process/environment.hpp b/vendor/boost_1.51/include/boost/process/environment.hpp similarity index 100% rename from include/boost/process/environment.hpp rename to vendor/boost_1.51/include/boost/process/environment.hpp diff --git a/include/boost/process/handle.hpp b/vendor/boost_1.51/include/boost/process/handle.hpp similarity index 100% rename from include/boost/process/handle.hpp rename to vendor/boost_1.51/include/boost/process/handle.hpp diff --git a/include/boost/process/operations.hpp b/vendor/boost_1.51/include/boost/process/operations.hpp similarity index 92% rename from include/boost/process/operations.hpp rename to vendor/boost_1.51/include/boost/process/operations.hpp index 5fcdd0461..1d6da12a1 100644 --- a/include/boost/process/operations.hpp +++ b/vendor/boost_1.51/include/boost/process/operations.hpp @@ -239,8 +239,8 @@ inline child create_child(const std::string &executable, Arguments args, { if (chdir(work_dir) == -1) { - write(STDERR_FILENO, "chdir() failed\n", 15); - _exit(127); + auto r = write(STDERR_FILENO, "chdir() failed\n", 15); + if( r || !r ) _exit(127); } for (handles_t::iterator it = handles.begin(); it != handles.end(); @@ -258,7 +258,8 @@ inline child create_child(const std::string &executable, Arguments args, it->first + 1); if (fd == -1) { - write(STDERR_FILENO, "fcntl() failed\n", 15); + auto r = write(STDERR_FILENO, "chdir() failed\n", 15); + (void)r; _exit(127); } it2->second.child = fd; @@ -267,8 +268,8 @@ inline child create_child(const std::string &executable, Arguments args, if (dup2(it->second.child.native(), it->first) == -1) { - write(STDERR_FILENO, "dup2() failed\n", 14); - _exit(127); + auto r = write(STDERR_FILENO, "chdir() failed\n", 15); + if( r || !r ) _exit(127); } closeflags[it->first] = false; } @@ -287,7 +288,8 @@ inline child create_child(const std::string &executable, Arguments args, // Actually we should delete argv and envp data. As we must not // call any non-async-signal-safe functions though we simply exit. - write(STDERR_FILENO, "execve() failed\n", 16); + auto r = write(STDERR_FILENO, "execve() failed\n", 16); + if( r || !r ) _exit(127); _exit(127); } else @@ -344,7 +346,11 @@ inline child create_child(const std::string &executable, Arguments args, boost::shared_array envstrs = detail::environment_to_windows_strings(ctx.env); - if (CreateProcessA(exe.get(), cmdline.get(), NULL, NULL, TRUE, 0, + DWORD creation_flags = 0; + if (ctx.suppress_console) + creation_flags |= CREATE_NO_WINDOW; + + if (CreateProcessA(exe.get(), cmdline.get(), NULL, NULL, TRUE, creation_flags, envstrs.get(), workdir.get(), &startup_info, &pi) == 0) BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateProcess() failed"); diff --git a/include/boost/process/pid_type.hpp b/vendor/boost_1.51/include/boost/process/pid_type.hpp similarity index 100% rename from include/boost/process/pid_type.hpp rename to vendor/boost_1.51/include/boost/process/pid_type.hpp diff --git a/include/boost/process/pipe.hpp b/vendor/boost_1.51/include/boost/process/pipe.hpp similarity index 100% rename from include/boost/process/pipe.hpp rename to vendor/boost_1.51/include/boost/process/pipe.hpp diff --git a/include/boost/process/pistream.hpp b/vendor/boost_1.51/include/boost/process/pistream.hpp similarity index 100% rename from include/boost/process/pistream.hpp rename to vendor/boost_1.51/include/boost/process/pistream.hpp diff --git a/include/boost/process/postream.hpp b/vendor/boost_1.51/include/boost/process/postream.hpp similarity index 100% rename from include/boost/process/postream.hpp rename to vendor/boost_1.51/include/boost/process/postream.hpp diff --git a/include/boost/process/process.hpp b/vendor/boost_1.51/include/boost/process/process.hpp similarity index 96% rename from include/boost/process/process.hpp rename to vendor/boost_1.51/include/boost/process/process.hpp index 8858954c0..e0e2149cb 100644 --- a/include/boost/process/process.hpp +++ b/vendor/boost_1.51/include/boost/process/process.hpp @@ -198,11 +198,12 @@ class process BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed"); return h; } - +protected: /** * The process handle. */ handle handle_; +private: #endif }; diff --git a/include/boost/process/self.hpp b/vendor/boost_1.51/include/boost/process/self.hpp similarity index 100% rename from include/boost/process/self.hpp rename to vendor/boost_1.51/include/boost/process/self.hpp diff --git a/include/boost/process/status.hpp b/vendor/boost_1.51/include/boost/process/status.hpp similarity index 100% rename from include/boost/process/status.hpp rename to vendor/boost_1.51/include/boost/process/status.hpp diff --git a/include/boost/process/stream_behavior.hpp b/vendor/boost_1.51/include/boost/process/stream_behavior.hpp similarity index 100% rename from include/boost/process/stream_behavior.hpp rename to vendor/boost_1.51/include/boost/process/stream_behavior.hpp diff --git a/include/boost/process/stream_ends.hpp b/vendor/boost_1.51/include/boost/process/stream_ends.hpp similarity index 100% rename from include/boost/process/stream_ends.hpp rename to vendor/boost_1.51/include/boost/process/stream_ends.hpp diff --git a/include/boost/process/stream_id.hpp b/vendor/boost_1.51/include/boost/process/stream_id.hpp similarity index 100% rename from include/boost/process/stream_id.hpp rename to vendor/boost_1.51/include/boost/process/stream_id.hpp diff --git a/include/boost/process/stream_type.hpp b/vendor/boost_1.51/include/boost/process/stream_type.hpp similarity index 100% rename from include/boost/process/stream_type.hpp rename to vendor/boost_1.51/include/boost/process/stream_type.hpp diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_arm_aapcs_elf_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_arm_aapcs_elf_gas.S new file mode 100644 index 000000000..7724441c5 --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_arm_aapcs_elf_gas.S @@ -0,0 +1,101 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************************* + * * + * ------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * + * ------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| 0x20| 0x24| * + * ------------------------------------------------------------- * + * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | sp | lr | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 10 | | * + * ------------------------------------------------------------- * + * | 0x28| | * + * ------------------------------------------------------------- * + * | pc | | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 11 | 12 | | * + * ------------------------------------------------------------- * + * | 0x2c| 0x30| | * + * ------------------------------------------------------------- * + * |sbase|slimit| | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | * + * ------------------------------------------------------------- * + * | 0x34| 0x38|0x3c| 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58 | * + * ------------------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | s24 | s25 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 23 | 24 | 25 | 26 | 27 | 28 | | * + * ------------------------------------------------------------- * + * | 0x5c| 0x60| 0x64| 0x68| 0x6c| 0x70| | * + * ------------------------------------------------------------- * + * | s26 | s27 | s28 | s29 | s30 | s31 | | * + * ------------------------------------------------------------- * + * * + * *****************************************************************/ + +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,%function +jump_fcontext: + stmia a1, {v1-v8,sp-lr} @ save V1-V8,SP-LR + str lr, [a1,#40] @ save LR as PC + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + cmp a4, #0 @ test if fpu env should be preserved + beq 1f + + mov a4, a1 + add a4, #52 + fstmiax a4, {d8-d15} @ save S16-S31 + + mov a4, a2 + add a4, #52 + fldmiax a4, {d8-d15} @ restore S16-S31 +1: +#endif + + mov a1, a3 @ use third arg as return value after jump + @ and as first arg in context function + ldmia a2, {v1-v8,sp-pc} @ restore v1-V8,SP-PC +.size jump_fcontext,.-jump_fcontext + +.text +.globl make_fcontext +.align 2 +.type make_fcontext,%function +make_fcontext: + str a1, [a1,#0] @ save the address of passed context + str a2, [a1,#40] @ save address of the context function + ldr a2, [a1,#44] @ load the stack base + + push {a1,lr} @ save pointer to fcontext_t + mov a1, a2 @ stack pointer as arg for align_stack + bl align_stack@PLT @ align stack + mov a2, a1 @ begin of aligned stack + pop {a1,lr} @ restore pointer to fcontext_t + + str a2, [a1,#32] @ save the aligned stack base + + adr a2, finish @ address of finish; called after context function returns + str a2, [a1,#36] + + mov a1, #0 + bx lr + +finish: + mov a1, #0 @ exit code is zero + bl _exit@PLT @ exit application +.size make_fcontext,.-make_fcontext diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_i386_ms_pe_masm.asm b/vendor/boost_1.51/libs/context/asm/fcontext_i386_ms_pe_masm.asm new file mode 100644 index 000000000..0f4dd671b --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_i386_ms_pe_masm.asm @@ -0,0 +1,151 @@ + +; Copyright Oliver Kowalke 2009. +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) + +; -------------------------------------------------------------- +; | 0 | 1 | 2 | 3 | 4 | 5 | +; -------------------------------------------------------------- +; | 0h | 04h | 08h | 0ch | 010h | 014h | +; -------------------------------------------------------------- +; | EDI | ESI | EBX | EBP | ESP | EIP | +; -------------------------------------------------------------- +; -------------------------------------------------------------- +; | 6 | 7 | | +; -------------------------------------------------------------- +; | 018h | 01ch | | +; -------------------------------------------------------------- +; | ss_base | ss_limit| | +; -------------------------------------------------------------- +; -------------------------------------------------------------- +; | 8 | | +; -------------------------------------------------------------- +; | 020h | | +; -------------------------------------------------------------- +; |fc_execpt| | +; -------------------------------------------------------------- +; -------------------------------------------------------------- +; | 9 | | +; -------------------------------------------------------------- +; | 024h | | +; -------------------------------------------------------------- +; |fc_strage| | +; -------------------------------------------------------------- +; -------------------------------------------------------------- +; | 10 | 11 | | +; -------------------------------------------------------------- +; | 028h | 02ch | | +; -------------------------------------------------------------- +; | fc_mxcsr|fc_x87_cw| | +; -------------------------------------------------------------- + +.386 +.XMM +.model flat, c +_exit PROTO, value:SDWORD +align_stack PROTO, vp:DWORD +seh_fcontext PROTO, except:DWORD, frame:DWORD, context:DWORD, dispatch:DWORD +.code + +jump_fcontext PROC EXPORT + mov ecx, [esp+04h] ; load address of the first fcontext_t arg + mov [ecx], edi ; save EDI + mov [ecx+04h], esi ; save ESI + mov [ecx+08h], ebx ; save EBX + mov [ecx+0ch], ebp ; save EBP + + assume fs:nothing + mov edx, fs:[018h] ; load NT_TIB + assume fs:error + mov eax, [edx] ; load current SEH exception list + mov [ecx+020h], eax ; save current exception list + mov eax, [edx+04h] ; load current stack base + mov [ecx+018h], eax ; save current stack base + mov eax, [edx+08h] ; load current stack limit + mov [ecx+01ch], eax ; save current stack limit + mov eax, [edx+010h] ; load fiber local storage + mov [ecx+024h], eax ; save fiber local storage + + lea eax, [esp+04h] ; exclude the return address + mov [ecx+010h], eax ; save as stack pointer + mov eax, [esp] ; load return address + mov [ecx+014h], eax ; save return address + + mov edx, [esp+08h] ; load address of the second fcontext_t arg + mov edi, [edx] ; restore EDI + mov esi, [edx+04h] ; restore ESI + mov ebx, [edx+08h] ; restore EBX + mov ebp, [edx+0ch] ; restore EBP + + mov eax, [esp+010h] ; check if fpu enve preserving was requested + test eax, eax + je nxt + + stmxcsr [ecx+028h] ; save MMX control word + fnstcw [ecx+02ch] ; save x87 control word + ldmxcsr [edx+028h] ; restore MMX control word + fldcw [edx+02ch] ; restore x87 control word +nxt: + mov ecx, edx + assume fs:nothing + mov edx, fs:[018h] ; load NT_TIB + assume fs:error + mov eax, [ecx+020h] ; load SEH exception list + mov [edx], eax ; restore next SEH item + mov eax, [ecx+018h] ; load stack base + mov [edx+04h], eax ; restore stack base + mov eax, [ecx+01ch] ; load stack limit + mov [edx+08h], eax ; restore stack limit + mov eax, [ecx+024h] ; load fiber local storage + mov [edx+010h], eax ; restore fiber local storage + + mov eax, [esp+0ch] ; use third arg as return value after jump + + mov esp, [ecx+010h] ; restore ESP + mov [esp+04h], eax ; use third arg as first arg in context function + mov ecx, [ecx+014h] ; fetch the address to return to + + jmp ecx ; indirect jump to context +jump_fcontext ENDP + +make_fcontext PROC EXPORT + mov eax, [esp+04h] ; load address of the fcontext_t arg0 + mov [eax], eax ; save the address of passed context + mov ecx, [esp+08h] ; load the address of the context function + mov [eax+014h], ecx ; save the address of the context function + mov edx, [eax+018h] ; load the stack base + + push eax ; save pointer to fcontext_t + push edx ; stack pointer as arg for align_stack + call align_stack ; align stack + mov edx, eax ; begin of aligned stack + pop eax ; remove arg for align_stack + pop eax ; restore pointer to fcontext_t + + lea edx, [edx-014h] ; reserve space for last frame on stack, (ESP + 4) & 15 == 0 + mov [eax+010h], edx ; save the aligned stack + + mov ecx, seh_fcontext ; set ECX to exception-handler + mov [edx+0ch], ecx ; save ECX as SEH handler + mov ecx, 0ffffffffh ; set ECX to -1 + mov [edx+08h], ecx ; save ECX as next SEH item + lea ecx, [edx+08h] ; load address of next SEH item + mov [eax+02ch], ecx ; save next SEH + + stmxcsr [eax+028h] ; save MMX control word + fnstcw [eax+02ch] ; save x87 control word + + mov ecx, finish ; address of finish + mov [edx], ecx + + xor eax, eax + ret + +finish: + xor eax, eax + push eax ; exit code is zero + call _exit ; exit application + hlt +make_fcontext ENDP +END diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_i386_sysv_elf_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_i386_sysv_elf_gas.S new file mode 100644 index 000000000..7c94ea068 --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_i386_sysv_elf_gas.S @@ -0,0 +1,122 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************************** + * * + * -------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | * + * -------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | * + * -------------------------------------------------------------- * + * | EDI | ESI | EBX | EBP | ESP | EIP | * + * -------------------------------------------------------------- * + * -------------------------------------------------------------- * + * | 6 | 7 | | * + * -------------------------------------------------------------- * + * | 0x18 | 0x1c | | * + * -------------------------------------------------------------- * + * | sbase | slimit | | * + * -------------------------------------------------------------- * + * -------------------------------------------------------------- * + * | 8 | 9 | | * + * -------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * -------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| | * + * -------------------------------------------------------------- * + * * + * *****************************************************************/ + +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +jump_fcontext: + movl 0x4(%esp), %ecx /* load address of the first fcontext_t arg */ + movl %edi, (%ecx) /* save EDI */ + movl %esi, 0x4(%ecx) /* save ESI */ + movl %ebx, 0x8(%ecx) /* save EBX */ + movl %ebp, 0xc(%ecx) /* save EBP */ + + leal 0x4(%esp), %eax /* exclude the return address */ + movl %eax, 0x10(%ecx) /* save as stack pointer */ + movl (%esp), %eax /* load return address */ + movl %eax, 0x14(%ecx) /* save return address */ + + movl 0x8(%esp), %edx /* load address of the second fcontext_t arg */ + movl (%edx), %edi /* restore EDI */ + movl 0x4(%edx), %esi /* restore ESI */ + movl 0x8(%edx), %ebx /* restore EBX */ + movl 0xc(%edx), %ebp /* restore EBP */ + + movl 0x10(%esp), %eax /* check if fpu enve preserving was requested */ + test %eax, %eax + je 1f + + stmxcsr 0x20(%ecx) /* save MMX control and status word */ + fnstcw 0x24(%ecx) /* save x87 control word */ + ldmxcsr 0x20(%edx) /* restore MMX control and status word */ + fldcw 0x24(%edx) /* restore x87 control word */ +1: + movl 0xc(%esp), %eax /* use third arg as return value after jump */ + + movl 0x10(%edx), %esp /* restore ESP */ + movl %eax, 0x4(%esp) /* use third arg as first arg in context function */ + movl 0x14(%edx), %edx /* fetch the address to return to */ + + jmp *%edx /* indirect jump to context */ +.size jump_fcontext,.-jump_fcontext + +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +make_fcontext: + movl 0x4(%esp), %eax /* load address of the fcontext_t */ + movl %eax, (%eax) /* save the address of current context */ + movl 0x8(%esp), %ecx /* load the address of the context function */ + movl %ecx, 0x14(%eax) /* save the address of the context function */ + movl 0x18(%eax), %edx /* load the stack base */ + + pushl %eax /* save pointer to fcontext_t */ + pushl %ebx /* save EBX */ + pushl %edx /* stack pointer as arg for align_stack */ + call 1f +1: popl %ebx /* address of label 1 */ + addl $_GLOBAL_OFFSET_TABLE_+[.-1b], %ebx /* compute address of GOT and store it in EBX */ + call align_stack@PLT /* align stack */ + movl %eax, %edx /* begin of aligned stack */ + popl %eax /* remove arg for align_stack */ + popl %ebx /* restore EBX */ + popl %eax /* restore pointer to fcontext_t */ + + leal -0x14(%edx), %edx /* reserve space for the last frame on stack, (ESP + 4) % 16 == 0 */ + movl %edx, 0x10(%eax) /* save the aligned stack base */ + + stmxcsr 0x20(%eax) /* save MMX control and status word */ + fnstcw 0x24(%eax) /* save x87 control word */ + + call 2f +2: popl %ecx /* address of label 2 */ + addl $finish-2b, %ecx /* helper code executed after context function returns */ + movl %ecx, (%edx) + + xorl %eax, %eax + ret + +finish: + leal -0xc(%esp), %esp + + call 3f +3: popl %ebx /* address of label 3 */ + addl $_GLOBAL_OFFSET_TABLE_+[.-3b], %ebx /* compute address of GOT and store it in EBX */ + + xorl %eax, %eax + pushl %eax /* exit code is zero */ + call _exit@PLT /* exit application */ + hlt +.size make_fcontext,.-make_fcontext diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_i386_sysv_macho_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_i386_sysv_macho_gas.S new file mode 100644 index 000000000..a28c87576 --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_i386_sysv_macho_gas.S @@ -0,0 +1,118 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************************** + * * + * -------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | * + * -------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | * + * -------------------------------------------------------------- * + * | EDI | ESI | EBX | EBP | ESP | EIP | * + * -------------------------------------------------------------- * + * -------------------------------------------------------------- * + * | 6 | 7 | | * + * -------------------------------------------------------------- * + * | 0x18 | 0x1c | | * + * -------------------------------------------------------------- * + * | sbase | slimit | | * + * -------------------------------------------------------------- * + * -------------------------------------------------------------- * + * | 8 | 9 | | * + * -------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * -------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| | * + * -------------------------------------------------------------- * + * * + * *****************************************************************/ + +.text +.globl _jump_fcontext +.align 2 +_jump_fcontext: + movl 0x4(%esp), %ecx /* load address of the first fcontext_t arg */ + movl %edi, (%ecx) /* save EDI */ + movl %esi, 0x4(%ecx) /* save ESI */ + movl %ebx, 0x8(%ecx) /* save EBX */ + movl %ebp, 0xc(%ecx) /* save EBP */ + + leal 0x4(%esp), %eax /* exclude the return address */ + movl %eax, 0x10(%ecx) /* save as stack pointer */ + movl (%esp), %eax /* load return address */ + movl %eax, 0x14(%ecx) /* save return address */ + + movl 0x8(%esp), %edx /* load address of the second fcontext_t arg */ + movl (%edx), %edi /* restore EDI */ + movl 0x4(%edx), %esi /* restore ESI */ + movl 0x8(%edx), %ebx /* restore EBX */ + movl 0xc(%edx), %ebp /* restore EBP */ + + movl 0x10(%esp), %eax /* check if fpu enve preserving was requested */ + test %eax, %eax + je 1f + + stmxcsr 0x20(%ecx) /* save MMX control and status word */ + fnstcw 0x24(%ecx) /* save x87 control word */ + ldmxcsr 0x20(%edx) /* restore MMX control and status word */ + fldcw 0x24(%edx) /* restore x87 control word */ +1: + movl 0xc(%esp), %eax /* use third arg as return value after jump */ + + movl 0x10(%edx), %esp /* restore ESP */ + movl %eax, 0x4(%esp) /* use third arg as first arg in context function */ + movl 0x14(%edx), %edx /* fetch the address to return to */ + + jmp *%edx /* indirect jump to context */ + +.text +.globl _make_fcontext +.align 2 +_make_fcontext: + movl 0x4(%esp), %eax /* load address of the fcontext_t */ + movl %eax, (%eax) /* save the address of current context */ + movl 0x8(%esp), %ecx /* load the address of the context function */ + movl %ecx, 0x14(%eax) /* save the address of the context function */ + movl 0x18(%eax), %edx /* load the stack base */ + + pushl %eax /* save pointer to fcontext_t */ + pushl %ebx /* save EBX */ + pushl %edx /* stack pointer as arg for align_stack */ + call 1f +1: popl %ebx /* address of label 1 */ + addl $_GLOBAL_OFFSET_TABLE_+[.-1b], %ebx /* compute address of GOT and store it in EBX */ + call align_stack@PLT /* align stack */ + movl %eax, %edx /* begin of aligned stack */ + popl %eax /* remove arg for align_stack */ + popl %ebx /* restore EBX */ + popl %eax /* restore pointer to fcontext_t */ + + leal -0x14(%edx), %edx /* reserve space for the last frame on stack, (ESP + 4) % 16 == 0 */ + movl %edx, 0x10(%eax) /* save the aligned stack base */ + + stmxcsr 0x20(%eax) /* save MMX control and status word */ + fnstcw 0x24(%eax) /* save x87 control word */ + + call 2f +2: popl %ecx /* address of label 2 */ + addl $finish-2b, %ecx /* helper code executed after context function returns */ + movl %ecx, (%edx) + + xorl %eax, %eax + ret + +finish: + leal -0xc(%esp), %esp + + call 3f +3: popl %ebx /* address of label 3 */ + addl $_GLOBAL_OFFSET_TABLE_+[.-3b], %ebx /* compute address of GOT and store it in EBX */ + + xorl %eax, %eax + pushl %eax /* exit code is zero */ + call _exit@PLT /* exit application */ + hlt diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_mips32_o32_elf_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_mips32_o32_elf_gas.S new file mode 100644 index 000000000..be86f185e --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_mips32_o32_elf_gas.S @@ -0,0 +1,144 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************************* + * * + * ------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * + * ------------------------------------------------------------- * + * | 0 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | * + * ------------------------------------------------------------- * + * | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | GP | SP | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 10 | 11 | 12 | | * + * ------------------------------------------------------------- * + * | 80 | 88 | 96 | | * + * ------------------------------------------------------------- * + * | S8 | RA | PC | | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 13 | 14 | | * + * ------------------------------------------------------------- * + * | 104 | 112 | | * + * ------------------------------------------------------------- * + * |sbase|slimt| | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 15 | 16 | 17 | 18 | 19 | 20 | | * + * ------------------------------------------------------------- * + * | 120 | 128 | 136 | 144 | 152 | 160 | | * + * ------------------------------------------------------------- * + * | F20 | F22 | F24 | F26 | F28 | F30 | | * + * ------------------------------------------------------------- * + * * + * *****************************************************************/ + +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +.ent jump_fcontext +jump_fcontext: + sw $s0, ($a0) # save S0 + sw $s1, 8($a0) # save S1 + sw $s2, 16($a0) # save S2 + sw $s3, 24($a0) # save S3 + sw $s4, 32($a0) # save S4 + sw $s5, 40($a0) # save S5 + sw $s6, 48($a0) # save S6 + sw $s7, 56($a0) # save S7 + sw $gp, 64($a0) # save GP + sw $sp, 72($a0) # save SP + sw $s8, 80($a0) # save S8 + sw $ra, 88($a0) # save RA + sw $ra, 96($a0) # save RA as PC + +#if defined(__mips_hard_float) + beqz $a3, 1f # test if fpu env should be preserved + s.d $f20, 120($a0) # save F20 + s.d $f22, 128($a0) # save F22 + s.d $f24, 136($a0) # save F24 + s.d $f26, 144($a0) # save F26 + s.d $f28, 152($a0) # save F28 + s.d $f30, 160($a0) # save F30 + + l.d $f20, 120($a1) # restore F20 + l.d $f22, 128($a1) # restore F22 + l.d $f24, 136($a1) # restore F24 + l.d $f26, 144($a1) # restore F26 + l.d $f28, 152($a1) # restore F28 + l.d $f30, 160($a1) # restore F30 +1: +#endif + + lw $s0, ($a1) # restore S0 + lw $s1, 8($a1) # restore S1 + lw $s2, 16($a1) # restore S2 + lw $s3, 24($a1) # restore S3 + lw $s4, 32($a1) # restore S4 + lw $s5, 40($a1) # restore S5 + lw $s6, 48($a1) # restore S6 + lw $s7, 56($a1) # restore S7 + lw $gp, 64($a1) # restore GP + lw $sp, 72($a1) # restore SP + lw $s8, 80($a1) # restore S8 + lw $ra, 88($a1) # restore RA + move $a0, $s2 # restore void pointer as argument + + move $v0, $a2 # use third arg as return value after jump + move $a0, $a2 # use third arg as first arg in context function + + lw $t9, 96($a1) # load PC + jr $t9 # jump to context +.end jump_fcontext +.size jump_fcontext, .-jump_fcontext + +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +.ent make_fcontext +make_fcontext: +#ifdef __PIC__ +.set noreorder +.cpload $t9 +.set reorder +#endif + sw $a0, ($a0) # save the current context + sw $gp, 24($a0) # save global pointer + sw $a1, 96($a0) # save the address of the context function + lw $t0, 104($a0) # load the stack base + + sub $sp, $sp, 28 + sw $ra, 24($sp) + sw $a0, 20($sp) + move $a0, $t0 # stack pointer as arg for align_stack + lw $t9, %call16(align_stack)($gp) # align stack + jalr $t9 + nop + move $t0, $v0 # begin of aligned stack + lw $ra, 24($sp) + lw $a0, 20($sp) + addi $sp, $sp, 28 + + sub $t0, $t0, 16 # reserve 16 byte of argument space + sw $t0, 72($a0) # save the algned stack base + + la $t9, finish # helper code executed after context function returns + sw $t9, 88($a0) + + move $v0, $zero + jr $ra + +finish: + move $gp, $s3 # restore GP (global pointer) + move $a0, $zero # exit code is zero + lw $t9, %call16(_exit)($gp) # exit application + jalr $t9 +.end make_fcontext +.size make_fcontext, .-make_fcontext diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_ppc32_sysv_elf_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_ppc32_sysv_elf_gas.S new file mode 100644 index 000000000..69b6ed90b --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_ppc32_sysv_elf_gas.S @@ -0,0 +1,222 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************************* + * * + * ------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * + * ------------------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | * + * ------------------------------------------------------------- * + * | R13 | R14 | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | * + * ------------------------------------------------------------- * + * | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | * + * ------------------------------------------------------------- * + * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | SP | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 20 | 21 | 22 | | * + * ------------------------------------------------------------- * + * | 80 | 84 | 88 | | * + * ------------------------------------------------------------- * + * | CR | LR | PC | | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 23 | 24 | | * + * ------------------------------------------------------------- * + * | 92 | 96 | | * + * ------------------------------------------------------------- * + * |sbase|slimt| | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | * + * ------------------------------------------------------------- * + * | 100 | 104 | 108 | 112 | 116 | 120 | 124 | 128 | 132 | 136 | * + * ------------------------------------------------------------- * + * | F14 | F15 | F16 | F17 | F18 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | * + * ------------------------------------------------------------- * + * | 140 | 144 | 148 | 152 | 156 | 160 | 164 | 168 | 172 | 176 | * + * ------------------------------------------------------------- * + * | F19 | F20 | F21 | F22 | F23 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | * + * ------------------------------------------------------------- * + * | 180 | 184 | 188 | 192 | 196 | 200 | 204 | 208 | 212 | 216 | * + * ------------------------------------------------------------- * + * | F24 | F25 | F26 | F27 | F28 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | | * + * ------------------------------------------------------------- * + * | 220 | 224 | 228 | 232 | 236 | 240 | 244 | 248 | | * + * ------------------------------------------------------------- * + * | F29 | F30 | F31 | fpscr | | * + * ------------------------------------------------------------- * + * * + * *****************************************************************/ + +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +jump_fcontext: + stw %r13, 0(%r3) # save R13 + stw %r14, 4(%r3) # save R14 + stw %r15, 8(%r3) # save R15 + stw %r16, 12(%r3) # save R16 + stw %r17, 16(%r3) # save R17 + stw %r18, 20(%r3) # save R18 + stw %r19, 24(%r3) # save R19 + stw %r20, 28(%r3) # save R20 + stw %r21, 32(%r3) # save R21 + stw %r22, 36(%r3) # save R22 + stw %r23, 40(%r3) # save R23 + stw %r24, 44(%r3) # save R24 + stw %r25, 48(%r3) # save R25 + stw %r26, 52(%r3) # save R26 + stw %r27, 56(%r3) # save R27 + stw %r28, 60(%r3) # save R28 + stw %r29, 64(%r3) # save R29 + stw %r30, 68(%r3) # save R30 + stw %r31, 72(%r3) # save R31 + stw %r1, 76(%r3) # save SP + + mfcr %r0 # load CR + stw %r0, 80(%r3) # save CR + mflr %r0 # load LR + stw %r0, 84(%r3) # save LR + stw %r0, 88(%r3) # save LR as PC + + cmpwi cr7, %r6, 0 # test if fpu env should be preserved + beq cr7, 1f + + stfd %f14, 100(%r3) # save F14 + stfd %f15, 108(%r3) # save F15 + stfd %f16, 116(%r3) # save F16 + stfd %f17, 124(%r3) # save F17 + stfd %f18, 132(%r3) # save F18 + stfd %f19, 140(%r3) # save F19 + stfd %f20, 148(%r3) # save F20 + stfd %f21, 156(%r3) # save F21 + stfd %f22, 164(%r3) # save F22 + stfd %f23, 172(%r3) # save F23 + stfd %f24, 180(%r3) # save F24 + stfd %f25, 188(%r3) # save F25 + stfd %f26, 196(%r3) # save F26 + stfd %f27, 204(%r3) # save F27 + stfd %f28, 212(%r3) # save F28 + stfd %f29, 220(%r3) # save F29 + stfd %f30, 228(%r3) # save F30 + stfd %f31, 236(%r3) # save F31 + mffs %f0 # load FPSCR + stfd %f0, 244(%r3) # save FPSCR + + lfd %f14, 100(%r4) # restore F14 + lfd %f15, 108(%r4) # restore F15 + lfd %f16, 116(%r4) # restore F16 + lfd %f17, 124(%r4) # restore F17 + lfd %f18, 132(%r4) # restore F18 + lfd %f19, 140(%r4) # restore F19 + lfd %f20, 148(%r4) # restore F20 + lfd %f21, 156(%r4) # restore F21 + lfd %f22, 164(%r4) # restore F22 + lfd %f23, 172(%r4) # restore F23 + lfd %f24, 180(%r4) # restore F24 + lfd %f25, 188(%r4) # restore F25 + lfd %f26, 196(%r4) # restore F26 + lfd %f27, 204(%r4) # restore F27 + lfd %f28, 212(%r4) # restore F28 + lfd %f29, 220(%r4) # restore F29 + lfd %f30, 228(%r4) # restore F30 + lfd %f31, 236(%r4) # restore F31 + lfd %f0, 244(%r4) # load FPSCR + mtfsf 0xff, %f0 # restore FPSCR +1: + + lwz %r13, 0(%r4) # restore R13 + lwz %r14, 4(%r4) # restore R14 + lwz %r15, 8(%r4) # restore R15 + lwz %r16, 12(%r4) # restore R16 + lwz %r17, 16(%r4) # restore R17 + lwz %r18, 20(%r4) # restore R18 + lwz %r19, 24(%r4) # restore R19 + lwz %r20, 28(%r4) # restore R20 + lwz %r21, 32(%r4) # restore R21 + lwz %r22, 36(%r4) # restore R22 + lwz %r23, 40(%r4) # restore R23 + lwz %r24, 44(%r4) # restore R24 + lwz %r25, 48(%r4) # restore R25 + lwz %r26, 52(%r4) # restore R26 + lwz %r27, 56(%r4) # restore R27 + lwz %r28, 60(%r4) # restore R28 + lwz %r29, 64(%r4) # restore R29 + lwz %r30, 68(%r4) # restore R30 + lwz %r31, 72(%r4) # restore R31 + lwz %r1, 76(%r4) # restore SP + + lwz %r0, 80(%r4) # load CR + mtcr %r0 # restore CR + lwz %r0, 84(%r4) # load LR + mtlr %r0 # restore LR + + mr. %r3, %r5 # use third arg as return value after jump + # and as first arg in context function + + lwz %r0, 88(%r4) # load PC + mtctr %r0 # restore CTR + + bctr # jump to context +.size jump_fcontext, .-jump_fcontext + +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +make_fcontext: + stw %r3, 0(%r3) # save the current context + stw %r4, 88(%r3) # save the address of the context function + lwz %r0, 92(%r3) # load the stack base + + li %r4, 28 + subf %r1, %r4, %r1 # reserve space on stack + stw %r3, 24(%r1) # store pointer to fcontext_t on stack + mflr %r4 # load LR + stw %r4, 20(%r1) # store LR on stack + mr. %r3, %r0 # context stack as arg to align_stack + bl align_stack@plt # call align_stack + mr. %r0, %r3 # load result into R0 + lwz %r4, 20(%r1) # pop LR from stack + mtlr %r4 # restore LR + lwz %r3, 24(%r1) # pop pointer to fcontext_t from stack + addi %r1, %r1, 28 # release space on stack + + li %r4, 32 + subf %r0, %r4, %r0 # 32 bytes on stack for parameter area(== 8 registers) + stw %r0, 76(%r3) # save the aligned stack base + + mflr %r0 # load LR + bl 1f # jump to label 1 +1: + mflr %r4 # load LR + addi %r4, %r4, finish - 1b # address of finish; called after context function returns + mtlr %r0 # restore LR + stw %r4, 84(%r3) + + li %r3, 0 + blr + +finish: + li %r3, 0 # exit code is zero + bl _exit@plt # exit application +.size make_fcontext, .-make_fcontext diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_ppc64_sysv_elf_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_ppc64_sysv_elf_gas.S new file mode 100644 index 000000000..0fcb38735 --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_ppc64_sysv_elf_gas.S @@ -0,0 +1,250 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************************* + * * + * ------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * + * ------------------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | * + * ------------------------------------------------------------- * + * | R13 | R14 | R15 | R16 | R17 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | * + * ------------------------------------------------------------- * + * | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | * + * ------------------------------------------------------------- * + * | R18 | R19 | R20 | R21 | R22 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | * + * ------------------------------------------------------------- * + * | 80 | 84 | 88 | 92 | 96 | 100 | 104 | 108 | 112 | 116 | * + * ------------------------------------------------------------- * + * | R23 | R24 | R25 | R26 | R27 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------------------- * + * | 120 | 124 | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------------------- * + * | R28 | R29 | R30 | R31 | SP | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | | * + * ------------------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | | * + * ------------------------------------------------------------- * + * | CR | LR | PC | | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 46 | 47 | 48 | 49 | | * + * ------------------------------------------------------------- * + * | 184 | 188 | 192 | 196 | | * + * ------------------------------------------------------------- * + * | sbase | slimt | | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | * + * ------------------------------------------------------------- * + * | 200 | 204 | 208 | 212 | 216 | 220 | 224 | 228 | 232 | 236 | * + * ------------------------------------------------------------- * + * | F14 | F15 | F16 | F17 | F18 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | * + * ------------------------------------------------------------- * + * | 240 | 244 | 248 | 252 | 256 | 260 | 264 | 268 | 272 | 276 | * + * ------------------------------------------------------------- * + * | F19 | F20 | F21 | F22 | F23 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * + * ------------------------------------------------------------- * + * | 280 | 284 | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * + * ------------------------------------------------------------- * + * | F24 | F25 | F26 | F27 | F28 | * + * ------------------------------------------------------------- * + * ------------------------------------------------------------- * + * | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | | * + * ------------------------------------------------------------- * + * | 320 | 324 | 328 | 332 | 336 | 340 | 344 | 348 | | * + * ------------------------------------------------------------- * + * | F29 | F30 | F31 | fpscr | | * + * ------------------------------------------------------------- * + * * + * *****************************************************************/ + +.section ".text" +.align 2 +.globl jump_fcontext +.section ".opd","aw" +.align 3 +jump_fcontext: +.quad .jump_fcontext,.TOC.@tocbase,0 +.previous +.size jump_fcontext,24 +.type .jump_fcontext,@function +.globl .jump_fcontext +.jump_fcontext: + std %r13, 0(%r3) # save R13 + std %r14, 8(%r3) # save R14 + std %r15, 16(%r3) # save R15 + std %r16, 24(%r3) # save R16 + std %r17, 32(%r3) # save R17 + std %r18, 40(%r3) # save R18 + std %r19, 48(%r3) # save R19 + std %r20, 56(%r3) # save R20 + std %r21, 64(%r3) # save R21 + std %r22, 72(%r3) # save R22 + std %r23, 80(%r3) # save R23 + std %r24, 88(%r3) # save R24 + std %r25, 96(%r3) # save R25 + std %r26, 104(%r3) # save R26 + std %r27, 112(%r3) # save R27 + std %r28, 120(%r3) # save R28 + std %r29, 128(%r3) # save R29 + std %r30, 136(%r3) # save R30 + std %r31, 144(%r3) # save R31 + std %r1, 152(%r3) # save SP + + mfcr %r0 # load CR + std %r0, 160(%r3) # save CR + mflr %r0 # load LR + std %r0, 168(%r3) # save LR + std %r0, 176(%r3) # save LR as PC + + cmpwi cr7, %r6, 0 # test if fpu env should be preserved + beq cr7, 1f + + stfd %f14, 200(%r3) # save F14 + stfd %f15, 208(%r3) # save F15 + stfd %f16, 216(%r3) # save F16 + stfd %f17, 224(%r3) # save F17 + stfd %f18, 232(%r3) # save F18 + stfd %f19, 240(%r3) # save F19 + stfd %f20, 248(%r3) # save F20 + stfd %f21, 256(%r3) # save F21 + stfd %f22, 264(%r3) # save F22 + stfd %f23, 272(%r3) # save F23 + stfd %f24, 280(%r3) # save F24 + stfd %f25, 288(%r3) # save F25 + stfd %f26, 296(%r3) # save F26 + stfd %f27, 304(%r3) # save F27 + stfd %f28, 312(%r3) # save F28 + stfd %f29, 320(%r3) # save F29 + stfd %f30, 328(%r3) # save F30 + stfd %f31, 336(%r3) # save F31 + mffs %f0 # load FPSCR + stfd %f0, 344(%r3) # save FPSCR + + lfd %f14, 200(%r4) # restore F14 + lfd %f15, 208(%r4) # restore F15 + lfd %f16, 216(%r4) # restore F16 + lfd %f17, 224(%r4) # restore F17 + lfd %f18, 232(%r4) # restore F18 + lfd %f19, 240(%r4) # restore F19 + lfd %f20, 248(%r4) # restore F20 + lfd %f21, 256(%r4) # restore F21 + lfd %f22, 264(%r4) # restore F22 + lfd %f23, 272(%r4) # restore F23 + lfd %f24, 280(%r4) # restore F24 + lfd %f25, 288(%r4) # restore F25 + lfd %f26, 296(%r4) # restore F26 + lfd %f27, 304(%r4) # restore F27 + lfd %f28, 312(%r4) # restore F28 + lfd %f29, 320(%r4) # restore F29 + lfd %f30, 328(%r4) # restore F30 + lfd %f31, 336(%r4) # restore F31 + lfd %f0, 344(%r4) # load FPSCR + mtfsf 0xff, %f0 # restore FPSCR +1: + + ld %r13, 0(%r4) # restore R13 + ld %r14, 8(%r4) # restore R14 + ld %r15, 16(%r4) # restore R15 + ld %r16, 24(%r4) # restore R16 + ld %r17, 32(%r4) # restore R17 + ld %r18, 40(%r4) # restore R18 + ld %r19, 48(%r4) # restore R19 + ld %r20, 56(%r4) # restore R20 + ld %r21, 64(%r4) # restore R21 + ld %r22, 72(%r4) # restore R22 + ld %r23, 80(%r4) # restore R23 + ld %r24, 88(%r4) # restore R24 + ld %r25, 96(%r4) # restore R25 + ld %r26, 104(%r4) # restore R26 + ld %r27, 112(%r4) # restore R27 + ld %r28, 120(%r4) # restore R28 + ld %r29, 128(%r4) # restore R29 + ld %r30, 136(%r4) # restore r30 + ld %r31, 144(%r4) # restore r31 + ld %r1, 152(%r4) # restore SP + + ld %r0, 160(%r4) # load CR + mtcr %r0 # restore CR + ld %r0, 168(%r4) # load LR + mtlr %r0 # restore LR + + mr. %r3, %r5 # use third arg as return value after jump + # and as first arg in context function + + ld %r0, 176(%r4) # load PC + mtctr %r0 # restore CTR + + bctr # jump to context +.size .jump_fcontext, .-.jump_fcontext + +.section ".text" +.align 2 +.globl make_fcontext +.section ".opd","aw" +.align 3 +make_fcontext: +.quad .make_fcontext,.TOC.@tocbase,0 +.previous +.size make_fcontext,24 +.type .make_fcontext,@function +.globl .make_fcontext +.make_fcontext: + std %r3, 0(%r3) # save the current context + std %r4, 176(%r3) # save the address of the function supposed to be run + ld %r0, 184(%r3) # load the stack base + + li %r4, 56 + subf %r1, %r4, %r1 # reserve space on stack + stw %r3, 48(%r1) # store pointer to fcontext_t on stack + mflr %r4 # load LR + stw %r4, 40(%r1) # store LR on stack + mr. %r3, %r0 # context stack as arg to align_stack + bl align_stack@plt # call align_stack + mr. %r0, %r3 # load result into R0 + lwz %r4, 40(%r1) # pop LR from stack + mtlr %r4 # restore LR + lwz %r3, 48(%r1) # pop pointer to fcontext_t from stack + addi %r1, %r1, 56 # release space on stack + + li %r4, 64 + subf %r0, %r4, %r0 # 64 bytes on stack for parameter area (== 8 registers) + std %r0, 152(%r3) # save the stack base + + mflr %r0 # load LR + bl 1f # jump to label 1 +1: + mflr %r4 # load LR + addi %r4, %r4, finish - 1b # calulate absolute address of finish + mtlr %r0 # restore LR + std %r4, 168(%r3) # save address of finish + + li %r3, 0 # set return value to zero + blr + +finish: + li %r3, 0 # set return value to zero + bl _exit@plt # exit application +.size .make_fcontext, .-.make_fcontext diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_ms_pe_masm.asm b/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_ms_pe_masm.asm new file mode 100644 index 000000000..f1f8ca12a --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_ms_pe_masm.asm @@ -0,0 +1,207 @@ + +; Copyright Oliver Kowalke 2009. +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) + +; ---------------------------------------------------------------------------------- +; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +; ---------------------------------------------------------------------------------- +; | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | +; ---------------------------------------------------------------------------------- +; | R12 | R13 | R14 | R15 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | +; ---------------------------------------------------------------------------------- +; | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | +; ---------------------------------------------------------------------------------- +; | RDI | RSI | RBX | RBP | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 16 | 17 | 18 | 19 | | +; ---------------------------------------------------------------------------------- +; | 0x40 | 0x44 | 0x48 | 0x4c | | +; ---------------------------------------------------------------------------------- +; | RSP | RIP | | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 20 | 21 | 22 | 23 | | +; ---------------------------------------------------------------------------------- +; | 0x50 | 0x54 | 0x58 | 0x5c | | +; ---------------------------------------------------------------------------------- +; | sbase | slimit | | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 24 | 25 | | +; ---------------------------------------------------------------------------------- +; | 0x60 | 0x64 | | +; ---------------------------------------------------------------------------------- +; | fbr_strg | | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 26 | 27 | 28 | 29 | | +; ---------------------------------------------------------------------------------- +; | 0x68 | 0x6c | 0x70 | 0x74 | | +; ---------------------------------------------------------------------------------- +; | fc_mxcsr|fc_x87_cw| fc_xmm | | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | +; ---------------------------------------------------------------------------------- +; | 0x78 | 0x7c | 0x80 | 0x84 | 0x88 | 0x8c | 0x90 | 0x94 | +; ---------------------------------------------------------------------------------- +; | XMM6 | XMM7 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | +; ---------------------------------------------------------------------------------- +; | 0x98 | 0x9c | 0x100 | 0x104 | 0x108 | 0x10c | 0x110 | 0x114 | +; ---------------------------------------------------------------------------------- +; | XMM8 | XMM9 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | +; ---------------------------------------------------------------------------------- +; | 0x118 | 0x11c | 0x120 | 0x124 | 0x128 | 0x12c | 0x130 | 0x134 | +; ---------------------------------------------------------------------------------- +; | XMM10 | XMM11 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | +; ---------------------------------------------------------------------------------- +; | 0x138 | 0x13c | 0x140 | 0x144 | 0x148 | 0x14c | 0x150 | 0x154 | +; ---------------------------------------------------------------------------------- +; | XMM12 | XMM13 | +; ---------------------------------------------------------------------------------- +; ---------------------------------------------------------------------------------- +; | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | +; ---------------------------------------------------------------------------------- +; | 0x158 | 0x15c | 0x160 | 0x164 | 0x168 | 0x16c | 0x170 | 0x174 | +; ---------------------------------------------------------------------------------- +; | XMM14 | XMM15 | +; ---------------------------------------------------------------------------------- + +EXTERN _exit:PROC ; standard C library function +EXTERN align_stack:PROC ; stack alignment +EXTERN seh_fcontext:PROC ; exception handler +.code + +jump_fcontext PROC EXPORT FRAME:seh_fcontext + .endprolog + + mov [rcx], r12 ; save R12 + mov [rcx+08h], r13 ; save R13 + mov [rcx+010h], r14 ; save R14 + mov [rcx+018h], r15 ; save R15 + mov [rcx+020h], rdi ; save RDI + mov [rcx+028h], rsi ; save RSI + mov [rcx+030h], rbx ; save RBX + mov [rcx+038h], rbp ; save RBP + + mov r10, gs:[030h] ; load NT_TIB + mov rax, [r10+08h] ; load current stack base + mov [rcx+050h], rax ; save current stack base + mov rax, [r10+010h] ; load current stack limit + mov [rcx+058h], rax ; save current stack limit + mov rax, [r10+018h] ; load fiber local storage + mov [rcx+060h], rax ; save fiber local storage + + test r9, r9 + je nxt + + stmxcsr [rcx+068h] ; save MMX control and status word + fnstcw [rcx+06ch] ; save x87 control word + mov r10, [rcx+070h] ; address of aligned XMM storage + movaps [r10], xmm6 + movaps [r10+010h], xmm7 + movaps [r10+020h], xmm8 + movaps [r10+030h], xmm9 + movaps [r10+040h], xmm10 + movaps [r10+050h], xmm11 + movaps [r10+060h], xmm12 + movaps [r10+070h], xmm13 + movaps [r10+080h], xmm14 + movaps [r10+090h], xmm15 + + ldmxcsr [rdx+068h] ; restore MMX control and status word + fldcw [rdx+06ch] ; restore x87 control word + mov r10, [rdx+070h] ; address of aligned XMM storage + movaps xmm6, [r10] + movaps xmm7, [r10+010h] + movaps xmm8, [r10+020h] + movaps xmm9, [r10+030h] + movaps xmm10, [r10+040h] + movaps xmm11, [r10+050h] + movaps xmm12, [r10+060h] + movaps xmm13, [r10+070h] + movaps xmm14, [r10+080h] + movaps xmm15, [r10+090h] +nxt: + + lea rax, [rsp+08h] ; exclude the return address + mov [rcx+040h], rax ; save as stack pointer + mov rax, [rsp] ; load return address + mov [rcx+048h], rax ; save return address + + mov r12, [rdx] ; restore R12 + mov r13, [rdx+08h] ; restore R13 + mov r14, [rdx+010h] ; restore R14 + mov r15, [rdx+018h] ; restore R15 + mov rdi, [rdx+020h] ; restore RDI + mov rsi, [rdx+028h] ; restore RSI + mov rbx, [rdx+030h] ; restore RBX + mov rbp, [rdx+038h] ; restore RBP + + mov r10, gs:[030h] ; load NT_TIB + mov rax, [rdx+050h] ; load stack base + mov [r10+08h], rax ; restore stack base + mov rax, [rdx+058h] ; load stack limit + mov [r10+010h], rax ; restore stack limit + mov rax, [rdx+060h] ; load fiber local storage + mov [r10+018h], rax ; restore fiber local storage + + mov rsp, [rdx+040h] ; restore RSP + mov r10, [rdx+048h] ; fetch the address to returned to + + mov rax, r8 ; use third arg as return value after jump + mov rcx, r8 ; use third arg as first arg in context function + + jmp r10 ; indirect jump to caller +jump_fcontext ENDP + +make_fcontext PROC EXPORT FRAME ; generate function table entry in .pdata and unwind information in E + .endprolog ; .xdata for a function's structured exception handling unwind behavior + + mov [rcx], rcx ; store the address of current context + mov [rcx+048h], rdx ; save the address of the function supposed to run + mov rdx, [rcx+050h] ; load the address where the context stack beginns + + push rcx ; save pointer to fcontext_t + sub rsp, 028h ; reserve shadow space for align_stack + mov rcx, rdx ; stack pointer as arg for align_stack + mov [rsp+8], rcx + call align_stack ; align stack + mov rdx, rax ; begin of aligned stack + add rsp, 028h + pop rcx ; restore pointer to fcontext_t + + lea rdx, [rdx-028h] ; reserve 32byte shadow space + return address on stack, (RSP + 8) % 16 == 0 + mov [rcx+040h], rdx ; save the address where the context stack beginns + + stmxcsr [rcx+068h] ; save MMX control and status word + fnstcw [rcx+06ch] ; save x87 control word + + lea rax, finish ; helper code executed after fn() returns + mov [rdx], rax ; store address off the helper function as return address + + xor rax, rax ; set RAX to zero + ret + +finish: + xor rcx, rcx + mov [rsp+08h], rcx + call _exit ; exit application + hlt +make_fcontext ENDP +END diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_sysv_elf_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_sysv_elf_gas.S new file mode 100644 index 000000000..ad2d42b46 --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_sysv_elf_gas.S @@ -0,0 +1,116 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | RBX | R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBP | RSP | RIP | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 16 | 17 | 18 | 19 | | * + * ---------------------------------------------------------------------------------- * + * | 0x40 | 0x44 | 0x48 | 0x4c | | * + * ---------------------------------------------------------------------------------- * + * | sbase | slimit | | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 20 | 21 | | * + * ---------------------------------------------------------------------------------- * + * | 0x50 | 0x54 | | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| | * + * ---------------------------------------------------------------------------------- * + * * + * **************************************************************************************/ + +.text +.globl jump_fcontext +.type jump_fcontext,@function +.align 16 +jump_fcontext: + movq %rbx, (%rdi) /* save RBX */ + movq %r12, 0x8(%rdi) /* save R12 */ + movq %r13, 0x10(%rdi) /* save R13 */ + movq %r14, 0x18(%rdi) /* save R14 */ + movq %r15, 0x20(%rdi) /* save R15 */ + movq %rbp, 0x28(%rdi) /* save RBP */ + + cmp $0, %rcx + je 1f + + stmxcsr 0x50(%rdi) /* save MMX control and status word */ + fnstcw 0x54(%rdi) /* save x87 control word */ + + ldmxcsr 0x50(%rsi) /* restore MMX control and status word */ + fldcw 0x54(%rsi) /* restore x87 control word */ +1: + + leaq 0x8(%rsp), %rax /* exclude the return address and save as stack pointer */ + movq %rax, 0x30(%rdi) /* save as stack pointer */ + movq (%rsp), %rax /* save return address */ + movq %rax, 0x38(%rdi) /* save return address as RIP */ + + movq (%rsi), %rbx /* restore RBX */ + movq 0x8(%rsi), %r12 /* restore R12 */ + movq 0x10(%rsi), %r13 /* restore R13 */ + movq 0x18(%rsi), %r14 /* restore R14 */ + movq 0x20(%rsi), %r15 /* restore R15 */ + movq 0x28(%rsi), %rbp /* restore RBP */ + + movq 0x30(%rsi), %rsp /* restore RSP */ + movq 0x38(%rsi), %rcx /* fetch the address to return to */ + + movq %rdx, %rax /* use third arg as return value after jump */ + movq %rdx, %rdi /* use third arg as first arg in context function */ + + jmp *%rcx /* indirect jump to context */ +.size jump_fcontext,.-jump_fcontext + +.text +.globl make_fcontext +.type make_fcontext,@function +.align 16 +make_fcontext: + movq %rdi, (%rdi) /* save the address of passed context */ + movq %rsi, 0x38(%rdi) /* save the address of the context function */ + movq 0x40(%rdi), %rdx /* load the stack base */ + + pushq %rdi /* save pointer to fcontext_t */ + movq %rdx, %rdi /* stack pointer as arg for align_stack */ + call align_stack@PLT /* align stack */ + movq %rax, %rdx /* begin of aligned stack */ + popq %rdi /* restore pointer to fcontext_t */ + + leaq -0x8(%rdx), %rdx /* reserve space for the last frame on stack, (RSP + 8) & 15 == 0 */ + movq %rdx, 0x30(%rdi) /* save the algined stack base */ + + stmxcsr 0x50(%rdi) /* save MMX control and status word */ + fnstcw 0x54(%rdi) /* save x87 control word */ + + leaq finish(%rip), %rcx /* address of finish; called after context function returns */ + movq %rcx, (%rdx) + + xorq %rax, %rax + ret + +finish: + xorq %rdi, %rdi /* exit code is zero */ + call _exit@PLT /* exit application */ + hlt +.size make_fcontext,.-make_fcontext + diff --git a/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_sysv_macho_gas.S b/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_sysv_macho_gas.S new file mode 100644 index 000000000..eea76e455 --- /dev/null +++ b/vendor/boost_1.51/libs/context/asm/fcontext_x86_64_sysv_macho_gas.S @@ -0,0 +1,111 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | RBX | R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBP | RSP | RIP | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 16 | 17 | 18 | 19 | | * + * ---------------------------------------------------------------------------------- * + * | 0x40 | 0x44 | 0x48 | 0x4c | | * + * ---------------------------------------------------------------------------------- * + * | sbase | slimit | | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 20 | 21 | | * + * ---------------------------------------------------------------------------------- * + * | 0x50 | 0x54 | | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| | * + * ---------------------------------------------------------------------------------- * + * * + * **************************************************************************************/ + +.text +.globl _jump_fcontext +.align 8 +_jump_fcontext: + movq %rbx, (%rdi) /* save RBX */ + movq %r12, 0x8(%rdi) /* save R12 */ + movq %r13, 0x10(%rdi) /* save R13 */ + movq %r14, 0x18(%rdi) /* save R14 */ + movq %r15, 0x20(%rdi) /* save R15 */ + movq %rbp, 0x28(%rdi) /* save RBP */ + + cmp $0, %rcx + je 1f + + stmxcsr 0x50(%rdi) /* save MMX control and status word */ + fnstcw 0x54(%rdi) /* save x87 control word */ + + ldmxcsr 0x50(%rsi) /* restore MMX control and status word */ + fldcw 0x54(%rsi) /* restore x87 control word */ +1: + + leaq 0x8(%rsp), %rax /* exclude the return address and save as stack pointer */ + movq %rax, 0x30(%rdi) /* save as stack pointer */ + movq (%rsp), %rax /* save return address */ + movq %rax, 0x38(%rdi) /* save return address as RIP */ + + movq (%rsi), %rbx /* restore RBX */ + movq 0x8(%rsi), %r12 /* restore R12 */ + movq 0x10(%rsi), %r13 /* restore R13 */ + movq 0x18(%rsi), %r14 /* restore R14 */ + movq 0x20(%rsi), %r15 /* restore R15 */ + movq 0x28(%rsi), %rbp /* restore RBP */ + + movq 0x30(%rsi), %rsp /* restore RSP */ + movq 0x38(%rsi), %rcx /* fetch the address to return to */ + + movq %rdx, %rax /* use third arg as return value after jump */ + movq %rdx, %rdi /* use third arg as first arg in context function */ + + jmp *%rcx /* indirect jump to context */ + +.text +.globl _make_fcontext +.align 8 +_make_fcontext: + movq %rdi, (%rdi) /* save the address of current context */ + movq %rsi, 0x38(%rdi) /* save the address of the function supposed to run */ + movq 0x40(%rdi), %rdx /* load the stack base */ + + pushq %rdi /* save pointer to fcontext_t */ + movq %rdx, %rdi /* stack pointer as arg for align_stack */ + call _align_stack /* align stack */ + movq %rax, %rdx /* begin of aligned stack */ + popq %rdi /* restore pointer to fcontext_t */ + + leaq -0x8(%rdx), %rdx /* reserve space for the last frame on stack, (RSP + 8) % 16 == 0 */ + movq %rdx, 0x30(%rdi) /* save the address */ + + stmxcsr 0x50(%rdi) /* save MMX control and status word */ + fnstcw 0x54(%rdi) /* save x87 control word */ + + leaq finish(%rip), %rcx /* helper code executed after context function returns */ + movq %rcx, (%rdx) + + xorq %rax, %rax /* set RAX to zero */ + ret + +finish: + xorq %rdi, %rdi /* exit code is zero */ + call _exit /* exit application */ + hlt diff --git a/vendor/boost_1.51/libs/context/fcontext.cpp b/vendor/boost_1.51/libs/context/fcontext.cpp new file mode 100644 index 000000000..596cbd8e8 --- /dev/null +++ b/vendor/boost_1.51/libs/context/fcontext.cpp @@ -0,0 +1,36 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_CONTEXT_SOURCE + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace ctx { +namespace detail { + +extern "C" BOOST_CONTEXT_DECL +void * BOOST_CONTEXT_CALLDECL align_stack( void * vp) +{ + void * base = vp; + if ( 0 != ( ( ( uintptr_t) base) & 15) ) + base = ( char * ) ( ( ( ( uintptr_t) base) - 15) & ~0x0F); + return base; +} + +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/vendor/boost_1.51/libs/context/seh.cpp b/vendor/boost_1.51/libs/context/seh.cpp new file mode 100644 index 000000000..9363805cf --- /dev/null +++ b/vendor/boost_1.51/libs/context/seh.cpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_CONTEXT_SOURCE + +extern "C" { + +#include +#include + +#include +#include +#include + +#if defined(_MSC_VER) +# define SNPRINTF _snprintf +#else +# define SNPRINTF snprintf +#endif + +static const char * exception_description( + _EXCEPTION_RECORD const* record, char * description, size_t len) +{ + const DWORD code = record->ExceptionCode; + const ULONG_PTR * info = record->ExceptionInformation; + + switch ( code) + { + case EXCEPTION_ACCESS_VIOLATION: + { + const char * accessType = ( info[0]) ? "writing" : "reading"; + const ULONG_PTR address = info[1]; + SNPRINTF( description, len, "Access violation %s %p", accessType, reinterpret_cast< void * >( address) ); + return description; + } + case EXCEPTION_DATATYPE_MISALIGNMENT: return "Datatype misalignment"; + case EXCEPTION_BREAKPOINT: return "Breakpoint"; + case EXCEPTION_SINGLE_STEP: return "Single step"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array bounds exceeded"; + case EXCEPTION_FLT_DENORMAL_OPERAND: return "FPU denormal operand"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FPU divide by zero"; + case EXCEPTION_FLT_INEXACT_RESULT: return "FPU inexact result"; + case EXCEPTION_FLT_INVALID_OPERATION: return "FPU invalid operation"; + case EXCEPTION_FLT_OVERFLOW: return "FPU overflow"; + case EXCEPTION_FLT_STACK_CHECK: return "FPU stack check"; + case EXCEPTION_FLT_UNDERFLOW: return "FPU underflow"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Integer divide by zero"; + case EXCEPTION_INT_OVERFLOW: return "Integer overflow"; + case EXCEPTION_PRIV_INSTRUCTION: return "Privileged instruction"; + case EXCEPTION_IN_PAGE_ERROR: return "In page error"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "Illegal instruction"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable exception"; + case EXCEPTION_STACK_OVERFLOW: return "Stack overflow"; + case EXCEPTION_INVALID_DISPOSITION: return "Invalid disposition"; + case EXCEPTION_GUARD_PAGE: return "Guard page"; + case EXCEPTION_INVALID_HANDLE: return "Invalid handle"; + } + + SNPRINTF( description, len, "Unknown (0x%08lX)", code); + return description; +} + +EXCEPTION_DISPOSITION seh_fcontext( + struct _EXCEPTION_RECORD * record, + void *, + struct _CONTEXT *, + void *) +{ + char description[255]; + + fprintf( stderr, "exception: %s (%08lX)\n", + exception_description( record, description, sizeof( description) ), + record->ExceptionCode); + + ExitProcess( -1); + + return ExceptionContinueSearch; // never reached +} + +} diff --git a/vendor/boost_1.51/libs/context/stack_allocator_posix.cpp b/vendor/boost_1.51/libs/context/stack_allocator_posix.cpp new file mode 100644 index 000000000..64291e73f --- /dev/null +++ b/vendor/boost_1.51/libs/context/stack_allocator_posix.cpp @@ -0,0 +1,85 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_CONTEXT_SOURCE + +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +#include + +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace ctx { + +void * +stack_allocator::allocate( std::size_t size) const +{ + if ( minimum_stacksize() > size) + throw std::invalid_argument( + boost::str( boost::format("invalid stack size: must be at least %d bytes") + % minimum_stacksize() ) ); + + if ( ! is_stack_unbound() && ( maximum_stacksize() < size) ) + throw std::invalid_argument( + boost::str( boost::format("invalid stack size: must not be larger than %d bytes") + % maximum_stacksize() ) ); + + const std::size_t pages( page_count( size) + 1); // add +1 for guard page + std::size_t size_ = pages * pagesize(); + + +# if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + void * limit = ::mmap( 0, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +# else + const int fd( ::open("/dev/zero", O_RDONLY) ); + BOOST_ASSERT( -1 != fd); + void * limit = ::mmap( 0, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + ::close( fd); +# endif + if ( ! limit) throw std::bad_alloc(); + + const int result( ::mprotect( limit, pagesize(), PROT_NONE) ); + BOOST_ASSERT( 0 == result); + (void)result; // unused in release build + + return static_cast< char * >( limit) + size_; +} + +void +stack_allocator::deallocate( void * vp, std::size_t size) const +{ + if ( vp) + { + const std::size_t pages( page_count( size) + 1); // add +1 for guard page + std::size_t size_ = pages * pagesize(); + BOOST_ASSERT( 0 < size && 0 < size_); + void * limit = static_cast< char * >( vp) - size_; + ::munmap( limit, size_); + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/vendor/boost_1.51/libs/context/stack_allocator_windows.cpp b/vendor/boost_1.51/libs/context/stack_allocator_windows.cpp new file mode 100644 index 000000000..518239f0e --- /dev/null +++ b/vendor/boost_1.51/libs/context/stack_allocator_windows.cpp @@ -0,0 +1,86 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_CONTEXT_SOURCE + +#include + +extern "C" { +#include +} + +#include + +#include +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4244 4267) +# endif + +namespace boost { +namespace ctx { + +void * +stack_allocator::allocate( std::size_t size) const +{ + if ( minimum_stacksize() > size) + throw std::invalid_argument( + boost::str( boost::format("invalid stack size: must be at least %d bytes") + % minimum_stacksize() ) ); + + if ( ! is_stack_unbound() && ( maximum_stacksize() < size) ) + throw std::invalid_argument( + boost::str( boost::format("invalid stack size: must not be larger than %d bytes") + % maximum_stacksize() ) ); + + const std::size_t pages( page_count( size) + 1); // add +1 for guard page + std::size_t size_ = pages * pagesize(); + +#ifndef BOOST_CONTEXT_FIBER + void * limit = ::VirtualAlloc( 0, size_, MEM_COMMIT, PAGE_READWRITE); + if ( ! limit) throw std::bad_alloc(); + + DWORD old_options; + const BOOL result = ::VirtualProtect( + limit, pagesize(), PAGE_READWRITE | PAGE_GUARD /*PAGE_NOACCESS*/, & old_options); + BOOST_ASSERT( FALSE != result); + + return static_cast< char * >( limit) + size_; +#endif +} + +void +stack_allocator::deallocate( void * vp, std::size_t size) const +{ + if ( vp) + { + const std::size_t pages( page_count( size) + 1); // add +1 for guard page + std::size_t size_ = pages * pagesize(); + BOOST_ASSERT( 0 < size && 0 < size_); + void * limit = static_cast< char * >( vp) - size_; + ::VirtualFree( limit, 0, MEM_RELEASE); + } +} + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/vendor/boost_1.51/libs/context/stack_utils_posix.cpp b/vendor/boost_1.51/libs/context/stack_utils_posix.cpp new file mode 100644 index 000000000..13c4e4eb0 --- /dev/null +++ b/vendor/boost_1.51/libs/context/stack_utils_posix.cpp @@ -0,0 +1,81 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_CONTEXT_SOURCE + +#include + +extern "C" { +#include +#include +#include +#include +} + +#include + +#include + +namespace { + +static rlimit stacksize_limit_() +{ + rlimit limit; + const int result = ::getrlimit( RLIMIT_STACK, & limit); + BOOST_ASSERT( 0 == result); + (void)result; // unused when in release mode... + return limit; +} + +static rlimit stacksize_limit() +{ + static rlimit limit = stacksize_limit_(); + return limit; +} + +} + +namespace boost { +namespace ctx { + +BOOST_CONTEXT_DECL +std::size_t default_stacksize() +{ + static std::size_t size = 256 * 1024; + return size; +} + +BOOST_CONTEXT_DECL +std::size_t minimum_stacksize() +{ return SIGSTKSZ; } + +BOOST_CONTEXT_DECL +std::size_t maximum_stacksize() +{ + BOOST_ASSERT( ! is_stack_unbound() ); + return static_cast< std::size_t >( stacksize_limit().rlim_max); +} + +BOOST_CONTEXT_DECL +bool is_stack_unbound() +{ return RLIM_INFINITY == stacksize_limit().rlim_max; } + +BOOST_CONTEXT_DECL +std::size_t pagesize() +{ + static std::size_t pagesize( ::getpagesize() ); + return pagesize; +} + +BOOST_CONTEXT_DECL +std::size_t page_count( std::size_t stacksize) +{ + return static_cast< std::size_t >( + std::ceil( + static_cast< float >( stacksize) / pagesize() ) ); +} + +}} diff --git a/vendor/boost_1.51/libs/context/stack_utils_windows.cpp b/vendor/boost_1.51/libs/context/stack_utils_windows.cpp new file mode 100644 index 000000000..373033dac --- /dev/null +++ b/vendor/boost_1.51/libs/context/stack_utils_windows.cpp @@ -0,0 +1,84 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_CONTEXT_SOURCE + +#include + +extern "C" { +#include +} + +#include +#include + +#include + +namespace { + +static SYSTEM_INFO system_info_() +{ + SYSTEM_INFO si; + ::GetSystemInfo( & si); + return si; +} + +static SYSTEM_INFO system_info() +{ + static SYSTEM_INFO si = system_info_(); + return si; +} + +} + +namespace boost { +namespace ctx { + +BOOST_CONTEXT_DECL +std::size_t default_stacksize() +{ + static std::size_t size = 256 * 1024; + return size; +} + +BOOST_CONTEXT_DECL +std::size_t minimum_stacksize() +{ + static std::size_t stacksize( + static_cast< std::size_t >( system_info().dwAllocationGranularity) ); + return stacksize; +} + +BOOST_CONTEXT_DECL +std::size_t maximum_stacksize() +{ + BOOST_ASSERT( ! is_stack_unbound() ); + static std::size_t stacksize = 8 * 1024 * 1024; + return stacksize; +} + +// Windows seams not to provide a limit for the stacksize +BOOST_CONTEXT_DECL +bool is_stack_unbound() +{ return true; } + +BOOST_CONTEXT_DECL +std::size_t pagesize() +{ + static std::size_t pagesize( + static_cast< std::size_t >( system_info().dwPageSize) ); + return pagesize; +} + +BOOST_CONTEXT_DECL +std::size_t page_count( std::size_t stacksize) +{ + return static_cast< std::size_t >( + std::ceil( + static_cast< float >( stacksize) / pagesize() ) ); +} + +}} diff --git a/vendor/cyoencode-1.0.2/LICENSE.TXT b/vendor/cyoencode-1.0.2/LICENSE.TXT new file mode 100644 index 000000000..4e10e7378 --- /dev/null +++ b/vendor/cyoencode-1.0.2/LICENSE.TXT @@ -0,0 +1,27 @@ +All the files in this library are covered under the terms of the Berkeley +Software Distribution (BSD) License: + +Copyright (c) 2009-2012, Graham Bull. +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. + + * 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 HOLDER 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. + diff --git a/vendor/cyoencode-1.0.2/README.TXT b/vendor/cyoencode-1.0.2/README.TXT new file mode 100644 index 000000000..30df17d8b --- /dev/null +++ b/vendor/cyoencode-1.0.2/README.TXT @@ -0,0 +1,50 @@ +=============================================================================== +CyoEncode +http://cyoencode.sourceforge.net/ + +Copyright (c) 2009-2012, Graham Bull. All rights reserved. +=============================================================================== + +Version 1.0.2 +Release Date 5th January 2012 + +------------------------------------------------------------------------------- +1. License +------------------------------------------------------------------------------- + +CyoEncode is made available under the terms of the Berkeley Software +Distribution (BSD) licence, as detailed in LICENSE.TXT. This allows you +complete freedom to use and distribute the code in source and/or binary form, +as long as you respect the original copyright. + +------------------------------------------------------------------------------- +2. Instructions +------------------------------------------------------------------------------- + +Simply copy the required source files (CyoEncode.h/cpp and CyoDecode.h/cpp) +into your C/C++ project. + +Examples of usage can be found in the test.c file. + +For Unix/Linux developers, there's a shell script that will build the test +using GCC. + +For Windows developers, Visual Studio projects are included. + +------------------------------------------------------------------------------- +3. Release Notes +------------------------------------------------------------------------------- + +1.0.2 - 5th January 2012 +- A little refactoring, added some shared functions. +- Added VS42010 project file. +- Added x64 build configurations. + +1.0.1 - 25th September 2009 +- Added the cyoBase??Validate() functions. +- Added detection of invalid encodings in the cyoBase??Decode() functions, + rather than relying on assertions. + +1.0.0 - 19th August 2009 +- First release. + diff --git a/vendor/cyoencode-1.0.2/src/CyoDecode.c b/vendor/cyoencode-1.0.2/src/CyoDecode.c new file mode 100644 index 000000000..d40e3a82f --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/CyoDecode.c @@ -0,0 +1,398 @@ +/* + * CyoDecode.c - part of the CyoEncode library + * + * Copyright (c) 2009-2012, Graham Bull. + * 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. + * + * * 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 HOLDER 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. + */ + +#include "CyoDecode.h" + +#include + +#include //TEMP + +/********************************** Shared ***********************************/ + +static int cyoBaseXXValidate( const char* src, size_t size, size_t inputBytes, size_t maxPadding, + unsigned char maxValue, const unsigned char table[] ) +{ + /* + * returns 0 if the source is a valid baseXX encoding + */ + + if (!src) + return -1; /*ERROR - NULL pointer*/ + + if (size % inputBytes != 0) + return -1; /*ERROR - extra characters*/ + + /* check the bytes */ + for (; size >= 1; --size, ++src) + { + unsigned char ch = *src; + if ((ch >= 0x80) || (table[ ch ] > maxValue)) + break; + } + + /* check any padding */ + for (; 1 <= size && size <= maxPadding; --size, ++src) + { + unsigned char ch = *src; + if ((ch >= 0x80) || (table[ ch ] != maxValue + 1)) + break; + } + + /* if size isn't zero then the encoded string isn't valid */ + if (size != 0) + return -2; /*ERROR - invalid baseXX character*/ + + /* OK */ + return 0; +} + +static size_t cyoBaseXXDecodeGetLength( size_t size, size_t inputBytes, size_t outputBytes ) +{ + if (size % inputBytes != 0) + return 0; /*ERROR - extra characters*/ + + /* OK */ + return (((size + inputBytes - 1) / inputBytes) * outputBytes) + 1; /*plus terminator*/ +} + +/****************************** Base16 Decoding ******************************/ + +static const size_t BASE16_INPUT = 2; +static const size_t BASE16_OUTPUT = 1; +static const size_t BASE16_MAX_PADDING = 0; +static const unsigned char BASE16_MAX_VALUE = 15; +static const unsigned char BASE16_TABLE[ 0x80 ] = { + /*00-07*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*08-0f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*10-17*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*18-1f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*20-27*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*28-2f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*30-37*/ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /*8 = '0'-'7'*/ + /*38-3f*/ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*2 = '8'-'9'*/ + /*40-47*/ 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, /*6 = 'A'-'F'*/ + /*48-4f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*50-57*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*58-5f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*60-67*/ 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, /*6 = 'a'-'f' (same as 'A'-'F')*/ + /*68-6f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*70-77*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*78-7f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +int cyoBase16Validate( const char* src, size_t size ) +{ + return cyoBaseXXValidate( src, size, BASE16_INPUT, BASE16_MAX_PADDING, BASE16_MAX_VALUE, BASE16_TABLE ); +} + +size_t cyoBase16DecodeGetLength( size_t size ) +{ + return cyoBaseXXDecodeGetLength( size, BASE16_INPUT, BASE16_OUTPUT ); +} + +size_t cyoBase16Decode( void* dest, const char* src, size_t size ) +{ + /* + * output 1 byte for every 2 input: + * + * outputs: 1 + * inputs: 1 = ----1111 = 1111---- + * 2 = ----2222 = ----2222 + */ + + if (dest && src && (size % BASE16_INPUT == 0)) + { + unsigned char* pDest = (unsigned char*)dest; + size_t dwSrcSize = size; + size_t dwDestSize = 0; + unsigned char in1, in2; + + while (dwSrcSize >= 1) + { + /* 2 inputs */ + in1 = *src++; + in2 = *src++; + dwSrcSize -= BASE16_INPUT; + + /* Validate ascii */ + if (in1 >= 0x80 || in2 >= 0x80) + return 0; /*ERROR - invalid base16 character*/ + + /* Convert ascii to base16 */ + in1 = BASE16_TABLE[ in1 ]; + in2 = BASE16_TABLE[ in2 ]; + + /* Validate base16 */ + if (in1 > BASE16_MAX_VALUE || in2 > BASE16_MAX_VALUE) + return 0; /*ERROR - invalid base16 character*/ + + /* 1 output */ + *pDest++ = ((in1 << 4) | in2); + dwDestSize += BASE16_OUTPUT; + } + *pDest++ = '\x0'; /*append terminator*/ + + return dwDestSize; + } + else + return 0; /*ERROR - null pointer, or size isn't a multiple of 2*/ +} + +/****************************** Base32 Decoding ******************************/ + +static const size_t BASE32_INPUT = 8; +static const size_t BASE32_OUTPUT = 5; +static const size_t BASE32_MAX_PADDING = 6; +static const unsigned char BASE32_MAX_VALUE = 31; +static const unsigned char BASE32_TABLE[ 0x80 ] = { + /*00-07*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*08-0f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*10-17*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*18-1f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*20-27*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*28-2f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*30-37*/ 0xFF, 0xFF, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /*6 = '2'-'7'*/ + /*38-3f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, /*1 = '='*/ + /*40-47*/ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /*7 = 'A'-'G'*/ + /*48-4f*/ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /*8 = 'H'-'O'*/ + /*50-57*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /*8 = 'P'-'W'*/ + /*58-5f*/ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*3 = 'X'-'Z'*/ + /*60-67*/ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /*7 = 'a'-'g' (same as 'A'-'G')*/ + /*68-6f*/ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /*8 = 'h'-'o' (same as 'H'-'O')*/ + /*70-77*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /*8 = 'p'-'w' (same as 'P'-'W')*/ + /*78-7f*/ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF /*3 = 'x'-'z' (same as 'X'-'Z')*/ +}; + +int cyoBase32Validate( const char* src, size_t size ) +{ + return cyoBaseXXValidate( src, size, BASE32_INPUT, BASE32_MAX_PADDING, BASE32_MAX_VALUE, BASE32_TABLE ); +} + +size_t cyoBase32DecodeGetLength( size_t size ) +{ + return cyoBaseXXDecodeGetLength( size, BASE32_INPUT, BASE32_OUTPUT ); +} + +size_t cyoBase32Decode( void* dest, const char* src, size_t size ) +{ + /* + * output 5 bytes for every 8 input: + * + * outputs: 1 2 3 4 5 + * inputs: 1 = ---11111 = 11111--- + * 2 = ---222XX = -----222 XX------ + * 3 = ---33333 = --33333- + * 4 = ---4XXXX = -------4 XXXX---- + * 5 = ---5555X = ----5555 X------- + * 6 = ---66666 = -66666-- + * 7 = ---77XXX = ------77 XXX----- + * 8 = ---88888 = ---88888 + */ + + if (dest && src && (size % BASE32_INPUT == 0)) + { + unsigned char* pDest = (unsigned char*)dest; + size_t dwSrcSize = size; + size_t dwDestSize = 0; + unsigned char in1, in2, in3, in4, in5, in6, in7, in8; + + while (dwSrcSize >= 1) + { + /* 8 inputs */ + in1 = *src++; + in2 = *src++; + in3 = *src++; + in4 = *src++; + in5 = *src++; + in6 = *src++; + in7 = *src++; + in8 = *src++; + dwSrcSize -= BASE32_INPUT; + + /* Validate ascii */ + if ( in1 >= 0x80 || in2 >= 0x80 || in3 >= 0x80 || in4 >= 0x80 + || in5 >= 0x80 || in6 >= 0x80 || in7 >= 0x80 || in8 >= 0x80) + return 0; /*ERROR - invalid base32 character*/ + + /* Convert ascii to base16 */ + in1 = BASE32_TABLE[ in1 ]; + in2 = BASE32_TABLE[ in2 ]; + in3 = BASE32_TABLE[ in3 ]; + in4 = BASE32_TABLE[ in4 ]; + in5 = BASE32_TABLE[ in5 ]; + in6 = BASE32_TABLE[ in6 ]; + in7 = BASE32_TABLE[ in7 ]; + in8 = BASE32_TABLE[ in8 ]; + + /* Validate base32 */ + if (in1 > BASE32_MAX_VALUE || in2 > BASE32_MAX_VALUE) + return 0; /*ERROR - invalid base32 character*/ + /*the following can be padding*/ + if ( in3 > BASE32_MAX_VALUE + 1 || in4 > BASE32_MAX_VALUE + 1 || in5 > BASE32_MAX_VALUE + 1 + || in6 > BASE32_MAX_VALUE + 1 || in7 > BASE32_MAX_VALUE + 1 || in8 > BASE32_MAX_VALUE + 1) + return 0; /*ERROR - invalid base32 character*/ + + /* 5 outputs */ + *pDest++ = ((in1 & 0x1f) << 3) | ((in2 & 0x1c) >> 2); + *pDest++ = ((in2 & 0x03) << 6) | ((in3 & 0x1f) << 1) | ((in4 & 0x10) >> 4); + *pDest++ = ((in4 & 0x0f) << 4) | ((in5 & 0x1e) >> 1); + *pDest++ = ((in5 & 0x01) << 7) | ((in6 & 0x1f) << 2) | ((in7 & 0x18) >> 3); + *pDest++ = ((in7 & 0x07) << 5) | (in8 & 0x1f); + dwDestSize += BASE32_OUTPUT; + + /* Padding */ + if (in8 == BASE32_MAX_VALUE + 1) + { + --dwDestSize; + assert( (in7 == BASE32_MAX_VALUE + 1 && in6 == BASE32_MAX_VALUE + 1) || (in7 != BASE32_MAX_VALUE + 1) ); + if (in6 == BASE32_MAX_VALUE + 1) + { + --dwDestSize; + if (in5 == BASE32_MAX_VALUE + 1) + { + --dwDestSize; + assert( (in4 == BASE32_MAX_VALUE + 1 && in3 == BASE32_MAX_VALUE + 1) || (in4 != BASE32_MAX_VALUE + 1) ); + if (in3 == BASE32_MAX_VALUE + 1) + { + --dwDestSize; + } + } + } + } + } + *pDest++ = '\x0'; /*append terminator*/ + + return dwDestSize; + } + else + return 0; /*ERROR - null pointer, or size isn't a multiple of 8*/ +} + +/****************************** Base64 Decoding ******************************/ + +static const size_t BASE64_INPUT = 4; +static const size_t BASE64_OUTPUT = 3; +static const size_t BASE64_MAX_PADDING = 2; +static const unsigned char BASE64_MAX_VALUE = 63; +static const unsigned char BASE64_TABLE[ 0x80 ] = { + /*00-07*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*08-0f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*10-17*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*18-1f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*20-27*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*28-2f*/ 0xFF, 0xFF, 0xFF, 0x3e, 0xFF, 0xFF, 0xFF, 0x3f, /*2 = '+' and '/'*/ + /*30-37*/ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /*8 = '0'-'7'*/ + /*38-3f*/ 0x3c, 0x3d, 0xFF, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, /*2 = '8'-'9' and '='*/ + /*40-47*/ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /*7 = 'A'-'G'*/ + /*48-4f*/ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /*8 = 'H'-'O'*/ + /*50-57*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /*8 = 'P'-'W'*/ + /*58-5f*/ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*3 = 'X'-'Z'*/ + /*60-67*/ 0xFF, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /*7 = 'a'-'g'*/ + /*68-6f*/ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /*8 = 'h'-'o'*/ + /*70-77*/ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /*8 = 'p'-'w'*/ + /*78-7f*/ 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF /*3 = 'x'-'z'*/ +}; + +int cyoBase64Validate( const char* src, size_t size ) +{ + return cyoBaseXXValidate( src, size, BASE64_INPUT, BASE64_MAX_PADDING, BASE64_MAX_VALUE, BASE64_TABLE ); +} + +size_t cyoBase64DecodeGetLength( size_t size ) +{ + return cyoBaseXXDecodeGetLength( size, BASE64_INPUT, BASE64_OUTPUT ); +} + +size_t cyoBase64Decode( void* dest, const char* src, size_t size ) +{ + /* + * output 3 bytes for every 4 input: + * + * outputs: 1 2 3 + * inputs: 1 = --111111 = 111111-- + * 2 = --22XXXX = ------22 XXXX---- + * 3 = --3333XX = ----3333 XX------ + * 4 = --444444 = --444444 + */ + + if (dest && src && (size % BASE64_INPUT == 0)) + { + unsigned char* pDest = (unsigned char*)dest; + size_t dwSrcSize = size; + size_t dwDestSize = 0; + unsigned char in1, in2, in3, in4; + + while (dwSrcSize >= 1) + { + /* 4 inputs */ + in1 = *src++; + in2 = *src++; + in3 = *src++; + in4 = *src++; + dwSrcSize -= BASE64_INPUT; + + /* Validate ascii */ + if (in1 >= 0x80 || in2 >= 0x80 || in3 >= 0x80 || in4 >= 0x80) + return 0; /*ERROR - invalid base64 character*/ + + /* Convert ascii to base64 */ + in1 = BASE64_TABLE[ in1 ]; + in2 = BASE64_TABLE[ in2 ]; + in3 = BASE64_TABLE[ in3 ]; + in4 = BASE64_TABLE[ in4 ]; + + /* Validate base64 */ + if (in1 > BASE64_MAX_VALUE || in2 > BASE64_MAX_VALUE) + return 0; /*ERROR - invalid base64 character*/ + /*the following can be padding*/ + if (in3 > BASE64_MAX_VALUE + 1 || in4 > BASE64_MAX_VALUE + 1) + return 0; /*ERROR - invalid base64 character*/ + + /* 3 outputs */ + *pDest++ = ((in1 & 0x3f) << 2) | ((in2 & 0x30) >> 4); + *pDest++ = ((in2 & 0x0f) << 4) | ((in3 & 0x3c) >> 2); + *pDest++ = ((in3 & 0x03) << 6) | (in4 & 0x3f); + dwDestSize += BASE64_OUTPUT; + + /* Padding */ + if (in4 == BASE64_MAX_VALUE + 1) + { + --dwDestSize; + if (in3 == BASE64_MAX_VALUE + 1) + { + --dwDestSize; + } + } + } + *pDest++ = '\x0'; /*append terminator*/ + + return dwDestSize; + } + else + return 0; /*ERROR - null pointer, or size isn't a multiple of 4*/ +} diff --git a/vendor/cyoencode-1.0.2/src/CyoDecode.h b/vendor/cyoencode-1.0.2/src/CyoDecode.h new file mode 100644 index 000000000..873ed20d7 --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/CyoDecode.h @@ -0,0 +1,58 @@ +/* + * CyoDecode.h - part of the CyoEncode library + * + * Copyright (c) 2009-2012, Graham Bull. + * 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. + * + * * 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 HOLDER 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 __CYODECODE_H +#define __CYODECODE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Base16 Decoding */ +int cyoBase16Validate( const char* src, size_t size ); +size_t cyoBase16DecodeGetLength( size_t size ); +size_t cyoBase16Decode( void* dest, const char* src, size_t size ); + +/* Base32 Decoding */ +int cyoBase32Validate( const char* src, size_t size ); +size_t cyoBase32DecodeGetLength( size_t size ); +size_t cyoBase32Decode( void* dest, const char* src, size_t size ); + +/* Base64 Decoding */ +int cyoBase64Validate( const char* src, size_t size ); +size_t cyoBase64DecodeGetLength( size_t size ); +size_t cyoBase64Decode( void* dest, const char* src, size_t size ); + +#ifdef __cplusplus +} +#endif + +#endif /*__CYODECODE_H*/ + diff --git a/vendor/cyoencode-1.0.2/src/CyoEncode.c b/vendor/cyoencode-1.0.2/src/CyoEncode.c new file mode 100644 index 000000000..3e9191fdc --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/CyoEncode.c @@ -0,0 +1,283 @@ +/* + * CyoEncode.c - part of the CyoEncode library + * + * Copyright (c) 2009-2012, Graham Bull. + * 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. + * + * * 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 HOLDER 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. + */ + +#include "CyoEncode.h" + +#include + +/********************************** Shared ***********************************/ + +static size_t cyoBaseXXEncodeGetLength( size_t size, size_t inputBytes, size_t outputBytes ) +{ + return (((size + inputBytes - 1) / inputBytes) * outputBytes) + 1; /*plus terminator*/ +} + +/****************************** Base16 Encoding ******************************/ + +static const size_t BASE16_INPUT = 1; +static const size_t BASE16_OUTPUT = 2; +static const char* const BASE16_TABLE = "0123456789ABCDEF"; + +size_t cyoBase16EncodeGetLength( size_t size ) +{ + return cyoBaseXXEncodeGetLength( size, BASE16_INPUT, BASE16_OUTPUT ); +} + +size_t cyoBase16Encode( char* dest, const void* src, size_t size ) +{ + /* + * output 2 bytes for every 1 input: + * + * inputs: 1 + * outputs: 1 = ----1111 = 1111---- + * 2 = ----2222 = ----2222 + */ + + if (dest && src) + { + unsigned char* pSrc = (unsigned char*)src; + size_t dwSrcSize = size; + size_t dwDestSize = 0; + unsigned char ch; + + while (dwSrcSize >= 1) + { + /* 1 input */ + ch = *pSrc++; + dwSrcSize -= BASE16_INPUT; + + /* 2 outputs */ + *dest++ = BASE16_TABLE[ (ch & 0xf0) >> 4 ]; + *dest++ = BASE16_TABLE[ (ch & 0x0f) ]; + dwDestSize += BASE16_OUTPUT; + } + *dest++ = '\x0'; /*append terminator*/ + + return dwDestSize; + } + else + return 0; /*ERROR - null pointer*/ +} + +/****************************** Base32 Encoding ******************************/ + +static const size_t BASE32_INPUT = 5; +static const size_t BASE32_OUTPUT = 8; +static const char* const BASE32_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="; + +size_t cyoBase32EncodeGetLength( size_t size ) +{ + return cyoBaseXXEncodeGetLength( size, BASE32_INPUT, BASE32_OUTPUT ); +} + +size_t cyoBase32Encode( char* dest, const void* src, size_t size ) +{ + /* + * output 8 bytes for every 5 input: + * + * inputs: 1 2 3 4 5 + * outputs: 1 = ---11111 = 11111--- + * 2 = ---222XX = -----222 XX------ + * 3 = ---33333 = --33333- + * 4 = ---4XXXX = -------4 XXXX---- + * 5 = ---5555X = ----5555 X------- + * 6 = ---66666 = -66666-- + * 7 = ---77XXX = ------77 XXX----- + * 8 = ---88888 = ---88888 + */ + + if (dest && src) + { + unsigned char* pSrc = (unsigned char*)src; + size_t dwSrcSize = size; + size_t dwDestSize = 0; + size_t dwBlockSize; + unsigned char n1, n2, n3, n4, n5, n6, n7, n8; + + while (dwSrcSize >= 1) + { + /* Encode inputs */ + dwBlockSize = (dwSrcSize < BASE32_INPUT ? dwSrcSize : BASE32_INPUT); + n1 = n2 = n3 = n4 = n5 = n6 = n7 = n8 = 0; + switch (dwBlockSize) + { + case 5: + n8 = (pSrc[ 4 ] & 0x1f); + n7 = ((pSrc[ 4 ] & 0xe0) >> 5); + case 4: + n7 |= ((pSrc[ 3 ] & 0x03) << 3); + n6 = ((pSrc[ 3 ] & 0x7c) >> 2); + n5 = ((pSrc[ 3 ] & 0x80) >> 7); + case 3: + n5 |= ((pSrc[ 2 ] & 0x0f) << 1); + n4 = ((pSrc[ 2 ] & 0xf0) >> 4); + case 2: + n4 |= ((pSrc[ 1 ] & 0x01) << 4); + n3 = ((pSrc[ 1 ] & 0x3e) >> 1); + n2 = ((pSrc[ 1 ] & 0xc0) >> 6); + case 1: + n2 |= ((pSrc[ 0 ] & 0x07) << 2); + n1 = ((pSrc[ 0 ] & 0xf8) >> 3); + break; + + default: + assert( 0 ); + } + pSrc += dwBlockSize; + dwSrcSize -= dwBlockSize; + + /* Validate */ + assert( n1 <= 31 ); + assert( n2 <= 31 ); + assert( n3 <= 31 ); + assert( n4 <= 31 ); + assert( n5 <= 31 ); + assert( n6 <= 31 ); + assert( n7 <= 31 ); + assert( n8 <= 31 ); + + /* Padding */ + switch (dwBlockSize) + { + case 1: n3 = n4 = 32; + case 2: n5 = 32; + case 3: n6 = n7 = 32; + case 4: n8 = 32; + case 5: + break; + + default: + assert( 0 ); + } + + /* 8 outputs */ + *dest++ = BASE32_TABLE[ n1 ]; + *dest++ = BASE32_TABLE[ n2 ]; + *dest++ = BASE32_TABLE[ n3 ]; + *dest++ = BASE32_TABLE[ n4 ]; + *dest++ = BASE32_TABLE[ n5 ]; + *dest++ = BASE32_TABLE[ n6 ]; + *dest++ = BASE32_TABLE[ n7 ]; + *dest++ = BASE32_TABLE[ n8 ]; + dwDestSize += BASE32_OUTPUT; + } + *dest++ = '\x0'; /*append terminator*/ + + return dwDestSize; + } + else + return 0; /*ERROR - null pointer*/ +} + +/****************************** Base64 Encoding ******************************/ + +static const size_t BASE64_INPUT = 3; +static const size_t BASE64_OUTPUT = 4; +static const char* const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +size_t cyoBase64EncodeGetLength( size_t size ) +{ + return cyoBaseXXEncodeGetLength( size, BASE64_INPUT, BASE64_OUTPUT ); +} + +size_t cyoBase64Encode( char* dest, const void* src, size_t size ) +{ + /* + * output 4 bytes for every 3 input: + * + * inputs: 1 2 3 + * outputs: 1 = --111111 = 111111-- + * 2 = --22XXXX = ------22 XXXX---- + * 3 = --3333XX = ----3333 XX------ + * 4 = --444444 = --444444 + */ + + if (dest && src) + { + unsigned char* pSrc = (unsigned char*)src; + size_t dwSrcSize = size; + size_t dwDestSize = 0; + size_t dwBlockSize = 0; + unsigned char n1, n2, n3, n4; + + while (dwSrcSize >= 1) + { + /* Encode inputs */ + dwBlockSize = (dwSrcSize < BASE64_INPUT ? dwSrcSize : BASE64_INPUT); + n1 = n2 = n3 = n4 = 0; + switch (dwBlockSize) + { + case 3: + n4 = (pSrc[ 2 ] & 0x3f); + n3 = ((pSrc[ 2 ] & 0xc0) >> 6); + case 2: + n3 |= ((pSrc[ 1 ] & 0x0f) << 2); + n2 = ((pSrc[ 1 ] & 0xf0) >> 4); + case 1: + n2 |= ((pSrc[ 0 ] & 0x03) << 4); + n1 = ((pSrc[ 0 ] & 0xfc) >> 2); + break; + + default: + assert( 0 ); + } + pSrc += dwBlockSize; + dwSrcSize -= dwBlockSize; + + /* Validate */ + assert( n1 <= 63 ); + assert( n2 <= 63 ); + assert( n3 <= 63 ); + assert( n4 <= 63 ); + + /* Padding */ + switch (dwBlockSize) + { + case 1: n3 = 64; + case 2: n4 = 64; + case 3: + break; + + default: + assert( 0 ); + } + + /* 4 outputs */ + *dest++ = BASE64_TABLE[ n1 ]; + *dest++ = BASE64_TABLE[ n2 ]; + *dest++ = BASE64_TABLE[ n3 ]; + *dest++ = BASE64_TABLE[ n4 ]; + dwDestSize += BASE64_OUTPUT; + } + *dest++ = '\x0'; /*append terminator*/ + + return dwDestSize; + } + else + return 0; /*ERROR - null pointer*/ +} diff --git a/vendor/cyoencode-1.0.2/src/CyoEncode.h b/vendor/cyoencode-1.0.2/src/CyoEncode.h new file mode 100644 index 000000000..183a66e06 --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/CyoEncode.h @@ -0,0 +1,55 @@ +/* + * CyoEncode.h - part of the CyoEncode library + * + * Copyright (c) 2009-2012, Graham Bull. + * 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. + * + * * 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 HOLDER 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 __CYOENCODE_H +#define __CYOENCODE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Base16 Encoding */ +size_t cyoBase16EncodeGetLength( size_t size ); +size_t cyoBase16Encode( char* dest, const void* src, size_t size ); + +/* Base32 Encoding */ +size_t cyoBase32EncodeGetLength( size_t size ); +size_t cyoBase32Encode( char* dest, const void* src, size_t size ); + +/* Base64 Encoding */ +size_t cyoBase64EncodeGetLength( size_t size ); +size_t cyoBase64Encode( char* dest, const void* src, size_t size ); + +#ifdef __cplusplus +} +#endif + +#endif /*__CYOENCODE_H*/ + diff --git a/vendor/cyoencode-1.0.2/src/build.sh b/vendor/cyoencode-1.0.2/src/build.sh new file mode 100755 index 000000000..67c0907eb --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/build.sh @@ -0,0 +1,2 @@ +gcc test.c CyoEncode.c CyoDecode.c -o test + diff --git a/vendor/cyoencode-1.0.2/src/cyoencode-vc100.vcxproj b/vendor/cyoencode-1.0.2/src/cyoencode-vc100.vcxproj new file mode 100644 index 000000000..8c3a8d236 --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/cyoencode-vc100.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + cyoencode-vc100 + {C773C1E9-CAC6-40AF-A400-567F73AB0178} + cyoencodevc100 + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + true + + + Application + Unicode + + + Application + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + true + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + false + false + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + EditAndContinue + + + true + Console + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + true + Console + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level4 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level4 + ProgramDatabase + + + true + Console + true + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/cyoencode-1.0.2/src/cyoencode-vc71.vcproj b/vendor/cyoencode-1.0.2/src/cyoencode-vc71.vcproj new file mode 100644 index 000000000..26c46ff5e --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/cyoencode-vc71.vcproj @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/cyoencode-1.0.2/src/cyoencode-vc80.vcproj b/vendor/cyoencode-1.0.2/src/cyoencode-vc80.vcproj new file mode 100644 index 000000000..c08685ade --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/cyoencode-vc80.vcproj @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/cyoencode-1.0.2/src/cyoencode-vc90.vcproj b/vendor/cyoencode-1.0.2/src/cyoencode-vc90.vcproj new file mode 100644 index 000000000..ee5927b81 --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/cyoencode-vc90.vcproj @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/cyoencode-1.0.2/src/test.c b/vendor/cyoencode-1.0.2/src/test.c new file mode 100644 index 000000000..927410513 --- /dev/null +++ b/vendor/cyoencode-1.0.2/src/test.c @@ -0,0 +1,191 @@ +/* + * test.c - part of the CyoEncode library + * + * Copyright (c) 2009-2012, Graham Bull. + * 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. + * + * * 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 HOLDER 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. + */ + +#include "CyoEncode.h" +#include "CyoDecode.h" + +#include +#include +#include + +#define TEST_BASExx(base,str,expected) \ + printf( "TEST_BASE%s('%s')='%s'", #base, str, expected ); \ + required = cyoBase##base##EncodeGetLength( strlen( str )); \ + encoded = (char*)malloc( required ); \ + if (encoded == NULL) { \ + printf( "\n*** ERROR: Unable to allocate buffer for encoding ***\n" ); \ + goto exit; \ + } \ + cyoBase##base##Encode( encoded, str, strlen( str )); \ + if (strcmp( encoded, expected ) != 0) { \ + printf( "\n*** ERROR: Encoding failure ***\n" ); \ + goto exit; \ + } \ + valid = cyoBase##base##Validate( encoded, strlen( encoded )); \ + if (valid < 0) \ + { \ + printf( "\n*** ERROR: Unable to validate encoding (error %d) ***\n", valid ); \ + goto exit; \ + } \ + printf( " [passed]\n" ); \ + free( encoded ); encoded = NULL; + +#define TEST_BASE64(str,expected) TEST_BASExx(64,str,expected) +#define TEST_BASE32(str,expected) TEST_BASExx(32,str,expected) +#define TEST_BASE16(str,expected) TEST_BASExx(16,str,expected) + +#define CHECK_INVALID_BASExx(base,str,res) \ + printf( "CHECK_INVALID_BASE%s('%s')=%d", #base, str, res ); \ + valid = cyoBase##base##Validate( str, strlen( str )); \ + if (valid == 0) \ + { \ + printf( "\n*** ERROR: This is a valid encoding! ***\n" ); \ + goto exit; \ + } \ + if (valid != res) \ + { \ + printf( "\n*** ERROR: Expected a different return code! (%d) ***\n", valid ); \ + goto exit; \ + } \ + printf( " [passed]\n", #base, str ); \ + +#define CHECK_INVALID_BASE16(enc,res) CHECK_INVALID_BASExx(16,enc,res) +#define CHECK_INVALID_BASE32(enc,res) CHECK_INVALID_BASExx(32,enc,res) +#define CHECK_INVALID_BASE64(enc,res) CHECK_INVALID_BASExx(64,enc,res) + +int main( void ) +{ + const char* const original = "A wise man speaks when he has something to say"; + size_t required = 0; + char* encoded = NULL; + char* decoded = NULL; + int valid = 0; + int retcode = 1; + + printf( "Running CyoEncode tests...\n" ); + + /* Encode using Base64 */ + + printf( "Original = '%s'\n", original ); + required = cyoBase64EncodeGetLength( strlen( original )); + encoded = (char*)malloc( required ); + if (encoded == NULL) + { + printf( "*** ERROR: Unable to allocate buffer for encoding ***\n" ); + goto exit; + } + cyoBase64Encode( encoded, original, strlen( original )); + printf( "Encoded = '%s'\n", encoded ); + + /* Validate encoding */ + + valid = cyoBase64Validate( encoded, strlen( encoded )); + if (valid < 0) + { + printf( "*** ERROR: Encoding failure (error %d) ***\n", valid ); + goto exit; + } + + /* Decode using Base64 */ + + required = cyoBase64DecodeGetLength( strlen( encoded )); + decoded = (char*)malloc( required ); + if (decoded == NULL) + { + printf( "*** ERROR: Unable to allocate buffer for decoding ***\n" ); + goto exit; + } + cyoBase64Decode( decoded, encoded, strlen( encoded )); + printf( "Decoded = '%s'\n", decoded ); + + /* Validate */ + + if (strcmp( original, decoded ) != 0) + { + printf( "*** ERROR: Encoding/decoding failure ***\n" ); + goto exit; + } + + free( encoded ); + encoded = NULL; + free( decoded ); + decoded = NULL; + + /* Test vectors from RFC 4648 */ + + TEST_BASE16( "", "" ); + TEST_BASE16( "f", "66" ); + TEST_BASE16( "fo", "666F" ); + TEST_BASE16( "foo", "666F6F" ); + TEST_BASE16( "foob", "666F6F62" ); + TEST_BASE16( "fooba", "666F6F6261" ); + TEST_BASE16( "foobar", "666F6F626172" ); + + TEST_BASE32( "", "" ); + TEST_BASE32( "f", "MY======" ); + TEST_BASE32( "fo", "MZXQ====" ); + TEST_BASE32( "foo", "MZXW6===" ); + TEST_BASE32( "foob", "MZXW6YQ=" ); + TEST_BASE32( "fooba", "MZXW6YTB" ); + TEST_BASE32( "foobar", "MZXW6YTBOI======" ); + + TEST_BASE64( "", "" ); + TEST_BASE64( "f", "Zg==" ); + TEST_BASE64( "fo", "Zm8=" ); + TEST_BASE64( "foo", "Zm9v" ); + TEST_BASE64( "foob", "Zm9vYg==" ); + TEST_BASE64( "fooba", "Zm9vYmE=" ); + TEST_BASE64( "foobar", "Zm9vYmFy" ); + + /* Other tests */ + + CHECK_INVALID_BASE16( "1", -1 ); + CHECK_INVALID_BASE16( "123", -1 ); + CHECK_INVALID_BASE16( "1G", -2 ); + + CHECK_INVALID_BASE32( "A", -1 ); + CHECK_INVALID_BASE32( "ABCDEFG", -1 ); + CHECK_INVALID_BASE32( "ABCDEFG1", -2 ); + CHECK_INVALID_BASE32( "A=======", -2 ); + + CHECK_INVALID_BASE64( "A", -1 ); + CHECK_INVALID_BASE64( "ABCDE", -1 ); + CHECK_INVALID_BASE64( "A&B=", -2 ); + CHECK_INVALID_BASE64( "A===", -2 ); + + printf( "*** All tests passed ***\n" ); + retcode = 0; + +exit: + if (encoded != NULL) + free( encoded ); + if (decoded != NULL) + free( decoded ); + + return retcode; +} diff --git a/vendor/libssh2-1.4.2/AUTHORS b/vendor/libssh2-1.4.2/AUTHORS deleted file mode 100644 index 214fca988..000000000 --- a/vendor/libssh2-1.4.2/AUTHORS +++ /dev/null @@ -1,48 +0,0 @@ - libssh2 is the result of many friendly people. This list is an attempt to - mention all contributors. If we've missed anyone, tell us! - - This list of names is a-z sorted. - -Adam Gobiowski -Alexander Holyapin -Alexander Lamaison -Ben Kibbey -Bjorn Stenborg -Carlo Bramini -Dan Casey -Dan Fandrich -Daniel Stenberg -David J Sullivan -David Robins -Edink Kadribasic -Erik Brossler -Francois Dupoux -Guenter Knauf -Heiner Steven -James Housleys -Jean-Louis Charton -Jussi Mononen -Mark McPherson -Markus Moeller -Mike Protts -Mikhail Gusarov -Neil Gierman -Olivier Hervieu -Paul Veldkamp -Peter Krempa -Peter O'Gorman -Peter Stuge -Romain Bondue -Sara Golemon -Satish Mittal -Sean Peterson -Selcuk Gueney -Simon Hart -Simon Josefsson -Steven Ayre -Steven Van Ingelgem -Tor Arntsen -Vincent Jaulin -Vlad Grachov -Wez Furlong -Yang Tse diff --git a/vendor/libssh2-1.4.2/CMakeLists.txt b/vendor/libssh2-1.4.2/CMakeLists.txt deleted file mode 100644 index 9ce81c194..000000000 --- a/vendor/libssh2-1.4.2/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -include_directories(include) -include_directories( ${OPENSSL_INCLUDE_DIR} ) -SET( sources - src/agent.c - src/channel.c - src/comp.c - src/crypt.c - src/global.c - src/hostkey.c - src/keepalive.c - src/kex.c - src/knownhost.c - src/libgcrypt.c - src/mac.c - src/misc.c - src/openssl.c - src/packet.c - src/pem.c - src/publickey.c - src/scp.c - src/session.c - src/sftp.c - src/transport.c - src/userauth.c - src/version.c -) -add_definitions( -DLIBSSH2DEBUG ) -SETUP_LIBRARY( ssh2 SOURCES ${sources} LIBRARIES ${libraries} LIBRARY_TYPE STATIC ) diff --git a/vendor/libssh2-1.4.2/COPYING b/vendor/libssh2-1.4.2/COPYING deleted file mode 100644 index 1bd78c9cb..000000000 --- a/vendor/libssh2-1.4.2/COPYING +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (c) 2004-2007 Sara Golemon - * Copyright (c) 2005,2006 Mikhail Gusarov - * Copyright (c) 2006-2007 The Written Word, Inc. - * Copyright (c) 2007 Eli Fant - * Copyright (c) 2009 Daniel Stenberg - * Copyright (C) 2008, 2009 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - diff --git a/vendor/libssh2-1.4.2/ChangeLog b/vendor/libssh2-1.4.2/ChangeLog deleted file mode 100644 index 404c887c2..000000000 --- a/vendor/libssh2-1.4.2/ChangeLog +++ /dev/null @@ -1 +0,0 @@ -see NEWS diff --git a/vendor/libssh2-1.4.2/HACKING b/vendor/libssh2-1.4.2/HACKING deleted file mode 100644 index 5da8e66c2..000000000 --- a/vendor/libssh2-1.4.2/HACKING +++ /dev/null @@ -1,13 +0,0 @@ - -libssh2 source code style guide: - - - 4 level indent - - spaces-only (no tabs) - - open braces on the if/for line: - - if (banana) { - go_nuts(); - } - - - keep source lines shorter than 80 columns - - See libssh2-style.el for how to achieve this within Emacs diff --git a/vendor/libssh2-1.4.2/README b/vendor/libssh2-1.4.2/README deleted file mode 100644 index d297f3110..000000000 --- a/vendor/libssh2-1.4.2/README +++ /dev/null @@ -1,102 +0,0 @@ -libssh2 - SSH2 library -====================== - -libssh2 is a library implementing the SSH2 protocol, available under -the revised BSD license. - -Web site: http://www.libssh2.org/ - -Mailing list: http://cool.haxx.se/mailman/listinfo/libssh2-devel - -The rest of this readme does not apply as the build system has been replaced -with CMake for use in mace on windows/linux and mac. - - - -Generic installation instructions are in INSTALL. Some ./configure -options deserve additional comments: - - * --enable-crypt-none - - The SSH2 Transport allows for unencrypted data - transmission using the "none" cipher. Because this is - such a huge security hole, it is typically disabled on - SSH2 implementations and is disabled in libssh2 by - default as well. - - Enabling this option will allow for "none" as a - negotiable method, however it still requires that the - method be advertized by the remote end and that no - more-preferable methods are available. - - * --enable-mac-none - - The SSH2 Transport also allows implementations to - forego a message authentication code. While this is - less of a security risk than using a "none" cipher, it - is still not recommended as disabling MAC hashes - removes a layer of security. - - Enabling this option will allow for "none" as a - negotiable method, however it still requires that the - method be advertized by the remote end and that no - more-preferable methods are available. - - * --disable-gex-new - - The diffie-hellman-group-exchange-sha1 (dh-gex) key - exchange method originally defined an exchange - negotiation using packet type 30 to request a - generation pair based on a single target value. Later - refinement of dh-gex provided for range and target - values. By default libssh2 will use the newer range - method. - - If you experience trouble connecting to an old SSH - server using dh-gex, try this option to fallback on - the older more reliable method. - - * --with-libgcrypt - * --without-libgcrypt - * --with-libgcrypt-prefix=DIR - - libssh2 can use the Libgcrypt library - (http://www.gnupg.org/) for cryptographic operations. - Either Libgcrypt or OpenSSL is required. - - Configure will attempt to locate Libgcrypt - automatically. - - If your installation of Libgcrypt is in another - location, specify it using --with-libgcrypt-prefix. - - * --with-openssl - * --without-openssl - * --with-libssl-prefix=[DIR] - - libssh2 can use the OpenSSL library - (http://www.openssl.org) for cryptographic operations. - Either Libgcrypt or OpenSSL is required. - - Configure will attempt to locate OpenSSL in the - default location. - - If your installation of OpenSSL is in another - location, specify it using --with-libssl-prefix. - - * --with-libz - * --without-libz - * --with-libz-prefix=[DIR] - - If present, libssh2 will attempt to use the zlib - (http://www.zlib.org) for payload compression, however - zlib is not required. - - If your installation of Libz is in another location, - specify it using --with-libz-prefix. - - * --enable-debug - - Will make the build use more pedantic and strict compiler - options as well as enable the libssh2_trace() function (for - showing debug traces). diff --git a/vendor/libssh2-1.4.2/RELEASE-NOTES b/vendor/libssh2-1.4.2/RELEASE-NOTES deleted file mode 100644 index ee52e8c89..000000000 --- a/vendor/libssh2-1.4.2/RELEASE-NOTES +++ /dev/null @@ -1,21 +0,0 @@ -libssh2 1.4.2 - -This release includes the following bugfixes: - - o Return LIBSSH2_ERROR_SOCKET_DISCONNECT on EOF when reading banner - o userauth.c: fread() from public key file to correctly detect any errors - o configure.ac: Add option to disable build of the example applications - o Added 'Requires.private:' line to libssh2.pc - o SFTP: filter off incoming "zombie" responses - o gettimeofday: no need for a replacement under cygwin - o SSH_MSG_CHANNEL_REQUEST: default to want_reply - o win32/libssh2_config.h: Remove hardcoded #define LIBSSH2_HAVE_ZLIB - -This release would not have looked like this without help, code, reports and -advice from friends like these: - - Alexander Lamaison, Rafael Kitover, Guenter Knauf, Peter Stuge, - Oleksiy Zagorskyi - - Thanks! (and sorry if I forgot to mention someone) - diff --git a/vendor/libssh2-1.4.2/include/libssh2.h b/vendor/libssh2-1.4.2/include/libssh2.h deleted file mode 100644 index 6af0028be..000000000 --- a/vendor/libssh2-1.4.2/include/libssh2.h +++ /dev/null @@ -1,1188 +0,0 @@ -/* Copyright (c) 2004-2009, Sara Golemon - * Copyright (c) 2009-2012 Daniel Stenberg - * Copyright (c) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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 LIBSSH2_H -#define LIBSSH2_H 1 - -#define LIBSSH2_COPYRIGHT "2004-2012 The libssh2 project and its contributors." - -/* We use underscore instead of dash when appending DEV in dev versions just - to make the BANNER define (used by src/session.c) be a valid SSH - banner. Release versions have no appended strings and may of course not - have dashes either. */ -#define LIBSSH2_VERSION "1.4.2" - -/* The numeric version number is also available "in parts" by using these - defines: */ -#define LIBSSH2_VERSION_MAJOR 1 -#define LIBSSH2_VERSION_MINOR 4 -#define LIBSSH2_VERSION_PATCH 2 - -/* This is the numeric version of the libssh2 version number, meant for easier - parsing and comparions by programs. The LIBSSH2_VERSION_NUM define will - always follow this syntax: - - 0xXXYYZZ - - Where XX, YY and ZZ are the main version, release and patch numbers in - hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 - appears as "0x090b07". - - This 6-digit (24 bits) hexadecimal number does not show pre-release number, - and it is always a greater number in a more recent release. It makes - comparisons with greater than and less than work. -*/ -#define LIBSSH2_VERSION_NUM 0x010402 - -/* - * This is the date and time when the full source package was created. The - * timestamp is not stored in the source code repo, as the timestamp is - * properly set in the tarballs by the maketgz script. - * - * The format of the date should follow this template: - * - * "Mon Feb 12 11:35:33 UTC 2007" - */ -#define LIBSSH2_TIMESTAMP "Fri May 18 21:30:56 UTC 2012" - -#ifndef RC_INVOKED - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef _WIN32 -# include -# include -#endif - -#include -#include -#include -#include - -/* Allow alternate API prefix from CFLAGS or calling app */ -#ifndef LIBSSH2_API -# ifdef LIBSSH2_WIN32 -# ifdef LIBSSH2_LIBRARY -# define LIBSSH2_API __declspec(dllexport) -# else -# define LIBSSH2_API __declspec(dllimport) -# endif /* LIBSSH2_LIBRARY */ -# else /* !LIBSSH2_WIN32 */ -# define LIBSSH2_API -# endif /* LIBSSH2_WIN32 */ -#endif /* LIBSSH2_API */ - -#if defined(LIBSSH2_DARWIN) -# include -#endif - -#if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) -# include -typedef unsigned char uint8_t; -typedef unsigned int uint32_t; -#endif - -#ifdef _MSC_VER -typedef unsigned char uint8_t; -typedef unsigned int uint32_t; -typedef unsigned __int64 libssh2_uint64_t; -typedef __int64 libssh2_int64_t; -#ifndef ssize_t -typedef SSIZE_T ssize_t; -#endif -#else -typedef unsigned long long libssh2_uint64_t; -typedef long long libssh2_int64_t; -#endif - -#ifdef WIN32 -typedef SOCKET libssh2_socket_t; -#define LIBSSH2_INVALID_SOCKET INVALID_SOCKET -#else /* !WIN32 */ -typedef int libssh2_socket_t; -#define LIBSSH2_INVALID_SOCKET -1 -#endif /* WIN32 */ - -/* Part of every banner, user specified or not */ -#define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION - -/* We *could* add a comment here if we so chose */ -#define LIBSSH2_SSH_DEFAULT_BANNER LIBSSH2_SSH_BANNER -#define LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF LIBSSH2_SSH_DEFAULT_BANNER "\r\n" - -/* Default generate and safe prime sizes for diffie-hellman-group-exchange-sha1 */ -#define LIBSSH2_DH_GEX_MINGROUP 1024 -#define LIBSSH2_DH_GEX_OPTGROUP 1536 -#define LIBSSH2_DH_GEX_MAXGROUP 2048 - -/* Defaults for pty requests */ -#define LIBSSH2_TERM_WIDTH 80 -#define LIBSSH2_TERM_HEIGHT 24 -#define LIBSSH2_TERM_WIDTH_PX 0 -#define LIBSSH2_TERM_HEIGHT_PX 0 - -/* 1/4 second */ -#define LIBSSH2_SOCKET_POLL_UDELAY 250000 -/* 0.25 * 120 == 30 seconds */ -#define LIBSSH2_SOCKET_POLL_MAXLOOPS 120 - -/* Maximum size to allow a payload to compress to, plays it safe by falling - short of spec limits */ -#define LIBSSH2_PACKET_MAXCOMP 32000 - -/* Maximum size to allow a payload to deccompress to, plays it safe by - allowing more than spec requires */ -#define LIBSSH2_PACKET_MAXDECOMP 40000 - -/* Maximum size for an inbound compressed payload, plays it safe by - overshooting spec limits */ -#define LIBSSH2_PACKET_MAXPAYLOAD 40000 - -/* Malloc callbacks */ -#define LIBSSH2_ALLOC_FUNC(name) void *name(size_t count, void **abstract) -#define LIBSSH2_REALLOC_FUNC(name) void *name(void *ptr, size_t count, \ - void **abstract) -#define LIBSSH2_FREE_FUNC(name) void name(void *ptr, void **abstract) - -typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT -{ - char* text; - unsigned int length; - unsigned char echo; -} LIBSSH2_USERAUTH_KBDINT_PROMPT; - -typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE -{ - char* text; - unsigned int length; -} LIBSSH2_USERAUTH_KBDINT_RESPONSE; - -/* 'publickey' authentication callback */ -#define LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC(name) \ - int name(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, \ - const unsigned char *data, size_t data_len, void **abstract) - -/* 'keyboard-interactive' authentication callback */ -#define LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC(name_) \ - void name_(const char* name, int name_len, const char* instruction, \ - int instruction_len, int num_prompts, \ - const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts, \ - LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract) - -/* Callbacks for special SSH packets */ -#define LIBSSH2_IGNORE_FUNC(name) \ - void name(LIBSSH2_SESSION *session, const char *message, int message_len, \ - void **abstract) - -#define LIBSSH2_DEBUG_FUNC(name) \ - void name(LIBSSH2_SESSION *session, int always_display, const char *message, \ - int message_len, const char *language, int language_len, \ - void **abstract) - -#define LIBSSH2_DISCONNECT_FUNC(name) \ - void name(LIBSSH2_SESSION *session, int reason, const char *message, \ - int message_len, const char *language, int language_len, \ - void **abstract) - -#define LIBSSH2_PASSWD_CHANGEREQ_FUNC(name) \ - void name(LIBSSH2_SESSION *session, char **newpw, int *newpw_len, \ - void **abstract) - -#define LIBSSH2_MACERROR_FUNC(name) \ - int name(LIBSSH2_SESSION *session, const char *packet, int packet_len, \ - void **abstract) - -#define LIBSSH2_X11_OPEN_FUNC(name) \ - void name(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, \ - const char *shost, int sport, void **abstract) - -#define LIBSSH2_CHANNEL_CLOSE_FUNC(name) \ - void name(LIBSSH2_SESSION *session, void **session_abstract, \ - LIBSSH2_CHANNEL *channel, void **channel_abstract) - -/* I/O callbacks */ -#define LIBSSH2_RECV_FUNC(name) ssize_t name(libssh2_socket_t socket, \ - void *buffer, size_t length, \ - int flags, void **abstract) -#define LIBSSH2_SEND_FUNC(name) ssize_t name(libssh2_socket_t socket, \ - const void *buffer, size_t length,\ - int flags, void **abstract) - -/* libssh2_session_callback_set() constants */ -#define LIBSSH2_CALLBACK_IGNORE 0 -#define LIBSSH2_CALLBACK_DEBUG 1 -#define LIBSSH2_CALLBACK_DISCONNECT 2 -#define LIBSSH2_CALLBACK_MACERROR 3 -#define LIBSSH2_CALLBACK_X11 4 -#define LIBSSH2_CALLBACK_SEND 5 -#define LIBSSH2_CALLBACK_RECV 6 - -/* libssh2_session_method_pref() constants */ -#define LIBSSH2_METHOD_KEX 0 -#define LIBSSH2_METHOD_HOSTKEY 1 -#define LIBSSH2_METHOD_CRYPT_CS 2 -#define LIBSSH2_METHOD_CRYPT_SC 3 -#define LIBSSH2_METHOD_MAC_CS 4 -#define LIBSSH2_METHOD_MAC_SC 5 -#define LIBSSH2_METHOD_COMP_CS 6 -#define LIBSSH2_METHOD_COMP_SC 7 -#define LIBSSH2_METHOD_LANG_CS 8 -#define LIBSSH2_METHOD_LANG_SC 9 - -/* flags */ -#define LIBSSH2_FLAG_SIGPIPE 1 -#define LIBSSH2_FLAG_COMPRESS 2 - -typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION; -typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL; -typedef struct _LIBSSH2_LISTENER LIBSSH2_LISTENER; -typedef struct _LIBSSH2_KNOWNHOSTS LIBSSH2_KNOWNHOSTS; -typedef struct _LIBSSH2_AGENT LIBSSH2_AGENT; - -typedef struct _LIBSSH2_POLLFD { - unsigned char type; /* LIBSSH2_POLLFD_* below */ - - union { - int socket; /* File descriptors -- examined with system select() call */ - LIBSSH2_CHANNEL *channel; /* Examined by checking internal state */ - LIBSSH2_LISTENER *listener; /* Read polls only -- are inbound - connections waiting to be accepted? */ - } fd; - - unsigned long events; /* Requested Events */ - unsigned long revents; /* Returned Events */ -} LIBSSH2_POLLFD; - -/* Poll FD Descriptor Types */ -#define LIBSSH2_POLLFD_SOCKET 1 -#define LIBSSH2_POLLFD_CHANNEL 2 -#define LIBSSH2_POLLFD_LISTENER 3 - -/* Note: Win32 Doesn't actually have a poll() implementation, so some of these - values are faked with select() data */ -/* Poll FD events/revents -- Match sys/poll.h where possible */ -#define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or - connection available -- - All */ -#define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to - be read -- Socket only */ -#define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to - be read -- Channel only */ -#define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- - Socket/Channel */ -/* revents only */ -#define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */ -#define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket */ -#define LIBSSH2_POLLFD_SESSION_CLOSED 0x0010 /* Session Disconnect */ -#define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket - Only */ -#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- - Socket/Win32 */ -#define LIBSSH2_POLLFD_CHANNEL_CLOSED 0x0080 /* Channel Disconnect */ -#define LIBSSH2_POLLFD_LISTENER_CLOSED 0x0080 /* Listener Disconnect */ - -#define HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION -/* Block Direction Types */ -#define LIBSSH2_SESSION_BLOCK_INBOUND 0x0001 -#define LIBSSH2_SESSION_BLOCK_OUTBOUND 0x0002 - -/* Hash Types */ -#define LIBSSH2_HOSTKEY_HASH_MD5 1 -#define LIBSSH2_HOSTKEY_HASH_SHA1 2 - -/* Hostkey Types */ -#define LIBSSH2_HOSTKEY_TYPE_UNKNOWN 0 -#define LIBSSH2_HOSTKEY_TYPE_RSA 1 -#define LIBSSH2_HOSTKEY_TYPE_DSS 2 - -/* Disconnect Codes (defined by SSH protocol) */ -#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 -#define SSH_DISCONNECT_PROTOCOL_ERROR 2 -#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3 -#define SSH_DISCONNECT_RESERVED 4 -#define SSH_DISCONNECT_MAC_ERROR 5 -#define SSH_DISCONNECT_COMPRESSION_ERROR 6 -#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7 -#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 -#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 -#define SSH_DISCONNECT_CONNECTION_LOST 10 -#define SSH_DISCONNECT_BY_APPLICATION 11 -#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12 -#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13 -#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 -#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15 - -/* Error Codes (defined by libssh2) */ -#define LIBSSH2_ERROR_NONE 0 - -/* The library once used -1 as a generic error return value on numerous places - through the code, which subsequently was converted to - LIBSSH2_ERROR_SOCKET_NONE uses over time. As this is a generic error code, - the goal is to never ever return this code but instead make sure that a - more accurate and descriptive error code is used. */ -#define LIBSSH2_ERROR_SOCKET_NONE -1 - -#define LIBSSH2_ERROR_BANNER_RECV -2 -#define LIBSSH2_ERROR_BANNER_SEND -3 -#define LIBSSH2_ERROR_INVALID_MAC -4 -#define LIBSSH2_ERROR_KEX_FAILURE -5 -#define LIBSSH2_ERROR_ALLOC -6 -#define LIBSSH2_ERROR_SOCKET_SEND -7 -#define LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE -8 -#define LIBSSH2_ERROR_TIMEOUT -9 -#define LIBSSH2_ERROR_HOSTKEY_INIT -10 -#define LIBSSH2_ERROR_HOSTKEY_SIGN -11 -#define LIBSSH2_ERROR_DECRYPT -12 -#define LIBSSH2_ERROR_SOCKET_DISCONNECT -13 -#define LIBSSH2_ERROR_PROTO -14 -#define LIBSSH2_ERROR_PASSWORD_EXPIRED -15 -#define LIBSSH2_ERROR_FILE -16 -#define LIBSSH2_ERROR_METHOD_NONE -17 -#define LIBSSH2_ERROR_AUTHENTICATION_FAILED -18 -#define LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED LIBSSH2_ERROR_AUTHENTICATION_FAILED -#define LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED -19 -#define LIBSSH2_ERROR_CHANNEL_OUTOFORDER -20 -#define LIBSSH2_ERROR_CHANNEL_FAILURE -21 -#define LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED -22 -#define LIBSSH2_ERROR_CHANNEL_UNKNOWN -23 -#define LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED -24 -#define LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED -25 -#define LIBSSH2_ERROR_CHANNEL_CLOSED -26 -#define LIBSSH2_ERROR_CHANNEL_EOF_SENT -27 -#define LIBSSH2_ERROR_SCP_PROTOCOL -28 -#define LIBSSH2_ERROR_ZLIB -29 -#define LIBSSH2_ERROR_SOCKET_TIMEOUT -30 -#define LIBSSH2_ERROR_SFTP_PROTOCOL -31 -#define LIBSSH2_ERROR_REQUEST_DENIED -32 -#define LIBSSH2_ERROR_METHOD_NOT_SUPPORTED -33 -#define LIBSSH2_ERROR_INVAL -34 -#define LIBSSH2_ERROR_INVALID_POLL_TYPE -35 -#define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL -36 -#define LIBSSH2_ERROR_EAGAIN -37 -#define LIBSSH2_ERROR_BUFFER_TOO_SMALL -38 -#define LIBSSH2_ERROR_BAD_USE -39 -#define LIBSSH2_ERROR_COMPRESS -40 -#define LIBSSH2_ERROR_OUT_OF_BOUNDARY -41 -#define LIBSSH2_ERROR_AGENT_PROTOCOL -42 -#define LIBSSH2_ERROR_SOCKET_RECV -43 -#define LIBSSH2_ERROR_ENCRYPT -44 -#define LIBSSH2_ERROR_BAD_SOCKET -45 - -/* this is a define to provide the old (<= 1.2.7) name */ -#define LIBSSH2_ERROR_BANNER_NONE LIBSSH2_ERROR_BANNER_RECV - -/* Global API */ -#define LIBSSH2_INIT_NO_CRYPTO 0x0001 - -/* - * libssh2_init() - * - * Initialize the libssh2 functions. This typically initialize the - * crypto library. It uses a global state, and is not thread safe -- - * you must make sure this function is not called concurrently. - * - * Flags can be: - * 0: Normal initialize - * LIBSSH2_INIT_NO_CRYPTO: Do not initialize the crypto library (ie. - * OPENSSL_add_cipher_algoritms() for OpenSSL - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int libssh2_init(int flags); - -/* - * libssh2_exit() - * - * Exit the libssh2 functions and free's all memory used internal. - */ -LIBSSH2_API void libssh2_exit(void); - -/* - * libssh2_free() - * - * Deallocate memory allocated by earlier call to libssh2 functions. - */ -LIBSSH2_API void libssh2_free(LIBSSH2_SESSION *session, void *ptr); - -/* - * libssh2_session_supported_algs() - * - * Fills algs with a list of supported acryptographic algorithms. Returns a - * non-negative number (number of supported algorithms) on success or a - * negative number (an eror code) on failure. - * - * NOTE: on success, algs must be deallocated (by calling libssh2_free) when - * not needed anymore - */ -LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session, - int method_type, - const char*** algs); - -/* Session API */ -LIBSSH2_API LIBSSH2_SESSION * -libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), - LIBSSH2_FREE_FUNC((*my_free)), - LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract); -#define libssh2_session_init() libssh2_session_init_ex(NULL, NULL, NULL, NULL) - -LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session); - -LIBSSH2_API void *libssh2_session_callback_set(LIBSSH2_SESSION *session, - int cbtype, void *callback); -LIBSSH2_API int libssh2_session_banner_set(LIBSSH2_SESSION *session, - const char *banner); -LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, - const char *banner); - -LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int sock); -LIBSSH2_API int libssh2_session_handshake(LIBSSH2_SESSION *session, - libssh2_socket_t sock); -LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, - int reason, - const char *description, - const char *lang); -#define libssh2_session_disconnect(session, description) \ - libssh2_session_disconnect_ex((session), SSH_DISCONNECT_BY_APPLICATION, \ - (description), "") - -LIBSSH2_API int libssh2_session_free(LIBSSH2_SESSION *session); - -LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, - int hash_type); - -LIBSSH2_API const char *libssh2_session_hostkey(LIBSSH2_SESSION *session, - size_t *len, int *type); - -LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, - int method_type, - const char *prefs); -LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, - int method_type); -LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, - char **errmsg, - int *errmsg_len, int want_buf); -LIBSSH2_API int libssh2_session_last_errno(LIBSSH2_SESSION *session); -LIBSSH2_API int libssh2_session_block_directions(LIBSSH2_SESSION *session); - -LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, - int value); -LIBSSH2_API const char *libssh2_session_banner_get(LIBSSH2_SESSION *session); - -/* Userauth API */ -LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len); -LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session); - -LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len, - const char *password, - unsigned int password_len, - LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))); - -#define libssh2_userauth_password(session, username, password) \ - libssh2_userauth_password_ex((session), (username), strlen(username), \ - (password), strlen(password), NULL) - -LIBSSH2_API int -libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len, - const char *publickey, - const char *privatekey, - const char *passphrase); - -#define libssh2_userauth_publickey_fromfile(session, username, publickey, \ - privatekey, passphrase) \ - libssh2_userauth_publickey_fromfile_ex((session), (username), \ - strlen(username), (publickey), \ - (privatekey), (passphrase)) - -LIBSSH2_API int -libssh2_userauth_publickey(LIBSSH2_SESSION *session, - const char *username, - const unsigned char *pubkeydata, - size_t pubkeydata_len, - LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), - void **abstract); - -LIBSSH2_API int -libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len, - const char *publickey, - const char *privatekey, - const char *passphrase, - const char *hostname, - unsigned int hostname_len, - const char *local_username, - unsigned int local_username_len); - -#define libssh2_userauth_hostbased_fromfile(session, username, publickey, \ - privatekey, passphrase, hostname) \ - libssh2_userauth_hostbased_fromfile_ex((session), (username), \ - strlen(username), (publickey), \ - (privatekey), (passphrase), \ - (hostname), strlen(hostname), \ - (username), strlen(username)) - -/* - * response_callback is provided with filled by library prompts array, - * but client must allocate and fill individual responses. Responses - * array is already allocated. Responses data will be freed by libssh2 - * after callback return, but before subsequent callback invokation. - */ -LIBSSH2_API int -libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION* session, - const char *username, - unsigned int username_len, - LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))); - -#define libssh2_userauth_keyboard_interactive(session, username, \ - response_callback) \ - libssh2_userauth_keyboard_interactive_ex((session), (username), \ - strlen(username), (response_callback)) - -LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, - long timeout); - -/* Channel API */ -#define LIBSSH2_CHANNEL_WINDOW_DEFAULT (256*1024) -#define LIBSSH2_CHANNEL_PACKET_DEFAULT 32768 -#define LIBSSH2_CHANNEL_MINADJUST 1024 - -/* Extended Data Handling */ -#define LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL 0 -#define LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE 1 -#define LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE 2 - -#define SSH_EXTENDED_DATA_STDERR 1 - -/* Returned by any function that would block during a read/write opperation */ -#define LIBSSH2CHANNEL_EAGAIN LIBSSH2_ERROR_EAGAIN - -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, - unsigned int channel_type_len, - unsigned int window_size, unsigned int packet_size, - const char *message, unsigned int message_len); - -#define libssh2_channel_open_session(session) \ - libssh2_channel_open_ex((session), "session", sizeof("session") - 1, \ - LIBSSH2_CHANNEL_WINDOW_DEFAULT, \ - LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0) - -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host, - int port, const char *shost, int sport); -#define libssh2_channel_direct_tcpip(session, host, port) \ - libssh2_channel_direct_tcpip_ex((session), (host), (port), "127.0.0.1", 22) - -LIBSSH2_API LIBSSH2_LISTENER * -libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host, - int port, int *bound_port, int queue_maxsize); -#define libssh2_channel_forward_listen(session, port) \ - libssh2_channel_forward_listen_ex((session), NULL, (port), NULL, 16) - -LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener); - -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener); - -LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, - const char *varname, - unsigned int varname_len, - const char *value, - unsigned int value_len); - -#define libssh2_channel_setenv(channel, varname, value) \ - libssh2_channel_setenv_ex((channel), (varname), strlen(varname), (value), \ - strlen(value)) - -LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, - const char *term, - unsigned int term_len, - const char *modes, - unsigned int modes_len, - int width, int height, - int width_px, int height_px); -#define libssh2_channel_request_pty(channel, term) \ - libssh2_channel_request_pty_ex((channel), (term), strlen(term), NULL, 0, \ - LIBSSH2_TERM_WIDTH, LIBSSH2_TERM_HEIGHT, \ - LIBSSH2_TERM_WIDTH_PX, LIBSSH2_TERM_HEIGHT_PX) - -LIBSSH2_API int libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL *channel, - int width, int height, - int width_px, - int height_px); -#define libssh2_channel_request_pty_size(channel, width, height) \ - libssh2_channel_request_pty_size_ex( (channel), (width), (height), 0, 0) - -LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, - int single_connection, - const char *auth_proto, - const char *auth_cookie, - int screen_number); -#define libssh2_channel_x11_req(channel, screen_number) \ - libssh2_channel_x11_req_ex((channel), 0, NULL, NULL, (screen_number)) - -LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, - const char *request, - unsigned int request_len, - const char *message, - unsigned int message_len); -#define libssh2_channel_shell(channel) \ - libssh2_channel_process_startup((channel), "shell", sizeof("shell") - 1, \ - NULL, 0) -#define libssh2_channel_exec(channel, command) \ - libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, \ - (command), strlen(command)) -#define libssh2_channel_subsystem(channel, subsystem) \ - libssh2_channel_process_startup((channel), "subsystem", \ - sizeof("subsystem") - 1, (subsystem), \ - strlen(subsystem)) - -LIBSSH2_API ssize_t libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, - int stream_id, char *buf, - size_t buflen); -#define libssh2_channel_read(channel, buf, buflen) \ - libssh2_channel_read_ex((channel), 0, (buf), (buflen)) -#define libssh2_channel_read_stderr(channel, buf, buflen) \ - libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) - -LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, - int extended); - -LIBSSH2_API unsigned long -libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, - unsigned long *read_avail, - unsigned long *window_size_initial); -#define libssh2_channel_window_read(channel) \ - libssh2_channel_window_read_ex((channel), NULL, NULL) - -/* libssh2_channel_receive_window_adjust is DEPRECATED, do not use! */ -LIBSSH2_API unsigned long -libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, - unsigned long adjustment, - unsigned char force); - -LIBSSH2_API int -libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel, - unsigned long adjustment, - unsigned char force, - unsigned int *storewindow); - -LIBSSH2_API ssize_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, - int stream_id, const char *buf, - size_t buflen); - -#define libssh2_channel_write(channel, buf, buflen) \ - libssh2_channel_write_ex((channel), 0, (buf), (buflen)) -#define libssh2_channel_write_stderr(channel, buf, buflen) \ - libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) - -LIBSSH2_API unsigned long -libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, - unsigned long *window_size_initial); -#define libssh2_channel_window_write(channel) \ - libssh2_channel_window_write_ex((channel), NULL) - -LIBSSH2_API void libssh2_session_set_blocking(LIBSSH2_SESSION* session, - int blocking); -LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION* session); - -LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, - int blocking); - -LIBSSH2_API void libssh2_session_set_timeout(LIBSSH2_SESSION* session, - long timeout); -LIBSSH2_API long libssh2_session_get_timeout(LIBSSH2_SESSION* session); - -/* libssh2_channel_handle_extended_data is DEPRECATED, do not use! */ -LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, - int ignore_mode); -LIBSSH2_API int libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, - int ignore_mode); - -/* libssh2_channel_ignore_extended_data() is defined below for BC with version - * 0.1 - * - * Future uses should use libssh2_channel_handle_extended_data() directly if - * LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE is passed, extended data will be read - * (FIFO) from the standard data channel - */ -/* DEPRECATED */ -#define libssh2_channel_ignore_extended_data(channel, ignore) \ - libssh2_channel_handle_extended_data((channel), \ - (ignore) ? \ - LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE : \ - LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL ) - -#define LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA -1 -#define LIBSSH2_CHANNEL_FLUSH_ALL -2 -LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, - int streamid); -#define libssh2_channel_flush(channel) libssh2_channel_flush_ex((channel), 0) -#define libssh2_channel_flush_stderr(channel) \ - libssh2_channel_flush_ex((channel), SSH_EXTENDED_DATA_STDERR) - -LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel); -LIBSSH2_API int libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL* channel, - char **exitsignal, - size_t *exitsignal_len, - char **errmsg, - size_t *errmsg_len, - char **langtag, - size_t *langtag_len); -LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel); -LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel); -LIBSSH2_API int libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel); -LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel); -LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel); -LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel); - -LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, - const char *path, - struct stat *sb); -LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, - const char *path, int mode, - size_t size, long mtime, - long atime); -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_scp_send64(LIBSSH2_SESSION *session, const char *path, int mode, - libssh2_int64_t size, time_t mtime, time_t atime); - -#define libssh2_scp_send(session, path, mode, size) \ - libssh2_scp_send_ex((session), (path), (mode), (size), 0, 0) - -LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **dest, - unsigned int *dest_len, - const char *src, unsigned int src_len); - -LIBSSH2_API -const char *libssh2_version(int req_version_num); - -#define HAVE_LIBSSH2_KNOWNHOST_API 0x010101 /* since 1.1.1 */ -#define HAVE_LIBSSH2_VERSION_API 0x010100 /* libssh2_version since 1.1 */ - -struct libssh2_knownhost { - unsigned int magic; /* magic stored by the library */ - void *node; /* handle to the internal representation of this host */ - char *name; /* this is NULL if no plain text host name exists */ - char *key; /* key in base64/printable format */ - int typemask; -}; - -/* - * libssh2_knownhost_init - * - * Init a collection of known hosts. Returns the pointer to a collection. - * - */ -LIBSSH2_API LIBSSH2_KNOWNHOSTS * -libssh2_knownhost_init(LIBSSH2_SESSION *session); - -/* - * libssh2_knownhost_add - * - * Add a host and its associated key to the collection of known hosts. - * - * The 'type' argument specifies on what format the given host and keys are: - * - * plain - ascii "hostname.domain.tld" - * sha1 - SHA1( ) base64-encoded! - * custom - another hash - * - * If 'sha1' is selected as type, the salt must be provided to the salt - * argument. This too base64 encoded. - * - * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If - * a custom type is used, salt is ignored and you must provide the host - * pre-hashed when checking for it in the libssh2_knownhost_check() function. - * - * The keylen parameter may be omitted (zero) if the key is provided as a - * NULL-terminated base64-encoded string. - */ - -/* host format (2 bits) */ -#define LIBSSH2_KNOWNHOST_TYPE_MASK 0xffff -#define LIBSSH2_KNOWNHOST_TYPE_PLAIN 1 -#define LIBSSH2_KNOWNHOST_TYPE_SHA1 2 /* always base64 encoded */ -#define LIBSSH2_KNOWNHOST_TYPE_CUSTOM 3 - -/* key format (2 bits) */ -#define LIBSSH2_KNOWNHOST_KEYENC_MASK (3<<16) -#define LIBSSH2_KNOWNHOST_KEYENC_RAW (1<<16) -#define LIBSSH2_KNOWNHOST_KEYENC_BASE64 (2<<16) - -/* type of key (2 bits) */ -#define LIBSSH2_KNOWNHOST_KEY_MASK (3<<18) -#define LIBSSH2_KNOWNHOST_KEY_SHIFT 18 -#define LIBSSH2_KNOWNHOST_KEY_RSA1 (1<<18) -#define LIBSSH2_KNOWNHOST_KEY_SSHRSA (2<<18) -#define LIBSSH2_KNOWNHOST_KEY_SSHDSS (3<<18) - -LIBSSH2_API int -libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, - const char *salt, - const char *key, size_t keylen, int typemask, - struct libssh2_knownhost **store); - -/* - * libssh2_knownhost_addc - * - * Add a host and its associated key to the collection of known hosts. - * - * Takes a comment argument that may be NULL. A NULL comment indicates - * there is no comment and the entry will end directly after the key - * when written out to a file. An empty string "" comment will indicate an - * empty comment which will cause a single space to be written after the key. - * - * The 'type' argument specifies on what format the given host and keys are: - * - * plain - ascii "hostname.domain.tld" - * sha1 - SHA1( ) base64-encoded! - * custom - another hash - * - * If 'sha1' is selected as type, the salt must be provided to the salt - * argument. This too base64 encoded. - * - * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If - * a custom type is used, salt is ignored and you must provide the host - * pre-hashed when checking for it in the libssh2_knownhost_check() function. - * - * The keylen parameter may be omitted (zero) if the key is provided as a - * NULL-terminated base64-encoded string. - */ - -LIBSSH2_API int -libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, - const char *salt, - const char *key, size_t keylen, - const char *comment, size_t commentlen, int typemask, - struct libssh2_knownhost **store); - -/* - * libssh2_knownhost_check - * - * Check a host and its associated key against the collection of known hosts. - * - * The type is the type/format of the given host name. - * - * plain - ascii "hostname.domain.tld" - * custom - prehashed base64 encoded. Note that this cannot use any salts. - * - * - * 'knownhost' may be set to NULL if you don't care about that info. - * - * Returns: - * - * LIBSSH2_KNOWNHOST_CHECK_* values, see below - * - */ - -#define LIBSSH2_KNOWNHOST_CHECK_MATCH 0 -#define LIBSSH2_KNOWNHOST_CHECK_MISMATCH 1 -#define LIBSSH2_KNOWNHOST_CHECK_NOTFOUND 2 -#define LIBSSH2_KNOWNHOST_CHECK_FAILURE 3 - -LIBSSH2_API int -libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, const char *key, size_t keylen, - int typemask, - struct libssh2_knownhost **knownhost); - -/* this function is identital to the above one, but also takes a port - argument that allows libssh2 to do a better check */ -LIBSSH2_API int -libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, int port, - const char *key, size_t keylen, - int typemask, - struct libssh2_knownhost **knownhost); - -/* - * libssh2_knownhost_del - * - * Remove a host from the collection of known hosts. The 'entry' struct is - * retrieved by a call to libssh2_knownhost_check(). - * - */ -LIBSSH2_API int -libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts, - struct libssh2_knownhost *entry); - -/* - * libssh2_knownhost_free - * - * Free an entire collection of known hosts. - * - */ -LIBSSH2_API void -libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts); - -/* - * libssh2_knownhost_readline() - * - * Pass in a line of a file of 'type'. It makes libssh2 read this line. - * - * LIBSSH2_KNOWNHOST_FILE_OPENSSH is the only supported type. - * - */ -LIBSSH2_API int -libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS *hosts, - const char *line, size_t len, int type); - -/* - * libssh2_knownhost_readfile - * - * Add hosts+key pairs from a given file. - * - * Returns a negative value for error or number of successfully added hosts. - * - * This implementation currently only knows one 'type' (openssh), all others - * are reserved for future use. - */ - -#define LIBSSH2_KNOWNHOST_FILE_OPENSSH 1 - -LIBSSH2_API int -libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts, - const char *filename, int type); - -/* - * libssh2_knownhost_writeline() - * - * Ask libssh2 to convert a known host to an output line for storage. - * - * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given - * output buffer is too small to hold the desired output. - * - * This implementation currently only knows one 'type' (openssh), all others - * are reserved for future use. - * - */ -LIBSSH2_API int -libssh2_knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, - struct libssh2_knownhost *known, - char *buffer, size_t buflen, - size_t *outlen, /* the amount of written data */ - int type); - -/* - * libssh2_knownhost_writefile - * - * Write hosts+key pairs to a given file. - * - * This implementation currently only knows one 'type' (openssh), all others - * are reserved for future use. - */ - -LIBSSH2_API int -libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts, - const char *filename, int type); - -/* - * libssh2_knownhost_get() - * - * Traverse the internal list of known hosts. Pass NULL to 'prev' to get - * the first one. Or pass a poiner to the previously returned one to get the - * next. - * - * Returns: - * 0 if a fine host was stored in 'store' - * 1 if end of hosts - * [negative] on errors - */ -LIBSSH2_API int -libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS *hosts, - struct libssh2_knownhost **store, - struct libssh2_knownhost *prev); - -#define HAVE_LIBSSH2_AGENT_API 0x010202 /* since 1.2.2 */ - -struct libssh2_agent_publickey { - unsigned int magic; /* magic stored by the library */ - void *node; /* handle to the internal representation of key */ - unsigned char *blob; /* public key blob */ - size_t blob_len; /* length of the public key blob */ - char *comment; /* comment in printable format */ -}; - -/* - * libssh2_agent_init - * - * Init an ssh-agent handle. Returns the pointer to the handle. - * - */ -LIBSSH2_API LIBSSH2_AGENT * -libssh2_agent_init(LIBSSH2_SESSION *session); - -/* - * libssh2_agent_connect() - * - * Connect to an ssh-agent. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_connect(LIBSSH2_AGENT *agent); - -/* - * libssh2_agent_list_identities() - * - * Request an ssh-agent to list identities. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_list_identities(LIBSSH2_AGENT *agent); - -/* - * libssh2_agent_get_identity() - * - * Traverse the internal list of public keys. Pass NULL to 'prev' to get - * the first one. Or pass a poiner to the previously returned one to get the - * next. - * - * Returns: - * 0 if a fine public key was stored in 'store' - * 1 if end of public keys - * [negative] on errors - */ -LIBSSH2_API int -libssh2_agent_get_identity(LIBSSH2_AGENT *agent, - struct libssh2_agent_publickey **store, - struct libssh2_agent_publickey *prev); - -/* - * libssh2_agent_userauth() - * - * Do publickey user authentication with the help of ssh-agent. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_userauth(LIBSSH2_AGENT *agent, - const char *username, - struct libssh2_agent_publickey *identity); - -/* - * libssh2_agent_disconnect() - * - * Close a connection to an ssh-agent. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_disconnect(LIBSSH2_AGENT *agent); - -/* - * libssh2_agent_free() - * - * Free an ssh-agent handle. This function also frees the internal - * collection of public keys. - */ -LIBSSH2_API void -libssh2_agent_free(LIBSSH2_AGENT *agent); - - -/* - * libssh2_keepalive_config() - * - * Set how often keepalive messages should be sent. WANT_REPLY - * indicates whether the keepalive messages should request a response - * from the server. INTERVAL is number of seconds that can pass - * without any I/O, use 0 (the default) to disable keepalives. To - * avoid some busy-loop corner-cases, if you specify an interval of 1 - * it will be treated as 2. - * - * Note that non-blocking applications are responsible for sending the - * keepalive messages using libssh2_keepalive_send(). - */ -LIBSSH2_API void libssh2_keepalive_config (LIBSSH2_SESSION *session, - int want_reply, - unsigned interval); - -/* - * libssh2_keepalive_send() - * - * Send a keepalive message if needed. SECONDS_TO_NEXT indicates how - * many seconds you can sleep after this call before you need to call - * it again. Returns 0 on success, or LIBSSH2_ERROR_SOCKET_SEND on - * I/O errors. - */ -LIBSSH2_API int libssh2_keepalive_send (LIBSSH2_SESSION *session, - int *seconds_to_next); - -/* NOTE NOTE NOTE - libssh2_trace() has no function in builds that aren't built with debug - enabled - */ -LIBSSH2_API int libssh2_trace(LIBSSH2_SESSION *session, int bitmask); -#define LIBSSH2_TRACE_TRANS (1<<1) -#define LIBSSH2_TRACE_KEX (1<<2) -#define LIBSSH2_TRACE_AUTH (1<<3) -#define LIBSSH2_TRACE_CONN (1<<4) -#define LIBSSH2_TRACE_SCP (1<<5) -#define LIBSSH2_TRACE_SFTP (1<<6) -#define LIBSSH2_TRACE_ERROR (1<<7) -#define LIBSSH2_TRACE_PUBLICKEY (1<<8) -#define LIBSSH2_TRACE_SOCKET (1<<9) - -typedef void (*libssh2_trace_handler_func)(LIBSSH2_SESSION*, - void*, - const char *, - size_t); -LIBSSH2_API int libssh2_trace_sethandler(LIBSSH2_SESSION *session, - void* context, - libssh2_trace_handler_func callback); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* !RC_INVOKED */ - -#endif /* LIBSSH2_H */ diff --git a/vendor/libssh2-1.4.2/include/libssh2_publickey.h b/vendor/libssh2-1.4.2/include/libssh2_publickey.h deleted file mode 100644 index 7350e9f3b..000000000 --- a/vendor/libssh2-1.4.2/include/libssh2_publickey.h +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (c) 2004-2006, Sara Golemon - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -/* Note: This include file is only needed for using the - * publickey SUBSYSTEM which is not the same as publickey - * authentication. For authentication you only need libssh2.h - * - * For more information on the publickey subsystem, - * refer to IETF draft: secsh-publickey - */ - -#ifndef LIBSSH2_PUBLICKEY_H -#define LIBSSH2_PUBLICKEY_H 1 - -#include "libssh2.h" - -typedef struct _LIBSSH2_PUBLICKEY LIBSSH2_PUBLICKEY; - -typedef struct _libssh2_publickey_attribute { - const char *name; - unsigned long name_len; - const char *value; - unsigned long value_len; - char mandatory; -} libssh2_publickey_attribute; - -typedef struct _libssh2_publickey_list { - unsigned char *packet; /* For freeing */ - - const unsigned char *name; - unsigned long name_len; - const unsigned char *blob; - unsigned long blob_len; - unsigned long num_attrs; - libssh2_publickey_attribute *attrs; /* free me */ -} libssh2_publickey_list; - -/* Generally use the first macro here, but if both name and value are string literals, you can use _fast() to take advantage of preprocessing */ -#define libssh2_publickey_attribute(name, value, mandatory) \ - { (name), strlen(name), (value), strlen(value), (mandatory) }, -#define libssh2_publickey_attribute_fast(name, value, mandatory) \ - { (name), sizeof(name) - 1, (value), sizeof(value) - 1, (mandatory) }, - -#ifdef __cplusplus -extern "C" { -#endif - -/* Publickey Subsystem */ -LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session); - -LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, - const unsigned char *name, - unsigned long name_len, - const unsigned char *blob, - unsigned long blob_len, char overwrite, - unsigned long num_attrs, - const libssh2_publickey_attribute attrs[]); -#define libssh2_publickey_add(pkey, name, blob, blob_len, overwrite, \ - num_attrs, attrs) \ - libssh2_publickey_add_ex((pkey), (name), strlen(name), (blob), (blob_len), \ - (overwrite), (num_attrs), (attrs)) - -LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, - const unsigned char *name, - unsigned long name_len, - const unsigned char *blob, - unsigned long blob_len); -#define libssh2_publickey_remove(pkey, name, blob, blob_len) \ - libssh2_publickey_remove_ex((pkey), (name), strlen(name), (blob), (blob_len)) - -LIBSSH2_API int -libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, - unsigned long *num_keys, - libssh2_publickey_list **pkey_list); -LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, - libssh2_publickey_list *pkey_list); - -LIBSSH2_API int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ifndef: LIBSSH2_PUBLICKEY_H */ diff --git a/vendor/libssh2-1.4.2/include/libssh2_sftp.h b/vendor/libssh2-1.4.2/include/libssh2_sftp.h deleted file mode 100644 index 74884fbfe..000000000 --- a/vendor/libssh2-1.4.2/include/libssh2_sftp.h +++ /dev/null @@ -1,345 +0,0 @@ -/* Copyright (c) 2004-2008, Sara Golemon - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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 LIBSSH2_SFTP_H -#define LIBSSH2_SFTP_H 1 - -#include "libssh2.h" - -#ifndef WIN32 -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Note: Version 6 was documented at the time of writing - * However it was marked as "DO NOT IMPLEMENT" due to pending changes - * - * Let's start with Version 3 (The version found in OpenSSH) and go from there - */ -#define LIBSSH2_SFTP_VERSION 3 - -typedef struct _LIBSSH2_SFTP LIBSSH2_SFTP; -typedef struct _LIBSSH2_SFTP_HANDLE LIBSSH2_SFTP_HANDLE; -typedef struct _LIBSSH2_SFTP_ATTRIBUTES LIBSSH2_SFTP_ATTRIBUTES; -typedef struct _LIBSSH2_SFTP_STATVFS LIBSSH2_SFTP_STATVFS; - -/* Flags for open_ex() */ -#define LIBSSH2_SFTP_OPENFILE 0 -#define LIBSSH2_SFTP_OPENDIR 1 - -/* Flags for rename_ex() */ -#define LIBSSH2_SFTP_RENAME_OVERWRITE 0x00000001 -#define LIBSSH2_SFTP_RENAME_ATOMIC 0x00000002 -#define LIBSSH2_SFTP_RENAME_NATIVE 0x00000004 - -/* Flags for stat_ex() */ -#define LIBSSH2_SFTP_STAT 0 -#define LIBSSH2_SFTP_LSTAT 1 -#define LIBSSH2_SFTP_SETSTAT 2 - -/* Flags for symlink_ex() */ -#define LIBSSH2_SFTP_SYMLINK 0 -#define LIBSSH2_SFTP_READLINK 1 -#define LIBSSH2_SFTP_REALPATH 2 - -/* SFTP attribute flag bits */ -#define LIBSSH2_SFTP_ATTR_SIZE 0x00000001 -#define LIBSSH2_SFTP_ATTR_UIDGID 0x00000002 -#define LIBSSH2_SFTP_ATTR_PERMISSIONS 0x00000004 -#define LIBSSH2_SFTP_ATTR_ACMODTIME 0x00000008 -#define LIBSSH2_SFTP_ATTR_EXTENDED 0x80000000 - -/* SFTP statvfs flag bits */ -#define LIBSSH2_SFTP_ST_RDONLY 0x00000001 -#define LIBSSH2_SFTP_ST_NOSUID 0x00000002 - -struct _LIBSSH2_SFTP_ATTRIBUTES { - /* If flags & ATTR_* bit is set, then the value in this struct will be - * meaningful Otherwise it should be ignored - */ - unsigned long flags; - - libssh2_uint64_t filesize; - unsigned long uid, gid; - unsigned long permissions; - unsigned long atime, mtime; -}; - -struct _LIBSSH2_SFTP_STATVFS { - libssh2_uint64_t f_bsize; /* file system block size */ - libssh2_uint64_t f_frsize; /* fragment size */ - libssh2_uint64_t f_blocks; /* size of fs in f_frsize units */ - libssh2_uint64_t f_bfree; /* # free blocks */ - libssh2_uint64_t f_bavail; /* # free blocks for non-root */ - libssh2_uint64_t f_files; /* # inodes */ - libssh2_uint64_t f_ffree; /* # free inodes */ - libssh2_uint64_t f_favail; /* # free inodes for non-root */ - libssh2_uint64_t f_fsid; /* file system ID */ - libssh2_uint64_t f_flag; /* mount flags */ - libssh2_uint64_t f_namemax; /* maximum filename length */ -}; - -/* SFTP filetypes */ -#define LIBSSH2_SFTP_TYPE_REGULAR 1 -#define LIBSSH2_SFTP_TYPE_DIRECTORY 2 -#define LIBSSH2_SFTP_TYPE_SYMLINK 3 -#define LIBSSH2_SFTP_TYPE_SPECIAL 4 -#define LIBSSH2_SFTP_TYPE_UNKNOWN 5 -#define LIBSSH2_SFTP_TYPE_SOCKET 6 -#define LIBSSH2_SFTP_TYPE_CHAR_DEVICE 7 -#define LIBSSH2_SFTP_TYPE_BLOCK_DEVICE 8 -#define LIBSSH2_SFTP_TYPE_FIFO 9 - -/* - * Reproduce the POSIX file modes here for systems that are not POSIX - * compliant. - * - * These is used in "permissions" of "struct _LIBSSH2_SFTP_ATTRIBUTES" - */ -/* File type */ -#define LIBSSH2_SFTP_S_IFMT 0170000 /* type of file mask */ -#define LIBSSH2_SFTP_S_IFIFO 0010000 /* named pipe (fifo) */ -#define LIBSSH2_SFTP_S_IFCHR 0020000 /* character special */ -#define LIBSSH2_SFTP_S_IFDIR 0040000 /* directory */ -#define LIBSSH2_SFTP_S_IFBLK 0060000 /* block special */ -#define LIBSSH2_SFTP_S_IFREG 0100000 /* regular */ -#define LIBSSH2_SFTP_S_IFLNK 0120000 /* symbolic link */ -#define LIBSSH2_SFTP_S_IFSOCK 0140000 /* socket */ - -/* File mode */ -/* Read, write, execute/search by owner */ -#define LIBSSH2_SFTP_S_IRWXU 0000700 /* RWX mask for owner */ -#define LIBSSH2_SFTP_S_IRUSR 0000400 /* R for owner */ -#define LIBSSH2_SFTP_S_IWUSR 0000200 /* W for owner */ -#define LIBSSH2_SFTP_S_IXUSR 0000100 /* X for owner */ -/* Read, write, execute/search by group */ -#define LIBSSH2_SFTP_S_IRWXG 0000070 /* RWX mask for group */ -#define LIBSSH2_SFTP_S_IRGRP 0000040 /* R for group */ -#define LIBSSH2_SFTP_S_IWGRP 0000020 /* W for group */ -#define LIBSSH2_SFTP_S_IXGRP 0000010 /* X for group */ -/* Read, write, execute/search by others */ -#define LIBSSH2_SFTP_S_IRWXO 0000007 /* RWX mask for other */ -#define LIBSSH2_SFTP_S_IROTH 0000004 /* R for other */ -#define LIBSSH2_SFTP_S_IWOTH 0000002 /* W for other */ -#define LIBSSH2_SFTP_S_IXOTH 0000001 /* X for other */ - -/* macros to check for specific file types, added in 1.2.5 */ -#define LIBSSH2_SFTP_S_ISLNK(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFLNK) -#define LIBSSH2_SFTP_S_ISREG(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFREG) -#define LIBSSH2_SFTP_S_ISDIR(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFDIR) -#define LIBSSH2_SFTP_S_ISCHR(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFCHR) -#define LIBSSH2_SFTP_S_ISBLK(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFBLK) -#define LIBSSH2_SFTP_S_ISFIFO(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFIFO) -#define LIBSSH2_SFTP_S_ISSOCK(m) \ - (((m) & LIBSSH2_SFTP_S_IFMT) == LIBSSH2_SFTP_S_IFSOCK) - -/* SFTP File Transfer Flags -- (e.g. flags parameter to sftp_open()) - * Danger will robinson... APPEND doesn't have any effect on OpenSSH servers */ -#define LIBSSH2_FXF_READ 0x00000001 -#define LIBSSH2_FXF_WRITE 0x00000002 -#define LIBSSH2_FXF_APPEND 0x00000004 -#define LIBSSH2_FXF_CREAT 0x00000008 -#define LIBSSH2_FXF_TRUNC 0x00000010 -#define LIBSSH2_FXF_EXCL 0x00000020 - -/* SFTP Status Codes (returned by libssh2_sftp_last_error() ) */ -#define LIBSSH2_FX_OK 0 -#define LIBSSH2_FX_EOF 1 -#define LIBSSH2_FX_NO_SUCH_FILE 2 -#define LIBSSH2_FX_PERMISSION_DENIED 3 -#define LIBSSH2_FX_FAILURE 4 -#define LIBSSH2_FX_BAD_MESSAGE 5 -#define LIBSSH2_FX_NO_CONNECTION 6 -#define LIBSSH2_FX_CONNECTION_LOST 7 -#define LIBSSH2_FX_OP_UNSUPPORTED 8 -#define LIBSSH2_FX_INVALID_HANDLE 9 -#define LIBSSH2_FX_NO_SUCH_PATH 10 -#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11 -#define LIBSSH2_FX_WRITE_PROTECT 12 -#define LIBSSH2_FX_NO_MEDIA 13 -#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14 -#define LIBSSH2_FX_QUOTA_EXCEEDED 15 -#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 /* Initial mis-spelling */ -#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16 -#define LIBSSH2_FX_LOCK_CONFlICT 17 /* Initial mis-spelling */ -#define LIBSSH2_FX_LOCK_CONFLICT 17 -#define LIBSSH2_FX_DIR_NOT_EMPTY 18 -#define LIBSSH2_FX_NOT_A_DIRECTORY 19 -#define LIBSSH2_FX_INVALID_FILENAME 20 -#define LIBSSH2_FX_LINK_LOOP 21 - -/* Returned by any function that would block during a read/write opperation */ -#define LIBSSH2SFTP_EAGAIN LIBSSH2_ERROR_EAGAIN - -/* SFTP API */ -LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session); -LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp); -LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp); -LIBSSH2_API LIBSSH2_CHANNEL *libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp); - -/* File / Directory Ops */ -LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, - const char *filename, - unsigned int filename_len, - unsigned long flags, - long mode, int open_type); -#define libssh2_sftp_open(sftp, filename, flags, mode) \ - libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), \ - (mode), LIBSSH2_SFTP_OPENFILE) -#define libssh2_sftp_opendir(sftp, path) \ - libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, \ - LIBSSH2_SFTP_OPENDIR) - -LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, - char *buffer, size_t buffer_maxlen); - -LIBSSH2_API int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle, \ - char *buffer, size_t buffer_maxlen, - char *longentry, - size_t longentry_maxlen, - LIBSSH2_SFTP_ATTRIBUTES *attrs); -#define libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs) \ - libssh2_sftp_readdir_ex((handle), (buffer), (buffer_maxlen), NULL, 0, \ - (attrs)) - -LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, - const char *buffer, size_t count); - -LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle); -#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle) -#define libssh2_sftp_closedir(handle) libssh2_sftp_close_handle(handle) - -LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset); -LIBSSH2_API void libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, - libssh2_uint64_t offset); -#define libssh2_sftp_rewind(handle) libssh2_sftp_seek64((handle), 0) - -LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle); -LIBSSH2_API libssh2_uint64_t libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle); - -LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, - LIBSSH2_SFTP_ATTRIBUTES *attrs, - int setstat); -#define libssh2_sftp_fstat(handle, attrs) \ - libssh2_sftp_fstat_ex((handle), (attrs), 0) -#define libssh2_sftp_fsetstat(handle, attrs) \ - libssh2_sftp_fstat_ex((handle), (attrs), 1) - -/* Miscellaneous Ops */ -LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, - const char *source_filename, - unsigned int srouce_filename_len, - const char *dest_filename, - unsigned int dest_filename_len, - long flags); -#define libssh2_sftp_rename(sftp, sourcefile, destfile) \ - libssh2_sftp_rename_ex((sftp), (sourcefile), strlen(sourcefile), \ - (destfile), strlen(destfile), \ - LIBSSH2_SFTP_RENAME_OVERWRITE | \ - LIBSSH2_SFTP_RENAME_ATOMIC | \ - LIBSSH2_SFTP_RENAME_NATIVE) - -LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, - const char *filename, - unsigned int filename_len); -#define libssh2_sftp_unlink(sftp, filename) \ - libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename)) - -LIBSSH2_API int libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, - LIBSSH2_SFTP_STATVFS *st); - -LIBSSH2_API int libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp, - const char *path, - size_t path_len, - LIBSSH2_SFTP_STATVFS *st); - -LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, - const char *path, - unsigned int path_len, long mode); -#define libssh2_sftp_mkdir(sftp, path, mode) \ - libssh2_sftp_mkdir_ex((sftp), (path), strlen(path), (mode)) - -LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, - const char *path, - unsigned int path_len); -#define libssh2_sftp_rmdir(sftp, path) \ - libssh2_sftp_rmdir_ex((sftp), (path), strlen(path)) - -LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, - const char *path, - unsigned int path_len, - int stat_type, - LIBSSH2_SFTP_ATTRIBUTES *attrs); -#define libssh2_sftp_stat(sftp, path, attrs) \ - libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_STAT, \ - (attrs)) -#define libssh2_sftp_lstat(sftp, path, attrs) \ - libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_LSTAT, \ - (attrs)) -#define libssh2_sftp_setstat(sftp, path, attrs) \ - libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_SETSTAT, \ - (attrs)) - -LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, - const char *path, - unsigned int path_len, - char *target, - unsigned int target_len, int link_type); -#define libssh2_sftp_symlink(sftp, orig, linkpath) \ - libssh2_sftp_symlink_ex((sftp), (orig), strlen(orig), (linkpath), \ - strlen(linkpath), LIBSSH2_SFTP_SYMLINK) -#define libssh2_sftp_readlink(sftp, path, target, maxlen) \ - libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), \ - LIBSSH2_SFTP_READLINK) -#define libssh2_sftp_realpath(sftp, path, target, maxlen) \ - libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), \ - LIBSSH2_SFTP_REALPATH) - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LIBSSH2_SFTP_H */ diff --git a/vendor/libssh2-1.4.2/src/agent.c b/vendor/libssh2-1.4.2/src/agent.c deleted file mode 100644 index 1c65149a6..000000000 --- a/vendor/libssh2-1.4.2/src/agent.c +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright (c) 2009 by Daiki Ueno - * Copyright (C) 2010 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include "misc.h" -#include -#ifdef HAVE_SYS_UN_H -#include -#else -/* Use the existence of sys/un.h as a test if Unix domain socket is - supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually - support them. */ -#undef PF_UNIX -#endif -#include "userauth.h" -#include "session.h" - -/* Requests from client to agent for protocol 1 key operations */ -#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 -#define SSH_AGENTC_RSA_CHALLENGE 3 -#define SSH_AGENTC_ADD_RSA_IDENTITY 7 -#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 -#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 -#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 - -/* Requests from client to agent for protocol 2 key operations */ -#define SSH2_AGENTC_REQUEST_IDENTITIES 11 -#define SSH2_AGENTC_SIGN_REQUEST 13 -#define SSH2_AGENTC_ADD_IDENTITY 17 -#define SSH2_AGENTC_REMOVE_IDENTITY 18 -#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 -#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 - -/* Key-type independent requests from client to agent */ -#define SSH_AGENTC_ADD_SMARTCARD_KEY 20 -#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 -#define SSH_AGENTC_LOCK 22 -#define SSH_AGENTC_UNLOCK 23 -#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 - -/* Generic replies from agent to client */ -#define SSH_AGENT_FAILURE 5 -#define SSH_AGENT_SUCCESS 6 - -/* Replies from agent to client for protocol 1 key operations */ -#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 -#define SSH_AGENT_RSA_RESPONSE 4 - -/* Replies from agent to client for protocol 2 key operations */ -#define SSH2_AGENT_IDENTITIES_ANSWER 12 -#define SSH2_AGENT_SIGN_RESPONSE 14 - -/* Key constraint identifiers */ -#define SSH_AGENT_CONSTRAIN_LIFETIME 1 -#define SSH_AGENT_CONSTRAIN_CONFIRM 2 - -/* non-blocking mode on agent connection is not yet implemented, but - for future use. */ -typedef enum { - agent_NB_state_init = 0, - agent_NB_state_request_created, - agent_NB_state_request_length_sent, - agent_NB_state_request_sent, - agent_NB_state_response_length_received, - agent_NB_state_response_received -} agent_nonblocking_states; - -typedef struct agent_transaction_ctx { - unsigned char *request; - size_t request_len; - unsigned char *response; - size_t response_len; - agent_nonblocking_states state; -} *agent_transaction_ctx_t; - -typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent); -typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent, - agent_transaction_ctx_t transctx); -typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent); - -struct agent_publickey { - struct list_node node; - - /* this is the struct we expose externally */ - struct libssh2_agent_publickey external; -}; - -struct agent_ops { - agent_connect_func connect; - agent_transact_func transact; - agent_disconnect_func disconnect; -}; - -struct _LIBSSH2_AGENT -{ - LIBSSH2_SESSION *session; /* the session this "belongs to" */ - - libssh2_socket_t fd; - - struct agent_ops *ops; - - struct agent_transaction_ctx transctx; - struct agent_publickey *identity; - struct list_head head; /* list of public keys */ -}; - -#ifdef PF_UNIX -static int -agent_connect_unix(LIBSSH2_AGENT *agent) -{ - const char *path; - struct sockaddr_un s_un; - - path = getenv("SSH_AUTH_SOCK"); - if (!path) - return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE, - "no auth sock variable"); - - agent->fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (agent->fd < 0) - return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET, - "failed creating socket"); - - s_un.sun_family = AF_UNIX; - strncpy (s_un.sun_path, path, sizeof s_un.sun_path); - if (connect(agent->fd, (struct sockaddr*)(&s_un), sizeof s_un) != 0) { - close (agent->fd); - return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, - "failed connecting with agent"); - } - - return LIBSSH2_ERROR_NONE; -} - -static int -agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) -{ - unsigned char buf[4]; - int rc; - - /* Send the length of the request */ - if (transctx->state == agent_NB_state_request_created) { - _libssh2_htonu32(buf, transctx->request_len); - rc = LIBSSH2_SEND_FD(agent->session, agent->fd, buf, sizeof buf, 0); - if (rc == -EAGAIN) - return LIBSSH2_ERROR_EAGAIN; - else if (rc < 0) - return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND, - "agent send failed"); - transctx->state = agent_NB_state_request_length_sent; - } - - /* Send the request body */ - if (transctx->state == agent_NB_state_request_length_sent) { - rc = LIBSSH2_SEND_FD(agent->session, agent->fd, transctx->request, - transctx->request_len, 0); - if (rc == -EAGAIN) - return LIBSSH2_ERROR_EAGAIN; - else if (rc < 0) - return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND, - "agent send failed"); - transctx->state = agent_NB_state_request_sent; - } - - /* Receive the length of a response */ - if (transctx->state == agent_NB_state_request_sent) { - rc = LIBSSH2_RECV_FD(agent->session, agent->fd, buf, sizeof buf, 0); - if (rc < 0) { - if (rc == -EAGAIN) - return LIBSSH2_ERROR_EAGAIN; - return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV, - "agent recv failed"); - } - transctx->response_len = _libssh2_ntohu32(buf); - transctx->response = LIBSSH2_ALLOC(agent->session, - transctx->response_len); - if (!transctx->response) - return LIBSSH2_ERROR_ALLOC; - - transctx->state = agent_NB_state_response_length_received; - } - - /* Receive the response body */ - if (transctx->state == agent_NB_state_response_length_received) { - rc = LIBSSH2_RECV_FD(agent->session, agent->fd, transctx->response, - transctx->response_len, 0); - if (rc < 0) { - if (rc == -EAGAIN) - return LIBSSH2_ERROR_EAGAIN; - return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND, - "agent recv failed"); - } - transctx->state = agent_NB_state_response_received; - } - - return 0; -} - -static int -agent_disconnect_unix(LIBSSH2_AGENT *agent) -{ - int ret; - ret = close(agent->fd); - - if(ret == -1) - return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT, - "failed closing the agent socket"); - return LIBSSH2_ERROR_NONE; -} - -struct agent_ops agent_ops_unix = { - agent_connect_unix, - agent_transact_unix, - agent_disconnect_unix -}; -#endif /* PF_UNIX */ - -#ifdef WIN32 -/* Code to talk to Pageant was taken from PuTTY. - * - * Portions copyright Robert de Bath, Joris van Rantwijk, Delian - * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas - * Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, - * Markus Kuhn, Colin Watson, and CORE SDI S.A. - */ -#define PAGEANT_COPYDATA_ID 0x804e50ba /* random goop */ -#define PAGEANT_MAX_MSGLEN 8192 - -static int -agent_connect_pageant(LIBSSH2_AGENT *agent) -{ - HWND hwnd; - hwnd = FindWindow("Pageant", "Pageant"); - if (!hwnd) - return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, - "failed connecting agent"); - agent->fd = 0; /* Mark as the connection has been established */ - return LIBSSH2_ERROR_NONE; -} - -static int -agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) -{ - HWND hwnd; - char mapname[23]; - HANDLE filemap; - unsigned char *p; - unsigned char *p2; - int id; - COPYDATASTRUCT cds; - - if (!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN) - return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL, - "illegal input"); - - hwnd = FindWindow("Pageant", "Pageant"); - if (!hwnd) - return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, - "found no pageant"); - - sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); - filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, PAGEANT_MAX_MSGLEN, mapname); - - if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) - return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, - "failed setting up pageant filemap"); - - p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); - _libssh2_store_str(&p2, (const char *)transctx->request, - transctx->request_len); - - cds.dwData = PAGEANT_COPYDATA_ID; - cds.cbData = 1 + strlen(mapname); - cds.lpData = mapname; - - id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); - if (id > 0) { - transctx->response_len = _libssh2_ntohu32(p); - if (transctx->response_len > PAGEANT_MAX_MSGLEN) { - UnmapViewOfFile(p); - CloseHandle(filemap); - return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, - "agent setup fail"); - } - transctx->response = LIBSSH2_ALLOC(agent->session, - transctx->response_len); - if (!transctx->response) { - UnmapViewOfFile(p); - CloseHandle(filemap); - return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC, - "agent malloc"); - } - memcpy(transctx->response, p + 4, transctx->response_len); - } - - UnmapViewOfFile(p); - CloseHandle(filemap); - return 0; -} - -static int -agent_disconnect_pageant(LIBSSH2_AGENT *agent) -{ - agent->fd = LIBSSH2_INVALID_SOCKET; - return 0; -} - -struct agent_ops agent_ops_pageant = { - agent_connect_pageant, - agent_transact_pageant, - agent_disconnect_pageant -}; -#endif /* WIN32 */ - -static struct { - const char *name; - struct agent_ops *ops; -} supported_backends[] = { -#ifdef WIN32 - {"Pageant", &agent_ops_pageant}, -#endif /* WIN32 */ -#ifdef PF_UNIX - {"Unix", &agent_ops_unix}, -#endif /* PF_UNIX */ - {NULL, NULL} -}; - -static int -agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, - const unsigned char *data, size_t data_len, void **abstract) -{ - LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract); - agent_transaction_ctx_t transctx = &agent->transctx; - struct agent_publickey *identity = agent->identity; - ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4; - ssize_t method_len; - unsigned char *s; - int rc; - - /* Create a request to sign the data */ - if (transctx->state == agent_NB_state_init) { - s = transctx->request = LIBSSH2_ALLOC(session, len); - if (!transctx->request) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "out of memory"); - - *s++ = SSH2_AGENTC_SIGN_REQUEST; - /* key blob */ - _libssh2_store_str(&s, (const char *)identity->external.blob, - identity->external.blob_len); - /* data */ - _libssh2_store_str(&s, (const char *)data, data_len); - - /* flags */ - _libssh2_store_u32(&s, 0); - - transctx->request_len = s - transctx->request; - transctx->state = agent_NB_state_request_created; - } - - /* Make sure to be re-called as a result of EAGAIN. */ - if (*transctx->request != SSH2_AGENTC_SIGN_REQUEST) - return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE, - "illegal request"); - - if (!agent->ops) - /* if no agent has been connected, bail out */ - return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE, - "agent not connected"); - - rc = agent->ops->transact(agent, transctx); - if (rc) { - goto error; - } - LIBSSH2_FREE(session, transctx->request); - transctx->request = NULL; - - len = transctx->response_len; - s = transctx->response; - len--; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - if (*s != SSH2_AGENT_SIGN_RESPONSE) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - s++; - - /* Skip the entire length of the signature */ - len -= 4; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - s += 4; - - /* Skip signing method */ - len -= 4; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - method_len = _libssh2_ntohu32(s); - s += 4; - len -= method_len; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - s += method_len; - - /* Read the signature */ - len -= 4; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - *sig_len = _libssh2_ntohu32(s); - s += 4; - len -= *sig_len; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - - *sig = LIBSSH2_ALLOC(session, *sig_len); - if (!*sig) { - rc = LIBSSH2_ERROR_ALLOC; - goto error; - } - memcpy(*sig, s, *sig_len); - - error: - LIBSSH2_FREE(session, transctx->request); - transctx->request = NULL; - - LIBSSH2_FREE(session, transctx->response); - transctx->response = NULL; - - return _libssh2_error(session, rc, "agent sign failure"); -} - -static int -agent_list_identities(LIBSSH2_AGENT *agent) -{ - agent_transaction_ctx_t transctx = &agent->transctx; - ssize_t len, num_identities; - unsigned char *s; - int rc; - unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES; - - /* Create a request to list identities */ - if (transctx->state == agent_NB_state_init) { - transctx->request = &c; - transctx->request_len = 1; - transctx->state = agent_NB_state_request_created; - } - - /* Make sure to be re-called as a result of EAGAIN. */ - if (*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES) - return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE, - "illegal agent request"); - - if (!agent->ops) - /* if no agent has been connected, bail out */ - return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE, - "agent not connected"); - - rc = agent->ops->transact(agent, transctx); - if (rc) { - goto error; - } - transctx->request = NULL; - - len = transctx->response_len; - s = transctx->response; - len--; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - if (*s != SSH2_AGENT_IDENTITIES_ANSWER) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - s++; - - /* Read the length of identities */ - len -= 4; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - num_identities = _libssh2_ntohu32(s); - s += 4; - - while (num_identities--) { - struct agent_publickey *identity; - ssize_t comment_len; - - identity = LIBSSH2_ALLOC(agent->session, sizeof *identity); - if (!identity) { - rc = LIBSSH2_ERROR_ALLOC; - goto error; - } - - /* Read the length of the blob */ - len -= 4; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - identity->external.blob_len = _libssh2_ntohu32(s); - s += 4; - - /* Read the blob */ - len -= identity->external.blob_len; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - identity->external.blob = LIBSSH2_ALLOC(agent->session, - identity->external.blob_len); - if (!identity->external.blob) { - rc = LIBSSH2_ERROR_ALLOC; - goto error; - } - memcpy(identity->external.blob, s, identity->external.blob_len); - s += identity->external.blob_len; - - /* Read the length of the comment */ - len -= 4; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - comment_len = _libssh2_ntohu32(s); - s += 4; - - /* Read the comment */ - len -= comment_len; - if (len < 0) { - rc = LIBSSH2_ERROR_AGENT_PROTOCOL; - goto error; - } - identity->external.comment = LIBSSH2_ALLOC(agent->session, - comment_len + 1); - if (!identity->external.comment) { - rc = LIBSSH2_ERROR_ALLOC; - goto error; - } - identity->external.comment[comment_len] = '\0'; - memcpy(identity->external.comment, s, comment_len); - s += comment_len; - - _libssh2_list_add(&agent->head, &identity->node); - } - error: - LIBSSH2_FREE(agent->session, transctx->response); - transctx->response = NULL; - - return _libssh2_error(agent->session, rc, - "agent list id failed"); -} - -static void -agent_free_identities(LIBSSH2_AGENT *agent) { - struct agent_publickey *node; - struct agent_publickey *next; - - for (node = _libssh2_list_first(&agent->head); node; node = next) { - next = _libssh2_list_next(&node->node); - LIBSSH2_FREE(agent->session, node->external.blob); - LIBSSH2_FREE(agent->session, node->external.comment); - LIBSSH2_FREE(agent->session, node); - } - _libssh2_list_init(&agent->head); -} - -#define AGENT_PUBLICKEY_MAGIC 0x3bdefed2 -/* - * agent_publickey_to_external() - * - * Copies data from the internal to the external representation struct. - * - */ -static struct libssh2_agent_publickey * -agent_publickey_to_external(struct agent_publickey *node) -{ - struct libssh2_agent_publickey *ext = &node->external; - - ext->magic = AGENT_PUBLICKEY_MAGIC; - ext->node = node; - - return ext; -} - -/* - * libssh2_agent_init - * - * Init an ssh-agent handle. Returns the pointer to the handle. - * - */ -LIBSSH2_API LIBSSH2_AGENT * -libssh2_agent_init(LIBSSH2_SESSION *session) -{ - LIBSSH2_AGENT *agent; - - agent = LIBSSH2_ALLOC(session, sizeof *agent); - if (!agent) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate space for agent connection"); - return NULL; - } - memset(agent, 0, sizeof *agent); - agent->session = session; - _libssh2_list_init(&agent->head); - - return agent; -} - -/* - * libssh2_agent_connect() - * - * Connect to an ssh-agent. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_connect(LIBSSH2_AGENT *agent) -{ - int i, rc = -1; - for (i = 0; supported_backends[i].name; i++) { - agent->ops = supported_backends[i].ops; - rc = agent->ops->connect(agent); - if (!rc) - return 0; - } - return rc; -} - -/* - * libssh2_agent_list_identities() - * - * Request ssh-agent to list identities. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_list_identities(LIBSSH2_AGENT *agent) -{ - memset(&agent->transctx, 0, sizeof agent->transctx); - /* Abondon the last fetched identities */ - agent_free_identities(agent); - return agent_list_identities(agent); -} - -/* - * libssh2_agent_get_identity() - * - * Traverse the internal list of public keys. Pass NULL to 'prev' to get - * the first one. Or pass a poiner to the previously returned one to get the - * next. - * - * Returns: - * 0 if a fine public key was stored in 'store' - * 1 if end of public keys - * [negative] on errors - */ -LIBSSH2_API int -libssh2_agent_get_identity(LIBSSH2_AGENT *agent, - struct libssh2_agent_publickey **ext, - struct libssh2_agent_publickey *oprev) -{ - struct agent_publickey *node; - if (oprev && oprev->node) { - /* we have a starting point */ - struct agent_publickey *prev = oprev->node; - - /* get the next node in the list */ - node = _libssh2_list_next(&prev->node); - } - else - node = _libssh2_list_first(&agent->head); - - if (!node) - /* no (more) node */ - return 1; - - *ext = agent_publickey_to_external(node); - - return 0; -} - -/* - * libssh2_agent_userauth() - * - * Do publickey user authentication with the help of ssh-agent. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_userauth(LIBSSH2_AGENT *agent, - const char *username, - struct libssh2_agent_publickey *identity) -{ - void *abstract = agent; - int rc; - - if (agent->session->userauth_pblc_state == libssh2_NB_state_idle) { - memset(&agent->transctx, 0, sizeof agent->transctx); - agent->identity = identity->node; - } - - BLOCK_ADJUST(rc, agent->session, - _libssh2_userauth_publickey(agent->session, username, - strlen(username), - identity->blob, - identity->blob_len, - agent_sign, - &abstract)); - return rc; -} - -/* - * libssh2_agent_disconnect() - * - * Close a connection to an ssh-agent. - * - * Returns 0 if succeeded, or a negative value for error. - */ -LIBSSH2_API int -libssh2_agent_disconnect(LIBSSH2_AGENT *agent) -{ - if (agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET) - return agent->ops->disconnect(agent); - return 0; -} - -/* - * libssh2_agent_free() - * - * Free an ssh-agent handle. This function also frees the internal - * collection of public keys. - */ -LIBSSH2_API void -libssh2_agent_free(LIBSSH2_AGENT *agent) { - /* Allow connection freeing when the socket has lost its connection */ - if (agent->fd != LIBSSH2_INVALID_SOCKET) { - libssh2_agent_disconnect(agent); - } - agent_free_identities(agent); - LIBSSH2_FREE(agent->session, agent); -} diff --git a/vendor/libssh2-1.4.2/src/channel.c b/vendor/libssh2-1.4.2/src/channel.c deleted file mode 100644 index 63e5d5bc0..000000000 --- a/vendor/libssh2-1.4.2/src/channel.c +++ /dev/null @@ -1,2570 +0,0 @@ -/* Copyright (c) 2004-2007 Sara Golemon - * Copyright (c) 2005 Mikhail Gusarov - * Copyright (c) 2008-2011 by Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_INTTYPES_H -#include -#endif -#include - -#include "channel.h" -#include "transport.h" -#include "packet.h" -#include "session.h" - -/* - * _libssh2_channel_nextid - * - * Determine the next channel ID we can use at our end - */ -uint32_t -_libssh2_channel_nextid(LIBSSH2_SESSION * session) -{ - uint32_t id = session->next_channel; - LIBSSH2_CHANNEL *channel; - - channel = _libssh2_list_first(&session->channels); - - while (channel) { - if (channel->local.id > id) { - id = channel->local.id; - } - channel = _libssh2_list_next(&channel->node); - } - - /* This is a shortcut to avoid waiting for close packets on channels we've - * forgotten about, This *could* be a problem if we request and close 4 - * billion or so channels in too rapid succession for the remote end to - * respond, but the worst case scenario is that some data meant for - * another channel Gets picked up by the new one.... Pretty unlikely all - * told... - */ - session->next_channel = id + 1; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Allocated new channel ID#%lu", - id); - return id; -} - -/* - * _libssh2_channel_locate - * - * Locate a channel pointer by number - */ -LIBSSH2_CHANNEL * -_libssh2_channel_locate(LIBSSH2_SESSION *session, uint32_t channel_id) -{ - LIBSSH2_CHANNEL *channel; - LIBSSH2_LISTENER *l; - - for(channel = _libssh2_list_first(&session->channels); - channel; - channel = _libssh2_list_next(&channel->node)) { - if (channel->local.id == channel_id) - return channel; - } - - /* We didn't find the channel in the session, let's then check its - listeners since each listener may have its own set of pending channels - */ - for(l = _libssh2_list_first(&session->listeners); l; - l = _libssh2_list_next(&l->node)) { - for(channel = _libssh2_list_first(&l->queue); - channel; - channel = _libssh2_list_next(&channel->node)) { - if (channel->local.id == channel_id) - return channel; - } - } - - return NULL; -} - -/* - * _libssh2_channel_open - * - * Establish a generic session channel - */ -LIBSSH2_CHANNEL * -_libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, - uint32_t channel_type_len, - uint32_t window_size, - uint32_t packet_size, - const unsigned char *message, - size_t message_len) -{ - static const unsigned char reply_codes[3] = { - SSH_MSG_CHANNEL_OPEN_CONFIRMATION, - SSH_MSG_CHANNEL_OPEN_FAILURE, - 0 - }; - unsigned char *s; - int rc; - - if (session->open_state == libssh2_NB_state_idle) { - session->open_channel = NULL; - session->open_packet = NULL; - session->open_data = NULL; - /* 17 = packet_type(1) + channel_type_len(4) + sender_channel(4) + - * window_size(4) + packet_size(4) */ - session->open_packet_len = channel_type_len + 17; - session->open_local_channel = _libssh2_channel_nextid(session); - - /* Zero the whole thing out */ - memset(&session->open_packet_requirev_state, 0, - sizeof(session->open_packet_requirev_state)); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Opening Channel - win %d pack %d", window_size, - packet_size); - session->open_channel = - LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); - if (!session->open_channel) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate space for channel data"); - return NULL; - } - memset(session->open_channel, 0, sizeof(LIBSSH2_CHANNEL)); - - session->open_channel->channel_type_len = channel_type_len; - session->open_channel->channel_type = - LIBSSH2_ALLOC(session, channel_type_len); - if (!session->open_channel->channel_type) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Failed allocating memory for channel type name"); - LIBSSH2_FREE(session, session->open_channel); - session->open_channel = NULL; - return NULL; - } - memcpy(session->open_channel->channel_type, channel_type, - channel_type_len); - - /* REMEMBER: local as in locally sourced */ - session->open_channel->local.id = session->open_local_channel; - session->open_channel->remote.window_size = window_size; - session->open_channel->remote.window_size_initial = window_size; - session->open_channel->remote.packet_size = packet_size; - session->open_channel->session = session; - - _libssh2_list_add(&session->channels, - &session->open_channel->node); - - s = session->open_packet = - LIBSSH2_ALLOC(session, session->open_packet_len); - if (!session->open_packet) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate temporary space for packet"); - goto channel_error; - } - *(s++) = SSH_MSG_CHANNEL_OPEN; - _libssh2_store_str(&s, channel_type, channel_type_len); - _libssh2_store_u32(&s, session->open_local_channel); - _libssh2_store_u32(&s, window_size); - _libssh2_store_u32(&s, packet_size); - - /* Do not copy the message */ - - session->open_state = libssh2_NB_state_created; - } - - if (session->open_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, - session->open_packet, - session->open_packet_len, - message, message_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending channel-open request"); - return NULL; - } - else if (rc) { - _libssh2_error(session, rc, - "Unable to send channel-open request"); - goto channel_error; - } - - session->open_state = libssh2_NB_state_sent; - } - - if (session->open_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, - &session->open_data, - &session->open_data_len, 1, - session->open_packet + 5 + - channel_type_len, 4, - &session->open_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - return NULL; - } else if (rc) { - goto channel_error; - } - - if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) { - session->open_channel->remote.id = - _libssh2_ntohu32(session->open_data + 5); - session->open_channel->local.window_size = - _libssh2_ntohu32(session->open_data + 9); - session->open_channel->local.window_size_initial = - _libssh2_ntohu32(session->open_data + 9); - session->open_channel->local.packet_size = - _libssh2_ntohu32(session->open_data + 13); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Connection Established - ID: %lu/%lu win: %lu/%lu" - " pack: %lu/%lu", - session->open_channel->local.id, - session->open_channel->remote.id, - session->open_channel->local.window_size, - session->open_channel->remote.window_size, - session->open_channel->local.packet_size, - session->open_channel->remote.packet_size); - LIBSSH2_FREE(session, session->open_packet); - session->open_packet = NULL; - LIBSSH2_FREE(session, session->open_data); - session->open_data = NULL; - - session->open_state = libssh2_NB_state_idle; - return session->open_channel; - } - - if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) { - _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, - "Channel open failure"); - } - } - - channel_error: - - if (session->open_data) { - LIBSSH2_FREE(session, session->open_data); - session->open_data = NULL; - } - if (session->open_packet) { - LIBSSH2_FREE(session, session->open_packet); - session->open_packet = NULL; - } - if (session->open_channel) { - unsigned char channel_id[4]; - LIBSSH2_FREE(session, session->open_channel->channel_type); - - _libssh2_list_remove(&session->open_channel->node); - - /* Clear out packets meant for this channel */ - _libssh2_htonu32(channel_id, session->open_channel->local.id); - while ((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA, - &session->open_data, - &session->open_data_len, 1, - channel_id, 4) >= 0) - || - (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, - &session->open_data, - &session->open_data_len, 1, - channel_id, 4) >= 0)) { - LIBSSH2_FREE(session, session->open_data); - session->open_data = NULL; - } - - LIBSSH2_FREE(session, session->open_channel); - session->open_channel = NULL; - } - - session->open_state = libssh2_NB_state_idle; - return NULL; -} - -/* - * libssh2_channel_open_ex - * - * Establish a generic session channel - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *type, - unsigned int type_len, - unsigned int window_size, unsigned int packet_size, - const char *msg, unsigned int msg_len) -{ - LIBSSH2_CHANNEL *ptr; - - if(!session) - return NULL; - - BLOCK_ADJUST_ERRNO(ptr, session, - _libssh2_channel_open(session, type, type_len, - window_size, packet_size, - (unsigned char *)msg, - msg_len)); - return ptr; -} - -/* - * libssh2_channel_direct_tcpip_ex - * - * Tunnel TCP/IP connect through the SSH session to direct host/port - */ -static LIBSSH2_CHANNEL * -channel_direct_tcpip(LIBSSH2_SESSION * session, const char *host, - int port, const char *shost, int sport) -{ - LIBSSH2_CHANNEL *channel; - unsigned char *s; - - if (session->direct_state == libssh2_NB_state_idle) { - session->direct_host_len = strlen(host); - session->direct_shost_len = strlen(shost); - /* host_len(4) + port(4) + shost_len(4) + sport(4) */ - session->direct_message_len = - session->direct_host_len + session->direct_shost_len + 16; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Requesting direct-tcpip session to from %s:%d to %s:%d", - shost, sport, host, port); - - s = session->direct_message = - LIBSSH2_ALLOC(session, session->direct_message_len); - if (!session->direct_message) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for direct-tcpip connection"); - return NULL; - } - _libssh2_store_str(&s, host, session->direct_host_len); - _libssh2_store_u32(&s, port); - _libssh2_store_str(&s, shost, session->direct_shost_len); - _libssh2_store_u32(&s, sport); - } - - channel = - _libssh2_channel_open(session, "direct-tcpip", - sizeof("direct-tcpip") - 1, - LIBSSH2_CHANNEL_WINDOW_DEFAULT, - LIBSSH2_CHANNEL_PACKET_DEFAULT, - session->direct_message, - session->direct_message_len); - - if (!channel && - libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { - /* The error code is still set to LIBSSH2_ERROR_EAGAIN, set our state - to created to avoid re-creating the package on next invoke */ - session->direct_state = libssh2_NB_state_created; - return NULL; - } - /* by default we set (keep?) idle state... */ - session->direct_state = libssh2_NB_state_idle; - - LIBSSH2_FREE(session, session->direct_message); - session->direct_message = NULL; - - return channel; -} - -/* - * libssh2_channel_direct_tcpip_ex - * - * Tunnel TCP/IP connect through the SSH session to direct host/port - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host, - int port, const char *shost, int sport) -{ - LIBSSH2_CHANNEL *ptr; - - if(!session) - return NULL; - - BLOCK_ADJUST_ERRNO(ptr, session, - channel_direct_tcpip(session, host, port, shost, sport)); - return ptr; -} - -/* - * channel_forward_listen - * - * Bind a port on the remote host and listen for connections - */ -static LIBSSH2_LISTENER * -channel_forward_listen(LIBSSH2_SESSION * session, const char *host, - int port, int *bound_port, int queue_maxsize) -{ - unsigned char *s; - static const unsigned char reply_codes[3] = - { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 }; - int rc; - - if(!host) - host = "0.0.0.0"; - - if (session->fwdLstn_state == libssh2_NB_state_idle) { - session->fwdLstn_host_len = strlen(host); - /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) - + port(4) */ - session->fwdLstn_packet_len = - session->fwdLstn_host_len + (sizeof("tcpip-forward") - 1) + 14; - - /* Zero the whole thing out */ - memset(&session->fwdLstn_packet_requirev_state, 0, - sizeof(session->fwdLstn_packet_requirev_state)); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Requesting tcpip-forward session for %s:%d", host, - port); - - s = session->fwdLstn_packet = - LIBSSH2_ALLOC(session, session->fwdLstn_packet_len); - if (!session->fwdLstn_packet) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memeory for setenv packet"); - return NULL; - } - - *(s++) = SSH_MSG_GLOBAL_REQUEST; - _libssh2_store_str(&s, "tcpip-forward", sizeof("tcpip-forward") - 1); - *(s++) = 0x01; /* want_reply */ - - _libssh2_store_str(&s, host, session->fwdLstn_host_len); - _libssh2_store_u32(&s, port); - - session->fwdLstn_state = libssh2_NB_state_created; - } - - if (session->fwdLstn_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, - session->fwdLstn_packet, - session->fwdLstn_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending global-request packet for " - "forward listen request"); - return NULL; - } - else if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send global-request packet for forward " - "listen request"); - LIBSSH2_FREE(session, session->fwdLstn_packet); - session->fwdLstn_packet = NULL; - session->fwdLstn_state = libssh2_NB_state_idle; - return NULL; - } - LIBSSH2_FREE(session, session->fwdLstn_packet); - session->fwdLstn_packet = NULL; - - session->fwdLstn_state = libssh2_NB_state_sent; - } - - if (session->fwdLstn_state == libssh2_NB_state_sent) { - unsigned char *data; - size_t data_len; - rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len, - 0, NULL, 0, - &session->fwdLstn_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - return NULL; - } else if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unknown"); - session->fwdLstn_state = libssh2_NB_state_idle; - return NULL; - } - - if (data[0] == SSH_MSG_REQUEST_SUCCESS) { - LIBSSH2_LISTENER *listener; - - listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER)); - if (!listener) - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for listener queue"); - else { - memset(listener, 0, sizeof(LIBSSH2_LISTENER)); - listener->host = - LIBSSH2_ALLOC(session, session->fwdLstn_host_len + 1); - if (!listener->host) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for listener queue"); - LIBSSH2_FREE(session, listener); - listener = NULL; - } - else { - listener->session = session; - memcpy(listener->host, host ? host : "0.0.0.0", - session->fwdLstn_host_len); - listener->host[session->fwdLstn_host_len] = 0; - if (data_len >= 5 && !port) { - listener->port = _libssh2_ntohu32(data + 1); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Dynamic tcpip-forward port allocated: %d", - listener->port); - } - else - listener->port = port; - - listener->queue_size = 0; - listener->queue_maxsize = queue_maxsize; - - /* append this to the parent's list of listeners */ - _libssh2_list_add(&session->listeners, &listener->node); - - if (bound_port) { - *bound_port = listener->port; - } - } - } - - LIBSSH2_FREE(session, data); - session->fwdLstn_state = libssh2_NB_state_idle; - return listener; - } - else if (data[0] == SSH_MSG_REQUEST_FAILURE) { - LIBSSH2_FREE(session, data); - _libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, - "Unable to complete request for forward-listen"); - session->fwdLstn_state = libssh2_NB_state_idle; - return NULL; - } - } - - session->fwdLstn_state = libssh2_NB_state_idle; - - return NULL; -} - -/* - * libssh2_channel_forward_listen_ex - * - * Bind a port on the remote host and listen for connections - */ -LIBSSH2_API LIBSSH2_LISTENER * -libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host, - int port, int *bound_port, int queue_maxsize) -{ - LIBSSH2_LISTENER *ptr; - - if(!session) - return NULL; - - BLOCK_ADJUST_ERRNO(ptr, session, - channel_forward_listen(session, host, port, bound_port, - queue_maxsize)); - return ptr; -} - -/* - * _libssh2_channel_forward_cancel - * - * Stop listening on a remote port and free the listener - * Toss out any pending (un-accept()ed) connections - * - * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error - */ -int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) -{ - LIBSSH2_SESSION *session = listener->session; - LIBSSH2_CHANNEL *queued; - unsigned char *packet, *s; - size_t host_len = strlen(listener->host); - /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + - port(4) */ - size_t packet_len = - host_len + 14 + sizeof("cancel-tcpip-forward") - 1; - int rc; - - if (listener->chanFwdCncl_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Cancelling tcpip-forward session for %s:%d", - listener->host, listener->port); - - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memeory for setenv packet"); - return LIBSSH2_ERROR_ALLOC; - } - - *(s++) = SSH_MSG_GLOBAL_REQUEST; - _libssh2_store_str(&s, "cancel-tcpip-forward", - sizeof("cancel-tcpip-forward") - 1); - *(s++) = 0x00; /* want_reply */ - - _libssh2_store_str(&s, listener->host, host_len); - _libssh2_store_u32(&s, listener->port); - - listener->chanFwdCncl_state = libssh2_NB_state_created; - } else { - packet = listener->chanFwdCncl_data; - } - - if (listener->chanFwdCncl_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, packet, packet_len, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending forward request"); - listener->chanFwdCncl_data = packet; - return rc; - } - else if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send global-request packet for forward " - "listen request"); - LIBSSH2_FREE(session, packet); - listener->chanFwdCncl_state = libssh2_NB_state_idle; - return LIBSSH2_ERROR_SOCKET_SEND; - } - LIBSSH2_FREE(session, packet); - - listener->chanFwdCncl_state = libssh2_NB_state_sent; - } - - queued = _libssh2_list_first(&listener->queue); - while (queued) { - LIBSSH2_CHANNEL *next = _libssh2_list_next(&queued->node); - - rc = _libssh2_channel_free(queued); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - queued = next; - } - LIBSSH2_FREE(session, listener->host); - - /* remove this entry from the parent's list of listeners */ - _libssh2_list_remove(&listener->node); - - LIBSSH2_FREE(session, listener); - - listener->chanFwdCncl_state = libssh2_NB_state_idle; - - return 0; -} - -/* - * libssh2_channel_forward_cancel - * - * Stop listening on a remote port and free the listener - * Toss out any pending (un-accept()ed) connections - * - * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error - */ -LIBSSH2_API int -libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) -{ - int rc; - - if(!listener) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, listener->session, - _libssh2_channel_forward_cancel(listener)); - return rc; -} - -/* - * channel_forward_accept - * - * Accept a connection - */ -static LIBSSH2_CHANNEL * -channel_forward_accept(LIBSSH2_LISTENER *listener) -{ - int rc; - - do { - rc = _libssh2_transport_read(listener->session); - } while (rc > 0); - - if (_libssh2_list_first(&listener->queue)) { - LIBSSH2_CHANNEL *channel = _libssh2_list_first(&listener->queue); - - /* detach channel from listener's queue */ - _libssh2_list_remove(&channel->node); - - listener->queue_size--; - - /* add channel to session's channel list */ - _libssh2_list_add(&channel->session->channels, &channel->node); - - return channel; - } - - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(listener->session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for packet"); - } - else - _libssh2_error(listener->session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, - "Channel not found"); - return NULL; -} - -/* - * libssh2_channel_forward_accept - * - * Accept a connection - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener) -{ - LIBSSH2_CHANNEL *ptr; - - if(!listener) - return NULL; - - BLOCK_ADJUST_ERRNO(ptr, listener->session, - channel_forward_accept(listener)); - return ptr; - -} - -/* - * channel_setenv - * - * Set an environment variable prior to requesting a shell/program/subsystem - */ -static int channel_setenv(LIBSSH2_CHANNEL *channel, - const char *varname, unsigned int varname_len, - const char *value, unsigned int value_len) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char *s, *data; - static const unsigned char reply_codes[3] = - { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; - size_t data_len; - int rc; - - if (channel->setenv_state == libssh2_NB_state_idle) { - /* 21 = packet_type(1) + channel_id(4) + request_len(4) + - * request(3)"env" + want_reply(1) + varname_len(4) + value_len(4) */ - channel->setenv_packet_len = varname_len + value_len + 21; - - /* Zero the whole thing out */ - memset(&channel->setenv_packet_requirev_state, 0, - sizeof(channel->setenv_packet_requirev_state)); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Setting remote environment variable: %s=%s on " - "channel %lu/%lu", - varname, value, channel->local.id, channel->remote.id); - - s = channel->setenv_packet = - LIBSSH2_ALLOC(session, channel->setenv_packet_len); - if (!channel->setenv_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memeory " - "for setenv packet"); - } - - *(s++) = SSH_MSG_CHANNEL_REQUEST; - _libssh2_store_u32(&s, channel->remote.id); - _libssh2_store_str(&s, "env", sizeof("env") - 1); - *(s++) = 0x01; - _libssh2_store_str(&s, varname, varname_len); - _libssh2_store_str(&s, value, value_len); - - channel->setenv_state = libssh2_NB_state_created; - } - - if (channel->setenv_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, - channel->setenv_packet, - channel->setenv_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending setenv request"); - return rc; - } else if (rc) { - LIBSSH2_FREE(session, channel->setenv_packet); - channel->setenv_packet = NULL; - channel->setenv_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send channel-request packet for " - "setenv request"); - } - LIBSSH2_FREE(session, channel->setenv_packet); - channel->setenv_packet = NULL; - - _libssh2_htonu32(channel->setenv_local_channel, channel->local.id); - - channel->setenv_state = libssh2_NB_state_sent; - } - - if (channel->setenv_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len, - 1, channel->setenv_local_channel, 4, - &channel-> - setenv_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - if (rc) { - channel->setenv_state = libssh2_NB_state_idle; - return rc; - } - - if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { - LIBSSH2_FREE(session, data); - channel->setenv_state = libssh2_NB_state_idle; - return 0; - } - - LIBSSH2_FREE(session, data); - } - - channel->setenv_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, - "Unable to complete request for channel-setenv"); -} - -/* - * libssh2_channel_setenv_ex - * - * Set an environment variable prior to requesting a shell/program/subsystem - */ -LIBSSH2_API int -libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, - const char *varname, unsigned int varname_len, - const char *value, unsigned int value_len) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - channel_setenv(channel, varname, varname_len, - value, value_len)); - return rc; -} - -/* - * channel_request_pty - * Duh... Request a PTY - */ -static int channel_request_pty(LIBSSH2_CHANNEL *channel, - const char *term, unsigned int term_len, - const char *modes, unsigned int modes_len, - int width, int height, - int width_px, int height_px) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char *s; - static const unsigned char reply_codes[3] = - { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; - int rc; - - if (channel->reqPTY_state == libssh2_NB_state_idle) { - /* 41 = packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + - * want_reply(1) + term_len(4) + width(4) + height(4) + width_px(4) + - * height_px(4) + modes_len(4) */ - if(term_len + modes_len > 256) { - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "term + mode lengths too large"); - } - - channel->reqPTY_packet_len = term_len + modes_len + 41; - - /* Zero the whole thing out */ - memset(&channel->reqPTY_packet_requirev_state, 0, - sizeof(channel->reqPTY_packet_requirev_state)); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Allocating tty on channel %lu/%lu", channel->local.id, - channel->remote.id); - - s = channel->reqPTY_packet; - - *(s++) = SSH_MSG_CHANNEL_REQUEST; - _libssh2_store_u32(&s, channel->remote.id); - _libssh2_store_str(&s, (char *)"pty-req", sizeof("pty-req") - 1); - - *(s++) = 0x01; - - _libssh2_store_str(&s, term, term_len); - _libssh2_store_u32(&s, width); - _libssh2_store_u32(&s, height); - _libssh2_store_u32(&s, width_px); - _libssh2_store_u32(&s, height_px); - _libssh2_store_str(&s, modes, modes_len); - - channel->reqPTY_state = libssh2_NB_state_created; - } - - if (channel->reqPTY_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, channel->reqPTY_packet, - channel->reqPTY_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending pty request"); - return rc; - } else if (rc) { - channel->reqPTY_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send pty-request packet"); - } - _libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id); - - channel->reqPTY_state = libssh2_NB_state_sent; - } - - if (channel->reqPTY_state == libssh2_NB_state_sent) { - unsigned char *data; - size_t data_len; - unsigned char code; - rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len, - 1, channel->reqPTY_local_channel, 4, - &channel->reqPTY_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - channel->reqPTY_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Failed to require the PTY package"); - } - - code = data[0]; - - LIBSSH2_FREE(session, data); - channel->reqPTY_state = libssh2_NB_state_idle; - - if (code == SSH_MSG_CHANNEL_SUCCESS) - return 0; - } - - return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, - "Unable to complete request for channel request-pty"); -} - -/* - * libssh2_channel_request_pty_ex - * Duh... Request a PTY - */ -LIBSSH2_API int -libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term, - unsigned int term_len, const char *modes, - unsigned int modes_len, int width, int height, - int width_px, int height_px) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - channel_request_pty(channel, term, term_len, modes, - modes_len, width, height, - width_px, height_px)); - return rc; -} - -static int -channel_request_pty_size(LIBSSH2_CHANNEL * channel, int width, - int height, int width_px, int height_px) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char *s; - int rc; - int retcode = LIBSSH2_ERROR_PROTO; - - if (channel->reqPTY_state == libssh2_NB_state_idle) { - channel->reqPTY_packet_len = 39; - - /* Zero the whole thing out */ - memset(&channel->reqPTY_packet_requirev_state, 0, - sizeof(channel->reqPTY_packet_requirev_state)); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "changing tty size on channel %lu/%lu", - channel->local.id, - channel->remote.id); - - s = channel->reqPTY_packet; - - *(s++) = SSH_MSG_CHANNEL_REQUEST; - _libssh2_store_u32(&s, channel->remote.id); - _libssh2_store_str(&s, (char *)"window-change", - sizeof("window-change") - 1); - *(s++) = 0x00; /* Don't reply */ - _libssh2_store_u32(&s, width); - _libssh2_store_u32(&s, height); - _libssh2_store_u32(&s, width_px); - _libssh2_store_u32(&s, height_px); - - channel->reqPTY_state = libssh2_NB_state_created; - } - - if (channel->reqPTY_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, channel->reqPTY_packet, - channel->reqPTY_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending window-change request"); - return rc; - } else if (rc) { - channel->reqPTY_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send window-change packet"); - } - _libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id); - retcode = LIBSSH2_ERROR_NONE; - } - - channel->reqPTY_state = libssh2_NB_state_idle; - return retcode; -} - -LIBSSH2_API int -libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL *channel, int width, - int height, int width_px, int height_px) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - channel_request_pty_size(channel, width, height, width_px, - height_px)); - return rc; -} - -/* Keep this an even number */ -#define LIBSSH2_X11_RANDOM_COOKIE_LEN 32 - -/* - * channel_x11_req - * Request X11 forwarding - */ -static int -channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection, - const char *auth_proto, const char *auth_cookie, - int screen_number) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char *s; - static const unsigned char reply_codes[3] = - { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; - size_t proto_len = - auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1); - size_t cookie_len = - auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN; - int rc; - - if (channel->reqX11_state == libssh2_NB_state_idle) { - /* 30 = packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + - * want_reply(1) + single_cnx(1) + proto_len(4) + cookie_len(4) + - * screen_num(4) */ - channel->reqX11_packet_len = proto_len + cookie_len + 30; - - /* Zero the whole thing out */ - memset(&channel->reqX11_packet_requirev_state, 0, - sizeof(channel->reqX11_packet_requirev_state)); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Requesting x11-req for channel %lu/%lu: single=%d " - "proto=%s cookie=%s screen=%d", - channel->local.id, channel->remote.id, - single_connection, - auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", - auth_cookie ? auth_cookie : "", screen_number); - - s = channel->reqX11_packet = - LIBSSH2_ALLOC(session, channel->reqX11_packet_len); - if (!channel->reqX11_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for pty-request"); - } - - *(s++) = SSH_MSG_CHANNEL_REQUEST; - _libssh2_store_u32(&s, channel->remote.id); - _libssh2_store_str(&s, "x11-req", sizeof("x11-req") - 1); - - *(s++) = 0x01; /* want_reply */ - *(s++) = single_connection ? 0x01 : 0x00; - - _libssh2_store_str(&s, auth_proto?auth_proto:"MIT-MAGIC-COOKIE-1", - proto_len); - - _libssh2_store_u32(&s, cookie_len); - if (auth_cookie) { - memcpy(s, auth_cookie, cookie_len); - } else { - int i; - /* note: the extra +1 below is necessary since the sprintf() - loop will always write 3 bytes so the last one will write - the trailing zero at the LIBSSH2_X11_RANDOM_COOKIE_LEN/2 - border */ - unsigned char buffer[(LIBSSH2_X11_RANDOM_COOKIE_LEN / 2) +1]; - - _libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); - for(i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) { - sprintf((char *)&s[i*2], "%02X", buffer[i]); - } - } - s += cookie_len; - - _libssh2_store_u32(&s, screen_number); - channel->reqX11_state = libssh2_NB_state_created; - } - - if (channel->reqX11_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, channel->reqX11_packet, - channel->reqX11_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending X11-req packet"); - return rc; - } - if (rc) { - LIBSSH2_FREE(session, channel->reqX11_packet); - channel->reqX11_packet = NULL; - channel->reqX11_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send x11-req packet"); - } - LIBSSH2_FREE(session, channel->reqX11_packet); - channel->reqX11_packet = NULL; - - _libssh2_htonu32(channel->reqX11_local_channel, channel->local.id); - - channel->reqX11_state = libssh2_NB_state_sent; - } - - if (channel->reqX11_state == libssh2_NB_state_sent) { - size_t data_len; - unsigned char *data; - unsigned char code; - - rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len, - 1, channel->reqX11_local_channel, 4, - &channel->reqX11_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - channel->reqX11_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "waiting for x11-req response packet"); - } - - code = data[0]; - LIBSSH2_FREE(session, data); - channel->reqX11_state = libssh2_NB_state_idle; - - if (code == SSH_MSG_CHANNEL_SUCCESS) - return 0; - } - - return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, - "Unable to complete request for channel x11-req"); -} - -/* - * libssh2_channel_x11_req_ex - * Request X11 forwarding - */ -LIBSSH2_API int -libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, - const char *auth_proto, const char *auth_cookie, - int screen_number) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - channel_x11_req(channel, single_connection, auth_proto, - auth_cookie, screen_number)); - return rc; -} - - -/* - * _libssh2_channel_process_startup - * - * Primitive for libssh2_channel_(shell|exec|subsystem) - */ -int -_libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, - const char *request, size_t request_len, - const char *message, size_t message_len) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char *s; - static const unsigned char reply_codes[3] = - { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; - int rc; - - if (channel->process_state == libssh2_NB_state_idle) { - /* 10 = packet_type(1) + channel(4) + request_len(4) + want_reply(1) */ - channel->process_packet_len = request_len + 10; - - /* Zero the whole thing out */ - memset(&channel->process_packet_requirev_state, 0, - sizeof(channel->process_packet_requirev_state)); - - if (message) - channel->process_packet_len += + 4; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "starting request(%s) on channel %lu/%lu, message=%s", - request, channel->local.id, channel->remote.id, - message?message:""); - s = channel->process_packet = - LIBSSH2_ALLOC(session, channel->process_packet_len); - if (!channel->process_packet) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory " - "for channel-process request"); - - *(s++) = SSH_MSG_CHANNEL_REQUEST; - _libssh2_store_u32(&s, channel->remote.id); - _libssh2_store_str(&s, request, request_len); - *(s++) = 0x01; - - if (message) - _libssh2_store_u32(&s, message_len); - - channel->process_state = libssh2_NB_state_created; - } - - if (channel->process_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, - channel->process_packet, - channel->process_packet_len, - (unsigned char *)message, message_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending channel request"); - return rc; - } - else if (rc) { - LIBSSH2_FREE(session, channel->process_packet); - channel->process_packet = NULL; - channel->process_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send channel request"); - } - LIBSSH2_FREE(session, channel->process_packet); - channel->process_packet = NULL; - - _libssh2_htonu32(channel->process_local_channel, channel->local.id); - - channel->process_state = libssh2_NB_state_sent; - } - - if (channel->process_state == libssh2_NB_state_sent) { - unsigned char *data; - size_t data_len; - unsigned char code; - rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len, - 1, channel->process_local_channel, 4, - &channel->process_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - channel->process_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Failed waiting for channel success"); - } - - code = data[0]; - LIBSSH2_FREE(session, data); - channel->process_state = libssh2_NB_state_idle; - - if (code == SSH_MSG_CHANNEL_SUCCESS) - return 0; - } - - return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, - "Unable to complete request for " - "channel-process-startup"); -} - -/* - * libssh2_channel_process_startup - * - * Primitive for libssh2_channel_(shell|exec|subsystem) - */ -LIBSSH2_API int -libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, - const char *req, unsigned int req_len, - const char *msg, unsigned int msg_len) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_process_startup(channel, req, req_len, - msg, msg_len)); - return rc; -} - - -/* - * libssh2_channel_set_blocking - * - * Set a channel's BEHAVIOR blocking on or off. The socket will remain non- - * blocking. - */ -LIBSSH2_API void -libssh2_channel_set_blocking(LIBSSH2_CHANNEL * channel, int blocking) -{ - if(channel) - (void) _libssh2_session_set_blocking(channel->session, blocking); -} - -/* - * _libssh2_channel_flush - * - * Flush data from one (or all) stream - * Returns number of bytes flushed, or negative on failure - */ -int -_libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid) -{ - if (channel->flush_state == libssh2_NB_state_idle) { - LIBSSH2_PACKET *packet = - _libssh2_list_first(&channel->session->packets); - channel->flush_refund_bytes = 0; - channel->flush_flush_bytes = 0; - - while (packet) { - LIBSSH2_PACKET *next = _libssh2_list_next(&packet->node); - unsigned char packet_type = packet->data[0]; - - if (((packet_type == SSH_MSG_CHANNEL_DATA) - || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) - && (_libssh2_ntohu32(packet->data + 1) == channel->local.id)) { - /* It's our channel at least */ - long packet_stream_id = - (packet_type == SSH_MSG_CHANNEL_DATA) ? 0 : - _libssh2_ntohu32(packet->data + 5); - if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) - || ((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) - && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) - || (streamid == packet_stream_id))) - || ((packet_type == SSH_MSG_CHANNEL_DATA) - && (streamid == 0))) { - int bytes_to_flush = packet->data_len - packet->data_head; - - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, - "Flushing %d bytes of data from stream " - "%lu on channel %lu/%lu", - bytes_to_flush, packet_stream_id, - channel->local.id, channel->remote.id); - - /* It's one of the streams we wanted to flush */ - channel->flush_refund_bytes += packet->data_len - 13; - channel->flush_flush_bytes += bytes_to_flush; - - LIBSSH2_FREE(channel->session, packet->data); - - /* remove this packet from the parent's list */ - _libssh2_list_remove(&packet->node); - LIBSSH2_FREE(channel->session, packet); - } - } - packet = next; - } - - channel->flush_state = libssh2_NB_state_created; - } - - if (channel->flush_refund_bytes) { - int rc; - - rc = _libssh2_channel_receive_window_adjust(channel, - channel->flush_refund_bytes, - 1, NULL); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - - channel->flush_state = libssh2_NB_state_idle; - - return channel->flush_flush_bytes; -} - -/* - * libssh2_channel_flush_ex - * - * Flush data from one (or all) stream - * Returns number of bytes flushed, or negative on failure - */ -LIBSSH2_API int -libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int stream) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_flush(channel, stream)); - return rc; -} - -/* - * libssh2_channel_get_exit_status - * - * Return the channel's program exit status. Note that the actual protocol - * provides the full 32bit this function returns. We cannot abuse it to - * return error values in case of errors so we return a zero if channel is - * NULL. - */ -LIBSSH2_API int -libssh2_channel_get_exit_status(LIBSSH2_CHANNEL *channel) -{ - if(!channel) - return 0; - - return channel->exit_status; -} - -/* - * libssh2_channel_get_exit_signal - * - * Get exit signal (without leading "SIG"), error message, and language - * tag into newly allocated buffers of indicated length. Caller can - * use NULL pointers to indicate that the value should not be set. The - * *_len variables are set if they are non-NULL even if the - * corresponding string parameter is NULL. Returns LIBSSH2_ERROR_NONE - * on success, or an API error code. - */ -LIBSSH2_API int -libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL *channel, - char **exitsignal, - size_t *exitsignal_len, - char **errmsg, - size_t *errmsg_len, - char **langtag, - size_t *langtag_len) -{ - LIBSSH2_SESSION *session = channel->session; - size_t namelen = 0; - - if (channel) { - if (channel->exit_signal) { - namelen = strlen(channel->exit_signal); - if (exitsignal) { - *exitsignal = LIBSSH2_ALLOC(session, namelen + 1); - if (!*exitsignal) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for signal name"); - } - memcpy(*exitsignal, channel->exit_signal, namelen); - (*exitsignal)[namelen] = '\0'; - } - if (exitsignal_len) - *exitsignal_len = namelen; - } else { - if (exitsignal) - *exitsignal = NULL; - if (exitsignal_len) - *exitsignal_len = 0; - } - - /* TODO: set error message and language tag */ - - if (errmsg) - *errmsg = NULL; - - if (errmsg_len) - *errmsg_len = 0; - - if (langtag) - *langtag = NULL; - - if (langtag_len) - *langtag_len = 0; - } - - return LIBSSH2_ERROR_NONE; -} - -/* - * _libssh2_channel_receive_window_adjust - * - * Adjust the receive window for a channel by adjustment bytes. If the amount - * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the - * adjustment amount will be queued for a later packet. - * - * Calls _libssh2_error() ! - */ -int -_libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, - uint32_t adjustment, - unsigned char force, - unsigned int *store) -{ - int rc; - - if (channel->adjust_state == libssh2_NB_state_idle) { - if (!force - && (adjustment + channel->adjust_queue < - LIBSSH2_CHANNEL_MINADJUST)) { - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, - "Queueing %lu bytes for receive window adjustment " - "for channel %lu/%lu", - adjustment, channel->local.id, channel->remote.id); - channel->adjust_queue += adjustment; - if(store) - *store = channel->remote.window_size; - return 0; - } - - if (!adjustment && !channel->adjust_queue) { - if(store) - *store = channel->remote.window_size; - return 0; - } - - adjustment += channel->adjust_queue; - channel->adjust_queue = 0; - - /* Adjust the window based on the block we just freed */ - channel->adjust_adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST; - _libssh2_htonu32(&channel->adjust_adjust[1], channel->remote.id); - _libssh2_htonu32(&channel->adjust_adjust[5], adjustment); - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, - "Adjusting window %lu bytes for data on " - "channel %lu/%lu", - adjustment, channel->local.id, channel->remote.id); - - channel->adjust_state = libssh2_NB_state_created; - } - - rc = _libssh2_transport_send(channel->session, channel->adjust_adjust, 9, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(channel->session, rc, - "Would block sending window adjust"); - return rc; - } - else if (rc) { - channel->adjust_queue = adjustment; - return _libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send transfer-window adjustment " - "packet, deferring"); - } - else { - channel->remote.window_size += adjustment; - } - - channel->adjust_state = libssh2_NB_state_idle; - - if(store) - *store = channel->remote.window_size; - return 0; -} - -/* - * libssh2_channel_receive_window_adjust - * - * DEPRECATED - * - * Adjust the receive window for a channel by adjustment bytes. If the amount - * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the - * adjustment amount will be queued for a later packet. - * - * Returns the new size of the receive window (as understood by remote end). - * Note that it might return EAGAIN too which is highly stupid. - * - */ -LIBSSH2_API unsigned long -libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, - unsigned long adj, - unsigned char force) -{ - unsigned int window; - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_receive_window_adjust(channel, adj, - force, &window)); - - /* stupid - but this is how it was made to work before and this is just - kept for backwards compatibility */ - return rc?(unsigned long)rc:window; -} - -/* - * libssh2_channel_receive_window_adjust2 - * - * Adjust the receive window for a channel by adjustment bytes. If the amount - * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the - * adjustment amount will be queued for a later packet. - * - * Stores the new size of the receive window in the data 'window' points to. - * - * Returns the "normal" error code: 0 for success, negative for failure. - */ -LIBSSH2_API int -libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel, - unsigned long adj, - unsigned char force, - unsigned int *window) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_receive_window_adjust(channel, adj, force, - window)); - return rc; -} - -int -_libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) -{ - if (channel->extData2_state == libssh2_NB_state_idle) { - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, - "Setting channel %lu/%lu handle_extended_data" - " mode to %d", - channel->local.id, channel->remote.id, ignore_mode); - channel->remote.extended_data_ignore_mode = ignore_mode; - - channel->extData2_state = libssh2_NB_state_created; - } - - if (channel->extData2_state == libssh2_NB_state_idle) { - if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { - int rc = - _libssh2_channel_flush(channel, - LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA); - if(LIBSSH2_ERROR_EAGAIN == rc) - return rc; - } - } - - channel->extData2_state = libssh2_NB_state_idle; - return 0; -} - -/* - * libssh2_channel_handle_extended_data2() - * - */ -LIBSSH2_API int -libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, - int mode) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, _libssh2_channel_extended_data(channel, - mode)); - return rc; -} - -/* - * libssh2_channel_handle_extended_data - * - * DEPRECATED DO NOTE USE! - * - * How should extended data look to the calling app? Keep it in separate - * channels[_read() _read_stdder()]? (NORMAL) Merge the extended data to the - * standard data? [everything via _read()]? (MERGE) Ignore it entirely [toss - * out packets as they come in]? (IGNORE) - */ -LIBSSH2_API void -libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, - int ignore_mode) -{ - (void)libssh2_channel_handle_extended_data2(channel, ignore_mode); -} - - - -/* - * _libssh2_channel_read - * - * Read data from a channel - * - * It is important to not return 0 until the currently read channel is - * complete. If we read stuff from the wire but it was no payload data to fill - * in the buffer with, we MUST make sure to return LIBSSH2_ERROR_EAGAIN. - * - * The receive window must be maintained (enlarged) by the user of this - * function. - */ -ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id, - char *buf, size_t buflen) -{ - LIBSSH2_SESSION *session = channel->session; - int rc; - int bytes_read = 0; - int bytes_want; - int unlink_packet; - LIBSSH2_PACKET *read_packet; - LIBSSH2_PACKET *read_next; - - if (channel->read_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "channel_read() wants %d bytes from channel %lu/%lu " - "stream #%d", - (int) buflen, channel->local.id, channel->remote.id, - stream_id); - channel->read_state = libssh2_NB_state_created; - } - - rc = 1; /* set to >0 to let the while loop start */ - - /* Process all pending incoming packets in all states in order to "even - out" the network readings. Tests prove that this way produces faster - transfers. */ - while (rc > 0) - rc = _libssh2_transport_read(session); - - if ((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) - return _libssh2_error(session, rc, "transport read"); - - read_packet = _libssh2_list_first(&session->packets); - while (read_packet && (bytes_read < (int) buflen)) { - /* previously this loop condition also checked for - !channel->remote.close but we cannot let it do this: - - We may have a series of packets to read that are still pending even - if a close has been received. Acknowledging the close too early - makes us flush buffers prematurely and loose data. - */ - - LIBSSH2_PACKET *readpkt = read_packet; - - /* In case packet gets destroyed during this iteration */ - read_next = _libssh2_list_next(&readpkt->node); - - channel->read_local_id = - _libssh2_ntohu32(readpkt->data + 1); - - /* - * Either we asked for a specific extended data stream - * (and data was available), - * or the standard stream (and data was available), - * or the standard stream with extended_data_merge - * enabled and data was available - */ - if ((stream_id - && (readpkt->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) - && (channel->local.id == channel->read_local_id) - && (stream_id == (int) _libssh2_ntohu32(readpkt->data + 5))) - || (!stream_id && (readpkt->data[0] == SSH_MSG_CHANNEL_DATA) - && (channel->local.id == channel->read_local_id)) - || (!stream_id - && (readpkt->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) - && (channel->local.id == channel->read_local_id) - && (channel->remote.extended_data_ignore_mode == - LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) { - - /* figure out much more data we want to read */ - bytes_want = buflen - bytes_read; - unlink_packet = FALSE; - - if (bytes_want >= (int) (readpkt->data_len - readpkt->data_head)) { - /* we want more than this node keeps, so adjust the number and - delete this node after the copy */ - bytes_want = readpkt->data_len - readpkt->data_head; - unlink_packet = TRUE; - } - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "channel_read() got %d of data from %lu/%lu/%d%s", - bytes_want, channel->local.id, - channel->remote.id, stream_id, - unlink_packet?" [ul]":""); - - /* copy data from this struct to the target buffer */ - memcpy(&buf[bytes_read], - &readpkt->data[readpkt->data_head], bytes_want); - - /* advance pointer and counter */ - readpkt->data_head += bytes_want; - bytes_read += bytes_want; - - /* if drained, remove from list */ - if (unlink_packet) { - /* detach readpkt from session->packets list */ - _libssh2_list_remove(&readpkt->node); - - LIBSSH2_FREE(session, readpkt->data); - LIBSSH2_FREE(session, readpkt); - } - } - - /* check the next struct in the chain */ - read_packet = read_next; - } - - if (!bytes_read) { - channel->read_state = libssh2_NB_state_idle; - - /* If the channel is already at EOF or even closed, we need to signal - that back. We may have gotten that info while draining the incoming - transport layer until EAGAIN so we must not be fooled by that - return code. */ - if(channel->remote.eof || channel->remote.close) - return 0; - else if(rc != LIBSSH2_ERROR_EAGAIN) - return 0; - - /* if the transport layer said EAGAIN then we say so as well */ - return _libssh2_error(session, rc, "would block"); - } - else - /* make sure we remain in the created state to focus on emptying the - data we already have in the packet brigade before we try to read - more off the network again */ - channel->read_state = libssh2_NB_state_created; - - return bytes_read; -} - -/* - * libssh2_channel_read_ex - * - * Read data from a channel (blocking or non-blocking depending on set state) - * - * When this is done non-blocking, it is important to not return 0 until the - * currently read channel is complete. If we read stuff from the wire but it - * was no payload data to fill in the buffer with, we MUST make sure to return - * LIBSSH2_ERROR_EAGAIN. - * - * This function will first make sure there's a receive window enough to - * receive a full buffer's wort of contents. An application may choose to - * adjust the receive window more to increase transfer performance. - */ -LIBSSH2_API ssize_t -libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, - size_t buflen) -{ - int rc; - unsigned long recv_window; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL); - - if(buflen > recv_window) { - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_receive_window_adjust(channel, buflen, - 1, NULL)); - } - - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_read(channel, stream_id, buf, buflen)); - return rc; -} - -/* - * _libssh2_channel_packet_data_len - * - * Return the size of the data block of the current packet, or 0 if there - * isn't a packet. - */ -size_t -_libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id) -{ - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_PACKET *read_packet; - uint32_t read_local_id; - - read_packet = _libssh2_list_first(&session->packets); - if (read_packet == NULL) - return 0; - - while (read_packet) { - read_local_id = _libssh2_ntohu32(read_packet->data + 1); - - /* - * Either we asked for a specific extended data stream - * (and data was available), - * or the standard stream (and data was available), - * or the standard stream with extended_data_merge - * enabled and data was available - */ - if ((stream_id - && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) - && (channel->local.id == read_local_id) - && (stream_id == (int) _libssh2_ntohu32(read_packet->data + 5))) - || - (!stream_id - && (read_packet->data[0] == SSH_MSG_CHANNEL_DATA) - && (channel->local.id == read_local_id)) - || - (!stream_id - && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) - && (channel->local.id == read_local_id) - && (channel->remote.extended_data_ignore_mode - == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) - { - return (read_packet->data_len - read_packet->data_head); - } - read_packet = _libssh2_list_next(&read_packet->node); - } - - return 0; -} - -/* - * _libssh2_channel_write - * - * Send data to a channel. Note that if this returns EAGAIN, the caller must - * call this function again with the SAME input arguments. - * - * Returns: number of bytes sent, or if it returns a negative number, that is - * the error code! - */ -ssize_t -_libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id, - const unsigned char *buf, size_t buflen) -{ - int rc = 0; - LIBSSH2_SESSION *session = channel->session; - ssize_t wrote = 0; /* counter for this specific this call */ - - /* In theory we could split larger buffers into several smaller packets - * but it turns out to be really hard and nasty to do while still offering - * the API/prototype. - * - * Instead we only deal with the first 32K in this call and for the parent - * function to call it again with the remainder! 32K is a conservative - * limit based on the text in RFC4253 section 6.1. - */ - if(buflen > 32700) - buflen = 32700; - - if (channel->write_state == libssh2_NB_state_idle) { - unsigned char *s = channel->write_packet; - - _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN, - "Writing %d bytes on channel %lu/%lu, stream #%d", - (int) buflen, channel->local.id, channel->remote.id, - stream_id); - - if (channel->local.close) - return _libssh2_error(channel->session, - LIBSSH2_ERROR_CHANNEL_CLOSED, - "We've already closed this channel"); - else if (channel->local.eof) - return _libssh2_error(channel->session, - LIBSSH2_ERROR_CHANNEL_EOF_SENT, - "EOF has already been received, " - "data might be ignored"); - - /* drain the incoming flow first, mostly to make sure we get all - * pending window adjust packets */ - do - rc = _libssh2_transport_read(session); - while (rc > 0); - - if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) - return rc; - - if(channel->local.window_size <= 0) - /* there's no room for data so we stop */ - return (rc==LIBSSH2_ERROR_EAGAIN?rc:0); - - channel->write_bufwrite = buflen; - - *(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : - SSH_MSG_CHANNEL_DATA; - _libssh2_store_u32(&s, channel->remote.id); - if (stream_id) - _libssh2_store_u32(&s, stream_id); - - /* Don't exceed the remote end's limits */ - /* REMEMBER local means local as the SOURCE of the data */ - if (channel->write_bufwrite > channel->local.window_size) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Splitting write block due to %lu byte " - "window_size on %lu/%lu/%d", - channel->local.window_size, channel->local.id, - channel->remote.id, stream_id); - channel->write_bufwrite = channel->local.window_size; - } - if (channel->write_bufwrite > channel->local.packet_size) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Splitting write block due to %lu byte " - "packet_size on %lu/%lu/%d", - channel->local.packet_size, channel->local.id, - channel->remote.id, stream_id); - channel->write_bufwrite = channel->local.packet_size; - } - /* store the size here only, the buffer is passed in as-is to - _libssh2_transport_send() */ - _libssh2_store_u32(&s, channel->write_bufwrite); - channel->write_packet_len = s - channel->write_packet; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Sending %d bytes on channel %lu/%lu, stream_id=%d", - (int) channel->write_bufwrite, channel->local.id, - channel->remote.id, stream_id); - - channel->write_state = libssh2_NB_state_created; - } - - if (channel->write_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, channel->write_packet, - channel->write_packet_len, - buf, channel->write_bufwrite); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, rc, - "Unable to send channel data"); - } - else if (rc) { - channel->write_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send channel data"); - } - /* Shrink local window size */ - channel->local.window_size -= channel->write_bufwrite; - - wrote += channel->write_bufwrite; - - /* Since _libssh2_transport_write() succeeded, we must return - now to allow the caller to provide the next chunk of data. - - We cannot move on to send the next piece of data that may - already have been provided in this same function call, as we - risk getting EAGAIN for that and we can't return information - both about sent data as well as EAGAIN. So, by returning short - now, the caller will call this function again with new data to - send */ - - channel->write_state = libssh2_NB_state_idle; - - return wrote; - } - - return LIBSSH2_ERROR_INVAL; /* reaching this point is really bad */ -} - -/* - * libssh2_channel_write_ex - * - * Send data to a channel - */ -LIBSSH2_API ssize_t -libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, - const char *buf, size_t buflen) -{ - ssize_t rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, - _libssh2_channel_write(channel, stream_id, - (unsigned char *)buf, buflen)); - return rc; -} - -/* - * channel_send_eof - * - * Send EOF on channel - */ -static int channel_send_eof(LIBSSH2_CHANNEL *channel) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char packet[5]; /* packet_type(1) + channelno(4) */ - int rc; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Sending EOF on channel %lu/%lu", - channel->local.id, channel->remote.id); - packet[0] = SSH_MSG_CHANNEL_EOF; - _libssh2_htonu32(packet + 1, channel->remote.id); - rc = _libssh2_transport_send(session, packet, 5, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending EOF"); - return rc; - } - else if (rc) { - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send EOF on channel"); - } - channel->local.eof = 1; - - return 0; -} - -/* - * libssh2_channel_send_eof - * - * Send EOF on channel - */ -LIBSSH2_API int -libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, channel_send_eof(channel)); - return rc; -} - -/* - * libssh2_channel_eof - * - * Read channel's eof status - */ -LIBSSH2_API int -libssh2_channel_eof(LIBSSH2_CHANNEL * channel) -{ - LIBSSH2_SESSION *session; - LIBSSH2_PACKET *packet; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - session = channel->session; - packet = _libssh2_list_first(&session->packets); - - while (packet) { - if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) - || (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) - && (channel->local.id == _libssh2_ntohu32(packet->data + 1))) { - /* There's data waiting to be read yet, mask the EOF status */ - return 0; - } - packet = _libssh2_list_next(&packet->node); - } - - return channel->remote.eof; -} - -/* - * channel_wait_eof - * - * Awaiting channel EOF - */ -static int channel_wait_eof(LIBSSH2_CHANNEL *channel) -{ - LIBSSH2_SESSION *session = channel->session; - int rc; - - if (channel->wait_eof_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Awaiting close of channel %lu/%lu", channel->local.id, - channel->remote.id); - - channel->wait_eof_state = libssh2_NB_state_created; - } - - /* - * While channel is not eof, read more packets from the network. - * Either the EOF will be set or network timeout will occur. - */ - do { - if (channel->remote.eof) { - break; - } - rc = _libssh2_transport_read(session); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - else if (rc < 0) { - channel->wait_eof_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "_libssh2_transport_read() bailed out!"); - } - } while (1); - - channel->wait_eof_state = libssh2_NB_state_idle; - - return 0; -} - -/* - * libssh2_channel_wait_eof - * - * Awaiting channel EOF - */ -LIBSSH2_API int -libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, channel_wait_eof(channel)); - return rc; -} - -int _libssh2_channel_close(LIBSSH2_CHANNEL * channel) -{ - LIBSSH2_SESSION *session = channel->session; - int rc = 0; - int retcode; - - if (channel->local.close) { - /* Already closed, act like we sent another close, - * even though we didn't... shhhhhh */ - channel->close_state = libssh2_NB_state_idle; - return 0; - } - - if (!channel->local.eof) - if ((retcode = channel_send_eof(channel))) - return retcode; - - /* ignore if we have received a remote eof or not, as it is now too - late for us to wait for it. Continue closing! */ - - if (channel->close_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Closing channel %lu/%lu", - channel->local.id, channel->remote.id); - - channel->close_packet[0] = SSH_MSG_CHANNEL_CLOSE; - _libssh2_htonu32(channel->close_packet + 1, channel->remote.id); - - channel->close_state = libssh2_NB_state_created; - } - - if (channel->close_state == libssh2_NB_state_created) { - retcode = _libssh2_transport_send(session, channel->close_packet, 5, - NULL, 0); - if (retcode == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, rc, - "Would block sending close-channel"); - return retcode; - } else if (retcode) { - channel->close_state = libssh2_NB_state_idle; - return _libssh2_error(session, retcode, - "Unable to send close-channel request"); - } - - channel->close_state = libssh2_NB_state_sent; - } - - if (channel->close_state == libssh2_NB_state_sent) { - /* We must wait for the remote SSH_MSG_CHANNEL_CLOSE message */ - - while (!channel->remote.close && !rc && - (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED)) - rc = _libssh2_transport_read(session); - } - - if(rc != LIBSSH2_ERROR_EAGAIN) { - /* set the local close state first when we're perfectly confirmed to not - do any more EAGAINs */ - channel->local.close = 1; - - /* We call the callback last in this function to make it keep the local - data as long as EAGAIN is returned. */ - if (channel->close_cb) { - LIBSSH2_CHANNEL_CLOSE(session, channel); - } - - channel->close_state = libssh2_NB_state_idle; - } - - /* return 0 or an error */ - return rc>=0?0:rc; -} - -/* - * libssh2_channel_close - * - * Close a channel - */ -LIBSSH2_API int -libssh2_channel_close(LIBSSH2_CHANNEL *channel) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, _libssh2_channel_close(channel) ); - return rc; -} - -/* - * channel_wait_closed - * - * Awaiting channel close after EOF - */ -static int channel_wait_closed(LIBSSH2_CHANNEL *channel) -{ - LIBSSH2_SESSION *session = channel->session; - int rc; - - if (!libssh2_channel_eof(channel)) { - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "libssh2_channel_wait_closed() invoked when " - "channel is not in EOF state"); - } - - if (channel->wait_closed_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Awaiting close of channel %lu/%lu", channel->local.id, - channel->remote.id); - - channel->wait_closed_state = libssh2_NB_state_created; - } - - /* - * While channel is not closed, read more packets from the network. - * Either the channel will be closed or network timeout will occur. - */ - if (!channel->remote.close) { - do { - rc = _libssh2_transport_read(session); - if (channel->remote.close) - /* it is now closed, move on! */ - break; - } while (rc > 0); - if(rc < 0) - return rc; - } - - channel->wait_closed_state = libssh2_NB_state_idle; - - return 0; -} - -/* - * libssh2_channel_wait_closed - * - * Awaiting channel close after EOF - */ -LIBSSH2_API int -libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, channel_wait_closed(channel)); - return rc; -} - -/* - * _libssh2_channel_free - * - * Make sure a channel is closed, then remove the channel from the session - * and free its resource(s) - * - * Returns 0 on success, negative on failure - */ -int _libssh2_channel_free(LIBSSH2_CHANNEL *channel) -{ - LIBSSH2_SESSION *session = channel->session; - unsigned char channel_id[4]; - unsigned char *data; - size_t data_len; - int rc; - - assert(session); - - if (channel->free_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Freeing channel %lu/%lu resources", channel->local.id, - channel->remote.id); - - channel->free_state = libssh2_NB_state_created; - } - - /* Allow channel freeing even when the socket has lost its connection */ - if (!channel->local.close - && (session->socket_state == LIBSSH2_SOCKET_CONNECTED)) { - rc = _libssh2_channel_close(channel); - - if(rc == LIBSSH2_ERROR_EAGAIN) - return rc; - - /* ignore all other errors as they otherwise risk blocking the channel - free from happening */ - } - - channel->free_state = libssh2_NB_state_idle; - - if (channel->exit_signal) { - LIBSSH2_FREE(session, channel->exit_signal); - } - - /* - * channel->remote.close *might* not be set yet, Well... - * We've sent the close packet, what more do you want? - * Just let packet_add ignore it when it finally arrives - */ - - /* Clear out packets meant for this channel */ - _libssh2_htonu32(channel_id, channel->local.id); - while ((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA, &data, - &data_len, 1, channel_id, 4) >= 0) - || - (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, - &data_len, 1, channel_id, 4) >= 0)) { - LIBSSH2_FREE(session, data); - } - - /* free "channel_type" */ - if (channel->channel_type) { - LIBSSH2_FREE(session, channel->channel_type); - } - - /* Unlink from channel list */ - _libssh2_list_remove(&channel->node); - - /* - * Make sure all memory used in the state variables are free - */ - if (channel->setenv_packet) { - LIBSSH2_FREE(session, channel->setenv_packet); - } - if (channel->reqX11_packet) { - LIBSSH2_FREE(session, channel->reqX11_packet); - } - if (channel->process_packet) { - LIBSSH2_FREE(session, channel->process_packet); - } - - LIBSSH2_FREE(session, channel); - - return 0; -} - -/* - * libssh2_channel_free - * - * Make sure a channel is closed, then remove the channel from the session - * and free its resource(s) - * - * Returns 0 on success, negative on failure - */ -LIBSSH2_API int -libssh2_channel_free(LIBSSH2_CHANNEL *channel) -{ - int rc; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, channel->session, _libssh2_channel_free(channel)); - return rc; -} -/* - * libssh2_channel_window_read_ex - * - * Check the status of the read window. Returns the number of bytes which the - * remote end may send without overflowing the window limit read_avail (if - * passed) will be populated with the number of bytes actually available to be - * read window_size_initial (if passed) will be populated with the - * window_size_initial as defined by the channel_open request - */ -LIBSSH2_API unsigned long -libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, - unsigned long *read_avail, - unsigned long *window_size_initial) -{ - if(!channel) - return 0; /* no channel, no window! */ - - if (window_size_initial) { - *window_size_initial = channel->remote.window_size_initial; - } - - if (read_avail) { - size_t bytes_queued = 0; - LIBSSH2_PACKET *packet = - _libssh2_list_first(&channel->session->packets); - - while (packet) { - unsigned char packet_type = packet->data[0]; - - if (((packet_type == SSH_MSG_CHANNEL_DATA) - || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) - && (_libssh2_ntohu32(packet->data + 1) == channel->local.id)) { - bytes_queued += packet->data_len - packet->data_head; - } - - packet = _libssh2_list_next(&packet->node); - } - - *read_avail = bytes_queued; - } - - return channel->remote.window_size; -} - -/* - * libssh2_channel_window_write_ex - * - * Check the status of the write window Returns the number of bytes which may - * be safely writen on the channel without blocking window_size_initial (if - * passed) will be populated with the size of the initial window as defined by - * the channel_open request - */ -LIBSSH2_API unsigned long -libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, - unsigned long *window_size_initial) -{ - if(!channel) - return 0; /* no channel, no window! */ - - if (window_size_initial) { - /* For locally initiated channels this is very often 0, so it's not - * *that* useful as information goes */ - *window_size_initial = channel->local.window_size_initial; - } - - return channel->local.window_size; -} diff --git a/vendor/libssh2-1.4.2/src/channel.h b/vendor/libssh2-1.4.2/src/channel.h deleted file mode 100644 index dc0ee3764..000000000 --- a/vendor/libssh2-1.4.2/src/channel.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef __LIBSSH2_CHANNEL_H -#define __LIBSSH2_CHANNEL_H -/* Copyright (c) 2008-2010 by Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -/* - * _libssh2_channel_receive_window_adjust - * - * Adjust the receive window for a channel by adjustment bytes. If the amount - * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the - * adjustment amount will be queued for a later packet. - * - * Always non-blocking. - */ -int _libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, - uint32_t adjustment, - unsigned char force, - unsigned int *store); - -/* - * _libssh2_channel_flush - * - * Flush data from one (or all) stream - * Returns number of bytes flushed, or negative on failure - */ -int _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid); - -/* - * _libssh2_channel_free - * - * Make sure a channel is closed, then remove the channel from the session - * and free its resource(s) - * - * Returns 0 on success, negative on failure - */ -int _libssh2_channel_free(LIBSSH2_CHANNEL *channel); - -int -_libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); - -/* - * _libssh2_channel_write - * - * Send data to a channel - */ -ssize_t -_libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id, - const unsigned char *buf, size_t buflen); - -/* - * _libssh2_channel_open - * - * Establish a generic session channel - */ -LIBSSH2_CHANNEL * -_libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, - uint32_t channel_type_len, - uint32_t window_size, - uint32_t packet_size, - const unsigned char *message, size_t message_len); - - -/* - * _libssh2_channel_process_startup - * - * Primitive for libssh2_channel_(shell|exec|subsystem) - */ -int -_libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, - const char *request, size_t request_len, - const char *message, size_t message_len); - -/* - * _libssh2_channel_read - * - * Read data from a channel - * - * It is important to not return 0 until the currently read channel is - * complete. If we read stuff from the wire but it was no payload data to fill - * in the buffer with, we MUST make sure to return PACKET_EAGAIN. - */ -ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id, - char *buf, size_t buflen); - -uint32_t _libssh2_channel_nextid(LIBSSH2_SESSION * session); - -LIBSSH2_CHANNEL *_libssh2_channel_locate(LIBSSH2_SESSION * session, - uint32_t channel_id); - -size_t _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, - int stream_id); - -int _libssh2_channel_close(LIBSSH2_CHANNEL * channel); - -/* - * _libssh2_channel_forward_cancel - * - * Stop listening on a remote port and free the listener - * Toss out any pending (un-accept()ed) connections - * - * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error - */ -int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener); - -#endif /* __LIBSSH2_CHANNEL_H */ - diff --git a/vendor/libssh2-1.4.2/src/comp.c b/vendor/libssh2-1.4.2/src/comp.c deleted file mode 100644 index 0296f625a..000000000 --- a/vendor/libssh2-1.4.2/src/comp.c +++ /dev/null @@ -1,390 +0,0 @@ -/* Copyright (c) 2004-2007, Sara Golemon - * Copyright (c) 2010, Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#ifdef LIBSSH2_HAVE_ZLIB -# include -#endif - -#include "comp.h" - -/* ******** - * none * - ******** */ - -/* - * comp_method_none_comp - * - * Minimalist compression: Absolutely none - */ -static int -comp_method_none_comp(LIBSSH2_SESSION *session, - unsigned char *dest, - size_t *dest_len, - const unsigned char *src, - size_t src_len, - void **abstract) -{ - (void) session; - (void) abstract; - (void) dest; - (void) dest_len; - (void) src; - (void) src_len; - - return 0; -} - -/* - * comp_method_none_decomp - * - * Minimalist decompression: Absolutely none - */ -static int -comp_method_none_decomp(LIBSSH2_SESSION * session, - unsigned char **dest, - size_t *dest_len, - size_t payload_limit, - const unsigned char *src, - size_t src_len, void **abstract) -{ - (void) session; - (void) payload_limit; - (void) abstract; - *dest = (unsigned char *) src; - *dest_len = src_len; - return 0; -} - - - -static const LIBSSH2_COMP_METHOD comp_method_none = { - "none", - 0, /* not really compressing */ - NULL, - comp_method_none_comp, - comp_method_none_decomp, - NULL -}; - -#ifdef LIBSSH2_HAVE_ZLIB -/* ******** - * zlib * - ******** */ - -/* Memory management wrappers - * Yes, I realize we're doing a callback to a callback, - * Deal... - */ - -static voidpf -comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size) -{ - LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque; - - return (voidpf) LIBSSH2_ALLOC(session, items * size); -} - -static void -comp_method_zlib_free(voidpf opaque, voidpf address) -{ - LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque; - - LIBSSH2_FREE(session, address); -} - - - -/* libssh2_comp_method_zlib_init - * All your bandwidth are belong to us (so save some) - */ -static int -comp_method_zlib_init(LIBSSH2_SESSION * session, int compr, - void **abstract) -{ - z_stream *strm; - int status; - - strm = LIBSSH2_ALLOC(session, sizeof(z_stream)); - if (!strm) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "zlib compression/decompression"); - } - memset(strm, 0, sizeof(z_stream)); - - strm->opaque = (voidpf) session; - strm->zalloc = (alloc_func) comp_method_zlib_alloc; - strm->zfree = (free_func) comp_method_zlib_free; - if (compr) { - /* deflate */ - status = deflateInit(strm, Z_DEFAULT_COMPRESSION); - } else { - /* inflate */ - status = inflateInit(strm); - } - - if (status != Z_OK) { - LIBSSH2_FREE(session, strm); - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "unhandled zlib error %d", status); - return LIBSSH2_ERROR_COMPRESS; - } - *abstract = strm; - - return LIBSSH2_ERROR_NONE; -} - -/* - * libssh2_comp_method_zlib_comp - * - * Compresses source to destination. Without allocation. - */ -static int -comp_method_zlib_comp(LIBSSH2_SESSION *session, - unsigned char *dest, - - /* dest_len is a pointer to allow this function to - update it with the final actual size used */ - size_t *dest_len, - const unsigned char *src, - size_t src_len, - void **abstract) -{ - z_stream *strm = *abstract; - int out_maxlen = *dest_len; - int status; - - strm->next_in = (unsigned char *) src; - strm->avail_in = src_len; - strm->next_out = dest; - strm->avail_out = out_maxlen; - - status = deflate(strm, Z_PARTIAL_FLUSH); - - if (status != Z_OK) { - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "unhandled zlib compression error %d", status); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "compression failure"); - } - - *dest_len = out_maxlen - strm->avail_out; - return 0; -} - -/* - * libssh2_comp_method_zlib_decomp - * - * Decompresses source to destination. Allocates the output memory. - */ -static int -comp_method_zlib_decomp(LIBSSH2_SESSION * session, - unsigned char **dest, - size_t *dest_len, - size_t payload_limit, - const unsigned char *src, - size_t src_len, void **abstract) -{ - z_stream *strm = *abstract; - /* A short-term alloc of a full data chunk is better than a series of - reallocs */ - char *out; - int out_maxlen = 8 * src_len; - int limiter = 0; - - /* If strm is null, then we have not yet been initialized. */ - if (strm == NULL) - return _libssh2_error(session, LIBSSH2_ERROR_COMPRESS, - "decompression unitilized");; - - /* In practice they never come smaller than this */ - if (out_maxlen < 25) - out_maxlen = 25; - - if (out_maxlen > (int) payload_limit) - out_maxlen = payload_limit; - - strm->next_in = (unsigned char *) src; - strm->avail_in = src_len; - strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen); - out = (char *) strm->next_out; - strm->avail_out = out_maxlen; - if (!strm->next_out) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate decompression buffer"); - while (strm->avail_in) { - int status; - - status = inflate(strm, Z_PARTIAL_FLUSH); - - if (status != Z_OK) { - LIBSSH2_FREE(session, out); - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "unhandled zlib error %d", status); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "decompression failure"); - } - if (strm->avail_in) { - size_t out_ofs = out_maxlen - strm->avail_out; - char *newout; - - out_maxlen += 8 * strm->avail_in; - - if ((out_maxlen > (int) payload_limit) && limiter++) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "Excessive growth in decompression phase"); - } - - newout = LIBSSH2_REALLOC(session, out, out_maxlen); - if (!newout) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to expand decompression buffer"); - } - out = newout; - strm->next_out = (unsigned char *) out + out_ofs; - strm->avail_out += 8 * strm->avail_in; - } else - while (!strm->avail_out) { - /* Done with input, might be a byte or two in internal buffer - * during compress. Or potentially many bytes if it's a - * decompress - */ - int grow_size = 2048; - char *newout; - - if (out_maxlen >= (int) payload_limit) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "Excessive growth in decompression " - "phase"); - } - - if (grow_size > (int) (payload_limit - out_maxlen)) { - grow_size = payload_limit - out_maxlen; - } - - out_maxlen += grow_size; - strm->avail_out = grow_size; - - newout = LIBSSH2_REALLOC(session, out, out_maxlen); - if (!newout) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to expand final " - "decompress buffer"); - } - out = newout; - strm->next_out = (unsigned char *) out + out_maxlen - - grow_size; - - status = inflate(strm, Z_PARTIAL_FLUSH); - - if (status != Z_OK) { - LIBSSH2_FREE(session, out); - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "unhandled zlib error %d", status); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "decompression failure"); - } - } - } - - *dest = (unsigned char *) out; - *dest_len = out_maxlen - strm->avail_out; - - return 0; -} - - -/* libssh2_comp_method_zlib_dtor - * All done, no more compression for you - */ -static int -comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compr, void **abstract) -{ - z_stream *strm = *abstract; - - if (strm) { - if (compr) - deflateEnd(strm); - else - inflateEnd(strm); - LIBSSH2_FREE(session, strm); - } - - *abstract = NULL; - return 0; -} - -static const LIBSSH2_COMP_METHOD comp_method_zlib = { - "zlib", - 1, /* yes, this compresses */ - comp_method_zlib_init, - comp_method_zlib_comp, - comp_method_zlib_decomp, - comp_method_zlib_dtor, -}; -#endif /* LIBSSH2_HAVE_ZLIB */ - -/* If compression is enabled by the API, then this array is used which then - may allow compression if zlib is available at build time */ -static const LIBSSH2_COMP_METHOD *comp_methods[] = { -#ifdef LIBSSH2_HAVE_ZLIB - &comp_method_zlib, -#endif /* LIBSSH2_HAVE_ZLIB */ - &comp_method_none, - NULL -}; - -/* If compression is disabled by the API, then this array is used */ -static const LIBSSH2_COMP_METHOD *no_comp_methods[] = { - &comp_method_none, - NULL -}; - -const LIBSSH2_COMP_METHOD ** -_libssh2_comp_methods(LIBSSH2_SESSION *session) -{ - if(session->flag.compress) - return comp_methods; - else - return no_comp_methods; -} diff --git a/vendor/libssh2-1.4.2/src/comp.h b/vendor/libssh2-1.4.2/src/comp.h deleted file mode 100644 index 8edc15029..000000000 --- a/vendor/libssh2-1.4.2/src/comp.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef __LIBSSH2_COMP_H -#define __LIBSSH2_COMP_H - -/* Copyright (C) 2009-2010 by Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - */ - -#include "libssh2_priv.h" - -const LIBSSH2_COMP_METHOD **_libssh2_comp_methods(LIBSSH2_SESSION *session); - -#endif /* __LIBSSH2_COMP_H */ diff --git a/vendor/libssh2-1.4.2/src/crypt.c b/vendor/libssh2-1.4.2/src/crypt.c deleted file mode 100644 index 93d99c4b9..000000000 --- a/vendor/libssh2-1.4.2/src/crypt.c +++ /dev/null @@ -1,334 +0,0 @@ -/* Copyright (c) 2009, 2010 Simon Josefsson - * Copyright (c) 2004-2007, Sara Golemon - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -#ifdef LIBSSH2_CRYPT_NONE - -/* crypt_none_crypt - * Minimalist cipher: VERY secure *wink* - */ -static int -crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf, - void **abstract) -{ - /* Do nothing to the data! */ - return 0; -} - -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_none = { - "none", - 8, /* blocksize (SSH2 defines minimum blocksize as 8) */ - 0, /* iv_len */ - 0, /* secret_len */ - 0, /* flags */ - NULL, - crypt_none_crypt, - NULL -}; -#endif /* LIBSSH2_CRYPT_NONE */ - -struct crypt_ctx -{ - int encrypt; - _libssh2_cipher_type(algo); - _libssh2_cipher_ctx h; -}; - -static int -crypt_init(LIBSSH2_SESSION * session, - const LIBSSH2_CRYPT_METHOD * method, - unsigned char *iv, int *free_iv, - unsigned char *secret, int *free_secret, - int encrypt, void **abstract) -{ - struct crypt_ctx *ctx = LIBSSH2_ALLOC(session, - sizeof(struct crypt_ctx)); - if (!ctx) - return LIBSSH2_ERROR_ALLOC; - - ctx->encrypt = encrypt; - ctx->algo = method->algo; - if (_libssh2_cipher_init(&ctx->h, ctx->algo, iv, secret, encrypt)) { - LIBSSH2_FREE(session, ctx); - return -1; - } - *abstract = ctx; - *free_iv = 1; - *free_secret = 1; - return 0; -} - -static int -crypt_encrypt(LIBSSH2_SESSION * session, unsigned char *block, - void **abstract) -{ - struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract; - (void) session; - return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block); -} - -static int -crypt_dtor(LIBSSH2_SESSION * session, void **abstract) -{ - struct crypt_ctx **cctx = (struct crypt_ctx **) abstract; - if (cctx && *cctx) { - _libssh2_cipher_dtor(&(*cctx)->h); - LIBSSH2_FREE(session, *cctx); - *abstract = NULL; - } - return 0; -} - -#if LIBSSH2_AES_CTR -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = { - "aes128-ctr", - 16, /* blocksize */ - 16, /* initial value length */ - 16, /* secret length -- 16*8 == 128bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes128ctr -}; - -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = { - "aes192-ctr", - 16, /* blocksize */ - 16, /* initial value length */ - 24, /* secret length -- 24*8 == 192bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes192ctr -}; - -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = { - "aes256-ctr", - 16, /* blocksize */ - 16, /* initial value length */ - 32, /* secret length -- 32*8 == 256bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes256ctr -}; -#endif - -#if LIBSSH2_AES -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = { - "aes128-cbc", - 16, /* blocksize */ - 16, /* initial value length */ - 16, /* secret length -- 16*8 == 128bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes128 -}; - -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = { - "aes192-cbc", - 16, /* blocksize */ - 16, /* initial value length */ - 24, /* secret length -- 24*8 == 192bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes192 -}; - -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = { - "aes256-cbc", - 16, /* blocksize */ - 16, /* initial value length */ - 32, /* secret length -- 32*8 == 256bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes256 -}; - -/* rijndael-cbc@lysator.liu.se == aes256-cbc */ -static const LIBSSH2_CRYPT_METHOD - libssh2_crypt_method_rijndael_cbc_lysator_liu_se = { - "rijndael-cbc@lysator.liu.se", - 16, /* blocksize */ - 16, /* initial value length */ - 32, /* secret length -- 32*8 == 256bit */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_aes256 -}; -#endif /* LIBSSH2_AES */ - -#if LIBSSH2_BLOWFISH -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = { - "blowfish-cbc", - 8, /* blocksize */ - 8, /* initial value length */ - 16, /* secret length */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_blowfish -}; -#endif /* LIBSSH2_BLOWFISH */ - -#if LIBSSH2_RC4 -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = { - "arcfour", - 8, /* blocksize */ - 8, /* initial value length */ - 16, /* secret length */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_arcfour -}; - -static int -crypt_init_arcfour128(LIBSSH2_SESSION * session, - const LIBSSH2_CRYPT_METHOD * method, - unsigned char *iv, int *free_iv, - unsigned char *secret, int *free_secret, - int encrypt, void **abstract) -{ - int rc; - - rc = crypt_init (session, method, iv, free_iv, secret, free_secret, - encrypt, abstract); - if (rc == 0) { - struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract; - unsigned char block[8]; - size_t discard = 1536; - for (; discard; discard -= 8) - _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block); - } - - return rc; -} - -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour128 = { - "arcfour128", - 8, /* blocksize */ - 8, /* initial value length */ - 16, /* secret length */ - 0, /* flags */ - &crypt_init_arcfour128, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_arcfour -}; -#endif /* LIBSSH2_RC4 */ - -#if LIBSSH2_CAST -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = { - "cast128-cbc", - 8, /* blocksize */ - 8, /* initial value length */ - 16, /* secret length */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_cast5 -}; -#endif /* LIBSSH2_CAST */ - -#if LIBSSH2_3DES -static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = { - "3des-cbc", - 8, /* blocksize */ - 8, /* initial value length */ - 24, /* secret length */ - 0, /* flags */ - &crypt_init, - &crypt_encrypt, - &crypt_dtor, - _libssh2_cipher_3des -}; -#endif - -static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = { -#if LIBSSH2_AES_CTR - &libssh2_crypt_method_aes128_ctr, - &libssh2_crypt_method_aes192_ctr, - &libssh2_crypt_method_aes256_ctr, -#endif /* LIBSSH2_AES */ -#if LIBSSH2_AES - &libssh2_crypt_method_aes256_cbc, - &libssh2_crypt_method_rijndael_cbc_lysator_liu_se, /* == aes256-cbc */ - &libssh2_crypt_method_aes192_cbc, - &libssh2_crypt_method_aes128_cbc, -#endif /* LIBSSH2_AES */ -#if LIBSSH2_BLOWFISH - &libssh2_crypt_method_blowfish_cbc, -#endif /* LIBSSH2_BLOWFISH */ -#if LIBSSH2_RC4 - &libssh2_crypt_method_arcfour128, - &libssh2_crypt_method_arcfour, -#endif /* LIBSSH2_RC4 */ -#if LIBSSH2_CAST - &libssh2_crypt_method_cast128_cbc, -#endif /* LIBSSH2_CAST */ -#if LIBSSH2_3DES - &libssh2_crypt_method_3des_cbc, -#endif /* LIBSSH2_DES */ -#ifdef LIBSSH2_CRYPT_NONE - &libssh2_crypt_method_none, -#endif - NULL -}; - -/* Expose to kex.c */ -const LIBSSH2_CRYPT_METHOD ** -libssh2_crypt_methods(void) -{ - return _libssh2_crypt_methods; -} diff --git a/vendor/libssh2-1.4.2/src/crypto.h b/vendor/libssh2-1.4.2/src/crypto.h deleted file mode 100644 index 8cf34f5b8..000000000 --- a/vendor/libssh2-1.4.2/src/crypto.h +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (C) 2009, 2010 Simon Josefsson - * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. - * Copyright (C) 2010 Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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 LIBSSH2_CRYPTO_H -#define LIBSSH2_CRYPTO_H - -#ifdef LIBSSH2_LIBGCRYPT -#include "libgcrypt.h" -#else -#include "openssl.h" -#endif - -int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, - const unsigned char *edata, - unsigned long elen, - const unsigned char *ndata, - unsigned long nlen, - const unsigned char *ddata, - unsigned long dlen, - const unsigned char *pdata, - unsigned long plen, - const unsigned char *qdata, - unsigned long qlen, - const unsigned char *e1data, - unsigned long e1len, - const unsigned char *e2data, - unsigned long e2len, - const unsigned char *coeffdata, unsigned long coefflen); -int _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, - LIBSSH2_SESSION * session, - const char *filename, - unsigned const char *passphrase); -int _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa, - const unsigned char *sig, - unsigned long sig_len, - const unsigned char *m, unsigned long m_len); -int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, - libssh2_rsa_ctx * rsactx, - const unsigned char *hash, - size_t hash_len, - unsigned char **signature, - size_t *signature_len); - -int _libssh2_dsa_new(libssh2_dsa_ctx ** dsa, - const unsigned char *pdata, - unsigned long plen, - const unsigned char *qdata, - unsigned long qlen, - const unsigned char *gdata, - unsigned long glen, - const unsigned char *ydata, - unsigned long ylen, - const unsigned char *x, unsigned long x_len); -int _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, - LIBSSH2_SESSION * session, - const char *filename, - unsigned const char *passphrase); -int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, - const unsigned char *sig, - const unsigned char *m, unsigned long m_len); -int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, - const unsigned char *hash, - unsigned long hash_len, unsigned char *sig); - -int _libssh2_cipher_init(_libssh2_cipher_ctx * h, - _libssh2_cipher_type(algo), - unsigned char *iv, - unsigned char *secret, int encrypt); - -int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, - _libssh2_cipher_type(algo), - int encrypt, unsigned char *block); - -int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - const char *privatekey, - const char *passphrase); - -void _libssh2_init_aes_ctr(void); - -#endif diff --git a/vendor/libssh2-1.4.2/src/global.c b/vendor/libssh2-1.4.2/src/global.c deleted file mode 100644 index dc45e7003..000000000 --- a/vendor/libssh2-1.4.2/src/global.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) 2010 Lars Nordin - * Copyright (C) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -static int _libssh2_initialized = 0; -static int _libssh2_init_flags = 0; - -LIBSSH2_API int -libssh2_init(int flags) -{ - if (_libssh2_initialized == 0 && !(flags & LIBSSH2_INIT_NO_CRYPTO)) { - libssh2_crypto_init(); - _libssh2_init_aes_ctr(); - } - - _libssh2_initialized++; - _libssh2_init_flags |= flags; - - return 0; -} - -LIBSSH2_API void -libssh2_exit(void) -{ - if (_libssh2_initialized == 0) - return; - - _libssh2_initialized--; - - if (!(_libssh2_init_flags & LIBSSH2_INIT_NO_CRYPTO)) { - libssh2_crypto_exit(); - } - - return; -} - -void -_libssh2_init_if_needed(void) -{ - if (_libssh2_initialized == 0) - (void)libssh2_init (0); -} diff --git a/vendor/libssh2-1.4.2/src/hostkey.c b/vendor/libssh2-1.4.2/src/hostkey.c deleted file mode 100644 index 53f7479ca..000000000 --- a/vendor/libssh2-1.4.2/src/hostkey.c +++ /dev/null @@ -1,485 +0,0 @@ -/* Copyright (c) 2004-2006, Sara Golemon - * Copyright (c) 2009 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include "misc.h" - -/* Needed for struct iovec on some platforms */ -#ifdef HAVE_SYS_UIO_H -#include -#endif - -#if LIBSSH2_RSA -/* *********** - * ssh-rsa * - *********** */ - -static int hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session, - void **abstract); - -/* - * hostkey_method_ssh_rsa_init - * - * Initialize the server hostkey working area with e/n pair - */ -static int -hostkey_method_ssh_rsa_init(LIBSSH2_SESSION * session, - const unsigned char *hostkey_data, - size_t hostkey_data_len, - void **abstract) -{ - libssh2_rsa_ctx *rsactx; - const unsigned char *s, *e, *n; - unsigned long len, e_len, n_len; - - (void) hostkey_data_len; - - if (*abstract) { - hostkey_method_ssh_rsa_dtor(session, abstract); - *abstract = NULL; - } - - s = hostkey_data; - len = _libssh2_ntohu32(s); - s += 4; - - if (len != 7 || strncmp((char *) s, "ssh-rsa", 7) != 0) { - return -1; - } - s += 7; - - e_len = _libssh2_ntohu32(s); - s += 4; - - e = s; - s += e_len; - n_len = _libssh2_ntohu32(s); - s += 4; - n = s; - - if (_libssh2_rsa_new(&rsactx, e, e_len, n, n_len, NULL, 0, - NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0)) - return -1; - - *abstract = rsactx; - - return 0; -} - -/* - * hostkey_method_ssh_rsa_initPEM - * - * Load a Private Key from a PEM file - */ -static int -hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION * session, - const char *privkeyfile, - unsigned const char *passphrase, - void **abstract) -{ - libssh2_rsa_ctx *rsactx; - int ret; - - if (*abstract) { - hostkey_method_ssh_rsa_dtor(session, abstract); - *abstract = NULL; - } - - ret = _libssh2_rsa_new_private(&rsactx, session, privkeyfile, passphrase); - if (ret) { - return -1; - } - - *abstract = rsactx; - - return 0; -} - -/* - * hostkey_method_ssh_rsa_sign - * - * Verify signature created by remote - */ -static int -hostkey_method_ssh_rsa_sig_verify(LIBSSH2_SESSION * session, - const unsigned char *sig, - size_t sig_len, - const unsigned char *m, - size_t m_len, void **abstract) -{ - libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract); - (void) session; - - /* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */ - sig += 15; - sig_len -= 15; - return _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len); -} - -/* - * hostkey_method_ssh_rsa_signv - * - * Construct a signature from an array of vectors - */ -static int -hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION * session, - unsigned char **signature, - size_t *signature_len, - int veccount, - const struct iovec datavec[], - void **abstract) -{ - libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract); - int ret; - int i; - unsigned char hash[SHA_DIGEST_LENGTH]; - libssh2_sha1_ctx ctx; - - libssh2_sha1_init(&ctx); - for(i = 0; i < veccount; i++) { - libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len); - } - libssh2_sha1_final(ctx, hash); - - ret = _libssh2_rsa_sha1_sign(session, rsactx, hash, SHA_DIGEST_LENGTH, - signature, signature_len); - if (ret) { - return -1; - } - - return 0; -} - -/* - * hostkey_method_ssh_rsa_dtor - * - * Shutdown the hostkey - */ -static int -hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session, void **abstract) -{ - libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract); - (void) session; - - _libssh2_rsa_free(rsactx); - - *abstract = NULL; - - return 0; -} - -static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa = { - "ssh-rsa", - MD5_DIGEST_LENGTH, - hostkey_method_ssh_rsa_init, - hostkey_method_ssh_rsa_initPEM, - hostkey_method_ssh_rsa_sig_verify, - hostkey_method_ssh_rsa_signv, - NULL, /* encrypt */ - hostkey_method_ssh_rsa_dtor, -}; -#endif /* LIBSSH2_RSA */ - -#if LIBSSH2_DSA -/* *********** - * ssh-dss * - *********** */ - -static int hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session, - void **abstract); - -/* - * hostkey_method_ssh_dss_init - * - * Initialize the server hostkey working area with p/q/g/y set - */ -static int -hostkey_method_ssh_dss_init(LIBSSH2_SESSION * session, - const unsigned char *hostkey_data, - size_t hostkey_data_len, - void **abstract) -{ - libssh2_dsa_ctx *dsactx; - const unsigned char *p, *q, *g, *y, *s; - unsigned long p_len, q_len, g_len, y_len, len; - (void) hostkey_data_len; - - if (*abstract) { - hostkey_method_ssh_dss_dtor(session, abstract); - *abstract = NULL; - } - - s = hostkey_data; - len = _libssh2_ntohu32(s); - s += 4; - if (len != 7 || strncmp((char *) s, "ssh-dss", 7) != 0) { - return -1; - } - s += 7; - - p_len = _libssh2_ntohu32(s); - s += 4; - p = s; - s += p_len; - q_len = _libssh2_ntohu32(s); - s += 4; - q = s; - s += q_len; - g_len = _libssh2_ntohu32(s); - s += 4; - g = s; - s += g_len; - y_len = _libssh2_ntohu32(s); - s += 4; - y = s; - /* s += y_len; */ - - _libssh2_dsa_new(&dsactx, p, p_len, q, q_len, g, g_len, y, y_len, NULL, 0); - - *abstract = dsactx; - - return 0; -} - -/* - * hostkey_method_ssh_dss_initPEM - * - * Load a Private Key from a PEM file - */ -static int -hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION * session, - const char *privkeyfile, - unsigned const char *passphrase, - void **abstract) -{ - libssh2_dsa_ctx *dsactx; - int ret; - - if (*abstract) { - hostkey_method_ssh_dss_dtor(session, abstract); - *abstract = NULL; - } - - ret = _libssh2_dsa_new_private(&dsactx, session, privkeyfile, passphrase); - if (ret) { - return -1; - } - - *abstract = dsactx; - - return 0; -} - -/* - * libssh2_hostkey_method_ssh_dss_sign - * - * Verify signature created by remote - */ -static int -hostkey_method_ssh_dss_sig_verify(LIBSSH2_SESSION * session, - const unsigned char *sig, - size_t sig_len, - const unsigned char *m, - size_t m_len, void **abstract) -{ - libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); - - /* Skip past keyname_len(4) + keyname(7){"ssh-dss"} + signature_len(4) */ - sig += 15; - sig_len -= 15; - if (sig_len != 40) { - return _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Invalid DSS signature length"); - } - return _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len); -} - -/* - * hostkey_method_ssh_dss_signv - * - * Construct a signature from an array of vectors - */ -static int -hostkey_method_ssh_dss_signv(LIBSSH2_SESSION * session, - unsigned char **signature, - size_t *signature_len, - int veccount, - const struct iovec datavec[], - void **abstract) -{ - libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); - unsigned char hash[SHA_DIGEST_LENGTH]; - libssh2_sha1_ctx ctx; - int i; - - *signature = LIBSSH2_ALLOC(session, 2 * SHA_DIGEST_LENGTH); - if (!*signature) { - return -1; - } - - *signature_len = 2 * SHA_DIGEST_LENGTH; - memset(*signature, 0, 2 * SHA_DIGEST_LENGTH); - - libssh2_sha1_init(&ctx); - for(i = 0; i < veccount; i++) { - libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len); - } - libssh2_sha1_final(ctx, hash); - - if (_libssh2_dsa_sha1_sign(dsactx, hash, SHA_DIGEST_LENGTH, *signature)) { - LIBSSH2_FREE(session, *signature); - return -1; - } - - return 0; -} - -/* - * libssh2_hostkey_method_ssh_dss_dtor - * - * Shutdown the hostkey method - */ -static int -hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session, void **abstract) -{ - libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); - (void) session; - - _libssh2_dsa_free(dsactx); - - *abstract = NULL; - - return 0; -} - -static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_dss = { - "ssh-dss", - MD5_DIGEST_LENGTH, - hostkey_method_ssh_dss_init, - hostkey_method_ssh_dss_initPEM, - hostkey_method_ssh_dss_sig_verify, - hostkey_method_ssh_dss_signv, - NULL, /* encrypt */ - hostkey_method_ssh_dss_dtor, -}; -#endif /* LIBSSH2_DSA */ - -static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = { -#if LIBSSH2_RSA - &hostkey_method_ssh_rsa, -#endif /* LIBSSH2_RSA */ -#if LIBSSH2_DSA - &hostkey_method_ssh_dss, -#endif /* LIBSSH2_DSA */ - NULL -}; - -const LIBSSH2_HOSTKEY_METHOD ** -libssh2_hostkey_methods(void) -{ - return hostkey_methods; -} - -/* - * libssh2_hostkey_hash - * - * Returns hash signature - * Returned buffer should NOT be freed - * Length of buffer is determined by hash type - * i.e. MD5 == 16, SHA1 == 20 - */ -LIBSSH2_API const char * -libssh2_hostkey_hash(LIBSSH2_SESSION * session, int hash_type) -{ - switch (hash_type) { -#if LIBSSH2_MD5 - case LIBSSH2_HOSTKEY_HASH_MD5: - return (char *) session->server_hostkey_md5; - break; -#endif /* LIBSSH2_MD5 */ - case LIBSSH2_HOSTKEY_HASH_SHA1: - return (char *) session->server_hostkey_sha1; - break; - default: - return NULL; - } -} - -static int hostkey_type(const unsigned char *hostkey, size_t len) -{ - const unsigned char rsa[] = { - 0, 0, 0, 0x07, 's', 's', 'h', '-', 'r', 's', 'a' - }; - const unsigned char dss[] = { - 0, 0, 0, 0x07, 's', 's', 'h', '-', 'd', 's', 's' - }; - - if (len < 11) - return LIBSSH2_HOSTKEY_TYPE_UNKNOWN; - - if (!memcmp(rsa, hostkey, 11)) - return LIBSSH2_HOSTKEY_TYPE_RSA; - - if (!memcmp(dss, hostkey, 11)) - return LIBSSH2_HOSTKEY_TYPE_DSS; - - return LIBSSH2_HOSTKEY_TYPE_UNKNOWN; -} - -/* - * libssh2_session_hostkey() - * - * Returns the server key and length. - * - */ -LIBSSH2_API const char * -libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type) -{ - if(session->server_hostkey_len) { - if(len) - *len = session->server_hostkey_len; - if (type) - *type = hostkey_type(session->server_hostkey, - session->server_hostkey_len); - return (char *) session->server_hostkey; - } - if(len) - *len = 0; - return NULL; -} - diff --git a/vendor/libssh2-1.4.2/src/keepalive.c b/vendor/libssh2-1.4.2/src/keepalive.c deleted file mode 100644 index 260206a2b..000000000 --- a/vendor/libssh2-1.4.2/src/keepalive.c +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (C) 2010 Simon Josefsson - * Author: Simon Josefsson - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - */ - -#include "libssh2_priv.h" -#include "transport.h" /* _libssh2_transport_write */ - -/* Keep-alive stuff. */ - -LIBSSH2_API void -libssh2_keepalive_config (LIBSSH2_SESSION *session, - int want_reply, - unsigned interval) -{ - if (interval == 1) - session->keepalive_interval = 2; - else - session->keepalive_interval = interval; - session->keepalive_want_reply = want_reply ? 1 : 0; -} - -LIBSSH2_API int -libssh2_keepalive_send (LIBSSH2_SESSION *session, - int *seconds_to_next) -{ - time_t now; - - if (!session->keepalive_interval) { - if (seconds_to_next) - *seconds_to_next = 0; - return 0; - } - - now = time (NULL); - - if (session->keepalive_last_sent + session->keepalive_interval <= now) { - /* Format is - "SSH_MSG_GLOBAL_REQUEST || 4-byte len || str || want-reply". */ - unsigned char keepalive_data[] - = "\x50\x00\x00\x00\x15keepalive@libssh2.orgW"; - size_t len = sizeof (keepalive_data) - 1; - int rc; - - keepalive_data[len - 1] = session->keepalive_want_reply; - - rc = _libssh2_transport_send(session, keepalive_data, len, NULL, 0); - /* Silently ignore PACKET_EAGAIN here: if the write buffer is - already full, sending another keepalive is not useful. */ - if (rc && rc != LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send keepalive message"); - return rc; - } - - session->keepalive_last_sent = now; - if (seconds_to_next) - *seconds_to_next = session->keepalive_interval; - } else if (seconds_to_next) { - *seconds_to_next = (int) session->keepalive_last_sent - + session->keepalive_interval - now; - } - - return 0; -} diff --git a/vendor/libssh2-1.4.2/src/kex.c b/vendor/libssh2-1.4.2/src/kex.c deleted file mode 100644 index 0a72cb754..000000000 --- a/vendor/libssh2-1.4.2/src/kex.c +++ /dev/null @@ -1,2008 +0,0 @@ -/* Copyright (c) 2004-2007, Sara Golemon - * Copyright (c) 2010, Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -#include "transport.h" -#include "comp.h" -#include "mac.h" - -/* TODO: Switch this to an inline and handle alloc() failures */ -/* Helper macro called from kex_method_diffie_hellman_group1_sha1_key_exchange */ -#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \ - { \ - libssh2_sha1_ctx hash; \ - unsigned long len = 0; \ - if (!(value)) { \ - value = LIBSSH2_ALLOC(session, reqlen + SHA_DIGEST_LENGTH); \ - } \ - if (value) \ - while (len < (unsigned long)reqlen) { \ - libssh2_sha1_init(&hash); \ - libssh2_sha1_update(hash, exchange_state->k_value, \ - exchange_state->k_value_len); \ - libssh2_sha1_update(hash, exchange_state->h_sig_comp, \ - SHA_DIGEST_LENGTH); \ - if (len > 0) { \ - libssh2_sha1_update(hash, value, len); \ - } else { \ - libssh2_sha1_update(hash, (version), 1); \ - libssh2_sha1_update(hash, session->session_id, \ - session->session_id_len); \ - } \ - libssh2_sha1_final(hash, (value) + len); \ - len += SHA_DIGEST_LENGTH; \ - } \ - } - -/* - * diffie_hellman_sha1 - * - * Diffie Hellman Key Exchange, Group Agnostic - */ -static int diffie_hellman_sha1(LIBSSH2_SESSION *session, - _libssh2_bn *g, - _libssh2_bn *p, - int group_order, - unsigned char packet_type_init, - unsigned char packet_type_reply, - unsigned char *midhash, - unsigned long midhash_len, - kmdhgGPsha1kex_state_t *exchange_state) -{ - int ret = 0; - int rc; - - if (exchange_state->state == libssh2_NB_state_idle) { - /* Setup initial values */ - exchange_state->e_packet = NULL; - exchange_state->s_packet = NULL; - exchange_state->k_value = NULL; - exchange_state->ctx = _libssh2_bn_ctx_new(); - exchange_state->x = _libssh2_bn_init(); /* Random from client */ - exchange_state->e = _libssh2_bn_init(); /* g^x mod p */ - exchange_state->f = _libssh2_bn_init(); /* g^(Random from server) mod p */ - exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ - - /* Zero the whole thing out */ - memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t)); - - /* Generate x and e */ - _libssh2_bn_rand(exchange_state->x, group_order, 0, -1); - _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p, - exchange_state->ctx); - - /* Send KEX init */ - /* packet_type(1) + String Length(4) + leading 0(1) */ - exchange_state->e_packet_len = - _libssh2_bn_bytes(exchange_state->e) + 6; - if (_libssh2_bn_bits(exchange_state->e) % 8) { - /* Leading 00 not needed */ - exchange_state->e_packet_len--; - } - - exchange_state->e_packet = - LIBSSH2_ALLOC(session, exchange_state->e_packet_len); - if (!exchange_state->e_packet) { - ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Out of memory error"); - goto clean_exit; - } - exchange_state->e_packet[0] = packet_type_init; - _libssh2_htonu32(exchange_state->e_packet + 1, - exchange_state->e_packet_len - 5); - if (_libssh2_bn_bits(exchange_state->e) % 8) { - _libssh2_bn_to_bin(exchange_state->e, - exchange_state->e_packet + 5); - } else { - exchange_state->e_packet[5] = 0; - _libssh2_bn_to_bin(exchange_state->e, - exchange_state->e_packet + 6); - } - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending KEX packet %d", - (int) packet_type_init); - exchange_state->state = libssh2_NB_state_created; - } - - if (exchange_state->state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, exchange_state->e_packet, - exchange_state->e_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - ret = _libssh2_error(session, rc, - "Unable to send KEX init message"); - goto clean_exit; - } - exchange_state->state = libssh2_NB_state_sent; - } - - if (exchange_state->state == libssh2_NB_state_sent) { - if (session->burn_optimistic_kexinit) { - /* The first KEX packet to come along will be the guess initially - * sent by the server. That guess turned out to be wrong so we - * need to silently ignore it */ - int burn_type; - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Waiting for badly guessed KEX packet (to be ignored)"); - burn_type = - _libssh2_packet_burn(session, &exchange_state->burn_state); - if (burn_type == LIBSSH2_ERROR_EAGAIN) { - return burn_type; - } else if (burn_type <= 0) { - /* Failed to receive a packet */ - ret = burn_type; - goto clean_exit; - } - session->burn_optimistic_kexinit = 0; - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Burnt packet of type: %02x", - (unsigned int) burn_type); - } - - exchange_state->state = libssh2_NB_state_sent1; - } - - if (exchange_state->state == libssh2_NB_state_sent1) { - /* Wait for KEX reply */ - rc = _libssh2_packet_require(session, packet_type_reply, - &exchange_state->s_packet, - &exchange_state->s_packet_len, 0, NULL, - 0, &exchange_state->req_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - if (rc) { - ret = _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, - "Timed out waiting for KEX reply"); - goto clean_exit; - } - - /* Parse KEXDH_REPLY */ - exchange_state->s = exchange_state->s_packet + 1; - - session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s); - exchange_state->s += 4; - session->server_hostkey = - LIBSSH2_ALLOC(session, session->server_hostkey_len); - if (!session->server_hostkey) { - ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for a copy " - "of the host key"); - goto clean_exit; - } - memcpy(session->server_hostkey, exchange_state->s, - session->server_hostkey_len); - exchange_state->s += session->server_hostkey_len; - -#if LIBSSH2_MD5 - { - libssh2_md5_ctx fingerprint_ctx; - - libssh2_md5_init(&fingerprint_ctx); - libssh2_md5_update(fingerprint_ctx, session->server_hostkey, - session->server_hostkey_len); - libssh2_md5_final(fingerprint_ctx, session->server_hostkey_md5); - } -#ifdef LIBSSH2DEBUG - { - char fingerprint[50], *fprint = fingerprint; - int i; - for(i = 0; i < 16; i++, fprint += 3) { - snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]); - } - *(--fprint) = '\0'; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Server's MD5 Fingerprint: %s", fingerprint); - } -#endif /* LIBSSH2DEBUG */ -#endif /* ! LIBSSH2_MD5 */ - - { - libssh2_sha1_ctx fingerprint_ctx; - - libssh2_sha1_init(&fingerprint_ctx); - libssh2_sha1_update(fingerprint_ctx, session->server_hostkey, - session->server_hostkey_len); - libssh2_sha1_final(fingerprint_ctx, session->server_hostkey_sha1); - } -#ifdef LIBSSH2DEBUG - { - char fingerprint[64], *fprint = fingerprint; - int i; - - for(i = 0; i < 20; i++, fprint += 3) { - snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]); - } - *(--fprint) = '\0'; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Server's SHA1 Fingerprint: %s", fingerprint); - } -#endif /* LIBSSH2DEBUG */ - - if (session->hostkey->init(session, session->server_hostkey, - session->server_hostkey_len, - &session->server_hostkey_abstract)) { - ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, - "Unable to initialize hostkey importer"); - goto clean_exit; - } - - exchange_state->f_value_len = _libssh2_ntohu32(exchange_state->s); - exchange_state->s += 4; - exchange_state->f_value = exchange_state->s; - exchange_state->s += exchange_state->f_value_len; - _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len, - exchange_state->f_value); - - exchange_state->h_sig_len = _libssh2_ntohu32(exchange_state->s); - exchange_state->s += 4; - exchange_state->h_sig = exchange_state->s; - - /* Compute the shared secret */ - _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f, - exchange_state->x, p, exchange_state->ctx); - exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5; - if (_libssh2_bn_bits(exchange_state->k) % 8) { - /* don't need leading 00 */ - exchange_state->k_value_len--; - } - exchange_state->k_value = - LIBSSH2_ALLOC(session, exchange_state->k_value_len); - if (!exchange_state->k_value) { - ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate buffer for K"); - goto clean_exit; - } - _libssh2_htonu32(exchange_state->k_value, - exchange_state->k_value_len - 4); - if (_libssh2_bn_bits(exchange_state->k) % 8) { - _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4); - } else { - exchange_state->k_value[4] = 0; - _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5); - } - - libssh2_sha1_init(&exchange_state->exchange_hash); - if (session->local.banner) { - _libssh2_htonu32(exchange_state->h_sig_comp, - strlen((char *) session->local.banner) - 2); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - (char *) session->local.banner, - strlen((char *) session->local.banner) - 2); - } else { - _libssh2_htonu32(exchange_state->h_sig_comp, - sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - LIBSSH2_SSH_DEFAULT_BANNER, - sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); - } - - _libssh2_htonu32(exchange_state->h_sig_comp, - strlen((char *) session->remote.banner)); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - session->remote.banner, - strlen((char *) session->remote.banner)); - - _libssh2_htonu32(exchange_state->h_sig_comp, - session->local.kexinit_len); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - session->local.kexinit, - session->local.kexinit_len); - - _libssh2_htonu32(exchange_state->h_sig_comp, - session->remote.kexinit_len); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - session->remote.kexinit, - session->remote.kexinit_len); - - _libssh2_htonu32(exchange_state->h_sig_comp, - session->server_hostkey_len); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - session->server_hostkey, - session->server_hostkey_len); - - if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) { - /* diffie-hellman-group-exchange hashes additional fields */ -#ifdef LIBSSH2_DH_GEX_NEW - _libssh2_htonu32(exchange_state->h_sig_comp, - LIBSSH2_DH_GEX_MINGROUP); - _libssh2_htonu32(exchange_state->h_sig_comp + 4, - LIBSSH2_DH_GEX_OPTGROUP); - _libssh2_htonu32(exchange_state->h_sig_comp + 8, - LIBSSH2_DH_GEX_MAXGROUP); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 12); -#else - _libssh2_htonu32(exchange_state->h_sig_comp, - LIBSSH2_DH_GEX_OPTGROUP); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); -#endif - } - - if (midhash) { - libssh2_sha1_update(exchange_state->exchange_hash, midhash, - midhash_len); - } - - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->e_packet + 1, - exchange_state->e_packet_len - 1); - - _libssh2_htonu32(exchange_state->h_sig_comp, - exchange_state->f_value_len); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->h_sig_comp, 4); - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->f_value, - exchange_state->f_value_len); - - libssh2_sha1_update(exchange_state->exchange_hash, - exchange_state->k_value, - exchange_state->k_value_len); - - libssh2_sha1_final(exchange_state->exchange_hash, - exchange_state->h_sig_comp); - - if (session->hostkey-> - sig_verify(session, exchange_state->h_sig, - exchange_state->h_sig_len, exchange_state->h_sig_comp, - 20, &session->server_hostkey_abstract)) { - ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, - "Unable to verify hostkey signature"); - goto clean_exit; - } - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending NEWKEYS message"); - exchange_state->c = SSH_MSG_NEWKEYS; - - exchange_state->state = libssh2_NB_state_sent2; - } - - if (exchange_state->state == libssh2_NB_state_sent2) { - rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message"); - goto clean_exit; - } - - exchange_state->state = libssh2_NB_state_sent3; - } - - if (exchange_state->state == libssh2_NB_state_sent3) { - rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS, - &exchange_state->tmp, - &exchange_state->tmp_len, 0, NULL, 0, - &exchange_state->req_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS"); - goto clean_exit; - } - /* The first key exchange has been performed, - switch to active crypt/comp/mac mode */ - session->state |= LIBSSH2_STATE_NEWKEYS; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message"); - - /* This will actually end up being just packet_type(1) - for this packet type anyway */ - LIBSSH2_FREE(session, exchange_state->tmp); - - if (!session->session_id) { - session->session_id = LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH); - if (!session->session_id) { - ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate buffer for SHA digest"); - goto clean_exit; - } - memcpy(session->session_id, exchange_state->h_sig_comp, - SHA_DIGEST_LENGTH); - session->session_id_len = SHA_DIGEST_LENGTH; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "session_id calculated"); - } - - /* Cleanup any existing cipher */ - if (session->local.crypt->dtor) { - session->local.crypt->dtor(session, - &session->local.crypt_abstract); - } - - /* Calculate IV/Secret/Key for each direction */ - if (session->local.crypt->init) { - unsigned char *iv = NULL, *secret = NULL; - int free_iv = 0, free_secret = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, - session->local.crypt-> - iv_len, "A"); - if (!iv) { - ret = -1; - goto clean_exit; - } - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, - session->local.crypt-> - secret_len, "C"); - if (!secret) { - LIBSSH2_FREE(session, iv); - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - if (session->local.crypt-> - init(session, session->local.crypt, iv, &free_iv, secret, - &free_secret, 1, &session->local.crypt_abstract)) { - LIBSSH2_FREE(session, iv); - LIBSSH2_FREE(session, secret); - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - - if (free_iv) { - memset(iv, 0, session->local.crypt->iv_len); - LIBSSH2_FREE(session, iv); - } - - if (free_secret) { - memset(secret, 0, session->local.crypt->secret_len); - LIBSSH2_FREE(session, secret); - } - } - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Client to Server IV and Key calculated"); - - if (session->remote.crypt->dtor) { - /* Cleanup any existing cipher */ - session->remote.crypt->dtor(session, - &session->remote.crypt_abstract); - } - - if (session->remote.crypt->init) { - unsigned char *iv = NULL, *secret = NULL; - int free_iv = 0, free_secret = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, - session->remote.crypt-> - iv_len, "B"); - if (!iv) { - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, - session->remote.crypt-> - secret_len, "D"); - if (!secret) { - LIBSSH2_FREE(session, iv); - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - if (session->remote.crypt-> - init(session, session->remote.crypt, iv, &free_iv, secret, - &free_secret, 0, &session->remote.crypt_abstract)) { - LIBSSH2_FREE(session, iv); - LIBSSH2_FREE(session, secret); - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - - if (free_iv) { - memset(iv, 0, session->remote.crypt->iv_len); - LIBSSH2_FREE(session, iv); - } - - if (free_secret) { - memset(secret, 0, session->remote.crypt->secret_len); - LIBSSH2_FREE(session, secret); - } - } - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Server to Client IV and Key calculated"); - - if (session->local.mac->dtor) { - session->local.mac->dtor(session, &session->local.mac_abstract); - } - - if (session->local.mac->init) { - unsigned char *key = NULL; - int free_key = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, - session->local.mac-> - key_len, "E"); - if (!key) { - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - session->local.mac->init(session, key, &free_key, - &session->local.mac_abstract); - - if (free_key) { - memset(key, 0, session->local.mac->key_len); - LIBSSH2_FREE(session, key); - } - } - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Client to Server HMAC Key calculated"); - - if (session->remote.mac->dtor) { - session->remote.mac->dtor(session, &session->remote.mac_abstract); - } - - if (session->remote.mac->init) { - unsigned char *key = NULL; - int free_key = 0; - - LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, - session->remote.mac-> - key_len, "F"); - if (!key) { - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - session->remote.mac->init(session, key, &free_key, - &session->remote.mac_abstract); - - if (free_key) { - memset(key, 0, session->remote.mac->key_len); - LIBSSH2_FREE(session, key); - } - } - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Server to Client HMAC Key calculated"); - - /* Initialize compression for each direction */ - - /* Cleanup any existing compression */ - if (session->local.comp && session->local.comp->dtor) { - session->local.comp->dtor(session, 1, - &session->local.comp_abstract); - } - - if (session->local.comp && session->local.comp->init) { - if (session->local.comp->init(session, 1, - &session->local.comp_abstract)) { - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - } - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Client to Server compression initialized"); - - if (session->remote.comp && session->remote.comp->dtor) { - session->remote.comp->dtor(session, 0, - &session->remote.comp_abstract); - } - - if (session->remote.comp && session->remote.comp->init) { - if (session->remote.comp->init(session, 0, - &session->remote.comp_abstract)) { - ret = LIBSSH2_ERROR_KEX_FAILURE; - goto clean_exit; - } - } - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Server to Client compression initialized"); - - } - - clean_exit: - _libssh2_bn_free(exchange_state->x); - exchange_state->x = NULL; - _libssh2_bn_free(exchange_state->e); - exchange_state->e = NULL; - _libssh2_bn_free(exchange_state->f); - exchange_state->f = NULL; - _libssh2_bn_free(exchange_state->k); - exchange_state->k = NULL; - _libssh2_bn_ctx_free(exchange_state->ctx); - exchange_state->ctx = NULL; - - if (exchange_state->e_packet) { - LIBSSH2_FREE(session, exchange_state->e_packet); - exchange_state->e_packet = NULL; - } - - if (exchange_state->s_packet) { - LIBSSH2_FREE(session, exchange_state->s_packet); - exchange_state->s_packet = NULL; - } - - if (exchange_state->k_value) { - LIBSSH2_FREE(session, exchange_state->k_value); - exchange_state->k_value = NULL; - } - - exchange_state->state = libssh2_NB_state_idle; - - return ret; -} - - - -/* kex_method_diffie_hellman_group1_sha1_key_exchange - * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1 - */ -static int -kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session, - key_exchange_state_low_t - * key_state) -{ - static const unsigned char p_value[128] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, - 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, - 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, - 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, - 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - - int ret; - - if (key_state->state == libssh2_NB_state_idle) { - /* g == 2 */ - key_state->p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ - key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */ - - /* Initialize P and G */ - _libssh2_bn_set_word(key_state->g, 2); - _libssh2_bn_from_bin(key_state->p, 128, p_value); - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Initiating Diffie-Hellman Group1 Key Exchange"); - - key_state->state = libssh2_NB_state_created; - } - ret = diffie_hellman_sha1(session, key_state->g, key_state->p, 128, - SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, - NULL, 0, &key_state->exchange_state); - if (ret == LIBSSH2_ERROR_EAGAIN) { - return ret; - } - - _libssh2_bn_free(key_state->p); - key_state->p = NULL; - _libssh2_bn_free(key_state->g); - key_state->g = NULL; - key_state->state = libssh2_NB_state_idle; - - return ret; -} - - - -/* kex_method_diffie_hellman_group14_sha1_key_exchange - * Diffie-Hellman Group14 Key Exchange using SHA1 - */ -static int -kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION *session, - key_exchange_state_low_t - * key_state) -{ - static const unsigned char p_value[256] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, - 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, - 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, - 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, - 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, - 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, - 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, - 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, - 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, - 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, - 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, - 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, - 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, - 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, - 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, - 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, - 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, - 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, - 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, - 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, - 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - int ret; - - if (key_state->state == libssh2_NB_state_idle) { - key_state->p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ - key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */ - - /* g == 2 */ - /* Initialize P and G */ - _libssh2_bn_set_word(key_state->g, 2); - _libssh2_bn_from_bin(key_state->p, 256, p_value); - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Initiating Diffie-Hellman Group14 Key Exchange"); - - key_state->state = libssh2_NB_state_created; - } - ret = diffie_hellman_sha1(session, key_state->g, key_state->p, - 256, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, - NULL, 0, &key_state->exchange_state); - if (ret == LIBSSH2_ERROR_EAGAIN) { - return ret; - } - - key_state->state = libssh2_NB_state_idle; - _libssh2_bn_free(key_state->p); - key_state->p = NULL; - _libssh2_bn_free(key_state->g); - key_state->g = NULL; - - return ret; -} - - - -/* kex_method_diffie_hellman_group_exchange_sha1_key_exchange - * Diffie-Hellman Group Exchange Key Exchange using SHA1 - * Negotiates random(ish) group for secret derivation - */ -static int -kex_method_diffie_hellman_group_exchange_sha1_key_exchange -(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state) -{ - unsigned long p_len, g_len; - int ret = 0; - int rc; - - if (key_state->state == libssh2_NB_state_idle) { - key_state->p = _libssh2_bn_init(); - key_state->g = _libssh2_bn_init(); - /* Ask for a P and G pair */ -#ifdef LIBSSH2_DH_GEX_NEW - key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST; - _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP); - _libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP); - _libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP); - key_state->request_len = 13; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Initiating Diffie-Hellman Group-Exchange (New Method)"); -#else - key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; - _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP); - key_state->request_len = 5; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, - "Initiating Diffie-Hellman Group-Exchange (Old Method)"); -#endif - - key_state->state = libssh2_NB_state_created; - } - - if (key_state->state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, key_state->request, - key_state->request_len, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - ret = _libssh2_error(session, rc, - "Unable to send Group Exchange Request"); - goto dh_gex_clean_exit; - } - - key_state->state = libssh2_NB_state_sent; - } - - if (key_state->state == libssh2_NB_state_sent) { - rc = _libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP, - &key_state->data, &key_state->data_len, - 0, NULL, 0, &key_state->req_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - ret = _libssh2_error(session, rc, - "Timeout waiting for GEX_GROUP reply"); - goto dh_gex_clean_exit; - } - - key_state->state = libssh2_NB_state_sent1; - } - - if (key_state->state == libssh2_NB_state_sent1) { - unsigned char *s = key_state->data + 1; - p_len = _libssh2_ntohu32(s); - s += 4; - _libssh2_bn_from_bin(key_state->p, p_len, s); - s += p_len; - - g_len = _libssh2_ntohu32(s); - s += 4; - _libssh2_bn_from_bin(key_state->g, g_len, s); - - ret = diffie_hellman_sha1(session, key_state->g, key_state->p, p_len, - SSH_MSG_KEX_DH_GEX_INIT, - SSH_MSG_KEX_DH_GEX_REPLY, - key_state->data + 1, - key_state->data_len - 1, - &key_state->exchange_state); - if (ret == LIBSSH2_ERROR_EAGAIN) { - return ret; - } - - LIBSSH2_FREE(session, key_state->data); - } - - dh_gex_clean_exit: - key_state->state = libssh2_NB_state_idle; - _libssh2_bn_free(key_state->g); - key_state->g = NULL; - _libssh2_bn_free(key_state->p); - key_state->p = NULL; - - return ret; -} - - - -#define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY 0x0001 -#define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY 0x0002 - -static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group1_sha1 = { - "diffie-hellman-group1-sha1", - kex_method_diffie_hellman_group1_sha1_key_exchange, - LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, -}; - -static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group14_sha1 = { - "diffie-hellman-group14-sha1", - kex_method_diffie_hellman_group14_sha1_key_exchange, - LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, -}; - -static const LIBSSH2_KEX_METHOD -kex_method_diffie_helman_group_exchange_sha1 = { - "diffie-hellman-group-exchange-sha1", - kex_method_diffie_hellman_group_exchange_sha1_key_exchange, - LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, -}; - -static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = { - &kex_method_diffie_helman_group14_sha1, - &kex_method_diffie_helman_group_exchange_sha1, - &kex_method_diffie_helman_group1_sha1, - NULL -}; - -typedef struct _LIBSSH2_COMMON_METHOD -{ - const char *name; -} LIBSSH2_COMMON_METHOD; - -/* kex_method_strlen - * Calculate the length of a particular method list's resulting string - * Includes SUM(strlen() of each individual method plus 1 (for coma)) - 1 (because the last coma isn't used) - * Another sign of bad coding practices gone mad. Pretend you don't see this. - */ -static size_t -kex_method_strlen(LIBSSH2_COMMON_METHOD ** method) -{ - size_t len = 0; - - if (!method || !*method) { - return 0; - } - - while (*method && (*method)->name) { - len += strlen((*method)->name) + 1; - method++; - } - - return len - 1; -} - - - -/* kex_method_list - * Generate formatted preference list in buf - */ -static size_t -kex_method_list(unsigned char *buf, size_t list_strlen, - LIBSSH2_COMMON_METHOD ** method) -{ - _libssh2_htonu32(buf, list_strlen); - buf += 4; - - if (!method || !*method) { - return 4; - } - - while (*method && (*method)->name) { - int mlen = strlen((*method)->name); - memcpy(buf, (*method)->name, mlen); - buf += mlen; - *(buf++) = ','; - method++; - } - - return list_strlen + 4; -} - - - -#define LIBSSH2_METHOD_PREFS_LEN(prefvar, defaultvar) \ - ((prefvar) ? strlen(prefvar) : \ - kex_method_strlen((LIBSSH2_COMMON_METHOD**)(defaultvar))) - -#define LIBSSH2_METHOD_PREFS_STR(buf, prefvarlen, prefvar, defaultvar) \ - if (prefvar) { \ - _libssh2_htonu32((buf), (prefvarlen)); \ - buf += 4; \ - memcpy((buf), (prefvar), (prefvarlen)); \ - buf += (prefvarlen); \ - } else { \ - buf += kex_method_list((buf), (prefvarlen), \ - (LIBSSH2_COMMON_METHOD**)(defaultvar)); \ - } - -/* kexinit - * Send SSH_MSG_KEXINIT packet - */ -static int kexinit(LIBSSH2_SESSION * session) -{ - /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) + - reserved(4) + length longs(40) */ - size_t data_len = 62; - size_t kex_len, hostkey_len = 0; - size_t crypt_cs_len, crypt_sc_len; - size_t comp_cs_len, comp_sc_len; - size_t mac_cs_len, mac_sc_len; - size_t lang_cs_len, lang_sc_len; - unsigned char *data, *s; - int rc; - - if (session->kexinit_state == libssh2_NB_state_idle) { - kex_len = - LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); - hostkey_len = - LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, - libssh2_hostkey_methods()); - crypt_cs_len = - LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, - libssh2_crypt_methods()); - crypt_sc_len = - LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, - libssh2_crypt_methods()); - mac_cs_len = - LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, - _libssh2_mac_methods()); - mac_sc_len = - LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, - _libssh2_mac_methods()); - comp_cs_len = - LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, - _libssh2_comp_methods(session)); - comp_sc_len = - LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, - _libssh2_comp_methods(session)); - lang_cs_len = - LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); - lang_sc_len = - LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); - - data_len += kex_len + hostkey_len + crypt_cs_len + crypt_sc_len + - comp_cs_len + comp_sc_len + mac_cs_len + mac_sc_len + - lang_cs_len + lang_sc_len; - - s = data = LIBSSH2_ALLOC(session, data_len); - if (!data) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory"); - } - - *(s++) = SSH_MSG_KEXINIT; - - _libssh2_random(s, 16); - s += 16; - - /* Ennumerating through these lists twice is probably (certainly?) - inefficient from a CPU standpoint, but it saves multiple - malloc/realloc calls */ - LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, - libssh2_kex_methods); - LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, - libssh2_hostkey_methods()); - LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, - libssh2_crypt_methods()); - LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, - libssh2_crypt_methods()); - LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, - _libssh2_mac_methods()); - LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, - _libssh2_mac_methods()); - LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, - _libssh2_comp_methods(session)); - LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, - _libssh2_comp_methods(session)); - LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, - NULL); - LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, - NULL); - - /* No optimistic KEX packet follows */ - /* Deal with optimistic packets - * session->flags |= KEXINIT_OPTIMISTIC - * session->flags |= KEXINIT_METHODSMATCH - */ - *(s++) = 0; - - /* Reserved == 0 */ - _libssh2_htonu32(s, 0); - -#ifdef LIBSSH2DEBUG - { - /* Funnily enough, they'll all "appear" to be '\0' terminated */ - unsigned char *p = data + 21; /* type(1) + cookie(16) + len(4) */ - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent KEX: %s", p); - p += kex_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent HOSTKEY: %s", p); - p += hostkey_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent CRYPT_CS: %s", p); - p += crypt_cs_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent CRYPT_SC: %s", p); - p += crypt_sc_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent MAC_CS: %s", p); - p += mac_cs_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent MAC_SC: %s", p); - p += mac_sc_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent COMP_CS: %s", p); - p += comp_cs_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent COMP_SC: %s", p); - p += comp_sc_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent LANG_CS: %s", p); - p += lang_cs_len + 4; - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent LANG_SC: %s", p); - p += lang_sc_len + 4; - } -#endif /* LIBSSH2DEBUG */ - - session->kexinit_state = libssh2_NB_state_created; - } else { - data = session->kexinit_data; - data_len = session->kexinit_data_len; - /* zap the variables to ensure there is NOT a double free later */ - session->kexinit_data = NULL; - session->kexinit_data_len = 0; - } - - rc = _libssh2_transport_send(session, data, data_len, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - session->kexinit_data = data; - session->kexinit_data_len = data_len; - return rc; - } - else if (rc) { - LIBSSH2_FREE(session, data); - session->kexinit_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send KEXINIT packet to remote host"); - - } - - if (session->local.kexinit) { - LIBSSH2_FREE(session, session->local.kexinit); - } - - session->local.kexinit = data; - session->local.kexinit_len = data_len; - - session->kexinit_state = libssh2_NB_state_idle; - - return 0; -} - -/* kex_agree_instr - * Kex specific variant of strstr() - * Needle must be preceed by BOL or ',', and followed by ',' or EOL - */ -static unsigned char * -kex_agree_instr(unsigned char *haystack, unsigned long haystack_len, - const unsigned char *needle, unsigned long needle_len) -{ - unsigned char *s; - - /* Haystack too short to bother trying */ - if (haystack_len < needle_len) { - return NULL; - } - - /* Needle at start of haystack */ - if ((strncmp((char *) haystack, (char *) needle, needle_len) == 0) && - (needle_len == haystack_len || haystack[needle_len] == ',')) { - return haystack; - } - - s = haystack; - /* Search until we run out of comas or we run out of haystack, - whichever comes first */ - while ((s = (unsigned char *) strchr((char *) s, ',')) - && ((haystack_len - (s - haystack)) > needle_len)) { - s++; - /* Needle at X position */ - if ((strncmp((char *) s, (char *) needle, needle_len) == 0) && - (((s - haystack) + needle_len) == haystack_len - || s[needle_len] == ',')) { - return s; - } - } - - return NULL; -} - - - -/* kex_get_method_by_name - */ -static const LIBSSH2_COMMON_METHOD * -kex_get_method_by_name(const char *name, size_t name_len, - const LIBSSH2_COMMON_METHOD ** methodlist) -{ - while (*methodlist) { - if ((strlen((*methodlist)->name) == name_len) && - (strncmp((*methodlist)->name, name, name_len) == 0)) { - return *methodlist; - } - methodlist++; - } - return NULL; -} - - - -/* kex_agree_hostkey - * Agree on a Hostkey which works with this kex - */ -static int kex_agree_hostkey(LIBSSH2_SESSION * session, - unsigned long kex_flags, - unsigned char *hostkey, unsigned long hostkey_len) -{ - const LIBSSH2_HOSTKEY_METHOD **hostkeyp = libssh2_hostkey_methods(); - unsigned char *s; - - if (session->hostkey_prefs) { - s = (unsigned char *) session->hostkey_prefs; - - while (s && *s) { - unsigned char *p = (unsigned char *) strchr((char *) s, ','); - size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); - if (kex_agree_instr(hostkey, hostkey_len, s, method_len)) { - const LIBSSH2_HOSTKEY_METHOD *method = - (const LIBSSH2_HOSTKEY_METHOD *) - kex_get_method_by_name((char *) s, method_len, - (const LIBSSH2_COMMON_METHOD **) - hostkeyp); - - if (!method) { - /* Invalid method -- Should never be reached */ - return -1; - } - - /* So far so good, but does it suit our purposes? (Encrypting - vs Signing) */ - if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == - 0) || (method->encrypt)) { - /* Either this hostkey can do encryption or this kex just - doesn't require it */ - if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) - == 0) || (method->sig_verify)) { - /* Either this hostkey can do signing or this kex just - doesn't require it */ - session->hostkey = method; - return 0; - } - } - } - - s = p ? p + 1 : NULL; - } - return -1; - } - - while (hostkeyp && (*hostkeyp) && (*hostkeyp)->name) { - s = kex_agree_instr(hostkey, hostkey_len, - (unsigned char *) (*hostkeyp)->name, - strlen((*hostkeyp)->name)); - if (s) { - /* So far so good, but does it suit our purposes? (Encrypting vs - Signing) */ - if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) || - ((*hostkeyp)->encrypt)) { - /* Either this hostkey can do encryption or this kex just - doesn't require it */ - if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) == - 0) || ((*hostkeyp)->sig_verify)) { - /* Either this hostkey can do signing or this kex just - doesn't require it */ - session->hostkey = *hostkeyp; - return 0; - } - } - } - hostkeyp++; - } - - return -1; -} - - - -/* kex_agree_kex_hostkey - * Agree on a Key Exchange method and a hostkey encoding type - */ -static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex, - unsigned long kex_len, unsigned char *hostkey, - unsigned long hostkey_len) -{ - const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods; - unsigned char *s; - - if (session->kex_prefs) { - s = (unsigned char *) session->kex_prefs; - - while (s && *s) { - unsigned char *q, *p = (unsigned char *) strchr((char *) s, ','); - size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); - if ((q = kex_agree_instr(kex, kex_len, s, method_len))) { - const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *) - kex_get_method_by_name((char *) s, method_len, - (const LIBSSH2_COMMON_METHOD **) - kexp); - - if (!method) { - /* Invalid method -- Should never be reached */ - return -1; - } - - /* We've agreed on a key exchange method, - * Can we agree on a hostkey that works with this kex? - */ - if (kex_agree_hostkey(session, method->flags, hostkey, - hostkey_len) == 0) { - session->kex = method; - if (session->burn_optimistic_kexinit && (kex == q)) { - /* Server sent an optimistic packet, - * and client agrees with preference - * cancel burning the first KEX_INIT packet that comes in */ - session->burn_optimistic_kexinit = 0; - } - return 0; - } - } - - s = p ? p + 1 : NULL; - } - return -1; - } - - while (*kexp && (*kexp)->name) { - s = kex_agree_instr(kex, kex_len, - (unsigned char *) (*kexp)->name, - strlen((*kexp)->name)); - if (s) { - /* We've agreed on a key exchange method, - * Can we agree on a hostkey that works with this kex? - */ - if (kex_agree_hostkey(session, (*kexp)->flags, hostkey, - hostkey_len) == 0) { - session->kex = *kexp; - if (session->burn_optimistic_kexinit && (kex == s)) { - /* Server sent an optimistic packet, - * and client agrees with preference - * cancel burning the first KEX_INIT packet that comes in */ - session->burn_optimistic_kexinit = 0; - } - return 0; - } - } - kexp++; - } - return -1; -} - - - -/* kex_agree_crypt - * Agree on a cipher algo - */ -static int kex_agree_crypt(LIBSSH2_SESSION * session, - libssh2_endpoint_data *endpoint, - unsigned char *crypt, - unsigned long crypt_len) -{ - const LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods(); - unsigned char *s; - - (void) session; - - if (endpoint->crypt_prefs) { - s = (unsigned char *) endpoint->crypt_prefs; - - while (s && *s) { - unsigned char *p = (unsigned char *) strchr((char *) s, ','); - size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); - - if (kex_agree_instr(crypt, crypt_len, s, method_len)) { - const LIBSSH2_CRYPT_METHOD *method = - (const LIBSSH2_CRYPT_METHOD *) - kex_get_method_by_name((char *) s, method_len, - (const LIBSSH2_COMMON_METHOD **) - cryptp); - - if (!method) { - /* Invalid method -- Should never be reached */ - return -1; - } - - endpoint->crypt = method; - return 0; - } - - s = p ? p + 1 : NULL; - } - return -1; - } - - while (*cryptp && (*cryptp)->name) { - s = kex_agree_instr(crypt, crypt_len, - (unsigned char *) (*cryptp)->name, - strlen((*cryptp)->name)); - if (s) { - endpoint->crypt = *cryptp; - return 0; - } - cryptp++; - } - - return -1; -} - - - -/* kex_agree_mac - * Agree on a message authentication hash - */ -static int kex_agree_mac(LIBSSH2_SESSION * session, - libssh2_endpoint_data * endpoint, unsigned char *mac, - unsigned long mac_len) -{ - const LIBSSH2_MAC_METHOD **macp = _libssh2_mac_methods(); - unsigned char *s; - (void) session; - - if (endpoint->mac_prefs) { - s = (unsigned char *) endpoint->mac_prefs; - - while (s && *s) { - unsigned char *p = (unsigned char *) strchr((char *) s, ','); - size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); - - if (kex_agree_instr(mac, mac_len, s, method_len)) { - const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *) - kex_get_method_by_name((char *) s, method_len, - (const LIBSSH2_COMMON_METHOD **) - macp); - - if (!method) { - /* Invalid method -- Should never be reached */ - return -1; - } - - endpoint->mac = method; - return 0; - } - - s = p ? p + 1 : NULL; - } - return -1; - } - - while (*macp && (*macp)->name) { - s = kex_agree_instr(mac, mac_len, (unsigned char *) (*macp)->name, - strlen((*macp)->name)); - if (s) { - endpoint->mac = *macp; - return 0; - } - macp++; - } - - return -1; -} - - - -/* kex_agree_comp - * Agree on a compression scheme - */ -static int kex_agree_comp(LIBSSH2_SESSION *session, - libssh2_endpoint_data *endpoint, unsigned char *comp, - unsigned long comp_len) -{ - const LIBSSH2_COMP_METHOD **compp = _libssh2_comp_methods(session); - unsigned char *s; - (void) session; - - if (endpoint->comp_prefs) { - s = (unsigned char *) endpoint->comp_prefs; - - while (s && *s) { - unsigned char *p = (unsigned char *) strchr((char *) s, ','); - size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); - - if (kex_agree_instr(comp, comp_len, s, method_len)) { - const LIBSSH2_COMP_METHOD *method = - (const LIBSSH2_COMP_METHOD *) - kex_get_method_by_name((char *) s, method_len, - (const LIBSSH2_COMMON_METHOD **) - compp); - - if (!method) { - /* Invalid method -- Should never be reached */ - return -1; - } - - endpoint->comp = method; - return 0; - } - - s = p ? p + 1 : NULL; - } - return -1; - } - - while (*compp && (*compp)->name) { - s = kex_agree_instr(comp, comp_len, (unsigned char *) (*compp)->name, - strlen((*compp)->name)); - if (s) { - endpoint->comp = *compp; - return 0; - } - compp++; - } - - return -1; -} - - - -/* TODO: When in server mode we need to turn this logic on its head - * The Client gets to make the final call on "agreed methods" - */ - -/* kex_agree_methods - * Decide which specific method to use of the methods offered by each party - */ -static int kex_agree_methods(LIBSSH2_SESSION * session, unsigned char *data, - unsigned data_len) -{ - unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc, - *mac_cs, *mac_sc; - size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len; - size_t comp_sc_len, mac_cs_len, mac_sc_len; - unsigned char *s = data; - - /* Skip packet_type, we know it already */ - s++; - - /* Skip cookie, don't worry, it's preserved in the kexinit field */ - s += 16; - - /* Locate each string */ - kex_len = _libssh2_ntohu32(s); - kex = s + 4; - s += 4 + kex_len; - hostkey_len = _libssh2_ntohu32(s); - hostkey = s + 4; - s += 4 + hostkey_len; - crypt_cs_len = _libssh2_ntohu32(s); - crypt_cs = s + 4; - s += 4 + crypt_cs_len; - crypt_sc_len = _libssh2_ntohu32(s); - crypt_sc = s + 4; - s += 4 + crypt_sc_len; - mac_cs_len = _libssh2_ntohu32(s); - mac_cs = s + 4; - s += 4 + mac_cs_len; - mac_sc_len = _libssh2_ntohu32(s); - mac_sc = s + 4; - s += 4 + mac_sc_len; - comp_cs_len = _libssh2_ntohu32(s); - comp_cs = s + 4; - s += 4 + comp_cs_len; - comp_sc_len = _libssh2_ntohu32(s); - comp_sc = s + 4; -#if 0 - s += 4 + comp_sc_len; - lang_cs_len = _libssh2_ntohu32(s); - lang_cs = s + 4; - s += 4 + lang_cs_len; - lang_sc_len = _libssh2_ntohu32(s); - lang_sc = s + 4; - s += 4 + lang_sc_len; -#endif - /* If the server sent an optimistic packet, assume that it guessed wrong. - * If the guess is determined to be right (by kex_agree_kex_hostkey) - * This flag will be reset to zero so that it's not ignored */ - session->burn_optimistic_kexinit = *(s++); - /* Next uint32 in packet is all zeros (reserved) */ - - if (data_len < (unsigned) (s - data)) - return -1; /* short packet */ - - if (kex_agree_kex_hostkey(session, kex, kex_len, hostkey, hostkey_len)) { - return -1; - } - - if (kex_agree_crypt(session, &session->local, crypt_cs, crypt_cs_len) - || kex_agree_crypt(session, &session->remote, crypt_sc, crypt_sc_len)) { - return -1; - } - - if (kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) || - kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) { - return -1; - } - - if (kex_agree_comp(session, &session->local, comp_cs, comp_cs_len) || - kex_agree_comp(session, &session->remote, comp_sc, comp_sc_len)) { - return -1; - } - -#if 0 - if (libssh2_kex_agree_lang(session, &session->local, lang_cs, lang_cs_len) - || libssh2_kex_agree_lang(session, &session->remote, lang_sc, - lang_sc_len)) { - return -1; - } -#endif - - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on KEX method: %s", - session->kex->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on HOSTKEY method: %s", - session->hostkey->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on CRYPT_CS method: %s", - session->local.crypt->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on CRYPT_SC method: %s", - session->remote.crypt->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on MAC_CS method: %s", - session->local.mac->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on MAC_SC method: %s", - session->remote.mac->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on COMP_CS method: %s", - session->local.comp->name); - _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on COMP_SC method: %s", - session->remote.comp->name); - - return 0; -} - - - -/* _libssh2_kex_exchange - * Exchange keys - * Returns 0 on success, non-zero on failure - * - * Returns some errors without _libssh2_error() - */ -int -_libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange, - key_exchange_state_t * key_state) -{ - int rc = 0; - int retcode; - - session->state |= LIBSSH2_STATE_KEX_ACTIVE; - - if (key_state->state == libssh2_NB_state_idle) { - /* Prevent loop in packet_add() */ - session->state |= LIBSSH2_STATE_EXCHANGING_KEYS; - - if (reexchange) { - session->kex = NULL; - - if (session->hostkey && session->hostkey->dtor) { - session->hostkey->dtor(session, - &session->server_hostkey_abstract); - } - session->hostkey = NULL; - } - - key_state->state = libssh2_NB_state_created; - } - - if (!session->kex || !session->hostkey) { - if (key_state->state == libssh2_NB_state_created) { - /* Preserve in case of failure */ - key_state->oldlocal = session->local.kexinit; - key_state->oldlocal_len = session->local.kexinit_len; - - session->local.kexinit = NULL; - - key_state->state = libssh2_NB_state_sent; - } - - if (key_state->state == libssh2_NB_state_sent) { - retcode = kexinit(session); - if (retcode == LIBSSH2_ERROR_EAGAIN) { - session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; - return retcode; - } else if (retcode) { - session->local.kexinit = key_state->oldlocal; - session->local.kexinit_len = key_state->oldlocal_len; - key_state->state = libssh2_NB_state_idle; - session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; - session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; - return -1; - } - - key_state->state = libssh2_NB_state_sent1; - } - - if (key_state->state == libssh2_NB_state_sent1) { - retcode = - _libssh2_packet_require(session, SSH_MSG_KEXINIT, - &key_state->data, - &key_state->data_len, 0, NULL, 0, - &key_state->req_state); - if (retcode == LIBSSH2_ERROR_EAGAIN) { - session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; - return retcode; - } - else if (retcode) { - if (session->local.kexinit) { - LIBSSH2_FREE(session, session->local.kexinit); - } - session->local.kexinit = key_state->oldlocal; - session->local.kexinit_len = key_state->oldlocal_len; - key_state->state = libssh2_NB_state_idle; - session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; - session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; - return -1; - } - - if (session->remote.kexinit) { - LIBSSH2_FREE(session, session->remote.kexinit); - } - session->remote.kexinit = key_state->data; - session->remote.kexinit_len = key_state->data_len; - - if (kex_agree_methods(session, key_state->data, - key_state->data_len)) - rc = LIBSSH2_ERROR_KEX_FAILURE; - - key_state->state = libssh2_NB_state_sent2; - } - } else { - key_state->state = libssh2_NB_state_sent2; - } - - if (rc == 0) { - if (key_state->state == libssh2_NB_state_sent2) { - retcode = session->kex->exchange_keys(session, - &key_state->key_state_low); - if (retcode == LIBSSH2_ERROR_EAGAIN) { - session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; - return retcode; - } else if (retcode) { - rc = _libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, - "Unrecoverable error exchanging keys"); - } - } - } - - /* Done with kexinit buffers */ - if (session->local.kexinit) { - LIBSSH2_FREE(session, session->local.kexinit); - session->local.kexinit = NULL; - } - if (session->remote.kexinit) { - LIBSSH2_FREE(session, session->remote.kexinit); - session->remote.kexinit = NULL; - } - - session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; - session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; - - key_state->state = libssh2_NB_state_idle; - - return rc; -} - - - -/* libssh2_session_method_pref - * Set preferred method - */ -LIBSSH2_API int -libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type, - const char *prefs) -{ - char **prefvar, *s, *newprefs; - int prefs_len = strlen(prefs); - const LIBSSH2_COMMON_METHOD **mlist; - - switch (method_type) { - case LIBSSH2_METHOD_KEX: - prefvar = &session->kex_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods; - break; - - case LIBSSH2_METHOD_HOSTKEY: - prefvar = &session->hostkey_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods(); - break; - - case LIBSSH2_METHOD_CRYPT_CS: - prefvar = &session->local.crypt_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods(); - break; - - case LIBSSH2_METHOD_CRYPT_SC: - prefvar = &session->remote.crypt_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods(); - break; - - case LIBSSH2_METHOD_MAC_CS: - prefvar = &session->local.mac_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods(); - break; - - case LIBSSH2_METHOD_MAC_SC: - prefvar = &session->remote.mac_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods(); - break; - - case LIBSSH2_METHOD_COMP_CS: - prefvar = &session->local.comp_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) - _libssh2_comp_methods(session); - break; - - case LIBSSH2_METHOD_COMP_SC: - prefvar = &session->remote.comp_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) - _libssh2_comp_methods(session); - break; - - case LIBSSH2_METHOD_LANG_CS: - prefvar = &session->local.lang_prefs; - mlist = NULL; - break; - - case LIBSSH2_METHOD_LANG_SC: - prefvar = &session->remote.lang_prefs; - mlist = NULL; - break; - - default: - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "Invalid parameter specified for method_type"); - } - - s = newprefs = LIBSSH2_ALLOC(session, prefs_len + 1); - if (!newprefs) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Error allocated space for method preferences"); - } - memcpy(s, prefs, prefs_len + 1); - - while (s && *s) { - char *p = strchr(s, ','); - int method_len = p ? (p - s) : (int) strlen(s); - - if (!kex_get_method_by_name(s, method_len, mlist)) { - /* Strip out unsupported method */ - if (p) { - memcpy(s, p + 1, strlen(s) - method_len); - } else { - if (s > newprefs) { - *(--s) = '\0'; - } else { - *s = '\0'; - } - } - } - - s = p ? (p + 1) : NULL; - } - - if (strlen(newprefs) == 0) { - LIBSSH2_FREE(session, newprefs); - return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "The requested method(s) are not currently " - "supported"); - } - - if (*prefvar) { - LIBSSH2_FREE(session, *prefvar); - } - *prefvar = newprefs; - - return 0; -} - -/* - * libssh2_session_supported_algs() - * returns a number of returned algorithms (a positive number) on success, - * a negative number on failure - */ - -LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session, - int method_type, - const char*** algs) -{ - unsigned int i; - unsigned int j; - unsigned int ialg; - const LIBSSH2_COMMON_METHOD **mlist; - - /* to prevent coredumps due to dereferencing of NULL */ - if (NULL == algs) - return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE, - "algs must not be NULL"); - - switch (method_type) { - case LIBSSH2_METHOD_KEX: - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods; - break; - - case LIBSSH2_METHOD_HOSTKEY: - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods(); - break; - - case LIBSSH2_METHOD_CRYPT_CS: - case LIBSSH2_METHOD_CRYPT_SC: - mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods(); - break; - - case LIBSSH2_METHOD_MAC_CS: - case LIBSSH2_METHOD_MAC_SC: - mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods(); - break; - - case LIBSSH2_METHOD_COMP_CS: - case LIBSSH2_METHOD_COMP_SC: - mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_comp_methods(session); - break; - - default: - return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unknown method type"); - } /* switch */ - - /* weird situation */ - if (NULL==mlist) - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "No algorithm found"); - - /* - mlist is looped through twice. The first time to find the number od - supported algorithms (needed to allocate the proper size of array) and - the second time to actually copy the pointers. Typically this function - will not be called often (typically at the beginning of a session) and - the number of algorithms (i.e. niumber of iterations in one loop) will - not be high (typically it will not exceed 20) for quite a long time. - - So double looping really shouldn't be an issue and it is definitely a - better solution than reallocation several times. - */ - - /* count the number of supported algorithms */ - for ( i=0, ialg=0; NULL!=mlist[i]; i++) { - /* do not count fields with NULL name */ - if (mlist[i]->name) - ialg++; - } - - /* weird situation, no algorithm found */ - if (0==ialg) - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "No algorithm found"); - - /* allocate buffer */ - *algs = (const char**) LIBSSH2_ALLOC(session, ialg*sizeof(const char*)); - if ( NULL==*algs ) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Memory allocation failed"); - } - /* Past this point *algs must be deallocated in case of an error!! */ - - /* copy non-NULL pointers only */ - for ( i=0, j=0; NULL!=mlist[i] && jname ){ - /* maybe a weird situation but if it occurs, do not include NULL - pointers */ - continue; - } - - /* note that [] has higher priority than * (dereferencing) */ - (*algs)[j++] = mlist[i]->name; - } - - /* correct number of pointers copied? (test the code above) */ - if ( j!=ialg ) { - /* deallocate buffer */ - LIBSSH2_FREE(session, (void *)*algs); - *algs = NULL; - - return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE, - "Internal error"); - } - - return ialg; -} diff --git a/vendor/libssh2-1.4.2/src/knownhost.c b/vendor/libssh2-1.4.2/src/knownhost.c deleted file mode 100644 index c58dfbb20..000000000 --- a/vendor/libssh2-1.4.2/src/knownhost.c +++ /dev/null @@ -1,1146 +0,0 @@ -/* - * Copyright (c) 2009-2011 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include "misc.h" - -struct known_host { - struct list_node node; - char *name; /* points to the name or the hash (allocated) */ - size_t name_len; /* needed for hashed data */ - int port; /* if non-zero, a specific port this key is for on this - host */ - int typemask; /* plain, sha1, custom, ... */ - char *salt; /* points to binary salt (allocated) */ - size_t salt_len; /* size of salt */ - char *key; /* the (allocated) associated key. This is kept base64 - encoded in memory. */ - char *comment; /* the (allocated) optional comment text, may be NULL */ - - /* this is the struct we expose externally */ - struct libssh2_knownhost external; -}; - -struct _LIBSSH2_KNOWNHOSTS -{ - LIBSSH2_SESSION *session; /* the session this "belongs to" */ - struct list_head head; -}; - -static void free_host(LIBSSH2_SESSION *session, struct known_host *entry) -{ - if(entry) { - if(entry->comment) - LIBSSH2_FREE(session, entry->comment); - if(entry->key) - LIBSSH2_FREE(session, entry->key); - if(entry->salt) - LIBSSH2_FREE(session, entry->salt); - if(entry->name) - LIBSSH2_FREE(session, entry->name); - LIBSSH2_FREE(session, entry); - } -} - -/* - * libssh2_knownhost_init - * - * Init a collection of known hosts. Returns the pointer to a collection. - * - */ -LIBSSH2_API LIBSSH2_KNOWNHOSTS * -libssh2_knownhost_init(LIBSSH2_SESSION *session) -{ - LIBSSH2_KNOWNHOSTS *knh = - LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS)); - - if(!knh) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for known-hosts " - "collection"); - return NULL; - } - - knh->session = session; - - _libssh2_list_init(&knh->head); - - return knh; -} - -#define KNOWNHOST_MAGIC 0xdeadcafe -/* - * knownhost_to_external() - * - * Copies data from the internal to the external representation struct. - * - */ -static struct libssh2_knownhost *knownhost_to_external(struct known_host *node) -{ - struct libssh2_knownhost *ext = &node->external; - - ext->magic = KNOWNHOST_MAGIC; - ext->node = node; - ext->name = ((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) == - LIBSSH2_KNOWNHOST_TYPE_PLAIN)? node->name:NULL; - ext->key = node->key; - ext->typemask = node->typemask; - - return ext; -} - -static int -knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, const char *salt, - const char *key, size_t keylen, - const char *comment, size_t commentlen, - int typemask, struct libssh2_knownhost **store) -{ - struct known_host *entry; - size_t hostlen = strlen(host); - int rc; - char *ptr; - unsigned int ptrlen; - - /* make sure we have a key type set */ - if(!(typemask & LIBSSH2_KNOWNHOST_KEY_MASK)) - return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL, - "No key type set"); - - if(!(entry = LIBSSH2_ALLOC(hosts->session, sizeof(struct known_host)))) - return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for known host " - "entry"); - - memset(entry, 0, sizeof(struct known_host)); - - entry->typemask = typemask; - - switch(entry->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) { - case LIBSSH2_KNOWNHOST_TYPE_PLAIN: - case LIBSSH2_KNOWNHOST_TYPE_CUSTOM: - entry->name = LIBSSH2_ALLOC(hosts->session, hostlen+1); - if(!entry->name) { - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for host name"); - goto error; - } - memcpy(entry->name, host, hostlen+1); - break; - case LIBSSH2_KNOWNHOST_TYPE_SHA1: - rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen, - host, hostlen); - if(rc) - goto error; - entry->name = ptr; - entry->name_len = ptrlen; - - rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen, - salt, strlen(salt)); - if(rc) - goto error; - entry->salt = ptr; - entry->salt_len = ptrlen; - break; - default: - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unknown host name type"); - goto error; - } - - if(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64) { - /* the provided key is base64 encoded already */ - if(!keylen) - keylen = strlen(key); - entry->key = LIBSSH2_ALLOC(hosts->session, keylen+1); - if(!entry->key) { - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for key"); - goto error; - } - memcpy(entry->key, key, keylen+1); - entry->key[keylen]=0; /* force a terminating zero trailer */ - } - else { - /* key is raw, we base64 encode it and store it as such */ - size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen, - &ptr); - if(!nlen) { - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "base64-encoded key"); - goto error; - } - - entry->key = ptr; - } - - if (comment) { - entry->comment = LIBSSH2_ALLOC(hosts->session, commentlen+1); - if(!entry->comment) { - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for comment"); - goto error; - } - memcpy(entry->comment, comment, commentlen+1); - entry->comment[commentlen]=0; /* force a terminating zero trailer */ - } - else { - entry->comment = NULL; - } - - /* add this new host to the big list of known hosts */ - _libssh2_list_add(&hosts->head, &entry->node); - - if(store) - *store = knownhost_to_external(entry); - - return LIBSSH2_ERROR_NONE; - error: - free_host(hosts->session, entry); - return rc; -} - -/* - * libssh2_knownhost_add - * - * Add a host and its associated key to the collection of known hosts. - * - * The 'type' argument specifies on what format the given host and keys are: - * - * plain - ascii "hostname.domain.tld" - * sha1 - SHA1( ) base64-encoded! - * custom - another hash - * - * If 'sha1' is selected as type, the salt must be provided to the salt - * argument. This too base64 encoded. - * - * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If - * a custom type is used, salt is ignored and you must provide the host - * pre-hashed when checking for it in the libssh2_knownhost_check() function. - * - * The keylen parameter may be omitted (zero) if the key is provided as a - * NULL-terminated base64-encoded string. - */ - -LIBSSH2_API int -libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, const char *salt, - const char *key, size_t keylen, - int typemask, struct libssh2_knownhost **store) -{ - return knownhost_add(hosts, host, salt, key, keylen, NULL, 0, typemask, - store); -} - - -/* - * libssh2_knownhost_addc - * - * Add a host and its associated key to the collection of known hosts. - * - * Takes a comment argument that may be NULL. A NULL comment indicates - * there is no comment and the entry will end directly after the key - * when written out to a file. An empty string "" comment will indicate an - * empty comment which will cause a single space to be written after the key. - * - * The 'type' argument specifies on what format the given host and keys are: - * - * plain - ascii "hostname.domain.tld" - * sha1 - SHA1( ) base64-encoded! - * custom - another hash - * - * If 'sha1' is selected as type, the salt must be provided to the salt - * argument. This too base64 encoded. - * - * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If - * a custom type is used, salt is ignored and you must provide the host - * pre-hashed when checking for it in the libssh2_knownhost_check() function. - * - * The keylen parameter may be omitted (zero) if the key is provided as a - * NULL-terminated base64-encoded string. - */ - -LIBSSH2_API int -libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, const char *salt, - const char *key, size_t keylen, - const char *comment, size_t commentlen, - int typemask, struct libssh2_knownhost **store) -{ - return knownhost_add(hosts, host, salt, key, keylen, comment, commentlen, - typemask, store); -} - -/* - * knownhost_check - * - * Check a host and its associated key against the collection of known hosts. - * - * The typemask is the type/format of the given host name and key - * - * plain - ascii "hostname.domain.tld" - * sha1 - NOT SUPPORTED AS INPUT - * custom - prehashed base64 encoded. Note that this cannot use any salts. - * - * Returns: - * - * LIBSSH2_KNOWNHOST_CHECK_FAILURE - * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND - * LIBSSH2_KNOWNHOST_CHECK_MATCH - * LIBSSH2_KNOWNHOST_CHECK_MISMATCH - */ -static int -knownhost_check(LIBSSH2_KNOWNHOSTS *hosts, - const char *hostp, int port, - const char *key, size_t keylen, - int typemask, - struct libssh2_knownhost **ext) -{ - struct known_host *node; - struct known_host *badkey = NULL; - int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK; - char *keyalloc = NULL; - int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND; - char hostbuff[270]; /* most host names can't be longer than like 256 */ - const char *host; - int numcheck; /* number of host combos to check */ - int match = 0; - - if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1) - /* we can't work with a sha1 as given input */ - return LIBSSH2_KNOWNHOST_CHECK_MISMATCH; - - if(!(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64)) { - /* we got a raw key input, convert it to base64 for the checks below */ - size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen, - &keyalloc); - if(!nlen) { - _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for base64-encoded " - "key"); - return LIBSSH2_KNOWNHOST_CHECK_FAILURE; - } - - /* make the key point to this */ - key = keyalloc; - } - - /* if a port number is given, check for a '[host]:port' first before the - plain 'host' */ - if(port >= 0) { - snprintf(hostbuff, sizeof(hostbuff), "[%s]:%d", hostp, port); - host = hostbuff; - numcheck = 2; /* check both combos, start with this */ - } - else { - host = hostp; - numcheck = 1; /* only check this host version */ - } - - do { - node = _libssh2_list_first(&hosts->head); - while (node) { - switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) { - case LIBSSH2_KNOWNHOST_TYPE_PLAIN: - if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) - match = !strcmp(host, node->name); - break; - case LIBSSH2_KNOWNHOST_TYPE_CUSTOM: - if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM) - match = !strcmp(host, node->name); - break; - case LIBSSH2_KNOWNHOST_TYPE_SHA1: - if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) { - /* when we have the sha1 version stored, we can use a - plain input to produce a hash to compare with the - stored hash. - */ - libssh2_hmac_ctx ctx; - unsigned char hash[SHA_DIGEST_LENGTH]; - - if(SHA_DIGEST_LENGTH != node->name_len) { - /* the name hash length must be the sha1 size or - we can't match it */ - break; - } - libssh2_hmac_sha1_init(&ctx, node->salt, node->salt_len); - libssh2_hmac_update(ctx, (unsigned char *)host, - strlen(host)); - libssh2_hmac_final(ctx, hash); - libssh2_hmac_cleanup(&ctx); - - if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH)) - /* this is a node we're interested in */ - match = 1; - } - break; - default: /* unsupported type */ - break; - } - if(match) { - /* host name match, now compare the keys */ - if(!strcmp(key, node->key)) { - /* they match! */ - if (ext) - *ext = knownhost_to_external(node); - badkey = NULL; - rc = LIBSSH2_KNOWNHOST_CHECK_MATCH; - break; - } - else { - /* remember the first node that had a host match but a - failed key match since we continue our search from - here */ - if(!badkey) - badkey = node; - match = 0; /* don't count this as a match anymore */ - } - } - node= _libssh2_list_next(&node->node); - } - host = hostp; - } while(!match && --numcheck); - - if(badkey) { - /* key mismatch */ - if (ext) - *ext = knownhost_to_external(badkey); - rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH; - } - - if(keyalloc) - LIBSSH2_FREE(hosts->session, keyalloc); - - return rc; -} - -/* - * libssh2_knownhost_check - * - * Check a host and its associated key against the collection of known hosts. - * - * The typemask is the type/format of the given host name and key - * - * plain - ascii "hostname.domain.tld" - * sha1 - NOT SUPPORTED AS INPUT - * custom - prehashed base64 encoded. Note that this cannot use any salts. - * - * Returns: - * - * LIBSSH2_KNOWNHOST_CHECK_FAILURE - * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND - * LIBSSH2_KNOWNHOST_CHECK_MATCH - * LIBSSH2_KNOWNHOST_CHECK_MISMATCH - */ -LIBSSH2_API int -libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts, - const char *hostp, const char *key, size_t keylen, - int typemask, - struct libssh2_knownhost **ext) -{ - return knownhost_check(hosts, hostp, -1, key, keylen, - typemask, ext); -} - -/* - * libssh2_knownhost_checkp - * - * Check a host+port and its associated key against the collection of known - * hosts. - * - * Note that if 'port' is specified as greater than zero, the check function - * will be able to check for a dedicated key for this particular host+port - * combo, and if 'port' is negative it only checks for the generic host key. - * - * The typemask is the type/format of the given host name and key - * - * plain - ascii "hostname.domain.tld" - * sha1 - NOT SUPPORTED AS INPUT - * custom - prehashed base64 encoded. Note that this cannot use any salts. - * - * Returns: - * - * LIBSSH2_KNOWNHOST_CHECK_FAILURE - * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND - * LIBSSH2_KNOWNHOST_CHECK_MATCH - * LIBSSH2_KNOWNHOST_CHECK_MISMATCH - */ -LIBSSH2_API int -libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts, - const char *hostp, int port, - const char *key, size_t keylen, - int typemask, - struct libssh2_knownhost **ext) -{ - return knownhost_check(hosts, hostp, port, key, keylen, - typemask, ext); -} - - -/* - * libssh2_knownhost_del - * - * Remove a host from the collection of known hosts. - * - */ -LIBSSH2_API int -libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts, - struct libssh2_knownhost *entry) -{ - struct known_host *node; - - /* check that this was retrieved the right way or get out */ - if(!entry || (entry->magic != KNOWNHOST_MAGIC)) - return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL, - "Invalid host information"); - - /* get the internal node pointer */ - node = entry->node; - - /* unlink from the list of all hosts */ - _libssh2_list_remove(&node->node); - - /* clear the struct now since the memory in which it is allocated is - about to be freed! */ - memset(entry, 0, sizeof(struct libssh2_knownhost)); - - /* free all resources */ - free_host(hosts->session, node); - - return 0; -} - -/* - * libssh2_knownhost_free - * - * Free an entire collection of known hosts. - * - */ -LIBSSH2_API void -libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts) -{ - struct known_host *node; - struct known_host *next; - - for(node = _libssh2_list_first(&hosts->head); node; node = next) { - next = _libssh2_list_next(&node->node); - free_host(hosts->session, node); - } - LIBSSH2_FREE(hosts->session, hosts); -} - - -/* old style plain text: [name]([,][name])* - - for the sake of simplicity, we add them as separate hosts with the same - key -*/ -static int oldstyle_hostline(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, size_t hostlen, - const char *key, size_t keylen, int key_type, - const char *comment, size_t commentlen) -{ - int rc = 0; - size_t namelen = 0; - const char *name = host + hostlen; - - if(hostlen < 1) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Failed to parse known_hosts line " - "(no host names)"); - - while(name > host) { - --name; - ++namelen; - - /* when we get the the start or see a comma coming up, add the host - name to the collection */ - if((name == host) || (*(name-1) == ',')) { - - char hostbuf[256]; - - /* make sure we don't overflow the buffer */ - if(namelen >= sizeof(hostbuf)-1) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Failed to parse known_hosts line " - "(unexpected length)"); - - /* copy host name to the temp buffer and zero terminate */ - memcpy(hostbuf, name, namelen); - hostbuf[namelen]=0; - - rc = knownhost_add(hosts, hostbuf, NULL, key, keylen, - comment, commentlen, - key_type | LIBSSH2_KNOWNHOST_TYPE_PLAIN | - LIBSSH2_KNOWNHOST_KEYENC_BASE64, NULL); - if(rc) - return rc; - - if(name > host) { - namelen = 0; - --name; /* skip comma */ - } - } - } - - return rc; -} - -/* |1|[salt]|[hash] */ -static int hashed_hostline(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, size_t hostlen, - const char *key, size_t keylen, int key_type, - const char *comment, size_t commentlen) -{ - const char *p; - char saltbuf[32]; - char hostbuf[256]; - - const char *salt = &host[3]; /* skip the magic marker */ - hostlen -= 3; /* deduct the marker */ - - /* this is where the salt starts, find the end of it */ - for(p = salt; *p && (*p != '|'); p++) - ; - - if(*p=='|') { - const char *hash = NULL; - size_t saltlen = p - salt; - if(saltlen >= (sizeof(saltbuf)-1)) /* weird length */ - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Failed to parse known_hosts line " - "(unexpectedly long salt)"); - - memcpy(saltbuf, salt, saltlen); - saltbuf[saltlen] = 0; /* zero terminate */ - salt = saltbuf; /* point to the stack based buffer */ - - hash = p+1; /* the host hash is after the separator */ - - /* now make the host point to the hash */ - host = hash; - hostlen -= saltlen+1; /* deduct the salt and separator */ - - /* check that the lengths seem sensible */ - if(hostlen >= sizeof(hostbuf)-1) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Failed to parse known_hosts line " - "(unexpected length)"); - - memcpy(hostbuf, host, hostlen); - hostbuf[hostlen]=0; - - return knownhost_add(hosts, hostbuf, salt, key, keylen, comment, - commentlen, - key_type | LIBSSH2_KNOWNHOST_TYPE_SHA1 | - LIBSSH2_KNOWNHOST_KEYENC_BASE64, NULL); - } - else - return 0; /* XXX: This should be an error, shouldn't it? */ -} - -/* - * hostline() - * - * Parse a single known_host line pre-split into host and key. - * - * The key part may include an optional comment which will be parsed here - * for ssh-rsa and ssh-dsa keys. Comments in other key types aren't handled. - * - * The function assumes new-lines have already been removed from the arguments. - */ -static int hostline(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, size_t hostlen, - const char *key, size_t keylen) -{ - const char *comment = NULL; - size_t commentlen = 0; - int key_type; - - /* make some checks that the lengths seem sensible */ - if(keylen < 20) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Failed to parse known_hosts line " - "(key too short)"); - - switch(key[0]) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - key_type = LIBSSH2_KNOWNHOST_KEY_RSA1; - - /* Note that the old-style keys (RSA1) aren't truly base64, but we - * claim it is for now since we can get away with strcmp()ing the - * entire anything anyway! We need to check and fix these to make them - * work properly. - */ - break; - - case 's': /* ssh-dss or ssh-rsa */ - if(!strncmp(key, "ssh-dss", 7)) - key_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS; - else if(!strncmp(key, "ssh-rsa", 7)) - key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA; - else - /* unknown key type */ - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unknown key type"); - - key += 7; - keylen -= 7; - - /* skip whitespaces */ - while((*key ==' ') || (*key == '\t')) { - key++; - keylen--; - } - - comment = key; - commentlen = keylen; - - /* move over key */ - while(commentlen && *comment && - (*comment != ' ') && (*comment != '\t')) { - comment++; - commentlen--; - } - - /* reduce key by comment length */ - keylen -= commentlen; - - /* Distinguish empty comment (a space) from no comment (no space) */ - if (commentlen == 0) - comment = NULL; - - /* skip whitespaces */ - while(commentlen && *comment && - ((*comment ==' ') || (*comment == '\t'))) { - comment++; - commentlen--; - } - break; - - default: /* unknown key format */ - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unknown key format"); - } - - /* Figure out host format */ - if((hostlen >2) && memcmp(host, "|1|", 3)) { - /* old style plain text: [name]([,][name])* - - for the sake of simplicity, we add them as separate hosts with the - same key - */ - return oldstyle_hostline(hosts, host, hostlen, key, keylen, key_type, - comment, commentlen); - } - else { - /* |1|[salt]|[hash] */ - return hashed_hostline(hosts, host, hostlen, key, keylen, key_type, - comment, commentlen); - } -} - -/* - * libssh2_knownhost_readline() - * - * Pass in a line of a file of 'type'. - * - * LIBSSH2_KNOWNHOST_FILE_OPENSSH is the only supported type. - * - * OpenSSH line format: - * - * - * - * Where the two parts can be created like: - * - * can be either - * or - * - * consists of - * [name] optionally followed by [,name] one or more times - * - * consists of - * |1||hash - * - * can be one of: - * [RSA bits] [e] [n as a decimal number] - * 'ssh-dss' [base64-encoded-key] - * 'ssh-rsa' [base64-encoded-key] - * - */ -LIBSSH2_API int -libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS *hosts, - const char *line, size_t len, int type) -{ - const char *cp; - const char *hostp; - const char *keyp; - size_t hostlen; - size_t keylen; - int rc; - - if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unsupported type of known-host information " - "store"); - - cp = line; - - /* skip leading whitespaces */ - while(len && ((*cp==' ') || (*cp == '\t'))) { - cp++; - len--; - } - - if(!len || !*cp || (*cp == '#') || (*cp == '\n')) - /* comment or empty line */ - return LIBSSH2_ERROR_NONE; - - /* the host part starts here */ - hostp = cp; - - /* move over the host to the separator */ - while(len && *cp && (*cp!=' ') && (*cp != '\t')) { - cp++; - len--; - } - - hostlen = cp - hostp; - - /* the key starts after the whitespaces */ - while(len && *cp && ((*cp==' ') || (*cp == '\t'))) { - cp++; - len--; - } - - if(!*cp || !len) /* illegal line */ - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Failed to parse known_hosts line"); - - keyp = cp; /* the key starts here */ - keylen = len; - - /* check if the line (key) ends with a newline and if so kill it */ - while(len && *cp && (*cp != '\n')) { - cp++; - len--; - } - - /* zero terminate where the newline is */ - if(*cp == '\n') - keylen--; /* don't include this in the count */ - - /* deal with this one host+key line */ - rc = hostline(hosts, hostp, hostlen, keyp, keylen); - if(rc) - return rc; /* failed */ - - return LIBSSH2_ERROR_NONE; /* success */ -} - -/* - * libssh2_knownhost_readfile - * - * Read hosts+key pairs from a given file. - * - * Returns a negative value for error or number of successfully added hosts. - * - */ - -LIBSSH2_API int -libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts, - const char *filename, int type) -{ - FILE *file; - int num = 0; - char buf[2048]; - - if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unsupported type of known-host information " - "store"); - - file = fopen(filename, "r"); - if(file) { - while(fgets(buf, sizeof(buf), file)) { - if(libssh2_knownhost_readline(hosts, buf, strlen(buf), type)) - break; - num++; - } - fclose(file); - } - else - return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE, - "Failed to open file"); - - return num; -} - -/* - * knownhost_writeline() - * - * Ask libssh2 to convert a known host to an output line for storage. - * - * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given - * output buffer is too small to hold the desired output. The 'outlen' field - * will then contain the size libssh2 wanted to store, which then is the - * smallest sufficient buffer it would require. - * - */ -static int -knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, - struct known_host *node, - char *buf, size_t buflen, - size_t *outlen, int type) -{ - int rc = LIBSSH2_ERROR_NONE; - int tindex; - const char *keytypes[4]={ - "", /* not used */ - "", /* this type has no name in the file */ - " ssh-rsa", - " ssh-dss" - }; - const char *keytype; - size_t nlen; - size_t commentlen = 0; - - /* we only support this single file type for now, bail out on all other - attempts */ - if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unsupported type of known-host information " - "store"); - - tindex = (node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) >> - LIBSSH2_KNOWNHOST_KEY_SHIFT; - - /* set the string used in the file */ - keytype = keytypes[tindex]; - - /* calculate extra space needed for comment */ - if(node->comment) - commentlen = strlen(node->comment) + 1; - - if((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) == - LIBSSH2_KNOWNHOST_TYPE_SHA1) { - char *namealloc; - char *saltalloc; - nlen = _libssh2_base64_encode(hosts->session, node->name, - node->name_len, &namealloc); - if(!nlen) - return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "base64-encoded host name"); - - nlen = _libssh2_base64_encode(hosts->session, - node->salt, node->salt_len, - &saltalloc); - if(!nlen) { - free(namealloc); - return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "base64-encoded salt"); - } - - nlen = strlen(saltalloc) + strlen(namealloc) + strlen(keytype) + - strlen(node->key) + commentlen + 7; - /* |1| + | + ' ' + \n + \0 = 7 */ - - if(nlen <= buflen) - if(node->comment) - snprintf(buf, buflen, "|1|%s|%s%s %s %s\n", saltalloc, namealloc, - keytype, node->key, node->comment); - else - snprintf(buf, buflen, "|1|%s|%s%s %s\n", saltalloc, namealloc, - keytype, node->key); - else - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL, - "Known-host write buffer too small"); - - free(namealloc); - free(saltalloc); - } - else { - nlen = strlen(node->name) + strlen(keytype) + strlen(node->key) + - commentlen + 3; - /* ' ' + '\n' + \0 = 3 */ - if(nlen <= buflen) - /* these types have the plain name */ - if(node->comment) - snprintf(buf, buflen, "%s%s %s %s\n", node->name, keytype, node->key, - node->comment); - else - snprintf(buf, buflen, "%s%s %s\n", node->name, keytype, node->key); - else - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL, - "Known-host write buffer too small"); - } - - /* we report the full length of the data with the trailing zero excluded */ - *outlen = nlen-1; - - return rc; -} - -/* - * libssh2_knownhost_writeline() - * - * Ask libssh2 to convert a known host to an output line for storage. - * - * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given - * output buffer is too small to hold the desired output. - */ -LIBSSH2_API int -libssh2_knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, - struct libssh2_knownhost *known, - char *buffer, size_t buflen, - size_t *outlen, /* the amount of written data */ - int type) -{ - struct known_host *node; - - if(known->magic != KNOWNHOST_MAGIC) - return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL, - "Invalid host information"); - - node = known->node; - - return knownhost_writeline(hosts, node, buffer, buflen, outlen, type); -} - -/* - * libssh2_knownhost_writefile() - * - * Write hosts+key pairs to the given file. - */ -LIBSSH2_API int -libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts, - const char *filename, int type) -{ - struct known_host *node; - FILE *file; - int rc = LIBSSH2_ERROR_NONE; - char buffer[2048]; - - /* we only support this single file type for now, bail out on all other - attempts */ - if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) - return _libssh2_error(hosts->session, - LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, - "Unsupported type of known-host information " - "store"); - - file = fopen(filename, "w"); - if(!file) - return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE, - "Failed to open file"); - - for(node = _libssh2_list_first(&hosts->head); - node; - node= _libssh2_list_next(&node->node) ) { - size_t wrote; - size_t nwrote; - rc = knownhost_writeline(hosts, node, buffer, sizeof(buffer), &wrote, - type); - if(rc) - break; - - nwrote = fwrite(buffer, 1, wrote, file); - if(nwrote != wrote) { - /* failed to write the whole thing, bail out */ - rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE, - "Write failed"); - break; - } - } - fclose(file); - - return rc; -} - - -/* - * libssh2_knownhost_get() - * - * Traverse the internal list of known hosts. Pass NULL to 'prev' to get - * the first one. - * - * Returns: - * 0 if a fine host was stored in 'store' - * 1 if end of hosts - * [negative] on errors - */ -LIBSSH2_API int -libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS *hosts, - struct libssh2_knownhost **ext, - struct libssh2_knownhost *oprev) -{ - struct known_host *node; - if(oprev && oprev->node) { - /* we have a starting point */ - struct known_host *prev = oprev->node; - - /* get the next node in the list */ - node = _libssh2_list_next(&prev->node); - - } - else - node = _libssh2_list_first(&hosts->head); - - if(!node) - /* no (more) node */ - return 1; - - *ext = knownhost_to_external(node); - - return 0; -} diff --git a/vendor/libssh2-1.4.2/src/libgcrypt.c b/vendor/libssh2-1.4.2/src/libgcrypt.c deleted file mode 100644 index 5c2787bba..000000000 --- a/vendor/libssh2-1.4.2/src/libgcrypt.c +++ /dev/null @@ -1,593 +0,0 @@ -/* Copyright (C) 2008, 2009, Simon Josefsson - * Copyright (C) 2006, 2007, The Written Word, Inc. - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -#ifdef LIBSSH2_LIBGCRYPT /* compile only if we build with libgcrypt */ - -#include - -int -_libssh2_rsa_new(libssh2_rsa_ctx ** rsa, - const unsigned char *edata, - unsigned long elen, - const unsigned char *ndata, - unsigned long nlen, - const unsigned char *ddata, - unsigned long dlen, - const unsigned char *pdata, - unsigned long plen, - const unsigned char *qdata, - unsigned long qlen, - const unsigned char *e1data, - unsigned long e1len, - const unsigned char *e2data, - unsigned long e2len, - const unsigned char *coeffdata, unsigned long coefflen) -{ - int rc; - (void) e1data; - (void) e1len; - (void) e2data; - (void) e2len; - - if (ddata) { - rc = gcry_sexp_build - (rsa, NULL, - "(private-key(rsa(n%b)(e%b)(d%b)(q%b)(p%b)(u%b)))", - nlen, ndata, elen, edata, dlen, ddata, plen, pdata, - qlen, qdata, coefflen, coeffdata); - } else { - rc = gcry_sexp_build(rsa, NULL, "(public-key(rsa(n%b)(e%b)))", - nlen, ndata, elen, edata); - } - if (rc) { - *rsa = NULL; - return -1; - } - - return 0; -} - -int -_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa, - const unsigned char *sig, - unsigned long sig_len, - const unsigned char *m, unsigned long m_len) -{ - unsigned char hash[SHA_DIGEST_LENGTH]; - gcry_sexp_t s_sig, s_hash; - int rc = -1; - - libssh2_sha1(m, m_len, hash); - - rc = gcry_sexp_build(&s_hash, NULL, - "(data (flags pkcs1) (hash sha1 %b))", - SHA_DIGEST_LENGTH, hash); - if (rc != 0) { - return -1; - } - - rc = gcry_sexp_build(&s_sig, NULL, "(sig-val(rsa(s %b)))", sig_len, sig); - if (rc != 0) { - gcry_sexp_release(s_hash); - return -1; - } - - rc = gcry_pk_verify(s_sig, s_hash, rsa); - gcry_sexp_release(s_sig); - gcry_sexp_release(s_hash); - - return (rc == 0) ? 0 : -1; -} - -int -_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, - const unsigned char *p, - unsigned long p_len, - const unsigned char *q, - unsigned long q_len, - const unsigned char *g, - unsigned long g_len, - const unsigned char *y, - unsigned long y_len, - const unsigned char *x, unsigned long x_len) -{ - int rc; - - if (x_len) { - rc = gcry_sexp_build - (dsactx, NULL, - "(private-key(dsa(p%b)(q%b)(g%b)(y%b)(x%b)))", - p_len, p, q_len, q, g_len, g, y_len, y, x_len, x); - } else { - rc = gcry_sexp_build(dsactx, NULL, - "(public-key(dsa(p%b)(q%b)(g%b)(y%b)))", - p_len, p, q_len, q, g_len, g, y_len, y); - } - - if (rc) { - *dsactx = NULL; - return -1; - } - - return 0; -} - -int -_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, - LIBSSH2_SESSION * session, - const char *filename, unsigned const char *passphrase) -{ - FILE *fp; - unsigned char *data, *save_data; - unsigned int datalen; - int ret; - unsigned char *n, *e, *d, *p, *q, *e1, *e2, *coeff; - unsigned int nlen, elen, dlen, plen, qlen, e1len, e2len, coefflen; - - (void) passphrase; - - fp = fopen(filename, "r"); - if (!fp) { - return -1; - } - - ret = _libssh2_pem_parse(session, - "-----BEGIN RSA PRIVATE KEY-----", - "-----END RSA PRIVATE KEY-----", - fp, &data, &datalen); - fclose(fp); - if (ret) { - return -1; - } - - save_data = data; - - if (_libssh2_pem_decode_sequence(&data, &datalen)) { - ret = -1; - goto fail; - } -/* First read Version field (should be 0). */ - ret = _libssh2_pem_decode_integer(&data, &datalen, &n, &nlen); - if (ret != 0 || (nlen != 1 && *n != '\0')) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &n, &nlen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &e, &elen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &d, &dlen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &q, &qlen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &e1, &e1len); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &e2, &e2len); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &coeff, &coefflen); - if (ret != 0) { - ret = -1; - goto fail; - } - - if (_libssh2_rsa_new(rsa, e, elen, n, nlen, d, dlen, p, plen, - q, qlen, e1, e1len, e2, e2len, coeff, coefflen)) { - ret = -1; - goto fail; - } - - ret = 0; - - fail: - LIBSSH2_FREE(session, save_data); - return ret; -} - -int -_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, - LIBSSH2_SESSION * session, - const char *filename, unsigned const char *passphrase) -{ - FILE *fp; - unsigned char *data, *save_data; - unsigned int datalen; - int ret; - unsigned char *p, *q, *g, *y, *x; - unsigned int plen, qlen, glen, ylen, xlen; - - (void) passphrase; - - fp = fopen(filename, "r"); - if (!fp) { - return -1; - } - - ret = _libssh2_pem_parse(session, - "-----BEGIN DSA PRIVATE KEY-----", - "-----END DSA PRIVATE KEY-----", - fp, &data, &datalen); - fclose(fp); - if (ret) { - return -1; - } - - save_data = data; - - if (_libssh2_pem_decode_sequence(&data, &datalen)) { - ret = -1; - goto fail; - } - -/* First read Version field (should be 0). */ - ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen); - if (ret != 0 || (plen != 1 && *p != '\0')) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &q, &qlen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &g, &glen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &y, &ylen); - if (ret != 0) { - ret = -1; - goto fail; - } - - ret = _libssh2_pem_decode_integer(&data, &datalen, &x, &xlen); - if (ret != 0) { - ret = -1; - goto fail; - } - - if (datalen != 0) { - ret = -1; - goto fail; - } - - if (_libssh2_dsa_new(dsa, p, plen, q, qlen, g, glen, y, ylen, x, xlen)) { - ret = -1; - goto fail; - } - - ret = 0; - - fail: - LIBSSH2_FREE(session, save_data); - return ret; -} - -int -_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, - libssh2_dsa_ctx * rsactx, - const unsigned char *hash, - size_t hash_len, - unsigned char **signature, size_t *signature_len) -{ - gcry_sexp_t sig_sexp; - gcry_sexp_t data; - int rc; - const char *tmp; - size_t size; - - if (hash_len != SHA_DIGEST_LENGTH) { - return -1; - } - - if (gcry_sexp_build(&data, NULL, - "(data (flags pkcs1) (hash sha1 %b))", - hash_len, hash)) { - return -1; - } - - rc = gcry_pk_sign(&sig_sexp, data, rsactx); - - gcry_sexp_release(data); - - if (rc != 0) { - return -1; - } - - data = gcry_sexp_find_token(sig_sexp, "s", 0); - if (!data) { - return -1; - } - - tmp = gcry_sexp_nth_data(data, 1, &size); - if (!tmp) { - return -1; - } - - if (tmp[0] == '\0') { - tmp++; - size--; - } - - *signature = LIBSSH2_ALLOC(session, size); - memcpy(*signature, tmp, size); - *signature_len = size; - - return rc; -} - -int -_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, - const unsigned char *hash, - unsigned long hash_len, unsigned char *sig) -{ - unsigned char zhash[SHA_DIGEST_LENGTH + 1]; - gcry_sexp_t sig_sexp; - gcry_sexp_t data; - int ret; - const char *tmp; - size_t size; - - if (hash_len != SHA_DIGEST_LENGTH) { - return -1; - } - - memcpy(zhash + 1, hash, hash_len); - zhash[0] = 0; - - if (gcry_sexp_build(&data, NULL, "(data (value %b))", hash_len + 1, zhash)) { - return -1; - } - - ret = gcry_pk_sign(&sig_sexp, data, dsactx); - - gcry_sexp_release(data); - - if (ret != 0) { - return -1; - } - - memset(sig, 0, 40); - -/* Extract R. */ - - data = gcry_sexp_find_token(sig_sexp, "r", 0); - if (!data) - goto err; - - tmp = gcry_sexp_nth_data(data, 1, &size); - if (!tmp) - goto err; - - if (tmp[0] == '\0') { - tmp++; - size--; - } - - if (size < 1 || size > 20) - goto err; - - memcpy(sig + (20 - size), tmp, size); - - gcry_sexp_release(data); - -/* Extract S. */ - - data = gcry_sexp_find_token(sig_sexp, "s", 0); - if (!data) - goto err; - - tmp = gcry_sexp_nth_data(data, 1, &size); - if (!tmp) - goto err; - - if (tmp[0] == '\0') { - tmp++; - size--; - } - - if (size < 1 || size > 20) - goto err; - - memcpy(sig + 20 + (20 - size), tmp, size); - goto out; - - err: - ret = -1; - - out: - if (sig_sexp) { - gcry_sexp_release(sig_sexp); - } - if (data) { - gcry_sexp_release(data); - } - return ret; -} - -int -_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, - const unsigned char *sig, - const unsigned char *m, unsigned long m_len) -{ - unsigned char hash[SHA_DIGEST_LENGTH + 1]; - gcry_sexp_t s_sig, s_hash; - int rc = -1; - - libssh2_sha1(m, m_len, hash + 1); - hash[0] = 0; - - if (gcry_sexp_build(&s_hash, NULL, "(data(flags raw)(value %b))", - SHA_DIGEST_LENGTH + 1, hash)) { - return -1; - } - - if (gcry_sexp_build(&s_sig, NULL, "(sig-val(dsa(r %b)(s %b)))", - 20, sig, 20, sig + 20)) { - gcry_sexp_release(s_hash); - return -1; - } - - rc = gcry_pk_verify(s_sig, s_hash, dsactx); - gcry_sexp_release(s_sig); - gcry_sexp_release(s_hash); - - return (rc == 0) ? 0 : -1; -} - -int -_libssh2_cipher_init(_libssh2_cipher_ctx * h, - _libssh2_cipher_type(algo), - unsigned char *iv, unsigned char *secret, int encrypt) -{ - int ret; - int cipher = _libssh2_gcry_cipher (algo); - int mode = _libssh2_gcry_mode (algo); - int keylen = gcry_cipher_get_algo_keylen(cipher); - - (void) encrypt; - - ret = gcry_cipher_open(h, cipher, mode, 0); - if (ret) { - return -1; - } - - ret = gcry_cipher_setkey(*h, secret, keylen); - if (ret) { - gcry_cipher_close(*h); - return -1; - } - - if (mode != GCRY_CIPHER_MODE_STREAM) { - int blklen = gcry_cipher_get_algo_blklen(cipher); - if (mode == GCRY_CIPHER_MODE_CTR) - ret = gcry_cipher_setctr(*h, iv, blklen); - else - ret = gcry_cipher_setiv(*h, iv, blklen); - if (ret) { - gcry_cipher_close(*h); - return -1; - } - } - - return 0; -} - -int -_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, - _libssh2_cipher_type(algo), - int encrypt, unsigned char *block) -{ - int cipher = _libssh2_gcry_cipher (algo); - size_t blklen = gcry_cipher_get_algo_blklen(cipher); - int ret; - - if (blklen == 1) { -/* Hack for arcfour. */ - blklen = 8; - } - - if (encrypt) { - ret = gcry_cipher_encrypt(*ctx, block, blklen, block, blklen); - } else { - ret = gcry_cipher_decrypt(*ctx, block, blklen, block, blklen); - } - return ret; -} - -int -_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - const char *privatekey, - const char *passphrase) -{ - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Unable to extract public key from private key file: " - "Method unimplemented in libgcrypt backend"); -} - -void _libssh2_init_aes_ctr(void) -{ - /* no implementation */ -} -#endif /* LIBSSH2_LIBGCRYPT */ diff --git a/vendor/libssh2-1.4.2/src/libgcrypt.h b/vendor/libssh2-1.4.2/src/libgcrypt.h deleted file mode 100644 index 04516e5d1..000000000 --- a/vendor/libssh2-1.4.2/src/libgcrypt.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2008, 2009, 2010 Simon Josefsson - * Copyright (C) 2006, 2007, The Written Word, Inc. - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include - -#define LIBSSH2_MD5 1 - -#define LIBSSH2_HMAC_RIPEMD 1 - -#define LIBSSH2_AES 1 -#define LIBSSH2_AES_CTR 1 -#define LIBSSH2_BLOWFISH 1 -#define LIBSSH2_RC4 1 -#define LIBSSH2_CAST 1 -#define LIBSSH2_3DES 1 - -#define LIBSSH2_RSA 1 -#define LIBSSH2_DSA 1 - -#define MD5_DIGEST_LENGTH 16 -#define SHA_DIGEST_LENGTH 20 - -#define _libssh2_random(buf, len) \ - (gcry_randomize ((buf), (len), GCRY_STRONG_RANDOM), 1) - -#define libssh2_sha1_ctx gcry_md_hd_t -#define libssh2_sha1_init(ctx) gcry_md_open (ctx, GCRY_MD_SHA1, 0); -#define libssh2_sha1_update(ctx, data, len) gcry_md_write (ctx, data, len) -#define libssh2_sha1_final(ctx, out) \ - memcpy (out, gcry_md_read (ctx, 0), SHA_DIGEST_LENGTH), gcry_md_close (ctx) -#define libssh2_sha1(message, len, out) \ - gcry_md_hash_buffer (GCRY_MD_SHA1, out, message, len) - -#define libssh2_md5_ctx gcry_md_hd_t -#define libssh2_md5_init(ctx) gcry_md_open (ctx, GCRY_MD_MD5, 0); -#define libssh2_md5_update(ctx, data, len) gcry_md_write (ctx, data, len) -#define libssh2_md5_final(ctx, out) \ - memcpy (out, gcry_md_read (ctx, 0), MD5_DIGEST_LENGTH), gcry_md_close (ctx) -#define libssh2_md5(message, len, out) \ - gcry_md_hash_buffer (GCRY_MD_MD5, out, message, len) - -#define libssh2_hmac_ctx gcry_md_hd_t -#define libssh2_hmac_sha1_init(ctx, key, keylen) \ - gcry_md_open (ctx, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC), \ - gcry_md_setkey (*ctx, key, keylen) -#define libssh2_hmac_md5_init(ctx, key, keylen) \ - gcry_md_open (ctx, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC), \ - gcry_md_setkey (*ctx, key, keylen) -#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \ - gcry_md_open (ctx, GCRY_MD_RMD160, GCRY_MD_FLAG_HMAC), \ - gcry_md_setkey (*ctx, key, keylen) -#define libssh2_hmac_update(ctx, data, datalen) \ - gcry_md_write (ctx, data, datalen) -#define libssh2_hmac_final(ctx, data) \ - memcpy (data, gcry_md_read (ctx, 0), \ - gcry_md_get_algo_dlen (gcry_md_get_algo (ctx))) -#define libssh2_hmac_cleanup(ctx) gcry_md_close (*ctx); - -#define libssh2_crypto_init() gcry_control (GCRYCTL_DISABLE_SECMEM) -#define libssh2_crypto_exit() - -#define libssh2_rsa_ctx struct gcry_sexp - -#define _libssh2_rsa_free(rsactx) gcry_sexp_release (rsactx) - -#define libssh2_dsa_ctx struct gcry_sexp - -#define _libssh2_dsa_free(dsactx) gcry_sexp_release (dsactx) - -#define _libssh2_cipher_type(name) int name -#define _libssh2_cipher_ctx gcry_cipher_hd_t - -#define _libssh2_gcry_ciphermode(c,m) ((c << 8) | m) -#define _libssh2_gcry_cipher(c) (c >> 8) -#define _libssh2_gcry_mode(m) (m & 0xFF) - -#define _libssh2_cipher_aes256ctr \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CTR) -#define _libssh2_cipher_aes192ctr \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CTR) -#define _libssh2_cipher_aes128ctr \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR) -#define _libssh2_cipher_aes256 \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC) -#define _libssh2_cipher_aes192 \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC) -#define _libssh2_cipher_aes128 \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC) -#define _libssh2_cipher_blowfish \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC) -#define _libssh2_cipher_arcfour \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM) -#define _libssh2_cipher_cast5 \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC) -#define _libssh2_cipher_3des \ - _libssh2_gcry_ciphermode(GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC) - - -#define _libssh2_cipher_dtor(ctx) gcry_cipher_close(*(ctx)) - -#define _libssh2_bn struct gcry_mpi -#define _libssh2_bn_ctx int -#define _libssh2_bn_ctx_new() 0 -#define _libssh2_bn_ctx_free(bnctx) ((void)0) -#define _libssh2_bn_init() gcry_mpi_new(0) -#define _libssh2_bn_rand(bn, bits, top, bottom) gcry_mpi_randomize (bn, bits, GCRY_WEAK_RANDOM) -#define _libssh2_bn_mod_exp(r, a, p, m, ctx) gcry_mpi_powm (r, a, p, m) -#define _libssh2_bn_set_word(bn, val) gcry_mpi_set_ui(bn, val) -#define _libssh2_bn_from_bin(bn, len, val) gcry_mpi_scan(&((bn)), GCRYMPI_FMT_USG, val, len, NULL) -#define _libssh2_bn_to_bin(bn, val) gcry_mpi_print (GCRYMPI_FMT_USG, val, _libssh2_bn_bytes(bn), NULL, bn) -#define _libssh2_bn_bytes(bn) (gcry_mpi_get_nbits (bn) / 8 + ((gcry_mpi_get_nbits (bn) % 8 == 0) ? 0 : 1)) -#define _libssh2_bn_bits(bn) gcry_mpi_get_nbits (bn) -#define _libssh2_bn_free(bn) gcry_mpi_release(bn) - diff --git a/vendor/libssh2-1.4.2/src/libssh2_config.h b/vendor/libssh2-1.4.2/src/libssh2_config.h deleted file mode 100644 index 4591f5f1c..000000000 --- a/vendor/libssh2-1.4.2/src/libssh2_config.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef WIN32 - #include "libssh2_config_win.h" -#else - #include "libssh2_config_osx.h" -#endif diff --git a/vendor/libssh2-1.4.2/src/libssh2_config_osx.h b/vendor/libssh2-1.4.2/src/libssh2_config_osx.h deleted file mode 100644 index d8917c2a8..000000000 --- a/vendor/libssh2-1.4.2/src/libssh2_config_osx.h +++ /dev/null @@ -1,224 +0,0 @@ -/* src/libssh2_config.h. Generated from libssh2_config.h.in by configure. */ -/* src/libssh2_config.h.in. Generated from configure.ac by autoheader. */ - -/* Define if building universal (internal helper macro) */ -/* #undef AC_APPLE_UNIVERSAL_BUILD */ - -/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP - systems. This function is required for `alloca.c' support on those systems. - */ -/* #undef CRAY_STACKSEG_END */ - -/* Define to 1 if using `alloca.c'. */ -/* #undef C_ALLOCA */ - -/* Define to 1 if you have `alloca', as a function or macro. */ -#define HAVE_ALLOCA 1 - -/* Define to 1 if you have and it should be used (not on Ultrix). - */ -#define HAVE_ALLOCA_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ARPA_INET_H 1 - -/* disabled non-blocking sockets */ -/* #undef HAVE_DISABLED_NONBLOCKING */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ERRNO_H 1 - -/* Define to 1 if you have the `EVP_aes_128_ctr' function. */ -/* #undef HAVE_EVP_AES_128_CTR */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* use FIONBIO for non-blocking sockets */ -/* #undef HAVE_FIONBIO */ - -/* Define to 1 if you have the `gettimeofday' function. */ -#define HAVE_GETTIMEOFDAY 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* use ioctlsocket() for non-blocking sockets */ -/* #undef HAVE_IOCTLSOCKET */ - -/* use Ioctlsocket() for non-blocking sockets */ -/* #undef HAVE_IOCTLSOCKET_CASE */ - -/* Define if you have the gcrypt library. */ -/* #undef HAVE_LIBGCRYPT */ - -/* Define if you have the ssl library. */ -#define HAVE_LIBSSL 1 - -/* Define if you have the z library. */ -#define HAVE_LIBZ 1 - -/* Define to 1 if the compiler supports the 'long long' data type. */ -#define HAVE_LONGLONG 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_NETINET_IN_H 1 - -/* use O_NONBLOCK for non-blocking sockets */ -#define HAVE_O_NONBLOCK 1 - -/* Define to 1 if you have the `poll' function. */ -/* #undef HAVE_POLL */ - -/* Define to 1 if you have the select function. */ -#define HAVE_SELECT 1 - -/* use SO_NONBLOCK for non-blocking sockets */ -/* #undef HAVE_SO_NONBLOCK */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strtoll' function. */ -#define HAVE_STRTOLL 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_IOCTL_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SELECT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SOCKET_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TIME_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_UIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_UN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WINDOWS_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WINSOCK2_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WS2TCPIP_H */ - -/* to make a symbol visible */ -/* #undef LIBSSH2_API */ - -/* Enable "none" cipher -- NOT RECOMMENDED */ -/* #undef LIBSSH2_CRYPT_NONE */ - -/* Enable newer diffie-hellman-group-exchange-sha1 syntax */ -#define LIBSSH2_DH_GEX_NEW 1 - -/* Compile in zlib support */ -#define LIBSSH2_HAVE_ZLIB 1 - -/* Use libgcrypt */ -/* #undef LIBSSH2_LIBGCRYPT */ - -/* Enable "none" MAC -- NOT RECOMMENDED */ -/* #undef LIBSSH2_MAC_NONE */ - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" - -/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ -/* #undef NEED_REENTRANT */ - -/* Name of package */ -#define PACKAGE "libssh2" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "libssh2-devel@cool.haxx.se" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "libssh2" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libssh2 -" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libssh2" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "-" - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at runtime. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ -/* #undef STACK_DIRECTION */ - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Version number of package */ -#define VERSION "-" - -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -/* # undef WORDS_BIGENDIAN */ -# endif -#endif - -/* Number of bits in a file offset, on hosts where this is settable. */ -/* #undef _FILE_OFFSET_BITS */ - -/* Define for large files, on AIX-style hosts. */ -/* #undef _LARGE_FILES */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif diff --git a/vendor/libssh2-1.4.2/src/libssh2_config_win.h b/vendor/libssh2-1.4.2/src/libssh2_config_win.h deleted file mode 100644 index 39e438f9f..000000000 --- a/vendor/libssh2-1.4.2/src/libssh2_config_win.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef LIBSSH2_CONFIG_H -#define LIBSSH2_CONFIG_H - -#ifndef WIN32 -#define WIN32 -#endif -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE 1 -#endif /* _CRT_SECURE_NO_DEPRECATE */ -#include -#include -#include - -#ifdef __MINGW32__ -#define HAVE_UNISTD_H -#define HAVE_INTTYPES_H -#define HAVE_SYS_TIME_H -#endif - -#define HAVE_WINSOCK2_H -#define HAVE_IOCTLSOCKET -#define HAVE_SELECT - -#ifdef _MSC_VER -#define snprintf _snprintf -#if _MSC_VER < 1500 -#define vsnprintf _vsnprintf -#endif -#define strdup _strdup -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#else -#define strncasecmp strnicmp -#define strcasecmp stricmp -#endif /* _MSC_VER */ - -/* Enable newer diffie-hellman-group-exchange-sha1 syntax */ -#define LIBSSH2_DH_GEX_NEW 1 - -#endif /* LIBSSH2_CONFIG_H */ - diff --git a/vendor/libssh2-1.4.2/src/libssh2_priv.h b/vendor/libssh2-1.4.2/src/libssh2_priv.h deleted file mode 100644 index c670a16b5..000000000 --- a/vendor/libssh2-1.4.2/src/libssh2_priv.h +++ /dev/null @@ -1,1038 +0,0 @@ -/* Copyright (c) 2004-2008, 2010, Sara Golemon - * Copyright (c) 2009-2011 by Daniel Stenberg - * Copyright (c) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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 LIBSSH2_PRIV_H -#define LIBSSH2_PRIV_H 1 - -#define LIBSSH2_LIBRARY -#include "libssh2_config.h" - -#ifdef HAVE_WINDOWS_H -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#undef WIN32_LEAN_AND_MEAN -#endif - -#ifdef HAVE_WS2TCPIP_H -#include -#endif - -#include -#include - -/* The following CPP block should really only be in session.c and - packet.c. However, AIX have #define's for 'events' and 'revents' - and we are using those names in libssh2.h, so we need to include - the AIX headers first, to make sure all code is compiled with - consistent names of these fields. While arguable the best would to - change libssh2.h to use other names, that would break backwards - compatibility. For more information, see: - http://www.mail-archive.com/libssh2-devel%40lists.sourceforge.net/msg00003.html - http://www.mail-archive.com/libssh2-devel%40lists.sourceforge.net/msg00224.html -*/ -#ifdef HAVE_POLL -# include -#else -# if defined(HAVE_SELECT) && !defined(WIN32) -# ifdef HAVE_SYS_SELECT_H -# include -# else -# include -# include -# endif -# endif -#endif - -/* Needed for struct iovec on some platforms */ -#ifdef HAVE_SYS_UIO_H -#include -#endif - -#ifdef HAVE_SYS_SOCKET_H -# include -#endif -#ifdef HAVE_SYS_IOCTL_H -# include -#endif -#ifdef HAVE_INTTYPES_H -#include -#endif - -#include "libssh2.h" -#include "libssh2_publickey.h" -#include "libssh2_sftp.h" -#include "misc.h" /* for the linked list stuff */ - -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif - -/* Provide iovec / writev on WIN32 platform. */ -#ifdef WIN32 - -struct iovec { - size_t iov_len; - void * iov_base; -}; - -#define inline __inline - -static inline int writev(int sock, struct iovec *iov, int nvecs) -{ - DWORD ret; - if (WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) { - return ret; - } - return -1; -} - -#endif /* WIN32 */ - -#include "crypto.h" - -#ifdef HAVE_WINSOCK2_H - -#include -#include -#include - -#ifdef _MSC_VER -/* "inline" keyword is valid only with C++ engine! */ -#define inline __inline -#endif - -#endif - -/* RFC4253 section 6.1 Maximum Packet Length says: - * - * "All implementations MUST be able to process packets with - * uncompressed payload length of 32768 bytes or less and - * total packet size of 35000 bytes or less (including length, - * padding length, payload, padding, and MAC.)." - */ -#define MAX_SSH_PACKET_LEN 35000 - -#define LIBSSH2_ALLOC(session, count) \ - session->alloc((count), &(session)->abstract) -#define LIBSSH2_REALLOC(session, ptr, count) \ - ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : \ - session->alloc((count), &(session)->abstract)) -#define LIBSSH2_FREE(session, ptr) \ - session->free((ptr), &(session)->abstract) -#define LIBSSH2_IGNORE(session, data, datalen) \ - session->ssh_msg_ignore((session), (data), (datalen), &(session)->abstract) -#define LIBSSH2_DEBUG(session, always_display, message, message_len, \ - language, language_len) \ - session->ssh_msg_debug((session), (always_display), (message), \ - (message_len), (language), (language_len), \ - &(session)->abstract) -#define LIBSSH2_DISCONNECT(session, reason, message, message_len, \ - language, language_len) \ - session->ssh_msg_disconnect((session), (reason), (message), \ - (message_len), (language), (language_len), \ - &(session)->abstract) - -#define LIBSSH2_MACERROR(session, data, datalen) \ - session->macerror((session), (data), (datalen), &(session)->abstract) -#define LIBSSH2_X11_OPEN(channel, shost, sport) \ - channel->session->x11(((channel)->session), (channel), \ - (shost), (sport), (&(channel)->session->abstract)) - -#define LIBSSH2_CHANNEL_CLOSE(session, channel) \ - channel->close_cb((session), &(session)->abstract, \ - (channel), &(channel)->abstract) - -#define LIBSSH2_SEND_FD(session, fd, buffer, length, flags) \ - session->send(fd, buffer, length, flags, &session->abstract) -#define LIBSSH2_RECV_FD(session, fd, buffer, length, flags) \ - session->recv(fd, buffer, length, flags, &session->abstract) - -#define LIBSSH2_SEND(session, buffer, length, flags) \ - LIBSSH2_SEND_FD(session, session->socket_fd, buffer, length, flags) -#define LIBSSH2_RECV(session, buffer, length, flags) \ - LIBSSH2_RECV_FD(session, session->socket_fd, buffer, length, flags) - -typedef struct _LIBSSH2_KEX_METHOD LIBSSH2_KEX_METHOD; -typedef struct _LIBSSH2_HOSTKEY_METHOD LIBSSH2_HOSTKEY_METHOD; -typedef struct _LIBSSH2_CRYPT_METHOD LIBSSH2_CRYPT_METHOD; -typedef struct _LIBSSH2_COMP_METHOD LIBSSH2_COMP_METHOD; - -typedef struct _LIBSSH2_PACKET LIBSSH2_PACKET; - -typedef enum -{ - libssh2_NB_state_idle = 0, - libssh2_NB_state_allocated, - libssh2_NB_state_created, - libssh2_NB_state_sent, - libssh2_NB_state_sent1, - libssh2_NB_state_sent2, - libssh2_NB_state_sent3, - libssh2_NB_state_sent4, - libssh2_NB_state_sent5, - libssh2_NB_state_sent6, - libssh2_NB_state_sent7, - libssh2_NB_state_jump1, - libssh2_NB_state_jump2, - libssh2_NB_state_jump3, - libssh2_NB_state_jump4, - libssh2_NB_state_jump5 -} libssh2_nonblocking_states; - -typedef struct packet_require_state_t -{ - libssh2_nonblocking_states state; - time_t start; -} packet_require_state_t; - -typedef struct packet_requirev_state_t -{ - time_t start; -} packet_requirev_state_t; - -typedef struct kmdhgGPsha1kex_state_t -{ - libssh2_nonblocking_states state; - unsigned char *e_packet; - unsigned char *s_packet; - unsigned char *tmp; - unsigned char h_sig_comp[SHA_DIGEST_LENGTH]; - unsigned char c; - size_t e_packet_len; - size_t s_packet_len; - size_t tmp_len; - _libssh2_bn_ctx *ctx; - _libssh2_bn *x; - _libssh2_bn *e; - _libssh2_bn *f; - _libssh2_bn *k; - unsigned char *s; - unsigned char *f_value; - unsigned char *k_value; - unsigned char *h_sig; - size_t f_value_len; - size_t k_value_len; - size_t h_sig_len; - libssh2_sha1_ctx exchange_hash; - packet_require_state_t req_state; - libssh2_nonblocking_states burn_state; -} kmdhgGPsha1kex_state_t; - -typedef struct key_exchange_state_low_t -{ - libssh2_nonblocking_states state; - packet_require_state_t req_state; - kmdhgGPsha1kex_state_t exchange_state; - _libssh2_bn *p; /* SSH2 defined value (p_value) */ - _libssh2_bn *g; /* SSH2 defined value (2) */ - unsigned char request[13]; - unsigned char *data; - size_t request_len; - size_t data_len; -} key_exchange_state_low_t; - -typedef struct key_exchange_state_t -{ - libssh2_nonblocking_states state; - packet_require_state_t req_state; - key_exchange_state_low_t key_state_low; - unsigned char *data; - size_t data_len; - unsigned char *oldlocal; - size_t oldlocal_len; -} key_exchange_state_t; - -#define FwdNotReq "Forward not requested" - -typedef struct packet_queue_listener_state_t -{ - libssh2_nonblocking_states state; - unsigned char packet[17 + (sizeof(FwdNotReq) - 1)]; - unsigned char *host; - unsigned char *shost; - uint32_t sender_channel; - uint32_t initial_window_size; - uint32_t packet_size; - uint32_t port; - uint32_t sport; - uint32_t host_len; - uint32_t shost_len; - LIBSSH2_CHANNEL *channel; -} packet_queue_listener_state_t; - -#define X11FwdUnAvil "X11 Forward Unavailable" - -typedef struct packet_x11_open_state_t -{ - libssh2_nonblocking_states state; - unsigned char packet[17 + (sizeof(X11FwdUnAvil) - 1)]; - unsigned char *shost; - uint32_t sender_channel; - uint32_t initial_window_size; - uint32_t packet_size; - uint32_t sport; - uint32_t shost_len; - LIBSSH2_CHANNEL *channel; -} packet_x11_open_state_t; - -struct _LIBSSH2_PACKET -{ - struct list_node node; /* linked list header */ - - /* the raw unencrypted payload */ - unsigned char *data; - size_t data_len; - - /* Where to start reading data from, - * used for channel data that's been partially consumed */ - size_t data_head; -}; - -typedef struct _libssh2_channel_data -{ - /* Identifier */ - uint32_t id; - - /* Limits and restrictions */ - uint32_t window_size_initial, window_size, packet_size; - - /* Set to 1 when CHANNEL_CLOSE / CHANNEL_EOF sent/received */ - char close, eof, extended_data_ignore_mode; -} libssh2_channel_data; - -struct _LIBSSH2_CHANNEL -{ - struct list_node node; - - unsigned char *channel_type; - unsigned channel_type_len; - - /* channel's program exit status */ - int exit_status; - - /* channel's program exit signal (without the SIG prefix) */ - char *exit_signal; - - libssh2_channel_data local, remote; - /* Amount of bytes to be refunded to receive window (but not yet sent) */ - uint32_t adjust_queue; - - LIBSSH2_SESSION *session; - - void *abstract; - LIBSSH2_CHANNEL_CLOSE_FUNC((*close_cb)); - - /* State variables used in libssh2_channel_setenv_ex() */ - libssh2_nonblocking_states setenv_state; - unsigned char *setenv_packet; - size_t setenv_packet_len; - unsigned char setenv_local_channel[4]; - packet_requirev_state_t setenv_packet_requirev_state; - - /* State variables used in libssh2_channel_request_pty_ex() - libssh2_channel_request_pty_size_ex() */ - libssh2_nonblocking_states reqPTY_state; - unsigned char reqPTY_packet[41 + 256]; - size_t reqPTY_packet_len; - unsigned char reqPTY_local_channel[4]; - packet_requirev_state_t reqPTY_packet_requirev_state; - - /* State variables used in libssh2_channel_x11_req_ex() */ - libssh2_nonblocking_states reqX11_state; - unsigned char *reqX11_packet; - size_t reqX11_packet_len; - unsigned char reqX11_local_channel[4]; - packet_requirev_state_t reqX11_packet_requirev_state; - - /* State variables used in libssh2_channel_process_startup() */ - libssh2_nonblocking_states process_state; - unsigned char *process_packet; - size_t process_packet_len; - unsigned char process_local_channel[4]; - packet_requirev_state_t process_packet_requirev_state; - - /* State variables used in libssh2_channel_flush_ex() */ - libssh2_nonblocking_states flush_state; - size_t flush_refund_bytes; - size_t flush_flush_bytes; - - /* State variables used in libssh2_channel_receive_window_adjust() */ - libssh2_nonblocking_states adjust_state; - unsigned char adjust_adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */ - - /* State variables used in libssh2_channel_read_ex() */ - libssh2_nonblocking_states read_state; - - uint32_t read_local_id; - - /* State variables used in libssh2_channel_write_ex() */ - libssh2_nonblocking_states write_state; - unsigned char write_packet[13]; - size_t write_packet_len; - size_t write_bufwrite; - - /* State variables used in libssh2_channel_close() */ - libssh2_nonblocking_states close_state; - unsigned char close_packet[5]; - - /* State variables used in libssh2_channel_wait_closedeof() */ - libssh2_nonblocking_states wait_eof_state; - - /* State variables used in libssh2_channel_wait_closed() */ - libssh2_nonblocking_states wait_closed_state; - - /* State variables used in libssh2_channel_free() */ - libssh2_nonblocking_states free_state; - - /* State variables used in libssh2_channel_handle_extended_data2() */ - libssh2_nonblocking_states extData2_state; - -}; - -struct _LIBSSH2_LISTENER -{ - struct list_node node; /* linked list header */ - - LIBSSH2_SESSION *session; - - char *host; - int port; - - /* a list of CHANNELs for this listener */ - struct list_head queue; - - int queue_size; - int queue_maxsize; - - /* State variables used in libssh2_channel_forward_cancel() */ - libssh2_nonblocking_states chanFwdCncl_state; - unsigned char *chanFwdCncl_data; - size_t chanFwdCncl_data_len; -}; - -typedef struct _libssh2_endpoint_data -{ - unsigned char *banner; - - unsigned char *kexinit; - size_t kexinit_len; - - const LIBSSH2_CRYPT_METHOD *crypt; - void *crypt_abstract; - - const struct _LIBSSH2_MAC_METHOD *mac; - uint32_t seqno; - void *mac_abstract; - - const LIBSSH2_COMP_METHOD *comp; - void *comp_abstract; - - /* Method Preferences -- NULL yields "load order" */ - char *crypt_prefs; - char *mac_prefs; - char *comp_prefs; - char *lang_prefs; -} libssh2_endpoint_data; - -#define PACKETBUFSIZE (1024*16) - -struct transportpacket -{ - /* ------------- for incoming data --------------- */ - unsigned char buf[PACKETBUFSIZE]; - unsigned char init[5]; /* first 5 bytes of the incoming data stream, - still encrypted */ - size_t writeidx; /* at what array index we do the next write into - the buffer */ - size_t readidx; /* at what array index we do the next read from - the buffer */ - uint32_t packet_length; /* the most recent packet_length as read from the - network data */ - uint8_t padding_length; /* the most recent padding_length as read from the - network data */ - size_t data_num; /* How much of the total package that has been read - so far. */ - size_t total_num; /* How much a total package is supposed to be, in - number of bytes. A full package is - packet_length + padding_length + 4 + - mac_length. */ - unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC() - area to which we write decrypted data */ - unsigned char *wptr; /* write pointer into the payload to where we - are currently writing decrypted data */ - - /* ------------- for outgoing data --------------- */ - unsigned char outbuf[MAX_SSH_PACKET_LEN]; /* area for the outgoing data */ - - int ototal_num; /* size of outbuf in number of bytes */ - const unsigned char *odata; /* original pointer to the data */ - size_t olen; /* original size of the data we stored in - outbuf */ - size_t osent; /* number of bytes already sent */ -}; - -struct _LIBSSH2_PUBLICKEY -{ - LIBSSH2_CHANNEL *channel; - uint32_t version; - - /* State variables used in libssh2_publickey_packet_receive() */ - libssh2_nonblocking_states receive_state; - unsigned char *receive_packet; - size_t receive_packet_len; - - /* State variables used in libssh2_publickey_add_ex() */ - libssh2_nonblocking_states add_state; - unsigned char *add_packet; - unsigned char *add_s; - - /* State variables used in libssh2_publickey_remove_ex() */ - libssh2_nonblocking_states remove_state; - unsigned char *remove_packet; - unsigned char *remove_s; - - /* State variables used in libssh2_publickey_list_fetch() */ - libssh2_nonblocking_states listFetch_state; - unsigned char *listFetch_s; - unsigned char listFetch_buffer[12]; - unsigned char *listFetch_data; - size_t listFetch_data_len; -}; - -#define LIBSSH2_SCP_RESPONSE_BUFLEN 256 - -struct flags { - int sigpipe; /* LIBSSH2_FLAG_SIGPIPE */ - int compress; /* LIBSSH2_FLAG_COMPRESS */ -}; - -struct _LIBSSH2_SESSION -{ - /* Memory management callbacks */ - void *abstract; - LIBSSH2_ALLOC_FUNC((*alloc)); - LIBSSH2_REALLOC_FUNC((*realloc)); - LIBSSH2_FREE_FUNC((*free)); - - /* Other callbacks */ - LIBSSH2_IGNORE_FUNC((*ssh_msg_ignore)); - LIBSSH2_DEBUG_FUNC((*ssh_msg_debug)); - LIBSSH2_DISCONNECT_FUNC((*ssh_msg_disconnect)); - LIBSSH2_MACERROR_FUNC((*macerror)); - LIBSSH2_X11_OPEN_FUNC((*x11)); - LIBSSH2_SEND_FUNC((*send)); - LIBSSH2_RECV_FUNC((*recv)); - - /* Method preferences -- NULL yields "load order" */ - char *kex_prefs; - char *hostkey_prefs; - - int state; - - /* Flag options */ - struct flags flag; - - /* Agreed Key Exchange Method */ - const LIBSSH2_KEX_METHOD *kex; - int burn_optimistic_kexinit:1; - - unsigned char *session_id; - uint32_t session_id_len; - - /* this is set to TRUE if a blocking API behavior is requested */ - int api_block_mode; - - /* Timeout used when blocking API behavior is active */ - long api_timeout; - - /* Server's public key */ - const LIBSSH2_HOSTKEY_METHOD *hostkey; - void *server_hostkey_abstract; - - /* Either set with libssh2_session_hostkey() (for server mode) - * Or read from server in (eg) KEXDH_INIT (for client mode) - */ - unsigned char *server_hostkey; - uint32_t server_hostkey_len; -#if LIBSSH2_MD5 - unsigned char server_hostkey_md5[MD5_DIGEST_LENGTH]; -#endif /* ! LIBSSH2_MD5 */ - unsigned char server_hostkey_sha1[SHA_DIGEST_LENGTH]; - - /* (remote as source of data -- packet_read ) */ - libssh2_endpoint_data remote; - - /* (local as source of data -- packet_write ) */ - libssh2_endpoint_data local; - - /* Inbound Data linked list -- Sometimes the packet that comes in isn't the - packet we're ready for */ - struct list_head packets; - - /* Active connection channels */ - struct list_head channels; - - uint32_t next_channel; - - struct list_head listeners; /* list of LIBSSH2_LISTENER structs */ - - /* Actual I/O socket */ - libssh2_socket_t socket_fd; - int socket_state; - int socket_block_directions; - int socket_prev_blockstate; /* stores the state of the socket blockiness - when libssh2_session_startup() is called */ - - /* Error tracking */ - const char *err_msg; - int err_code; - - /* struct members for packet-level reading */ - struct transportpacket packet; -#ifdef LIBSSH2DEBUG - int showmask; /* what debug/trace messages to display */ - libssh2_trace_handler_func tracehandler; /* callback to display trace messages */ - void* tracehandler_context; /* context for the trace handler */ -#endif - - /* State variables used in libssh2_banner_send() */ - libssh2_nonblocking_states banner_TxRx_state; - char banner_TxRx_banner[256]; - ssize_t banner_TxRx_total_send; - - /* State variables used in libssh2_kexinit() */ - libssh2_nonblocking_states kexinit_state; - unsigned char *kexinit_data; - size_t kexinit_data_len; - - /* State variables used in libssh2_session_startup() */ - libssh2_nonblocking_states startup_state; - unsigned char *startup_data; - size_t startup_data_len; - unsigned char startup_service[sizeof("ssh-userauth") + 5 - 1]; - size_t startup_service_length; - packet_require_state_t startup_req_state; - key_exchange_state_t startup_key_state; - - /* State variables used in libssh2_session_free() */ - libssh2_nonblocking_states free_state; - - /* State variables used in libssh2_session_disconnect_ex() */ - libssh2_nonblocking_states disconnect_state; - unsigned char disconnect_data[256 + 13]; - size_t disconnect_data_len; - - /* State variables used in libssh2_packet_read() */ - libssh2_nonblocking_states readPack_state; - int readPack_encrypted; - - /* State variables used in libssh2_userauth_list() */ - libssh2_nonblocking_states userauth_list_state; - unsigned char *userauth_list_data; - size_t userauth_list_data_len; - packet_requirev_state_t userauth_list_packet_requirev_state; - - /* State variables used in libssh2_userauth_password_ex() */ - libssh2_nonblocking_states userauth_pswd_state; - unsigned char *userauth_pswd_data; - unsigned char userauth_pswd_data0; - size_t userauth_pswd_data_len; - char *userauth_pswd_newpw; - int userauth_pswd_newpw_len; - packet_requirev_state_t userauth_pswd_packet_requirev_state; - - /* State variables used in libssh2_userauth_hostbased_fromfile_ex() */ - libssh2_nonblocking_states userauth_host_state; - unsigned char *userauth_host_data; - size_t userauth_host_data_len; - unsigned char *userauth_host_packet; - size_t userauth_host_packet_len; - unsigned char *userauth_host_method; - size_t userauth_host_method_len; - unsigned char *userauth_host_s; - packet_requirev_state_t userauth_host_packet_requirev_state; - - /* State variables used in libssh2_userauth_publickey_fromfile_ex() */ - libssh2_nonblocking_states userauth_pblc_state; - unsigned char *userauth_pblc_data; - size_t userauth_pblc_data_len; - unsigned char *userauth_pblc_packet; - size_t userauth_pblc_packet_len; - unsigned char *userauth_pblc_method; - size_t userauth_pblc_method_len; - unsigned char *userauth_pblc_s; - unsigned char *userauth_pblc_b; - packet_requirev_state_t userauth_pblc_packet_requirev_state; - - /* State variables used in libssh2_userauth_keyboard_interactive_ex() */ - libssh2_nonblocking_states userauth_kybd_state; - unsigned char *userauth_kybd_data; - size_t userauth_kybd_data_len; - unsigned char *userauth_kybd_packet; - size_t userauth_kybd_packet_len; - unsigned int userauth_kybd_auth_name_len; - char *userauth_kybd_auth_name; - unsigned userauth_kybd_auth_instruction_len; - char *userauth_kybd_auth_instruction; - unsigned int userauth_kybd_num_prompts; - int userauth_kybd_auth_failure; - LIBSSH2_USERAUTH_KBDINT_PROMPT *userauth_kybd_prompts; - LIBSSH2_USERAUTH_KBDINT_RESPONSE *userauth_kybd_responses; - packet_requirev_state_t userauth_kybd_packet_requirev_state; - - /* State variables used in libssh2_channel_open_ex() */ - libssh2_nonblocking_states open_state; - packet_requirev_state_t open_packet_requirev_state; - LIBSSH2_CHANNEL *open_channel; - unsigned char *open_packet; - size_t open_packet_len; - unsigned char *open_data; - size_t open_data_len; - uint32_t open_local_channel; - - /* State variables used in libssh2_channel_direct_tcpip_ex() */ - libssh2_nonblocking_states direct_state; - unsigned char *direct_message; - size_t direct_host_len; - size_t direct_shost_len; - size_t direct_message_len; - - /* State variables used in libssh2_channel_forward_listen_ex() */ - libssh2_nonblocking_states fwdLstn_state; - unsigned char *fwdLstn_packet; - uint32_t fwdLstn_host_len; - uint32_t fwdLstn_packet_len; - packet_requirev_state_t fwdLstn_packet_requirev_state; - - /* State variables used in libssh2_publickey_init() */ - libssh2_nonblocking_states pkeyInit_state; - LIBSSH2_PUBLICKEY *pkeyInit_pkey; - LIBSSH2_CHANNEL *pkeyInit_channel; - unsigned char *pkeyInit_data; - size_t pkeyInit_data_len; - /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */ - unsigned char pkeyInit_buffer[19]; - size_t pkeyInit_buffer_sent; /* how much of buffer that has been sent */ - - /* State variables used in libssh2_packet_add() */ - libssh2_nonblocking_states packAdd_state; - LIBSSH2_CHANNEL *packAdd_channelp; /* keeper of the channel during EAGAIN - states */ - packet_queue_listener_state_t packAdd_Qlstn_state; - packet_x11_open_state_t packAdd_x11open_state; - - /* State variables used in fullpacket() */ - libssh2_nonblocking_states fullpacket_state; - int fullpacket_macstate; - size_t fullpacket_payload_len; - int fullpacket_packet_type; - - /* State variables used in libssh2_sftp_init() */ - libssh2_nonblocking_states sftpInit_state; - LIBSSH2_SFTP *sftpInit_sftp; - LIBSSH2_CHANNEL *sftpInit_channel; - unsigned char sftpInit_buffer[9]; /* sftp_header(5){excludes request_id} - + version_id(4) */ - int sftpInit_sent; /* number of bytes from the buffer that have been - sent */ - - /* State variables used in libssh2_scp_recv() */ - libssh2_nonblocking_states scpRecv_state; - unsigned char *scpRecv_command; - size_t scpRecv_command_len; - unsigned char scpRecv_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; - size_t scpRecv_response_len; - long scpRecv_mode; -#if defined(HAVE_LONGLONG) && defined(HAVE_STRTOLL) - /* we have the type and we can parse such numbers */ - long long scpRecv_size; -#define scpsize_strtol strtoll -#else - long scpRecv_size; -#define scpsize_strtol strtol -#endif - long scpRecv_mtime; - long scpRecv_atime; - LIBSSH2_CHANNEL *scpRecv_channel; - - /* State variables used in libssh2_scp_send_ex() */ - libssh2_nonblocking_states scpSend_state; - unsigned char *scpSend_command; - size_t scpSend_command_len; - unsigned char scpSend_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; - size_t scpSend_response_len; - LIBSSH2_CHANNEL *scpSend_channel; - - /* Keepalive variables used by keepalive.c. */ - int keepalive_interval; - int keepalive_want_reply; - time_t keepalive_last_sent; -}; - -/* session.state bits */ -#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001 -#define LIBSSH2_STATE_NEWKEYS 0x00000002 -#define LIBSSH2_STATE_AUTHENTICATED 0x00000004 -#define LIBSSH2_STATE_KEX_ACTIVE 0x00000008 - -/* session.flag helpers */ -#ifdef MSG_NOSIGNAL -#define LIBSSH2_SOCKET_SEND_FLAGS(session) \ - (((session)->flag.sigpipe) ? 0 : MSG_NOSIGNAL) -#define LIBSSH2_SOCKET_RECV_FLAGS(session) \ - (((session)->flag.sigpipe) ? 0 : MSG_NOSIGNAL) -#else -/* If MSG_NOSIGNAL isn't defined we're SOL on blocking SIGPIPE */ -#define LIBSSH2_SOCKET_SEND_FLAGS(session) 0 -#define LIBSSH2_SOCKET_RECV_FLAGS(session) 0 -#endif - -/* --------- */ - -/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional - methods via .so/.dll */ - -struct _LIBSSH2_KEX_METHOD -{ - const char *name; - - /* Key exchange, populates session->* and returns 0 on success, non-0 on error */ - int (*exchange_keys) (LIBSSH2_SESSION * session, - key_exchange_state_low_t * key_state); - - long flags; -}; - -struct _LIBSSH2_HOSTKEY_METHOD -{ - const char *name; - unsigned long hash_len; - - int (*init) (LIBSSH2_SESSION * session, const unsigned char *hostkey_data, - size_t hostkey_data_len, void **abstract); - int (*initPEM) (LIBSSH2_SESSION * session, const char *privkeyfile, - unsigned const char *passphrase, void **abstract); - int (*sig_verify) (LIBSSH2_SESSION * session, const unsigned char *sig, - size_t sig_len, const unsigned char *m, - size_t m_len, void **abstract); - int (*signv) (LIBSSH2_SESSION * session, unsigned char **signature, - size_t *signature_len, int veccount, - const struct iovec datavec[], void **abstract); - int (*encrypt) (LIBSSH2_SESSION * session, unsigned char **dst, - size_t *dst_len, const unsigned char *src, - size_t src_len, void **abstract); - int (*dtor) (LIBSSH2_SESSION * session, void **abstract); -}; - -struct _LIBSSH2_CRYPT_METHOD -{ - const char *name; - - int blocksize; - - /* iv and key sizes (-1 for variable length) */ - int iv_len; - int secret_len; - - long flags; - - int (*init) (LIBSSH2_SESSION * session, - const LIBSSH2_CRYPT_METHOD * method, unsigned char *iv, - int *free_iv, unsigned char *secret, int *free_secret, - int encrypt, void **abstract); - int (*crypt) (LIBSSH2_SESSION * session, unsigned char *block, - void **abstract); - int (*dtor) (LIBSSH2_SESSION * session, void **abstract); - - _libssh2_cipher_type(algo); -}; - -struct _LIBSSH2_COMP_METHOD -{ - const char *name; - int compress; /* 1 if it does compress, 0 if it doesn't */ - int (*init) (LIBSSH2_SESSION *session, int compress, void **abstract); - int (*comp) (LIBSSH2_SESSION *session, - unsigned char *dest, - size_t *dest_len, - const unsigned char *src, - size_t src_len, - void **abstract); - int (*decomp) (LIBSSH2_SESSION *session, - unsigned char **dest, - size_t *dest_len, - size_t payload_limit, - const unsigned char *src, - size_t src_len, - void **abstract); - int (*dtor) (LIBSSH2_SESSION * session, int compress, void **abstract); -}; - -#ifdef LIBSSH2DEBUG -void _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, - ...); -#else -#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__GNUC__) -/* C99 supported and also by older GCC */ -#define _libssh2_debug(x,y,z,...) do {} while (0) -#else -/* no gcc and not C99, do static and hopefully inline */ -static inline void -_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...) -{ -} -#endif -#endif - -#define LIBSSH2_SOCKET_UNKNOWN 1 -#define LIBSSH2_SOCKET_CONNECTED 0 -#define LIBSSH2_SOCKET_DISCONNECTED -1 - -/* Initial packet state, prior to MAC check */ -#define LIBSSH2_MAC_UNCONFIRMED 1 -/* When MAC type is "none" (proto initiation phase) all packets are deemed "confirmed" */ -#define LIBSSH2_MAC_CONFIRMED 0 -/* Something very bad is going on */ -#define LIBSSH2_MAC_INVALID -1 - -/* SSH Packet Types -- Defined by internet draft */ -/* Transport Layer */ -#define SSH_MSG_DISCONNECT 1 -#define SSH_MSG_IGNORE 2 -#define SSH_MSG_UNIMPLEMENTED 3 -#define SSH_MSG_DEBUG 4 -#define SSH_MSG_SERVICE_REQUEST 5 -#define SSH_MSG_SERVICE_ACCEPT 6 - -#define SSH_MSG_KEXINIT 20 -#define SSH_MSG_NEWKEYS 21 - -/* diffie-hellman-group1-sha1 */ -#define SSH_MSG_KEXDH_INIT 30 -#define SSH_MSG_KEXDH_REPLY 31 - -/* diffie-hellman-group-exchange-sha1 */ -#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD 30 -#define SSH_MSG_KEX_DH_GEX_REQUEST 34 -#define SSH_MSG_KEX_DH_GEX_GROUP 31 -#define SSH_MSG_KEX_DH_GEX_INIT 32 -#define SSH_MSG_KEX_DH_GEX_REPLY 33 - -/* User Authentication */ -#define SSH_MSG_USERAUTH_REQUEST 50 -#define SSH_MSG_USERAUTH_FAILURE 51 -#define SSH_MSG_USERAUTH_SUCCESS 52 -#define SSH_MSG_USERAUTH_BANNER 53 - -/* "public key" method */ -#define SSH_MSG_USERAUTH_PK_OK 60 -/* "password" method */ -#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60 -/* "keyboard-interactive" method */ -#define SSH_MSG_USERAUTH_INFO_REQUEST 60 -#define SSH_MSG_USERAUTH_INFO_RESPONSE 61 - -/* Channels */ -#define SSH_MSG_GLOBAL_REQUEST 80 -#define SSH_MSG_REQUEST_SUCCESS 81 -#define SSH_MSG_REQUEST_FAILURE 82 - -#define SSH_MSG_CHANNEL_OPEN 90 -#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91 -#define SSH_MSG_CHANNEL_OPEN_FAILURE 92 -#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93 -#define SSH_MSG_CHANNEL_DATA 94 -#define SSH_MSG_CHANNEL_EXTENDED_DATA 95 -#define SSH_MSG_CHANNEL_EOF 96 -#define SSH_MSG_CHANNEL_CLOSE 97 -#define SSH_MSG_CHANNEL_REQUEST 98 -#define SSH_MSG_CHANNEL_SUCCESS 99 -#define SSH_MSG_CHANNEL_FAILURE 100 - -/* Error codes returned in SSH_MSG_CHANNEL_OPEN_FAILURE message - (see RFC4254) */ -#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1 -#define SSH_OPEN_CONNECT_FAILED 2 -#define SSH_OPEN_UNKNOWN_CHANNELTYPE 3 -#define SSH_OPEN_RESOURCE_SHORTAGE 4 - -ssize_t _libssh2_recv(libssh2_socket_t socket, void *buffer, - size_t length, int flags, void **abstract); -ssize_t _libssh2_send(libssh2_socket_t socket, const void *buffer, - size_t length, int flags, void **abstract); - -#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when - waiting for more data to arrive */ - - -int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange, - key_exchange_state_t * state); - -/* Let crypt.c/hostkey.c expose their method structs */ -const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void); -const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void); - -/* pem.c */ -int _libssh2_pem_parse(LIBSSH2_SESSION * session, - const char *headerbegin, - const char *headerend, - FILE * fp, unsigned char **data, unsigned int *datalen); -int _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen); -int _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen, - unsigned char **i, unsigned int *ilen); - -/* global.c */ -void _libssh2_init_if_needed (void); - - -#define ARRAY_SIZE(a) (sizeof ((a)) / sizeof ((a)[0])) - -/* define to output the libssh2_int64_t type in a *printf() */ -#if defined( __BORLANDC__ ) || defined( _MSC_VER ) || defined( __MINGW32__ ) -#define LIBSSH2_INT64_T_FORMAT "I64" -#else -#define LIBSSH2_INT64_T_FORMAT "ll" -#endif - -#endif /* LIBSSH2_H */ diff --git a/vendor/libssh2-1.4.2/src/mac.c b/vendor/libssh2-1.4.2/src/mac.c deleted file mode 100644 index 76894fc58..000000000 --- a/vendor/libssh2-1.4.2/src/mac.c +++ /dev/null @@ -1,314 +0,0 @@ -/* Copyright (c) 2004-2007, Sara Golemon - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include "mac.h" - -#ifdef LIBSSH2_MAC_NONE -/* mac_none_MAC - * Minimalist MAC: No MAC - */ -static int -mac_none_MAC(LIBSSH2_SESSION * session, unsigned char *buf, - uint32_t seqno, const unsigned char *packet, - uint32_t packet_len, const unsigned char *addtl, - uint32_t addtl_len, void **abstract) -{ - return 0; -} - - - - -static LIBSSH2_MAC_METHOD mac_method_none = { - "none", - 0, - 0, - NULL, - mac_none_MAC, - NULL -}; -#endif /* LIBSSH2_MAC_NONE */ - -/* mac_method_common_init - * Initialize simple mac methods - */ -static int -mac_method_common_init(LIBSSH2_SESSION * session, unsigned char *key, - int *free_key, void **abstract) -{ - *abstract = key; - *free_key = 0; - (void) session; - - return 0; -} - - - -/* mac_method_common_dtor - * Cleanup simple mac methods - */ -static int -mac_method_common_dtor(LIBSSH2_SESSION * session, void **abstract) -{ - if (*abstract) { - LIBSSH2_FREE(session, *abstract); - } - *abstract = NULL; - - return 0; -} - - - -/* mac_method_hmac_sha1_hash - * Calculate hash using full sha1 value - */ -static int -mac_method_hmac_sha1_hash(LIBSSH2_SESSION * session, - unsigned char *buf, uint32_t seqno, - const unsigned char *packet, - uint32_t packet_len, - const unsigned char *addtl, - uint32_t addtl_len, void **abstract) -{ - libssh2_hmac_ctx ctx; - unsigned char seqno_buf[4]; - (void) session; - - _libssh2_htonu32(seqno_buf, seqno); - - libssh2_hmac_sha1_init(&ctx, *abstract, 20); - libssh2_hmac_update(ctx, seqno_buf, 4); - libssh2_hmac_update(ctx, packet, packet_len); - if (addtl && addtl_len) { - libssh2_hmac_update(ctx, addtl, addtl_len); - } - libssh2_hmac_final(ctx, buf); - libssh2_hmac_cleanup(&ctx); - - return 0; -} - - - -static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1 = { - "hmac-sha1", - 20, - 20, - mac_method_common_init, - mac_method_hmac_sha1_hash, - mac_method_common_dtor, -}; - -/* mac_method_hmac_sha1_96_hash - * Calculate hash using first 96 bits of sha1 value - */ -static int -mac_method_hmac_sha1_96_hash(LIBSSH2_SESSION * session, - unsigned char *buf, uint32_t seqno, - const unsigned char *packet, - uint32_t packet_len, - const unsigned char *addtl, - uint32_t addtl_len, void **abstract) -{ - unsigned char temp[SHA_DIGEST_LENGTH]; - - mac_method_hmac_sha1_hash(session, temp, seqno, packet, packet_len, - addtl, addtl_len, abstract); - memcpy(buf, (char *) temp, 96 / 8); - - return 0; -} - - - -static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1_96 = { - "hmac-sha1-96", - 12, - 20, - mac_method_common_init, - mac_method_hmac_sha1_96_hash, - mac_method_common_dtor, -}; - -#if LIBSSH2_MD5 -/* mac_method_hmac_md5_hash - * Calculate hash using full md5 value - */ -static int -mac_method_hmac_md5_hash(LIBSSH2_SESSION * session, unsigned char *buf, - uint32_t seqno, - const unsigned char *packet, - uint32_t packet_len, - const unsigned char *addtl, - uint32_t addtl_len, void **abstract) -{ - libssh2_hmac_ctx ctx; - unsigned char seqno_buf[4]; - (void) session; - - _libssh2_htonu32(seqno_buf, seqno); - - libssh2_hmac_md5_init(&ctx, *abstract, 16); - libssh2_hmac_update(ctx, seqno_buf, 4); - libssh2_hmac_update(ctx, packet, packet_len); - if (addtl && addtl_len) { - libssh2_hmac_update(ctx, addtl, addtl_len); - } - libssh2_hmac_final(ctx, buf); - libssh2_hmac_cleanup(&ctx); - - return 0; -} - - - -static const LIBSSH2_MAC_METHOD mac_method_hmac_md5 = { - "hmac-md5", - 16, - 16, - mac_method_common_init, - mac_method_hmac_md5_hash, - mac_method_common_dtor, -}; - -/* mac_method_hmac_md5_96_hash - * Calculate hash using first 96 bits of md5 value - */ -static int -mac_method_hmac_md5_96_hash(LIBSSH2_SESSION * session, - unsigned char *buf, uint32_t seqno, - const unsigned char *packet, - uint32_t packet_len, - const unsigned char *addtl, - uint32_t addtl_len, void **abstract) -{ - unsigned char temp[MD5_DIGEST_LENGTH]; - mac_method_hmac_md5_hash(session, temp, seqno, packet, packet_len, - addtl, addtl_len, abstract); - memcpy(buf, (char *) temp, 96 / 8); - return 0; -} - - - -static const LIBSSH2_MAC_METHOD mac_method_hmac_md5_96 = { - "hmac-md5-96", - 12, - 16, - mac_method_common_init, - mac_method_hmac_md5_96_hash, - mac_method_common_dtor, -}; -#endif /* LIBSSH2_MD5 */ - -#if LIBSSH2_HMAC_RIPEMD -/* mac_method_hmac_ripemd160_hash - * Calculate hash using ripemd160 value - */ -static int -mac_method_hmac_ripemd160_hash(LIBSSH2_SESSION * session, - unsigned char *buf, uint32_t seqno, - const unsigned char *packet, - uint32_t packet_len, - const unsigned char *addtl, - uint32_t addtl_len, - void **abstract) -{ - libssh2_hmac_ctx ctx; - unsigned char seqno_buf[4]; - (void) session; - - _libssh2_htonu32(seqno_buf, seqno); - - libssh2_hmac_ripemd160_init(&ctx, *abstract, 20); - libssh2_hmac_update(ctx, seqno_buf, 4); - libssh2_hmac_update(ctx, packet, packet_len); - if (addtl && addtl_len) { - libssh2_hmac_update(ctx, addtl, addtl_len); - } - libssh2_hmac_final(ctx, buf); - libssh2_hmac_cleanup(&ctx); - - return 0; -} - - - -static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160 = { - "hmac-ripemd160", - 20, - 20, - mac_method_common_init, - mac_method_hmac_ripemd160_hash, - mac_method_common_dtor, -}; - -static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160_openssh_com = { - "hmac-ripemd160@openssh.com", - 20, - 20, - mac_method_common_init, - mac_method_hmac_ripemd160_hash, - mac_method_common_dtor, -}; -#endif /* LIBSSH2_HMAC_RIPEMD */ - -static const LIBSSH2_MAC_METHOD *mac_methods[] = { - &mac_method_hmac_sha1, - &mac_method_hmac_sha1_96, -#if LIBSSH2_MD5 - &mac_method_hmac_md5, - &mac_method_hmac_md5_96, -#endif -#if LIBSSH2_HMAC_RIPEMD - &mac_method_hmac_ripemd160, - &mac_method_hmac_ripemd160_openssh_com, -#endif /* LIBSSH2_HMAC_RIPEMD */ -#ifdef LIBSSH2_MAC_NONE - &mac_method_none, -#endif /* LIBSSH2_MAC_NONE */ - NULL -}; - -const LIBSSH2_MAC_METHOD ** -_libssh2_mac_methods(void) -{ - return mac_methods; -} diff --git a/vendor/libssh2-1.4.2/src/mac.h b/vendor/libssh2-1.4.2/src/mac.h deleted file mode 100644 index 66d3e6101..000000000 --- a/vendor/libssh2-1.4.2/src/mac.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef __LIBSSH2_MAC_H -#define __LIBSSH2_MAC_H - -/* Copyright (C) 2009-2010 by Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - */ - -#include "libssh2_priv.h" - -struct _LIBSSH2_MAC_METHOD -{ - const char *name; - - /* The length of a given MAC packet */ - int mac_len; - - /* integrity key length */ - int key_len; - - /* Message Authentication Code Hashing algo */ - int (*init) (LIBSSH2_SESSION * session, unsigned char *key, int *free_key, - void **abstract); - int (*hash) (LIBSSH2_SESSION * session, unsigned char *buf, - uint32_t seqno, const unsigned char *packet, - uint32_t packet_len, const unsigned char *addtl, - uint32_t addtl_len, void **abstract); - int (*dtor) (LIBSSH2_SESSION * session, void **abstract); -}; - -typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD; - -const LIBSSH2_MAC_METHOD **_libssh2_mac_methods(void); - -#endif /* __LIBSSH2_MAC_H */ diff --git a/vendor/libssh2-1.4.2/src/misc.c b/vendor/libssh2-1.4.2/src/misc.c deleted file mode 100644 index a9f423a69..000000000 --- a/vendor/libssh2-1.4.2/src/misc.c +++ /dev/null @@ -1,612 +0,0 @@ -/* Copyright (c) 2004-2007 Sara Golemon - * Copyright (c) 2009-2010 by Daniel Stenberg - * Copyright (c) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include "misc.h" - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#include -#include - -int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char* errmsg) -{ - session->err_msg = errmsg; - session->err_code = errcode; -#ifdef LIBSSH2DEBUG - if((errcode == LIBSSH2_ERROR_EAGAIN) && !session->api_block_mode) - /* if this is EAGAIN and we're in non-blocking mode, don't generate - a debug output for this */ - return errcode; - _libssh2_debug(session, LIBSSH2_TRACE_ERROR, "%d - %s", session->err_code, - session->err_msg); -#endif - - return errcode; -} - -#ifdef WIN32 -static int wsa2errno(void) -{ - switch (WSAGetLastError()) { - case WSAEWOULDBLOCK: - return EAGAIN; - - case WSAENOTSOCK: - return EBADF; - - case WSAEINTR: - return EINTR; - - default: - /* It is most important to ensure errno does not stay at EAGAIN - * when a different error occurs so just set errno to a generic - * error */ - return EIO; - } -} -#endif - -/* _libssh2_recv - * - * Replacement for the standard recv, return -errno on failure. - */ -ssize_t -_libssh2_recv(libssh2_socket_t sock, void *buffer, size_t length, int flags, void **abstract) -{ - ssize_t rc = recv(sock, buffer, length, flags); -#ifdef WIN32 - if (rc < 0 ) - return -wsa2errno(); -#elif defined(__VMS) - if (rc < 0 ){ - if ( errno == EWOULDBLOCK ) - return -EAGAIN; - else - return -errno; - } -#else - if (rc < 0 ){ - /* Sometimes the first recv() function call sets errno to ENOENT on - Solaris and HP-UX */ - if ( errno == ENOENT ) - return -EAGAIN; - else - return -errno; - } -#endif - return rc; -} - -/* _libssh2_send - * - * Replacement for the standard send, return -errno on failure. - */ -ssize_t -_libssh2_send(libssh2_socket_t sock, const void *buffer, size_t length, - int flags, void **abstract) -{ - ssize_t rc = send(sock, buffer, length, flags); -#ifdef WIN32 - if (rc < 0 ) - return -wsa2errno(); -#elif defined(__VMS) - if (rc < 0 ) { - if ( errno == EWOULDBLOCK ) - return -EAGAIN; - else - return -errno; - } -#else - if (rc < 0 ) - return -errno; -#endif - return rc; -} - -/* libssh2_ntohu32 - */ -unsigned int -_libssh2_ntohu32(const unsigned char *buf) -{ - return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; -} - - -/* _libssh2_ntohu64 - */ -libssh2_uint64_t -_libssh2_ntohu64(const unsigned char *buf) -{ - unsigned long msl, lsl; - - msl = ((libssh2_uint64_t)buf[0] << 24) | ((libssh2_uint64_t)buf[1] << 16) - | ((libssh2_uint64_t)buf[2] << 8) | (libssh2_uint64_t)buf[3]; - lsl = ((libssh2_uint64_t)buf[4] << 24) | ((libssh2_uint64_t)buf[5] << 16) - | ((libssh2_uint64_t)buf[6] << 8) | (libssh2_uint64_t)buf[7]; - - return ((libssh2_uint64_t)msl <<32) | lsl; -} - -/* _libssh2_htonu32 - */ -void -_libssh2_htonu32(unsigned char *buf, uint32_t value) -{ - buf[0] = (value >> 24) & 0xFF; - buf[1] = (value >> 16) & 0xFF; - buf[2] = (value >> 8) & 0xFF; - buf[3] = value & 0xFF; -} - -/* _libssh2_store_u32 - */ -void _libssh2_store_u32(unsigned char **buf, uint32_t value) -{ - _libssh2_htonu32(*buf, value); - *buf += sizeof(uint32_t); -} - -/* _libssh2_store_str - */ -void _libssh2_store_str(unsigned char **buf, const char *str, size_t len) -{ - _libssh2_store_u32(buf, (uint32_t)len); - if(len) { - memcpy(*buf, str, len); - *buf += len; - } -} - -/* Base64 Conversion */ - -static const char base64_table[] = -{ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' -}; - -static const char base64_pad = '='; - -static const short base64_reverse_table[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; - -/* libssh2_base64_decode - * - * Decode a base64 chunk and store it into a newly alloc'd buffer - */ -LIBSSH2_API int -libssh2_base64_decode(LIBSSH2_SESSION *session, char **data, - unsigned int *datalen, const char *src, - unsigned int src_len) -{ - unsigned char *s, *d; - short v; - int i = 0, len = 0; - - *data = LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1); - d = (unsigned char *) *data; - if (!d) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for base64 decoding"); - } - - for(s = (unsigned char *) src; ((char *) s) < (src + src_len); s++) { - if ((v = base64_reverse_table[*s]) < 0) - continue; - switch (i % 4) { - case 0: - d[len] = v << 2; - break; - case 1: - d[len++] |= v >> 4; - d[len] = v << 4; - break; - case 2: - d[len++] |= v >> 2; - d[len] = v << 6; - break; - case 3: - d[len++] |= v; - break; - } - i++; - } - if ((i % 4) == 1) { - /* Invalid -- We have a byte which belongs exclusively to a partial - octet */ - LIBSSH2_FREE(session, *data); - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid base64"); - } - - *datalen = len; - return 0; -} - -/* ---- Base64 Encoding/Decoding Table --- */ -static const char table64[]= - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -/* - * _libssh2_base64_encode() - * - * Returns the length of the newly created base64 string. The third argument - * is a pointer to an allocated area holding the base64 data. If something - * went wrong, 0 is returned. - * - */ -size_t _libssh2_base64_encode(LIBSSH2_SESSION *session, - const char *inp, size_t insize, char **outptr) -{ - unsigned char ibuf[3]; - unsigned char obuf[4]; - int i; - int inputparts; - char *output; - char *base64data; - const char *indata = inp; - - *outptr = NULL; /* set to NULL in case of failure before we reach the end */ - - if(0 == insize) - insize = strlen(indata); - - base64data = output = LIBSSH2_ALLOC(session, insize*4/3+4); - if(NULL == output) - return 0; - - while(insize > 0) { - for (i = inputparts = 0; i < 3; i++) { - if(insize > 0) { - inputparts++; - ibuf[i] = *indata; - indata++; - insize--; - } - else - ibuf[i] = 0; - } - - obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2); - obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \ - ((ibuf[1] & 0xF0) >> 4)); - obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \ - ((ibuf[2] & 0xC0) >> 6)); - obuf[3] = (unsigned char) (ibuf[2] & 0x3F); - - switch(inputparts) { - case 1: /* only one byte read */ - snprintf(output, 5, "%c%c==", - table64[obuf[0]], - table64[obuf[1]]); - break; - case 2: /* two bytes read */ - snprintf(output, 5, "%c%c%c=", - table64[obuf[0]], - table64[obuf[1]], - table64[obuf[2]]); - break; - default: - snprintf(output, 5, "%c%c%c%c", - table64[obuf[0]], - table64[obuf[1]], - table64[obuf[2]], - table64[obuf[3]] ); - break; - } - output += 4; - } - *output=0; - *outptr = base64data; /* make it return the actual data memory */ - - return strlen(base64data); /* return the length of the new data */ -} -/* ---- End of Base64 Encoding ---- */ - -LIBSSH2_API void -libssh2_free(LIBSSH2_SESSION *session, void *ptr) -{ - LIBSSH2_FREE(session, ptr); -} - -#ifdef LIBSSH2DEBUG -LIBSSH2_API int -libssh2_trace(LIBSSH2_SESSION * session, int bitmask) -{ - session->showmask = bitmask; - return 0; -} - -LIBSSH2_API int -libssh2_trace_sethandler(LIBSSH2_SESSION *session, void* handler_context, - libssh2_trace_handler_func callback) -{ - session->tracehandler = callback; - session->tracehandler_context = handler_context; - return 0; -} - -void -_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...) -{ - char buffer[1536]; - int len, msglen, buflen = sizeof(buffer); - va_list vargs; - struct timeval now; - static int firstsec; - static const char *const contexts[] = { - "Unknown", - "Transport", - "Key Ex", - "Userauth", - "Conn", - "SCP", - "SFTP", - "Failure Event", - "Publickey", - "Socket", - }; - const char* contexttext = contexts[0]; - unsigned int contextindex; - - if (!(session->showmask & context)) { - /* no such output asked for */ - return; - } - - /* Find the first matching context string for this message */ - for (contextindex = 0; contextindex < ARRAY_SIZE(contexts); - contextindex++) { - if ((context & (1 << contextindex)) != 0) { - contexttext = contexts[contextindex]; - break; - } - } - - _libssh2_gettimeofday(&now, NULL); - if(!firstsec) { - firstsec = now.tv_sec; - } - now.tv_sec -= firstsec; - - len = snprintf(buffer, buflen, "[libssh2] %d.%06d %s: ", - (int)now.tv_sec, (int)now.tv_usec, contexttext); - - if (len >= buflen) - msglen = buflen - 1; - else { - buflen -= len; - msglen = len; - va_start(vargs, format); - len = vsnprintf(buffer + msglen, buflen, format, vargs); - va_end(vargs); - msglen += len < buflen ? len : buflen - 1; - } - - if (session->tracehandler) - (session->tracehandler)(session, session->tracehandler_context, buffer, - msglen); - else - fprintf(stderr, "%s\n", buffer); -} - -#else -LIBSSH2_API int -libssh2_trace(LIBSSH2_SESSION * session, int bitmask) -{ - (void) session; - (void) bitmask; - return 0; -} - -LIBSSH2_API int -libssh2_trace_sethandler(LIBSSH2_SESSION *session, void* handler_context, - libssh2_trace_handler_func callback) -{ - (void) session; - (void) handler_context; - (void) callback; - return 0; -} -#endif - -/* init the list head */ -void _libssh2_list_init(struct list_head *head) -{ - head->first = head->last = NULL; -} - -/* add a node to the list */ -void _libssh2_list_add(struct list_head *head, - struct list_node *entry) -{ - /* store a pointer to the head */ - entry->head = head; - - /* we add this entry at the "top" so it has no next */ - entry->next = NULL; - - /* make our prev point to what the head thinks is last */ - entry->prev = head->last; - - /* and make head's last be us now */ - head->last = entry; - - /* make sure our 'prev' node points to us next */ - if(entry->prev) - entry->prev->next = entry; - else - head->first = entry; -} - -/* return the "first" node in the list this head points to */ -void *_libssh2_list_first(struct list_head *head) -{ - return head->first; -} - -/* return the next node in the list */ -void *_libssh2_list_next(struct list_node *node) -{ - return node->next; -} - -/* return the prev node in the list */ -void *_libssh2_list_prev(struct list_node *node) -{ - return node->prev; -} - -/* remove this node from the list */ -void _libssh2_list_remove(struct list_node *entry) -{ - if(entry->prev) - entry->prev->next = entry->next; - else - entry->head->first = entry->next; - - if(entry->next) - entry->next->prev = entry->prev; - else - entry->head->last = entry->prev; -} - -#if 0 -/* insert a node before the given 'after' entry */ -void _libssh2_list_insert(struct list_node *after, /* insert before this */ - struct list_node *entry) -{ - /* 'after' is next to 'entry' */ - bentry->next = after; - - /* entry's prev is then made to be the prev after current has */ - entry->prev = after->prev; - - /* the node that is now before 'entry' was previously before 'after' - and must be made to point to 'entry' correctly */ - if(entry->prev) - entry->prev->next = entry; - else - /* there was no node before this, so we make sure we point the head - pointer to this node */ - after->head->first = entry; - - /* after's prev entry points back to entry */ - after->prev = entry; - - /* after's next entry is still the same as before */ - - /* entry's head is the same as after's */ - entry->head = after->head; -} - -#endif - -/* this define is defined in misc.h for the correct platforms */ -#ifdef LIBSSH2_GETTIMEOFDAY_WIN32 -/* - * gettimeofday - * Implementation according to: - * The Open Group Base Specifications Issue 6 - * IEEE Std 1003.1, 2004 Edition - */ - -/* - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * Contributed by: - * Danny Smith - */ - -/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */ -#define _W32_FT_OFFSET (116444736000000000) - -int __cdecl _libssh2_gettimeofday(struct timeval *tp, void *tzp) - { - union { - unsigned __int64 ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } _now; - - if(tp) - { - GetSystemTimeAsFileTime (&_now.ft); - tp->tv_usec=(long)((_now.ns100 / 10) % 1000000 ); - tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000); - } - /* Always return 0 as per Open Group Base Specifications Issue 6. - Do not set errno on error. */ - return 0; -} - - -#endif diff --git a/vendor/libssh2-1.4.2/src/misc.h b/vendor/libssh2-1.4.2/src/misc.h deleted file mode 100644 index 8a8294afb..000000000 --- a/vendor/libssh2-1.4.2/src/misc.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef __LIBSSH2_MISC_H -#define __LIBSSH2_MISC_H -/* Copyright (c) 2009-2011 by Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -struct list_head { - struct list_node *last; - struct list_node *first; -}; - -struct list_node { - struct list_node *next; - struct list_node *prev; - struct list_head *head; -}; - -int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char* errmsg); - -void _libssh2_list_init(struct list_head *head); - -/* add a node last in the list */ -void _libssh2_list_add(struct list_head *head, - struct list_node *entry); - -/* return the "first" node in the list this head points to */ -void *_libssh2_list_first(struct list_head *head); - -/* return the next node in the list */ -void *_libssh2_list_next(struct list_node *node); - -/* return the prev node in the list */ -void *_libssh2_list_prev(struct list_node *node); - -/* remove this node from the list */ -void _libssh2_list_remove(struct list_node *entry); - -size_t _libssh2_base64_encode(struct _LIBSSH2_SESSION *session, - const char *inp, size_t insize, char **outptr); - -unsigned int _libssh2_ntohu32(const unsigned char *buf); -libssh2_uint64_t _libssh2_ntohu64(const unsigned char *buf); -void _libssh2_htonu32(unsigned char *buf, uint32_t val); -void _libssh2_store_u32(unsigned char **buf, uint32_t value); -void _libssh2_store_str(unsigned char **buf, const char *str, size_t len); - -#if defined(WIN32) || defined(LIBSSH2_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) -/* provide a private one */ -#undef HAVE_GETTIMEOFDAY -int __cdecl _libssh2_gettimeofday(struct timeval *tp, void *tzp); -#define HAVE_LIBSSH2_GETTIMEOFDAY -#define LIBSSH2_GETTIMEOFDAY_WIN32 /* enable the win32 implementation */ -#else -#ifdef HAVE_GETTIMEOFDAY -#define _libssh2_gettimeofday(x,y) gettimeofday(x,y) -#define HAVE_LIBSSH2_GETTIMEOFDAY -#endif -#endif - -#endif /* _LIBSSH2_MISC_H */ diff --git a/vendor/libssh2-1.4.2/src/openssl.c b/vendor/libssh2-1.4.2/src/openssl.c deleted file mode 100644 index 481982cb8..000000000 --- a/vendor/libssh2-1.4.2/src/openssl.c +++ /dev/null @@ -1,804 +0,0 @@ -/* Copyright (C) 2009, 2010 Simon Josefsson - * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. - * Copyright (c) 2004-2006, Sara Golemon - * - * Author: Simon Josefsson - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -#ifndef LIBSSH2_LIBGCRYPT /* compile only if we build with OpenSSL */ - -#include - -#ifndef EVP_MAX_BLOCK_LENGTH -#define EVP_MAX_BLOCK_LENGTH 32 -#endif - -int -_libssh2_rsa_new(libssh2_rsa_ctx ** rsa, - const unsigned char *edata, - unsigned long elen, - const unsigned char *ndata, - unsigned long nlen, - const unsigned char *ddata, - unsigned long dlen, - const unsigned char *pdata, - unsigned long plen, - const unsigned char *qdata, - unsigned long qlen, - const unsigned char *e1data, - unsigned long e1len, - const unsigned char *e2data, - unsigned long e2len, - const unsigned char *coeffdata, unsigned long coefflen) -{ - *rsa = RSA_new(); - - (*rsa)->e = BN_new(); - BN_bin2bn(edata, elen, (*rsa)->e); - - (*rsa)->n = BN_new(); - BN_bin2bn(ndata, nlen, (*rsa)->n); - - if (ddata) { - (*rsa)->d = BN_new(); - BN_bin2bn(ddata, dlen, (*rsa)->d); - - (*rsa)->p = BN_new(); - BN_bin2bn(pdata, plen, (*rsa)->p); - - (*rsa)->q = BN_new(); - BN_bin2bn(qdata, qlen, (*rsa)->q); - - (*rsa)->dmp1 = BN_new(); - BN_bin2bn(e1data, e1len, (*rsa)->dmp1); - - (*rsa)->dmq1 = BN_new(); - BN_bin2bn(e2data, e2len, (*rsa)->dmq1); - - (*rsa)->iqmp = BN_new(); - BN_bin2bn(coeffdata, coefflen, (*rsa)->iqmp); - } - return 0; -} - -int -_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx, - const unsigned char *sig, - unsigned long sig_len, - const unsigned char *m, unsigned long m_len) -{ - unsigned char hash[SHA_DIGEST_LENGTH]; - int ret; - - libssh2_sha1(m, m_len, hash); - ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, - (unsigned char *) sig, sig_len, rsactx); - return (ret == 1) ? 0 : -1; -} - -#if LIBSSH2_DSA -int -_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, - const unsigned char *p, - unsigned long p_len, - const unsigned char *q, - unsigned long q_len, - const unsigned char *g, - unsigned long g_len, - const unsigned char *y, - unsigned long y_len, - const unsigned char *x, unsigned long x_len) -{ - *dsactx = DSA_new(); - - (*dsactx)->p = BN_new(); - BN_bin2bn(p, p_len, (*dsactx)->p); - - (*dsactx)->q = BN_new(); - BN_bin2bn(q, q_len, (*dsactx)->q); - - (*dsactx)->g = BN_new(); - BN_bin2bn(g, g_len, (*dsactx)->g); - - (*dsactx)->pub_key = BN_new(); - BN_bin2bn(y, y_len, (*dsactx)->pub_key); - - if (x_len) { - (*dsactx)->priv_key = BN_new(); - BN_bin2bn(x, x_len, (*dsactx)->priv_key); - } - - return 0; -} - -int -_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, - const unsigned char *sig, - const unsigned char *m, unsigned long m_len) -{ - unsigned char hash[SHA_DIGEST_LENGTH]; - DSA_SIG dsasig; - int ret; - - dsasig.r = BN_new(); - BN_bin2bn(sig, 20, dsasig.r); - dsasig.s = BN_new(); - BN_bin2bn(sig + 20, 20, dsasig.s); - - libssh2_sha1(m, m_len, hash); - ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, &dsasig, dsactx); - BN_clear_free(dsasig.s); - BN_clear_free(dsasig.r); - - return (ret == 1) ? 0 : -1; -} -#endif /* LIBSSH_DSA */ - -int -_libssh2_cipher_init(_libssh2_cipher_ctx * h, - _libssh2_cipher_type(algo), - unsigned char *iv, unsigned char *secret, int encrypt) -{ - EVP_CIPHER_CTX_init(h); - EVP_CipherInit(h, algo(), secret, iv, encrypt); - return 0; -} - -int -_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, - _libssh2_cipher_type(algo), - int encrypt, unsigned char *block) -{ - int blocksize = ctx->cipher->block_size; - unsigned char buf[EVP_MAX_BLOCK_LENGTH]; - int ret; - (void) algo; - (void) encrypt; - - if (blocksize == 1) { -/* Hack for arcfour. */ - blocksize = 8; - } - ret = EVP_Cipher(ctx, buf, block, blocksize); - if (ret == 1) { - memcpy(block, buf, blocksize); - } - return ret == 1 ? 0 : 1; -} - -#if LIBSSH2_AES_CTR - -#include -#include - -typedef struct -{ - AES_KEY key; - EVP_CIPHER_CTX *aes_ctx; - unsigned char ctr[AES_BLOCK_SIZE]; -} aes_ctr_ctx; - -static int -aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, - const unsigned char *iv, int enc) /* init key */ -{ - /* - * variable "c" is leaked from this scope, but is later freed - * in aes_ctr_cleanup - */ - aes_ctr_ctx *c = malloc(sizeof(*c)); - const EVP_CIPHER *aes_cipher; - (void) enc; - - if (c == NULL) - return 0; - - switch (ctx->key_len) { - case 16: - aes_cipher = EVP_aes_128_ecb(); - break; - case 24: - aes_cipher = EVP_aes_192_ecb(); - break; - case 32: - aes_cipher = EVP_aes_256_ecb(); - break; - default: - return 0; - } - c->aes_ctx = malloc(sizeof(EVP_CIPHER_CTX)); - if (c->aes_ctx == NULL) - return 0; - - if (EVP_EncryptInit(c->aes_ctx, aes_cipher, key, NULL) != 1) { - return 0; - } - - EVP_CIPHER_CTX_set_padding(c->aes_ctx, 0); - - memcpy(c->ctr, iv, AES_BLOCK_SIZE); - - EVP_CIPHER_CTX_set_app_data(ctx, c); - - return 1; -} - -static int -aes_ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, - const unsigned char *in, - size_t inl) /* encrypt/decrypt data */ -{ - aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx); - unsigned char b1[AES_BLOCK_SIZE]; - size_t i = 0; - int outlen = 0; - - if (inl != 16) /* libssh2 only ever encrypt one block */ - return 0; - - if (c == NULL) { - return 0; - } - -/* - To encrypt a packet P=P1||P2||...||Pn (where P1, P2, ..., Pn are each - blocks of length L), the encryptor first encrypts with - to obtain a block B1. The block B1 is then XORed with P1 to generate - the ciphertext block C1. The counter X is then incremented -*/ - - if (EVP_EncryptUpdate(c->aes_ctx, b1, &outlen, c->ctr, AES_BLOCK_SIZE) != 1) { - return 0; - } - - for (i = 0; i < 16; i++) - *out++ = *in++ ^ b1[i]; - - i = 15; - while (c->ctr[i]++ == 0xFF) { - if (i == 0) - break; - i--; - } - - return 1; -} - -static int -aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) /* cleanup ctx */ -{ - aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx); - - if (c == NULL) { - return 1; - } - - if (c->aes_ctx != NULL) { - _libssh2_cipher_dtor(c->aes_ctx); - free(c->aes_ctx); - } - - free(c); - - return 1; -} - -static const EVP_CIPHER * -make_ctr_evp (size_t keylen, EVP_CIPHER *aes_ctr_cipher) -{ - aes_ctr_cipher->block_size = 16; - aes_ctr_cipher->key_len = keylen; - aes_ctr_cipher->iv_len = 16; - aes_ctr_cipher->init = aes_ctr_init; - aes_ctr_cipher->do_cipher = aes_ctr_do_cipher; - aes_ctr_cipher->cleanup = aes_ctr_cleanup; - - return aes_ctr_cipher; -} - -const EVP_CIPHER * -_libssh2_EVP_aes_128_ctr(void) -{ - static EVP_CIPHER aes_ctr_cipher; - return !aes_ctr_cipher.key_len? - make_ctr_evp (16, &aes_ctr_cipher) : &aes_ctr_cipher; -} - -const EVP_CIPHER * -_libssh2_EVP_aes_192_ctr(void) -{ - static EVP_CIPHER aes_ctr_cipher; - return !aes_ctr_cipher.key_len? - make_ctr_evp (24, &aes_ctr_cipher) : &aes_ctr_cipher; -} - -const EVP_CIPHER * -_libssh2_EVP_aes_256_ctr(void) -{ - static EVP_CIPHER aes_ctr_cipher; - return !aes_ctr_cipher.key_len? - make_ctr_evp (32, &aes_ctr_cipher) : &aes_ctr_cipher; -} - -void _libssh2_init_aes_ctr(void) -{ - _libssh2_EVP_aes_128_ctr(); - _libssh2_EVP_aes_192_ctr(); - _libssh2_EVP_aes_256_ctr(); -} - -#else -void _libssh2_init_aes_ctr(void) {} -#endif /* LIBSSH2_AES_CTR */ - -/* TODO: Optionally call a passphrase callback specified by the - * calling program - */ -static int -passphrase_cb(char *buf, int size, int rwflag, char *passphrase) -{ - int passphrase_len = strlen(passphrase); - (void) rwflag; - - if (passphrase_len > (size - 1)) { - passphrase_len = size - 1; - } - memcpy(buf, passphrase, passphrase_len); - buf[passphrase_len] = '\0'; - - return passphrase_len; -} - -typedef void * (*pem_read_bio_func)(BIO *, void **, pem_password_cb *, - void * u); - -static int -read_private_key_from_file(void ** key_ctx, - pem_read_bio_func read_private_key, - const char * filename, - unsigned const char *passphrase) -{ - BIO * bp; - - *key_ctx = NULL; - - bp = BIO_new_file(filename, "r"); - if (!bp) { - return -1; - } - - *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb, - (void *) passphrase); - - BIO_free(bp); - return (*key_ctx) ? 0 : -1; -} - -int -_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, - LIBSSH2_SESSION * session, - const char *filename, unsigned const char *passphrase) -{ - pem_read_bio_func read_rsa = - (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey; - (void) session; - - _libssh2_init_if_needed (); - - return read_private_key_from_file((void **) rsa, read_rsa, - filename, passphrase); -} - -#if LIBSSH2_DSA -int -_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, - LIBSSH2_SESSION * session, - const char *filename, unsigned const char *passphrase) -{ - pem_read_bio_func read_dsa = - (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey; - (void) session; - - _libssh2_init_if_needed (); - - return read_private_key_from_file((void **) dsa, read_dsa, - filename, passphrase); -} -#endif /* LIBSSH_DSA */ - -int -_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, - libssh2_rsa_ctx * rsactx, - const unsigned char *hash, - size_t hash_len, - unsigned char **signature, size_t *signature_len) -{ - int ret; - unsigned char *sig; - unsigned int sig_len; - - sig_len = RSA_size(rsactx); - sig = LIBSSH2_ALLOC(session, sig_len); - - if (!sig) { - return -1; - } - - ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx); - - if (!ret) { - LIBSSH2_FREE(session, sig); - return -1; - } - - *signature = sig; - *signature_len = sig_len; - - return 0; -} - -#if LIBSSH2_DSA -int -_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, - const unsigned char *hash, - unsigned long hash_len, unsigned char *signature) -{ - DSA_SIG *sig; - int r_len, s_len; - (void) hash_len; - - sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); - if (!sig) { - return -1; - } - - r_len = BN_num_bytes(sig->r); - if (r_len < 1 || r_len > 20) { - DSA_SIG_free(sig); - return -1; - } - s_len = BN_num_bytes(sig->s); - if (s_len < 1 || s_len > 20) { - DSA_SIG_free(sig); - return -1; - } - - memset(signature, 0, 40); - - BN_bn2bin(sig->r, signature + (20 - r_len)); - BN_bn2bin(sig->s, signature + 20 + (20 - s_len)); - - DSA_SIG_free(sig); - - return 0; -} -#endif /* LIBSSH_DSA */ - -void -libssh2_sha1(const unsigned char *message, unsigned long len, - unsigned char *out) -{ - EVP_MD_CTX ctx; - - EVP_DigestInit(&ctx, EVP_get_digestbyname("sha1")); - EVP_DigestUpdate(&ctx, message, len); - EVP_DigestFinal(&ctx, out, NULL); -} - -void -libssh2_md5(const unsigned char *message, unsigned long len, - unsigned char *out) -{ - EVP_MD_CTX ctx; - - EVP_DigestInit(&ctx, EVP_get_digestbyname("md5")); - EVP_DigestUpdate(&ctx, message, len); - EVP_DigestFinal(&ctx, out, NULL); -} - -static unsigned char * -write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes) -{ - unsigned char *p = buf; - - /* Left space for bn size which will be written below. */ - p += 4; - - *p = 0; - BN_bn2bin(bn, p + 1); - if (!(*(p + 1) & 0x80)) { - memmove(p, p + 1, --bn_bytes); - } - _libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */ - - return p + bn_bytes; -} - -static unsigned char * -gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa, - size_t *key_len) -{ - int e_bytes, n_bytes; - unsigned long len; - unsigned char* key; - unsigned char* p; - - e_bytes = BN_num_bytes(rsa->e) + 1; - n_bytes = BN_num_bytes(rsa->n) + 1; - - /* Key form is "ssh-rsa" + e + n. */ - len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; - - key = LIBSSH2_ALLOC(session, len); - if (key == NULL) { - return NULL; - } - - /* Process key encoding. */ - p = key; - - _libssh2_htonu32(p, 7); /* Key type. */ - p += 4; - memcpy(p, "ssh-rsa", 7); - p += 7; - - p = write_bn(p, rsa->e, e_bytes); - p = write_bn(p, rsa->n, n_bytes); - - *key_len = (size_t)(p - key); - return key; -} - -static unsigned char * -gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa, - size_t *key_len) -{ - int p_bytes, q_bytes, g_bytes, k_bytes; - unsigned long len; - unsigned char* key; - unsigned char* p; - - p_bytes = BN_num_bytes(dsa->p) + 1; - q_bytes = BN_num_bytes(dsa->q) + 1; - g_bytes = BN_num_bytes(dsa->g) + 1; - k_bytes = BN_num_bytes(dsa->pub_key) + 1; - - /* Key form is "ssh-dss" + p + q + g + pub_key. */ - len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes; - - key = LIBSSH2_ALLOC(session, len); - if (key == NULL) { - return NULL; - } - - /* Process key encoding. */ - p = key; - - _libssh2_htonu32(p, 7); /* Key type. */ - p += 4; - memcpy(p, "ssh-dss", 7); - p += 7; - - p = write_bn(p, dsa->p, p_bytes); - p = write_bn(p, dsa->q, q_bytes); - p = write_bn(p, dsa->g, g_bytes); - p = write_bn(p, dsa->pub_key, k_bytes); - - *key_len = (size_t)(p - key); - return key; -} - -static int -gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - EVP_PKEY *pk) -{ - RSA* rsa = NULL; - unsigned char* key; - unsigned char* method_buf = NULL; - size_t key_len; - - _libssh2_debug(session, - LIBSSH2_TRACE_AUTH, - "Computing public key from RSA private key envelop"); - - rsa = EVP_PKEY_get1_RSA(pk); - if (rsa == NULL) { - /* Assume memory allocation error... what else could it be ? */ - goto __alloc_error; - } - - method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */ - if (method_buf == NULL) { - goto __alloc_error; - } - - key = gen_publickey_from_rsa(session, rsa, &key_len); - if (key == NULL) { - goto __alloc_error; - } - RSA_free(rsa); - - memcpy(method_buf, "ssh-rsa", 7); - *method = method_buf; - *method_len = 7; - *pubkeydata = key; - *pubkeydata_len = key_len; - return 0; - - __alloc_error: - if (rsa != NULL) { - RSA_free(rsa); - } - if (method_buf != NULL) { - LIBSSH2_FREE(session, method_buf); - } - - return _libssh2_error(session, - LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for private key data"); -} - -static int -gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - EVP_PKEY *pk) -{ - DSA* dsa = NULL; - unsigned char* key; - unsigned char* method_buf = NULL; - size_t key_len; - - _libssh2_debug(session, - LIBSSH2_TRACE_AUTH, - "Computing public key from DSA private key envelop"); - - dsa = EVP_PKEY_get1_DSA(pk); - if (dsa == NULL) { - /* Assume memory allocation error... what else could it be ? */ - goto __alloc_error; - } - - method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */ - if (method_buf == NULL) { - goto __alloc_error; - } - - key = gen_publickey_from_dsa(session, dsa, &key_len); - if (key == NULL) { - goto __alloc_error; - } - DSA_free(dsa); - - memcpy(method_buf, "ssh-dss", 7); - *method = method_buf; - *method_len = 7; - *pubkeydata = key; - *pubkeydata_len = key_len; - return 0; - - __alloc_error: - if (dsa != NULL) { - DSA_free(dsa); - } - if (method_buf != NULL) { - LIBSSH2_FREE(session, method_buf); - } - - return _libssh2_error(session, - LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for private key data"); -} - -int -_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - const char *privatekey, - const char *passphrase) -{ - int st; - BIO* bp; - EVP_PKEY* pk; - - _libssh2_debug(session, - LIBSSH2_TRACE_AUTH, - "Computing public key from private key file: %s", - privatekey); - - bp = BIO_new_file(privatekey, "r"); - if (bp == NULL) { - return _libssh2_error(session, - LIBSSH2_ERROR_FILE, - "Unable to extract public key from private key " - "file: Unable to open private key file"); - } - if (!EVP_get_cipherbyname("des")) { - /* If this cipher isn't loaded it's a pretty good indication that none - * are. I have *NO DOUBT* that there's a better way to deal with this - * ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on - * it. - */ - OpenSSL_add_all_ciphers(); - } - BIO_reset(bp); - pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase); - BIO_free(bp); - - if (pk == NULL) { - return _libssh2_error(session, - LIBSSH2_ERROR_FILE, - "Unable to extract public key " - "from private key file: " - "Wrong passphrase or invalid/unrecognized " - "private key file format"); - } - - switch (pk->type) { - case EVP_PKEY_RSA : - st = gen_publickey_from_rsa_evp( - session, method, method_len, pubkeydata, pubkeydata_len, pk); - break; - - case EVP_PKEY_DSA : - st = gen_publickey_from_dsa_evp( - session, method, method_len, pubkeydata, pubkeydata_len, pk); - break; - - default : - st = _libssh2_error(session, - LIBSSH2_ERROR_FILE, - "Unable to extract public key " - "from private key file: " - "Unsupported private key file format"); - break; - } - - EVP_PKEY_free(pk); - return st; -} - -#endif /* !LIBSSH2_LIBGCRYPT */ diff --git a/vendor/libssh2-1.4.2/src/openssl.h b/vendor/libssh2-1.4.2/src/openssl.h deleted file mode 100644 index 6d2aeed56..000000000 --- a/vendor/libssh2-1.4.2/src/openssl.h +++ /dev/null @@ -1,178 +0,0 @@ -/* Copyright (C) 2009, 2010 Simon Josefsson - * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. - * - * Author: Simon Josefsson - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include -#include -#ifndef OPENSSL_NO_MD5 -#include -#endif -#include -#include -#include -#include -#include - -#ifdef OPENSSL_NO_RSA -# define LIBSSH2_RSA 0 -#else -# define LIBSSH2_RSA 1 -#endif - -#ifdef OPENSSL_NO_DSA -# define LIBSSH2_DSA 0 -#else -# define LIBSSH2_DSA 1 -#endif - -#ifdef OPENSSL_NO_MD5 -# define LIBSSH2_MD5 0 -#else -# define LIBSSH2_MD5 1 -#endif - -#ifdef OPENSSL_NO_RIPEMD -# define LIBSSH2_HMAC_RIPEMD 0 -#else -# define LIBSSH2_HMAC_RIPEMD 1 -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES) -# define LIBSSH2_AES_CTR 1 -# define LIBSSH2_AES 1 -#else -# define LIBSSH2_AES_CTR 0 -# define LIBSSH2_AES 0 -#endif - -#ifdef OPENSSL_NO_BLOWFISH -# define LIBSSH2_BLOWFISH 0 -#else -# define LIBSSH2_BLOWFISH 1 -#endif - -#ifdef OPENSSL_NO_RC4 -# define LIBSSH2_RC4 0 -#else -# define LIBSSH2_RC4 1 -#endif - -#ifdef OPENSSL_NO_CAST -# define LIBSSH2_CAST 0 -#else -# define LIBSSH2_CAST 1 -#endif - -#ifdef OPENSSL_NO_DES -# define LIBSSH2_3DES 0 -#else -# define LIBSSH2_3DES 1 -#endif - -#define _libssh2_random(buf, len) RAND_bytes ((buf), (len)) - -#define libssh2_sha1_ctx EVP_MD_CTX -#define libssh2_sha1_init(ctx) EVP_DigestInit(ctx, EVP_get_digestbyname("sha1")) -#define libssh2_sha1_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len) -#define libssh2_sha1_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL) -void libssh2_sha1(const unsigned char *message, unsigned long len, unsigned char *out); - -#define libssh2_md5_ctx EVP_MD_CTX -#define libssh2_md5_init(ctx) EVP_DigestInit(ctx, EVP_get_digestbyname("md5")) -#define libssh2_md5_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len) -#define libssh2_md5_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL) -void libssh2_md5(const unsigned char *message, unsigned long len, unsigned char *out); - -#define libssh2_hmac_ctx HMAC_CTX -#define libssh2_hmac_sha1_init(ctx, key, keylen) \ - HMAC_Init(ctx, key, keylen, EVP_sha1()) -#define libssh2_hmac_md5_init(ctx, key, keylen) \ - HMAC_Init(ctx, key, keylen, EVP_md5()) -#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \ - HMAC_Init(ctx, key, keylen, EVP_ripemd160()) -#define libssh2_hmac_update(ctx, data, datalen) \ - HMAC_Update(&(ctx), data, datalen) -#define libssh2_hmac_final(ctx, data) HMAC_Final(&(ctx), data, NULL) -#define libssh2_hmac_cleanup(ctx) HMAC_cleanup(ctx) - -#define libssh2_crypto_init() OpenSSL_add_all_algorithms() -#define libssh2_crypto_exit() - -#define libssh2_rsa_ctx RSA - -#define _libssh2_rsa_free(rsactx) RSA_free(rsactx) - -#define libssh2_dsa_ctx DSA - - -#define _libssh2_dsa_free(dsactx) DSA_free(dsactx) - -#define _libssh2_cipher_type(name) const EVP_CIPHER *(*name)(void) -#define _libssh2_cipher_ctx EVP_CIPHER_CTX - -#define _libssh2_cipher_aes256 EVP_aes_256_cbc -#define _libssh2_cipher_aes192 EVP_aes_192_cbc -#define _libssh2_cipher_aes128 EVP_aes_128_cbc -#define _libssh2_cipher_aes128ctr _libssh2_EVP_aes_128_ctr -#define _libssh2_cipher_aes192ctr _libssh2_EVP_aes_192_ctr -#define _libssh2_cipher_aes256ctr _libssh2_EVP_aes_256_ctr -#define _libssh2_cipher_blowfish EVP_bf_cbc -#define _libssh2_cipher_arcfour EVP_rc4 -#define _libssh2_cipher_cast5 EVP_cast5_cbc -#define _libssh2_cipher_3des EVP_des_ede3_cbc - -#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_cleanup(ctx) - -#define _libssh2_bn BIGNUM -#define _libssh2_bn_ctx BN_CTX -#define _libssh2_bn_ctx_new() BN_CTX_new() -#define _libssh2_bn_ctx_free(bnctx) BN_CTX_free(bnctx) -#define _libssh2_bn_init() BN_new() -#define _libssh2_bn_rand(bn, bits, top, bottom) BN_rand(bn, bits, top, bottom) -#define _libssh2_bn_mod_exp(r, a, p, m, ctx) BN_mod_exp(r, a, p, m, ctx) -#define _libssh2_bn_set_word(bn, val) BN_set_word(bn, val) -#define _libssh2_bn_from_bin(bn, len, val) BN_bin2bn(val, len, bn) -#define _libssh2_bn_to_bin(bn, val) BN_bn2bin(bn, val) -#define _libssh2_bn_bytes(bn) BN_num_bytes(bn) -#define _libssh2_bn_bits(bn) BN_num_bits(bn) -#define _libssh2_bn_free(bn) BN_clear_free(bn) - -const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void); -const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void); -const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void); - diff --git a/vendor/libssh2-1.4.2/src/packet.c b/vendor/libssh2-1.4.2/src/packet.c deleted file mode 100644 index bfbd56a1e..000000000 --- a/vendor/libssh2-1.4.2/src/packet.c +++ /dev/null @@ -1,1243 +0,0 @@ -/* Copyright (c) 2004-2007, Sara Golemon - * Copyright (c) 2005,2006 Mikhail Gusarov - * Copyright (c) 2009-2010 by Daniel Stenberg - * Copyright (c) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include -#include - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#ifdef HAVE_INTTYPES_H -#include -#endif - -/* Needed for struct iovec on some platforms */ -#ifdef HAVE_SYS_UIO_H -#include -#endif - -#include - -#include "transport.h" -#include "channel.h" -#include "packet.h" - -/* - * libssh2_packet_queue_listener - * - * Queue a connection request for a listener - */ -static inline int -packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data, - unsigned long datalen, - packet_queue_listener_state_t *listen_state) -{ - /* - * Look for a matching listener - */ - /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ - unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1); - unsigned char *p; - LIBSSH2_LISTENER *listn = _libssh2_list_first(&session->listeners); - char failure_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; - int rc; - - (void) datalen; - - if (listen_state->state == libssh2_NB_state_idle) { - unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; - listen_state->sender_channel = _libssh2_ntohu32(s); - s += 4; - - listen_state->initial_window_size = _libssh2_ntohu32(s); - s += 4; - listen_state->packet_size = _libssh2_ntohu32(s); - s += 4; - - listen_state->host_len = _libssh2_ntohu32(s); - s += 4; - listen_state->host = s; - s += listen_state->host_len; - listen_state->port = _libssh2_ntohu32(s); - s += 4; - - listen_state->shost_len = _libssh2_ntohu32(s); - s += 4; - listen_state->shost = s; - s += listen_state->shost_len; - listen_state->sport = _libssh2_ntohu32(s); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Remote received connection from %s:%ld to %s:%ld", - listen_state->shost, listen_state->sport, - listen_state->host, listen_state->port); - - listen_state->state = libssh2_NB_state_allocated; - } - - if (listen_state->state != libssh2_NB_state_sent) { - while (listn) { - if ((listn->port == (int) listen_state->port) && - (strlen(listn->host) == listen_state->host_len) && - (memcmp (listn->host, listen_state->host, - listen_state->host_len) == 0)) { - /* This is our listener */ - LIBSSH2_CHANNEL *channel = NULL; - listen_state->channel = NULL; - - if (listen_state->state == libssh2_NB_state_allocated) { - if (listn->queue_maxsize && - (listn->queue_maxsize <= listn->queue_size)) { - /* Queue is full */ - failure_code = SSH_OPEN_RESOURCE_SHORTAGE; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Listener queue full, ignoring"); - listen_state->state = libssh2_NB_state_sent; - break; - } - - channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); - if (!channel) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate a channel for " - "new connection"); - failure_code = SSH_OPEN_RESOURCE_SHORTAGE; - listen_state->state = libssh2_NB_state_sent; - break; - } - listen_state->channel = channel; - - memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); - - channel->session = session; - channel->channel_type_len = sizeof("forwarded-tcpip") - 1; - channel->channel_type = LIBSSH2_ALLOC(session, - channel-> - channel_type_len + - 1); - if (!channel->channel_type) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate a channel for new" - " connection"); - LIBSSH2_FREE(session, channel); - failure_code = SSH_OPEN_RESOURCE_SHORTAGE; - listen_state->state = libssh2_NB_state_sent; - break; - } - memcpy(channel->channel_type, "forwarded-tcpip", - channel->channel_type_len + 1); - - channel->remote.id = listen_state->sender_channel; - channel->remote.window_size_initial = - LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.window_size = - LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.packet_size = - LIBSSH2_CHANNEL_PACKET_DEFAULT; - - channel->local.id = _libssh2_channel_nextid(session); - channel->local.window_size_initial = - listen_state->initial_window_size; - channel->local.window_size = - listen_state->initial_window_size; - channel->local.packet_size = listen_state->packet_size; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Connection queued: channel %lu/%lu " - "win %lu/%lu packet %lu/%lu", - channel->local.id, channel->remote.id, - channel->local.window_size, - channel->remote.window_size, - channel->local.packet_size, - channel->remote.packet_size); - - p = listen_state->packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; - _libssh2_store_u32(&p, channel->remote.id); - _libssh2_store_u32(&p, channel->local.id); - _libssh2_store_u32(&p, - channel->remote.window_size_initial); - _libssh2_store_u32(&p, channel->remote.packet_size); - - listen_state->state = libssh2_NB_state_created; - } - - if (listen_state->state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, listen_state->packet, - 17, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - else if (rc) { - listen_state->state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Unable to send channel " - "open confirmation"); - } - - /* Link the channel into the end of the queue list */ - _libssh2_list_add(&listn->queue, - &listen_state->channel->node); - listn->queue_size++; - - listen_state->state = libssh2_NB_state_idle; - return 0; - } - } - - listn = _libssh2_list_next(&listn->node); - } - - listen_state->state = libssh2_NB_state_sent; - } - - /* We're not listening to you */ - p = listen_state->packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; - _libssh2_store_u32(&p, listen_state->sender_channel); - _libssh2_store_u32(&p, failure_code); - _libssh2_store_str(&p, FwdNotReq, sizeof(FwdNotReq) - 1); - _libssh2_htonu32(p, 0); - - rc = _libssh2_transport_send(session, listen_state->packet, - packet_len, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - listen_state->state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, "Unable to send open failure"); - - } - listen_state->state = libssh2_NB_state_idle; - return 0; -} - -/* - * packet_x11_open - * - * Accept a forwarded X11 connection - */ -static inline int -packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data, - unsigned long datalen, - packet_x11_open_state_t *x11open_state) -{ - int failure_code = SSH_OPEN_CONNECT_FAILED; - /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ - unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1); - unsigned char *p; - LIBSSH2_CHANNEL *channel = x11open_state->channel; - int rc; - - (void) datalen; - - if (x11open_state->state == libssh2_NB_state_idle) { - unsigned char *s = data + (sizeof("x11") - 1) + 5; - x11open_state->sender_channel = _libssh2_ntohu32(s); - s += 4; - x11open_state->initial_window_size = _libssh2_ntohu32(s); - s += 4; - x11open_state->packet_size = _libssh2_ntohu32(s); - s += 4; - x11open_state->shost_len = _libssh2_ntohu32(s); - s += 4; - x11open_state->shost = s; - s += x11open_state->shost_len; - x11open_state->sport = _libssh2_ntohu32(s); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "X11 Connection Received from %s:%ld on channel %lu", - x11open_state->shost, x11open_state->sport, - x11open_state->sender_channel); - - x11open_state->state = libssh2_NB_state_allocated; - } - - if (session->x11) { - if (x11open_state->state == libssh2_NB_state_allocated) { - channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); - if (!channel) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "allocate a channel for new connection"); - failure_code = SSH_OPEN_RESOURCE_SHORTAGE; - goto x11_exit; - } - memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); - - channel->session = session; - channel->channel_type_len = sizeof("x11") - 1; - channel->channel_type = LIBSSH2_ALLOC(session, - channel->channel_type_len + - 1); - if (!channel->channel_type) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "allocate a channel for new connection"); - LIBSSH2_FREE(session, channel); - failure_code = SSH_OPEN_RESOURCE_SHORTAGE; - goto x11_exit; - } - memcpy(channel->channel_type, "x11", - channel->channel_type_len + 1); - - channel->remote.id = x11open_state->sender_channel; - channel->remote.window_size_initial = - LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; - channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; - - channel->local.id = _libssh2_channel_nextid(session); - channel->local.window_size_initial = - x11open_state->initial_window_size; - channel->local.window_size = x11open_state->initial_window_size; - channel->local.packet_size = x11open_state->packet_size; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "X11 Connection established: channel %lu/%lu " - "win %lu/%lu packet %lu/%lu", - channel->local.id, channel->remote.id, - channel->local.window_size, - channel->remote.window_size, - channel->local.packet_size, - channel->remote.packet_size); - p = x11open_state->packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; - _libssh2_store_u32(&p, channel->remote.id); - _libssh2_store_u32(&p, channel->local.id); - _libssh2_store_u32(&p, channel->remote.window_size_initial); - _libssh2_store_u32(&p, channel->remote.packet_size); - - x11open_state->state = libssh2_NB_state_created; - } - - if (x11open_state->state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, x11open_state->packet, 17, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - x11open_state->state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send channel open " - "confirmation"); - } - - /* Link the channel into the session */ - _libssh2_list_add(&session->channels, &channel->node); - - /* - * Pass control to the callback, they may turn right around and - * free the channel, or actually use it - */ - LIBSSH2_X11_OPEN(channel, (char *)x11open_state->shost, - x11open_state->sport); - - x11open_state->state = libssh2_NB_state_idle; - return 0; - } - } - else - failure_code = SSH_OPEN_RESOURCE_SHORTAGE; - /* fall-trough */ - x11_exit: - p = x11open_state->packet; - *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; - _libssh2_store_u32(&p, x11open_state->sender_channel); - _libssh2_store_u32(&p, failure_code); - _libssh2_store_str(&p, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1); - _libssh2_htonu32(p, 0); - - rc = _libssh2_transport_send(session, x11open_state->packet, packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - x11open_state->state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, "Unable to send open failure"); - } - x11open_state->state = libssh2_NB_state_idle; - return 0; -} - -/* - * _libssh2_packet_add - * - * Create a new packet and attach it to the brigade. Called from the transport - * layer when it has received a packet. - * - * The input pointer 'data' is pointing to allocated data that this function - * is asked to deal with so on failure OR success, it must be freed fine. - * - * This function will always be called with 'datalen' greater than zero. - */ -int -_libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, - size_t datalen, int macstate) -{ - int rc = 0; - char *message=NULL; - char *language=NULL; - size_t message_len=0; - size_t language_len=0; - LIBSSH2_CHANNEL *channelp = NULL; - size_t data_head = 0; - unsigned char msg = data[0]; - - switch(session->packAdd_state) { - case libssh2_NB_state_idle: - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Packet type %d received, length=%d", - (int) msg, (int) datalen); - - if ((macstate == LIBSSH2_MAC_INVALID) && - (!session->macerror || - LIBSSH2_MACERROR(session, (char *) data, datalen))) { - /* Bad MAC input, but no callback set or non-zero return from the - callback */ - - LIBSSH2_FREE(session, data); - return _libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, - "Invalid MAC received"); - } - session->packAdd_state = libssh2_NB_state_allocated; - break; - case libssh2_NB_state_jump1: - goto libssh2_packet_add_jump_point1; - case libssh2_NB_state_jump2: - goto libssh2_packet_add_jump_point2; - case libssh2_NB_state_jump3: - goto libssh2_packet_add_jump_point3; - case libssh2_NB_state_jump4: - goto libssh2_packet_add_jump_point4; - case libssh2_NB_state_jump5: - goto libssh2_packet_add_jump_point5; - default: /* nothing to do */ - break; - } - - if (session->packAdd_state == libssh2_NB_state_allocated) { - /* A couple exceptions to the packet adding rule: */ - switch (msg) { - - /* - byte SSH_MSG_DISCONNECT - uint32 reason code - string description in ISO-10646 UTF-8 encoding [RFC3629] - string language tag [RFC3066] - */ - - case SSH_MSG_DISCONNECT: - if(datalen >= 5) { - size_t reason = _libssh2_ntohu32(data + 1); - - if(datalen >= 9) { - message_len = _libssh2_ntohu32(data + 5); - - if(message_len < datalen-13) { - /* 9 = packet_type(1) + reason(4) + message_len(4) */ - message = (char *) data + 9; - - language_len = _libssh2_ntohu32(data + 9 + message_len); - language = (char *) data + 9 + message_len + 4; - - if(language_len > (datalen-13-message_len)) { - /* bad input, clear info */ - language = message = NULL; - language_len = message_len = 0; - } - } - else - /* bad size, clear it */ - message_len=0; - } - if (session->ssh_msg_disconnect) { - LIBSSH2_DISCONNECT(session, reason, message, - message_len, language, language_len); - } - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Disconnect(%d): %s(%s)", reason, - message, language); - } - - LIBSSH2_FREE(session, data); - session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; - session->packAdd_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT, - "socket disconnect"); - /* - byte SSH_MSG_IGNORE - string data - */ - - case SSH_MSG_IGNORE: - if (datalen >= 2) { - if (session->ssh_msg_ignore) { - LIBSSH2_IGNORE(session, (char *) data + 1, datalen - 1); - } - } else if (session->ssh_msg_ignore) { - LIBSSH2_IGNORE(session, "", 0); - } - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - - /* - byte SSH_MSG_DEBUG - boolean always_display - string message in ISO-10646 UTF-8 encoding [RFC3629] - string language tag [RFC3066] - */ - - case SSH_MSG_DEBUG: - if(datalen >= 2) { - int always_display= data[1]; - - if(datalen >= 6) { - message_len = _libssh2_ntohu32(data + 2); - - if(message_len <= (datalen - 10)) { - /* 6 = packet_type(1) + display(1) + message_len(4) */ - message = (char *) data + 6; - language_len = _libssh2_ntohu32(data + 6 + message_len); - - if(language_len <= (datalen - 10 - message_len)) - language = (char *) data + 10 + message_len; - } - } - - if (session->ssh_msg_debug) { - LIBSSH2_DEBUG(session, always_display, message, - message_len, language, language_len); - } - } - /* - * _libssh2_debug will actually truncate this for us so - * that it's not an inordinate about of data - */ - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Debug Packet: %s", message); - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - - /* - byte SSH_MSG_GLOBAL_REQUEST - string request name in US-ASCII only - boolean want reply - .... request-specific data follows - */ - - case SSH_MSG_GLOBAL_REQUEST: - if(datalen >= 5) { - uint32_t len =0; - unsigned char want_reply=0; - len = _libssh2_ntohu32(data + 1); - if(datalen >= (6 + len)) { - want_reply = data[5 + len]; - _libssh2_debug(session, - LIBSSH2_TRACE_CONN, - "Received global request type %.*s (wr %X)", - len, data + 5, want_reply); - } - - - if (want_reply) { - unsigned char packet = SSH_MSG_REQUEST_FAILURE; - libssh2_packet_add_jump_point5: - session->packAdd_state = libssh2_NB_state_jump5; - rc = _libssh2_transport_send(session, &packet, 1, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - } - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - - /* - byte SSH_MSG_CHANNEL_EXTENDED_DATA - uint32 recipient channel - uint32 data_type_code - string data - */ - - case SSH_MSG_CHANNEL_EXTENDED_DATA: - /* streamid(4) */ - data_head += 4; - - /* fall-through */ - - /* - byte SSH_MSG_CHANNEL_DATA - uint32 recipient channel - string data - */ - - case SSH_MSG_CHANNEL_DATA: - /* packet_type(1) + channelno(4) + datalen(4) */ - data_head += 9; - - if(datalen >= data_head) - channelp = - _libssh2_channel_locate(session, - _libssh2_ntohu32(data + 1)); - - if (!channelp) { - _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, - "Packet received for unknown channel"); - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - } -#ifdef LIBSSH2DEBUG - { - uint32_t stream_id = 0; - if (msg == SSH_MSG_CHANNEL_EXTENDED_DATA) - stream_id = _libssh2_ntohu32(data + 5); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "%d bytes packet_add() for %lu/%lu/%lu", - (int) (datalen - data_head), - channelp->local.id, - channelp->remote.id, - stream_id); - } -#endif - if ((channelp->remote.extended_data_ignore_mode == - LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && - (msg == SSH_MSG_CHANNEL_EXTENDED_DATA)) { - /* Pretend we didn't receive this */ - LIBSSH2_FREE(session, data); - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Ignoring extended data and refunding %d bytes", - (int) (datalen - 13)); - session->packAdd_channelp = channelp; - - /* Adjust the window based on the block we just freed */ - libssh2_packet_add_jump_point1: - session->packAdd_state = libssh2_NB_state_jump1; - rc = _libssh2_channel_receive_window_adjust(session-> - packAdd_channelp, - datalen - 13, - 1, NULL); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - - session->packAdd_state = libssh2_NB_state_idle; - return 0; - } - - /* - * REMEMBER! remote means remote as source of data, - * NOT remote window! - */ - if (channelp->remote.packet_size < (datalen - data_head)) { - /* - * Spec says we MAY ignore bytes sent beyond - * packet_size - */ - _libssh2_error(session, - LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, - "Packet contains more data than we offered" - " to receive, truncating"); - datalen = channelp->remote.packet_size + data_head; - } - if (channelp->remote.window_size <= 0) { - /* - * Spec says we MAY ignore bytes sent beyond - * window_size - */ - _libssh2_error(session, - LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, - "The current receive window is full," - " data ignored"); - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - } - /* Reset EOF status */ - channelp->remote.eof = 0; - - if ((datalen - data_head) > channelp->remote.window_size) { - _libssh2_error(session, - LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, - "Remote sent more data than current " - "window allows, truncating"); - datalen = channelp->remote.window_size + data_head; - channelp->remote.window_size = 0; - } - else - /* Now that we've received it, shrink our window */ - channelp->remote.window_size -= datalen - data_head; - - break; - - /* - byte SSH_MSG_CHANNEL_EOF - uint32 recipient channel - */ - - case SSH_MSG_CHANNEL_EOF: - if(datalen >= 5) - channelp = - _libssh2_channel_locate(session, - _libssh2_ntohu32(data + 1)); - if (!channelp) - /* We may have freed already, just quietly ignore this... */ - ; - else { - _libssh2_debug(session, - LIBSSH2_TRACE_CONN, - "EOF received for channel %lu/%lu", - channelp->local.id, - channelp->remote.id); - channelp->remote.eof = 1; - } - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - - /* - byte SSH_MSG_CHANNEL_REQUEST - uint32 recipient channel - string request type in US-ASCII characters only - boolean want reply - .... type-specific data follows - */ - - case SSH_MSG_CHANNEL_REQUEST: - if(datalen >= 9) { - uint32_t channel = _libssh2_ntohu32(data + 1); - uint32_t len = _libssh2_ntohu32(data + 5); - unsigned char want_reply = 1; - - if(len < (datalen - 10)) - want_reply = data[9 + len]; - - _libssh2_debug(session, - LIBSSH2_TRACE_CONN, - "Channel %d received request type %.*s (wr %X)", - channel, len, data + 9, want_reply); - - if (len == sizeof("exit-status") - 1 - && !memcmp("exit-status", data + 9, - sizeof("exit-status") - 1)) { - - /* we've got "exit-status" packet. Set the session value */ - if(datalen >= 20) - channelp = - _libssh2_channel_locate(session, channel); - - if (channelp) { - channelp->exit_status = - _libssh2_ntohu32(data + 9 + sizeof("exit-status")); - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Exit status %lu received for " - "channel %lu/%lu", - channelp->exit_status, - channelp->local.id, - channelp->remote.id); - } - - } - else if (len == sizeof("exit-signal") - 1 - && !memcmp("exit-signal", data + 9, - sizeof("exit-signal") - 1)) { - /* command terminated due to signal */ - if(datalen >= 20) - channelp = _libssh2_channel_locate(session, channel); - - if (channelp) { - /* set signal name (without SIG prefix) */ - uint32_t namelen = - _libssh2_ntohu32(data + 9 + sizeof("exit-signal")); - channelp->exit_signal = - LIBSSH2_ALLOC(session, namelen + 1); - if (!channelp->exit_signal) - rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "memory for signal name"); - else { - memcpy(channelp->exit_signal, - data + 13 + sizeof("exit_signal"), namelen); - channelp->exit_signal[namelen] = '\0'; - /* TODO: save error message and language tag */ - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Exit signal %s received for " - "channel %lu/%lu", - channelp->exit_signal, - channelp->local.id, - channelp->remote.id); - } - } - } - - - if (want_reply) { - unsigned char packet[5]; - libssh2_packet_add_jump_point4: - session->packAdd_state = libssh2_NB_state_jump4; - packet[0] = SSH_MSG_CHANNEL_FAILURE; - memcpy(&packet[1], data+1, 4); - rc = _libssh2_transport_send(session, packet, 5, NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - } - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return rc; - - /* - byte SSH_MSG_CHANNEL_CLOSE - uint32 recipient channel - */ - - case SSH_MSG_CHANNEL_CLOSE: - if(datalen >= 5) - channelp = - _libssh2_channel_locate(session, - _libssh2_ntohu32(data + 1)); - if (!channelp) { - /* We may have freed already, just quietly ignore this... */ - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - } - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Close received for channel %lu/%lu", - channelp->local.id, - channelp->remote.id); - - channelp->remote.close = 1; - channelp->remote.eof = 1; - - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - - /* - byte SSH_MSG_CHANNEL_OPEN - string "session" - uint32 sender channel - uint32 initial window size - uint32 maximum packet size - */ - - case SSH_MSG_CHANNEL_OPEN: - if(datalen < 17) - ; - else if ((datalen >= (sizeof("forwarded-tcpip") + 4)) && - ((sizeof("forwarded-tcpip") - 1) == - _libssh2_ntohu32(data + 1)) - && - (memcmp(data + 5, "forwarded-tcpip", - sizeof("forwarded-tcpip") - 1) == 0)) { - - /* init the state struct */ - memset(&session->packAdd_Qlstn_state, 0, - sizeof(session->packAdd_Qlstn_state)); - - libssh2_packet_add_jump_point2: - session->packAdd_state = libssh2_NB_state_jump2; - rc = packet_queue_listener(session, data, datalen, - &session->packAdd_Qlstn_state); - } - else if ((datalen >= (sizeof("x11") + 4)) && - ((sizeof("x11") - 1) == _libssh2_ntohu32(data + 1)) && - (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { - - /* init the state struct */ - memset(&session->packAdd_x11open_state, 0, - sizeof(session->packAdd_x11open_state)); - - libssh2_packet_add_jump_point3: - session->packAdd_state = libssh2_NB_state_jump3; - rc = packet_x11_open(session, data, datalen, - &session->packAdd_x11open_state); - } - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return rc; - - /* - byte SSH_MSG_CHANNEL_WINDOW_ADJUST - uint32 recipient channel - uint32 bytes to add - */ - case SSH_MSG_CHANNEL_WINDOW_ADJUST: - if(datalen < 9) - ; - else { - uint32_t bytestoadd = _libssh2_ntohu32(data + 5); - channelp = - _libssh2_channel_locate(session, - _libssh2_ntohu32(data + 1)); - if(channelp) { - channelp->local.window_size += bytestoadd; - - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Window adjust for channel %lu/%lu, " - "adding %lu bytes, new window_size=%lu", - channelp->local.id, - channelp->remote.id, - bytestoadd, - channelp->local.window_size); - } - } - LIBSSH2_FREE(session, data); - session->packAdd_state = libssh2_NB_state_idle; - return 0; - default: - break; - } - - session->packAdd_state = libssh2_NB_state_sent; - } - - if (session->packAdd_state == libssh2_NB_state_sent) { - LIBSSH2_PACKET *packetp = - LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); - if (!packetp) { - _libssh2_debug(session, LIBSSH2_ERROR_ALLOC, - "memory for packet"); - session->packAdd_state = libssh2_NB_state_idle; - return LIBSSH2_ERROR_ALLOC; - } - packetp->data = data; - packetp->data_len = datalen; - packetp->data_head = data_head; - - _libssh2_list_add(&session->packets, &packetp->node); - - session->packAdd_state = libssh2_NB_state_sent1; - } - - if ((msg == SSH_MSG_KEXINIT && - !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) || - (session->packAdd_state == libssh2_NB_state_sent2)) { - if (session->packAdd_state == libssh2_NB_state_sent1) { - /* - * Remote wants new keys - * Well, it's already in the brigade, - * let's just call back into ourselves - */ - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Renegotiating Keys"); - - session->packAdd_state = libssh2_NB_state_sent2; - } - - /* - * The KEXINIT message has been added to the queue. The packAdd and - * readPack states need to be reset because _libssh2_kex_exchange - * (eventually) calls upon _libssh2_transport_read to read the rest of - * the key exchange conversation. - */ - session->readPack_state = libssh2_NB_state_idle; - session->packet.total_num = 0; - session->packAdd_state = libssh2_NB_state_idle; - session->fullpacket_state = libssh2_NB_state_idle; - - memset(&session->startup_key_state, 0, sizeof(key_exchange_state_t)); - - /* - * If there was a key reexchange failure, let's just hope we didn't - * send NEWKEYS yet, otherwise remote will drop us like a rock - */ - rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - - session->packAdd_state = libssh2_NB_state_idle; - return 0; -} - -/* - * _libssh2_packet_ask - * - * Scan the brigade for a matching packet type, optionally poll the socket for - * a packet first - */ -int -_libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type, - unsigned char **data, size_t *data_len, - int match_ofs, const unsigned char *match_buf, - size_t match_len) -{ - LIBSSH2_PACKET *packet = _libssh2_list_first(&session->packets); - - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Looking for packet of type: %d", (int) packet_type); - - while (packet) { - if (packet->data[0] == packet_type - && (packet->data_len >= (match_ofs + match_len)) - && (!match_buf || - (memcmp(packet->data + match_ofs, match_buf, - match_len) == 0))) { - *data = packet->data; - *data_len = packet->data_len; - - /* unlink struct from session->packets */ - _libssh2_list_remove(&packet->node); - - LIBSSH2_FREE(session, packet); - - return 0; - } - packet = _libssh2_list_next(&packet->node); - } - return -1; -} - -/* - * libssh2_packet_askv - * - * Scan for any of a list of packet types in the brigade, optionally poll the - * socket for a packet first - */ -int -_libssh2_packet_askv(LIBSSH2_SESSION * session, - const unsigned char *packet_types, - unsigned char **data, size_t *data_len, - int match_ofs, - const unsigned char *match_buf, - size_t match_len) -{ - int i, packet_types_len = strlen((char *) packet_types); - - for(i = 0; i < packet_types_len; i++) { - if (0 == _libssh2_packet_ask(session, packet_types[i], data, - data_len, match_ofs, - match_buf, match_len)) { - return 0; - } - } - - return -1; -} - -/* - * _libssh2_packet_require - * - * Loops _libssh2_transport_read() until the packet requested is available - * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout - * - * Returns negative on error - * Returns 0 when it has taken care of the requested packet. - */ -int -_libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type, - unsigned char **data, size_t *data_len, - int match_ofs, - const unsigned char *match_buf, - size_t match_len, - packet_require_state_t *state) -{ - if (state->start == 0) { - if (_libssh2_packet_ask(session, packet_type, data, data_len, - match_ofs, match_buf, - match_len) == 0) { - /* A packet was available in the packet brigade */ - return 0; - } - - state->start = time(NULL); - } - - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret = _libssh2_transport_read(session); - if (ret == LIBSSH2_ERROR_EAGAIN) - return ret; - else if (ret < 0) { - state->start = 0; - /* an error which is not just because of blocking */ - return ret; - } else if (ret == packet_type) { - /* Be lazy, let packet_ask pull it out of the brigade */ - ret = _libssh2_packet_ask(session, packet_type, data, data_len, - match_ofs, match_buf, match_len); - state->start = 0; - return ret; - } else if (ret == 0) { - /* nothing available, wait until data arrives or we time out */ - long left = LIBSSH2_READ_TIMEOUT - (long)(time(NULL) - - state->start); - - if (left <= 0) { - state->start = 0; - return LIBSSH2_ERROR_TIMEOUT; - } - return -1; /* no packet available yet */ - } - } - - /* Only reached if the socket died */ - return LIBSSH2_ERROR_SOCKET_DISCONNECT; -} - -/* - * _libssh2_packet_burn - * - * Loops _libssh2_transport_read() until any packet is available and promptly - * discards it. - * Used during KEX exchange to discard badly guessed KEX_INIT packets - */ -int -_libssh2_packet_burn(LIBSSH2_SESSION * session, - libssh2_nonblocking_states * state) -{ - unsigned char *data; - size_t data_len; - unsigned char all_packets[255]; - int i; - int ret; - - if (*state == libssh2_NB_state_idle) { - for(i = 1; i < 256; i++) { - all_packets[i - 1] = i; - } - - if (_libssh2_packet_askv(session, all_packets, &data, &data_len, 0, - NULL, 0) == 0) { - i = data[0]; - /* A packet was available in the packet brigade, burn it */ - LIBSSH2_FREE(session, data); - return i; - } - - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Blocking until packet becomes available to burn"); - *state = libssh2_NB_state_created; - } - - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - ret = _libssh2_transport_read(session); - if (ret == LIBSSH2_ERROR_EAGAIN) { - return ret; - } else if (ret < 0) { - *state = libssh2_NB_state_idle; - return ret; - } else if (ret == 0) { - /* FIXME: this might busyloop */ - continue; - } - - /* Be lazy, let packet_ask pull it out of the brigade */ - if (0 == - _libssh2_packet_ask(session, ret, &data, &data_len, 0, NULL, 0)) { - /* Smoke 'em if you got 'em */ - LIBSSH2_FREE(session, data); - *state = libssh2_NB_state_idle; - return ret; - } - } - - /* Only reached if the socket died */ - return LIBSSH2_ERROR_SOCKET_DISCONNECT; -} - -/* - * _libssh2_packet_requirev - * - * Loops _libssh2_transport_read() until one of a list of packet types - * requested is available. SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause - * a bailout. packet_types is a null terminated list of packet_type numbers - */ - -int -_libssh2_packet_requirev(LIBSSH2_SESSION *session, - const unsigned char *packet_types, - unsigned char **data, size_t *data_len, - int match_ofs, - const unsigned char *match_buf, size_t match_len, - packet_requirev_state_t * state) -{ - if (_libssh2_packet_askv(session, packet_types, data, data_len, match_ofs, - match_buf, match_len) == 0) { - /* One of the packets listed was available in the packet brigade */ - state->start = 0; - return 0; - } - - if (state->start == 0) { - state->start = time(NULL); - } - - while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { - int ret = _libssh2_transport_read(session); - if ((ret < 0) && (ret != LIBSSH2_ERROR_EAGAIN)) { - state->start = 0; - return ret; - } - if (ret <= 0) { - long left = LIBSSH2_READ_TIMEOUT - - (long)(time(NULL) - state->start); - - if (left <= 0) { - state->start = 0; - return LIBSSH2_ERROR_TIMEOUT; - } - else if (ret == LIBSSH2_ERROR_EAGAIN) { - return ret; - } - } - - if (strchr((char *) packet_types, ret)) { - /* Be lazy, let packet_ask pull it out of the brigade */ - return _libssh2_packet_askv(session, packet_types, data, - data_len, match_ofs, match_buf, - match_len); - } - } - - /* Only reached if the socket died */ - state->start = 0; - return LIBSSH2_ERROR_SOCKET_DISCONNECT; -} - diff --git a/vendor/libssh2-1.4.2/src/packet.h b/vendor/libssh2-1.4.2/src/packet.h deleted file mode 100644 index d66b15b50..000000000 --- a/vendor/libssh2-1.4.2/src/packet.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef LIBSSH2_PACKET_H -#define LIBSSH2_PACKET_H -/* - * Copyright (C) 2010 by Daniel Stenberg - * Author: Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - */ - -int _libssh2_packet_read(LIBSSH2_SESSION * session); - -int _libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type, - unsigned char **data, size_t *data_len, - int match_ofs, - const unsigned char *match_buf, - size_t match_len); - -int _libssh2_packet_askv(LIBSSH2_SESSION * session, - const unsigned char *packet_types, - unsigned char **data, size_t *data_len, - int match_ofs, - const unsigned char *match_buf, - size_t match_len); -int _libssh2_packet_require(LIBSSH2_SESSION * session, - unsigned char packet_type, unsigned char **data, - size_t *data_len, int match_ofs, - const unsigned char *match_buf, - size_t match_len, - packet_require_state_t * state); -int _libssh2_packet_requirev(LIBSSH2_SESSION *session, - const unsigned char *packet_types, - unsigned char **data, size_t *data_len, - int match_ofs, - const unsigned char *match_buf, - size_t match_len, - packet_requirev_state_t * state); -int _libssh2_packet_burn(LIBSSH2_SESSION * session, - libssh2_nonblocking_states * state); -int _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, - unsigned long data_len); -int _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, - size_t datalen, int macstate); - -#endif /* LIBSSH2_PACKET_H */ diff --git a/vendor/libssh2-1.4.2/src/pem.c b/vendor/libssh2-1.4.2/src/pem.c deleted file mode 100644 index 5749bc838..000000000 --- a/vendor/libssh2-1.4.2/src/pem.c +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright (C) 2007 The Written Word, Inc. - * Copyright (C) 2008, Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -#ifdef LIBSSH2_LIBGCRYPT /* compile only if we build with libgcrypt */ - -static int -readline(char *line, int line_size, FILE * fp) -{ - if (!fgets(line, line_size, fp)) { - return -1; - } - if (*line && line[strlen(line) - 1] == '\n') { - line[strlen(line) - 1] = '\0'; - } - if (*line && line[strlen(line) - 1] == '\r') { - line[strlen(line) - 1] = '\0'; - } - return 0; -} - -#define LINE_SIZE 128 - -int -_libssh2_pem_parse(LIBSSH2_SESSION * session, - const char *headerbegin, - const char *headerend, - FILE * fp, unsigned char **data, unsigned int *datalen) -{ - char line[LINE_SIZE]; - char *b64data = NULL; - unsigned int b64datalen = 0; - int ret; - - do { - if (readline(line, LINE_SIZE, fp)) { - return -1; - } - } - while (strcmp(line, headerbegin) != 0); - - *line = '\0'; - - do { - if (*line) { - char *tmp; - size_t linelen; - - linelen = strlen(line); - tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen); - if (!tmp) { - ret = -1; - goto out; - } - memcpy(tmp + b64datalen, line, linelen); - b64data = tmp; - b64datalen += linelen; - } - - if (readline(line, LINE_SIZE, fp)) { - ret = -1; - goto out; - } - } while (strcmp(line, headerend) != 0); - - if (libssh2_base64_decode(session, (char**) data, datalen, - b64data, b64datalen)) { - ret = -1; - goto out; - } - - ret = 0; - out: - if (b64data) { - LIBSSH2_FREE(session, b64data); - } - return ret; -} - -static int -read_asn1_length(const unsigned char *data, - unsigned int datalen, unsigned int *len) -{ - unsigned int lenlen; - int nextpos; - - if (datalen < 1) { - return -1; - } - *len = data[0]; - - if (*len >= 0x80) { - lenlen = *len & 0x7F; - *len = data[1]; - if (1 + lenlen > datalen) { - return -1; - } - if (lenlen > 1) { - *len <<= 8; - *len |= data[2]; - } - } else { - lenlen = 0; - } - - nextpos = 1 + lenlen; - if (lenlen > 2 || 1 + lenlen + *len > datalen) { - return -1; - } - - return nextpos; -} - -int -_libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen) -{ - unsigned int len; - int lenlen; - - if (*datalen < 1) { - return -1; - } - - if ((*data)[0] != '\x30') { - return -1; - } - - (*data)++; - (*datalen)--; - - lenlen = read_asn1_length(*data, *datalen, &len); - if (lenlen < 0 || lenlen + len != *datalen) { - return -1; - } - - *data += lenlen; - *datalen -= lenlen; - - return 0; -} - -int -_libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen, - unsigned char **i, unsigned int *ilen) -{ - unsigned int len; - int lenlen; - - if (*datalen < 1) { - return -1; - } - - if ((*data)[0] != '\x02') { - return -1; - } - - (*data)++; - (*datalen)--; - - lenlen = read_asn1_length(*data, *datalen, &len); - if (lenlen < 0 || lenlen + len > *datalen) { - return -1; - } - - *data += lenlen; - *datalen -= lenlen; - - *i = *data; - *ilen = len; - - *data += len; - *datalen -= len; - - return 0; -} - -#endif /* LIBSSH2_LIBGCRYPT */ diff --git a/vendor/libssh2-1.4.2/src/publickey.c b/vendor/libssh2-1.4.2/src/publickey.c deleted file mode 100644 index ab76ab2b7..000000000 --- a/vendor/libssh2-1.4.2/src/publickey.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* Copyright (c) 2004-2007, Sara Golemon - * Copyright (c) 2010 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include "libssh2_publickey.h" -#include "channel.h" -#include "session.h" - -#define LIBSSH2_PUBLICKEY_VERSION 2 - -/* Numericised response codes -- Not IETF, just local representation */ -#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0 -#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1 -#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2 - -typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST -{ - int code; - const char *name; - int name_len; -} LIBSSH2_PUBLICKEY_CODE_LIST; - -static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_response_codes[] = -{ - {LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1}, - {LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1}, - {LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey", - sizeof("publickey") - 1} , - {0, NULL, 0} -}; - -/* PUBLICKEY status codes -- IETF defined */ -#define LIBSSH2_PUBLICKEY_SUCCESS 0 -#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1 -#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2 -#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3 -#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4 -#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5 -#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6 -#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7 -#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8 - -#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8 - -static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_status_codes[] = { - {LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1} , - {LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied", - sizeof("access denied") - 1}, - {LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded", - sizeof("storage exceeded") - 1} , - {LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported", - sizeof("version not supported") - 1} , - {LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found", - sizeof("key not found") - 1}, - {LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported", - sizeof("key not supported") - 1}, - {LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present", - sizeof("key already present") - 1}, - {LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure", - sizeof("general failure") - 1}, - {LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported", - sizeof("request not supported") - 1}, - {0, NULL, 0} -}; - -/* - * publickey_status_error - * - * Format an error message from a status code - */ -static void -publickey_status_error(const LIBSSH2_PUBLICKEY *pkey, - LIBSSH2_SESSION *session, int status) -{ - const char *msg; - - /* GENERAL_FAILURE got remapped between version 1 and 2 */ - if (status == 6 && pkey && pkey->version == 1) { - status = 7; - } - - if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) { - msg = "unknown"; - } else { - msg = publickey_status_codes[status].name; - } - - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, msg); -} - -/* - * publickey_packet_receive - * - * Read a packet from the subsystem - */ -static int -publickey_packet_receive(LIBSSH2_PUBLICKEY * pkey, - unsigned char **data, size_t *data_len) -{ - LIBSSH2_CHANNEL *channel = pkey->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned char buffer[4]; - int rc; - - if (pkey->receive_state == libssh2_NB_state_idle) { - rc = _libssh2_channel_read(channel, 0, (char *) buffer, 4); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc != 4) { - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Invalid response from publickey subsystem"); - } - - pkey->receive_packet_len = _libssh2_ntohu32(buffer); - pkey->receive_packet = - LIBSSH2_ALLOC(session, pkey->receive_packet_len); - if (!pkey->receive_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate publickey response " - "buffer"); - } - - pkey->receive_state = libssh2_NB_state_sent; - } - - if (pkey->receive_state == libssh2_NB_state_sent) { - rc = _libssh2_channel_read(channel, 0, (char *) pkey->receive_packet, - pkey->receive_packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc != (int)pkey->receive_packet_len) { - LIBSSH2_FREE(session, pkey->receive_packet); - pkey->receive_packet = NULL; - pkey->receive_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, - "Timeout waiting for publickey subsystem " - "response packet"); - } - - *data = pkey->receive_packet; - *data_len = pkey->receive_packet_len; - } - - pkey->receive_state = libssh2_NB_state_idle; - - return 0; -} - -/* publickey_response_id - * - * Translate a string response name to a numeric code - * Data will be incremented by 4 + response_len on success only - */ -static int -publickey_response_id(unsigned char **pdata, size_t data_len) -{ - size_t response_len; - unsigned char *data = *pdata; - const LIBSSH2_PUBLICKEY_CODE_LIST *codes = publickey_response_codes; - - if (data_len < 4) { - /* Malformed response */ - return -1; - } - response_len = _libssh2_ntohu32(data); - data += 4; - data_len -= 4; - if (data_len < response_len) { - /* Malformed response */ - return -1; - } - - while (codes->name) { - if ((unsigned long)codes->name_len == response_len && - strncmp(codes->name, (char *) data, response_len) == 0) { - *pdata = data + response_len; - return codes->code; - } - codes++; - } - - return -1; -} - -/* publickey_response_success - * - * Generic helper routine to wait for success response and nothing else - */ -static int -publickey_response_success(LIBSSH2_PUBLICKEY * pkey) -{ - LIBSSH2_SESSION *session = pkey->channel->session; - unsigned char *data, *s; - size_t data_len; - int response; - - while (1) { - int rc = publickey_packet_receive(pkey, &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, - "Timeout waiting for response from " - "publickey subsystem"); - } - - s = data; - response = publickey_response_id(&s, data_len); - - switch (response) { - case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: - /* Error, or processing complete */ - { - unsigned long status = _libssh2_ntohu32(s); - - LIBSSH2_FREE(session, data); - - if (status == LIBSSH2_PUBLICKEY_SUCCESS) - return 0; - - publickey_status_error(pkey, session, status); - return -1; - } - default: - LIBSSH2_FREE(session, data); - if (response < 0) { - return _libssh2_error(session, - LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Invalid publickey subsystem response"); - } - /* Unknown/Unexpected */ - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Unexpected publickey subsystem response"); - data = NULL; - } - } - /* never reached, but include `return` to silence compiler warnings */ - return -1; -} - -/* ***************** - * Publickey API * - ***************** */ - -/* - * publickey_init - * - * Startup the publickey subsystem - */ -static LIBSSH2_PUBLICKEY *publickey_init(LIBSSH2_SESSION *session) -{ - int response; - int rc; - - if (session->pkeyInit_state == libssh2_NB_state_idle) { - session->pkeyInit_data = NULL; - session->pkeyInit_pkey = NULL; - session->pkeyInit_channel = NULL; - - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Initializing publickey subsystem"); - - session->pkeyInit_state = libssh2_NB_state_allocated; - } - - if (session->pkeyInit_state == libssh2_NB_state_allocated) { - - session->pkeyInit_channel = - _libssh2_channel_open(session, "session", - sizeof("session") - 1, - LIBSSH2_CHANNEL_WINDOW_DEFAULT, - LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, - 0); - if (!session->pkeyInit_channel) { - if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) - /* The error state is already set, so leave it */ - return NULL; - _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, - "Unable to startup channel"); - goto err_exit; - } - - session->pkeyInit_state = libssh2_NB_state_sent; - } - - if (session->pkeyInit_state == libssh2_NB_state_sent) { - rc = _libssh2_channel_process_startup(session->pkeyInit_channel, - "subsystem", - sizeof("subsystem") - 1, - "publickey", - sizeof("publickey") - 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block starting publickey subsystem"); - return NULL; - } else if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, - "Unable to request publickey subsystem"); - goto err_exit; - } - - session->pkeyInit_state = libssh2_NB_state_sent1; - } - - if (session->pkeyInit_state == libssh2_NB_state_sent1) { - unsigned char *s; - rc = _libssh2_channel_extended_data(session->pkeyInit_channel, - LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block starting publickey subsystem"); - return NULL; - } - - session->pkeyInit_pkey = - LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); - if (!session->pkeyInit_pkey) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate a new publickey structure"); - goto err_exit; - } - memset(session->pkeyInit_pkey, 0, sizeof(LIBSSH2_PUBLICKEY)); - session->pkeyInit_pkey->channel = session->pkeyInit_channel; - session->pkeyInit_pkey->version = 0; - - s = session->pkeyInit_buffer; - _libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); - s += 4; - _libssh2_htonu32(s, sizeof("version") - 1); - s += 4; - memcpy(s, "version", sizeof("version") - 1); - s += sizeof("version") - 1; - _libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); - - session->pkeyInit_buffer_sent = 0; - - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Sending publickey advertising version %d support", - (int) LIBSSH2_PUBLICKEY_VERSION); - - session->pkeyInit_state = libssh2_NB_state_sent2; - } - - if (session->pkeyInit_state == libssh2_NB_state_sent2) { - rc = _libssh2_channel_write(session->pkeyInit_channel, 0, - session->pkeyInit_buffer, - 19 - session->pkeyInit_buffer_sent); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending publickey version packet"); - return NULL; - } else if (rc) { - _libssh2_error(session, rc, - "Unable to send publickey version packet"); - goto err_exit; - } - session->pkeyInit_buffer_sent += rc; - if(session->pkeyInit_buffer_sent < 19) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Need to be called again to complete this"); - return NULL; - } - - session->pkeyInit_state = libssh2_NB_state_sent3; - } - - if (session->pkeyInit_state == libssh2_NB_state_sent3) { - while (1) { - unsigned char *s; - rc = publickey_packet_receive(session->pkeyInit_pkey, - &session->pkeyInit_data, - &session->pkeyInit_data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for response from " - "publickey subsystem"); - return NULL; - } else if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, - "Timeout waiting for response from " - "publickey subsystem"); - goto err_exit; - } - - s = session->pkeyInit_data; - if ((response = - publickey_response_id(&s, session->pkeyInit_data_len)) < 0) { - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Invalid publickey subsystem response code"); - goto err_exit; - } - - switch (response) { - case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: - /* Error */ - { - unsigned long status, descr_len, lang_len; - - status = _libssh2_ntohu32(s); - s += 4; - descr_len = _libssh2_ntohu32(s); - s += 4; - /* description starts here */ - s += descr_len; - lang_len = _libssh2_ntohu32(s); - s += 4; - /* lang starts here */ - s += lang_len; - - if (s > - session->pkeyInit_data + session->pkeyInit_data_len) { - _libssh2_error(session, - LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Malformed publickey subsystem packet"); - goto err_exit; - } - - publickey_status_error(NULL, session, status); - - goto err_exit; - } - - case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: - /* What we want */ - session->pkeyInit_pkey->version = _libssh2_ntohu32(s); - if (session->pkeyInit_pkey->version > - LIBSSH2_PUBLICKEY_VERSION) { - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Truncate remote publickey version from %lu", - session->pkeyInit_pkey->version); - session->pkeyInit_pkey->version = - LIBSSH2_PUBLICKEY_VERSION; - } - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Enabling publickey subsystem version %lu", - session->pkeyInit_pkey->version); - LIBSSH2_FREE(session, session->pkeyInit_data); - session->pkeyInit_data = NULL; - session->pkeyInit_state = libssh2_NB_state_idle; - return session->pkeyInit_pkey; - - default: - /* Unknown/Unexpected */ - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Unexpected publickey subsystem response, " - "ignoring"); - LIBSSH2_FREE(session, session->pkeyInit_data); - session->pkeyInit_data = NULL; - } - } - } - - /* Never reached except by direct goto */ - err_exit: - session->pkeyInit_state = libssh2_NB_state_sent4; - if (session->pkeyInit_channel) { - rc = _libssh2_channel_close(session->pkeyInit_channel); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block closing channel"); - return NULL; - } - } - if (session->pkeyInit_pkey) { - LIBSSH2_FREE(session, session->pkeyInit_pkey); - session->pkeyInit_pkey = NULL; - } - if (session->pkeyInit_data) { - LIBSSH2_FREE(session, session->pkeyInit_data); - session->pkeyInit_data = NULL; - } - session->pkeyInit_state = libssh2_NB_state_idle; - return NULL; -} - -/* - * libssh2_publickey_init - * - * Startup the publickey subsystem - */ -LIBSSH2_API LIBSSH2_PUBLICKEY * -libssh2_publickey_init(LIBSSH2_SESSION *session) -{ - LIBSSH2_PUBLICKEY *ptr; - - BLOCK_ADJUST_ERRNO(ptr, session, - publickey_init(session)); - return ptr; -} - - - -/* - * libssh2_publickey_add_ex - * - * Add a new public key entry - */ -LIBSSH2_API int -libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, - unsigned long name_len, const unsigned char *blob, - unsigned long blob_len, char overwrite, - unsigned long num_attrs, - const libssh2_publickey_attribute attrs[]) -{ - LIBSSH2_CHANNEL *channel; - LIBSSH2_SESSION *session; - /* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name} - blob_len(4) + {blob} */ - unsigned long i, packet_len = 19 + name_len + blob_len; - unsigned char *comment = NULL; - unsigned long comment_len = 0; - int rc; - - if(!pkey) - return LIBSSH2_ERROR_BAD_USE; - - channel = pkey->channel; - session = channel->session; - - if (pkey->add_state == libssh2_NB_state_idle) { - pkey->add_packet = NULL; - - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, "Adding %s publickey", - name); - - if (pkey->version == 1) { - for(i = 0; i < num_attrs; i++) { - /* Search for a comment attribute */ - if (attrs[i].name_len == (sizeof("comment") - 1) && - strncmp(attrs[i].name, "comment", - sizeof("comment") - 1) == 0) { - comment = (unsigned char *) attrs[i].value; - comment_len = attrs[i].value_len; - break; - } - } - packet_len += 4 + comment_len; - } else { - packet_len += 5; /* overwrite(1) + attribute_count(4) */ - for(i = 0; i < num_attrs; i++) { - packet_len += 9 + attrs[i].name_len + attrs[i].value_len; - /* name_len(4) + value_len(4) + mandatory(1) */ - } - } - - pkey->add_packet = LIBSSH2_ALLOC(session, packet_len); - if (!pkey->add_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "publickey \"add\" packet"); - } - - pkey->add_s = pkey->add_packet; - _libssh2_htonu32(pkey->add_s, packet_len - 4); - pkey->add_s += 4; - _libssh2_htonu32(pkey->add_s, sizeof("add") - 1); - pkey->add_s += 4; - memcpy(pkey->add_s, "add", sizeof("add") - 1); - pkey->add_s += sizeof("add") - 1; - if (pkey->version == 1) { - _libssh2_htonu32(pkey->add_s, comment_len); - pkey->add_s += 4; - if (comment) { - memcpy(pkey->add_s, comment, comment_len); - pkey->add_s += comment_len; - } - - _libssh2_htonu32(pkey->add_s, name_len); - pkey->add_s += 4; - memcpy(pkey->add_s, name, name_len); - pkey->add_s += name_len; - _libssh2_htonu32(pkey->add_s, blob_len); - pkey->add_s += 4; - memcpy(pkey->add_s, blob, blob_len); - pkey->add_s += blob_len; - } else { - /* Version == 2 */ - - _libssh2_htonu32(pkey->add_s, name_len); - pkey->add_s += 4; - memcpy(pkey->add_s, name, name_len); - pkey->add_s += name_len; - _libssh2_htonu32(pkey->add_s, blob_len); - pkey->add_s += 4; - memcpy(pkey->add_s, blob, blob_len); - pkey->add_s += blob_len; - *(pkey->add_s++) = overwrite ? 0x01 : 0; - _libssh2_htonu32(pkey->add_s, num_attrs); - pkey->add_s += 4; - for(i = 0; i < num_attrs; i++) { - _libssh2_htonu32(pkey->add_s, attrs[i].name_len); - pkey->add_s += 4; - memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len); - pkey->add_s += attrs[i].name_len; - _libssh2_htonu32(pkey->add_s, attrs[i].value_len); - pkey->add_s += 4; - memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len); - pkey->add_s += attrs[i].value_len; - *(pkey->add_s++) = attrs[i].mandatory ? 0x01 : 0; - } - } - - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Sending publickey \"add\" packet: " - "type=%s blob_len=%ld num_attrs=%ld", - name, blob_len, num_attrs); - - pkey->add_state = libssh2_NB_state_created; - } - - if (pkey->add_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, pkey->add_packet, - (pkey->add_s - pkey->add_packet)); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if ((pkey->add_s - pkey->add_packet) != rc) { - LIBSSH2_FREE(session, pkey->add_packet); - pkey->add_packet = NULL; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send publickey add packet"); - } - LIBSSH2_FREE(session, pkey->add_packet); - pkey->add_packet = NULL; - - pkey->add_state = libssh2_NB_state_sent; - } - - rc = publickey_response_success(pkey); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - - pkey->add_state = libssh2_NB_state_idle; - - return rc; -} - -/* libssh2_publickey_remove_ex - * Remove an existing publickey so that authentication can no longer be - * performed using it - */ -LIBSSH2_API int -libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY * pkey, - const unsigned char *name, unsigned long name_len, - const unsigned char *blob, unsigned long blob_len) -{ - LIBSSH2_CHANNEL *channel; - LIBSSH2_SESSION *session; - /* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name} - + blob_len(4) + {blob} */ - unsigned long packet_len = 22 + name_len + blob_len; - int rc; - - if(!pkey) - return LIBSSH2_ERROR_BAD_USE; - - channel = pkey->channel; - session = channel->session; - - if (pkey->remove_state == libssh2_NB_state_idle) { - pkey->remove_packet = NULL; - - pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len); - if (!pkey->remove_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "publickey \"remove\" packet"); - } - - pkey->remove_s = pkey->remove_packet; - _libssh2_htonu32(pkey->remove_s, packet_len - 4); - pkey->remove_s += 4; - _libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1); - pkey->remove_s += 4; - memcpy(pkey->remove_s, "remove", sizeof("remove") - 1); - pkey->remove_s += sizeof("remove") - 1; - _libssh2_htonu32(pkey->remove_s, name_len); - pkey->remove_s += 4; - memcpy(pkey->remove_s, name, name_len); - pkey->remove_s += name_len; - _libssh2_htonu32(pkey->remove_s, blob_len); - pkey->remove_s += 4; - memcpy(pkey->remove_s, blob, blob_len); - pkey->remove_s += blob_len; - - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Sending publickey \"remove\" packet: " - "type=%s blob_len=%ld", - name, blob_len); - - pkey->remove_state = libssh2_NB_state_created; - } - - if (pkey->remove_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, pkey->remove_packet, - (pkey->remove_s - pkey->remove_packet)); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if ((pkey->remove_s - pkey->remove_packet) != rc) { - LIBSSH2_FREE(session, pkey->remove_packet); - pkey->remove_packet = NULL; - pkey->remove_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send publickey remove packet"); - } - LIBSSH2_FREE(session, pkey->remove_packet); - pkey->remove_packet = NULL; - - pkey->remove_state = libssh2_NB_state_sent; - } - - rc = publickey_response_success(pkey); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - - pkey->remove_state = libssh2_NB_state_idle; - - return rc; -} - -/* libssh2_publickey_list_fetch - * Fetch a list of supported public key from a server - */ -LIBSSH2_API int -libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY * pkey, unsigned long *num_keys, - libssh2_publickey_list ** pkey_list) -{ - LIBSSH2_CHANNEL *channel; - LIBSSH2_SESSION *session; - libssh2_publickey_list *list = NULL; - unsigned long buffer_len = 12, keys = 0, max_keys = 0, i; - /* 12 = packet_len(4) + list_len(4) + "list"(4) */ - int response; - int rc; - - if(!pkey) - return LIBSSH2_ERROR_BAD_USE; - - channel = pkey->channel; - session = channel->session; - - if (pkey->listFetch_state == libssh2_NB_state_idle) { - pkey->listFetch_data = NULL; - - pkey->listFetch_s = pkey->listFetch_buffer; - _libssh2_htonu32(pkey->listFetch_s, buffer_len - 4); - pkey->listFetch_s += 4; - _libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1); - pkey->listFetch_s += 4; - memcpy(pkey->listFetch_s, "list", sizeof("list") - 1); - pkey->listFetch_s += sizeof("list") - 1; - - _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, - "Sending publickey \"list\" packet"); - - pkey->listFetch_state = libssh2_NB_state_created; - } - - if (pkey->listFetch_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, - pkey->listFetch_buffer, - (pkey->listFetch_s - - pkey->listFetch_buffer)); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if ((pkey->listFetch_s - pkey->listFetch_buffer) != rc) { - pkey->listFetch_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send publickey list packet"); - } - - pkey->listFetch_state = libssh2_NB_state_sent; - } - - while (1) { - rc = publickey_packet_receive(pkey, &pkey->listFetch_data, - &pkey->listFetch_data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, - "Timeout waiting for response from " - "publickey subsystem"); - goto err_exit; - } - - pkey->listFetch_s = pkey->listFetch_data; - if ((response = - publickey_response_id(&pkey->listFetch_s, - pkey->listFetch_data_len)) < 0) { - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Invalid publickey subsystem response code"); - goto err_exit; - } - - switch (response) { - case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: - /* Error, or processing complete */ - { - unsigned long status, descr_len, lang_len; - - status = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - descr_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - /* description starts at pkey->listFetch_s */ - pkey->listFetch_s += descr_len; - lang_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - /* lang starts at pkey->listFetch_s */ - pkey->listFetch_s += lang_len; - - if (pkey->listFetch_s > - pkey->listFetch_data + pkey->listFetch_data_len) { - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Malformed publickey subsystem packet"); - goto err_exit; - } - - if (status == LIBSSH2_PUBLICKEY_SUCCESS) { - LIBSSH2_FREE(session, pkey->listFetch_data); - pkey->listFetch_data = NULL; - *pkey_list = list; - *num_keys = keys; - pkey->listFetch_state = libssh2_NB_state_idle; - return 0; - } - - publickey_status_error(pkey, session, status); - goto err_exit; - } - case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY: - /* What we want */ - if (keys >= max_keys) { - libssh2_publickey_list *newlist; - /* Grow the key list if necessary */ - max_keys += 8; - newlist = - LIBSSH2_REALLOC(session, list, - (max_keys + - 1) * sizeof(libssh2_publickey_list)); - if (!newlist) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "publickey list"); - goto err_exit; - } - list = newlist; - } - if (pkey->version == 1) { - unsigned long comment_len; - - comment_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - if (comment_len) { - list[keys].num_attrs = 1; - list[keys].attrs = - LIBSSH2_ALLOC(session, - sizeof(libssh2_publickey_attribute)); - if (!list[keys].attrs) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "publickey attributes"); - goto err_exit; - } - list[keys].attrs[0].name = "comment"; - list[keys].attrs[0].name_len = sizeof("comment") - 1; - list[keys].attrs[0].value = (char *) pkey->listFetch_s; - list[keys].attrs[0].value_len = comment_len; - list[keys].attrs[0].mandatory = 0; - - pkey->listFetch_s += comment_len; - } else { - list[keys].num_attrs = 0; - list[keys].attrs = NULL; - } - list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - list[keys].name = pkey->listFetch_s; - pkey->listFetch_s += list[keys].name_len; - list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - list[keys].blob = pkey->listFetch_s; - pkey->listFetch_s += list[keys].blob_len; - } else { - /* Version == 2 */ - list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - list[keys].name = pkey->listFetch_s; - pkey->listFetch_s += list[keys].name_len; - list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - list[keys].blob = pkey->listFetch_s; - pkey->listFetch_s += list[keys].blob_len; - list[keys].num_attrs = _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - if (list[keys].num_attrs) { - list[keys].attrs = - LIBSSH2_ALLOC(session, - list[keys].num_attrs * - sizeof(libssh2_publickey_attribute)); - if (!list[keys].attrs) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "publickey attributes"); - goto err_exit; - } - for(i = 0; i < list[keys].num_attrs; i++) { - list[keys].attrs[i].name_len = - _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - list[keys].attrs[i].name = (char *) pkey->listFetch_s; - pkey->listFetch_s += list[keys].attrs[i].name_len; - list[keys].attrs[i].value_len = - _libssh2_ntohu32(pkey->listFetch_s); - pkey->listFetch_s += 4; - list[keys].attrs[i].value = (char *) pkey->listFetch_s; - pkey->listFetch_s += list[keys].attrs[i].value_len; - - /* actually an ignored value */ - list[keys].attrs[i].mandatory = 0; - } - } else { - list[keys].attrs = NULL; - } - } - /* To be FREEd in libssh2_publickey_list_free() */ - list[keys].packet = pkey->listFetch_data; - keys++; - - list[keys].packet = NULL; /* Terminate the list */ - pkey->listFetch_data = NULL; - break; - default: - /* Unknown/Unexpected */ - _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, - "Unexpected publickey subsystem response"); - LIBSSH2_FREE(session, pkey->listFetch_data); - pkey->listFetch_data = NULL; - } - } - - /* Only reached via explicit goto */ - err_exit: - if (pkey->listFetch_data) { - LIBSSH2_FREE(session, pkey->listFetch_data); - pkey->listFetch_data = NULL; - } - if (list) { - libssh2_publickey_list_free(pkey, list); - } - pkey->listFetch_state = libssh2_NB_state_idle; - return -1; -} - -/* libssh2_publickey_list_free - * Free a previously fetched list of public keys - */ -LIBSSH2_API void -libssh2_publickey_list_free(LIBSSH2_PUBLICKEY * pkey, - libssh2_publickey_list * pkey_list) -{ - LIBSSH2_SESSION *session; - libssh2_publickey_list *p = pkey_list; - - if(!pkey || !p) - return; - - session = pkey->channel->session; - - while (p->packet) { - if (p->attrs) { - LIBSSH2_FREE(session, p->attrs); - } - LIBSSH2_FREE(session, p->packet); - p++; - } - - LIBSSH2_FREE(session, pkey_list); -} - -/* libssh2_publickey_shutdown - * Shutdown the publickey subsystem - */ -LIBSSH2_API int -libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey) -{ - LIBSSH2_SESSION *session; - int rc; - - if(!pkey) - return LIBSSH2_ERROR_BAD_USE; - - session = pkey->channel->session; - - /* - * Make sure all memory used in the state variables are free - */ - if (pkey->receive_packet) { - LIBSSH2_FREE(session, pkey->receive_packet); - pkey->receive_packet = NULL; - } - if (pkey->add_packet) { - LIBSSH2_FREE(session, pkey->add_packet); - pkey->add_packet = NULL; - } - if (pkey->remove_packet) { - LIBSSH2_FREE(session, pkey->remove_packet); - pkey->remove_packet = NULL; - } - if (pkey->listFetch_data) { - LIBSSH2_FREE(session, pkey->listFetch_data); - pkey->listFetch_data = NULL; - } - - rc = _libssh2_channel_free(pkey->channel); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - - LIBSSH2_FREE(session, pkey); - return 0; -} diff --git a/vendor/libssh2-1.4.2/src/scp.c b/vendor/libssh2-1.4.2/src/scp.c deleted file mode 100644 index 6401dac26..000000000 --- a/vendor/libssh2-1.4.2/src/scp.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* Copyright (c) 2009-2010 by Daniel Stenberg - * Copyright (c) 2004-2008, Sara Golemon - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include -#include - -#include "channel.h" -#include "session.h" - - -/* Max. length of a quoted string after libssh2_shell_quotearg() processing */ -#define _libssh2_shell_quotedsize(s) (3 * strlen(s) + 2) - -/* - This function quotes a string in a way suitable to be used with a - shell, e.g. the file name - one two - becomes - 'one two' - - The resulting output string is crafted in a way that makes it usable - with the two most common shell types: Bourne Shell derived shells - (sh, ksh, ksh93, bash, zsh) and C-Shell derivates (csh, tcsh). - - The following special cases are handled: - o If the string contains an apostrophy itself, the apostrophy - character is written in quotation marks, e.g. "'". - The shell cannot handle the syntax 'doesn\'t', so we close the - current argument word, add the apostrophe in quotation marks "", - and open a new argument word instead (_ indicate the input - string characters): - _____ _ _ - 'doesn' "'" 't' - - Sequences of apostrophes are combined in one pair of quotation marks: - a'''b - becomes - _ ___ _ - 'a'"'''"'b' - - o If the string contains an exclamation mark (!), the C-Shell - interprets it as an event number. Using \! (not within quotation - marks or single quotation marks) is a mechanism understood by - both Bourne Shell and C-Shell. - - If a quotation was already started, the argument word is closed - first: - a!b - - become - _ _ _ - 'a'\!'b' - - The result buffer must be large enough for the expanded result. A - bad case regarding expansion is alternating characters and - apostrophes: - - a'b'c'd' (length 8) gets converted to - 'a'"'"'b'"'"'c'"'"'d'"'" (length 24) - - This is the worst case. - - Maximum length of the result: - 1 + 6 * (length(input) + 1) / 2) + 1 - - => 3 * length(input) + 2 - - Explanation: - o leading apostrophe - o one character / apostrophe pair (two characters) can get - represented as 6 characters: a' -> a'"'"' - o String terminator (+1) - - A result buffer three times the size of the input buffer + 2 - characters should be safe. - - References: - o csh-compatible quotation (special handling for '!' etc.), see - http://www.grymoire.com/Unix/Csh.html#toc-uh-10 - - Return value: - Length of the resulting string (not counting the terminating '\0'), - or 0 in case of errors, e.g. result buffer too small - - Note: this function could possible be used elsewhere within libssh2, but - until then it is kept static and in this source file. -*/ - -static unsigned -shell_quotearg(const char *path, unsigned char *buf, - unsigned bufsize) -{ - const char *src; - unsigned char *dst, *endp; - - /* - * Processing States: - * UQSTRING: unquoted string: ... -- used for quoting exclamation - * marks. This is the initial state - * SQSTRING: single-qouted-string: '... -- any character may follow - * QSTRING: quoted string: "... -- only apostrophes may follow - */ - enum { UQSTRING, SQSTRING, QSTRING } state = UQSTRING; - - endp = &buf[bufsize]; - src = path; - dst = buf; - while (*src && dst < endp - 1) { - - switch (*src) { - /* - * Special handling for apostrophe. - * An apostrophe is always written in quotation marks, e.g. - * ' -> "'". - */ - - case '\'': - switch (state) { - case UQSTRING: /* Unquoted string */ - if (dst+1 >= endp) - return 0; - *dst++ = '"'; - break; - case QSTRING: /* Continue quoted string */ - break; - case SQSTRING: /* Close single quoted string */ - if (dst+2 >= endp) - return 0; - *dst++ = '\''; - *dst++ = '"'; - break; - default: - break; - } - state = QSTRING; - break; - - /* - * Special handling for exclamation marks. CSH interprets - * exclamation marks even when quoted with apostrophes. We convert - * it to the plain string \!, because both Bourne Shell and CSH - * interpret that as a verbatim exclamation mark. - */ - - case '!': - switch (state) { - case UQSTRING: - if (dst+1 >= endp) - return 0; - *dst++ = '\\'; - break; - case QSTRING: - if (dst+2 >= endp) - return 0; - *dst++ = '"'; /* Closing quotation mark */ - *dst++ = '\\'; - break; - case SQSTRING: /* Close single quoted string */ - if (dst+2 >= endp) - return 0; - *dst++ = '\''; - *dst++ = '\\'; - break; - default: - break; - } - state = UQSTRING; - break; - - /* - * Ordinary character: prefer single-quoted string - */ - - default: - switch (state) { - case UQSTRING: - if (dst+1 >= endp) - return 0; - *dst++ = '\''; - break; - case QSTRING: - if (dst+2 >= endp) - return 0; - *dst++ = '"'; /* Closing quotation mark */ - *dst++ = '\''; - break; - case SQSTRING: /* Continue single quoted string */ - break; - default: - break; - } - state = SQSTRING; /* Start single-quoted string */ - break; - } - - if (dst+1 >= endp) - return 0; - *dst++ = *src++; - } - - switch (state) { - case UQSTRING: - break; - case QSTRING: /* Close quoted string */ - if (dst+1 >= endp) - return 0; - *dst++ = '"'; - break; - case SQSTRING: /* Close single quoted string */ - if (dst+1 >= endp) - return 0; - *dst++ = '\''; - break; - default: - break; - } - - if (dst+1 >= endp) - return 0; - *dst = '\0'; - - /* The result cannot be larger than 3 * strlen(path) + 2 */ - /* assert((dst - buf) <= (3 * (src - path) + 2)); */ - - return dst - buf; -} - -/* - * scp_recv - * - * Open a channel and request a remote file via SCP - * - */ -static LIBSSH2_CHANNEL * -scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb) -{ - int cmd_len; - int rc; - int tmp_err_code; - const char *tmp_err_msg; - - if (session->scpRecv_state == libssh2_NB_state_idle) { - session->scpRecv_mode = 0; - session->scpRecv_size = 0; - session->scpRecv_mtime = 0; - session->scpRecv_atime = 0; - - session->scpRecv_command_len = - _libssh2_shell_quotedsize(path) + sizeof("scp -f ") + (sb?1:0); - - session->scpRecv_command = - LIBSSH2_ALLOC(session, session->scpRecv_command_len); - - if (!session->scpRecv_command) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate a command buffer for " - "SCP session"); - return NULL; - } - - snprintf((char *)session->scpRecv_command, - session->scpRecv_command_len, "scp -%sf ", sb?"p":""); - - cmd_len = strlen((char *)session->scpRecv_command); - - (void) shell_quotearg(path, - &session->scpRecv_command[cmd_len], - session->scpRecv_command_len - cmd_len); - - - _libssh2_debug(session, LIBSSH2_TRACE_SCP, - "Opening channel for SCP receive"); - - session->scpRecv_state = libssh2_NB_state_created; - } - - if (session->scpRecv_state == libssh2_NB_state_created) { - /* Allocate a channel */ - session->scpRecv_channel = - _libssh2_channel_open(session, "session", - sizeof("session") - 1, - LIBSSH2_CHANNEL_WINDOW_DEFAULT, - LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, - 0); - if (!session->scpRecv_channel) { - if (libssh2_session_last_errno(session) != - LIBSSH2_ERROR_EAGAIN) { - LIBSSH2_FREE(session, session->scpRecv_command); - session->scpRecv_command = NULL; - session->scpRecv_state = libssh2_NB_state_idle; - } - else { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block starting up channel"); - } - return NULL; - } - - session->scpRecv_state = libssh2_NB_state_sent; - } - - if (session->scpRecv_state == libssh2_NB_state_sent) { - /* Request SCP for the desired file */ - rc = _libssh2_channel_process_startup(session->scpRecv_channel, "exec", - sizeof("exec") - 1, - (char *) session->scpRecv_command, - session->scpRecv_command_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting SCP startup"); - return NULL; - } else if (rc) { - LIBSSH2_FREE(session, session->scpRecv_command); - session->scpRecv_command = NULL; - goto scp_recv_error; - } - LIBSSH2_FREE(session, session->scpRecv_command); - session->scpRecv_command = NULL; - - _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sending initial wakeup"); - /* SCP ACK */ - session->scpRecv_response[0] = '\0'; - - session->scpRecv_state = libssh2_NB_state_sent1; - } - - if (session->scpRecv_state == libssh2_NB_state_sent1) { - rc = _libssh2_channel_write(session->scpRecv_channel, 0, - session->scpRecv_response, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending initial wakeup"); - return NULL; - } else if (rc != 1) { - goto scp_recv_error; - } - - /* Parse SCP response */ - session->scpRecv_response_len = 0; - - session->scpRecv_state = libssh2_NB_state_sent2; - } - - if ((session->scpRecv_state == libssh2_NB_state_sent2) - || (session->scpRecv_state == libssh2_NB_state_sent3)) { - while (sb && (session->scpRecv_response_len < - LIBSSH2_SCP_RESPONSE_BUFLEN)) { - unsigned char *s, *p; - - if (session->scpRecv_state == libssh2_NB_state_sent2) { - rc = _libssh2_channel_read(session->scpRecv_channel, 0, - (char *) session-> - scpRecv_response + - session->scpRecv_response_len, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for SCP response"); - return NULL; - } - else if (rc < 0) { - /* error, give up */ - _libssh2_error(session, rc, "Failed reading SCP response"); - goto scp_recv_error; - } - else if(rc == 0) - goto scp_recv_empty_channel; - - session->scpRecv_response_len++; - - if (session->scpRecv_response[0] != 'T') { - size_t err_len; - char *err_msg; - - /* there can be - 01 for warnings - 02 for errors - - The following string MUST be newline terminated - */ - err_len = - _libssh2_channel_packet_data_len(session-> - scpRecv_channel, 0); - err_msg = LIBSSH2_ALLOC(session, err_len + 1); - if (!err_msg) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Failed to get memory "); - goto scp_recv_error; - } - - /* Read the remote error message */ - (void)_libssh2_channel_read(session->scpRecv_channel, 0, - err_msg, err_len); - /* If it failed for any reason, we ignore it anyway. */ - - /* zero terminate the error */ - err_msg[err_len]=0; - - _libssh2_debug(session, LIBSSH2_TRACE_SCP, - "got %02x %s", session->scpRecv_response[0], - err_msg); - - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Failed to recv file"); - - LIBSSH2_FREE(session, err_msg); - goto scp_recv_error; - } - - if ((session->scpRecv_response_len > 1) && - ((session-> - scpRecv_response[session->scpRecv_response_len - 1] < - '0') - || (session-> - scpRecv_response[session->scpRecv_response_len - 1] > - '9')) - && (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - ' ') - && (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - '\r') - && (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - '\n')) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid data in SCP response"); - goto scp_recv_error; - } - - if ((session->scpRecv_response_len < 9) - || (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - '\n')) { - if (session->scpRecv_response_len == - LIBSSH2_SCP_RESPONSE_BUFLEN) { - /* You had your chance */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Unterminated response from SCP server"); - goto scp_recv_error; - } - /* Way too short to be an SCP response, or not done yet, - short circuit */ - continue; - } - - /* We're guaranteed not to go under response_len == 0 by the - logic above */ - while ((session-> - scpRecv_response[session->scpRecv_response_len - 1] == - '\r') - || (session-> - scpRecv_response[session->scpRecv_response_len - - 1] == '\n')) - session->scpRecv_response_len--; - session->scpRecv_response[session->scpRecv_response_len] = - '\0'; - - if (session->scpRecv_response_len < 8) { - /* EOL came too soon */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, " - "too short" ); - goto scp_recv_error; - } - - s = session->scpRecv_response + 1; - - p = (unsigned char *) strchr((char *) s, ' '); - if (!p || ((p - s) <= 0)) { - /* No spaces or space in the wrong spot */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, " - "malformed mtime"); - goto scp_recv_error; - } - - *(p++) = '\0'; - /* Make sure we don't get fooled by leftover values */ - session->scpRecv_mtime = strtol((char *) s, NULL, 10); - - s = (unsigned char *) strchr((char *) p, ' '); - if (!s || ((s - p) <= 0)) { - /* No spaces or space in the wrong spot */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, malformed mtime.usec"); - goto scp_recv_error; - } - - /* Ignore mtime.usec */ - s++; - p = (unsigned char *) strchr((char *) s, ' '); - if (!p || ((p - s) <= 0)) { - /* No spaces or space in the wrong spot */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, too short or malformed"); - goto scp_recv_error; - } - - *p = '\0'; - /* Make sure we don't get fooled by leftover values */ - session->scpRecv_atime = strtol((char *) s, NULL, 10); - - /* SCP ACK */ - session->scpRecv_response[0] = '\0'; - - session->scpRecv_state = libssh2_NB_state_sent3; - } - - if (session->scpRecv_state == libssh2_NB_state_sent3) { - rc = _libssh2_channel_write(session->scpRecv_channel, 0, - session->scpRecv_response, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting to send SCP ACK"); - return NULL; - } else if (rc != 1) { - goto scp_recv_error; - } - - _libssh2_debug(session, LIBSSH2_TRACE_SCP, - "mtime = %ld, atime = %ld", - session->scpRecv_mtime, session->scpRecv_atime); - - /* We *should* check that atime.usec is valid, but why let - that stop use? */ - break; - } - } - - session->scpRecv_state = libssh2_NB_state_sent4; - } - - if (session->scpRecv_state == libssh2_NB_state_sent4) { - session->scpRecv_response_len = 0; - - session->scpRecv_state = libssh2_NB_state_sent5; - } - - if ((session->scpRecv_state == libssh2_NB_state_sent5) - || (session->scpRecv_state == libssh2_NB_state_sent6)) { - while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { - char *s, *p, *e = NULL; - - if (session->scpRecv_state == libssh2_NB_state_sent5) { - rc = _libssh2_channel_read(session->scpRecv_channel, 0, - (char *) session-> - scpRecv_response + - session->scpRecv_response_len, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for SCP response"); - return NULL; - } - else if (rc < 0) { - /* error, bail out*/ - _libssh2_error(session, rc, "Failed reading SCP response"); - goto scp_recv_error; - } - else if(rc == 0) - goto scp_recv_empty_channel; - - session->scpRecv_response_len++; - - if (session->scpRecv_response[0] != 'C') { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server"); - goto scp_recv_error; - } - - if ((session->scpRecv_response_len > 1) && - (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - '\r') - && (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - '\n') - && - (session-> - scpRecv_response[session->scpRecv_response_len - 1] - < 32)) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid data in SCP response"); - goto scp_recv_error; - } - - if ((session->scpRecv_response_len < 7) - || (session-> - scpRecv_response[session->scpRecv_response_len - 1] != - '\n')) { - if (session->scpRecv_response_len == - LIBSSH2_SCP_RESPONSE_BUFLEN) { - /* You had your chance */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Unterminated response from SCP server"); - goto scp_recv_error; - } - /* Way too short to be an SCP response, or not done yet, - short circuit */ - continue; - } - - /* We're guaranteed not to go under response_len == 0 by the - logic above */ - while ((session-> - scpRecv_response[session->scpRecv_response_len - 1] == - '\r') - || (session-> - scpRecv_response[session->scpRecv_response_len - - 1] == '\n')) { - session->scpRecv_response_len--; - } - session->scpRecv_response[session->scpRecv_response_len] = - '\0'; - - if (session->scpRecv_response_len < 6) { - /* EOL came too soon */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, too short"); - goto scp_recv_error; - } - - s = (char *) session->scpRecv_response + 1; - - p = strchr(s, ' '); - if (!p || ((p - s) <= 0)) { - /* No spaces or space in the wrong spot */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, malformed mode"); - goto scp_recv_error; - } - - *(p++) = '\0'; - /* Make sure we don't get fooled by leftover values */ - - session->scpRecv_mode = strtol(s, &e, 8); - if (e && *e) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, invalid mode"); - goto scp_recv_error; - } - - s = strchr(p, ' '); - if (!s || ((s - p) <= 0)) { - /* No spaces or space in the wrong spot */ - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, too short or malformed"); - goto scp_recv_error; - } - - *s = '\0'; - /* Make sure we don't get fooled by leftover values */ - session->scpRecv_size = scpsize_strtol(p, &e, 10); - if (e && *e) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid response from SCP server, invalid size"); - goto scp_recv_error; - } - - /* SCP ACK */ - session->scpRecv_response[0] = '\0'; - - session->scpRecv_state = libssh2_NB_state_sent6; - } - - if (session->scpRecv_state == libssh2_NB_state_sent6) { - rc = _libssh2_channel_write(session->scpRecv_channel, 0, - session->scpRecv_response, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending SCP ACK"); - return NULL; - } else if (rc != 1) { - goto scp_recv_error; - } - _libssh2_debug(session, LIBSSH2_TRACE_SCP, - "mode = 0%lo size = %ld", session->scpRecv_mode, - session->scpRecv_size); - - /* We *should* check that basename is valid, but why let that - stop us? */ - break; - } - } - - session->scpRecv_state = libssh2_NB_state_sent7; - } - - if (sb) { - memset(sb, 0, sizeof(struct stat)); - - sb->st_mtime = session->scpRecv_mtime; - sb->st_atime = session->scpRecv_atime; - sb->st_size = session->scpRecv_size; - sb->st_mode = session->scpRecv_mode; - } - - session->scpRecv_state = libssh2_NB_state_idle; - return session->scpRecv_channel; - - scp_recv_empty_channel: - /* the code only jumps here as a result of a zero read from channel_read() - so we check EOF status to avoid getting stuck in a loop */ - if(libssh2_channel_eof(session->scpRecv_channel)) - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Unexpected channel close"); - else - return session->scpRecv_channel; - /* fall-through */ - scp_recv_error: - tmp_err_code = session->err_code; - tmp_err_msg = session->err_msg; - while (libssh2_channel_free(session->scpRecv_channel) == - LIBSSH2_ERROR_EAGAIN); - session->err_code = tmp_err_code; - session->err_msg = tmp_err_msg; - session->scpRecv_channel = NULL; - session->scpRecv_state = libssh2_NB_state_idle; - return NULL; -} - -/* - * libssh2_scp_recv - * - * Open a channel and request a remote file via SCP - * - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat * sb) -{ - LIBSSH2_CHANNEL *ptr; - BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb)); - return ptr; -} - -/* - * scp_send() - * - * Send a file using SCP - * - */ -static LIBSSH2_CHANNEL * -scp_send(LIBSSH2_SESSION * session, const char *path, int mode, - libssh2_int64_t size, time_t mtime, time_t atime) -{ - int cmd_len; - int rc; - int tmp_err_code; - const char *tmp_err_msg; - - if (session->scpSend_state == libssh2_NB_state_idle) { - session->scpSend_command_len = - _libssh2_shell_quotedsize(path) + sizeof("scp -t ") + - ((mtime || atime)?1:0); - - session->scpSend_command = - LIBSSH2_ALLOC(session, session->scpSend_command_len); - if (!session->scpSend_command) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate a command buffer for scp session"); - return NULL; - } - - snprintf((char *)session->scpSend_command, session->scpSend_command_len, - "scp -%st ", (mtime || atime)?"p":""); - - cmd_len = strlen((char *)session->scpSend_command); - - (void)shell_quotearg(path, - &session->scpSend_command[cmd_len], - session->scpSend_command_len - cmd_len); - - session->scpSend_command[session->scpSend_command_len - 1] = '\0'; - - _libssh2_debug(session, LIBSSH2_TRACE_SCP, - "Opening channel for SCP send"); - /* Allocate a channel */ - - session->scpSend_state = libssh2_NB_state_created; - } - - if (session->scpSend_state == libssh2_NB_state_created) { - session->scpSend_channel = - _libssh2_channel_open(session, "session", sizeof("session") - 1, - LIBSSH2_CHANNEL_WINDOW_DEFAULT, - LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); - if (!session->scpSend_channel) { - if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { - /* previous call set libssh2_session_last_error(), pass it - through */ - LIBSSH2_FREE(session, session->scpSend_command); - session->scpSend_command = NULL; - session->scpSend_state = libssh2_NB_state_idle; - } - else { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block starting up channel"); - } - return NULL; - } - - session->scpSend_state = libssh2_NB_state_sent; - } - - if (session->scpSend_state == libssh2_NB_state_sent) { - /* Request SCP for the desired file */ - rc = _libssh2_channel_process_startup(session->scpSend_channel, "exec", - sizeof("exec") - 1, - (char *) session->scpSend_command, - session->scpSend_command_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting SCP startup"); - return NULL; - } - else if (rc) { - /* previous call set libssh2_session_last_error(), pass it - through */ - LIBSSH2_FREE(session, session->scpSend_command); - session->scpSend_command = NULL; - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Unknown error while getting error string"); - goto scp_send_error; - } - LIBSSH2_FREE(session, session->scpSend_command); - session->scpSend_command = NULL; - - session->scpSend_state = libssh2_NB_state_sent1; - } - - if (session->scpSend_state == libssh2_NB_state_sent1) { - /* Wait for ACK */ - rc = _libssh2_channel_read(session->scpSend_channel, 0, - (char *) session->scpSend_response, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for response from remote"); - return NULL; - } - else if (rc < 0) { - _libssh2_error(session, rc, "SCP failure"); - goto scp_send_error; - } - else if(!rc) - /* remain in the same state */ - goto scp_send_empty_channel; - else if (session->scpSend_response[0] != 0) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid ACK response from remote"); - goto scp_send_error; - } - if (mtime || atime) { - /* Send mtime and atime to be used for file */ - session->scpSend_response_len = - snprintf((char *) session->scpSend_response, - LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", - (long)mtime, (long)atime); - _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s", - session->scpSend_response); - } - - session->scpSend_state = libssh2_NB_state_sent2; - } - - /* Send mtime and atime to be used for file */ - if (mtime || atime) { - if (session->scpSend_state == libssh2_NB_state_sent2) { - rc = _libssh2_channel_write(session->scpSend_channel, 0, - session->scpSend_response, - session->scpSend_response_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending time data for SCP file"); - return NULL; - } else if (rc != (int)session->scpSend_response_len) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send time data for SCP file"); - goto scp_send_error; - } - - session->scpSend_state = libssh2_NB_state_sent3; - } - - if (session->scpSend_state == libssh2_NB_state_sent3) { - /* Wait for ACK */ - rc = _libssh2_channel_read(session->scpSend_channel, 0, - (char *) session->scpSend_response, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for response"); - return NULL; - } - else if (rc < 0) { - _libssh2_error(session, rc, "SCP failure"); - goto scp_send_error; - } - else if(!rc) - /* remain in the same state */ - goto scp_send_empty_channel; - else if (session->scpSend_response[0] != 0) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid SCP ACK response"); - goto scp_send_error; - } - - session->scpSend_state = libssh2_NB_state_sent4; - } - } else { - if (session->scpSend_state == libssh2_NB_state_sent2) { - session->scpSend_state = libssh2_NB_state_sent4; - } - } - - if (session->scpSend_state == libssh2_NB_state_sent4) { - /* Send mode, size, and basename */ - const char *base = strrchr(path, '/'); - if (base) - base++; - else - base = path; - - session->scpSend_response_len = - snprintf((char *) session->scpSend_response, - LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %" - LIBSSH2_INT64_T_FORMAT "u %s\n", mode, - size, base); - _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s", - session->scpSend_response); - - session->scpSend_state = libssh2_NB_state_sent5; - } - - if (session->scpSend_state == libssh2_NB_state_sent5) { - rc = _libssh2_channel_write(session->scpSend_channel, 0, - session->scpSend_response, - session->scpSend_response_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block send core file data for SCP file"); - return NULL; - } else if (rc != (int)session->scpSend_response_len) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send core file data for SCP file"); - goto scp_send_error; - } - - session->scpSend_state = libssh2_NB_state_sent6; - } - - if (session->scpSend_state == libssh2_NB_state_sent6) { - /* Wait for ACK */ - rc = _libssh2_channel_read(session->scpSend_channel, 0, - (char *) session->scpSend_response, 1); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for response"); - return NULL; - } - else if (rc < 0) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Invalid ACK response from remote"); - goto scp_send_error; - } - else if (rc == 0) - goto scp_send_empty_channel; - - else if (session->scpSend_response[0] != 0) { - size_t err_len; - char *err_msg; - - err_len = - _libssh2_channel_packet_data_len(session->scpSend_channel, 0); - err_msg = LIBSSH2_ALLOC(session, err_len + 1); - if (!err_msg) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "failed to get memory"); - goto scp_send_error; - } - - /* Read the remote error message */ - rc = _libssh2_channel_read(session->scpSend_channel, 0, - err_msg, err_len); - if (rc > 0) { - err_msg[err_len]=0; - _libssh2_debug(session, LIBSSH2_TRACE_SCP, - "got %02x %s", session->scpSend_response[0], - err_msg); - } - LIBSSH2_FREE(session, err_msg); - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "failed to send file"); - goto scp_send_error; - } - } - - session->scpSend_state = libssh2_NB_state_idle; - return session->scpSend_channel; - - scp_send_empty_channel: - /* the code only jumps here as a result of a zero read from channel_read() - so we check EOF status to avoid getting stuck in a loop */ - if(libssh2_channel_eof(session->scpSend_channel)) { - _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, - "Unexpected channel close"); - } - else - return session->scpSend_channel; - /* fall-through */ - scp_send_error: - tmp_err_code = session->err_code; - tmp_err_msg = session->err_msg; - while (libssh2_channel_free(session->scpSend_channel) == - LIBSSH2_ERROR_EAGAIN); - session->err_code = tmp_err_code; - session->err_msg = tmp_err_msg; - session->scpSend_channel = NULL; - session->scpSend_state = libssh2_NB_state_idle; - return NULL; -} - -/* - * libssh2_scp_send_ex - * - * Send a file using SCP. Old API. - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, - size_t size, long mtime, long atime) -{ - LIBSSH2_CHANNEL *ptr; - BLOCK_ADJUST_ERRNO(ptr, session, - scp_send(session, path, mode, size, - (time_t)mtime, (time_t)atime)); - return ptr; -} - -/* - * libssh2_scp_send64 - * - * Send a file using SCP - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_scp_send64(LIBSSH2_SESSION *session, const char *path, int mode, - libssh2_int64_t size, time_t mtime, time_t atime) -{ - LIBSSH2_CHANNEL *ptr; - BLOCK_ADJUST_ERRNO(ptr, session, - scp_send(session, path, mode, size, mtime, atime)); - return ptr; -} diff --git a/vendor/libssh2-1.4.2/src/session.c b/vendor/libssh2-1.4.2/src/session.c deleted file mode 100644 index 77ab9bcca..000000000 --- a/vendor/libssh2-1.4.2/src/session.c +++ /dev/null @@ -1,1751 +0,0 @@ -/* Copyright (c) 2004-2007 Sara Golemon - * Copyright (c) 2009-2011 by Daniel Stenberg - * Copyright (c) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#include - -#ifdef HAVE_GETTIMEOFDAY -#include -#endif -#ifdef HAVE_ALLOCA_H -#include -#endif - -#include "transport.h" -#include "session.h" -#include "channel.h" -#include "mac.h" -#include "misc.h" - -/* libssh2_default_alloc - */ -static -LIBSSH2_ALLOC_FUNC(libssh2_default_alloc) -{ - (void) abstract; - return malloc(count); -} - -/* libssh2_default_free - */ -static -LIBSSH2_FREE_FUNC(libssh2_default_free) -{ - (void) abstract; - free(ptr); -} - -/* libssh2_default_realloc - */ -static -LIBSSH2_REALLOC_FUNC(libssh2_default_realloc) -{ - (void) abstract; - return realloc(ptr, count); -} - -/* - * banner_receive - * - * Wait for a hello from the remote host - * Allocate a buffer and store the banner in session->remote.banner - * Returns: 0 on success, LIBSSH2_ERROR_EAGAIN if read would block, negative - * on failure - */ -static int -banner_receive(LIBSSH2_SESSION * session) -{ - int ret; - int banner_len; - - if (session->banner_TxRx_state == libssh2_NB_state_idle) { - banner_len = 0; - - session->banner_TxRx_state = libssh2_NB_state_created; - } else { - banner_len = session->banner_TxRx_total_send; - } - - while ((banner_len < (int) sizeof(session->banner_TxRx_banner)) && - ((banner_len == 0) - || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) { - char c = '\0'; - - /* no incoming block yet! */ - session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND; - - ret = LIBSSH2_RECV(session, &c, 1, - LIBSSH2_SOCKET_RECV_FLAGS(session)); - if (ret < 0) { - if(session->api_block_mode || (ret != -EAGAIN)) - /* ignore EAGAIN when non-blocking */ - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Error recving %d bytes: %d", 1, -ret); - } - else - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Recved %d bytes banner", ret); - - if (ret < 0) { - if (ret == -EAGAIN) { - session->socket_block_directions = - LIBSSH2_SESSION_BLOCK_INBOUND; - session->banner_TxRx_total_send = banner_len; - return LIBSSH2_ERROR_EAGAIN; - } - - /* Some kinda error */ - session->banner_TxRx_state = libssh2_NB_state_idle; - session->banner_TxRx_total_send = 0; - return LIBSSH2_ERROR_SOCKET_RECV; - } - - if (ret == 0) { - session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; - return LIBSSH2_ERROR_SOCKET_DISCONNECT; - } - - if (c == '\0') { - /* NULLs are not allowed in SSH banners */ - session->banner_TxRx_state = libssh2_NB_state_idle; - session->banner_TxRx_total_send = 0; - return LIBSSH2_ERROR_BANNER_RECV; - } - - session->banner_TxRx_banner[banner_len++] = c; - } - - while (banner_len && - ((session->banner_TxRx_banner[banner_len - 1] == '\n') || - (session->banner_TxRx_banner[banner_len - 1] == '\r'))) { - banner_len--; - } - - /* From this point on, we are done here */ - session->banner_TxRx_state = libssh2_NB_state_idle; - session->banner_TxRx_total_send = 0; - - if (!banner_len) - return LIBSSH2_ERROR_BANNER_RECV; - - session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1); - if (!session->remote.banner) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Error allocating space for remote banner"); - } - memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len); - session->remote.banner[banner_len] = '\0'; - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Received Banner: %s", - session->remote.banner); - return LIBSSH2_ERROR_NONE; -} - -/* - * banner_send - * - * Send the default banner, or the one set via libssh2_setopt_string - * - * Returns LIBSSH2_ERROR_EAGAIN if it would block - and if it does so, you - * should call this function again as soon as it is likely that more data can - * be sent, and this function should then be called with the same argument set - * (same data pointer and same data_len) until zero or failure is returned. - */ -static int -banner_send(LIBSSH2_SESSION * session) -{ - char *banner = (char *) LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF; - int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1; - ssize_t ret; -#ifdef LIBSSH2DEBUG - char banner_dup[256]; -#endif - - if (session->banner_TxRx_state == libssh2_NB_state_idle) { - if (session->local.banner) { - /* setopt_string will have given us our \r\n characters */ - banner_len = strlen((char *) session->local.banner); - banner = (char *) session->local.banner; - } -#ifdef LIBSSH2DEBUG - /* Hack and slash to avoid sending CRLF in debug output */ - if (banner_len < 256) { - memcpy(banner_dup, banner, banner_len - 2); - banner_dup[banner_len - 2] = '\0'; - } else { - memcpy(banner_dup, banner, 255); - banner[255] = '\0'; - } - - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Sending Banner: %s", - banner_dup); -#endif - - session->banner_TxRx_state = libssh2_NB_state_created; - } - - /* no outgoing block yet! */ - session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND; - - ret = LIBSSH2_SEND(session, - banner + session->banner_TxRx_total_send, - banner_len - session->banner_TxRx_total_send, - LIBSSH2_SOCKET_SEND_FLAGS(session)); - if (ret < 0) - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Error sending %d bytes: %d", - banner_len - session->banner_TxRx_total_send, -ret); - else - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Sent %d/%d bytes at %p+%d", ret, - banner_len - session->banner_TxRx_total_send, - banner, session->banner_TxRx_total_send); - - if (ret != (banner_len - session->banner_TxRx_total_send)) { - if (ret >= 0 || ret == -EAGAIN) { - /* the whole packet could not be sent, save the what was */ - session->socket_block_directions = - LIBSSH2_SESSION_BLOCK_OUTBOUND; - if (ret > 0) - session->banner_TxRx_total_send += ret; - return LIBSSH2_ERROR_EAGAIN; - } - session->banner_TxRx_state = libssh2_NB_state_idle; - session->banner_TxRx_total_send = 0; - return LIBSSH2_ERROR_SOCKET_RECV; - } - - /* Set the state back to idle */ - session->banner_TxRx_state = libssh2_NB_state_idle; - session->banner_TxRx_total_send = 0; - - return 0; -} - -/* - * session_nonblock() sets the given socket to either blocking or - * non-blocking mode based on the 'nonblock' boolean argument. This function - * is copied from the libcurl sources with permission. - */ -static int -session_nonblock(libssh2_socket_t sockfd, /* operate on this */ - int nonblock /* TRUE or FALSE */ ) -{ -#undef SETBLOCK -#define SETBLOCK 0 -#ifdef HAVE_O_NONBLOCK - /* most recent unix versions */ - int flags; - - flags = fcntl(sockfd, F_GETFL, 0); - if (nonblock) - return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - else - return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); -#undef SETBLOCK -#define SETBLOCK 1 -#endif - -#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) - /* older unix versions and VMS*/ - int flags; - - flags = nonblock; - return ioctl(sockfd, FIONBIO, &flags); -#undef SETBLOCK -#define SETBLOCK 2 -#endif - -#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) - /* Windows? */ - unsigned long flags; - flags = nonblock; - - return ioctlsocket(sockfd, FIONBIO, &flags); -#undef SETBLOCK -#define SETBLOCK 3 -#endif - -#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) - /* presumably for Amiga */ - return IoctlSocket(sockfd, FIONBIO, (long) nonblock); -#undef SETBLOCK -#define SETBLOCK 4 -#endif - -#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) - /* BeOS */ - long b = nonblock ? 1 : 0; - return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); -#undef SETBLOCK -#define SETBLOCK 5 -#endif - -#ifdef HAVE_DISABLED_NONBLOCKING - return 0; /* returns success */ -#undef SETBLOCK -#define SETBLOCK 6 -#endif - -#if (SETBLOCK == 0) -#error "no non-blocking method was found/used/set" -#endif -} - -/* - * get_socket_nonblocking() - * - * gets the given blocking or non-blocking state of the socket. - */ -static int -get_socket_nonblocking(int sockfd) -{ /* operate on this */ -#undef GETBLOCK -#define GETBLOCK 0 -#ifdef HAVE_O_NONBLOCK - /* most recent unix versions */ - int flags; - - if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) { - /* Assume blocking on error */ - return 1; - } - return (flags & O_NONBLOCK); -#undef GETBLOCK -#define GETBLOCK 1 -#endif - -#if defined(WSAEWOULDBLOCK) && (GETBLOCK == 0) - /* Windows? */ - unsigned int option_value; - socklen_t option_len = sizeof(option_value); - - if (getsockopt - (sockfd, SOL_SOCKET, SO_ERROR, (void *) &option_value, &option_len)) { - /* Assume blocking on error */ - return 1; - } - return (int) option_value; -#undef GETBLOCK -#define GETBLOCK 2 -#endif - -#if defined(HAVE_SO_NONBLOCK) && (GETBLOCK == 0) - /* BeOS */ - long b; - if (getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) { - /* Assume blocking on error */ - return 1; - } - return (int) b; -#undef GETBLOCK -#define GETBLOCK 5 -#endif - -#if defined(SO_STATE) && defined( __VMS ) && (GETBLOCK == 0) - - /* VMS TCP/IP Services */ - - size_t sockstat = 0; - int callstat = 0; - size_t size = sizeof( int ); - - callstat = getsockopt(sockfd, SOL_SOCKET, SO_STATE, - (char *)&sockstat, &size); - if ( callstat == -1 ) return(0); - if ( (sockstat&SS_NBIO) )return(1); - return(0); - -#undef GETBLOCK -#define GETBLOCK 6 -#endif - -#ifdef HAVE_DISABLED_NONBLOCKING - return 1; /* returns blocking */ -#undef GETBLOCK -#define GETBLOCK 7 -#endif - -#if (GETBLOCK == 0) -#error "no non-blocking method was found/used/get" -#endif -} - -/* libssh2_session_banner_set - * Set the local banner to use in the server handshake. - */ -LIBSSH2_API int -libssh2_session_banner_set(LIBSSH2_SESSION * session, const char *banner) -{ - size_t banner_len = banner ? strlen(banner) : 0; - - if (session->local.banner) { - LIBSSH2_FREE(session, session->local.banner); - session->local.banner = NULL; - } - - if (!banner_len) - return 0; - - session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3); - if (!session->local.banner) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for local banner"); - } - - memcpy(session->local.banner, banner, banner_len); - - /* first zero terminate like this so that the debug output is nice */ - session->local.banner[banner_len] = '\0'; - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting local Banner: %s", - session->local.banner); - session->local.banner[banner_len++] = '\r'; - session->local.banner[banner_len++] = '\n'; - session->local.banner[banner_len] = '\0'; - - return 0; -} - -/* libssh2_banner_set - * Set the local banner. DEPRECATED VERSION - */ -LIBSSH2_API int -libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner) -{ - return libssh2_session_banner_set(session, banner); -} - -/* - * libssh2_session_init_ex - * - * Allocate and initialize a libssh2 session structure. Allows for malloc - * callbacks in case the calling program has its own memory manager It's - * allowable (but unadvisable) to define some but not all of the malloc - * callbacks An additional pointer value may be optionally passed to be sent - * to the callbacks (so they know who's asking) - */ -LIBSSH2_API LIBSSH2_SESSION * -libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), - LIBSSH2_FREE_FUNC((*my_free)), - LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract) -{ - LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc; - LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free; - LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc; - LIBSSH2_SESSION *session; - - if (my_alloc) { - local_alloc = my_alloc; - } - if (my_free) { - local_free = my_free; - } - if (my_realloc) { - local_realloc = my_realloc; - } - - session = local_alloc(sizeof(LIBSSH2_SESSION), &abstract); - if (session) { - memset(session, 0, sizeof(LIBSSH2_SESSION)); - session->alloc = local_alloc; - session->free = local_free; - session->realloc = local_realloc; - session->send = _libssh2_send; - session->recv = _libssh2_recv; - session->abstract = abstract; - session->api_timeout = 0; /* timeout-free API by default */ - session->api_block_mode = 1; /* blocking API by default */ - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "New session resource allocated"); - _libssh2_init_if_needed (); - } - return session; -} - -/* - * libssh2_session_callback_set - * - * Set (or reset) a callback function - * Returns the prior address - * - * FIXME: this function relies on that we can typecast function pointers - * to void pointers, which isn't allowed in ISO C! - */ -LIBSSH2_API void * -libssh2_session_callback_set(LIBSSH2_SESSION * session, - int cbtype, void *callback) -{ - void *oldcb; - - switch (cbtype) { - case LIBSSH2_CALLBACK_IGNORE: - oldcb = session->ssh_msg_ignore; - session->ssh_msg_ignore = callback; - return oldcb; - - case LIBSSH2_CALLBACK_DEBUG: - oldcb = session->ssh_msg_debug; - session->ssh_msg_debug = callback; - return oldcb; - - case LIBSSH2_CALLBACK_DISCONNECT: - oldcb = session->ssh_msg_disconnect; - session->ssh_msg_disconnect = callback; - return oldcb; - - case LIBSSH2_CALLBACK_MACERROR: - oldcb = session->macerror; - session->macerror = callback; - return oldcb; - - case LIBSSH2_CALLBACK_X11: - oldcb = session->x11; - session->x11 = callback; - return oldcb; - - case LIBSSH2_CALLBACK_SEND: - oldcb = session->send; - session->send = callback; - return oldcb; - - case LIBSSH2_CALLBACK_RECV: - oldcb = session->recv; - session->recv = callback; - return oldcb; - } - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting Callback %d", cbtype); - - return NULL; -} - -/* - * _libssh2_wait_socket() - * - * Utility function that waits for action on the socket. Returns 0 when ready - * to run again or error on timeout. - */ -int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time) -{ - int rc; - int seconds_to_next; - int dir; - int has_timeout; - long ms_to_next = 0; - long elapsed_ms; - - /* since libssh2 often sets EAGAIN internally before this function is - called, we can decrease some amount of confusion in user programs by - resetting the error code in this function to reduce the risk of EAGAIN - being stored as error when a blocking function has returned */ - session->err_code = LIBSSH2_ERROR_NONE; - - rc = libssh2_keepalive_send (session, &seconds_to_next); - if (rc < 0) - return rc; - - ms_to_next = seconds_to_next * 1000; - - /* figure out what to wait for */ - dir = libssh2_session_block_directions(session); - - if(!dir) { - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Nothing to wait for in wait_socket"); - /* To avoid that we hang below just because there's nothing set to - wait for, we timeout on 1 second to also avoid busy-looping - during this condition */ - ms_to_next = 1000; - } - - if (session->api_timeout > 0 && - (seconds_to_next == 0 || - seconds_to_next > session->api_timeout)) { - time_t now = time (NULL); - elapsed_ms = (long)(1000*difftime(start_time, now)); - if (elapsed_ms > session->api_timeout) { - session->err_code = LIBSSH2_ERROR_TIMEOUT; - return LIBSSH2_ERROR_TIMEOUT; - } - ms_to_next = (session->api_timeout - elapsed_ms); - has_timeout = 1; - } - else if (ms_to_next > 0) { - has_timeout = 1; - } - else - has_timeout = 0; - -#ifdef HAVE_POLL - { - struct pollfd sockets[1]; - - sockets[0].fd = session->socket_fd; - sockets[0].events = 0; - sockets[0].revents = 0; - - if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) - sockets[0].events |= POLLIN; - - if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) - sockets[0].events |= POLLOUT; - - rc = poll(sockets, 1, has_timeout?ms_to_next: -1); - } -#else - { - fd_set rfd; - fd_set wfd; - fd_set *writefd = NULL; - fd_set *readfd = NULL; - struct timeval tv; - - tv.tv_sec = ms_to_next / 1000; - tv.tv_usec = (ms_to_next - tv.tv_sec*1000) * 1000; - - if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) { - FD_ZERO(&rfd); - FD_SET(session->socket_fd, &rfd); - readfd = &rfd; - } - - if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) { - FD_ZERO(&wfd); - FD_SET(session->socket_fd, &wfd); - writefd = &wfd; - } - - rc = select(session->socket_fd + 1, readfd, writefd, NULL, - has_timeout ? &tv : NULL); - } -#endif - if(rc <= 0) { - /* timeout (or error), bail out with a timeout error */ - session->err_code = LIBSSH2_ERROR_TIMEOUT; - return LIBSSH2_ERROR_TIMEOUT; - } - - return 0; /* ready to try again */ -} - -static int -session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock) -{ - int rc; - - if (session->startup_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "session_startup for socket %d", sock); - if (LIBSSH2_INVALID_SOCKET == sock) { - /* Did we forget something? */ - return _libssh2_error(session, LIBSSH2_ERROR_BAD_SOCKET, - "Bad socket provided"); - } - session->socket_fd = sock; - - session->socket_prev_blockstate = - !get_socket_nonblocking(session->socket_fd); - - if (session->socket_prev_blockstate) { - /* If in blocking state chang to non-blocking */ - session_nonblock(session->socket_fd, 1); - } - - session->startup_state = libssh2_NB_state_created; - } - - if (session->startup_state == libssh2_NB_state_created) { - rc = banner_send(session); - if (rc) { - return _libssh2_error(session, rc, - "Failed sending banner"); - } - session->startup_state = libssh2_NB_state_sent; - session->banner_TxRx_state = libssh2_NB_state_idle; - } - - if (session->startup_state == libssh2_NB_state_sent) { - do { - rc = banner_receive(session); - if (rc) - return _libssh2_error(session, rc, - "Failed getting banner"); - } while(strncmp("SSH-", (char *)session->remote.banner, 4)); - - session->startup_state = libssh2_NB_state_sent1; - } - - if (session->startup_state == libssh2_NB_state_sent1) { - rc = _libssh2_kex_exchange(session, 0, &session->startup_key_state); - if (rc) - return _libssh2_error(session, rc, - "Unable to exchange encryption keys"); - - session->startup_state = libssh2_NB_state_sent2; - } - - if (session->startup_state == libssh2_NB_state_sent2) { - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Requesting userauth service"); - - /* Request the userauth service */ - session->startup_service[0] = SSH_MSG_SERVICE_REQUEST; - _libssh2_htonu32(session->startup_service + 1, - sizeof("ssh-userauth") - 1); - memcpy(session->startup_service + 5, "ssh-userauth", - sizeof("ssh-userauth") - 1); - - session->startup_state = libssh2_NB_state_sent3; - } - - if (session->startup_state == libssh2_NB_state_sent3) { - rc = _libssh2_transport_send(session, session->startup_service, - sizeof("ssh-userauth") + 5 - 1, - NULL, 0); - if (rc) { - return _libssh2_error(session, rc, - "Unable to ask for ssh-userauth service"); - } - - session->startup_state = libssh2_NB_state_sent4; - } - - if (session->startup_state == libssh2_NB_state_sent4) { - rc = _libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, - &session->startup_data, - &session->startup_data_len, 0, NULL, 0, - &session->startup_req_state); - if (rc) - return rc; - - session->startup_service_length = - _libssh2_ntohu32(session->startup_data + 1); - - if ((session->startup_service_length != (sizeof("ssh-userauth") - 1)) - || strncmp("ssh-userauth", (char *) session->startup_data + 5, - session->startup_service_length)) { - LIBSSH2_FREE(session, session->startup_data); - session->startup_data = NULL; - return _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Invalid response received from server"); - } - LIBSSH2_FREE(session, session->startup_data); - session->startup_data = NULL; - - session->startup_state = libssh2_NB_state_idle; - - return 0; - } - - /* just for safety return some error */ - return LIBSSH2_ERROR_INVAL; -} - -/* - * libssh2_session_handshake() - * - * session: LIBSSH2_SESSION struct allocated and owned by the calling program - * sock: *must* be populated with an opened and connected socket. - * - * Returns: 0 on success, or non-zero on failure - */ -LIBSSH2_API int -libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock) -{ - int rc; - - BLOCK_ADJUST(rc, session, session_startup(session, sock) ); - - return rc; -} - -/* - * libssh2_session_startup() - * - * DEPRECATED. Use libssh2_session_handshake() instead! This function is not - * portable enough. - * - * session: LIBSSH2_SESSION struct allocated and owned by the calling program - * sock: *must* be populated with an opened and connected socket. - * - * Returns: 0 on success, or non-zero on failure - */ -LIBSSH2_API int -libssh2_session_startup(LIBSSH2_SESSION *session, int sock) -{ - return libssh2_session_handshake(session, (libssh2_socket_t) sock); -} - -/* - * libssh2_session_free - * - * Frees the memory allocated to the session - * Also closes and frees any channels attached to this session - */ -static int -session_free(LIBSSH2_SESSION *session) -{ - int rc; - LIBSSH2_PACKET *pkg; - LIBSSH2_CHANNEL *ch; - LIBSSH2_LISTENER *l; - int packets_left = 0; - - if (session->free_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Freeing session resource", - session->remote.banner); - - session->state = libssh2_NB_state_created; - } - - if (session->free_state == libssh2_NB_state_created) { - while ((ch = _libssh2_list_first(&session->channels))) { - - rc = _libssh2_channel_free(ch); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - - session->state = libssh2_NB_state_sent; - } - - if (session->state == libssh2_NB_state_sent) { - while ((l = _libssh2_list_first(&session->listeners))) { - rc = _libssh2_channel_forward_cancel(l); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - - session->state = libssh2_NB_state_sent1; - } - - if (session->state & LIBSSH2_STATE_NEWKEYS) { - /* hostkey */ - if (session->hostkey && session->hostkey->dtor) { - session->hostkey->dtor(session, &session->server_hostkey_abstract); - } - - /* Client to Server */ - /* crypt */ - if (session->local.crypt && session->local.crypt->dtor) { - session->local.crypt->dtor(session, - &session->local.crypt_abstract); - } - /* comp */ - if (session->local.comp && session->local.comp->dtor) { - session->local.comp->dtor(session, 1, - &session->local.comp_abstract); - } - /* mac */ - if (session->local.mac && session->local.mac->dtor) { - session->local.mac->dtor(session, &session->local.mac_abstract); - } - - /* Server to Client */ - /* crypt */ - if (session->remote.crypt && session->remote.crypt->dtor) { - session->remote.crypt->dtor(session, - &session->remote.crypt_abstract); - } - /* comp */ - if (session->remote.comp && session->remote.comp->dtor) { - session->remote.comp->dtor(session, 0, - &session->remote.comp_abstract); - } - /* mac */ - if (session->remote.mac && session->remote.mac->dtor) { - session->remote.mac->dtor(session, &session->remote.mac_abstract); - } - - /* session_id */ - if (session->session_id) { - LIBSSH2_FREE(session, session->session_id); - } - } - - /* Free banner(s) */ - if (session->remote.banner) { - LIBSSH2_FREE(session, session->remote.banner); - } - if (session->local.banner) { - LIBSSH2_FREE(session, session->local.banner); - } - - /* Free preference(s) */ - if (session->kex_prefs) { - LIBSSH2_FREE(session, session->kex_prefs); - } - if (session->hostkey_prefs) { - LIBSSH2_FREE(session, session->hostkey_prefs); - } - - if (session->local.kexinit) { - LIBSSH2_FREE(session, session->local.kexinit); - } - if (session->local.crypt_prefs) { - LIBSSH2_FREE(session, session->local.crypt_prefs); - } - if (session->local.mac_prefs) { - LIBSSH2_FREE(session, session->local.mac_prefs); - } - if (session->local.comp_prefs) { - LIBSSH2_FREE(session, session->local.comp_prefs); - } - if (session->local.lang_prefs) { - LIBSSH2_FREE(session, session->local.lang_prefs); - } - - if (session->remote.kexinit) { - LIBSSH2_FREE(session, session->remote.kexinit); - } - if (session->remote.crypt_prefs) { - LIBSSH2_FREE(session, session->remote.crypt_prefs); - } - if (session->remote.mac_prefs) { - LIBSSH2_FREE(session, session->remote.mac_prefs); - } - if (session->remote.comp_prefs) { - LIBSSH2_FREE(session, session->remote.comp_prefs); - } - if (session->remote.lang_prefs) { - LIBSSH2_FREE(session, session->remote.lang_prefs); - } - - /* - * Make sure all memory used in the state variables are free - */ - if (session->kexinit_data) { - LIBSSH2_FREE(session, session->kexinit_data); - } - if (session->startup_data) { - LIBSSH2_FREE(session, session->startup_data); - } - if (session->userauth_list_data) { - LIBSSH2_FREE(session, session->userauth_list_data); - } - if (session->userauth_pswd_data) { - LIBSSH2_FREE(session, session->userauth_pswd_data); - } - if (session->userauth_pswd_newpw) { - LIBSSH2_FREE(session, session->userauth_pswd_newpw); - } - if (session->userauth_host_packet) { - LIBSSH2_FREE(session, session->userauth_host_packet); - } - if (session->userauth_host_method) { - LIBSSH2_FREE(session, session->userauth_host_method); - } - if (session->userauth_host_data) { - LIBSSH2_FREE(session, session->userauth_host_data); - } - if (session->userauth_pblc_data) { - LIBSSH2_FREE(session, session->userauth_pblc_data); - } - if (session->userauth_pblc_packet) { - LIBSSH2_FREE(session, session->userauth_pblc_packet); - } - if (session->userauth_pblc_method) { - LIBSSH2_FREE(session, session->userauth_pblc_method); - } - if (session->userauth_kybd_data) { - LIBSSH2_FREE(session, session->userauth_kybd_data); - } - if (session->userauth_kybd_packet) { - LIBSSH2_FREE(session, session->userauth_kybd_packet); - } - if (session->userauth_kybd_auth_instruction) { - LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction); - } - if (session->open_packet) { - LIBSSH2_FREE(session, session->open_packet); - } - if (session->open_data) { - LIBSSH2_FREE(session, session->open_data); - } - if (session->direct_message) { - LIBSSH2_FREE(session, session->direct_message); - } - if (session->fwdLstn_packet) { - LIBSSH2_FREE(session, session->fwdLstn_packet); - } - if (session->pkeyInit_data) { - LIBSSH2_FREE(session, session->pkeyInit_data); - } - if (session->scpRecv_command) { - LIBSSH2_FREE(session, session->scpRecv_command); - } - if (session->scpSend_command) { - LIBSSH2_FREE(session, session->scpSend_command); - } - - /* Cleanup all remaining packets */ - while ((pkg = _libssh2_list_first(&session->packets))) { - packets_left++; - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "packet left with id %d", pkg->data[0]); - /* unlink the node */ - _libssh2_list_remove(&pkg->node); - - /* free */ - LIBSSH2_FREE(session, pkg->data); - LIBSSH2_FREE(session, pkg); - } - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Extra packets left %d", packets_left); - - if(session->socket_prev_blockstate) - /* if the socket was previously blocking, put it back so */ - session_nonblock(session->socket_fd, 0); - - if (session->server_hostkey) { - LIBSSH2_FREE(session, session->server_hostkey); - } - - LIBSSH2_FREE(session, session); - - return 0; -} - -/* - * libssh2_session_free - * - * Frees the memory allocated to the session - * Also closes and frees any channels attached to this session - */ -LIBSSH2_API int -libssh2_session_free(LIBSSH2_SESSION * session) -{ - int rc; - - BLOCK_ADJUST(rc, session, session_free(session) ); - - return rc; -} - -/* - * libssh2_session_disconnect_ex - */ -static int -session_disconnect(LIBSSH2_SESSION *session, int reason, - const char *description, - const char *lang) -{ - unsigned char *s; - unsigned long descr_len = 0, lang_len = 0; - int rc; - - if (session->disconnect_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, - "Disconnecting: reason=%d, desc=%s, lang=%s", reason, - description, lang); - if (description) - descr_len = strlen(description); - - if (lang) - lang_len = strlen(lang); - - if(descr_len > 256) - return _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "too long description"); - - /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ - session->disconnect_data_len = descr_len + lang_len + 13; - - s = session->disconnect_data; - - *(s++) = SSH_MSG_DISCONNECT; - _libssh2_store_u32(&s, reason); - _libssh2_store_str(&s, description, descr_len); - /* store length only, lang is sent separately */ - _libssh2_store_u32(&s, lang_len); - - session->disconnect_state = libssh2_NB_state_created; - } - - rc = _libssh2_transport_send(session, session->disconnect_data, - session->disconnect_data_len, - (unsigned char *)lang, lang_len); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - - session->disconnect_state = libssh2_NB_state_idle; - - return 0; -} - -/* - * libssh2_session_disconnect_ex - */ -LIBSSH2_API int -libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, - const char *desc, const char *lang) -{ - int rc; - - BLOCK_ADJUST(rc, session, - session_disconnect(session, reason, desc, lang)); - - return rc; -} - -/* libssh2_session_methods - * - * Return the currently active methods for method_type - * - * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string - * regardless of actual negotiation Strings should NOT be freed - */ -LIBSSH2_API const char * -libssh2_session_methods(LIBSSH2_SESSION * session, int method_type) -{ - /* All methods have char *name as their first element */ - const LIBSSH2_KEX_METHOD *method = NULL; - - switch (method_type) { - case LIBSSH2_METHOD_KEX: - method = session->kex; - break; - - case LIBSSH2_METHOD_HOSTKEY: - method = (LIBSSH2_KEX_METHOD *) session->hostkey; - break; - - case LIBSSH2_METHOD_CRYPT_CS: - method = (LIBSSH2_KEX_METHOD *) session->local.crypt; - break; - - case LIBSSH2_METHOD_CRYPT_SC: - method = (LIBSSH2_KEX_METHOD *) session->remote.crypt; - break; - - case LIBSSH2_METHOD_MAC_CS: - method = (LIBSSH2_KEX_METHOD *) session->local.mac; - break; - - case LIBSSH2_METHOD_MAC_SC: - method = (LIBSSH2_KEX_METHOD *) session->remote.mac; - break; - - case LIBSSH2_METHOD_COMP_CS: - method = (LIBSSH2_KEX_METHOD *) session->local.comp; - break; - - case LIBSSH2_METHOD_COMP_SC: - method = (LIBSSH2_KEX_METHOD *) session->remote.comp; - break; - - case LIBSSH2_METHOD_LANG_CS: - return ""; - - case LIBSSH2_METHOD_LANG_SC: - return ""; - - default: - _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "Invalid parameter specified for method_type"); - return NULL; - } - - if (!method) { - _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, - "No method negotiated"); - return NULL; - } - - return method->name; -} - -/* libssh2_session_abstract - * Retrieve a pointer to the abstract property - */ -LIBSSH2_API void ** -libssh2_session_abstract(LIBSSH2_SESSION * session) -{ - return &session->abstract; -} - -/* libssh2_session_last_error - * - * Returns error code and populates an error string into errmsg If want_buf is - * non-zero then the string placed into errmsg must be freed by the calling - * program. Otherwise it is assumed to be owned by libssh2 - */ -LIBSSH2_API int -libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg, - int *errmsg_len, int want_buf) -{ - size_t msglen = 0; - - /* No error to report */ - if (!session->err_code) { - if (errmsg) { - if (want_buf) { - *errmsg = LIBSSH2_ALLOC(session, 1); - if (*errmsg) { - **errmsg = 0; - } - } else { - *errmsg = (char *) ""; - } - } - if (errmsg_len) { - *errmsg_len = 0; - } - return 0; - } - - if (errmsg) { - const char *error = session->err_msg ? session->err_msg : ""; - - msglen = strlen(error); - - if (want_buf) { - /* Make a copy so the calling program can own it */ - *errmsg = LIBSSH2_ALLOC(session, msglen + 1); - if (*errmsg) { - memcpy(*errmsg, error, msglen); - (*errmsg)[msglen] = 0; - } - } - else - *errmsg = (char *)error; - } - - if (errmsg_len) { - *errmsg_len = msglen; - } - - return session->err_code; -} - -/* libssh2_session_last_errno - * - * Returns error code - */ -LIBSSH2_API int -libssh2_session_last_errno(LIBSSH2_SESSION * session) -{ - return session->err_code; -} - -/* libssh2_session_flag - * - * Set/Get session flags - * - * Return error code. - */ -LIBSSH2_API int -libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value) -{ - switch(flag) { - case LIBSSH2_FLAG_SIGPIPE: - session->flag.sigpipe = value; - break; - case LIBSSH2_FLAG_COMPRESS: - session->flag.compress = value; - break; - default: - /* unknown flag */ - return LIBSSH2_ERROR_INVAL; - } - - return LIBSSH2_ERROR_NONE; -} - -/* _libssh2_session_set_blocking - * - * Set a session's blocking mode on or off, return the previous status when - * this function is called. Note this function does not alter the state of the - * actual socket involved. - */ -int -_libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking) -{ - int bl = session->api_block_mode; - _libssh2_debug(session, LIBSSH2_TRACE_CONN, - "Setting blocking mode %s", blocking?"ON":"OFF"); - session->api_block_mode = blocking; - - return bl; -} - -/* libssh2_session_set_blocking - * - * Set a channel's blocking mode on or off, similar to a socket's - * fcntl(fd, F_SETFL, O_NONBLOCK); type command - */ -LIBSSH2_API void -libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking) -{ - (void) _libssh2_session_set_blocking(session, blocking); -} - -/* libssh2_session_get_blocking - * - * Returns a session's blocking mode on or off - */ -LIBSSH2_API int -libssh2_session_get_blocking(LIBSSH2_SESSION * session) -{ - return session->api_block_mode; -} - - -/* libssh2_session_set_timeout - * - * Set a session's timeout (in msec) for blocking mode, - * or 0 to disable timeouts. - */ -LIBSSH2_API void -libssh2_session_set_timeout(LIBSSH2_SESSION * session, long timeout) -{ - session->api_timeout = timeout; -} - -/* libssh2_session_get_timeout - * - * Returns a session's timeout, or 0 if disabled - */ -LIBSSH2_API long -libssh2_session_get_timeout(LIBSSH2_SESSION * session) -{ - return session->api_timeout; -} - -/* - * libssh2_poll_channel_read - * - * Returns 0 if no data is waiting on channel, - * non-0 if data is available - */ -LIBSSH2_API int -libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended) -{ - LIBSSH2_SESSION *session; - LIBSSH2_PACKET *packet; - - if(!channel) - return LIBSSH2_ERROR_BAD_USE; - - session = channel->session; - packet = _libssh2_list_first(&session->packets); - - while (packet) { - if ( channel->local.id == _libssh2_ntohu32(packet->data + 1)) { - if ( extended == 1 && - (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA - || packet->data[0] == SSH_MSG_CHANNEL_DATA )) { - return 1; - } else if ( extended == 0 && - packet->data[0] == SSH_MSG_CHANNEL_DATA) { - return 1; - } - /* else - no data of any type is ready to be read */ - } - packet = _libssh2_list_next(&packet->node); - } - - return 0; -} - -/* - * poll_channel_write - * - * Returns 0 if writing to channel would block, - * non-0 if data can be written without blocking - */ -static inline int -poll_channel_write(LIBSSH2_CHANNEL * channel) -{ - return channel->local.window_size ? 1 : 0; -} - -/* poll_listener_queued - * - * Returns 0 if no connections are waiting to be accepted - * non-0 if one or more connections are available - */ -static inline int -poll_listener_queued(LIBSSH2_LISTENER * listener) -{ - return _libssh2_list_first(&listener->queue) ? 1 : 0; -} - -/* - * libssh2_poll - * - * Poll sockets, channels, and listeners for activity - */ -LIBSSH2_API int -libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) -{ - long timeout_remaining; - unsigned int i, active_fds; -#ifdef HAVE_POLL - LIBSSH2_SESSION *session = NULL; -#ifdef HAVE_ALLOCA - struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds); -#else - struct pollfd sockets[256]; - - if (nfds > 256) - /* systems without alloca use a fixed-size array, this can be fixed if - we really want to, at least if the compiler is a C99 capable one */ - return -1; -#endif - /* Setup sockets for polling */ - for(i = 0; i < nfds; i++) { - fds[i].revents = 0; - switch (fds[i].type) { - case LIBSSH2_POLLFD_SOCKET: - sockets[i].fd = fds[i].fd.socket; - sockets[i].events = fds[i].events; - sockets[i].revents = 0; - break; - - case LIBSSH2_POLLFD_CHANNEL: - sockets[i].fd = fds[i].fd.channel->session->socket_fd; - sockets[i].events = POLLIN; - sockets[i].revents = 0; - if (!session) - session = fds[i].fd.channel->session; - break; - - case LIBSSH2_POLLFD_LISTENER: - sockets[i].fd = fds[i].fd.listener->session->socket_fd; - sockets[i].events = POLLIN; - sockets[i].revents = 0; - if (!session) - session = fds[i].fd.listener->session; - break; - - default: - if (session) - _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, - "Invalid descriptor passed to libssh2_poll()"); - return -1; - } - } -#elif defined(HAVE_SELECT) - LIBSSH2_SESSION *session = NULL; - libssh2_socket_t maxfd = 0; - fd_set rfds, wfds; - struct timeval tv; - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - for(i = 0; i < nfds; i++) { - fds[i].revents = 0; - switch (fds[i].type) { - case LIBSSH2_POLLFD_SOCKET: - if (fds[i].events & LIBSSH2_POLLFD_POLLIN) { - FD_SET(fds[i].fd.socket, &rfds); - if (fds[i].fd.socket > maxfd) - maxfd = fds[i].fd.socket; - } - if (fds[i].events & LIBSSH2_POLLFD_POLLOUT) { - FD_SET(fds[i].fd.socket, &wfds); - if (fds[i].fd.socket > maxfd) - maxfd = fds[i].fd.socket; - } - break; - - case LIBSSH2_POLLFD_CHANNEL: - FD_SET(fds[i].fd.channel->session->socket_fd, &rfds); - if (fds[i].fd.channel->session->socket_fd > maxfd) - maxfd = fds[i].fd.channel->session->socket_fd; - if (!session) - session = fds[i].fd.channel->session; - break; - - case LIBSSH2_POLLFD_LISTENER: - FD_SET(fds[i].fd.listener->session->socket_fd, &rfds); - if (fds[i].fd.listener->session->socket_fd > maxfd) - maxfd = fds[i].fd.listener->session->socket_fd; - if (!session) - session = fds[i].fd.listener->session; - break; - - default: - if (session) - _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, - "Invalid descriptor passed to libssh2_poll()"); - return -1; - } - } -#else - /* No select() or poll() - * no sockets sturcture to setup - */ - - timeout = 0; -#endif /* HAVE_POLL or HAVE_SELECT */ - - timeout_remaining = timeout; - do { -#if defined(HAVE_POLL) || defined(HAVE_SELECT) - int sysret; -#endif - - active_fds = 0; - - for(i = 0; i < nfds; i++) { - if (fds[i].events != fds[i].revents) { - switch (fds[i].type) { - case LIBSSH2_POLLFD_CHANNEL: - if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && - /* Want to be ready for read */ - ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { - /* Not yet known to be ready for read */ - fds[i].revents |= - libssh2_poll_channel_read(fds[i].fd.channel, - 0) ? - LIBSSH2_POLLFD_POLLIN : 0; - } - if ((fds[i].events & LIBSSH2_POLLFD_POLLEXT) && - /* Want to be ready for extended read */ - ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) { - /* Not yet known to be ready for extended read */ - fds[i].revents |= - libssh2_poll_channel_read(fds[i].fd.channel, - 1) ? - LIBSSH2_POLLFD_POLLEXT : 0; - } - if ((fds[i].events & LIBSSH2_POLLFD_POLLOUT) && - /* Want to be ready for write */ - ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { - /* Not yet known to be ready for write */ - fds[i].revents |= - poll_channel_write(fds[i].fd. channel) ? - LIBSSH2_POLLFD_POLLOUT : 0; - } - if (fds[i].fd.channel->remote.close - || fds[i].fd.channel->local.close) { - fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED; - } - if (fds[i].fd.channel->session->socket_state == - LIBSSH2_SOCKET_DISCONNECTED) { - fds[i].revents |= - LIBSSH2_POLLFD_CHANNEL_CLOSED | - LIBSSH2_POLLFD_SESSION_CLOSED; - } - break; - - case LIBSSH2_POLLFD_LISTENER: - if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && - /* Want a connection */ - ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { - /* No connections known of yet */ - fds[i].revents |= - poll_listener_queued(fds[i].fd. listener) ? - LIBSSH2_POLLFD_POLLIN : 0; - } - if (fds[i].fd.listener->session->socket_state == - LIBSSH2_SOCKET_DISCONNECTED) { - fds[i].revents |= - LIBSSH2_POLLFD_LISTENER_CLOSED | - LIBSSH2_POLLFD_SESSION_CLOSED; - } - break; - } - } - if (fds[i].revents) { - active_fds++; - } - } - - if (active_fds) { - /* Don't block on the sockets if we have channels/listeners which - are ready */ - timeout_remaining = 0; - } -#ifdef HAVE_POLL - -#ifdef HAVE_LIBSSH2_GETTIMEOFDAY - { - struct timeval tv_begin, tv_end; - - _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL); - sysret = poll(sockets, nfds, timeout_remaining); - _libssh2_gettimeofday((struct timeval *) &tv_end, NULL); - timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; - timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; - } -#else - /* If the platform doesn't support gettimeofday, - * then just make the call non-blocking and walk away - */ - sysret = poll(sockets, nfds, timeout_remaining); - timeout_remaining = 0; -#endif /* HAVE_GETTIMEOFDAY */ - - if (sysret > 0) { - for(i = 0; i < nfds; i++) { - switch (fds[i].type) { - case LIBSSH2_POLLFD_SOCKET: - fds[i].revents = sockets[i].revents; - sockets[i].revents = 0; /* In case we loop again, be nice */ - if (fds[i].revents) { - active_fds++; - } - break; - case LIBSSH2_POLLFD_CHANNEL: - if (sockets[i].events & POLLIN) { - /* Spin session until no data available */ - while (_libssh2_transport_read(fds[i].fd.channel->session) - > 0); - } - if (sockets[i].revents & POLLHUP) { - fds[i].revents |= - LIBSSH2_POLLFD_CHANNEL_CLOSED | - LIBSSH2_POLLFD_SESSION_CLOSED; - } - sockets[i].revents = 0; - break; - case LIBSSH2_POLLFD_LISTENER: - if (sockets[i].events & POLLIN) { - /* Spin session until no data available */ - while (_libssh2_transport_read(fds[i].fd.listener->session) - > 0); - } - if (sockets[i].revents & POLLHUP) { - fds[i].revents |= - LIBSSH2_POLLFD_LISTENER_CLOSED | - LIBSSH2_POLLFD_SESSION_CLOSED; - } - sockets[i].revents = 0; - break; - } - } - } -#elif defined(HAVE_SELECT) - tv.tv_sec = timeout_remaining / 1000; - tv.tv_usec = (timeout_remaining % 1000) * 1000; -#ifdef HAVE_LIBSSH2_GETTIMEOFDAY - { - struct timeval tv_begin, tv_end; - - _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL); - sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv); - _libssh2_gettimeofday((struct timeval *) &tv_end, NULL); - - timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; - timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; - } -#else - /* If the platform doesn't support gettimeofday, - * then just make the call non-blocking and walk away - */ - sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv); - timeout_remaining = 0; -#endif - - if (sysret > 0) { - for(i = 0; i < nfds; i++) { - switch (fds[i].type) { - case LIBSSH2_POLLFD_SOCKET: - if (FD_ISSET(fds[i].fd.socket, &rfds)) { - fds[i].revents |= LIBSSH2_POLLFD_POLLIN; - } - if (FD_ISSET(fds[i].fd.socket, &wfds)) { - fds[i].revents |= LIBSSH2_POLLFD_POLLOUT; - } - if (fds[i].revents) { - active_fds++; - } - break; - - case LIBSSH2_POLLFD_CHANNEL: - if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) { - /* Spin session until no data available */ - while (_libssh2_transport_read(fds[i].fd.channel->session) - > 0); - } - break; - - case LIBSSH2_POLLFD_LISTENER: - if (FD_ISSET - (fds[i].fd.listener->session->socket_fd, &rfds)) { - /* Spin session until no data available */ - while (_libssh2_transport_read(fds[i].fd.listener->session) - > 0); - } - break; - } - } - } -#endif /* else no select() or poll() -- timeout (and by extension - * timeout_remaining) will be equal to 0 */ - } while ((timeout_remaining > 0) && !active_fds); - - return active_fds; -} - -/* - * libssh2_session_block_directions - * - * Get blocked direction when a function returns LIBSSH2_ERROR_EAGAIN - * Returns LIBSSH2_SOCKET_BLOCK_INBOUND if recv() blocked - * or LIBSSH2_SOCKET_BLOCK_OUTBOUND if send() blocked - */ -LIBSSH2_API int -libssh2_session_block_directions(LIBSSH2_SESSION *session) -{ - return session->socket_block_directions; -} - -/* libssh2_session_banner_get - * Get the remote banner (server ID string) - */ - -LIBSSH2_API const char * -libssh2_session_banner_get(LIBSSH2_SESSION *session) -{ - /* to avoid a coredump when session is NULL */ - if (NULL == session) - return NULL; - - if (NULL==session->remote.banner) - return NULL; - - return (const char *) session->remote.banner; -} diff --git a/vendor/libssh2-1.4.2/src/session.h b/vendor/libssh2-1.4.2/src/session.h deleted file mode 100644 index aff4f2c5c..000000000 --- a/vendor/libssh2-1.4.2/src/session.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef LIBSSH2_SESSION_H -#define LIBSSH2_SESSION_H -/* Copyright (c) 2004-2007 Sara Golemon - * Copyright (c) 2009-2010 by Daniel Stenberg - * Copyright (c) 2010 Simon Josefsson - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -/* Conveniance-macros to allow code like this; - - int rc = BLOCK_ADJUST(rc, session, session_startup(session, sock) ); - - int rc = BLOCK_ADJUST_ERRNO(ptr, session, session_startup(session, sock) ); - - The point of course being to make sure that while in non-blocking mode - these always return no matter what the return code is, but in blocking mode - it blocks if EAGAIN is the reason for the return from the underlying - function. - -*/ -#define BLOCK_ADJUST(rc,sess,x) \ - do { \ - time_t entry_time = time (NULL); \ - do { \ - rc = x; \ - /* the order of the check below is important to properly deal with \ - the case when the 'sess' is freed */ \ - if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \ - break; \ - rc = _libssh2_wait_socket(sess, entry_time); \ - } while(!rc); \ - } while(0) - -/* - * For functions that returns a pointer, we need to check if the API is - * non-blocking and return immediately. If the pointer is non-NULL we return - * immediately. If the API is blocking and we get a NULL we check the errno - * and *only* if that is EAGAIN we loop and wait for socket action. - */ -#define BLOCK_ADJUST_ERRNO(ptr,sess,x) \ - do { \ - time_t entry_time = time (NULL); \ - int rc; \ - do { \ - ptr = x; \ - if(!sess->api_block_mode || \ - (ptr != NULL) || \ - (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \ - break; \ - rc = _libssh2_wait_socket(sess, entry_time); \ - } while(!rc); \ - } while(0) - - -int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t entry_time); - -/* this is the lib-internal set blocking function */ -int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking); - -#endif /* LIBSSH2_SESSION_H */ diff --git a/vendor/libssh2-1.4.2/src/sftp.c b/vendor/libssh2-1.4.2/src/sftp.c deleted file mode 100644 index ec9d0338b..000000000 --- a/vendor/libssh2-1.4.2/src/sftp.c +++ /dev/null @@ -1,3278 +0,0 @@ -/* Copyright (c) 2004-2008, Sara Golemon - * Copyright (c) 2007 Eli Fant - * Copyright (c) 2009-2012 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include - -#include "libssh2_priv.h" -#include "libssh2_sftp.h" -#include "channel.h" -#include "session.h" -#include "sftp.h" - -/* Note: Version 6 was documented at the time of writing - * However it was marked as "DO NOT IMPLEMENT" due to pending changes - * - * This release of libssh2 implements Version 5 with automatic downgrade - * based on server's declaration - */ - -/* SFTP packet types */ -#define SSH_FXP_INIT 1 -#define SSH_FXP_VERSION 2 -#define SSH_FXP_OPEN 3 -#define SSH_FXP_CLOSE 4 -#define SSH_FXP_READ 5 -#define SSH_FXP_WRITE 6 -#define SSH_FXP_LSTAT 7 -#define SSH_FXP_FSTAT 8 -#define SSH_FXP_SETSTAT 9 -#define SSH_FXP_FSETSTAT 10 -#define SSH_FXP_OPENDIR 11 -#define SSH_FXP_READDIR 12 -#define SSH_FXP_REMOVE 13 -#define SSH_FXP_MKDIR 14 -#define SSH_FXP_RMDIR 15 -#define SSH_FXP_REALPATH 16 -#define SSH_FXP_STAT 17 -#define SSH_FXP_RENAME 18 -#define SSH_FXP_READLINK 19 -#define SSH_FXP_SYMLINK 20 -#define SSH_FXP_STATUS 101 -#define SSH_FXP_HANDLE 102 -#define SSH_FXP_DATA 103 -#define SSH_FXP_NAME 104 -#define SSH_FXP_ATTRS 105 -#define SSH_FXP_EXTENDED 200 -#define SSH_FXP_EXTENDED_REPLY 201 - -/* S_IFREG */ -#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000 -/* S_IFDIR */ -#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000 - -#define SSH_FXE_STATVFS_ST_RDONLY 0x00000001 -#define SSH_FXE_STATVFS_ST_NOSUID 0x00000002 - -/* This is the maximum packet length to accept, as larger than this indicate - some kind of server problem. */ -#define LIBSSH2_SFTP_PACKET_MAXLEN 80000 - -static int sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle); -static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type, - uint32_t request_id, unsigned char **data, - size_t *data_len); -static void sftp_packet_flush(LIBSSH2_SFTP *sftp); - -/* sftp_attrsize - * Size that attr with this flagset will occupy when turned into a bin struct - */ -static int sftp_attrsize(unsigned long flags) -{ - return (4 + /* flags(4) */ - ((flags & LIBSSH2_SFTP_ATTR_SIZE) ? 8 : 0) + - ((flags & LIBSSH2_SFTP_ATTR_UIDGID) ? 8 : 0) + - ((flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) ? 4 : 0) + - ((flags & LIBSSH2_SFTP_ATTR_ACMODTIME) ? 8 : 0)); - /* atime + mtime as u32 */ -} - -/* _libssh2_store_u64 - */ -static void _libssh2_store_u64(unsigned char **ptr, libssh2_uint64_t value) -{ - uint32_t msl = (uint32_t)(value >> 32); - unsigned char *buf = *ptr; - - buf[0] = (unsigned char)((msl >> 24) & 0xFF); - buf[1] = (unsigned char)((msl >> 16) & 0xFF); - buf[2] = (unsigned char)((msl >> 8) & 0xFF); - buf[3] = (unsigned char)( msl & 0xFF); - - buf[4] = (unsigned char)((value >> 24) & 0xFF); - buf[5] = (unsigned char)((value >> 16) & 0xFF); - buf[6] = (unsigned char)((value >> 8) & 0xFF); - buf[7] = (unsigned char)( value & 0xFF); - - *ptr += 8; -} - -/* - * Search list of zombied FXP_READ request IDs. - * - * Returns NULL if ID not in list. - */ -static struct sftp_zombie_requests * -find_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id) -{ - struct sftp_zombie_requests *zombie = - _libssh2_list_first(&sftp->zombie_requests); - - while(zombie) { - if(zombie->request_id == request_id) - break; - else - zombie = _libssh2_list_next(&zombie->node); - } - - return zombie; -} - -static void -remove_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id) -{ - LIBSSH2_SESSION *session = sftp->channel->session; - - struct sftp_zombie_requests *zombie = find_zombie_request(sftp, - request_id); - if(zombie) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Removing request ID %ld from the list of zombie requests", - request_id); - - _libssh2_list_remove(&zombie->node); - LIBSSH2_FREE(session, zombie); - } -} - -static int -add_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id) -{ - LIBSSH2_SESSION *session = sftp->channel->session; - - struct sftp_zombie_requests *zombie; - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Marking request ID %ld as a zombie request", request_id); - - zombie = LIBSSH2_ALLOC(sftp->channel->session, - sizeof(struct sftp_zombie_requests)); - if (!zombie) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "malloc fail for zombie request ID"); - else { - zombie->request_id = request_id; - _libssh2_list_add(&sftp->zombie_requests, &zombie->node); - return LIBSSH2_ERROR_NONE; - } -} - -/* - * sftp_packet_add - * - * Add a packet to the SFTP packet brigade - */ -static int -sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, - size_t data_len) -{ - LIBSSH2_SESSION *session = sftp->channel->session; - LIBSSH2_SFTP_PACKET *packet; - uint32_t request_id; - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Received packet %d (len %d)", - (int) data[0], data_len); - - /* - * Experience shows that if we mess up EAGAIN handling somewhere or - * otherwise get out of sync with the channel, this is where we first get - * a wrong byte and if so we need to bail out at once to aid tracking the - * problem better. - */ - - switch(data[0]) { - case SSH_FXP_INIT: - case SSH_FXP_VERSION: - case SSH_FXP_OPEN: - case SSH_FXP_CLOSE: - case SSH_FXP_READ: - case SSH_FXP_WRITE: - case SSH_FXP_LSTAT: - case SSH_FXP_FSTAT: - case SSH_FXP_SETSTAT: - case SSH_FXP_FSETSTAT: - case SSH_FXP_OPENDIR: - case SSH_FXP_READDIR: - case SSH_FXP_REMOVE: - case SSH_FXP_MKDIR: - case SSH_FXP_RMDIR: - case SSH_FXP_REALPATH: - case SSH_FXP_STAT: - case SSH_FXP_RENAME: - case SSH_FXP_READLINK: - case SSH_FXP_SYMLINK: - case SSH_FXP_STATUS: - case SSH_FXP_HANDLE: - case SSH_FXP_DATA: - case SSH_FXP_NAME: - case SSH_FXP_ATTRS: - case SSH_FXP_EXTENDED: - case SSH_FXP_EXTENDED_REPLY: - break; - default: - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Out of sync with the world"); - } - - request_id = _libssh2_ntohu32(&data[1]); - - /* Don't add the packet if it answers a request we've given up on. */ - if((data[0] == SSH_FXP_STATUS || data[0] == SSH_FXP_DATA) - && find_zombie_request(sftp, request_id)) { - - /* If we get here, the file ended before the response arrived. We - are no longer interested in the request so we discard it */ - - LIBSSH2_FREE(session, data); - - remove_zombie_request(sftp, request_id); - return LIBSSH2_ERROR_NONE; - } - - packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_PACKET)); - if (!packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate datablock for SFTP packet"); - } - - packet->data = data; - packet->data_len = data_len; - packet->request_id = request_id; - - _libssh2_list_add(&sftp->packets, &packet->node); - - return LIBSSH2_ERROR_NONE; -} - -/* - * sftp_packet_read - * - * Frame an SFTP packet off the channel - */ -static int -sftp_packet_read(LIBSSH2_SFTP *sftp) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned char *packet = NULL; - ssize_t rc; - unsigned long recv_window; - int packet_type; - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet"); - - switch(sftp->packet_state) { - case libssh2_NB_state_sent: /* EAGAIN from window adjusting */ - sftp->packet_state = libssh2_NB_state_idle; - - packet = sftp->partial_packet; - goto window_adjust; - - case libssh2_NB_state_sent1: /* EAGAIN from channel read */ - sftp->packet_state = libssh2_NB_state_idle; - - packet = sftp->partial_packet; - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "partial read cont, len: %lu", sftp->partial_len); - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "partial read cont, already recvd: %lu", - sftp->partial_received); - /* fall-through */ - default: - if(!packet) { - /* only do this if there's not already a packet buffer allocated - to use */ - - /* each packet starts with a 32 bit length field */ - rc = _libssh2_channel_read(channel, 0, - (char *)&sftp->partial_size[ - sftp->partial_size_len], - 4 - sftp->partial_size_len); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - else if (rc < 0) - return _libssh2_error(session, rc, "channel read"); - - sftp->partial_size_len += rc; - - if(4 != sftp->partial_size_len) - /* we got a short read for the length part */ - return LIBSSH2_ERROR_EAGAIN; - - sftp->partial_len = _libssh2_ntohu32(sftp->partial_size); - /* make sure we don't proceed if the packet size is unreasonably - large */ - if (sftp->partial_len > LIBSSH2_SFTP_PACKET_MAXLEN) - return _libssh2_error(session, - LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, - "SFTP packet too large"); - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Data begin - Packet Length: %lu", - sftp->partial_len); - packet = LIBSSH2_ALLOC(session, sftp->partial_len); - if (!packet) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate SFTP packet"); - sftp->partial_size_len = 0; - sftp->partial_received = 0; /* how much of the packet already - received */ - sftp->partial_packet = packet; - - window_adjust: - recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL); - - if(sftp->partial_len > recv_window) { - /* ask for twice the data amount we need at once */ - rc = _libssh2_channel_receive_window_adjust(channel, - sftp->partial_len*2, - 1, NULL); - /* store the state so that we continue with the correct - operation at next invoke */ - sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN)? - libssh2_NB_state_sent: - libssh2_NB_state_idle; - - if(rc == LIBSSH2_ERROR_EAGAIN) - return rc; - } - } - - /* Read as much of the packet as we can */ - while (sftp->partial_len > sftp->partial_received) { - rc = _libssh2_channel_read(channel, 0, - (char *)&packet[sftp->partial_received], - sftp->partial_len - - sftp->partial_received); - - if (rc == LIBSSH2_ERROR_EAGAIN) { - /* - * We received EAGAIN, save what we have and return EAGAIN to - * the caller. Set 'partial_packet' so that this function - * knows how to continue on the next invoke. - */ - sftp->packet_state = libssh2_NB_state_sent1; - return rc; - } - else if (rc < 0) { - LIBSSH2_FREE(session, packet); - sftp->partial_packet = NULL; - return _libssh2_error(session, rc, - "Error waiting for SFTP packet"); - } - sftp->partial_received += rc; - } - - sftp->partial_packet = NULL; - - /* sftp_packet_add takes ownership of the packet and might free it - so we take a copy of the packet type before we call it. */ - packet_type = packet[0]; - rc = sftp_packet_add(sftp, packet, sftp->partial_len); - if (rc) { - LIBSSH2_FREE(session, packet); - return rc; - } - else { - return packet_type; - } - } - /* WON'T REACH */ -} -/* - * sftp_packetlist_flush - * - * Remove all pending packets in the packet_list and the corresponding one(s) - * in the SFTP packet brigade. - */ -static void sftp_packetlist_flush(LIBSSH2_SFTP_HANDLE *handle) -{ - struct sftp_pipeline_chunk *chunk; - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_SESSION *session = sftp->channel->session; - - /* remove pending packets, if any */ - chunk = _libssh2_list_first(&handle->packet_list); - while(chunk) { - unsigned char *data; - size_t data_len; - int rc; - struct sftp_pipeline_chunk *next = _libssh2_list_next(&chunk->node); - - rc = sftp_packet_ask(sftp, SSH_FXP_STATUS, - chunk->request_id, &data, &data_len); - if(rc) - rc = sftp_packet_ask(sftp, SSH_FXP_DATA, - chunk->request_id, &data, &data_len); - - if(!rc) - /* we found a packet, free it */ - LIBSSH2_FREE(session, data); - else if(chunk->sent) - /* there was no incoming packet for this request, mark this - request as a zombie if it ever sent the request */ - add_zombie_request(sftp, chunk->request_id); - - _libssh2_list_remove(&chunk->node); - LIBSSH2_FREE(session, chunk); - chunk = next; - } -} - - -/* - * sftp_packet_ask() - * - * Checks if there's a matching SFTP packet available. - */ -static int -sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type, - uint32_t request_id, unsigned char **data, - size_t *data_len) -{ - LIBSSH2_SESSION *session = sftp->channel->session; - LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets); - - if(!packet) - return -1; - - /* Special consideration when getting VERSION packet */ - - while (packet) { - if((packet->data[0] == packet_type) && - ((packet_type == SSH_FXP_VERSION) || - (packet->request_id == request_id))) { - - /* Match! Fetch the data */ - *data = packet->data; - *data_len = packet->data_len; - - /* unlink and free this struct */ - _libssh2_list_remove(&packet->node); - LIBSSH2_FREE(session, packet); - - return 0; - } - /* check next struct in the list */ - packet = _libssh2_list_next(&packet->node); - } - return -1; -} - -/* sftp_packet_require - * A la libssh2_packet_require - */ -static int -sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type, - uint32_t request_id, unsigned char **data, - size_t *data_len) -{ - LIBSSH2_SESSION *session = sftp->channel->session; - int rc; - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Requiring packet %d id %ld", - (int) packet_type, request_id); - - if (sftp_packet_ask(sftp, packet_type, request_id, data, data_len) == 0) { - /* The right packet was available in the packet brigade */ - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d", - (int) packet_type); - return LIBSSH2_ERROR_NONE; - } - - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - rc = sftp_packet_read(sftp); - if (rc < 0) - return rc; - - /* data was read, check the queue again */ - if (!sftp_packet_ask(sftp, packet_type, request_id, data, data_len)) { - /* The right packet was available in the packet brigade */ - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d", - (int) packet_type); - return LIBSSH2_ERROR_NONE; - } - } - - /* Only reached if the socket died */ - return LIBSSH2_ERROR_SOCKET_DISCONNECT; -} - -/* sftp_packet_requirev - * Require one of N possible reponses - */ -static int -sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses, - const unsigned char *valid_responses, - uint32_t request_id, unsigned char **data, - size_t *data_len) -{ - int i; - int rc; - - /* If no timeout is active, start a new one */ - if (sftp->requirev_start == 0) - sftp->requirev_start = time(NULL); - - while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - for(i = 0; i < num_valid_responses; i++) { - if (sftp_packet_ask(sftp, valid_responses[i], request_id, - data, data_len) == 0) { - /* - * Set to zero before all returns to say - * the timeout is not active - */ - sftp->requirev_start = 0; - return LIBSSH2_ERROR_NONE; - } - } - - rc = sftp_packet_read(sftp); - if ((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) { - sftp->requirev_start = 0; - return rc; - } else if (rc <= 0) { - /* prevent busy-looping */ - long left = - LIBSSH2_READ_TIMEOUT - (long)(time(NULL) - sftp->requirev_start); - - if (left <= 0) { - sftp->requirev_start = 0; - return LIBSSH2_ERROR_TIMEOUT; - } - else if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - } - } - - sftp->requirev_start = 0; - - /* Only reached if the socket died */ - return LIBSSH2_ERROR_SOCKET_DISCONNECT; -} - -/* sftp_attr2bin - * Populate attributes into an SFTP block - */ -static ssize_t -sftp_attr2bin(unsigned char *p, const LIBSSH2_SFTP_ATTRIBUTES * attrs) -{ - unsigned char *s = p; - uint32_t flag_mask = - LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID | - LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME; - - /* TODO: When we add SFTP4+ functionality flag_mask can get additional - bits */ - - if (!attrs) { - _libssh2_htonu32(s, 0); - return 4; - } - - _libssh2_store_u32(&s, attrs->flags & flag_mask); - - if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { - _libssh2_store_u64(&s, attrs->filesize); - } - - if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { - _libssh2_store_u32(&s, attrs->uid); - _libssh2_store_u32(&s, attrs->gid); - } - - if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - _libssh2_store_u32(&s, attrs->permissions); - } - - if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { - _libssh2_store_u32(&s, attrs->atime); - _libssh2_store_u32(&s, attrs->mtime); - } - - return (s - p); -} - -/* sftp_bin2attr - */ -static int -sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES * attrs, const unsigned char *p) -{ - const unsigned char *s = p; - - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - attrs->flags = _libssh2_ntohu32(s); - s += 4; - - if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { - attrs->filesize = _libssh2_ntohu64(s); - s += 8; - } - - if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { - attrs->uid = _libssh2_ntohu32(s); - s += 4; - attrs->gid = _libssh2_ntohu32(s); - s += 4; - } - - if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - attrs->permissions = _libssh2_ntohu32(s); - s += 4; - } - - if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { - attrs->atime = _libssh2_ntohu32(s); - s += 4; - attrs->mtime = _libssh2_ntohu32(s); - s += 4; - } - - return (s - p); -} - -/* ************ - * SFTP API * - ************ */ - -LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor); - -/* libssh2_sftp_dtor - * Shutdown an SFTP stream when the channel closes - */ -LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) -{ - LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP *) (*channel_abstract); - - (void) session_abstract; - (void) channel; - - /* Free the partial packet storage for sftp_packet_read */ - if (sftp->partial_packet) { - LIBSSH2_FREE(session, sftp->partial_packet); - } - - /* Free the packet storage for _libssh2_sftp_packet_readdir */ - if (sftp->readdir_packet) { - LIBSSH2_FREE(session, sftp->readdir_packet); - } - - LIBSSH2_FREE(session, sftp); -} - -/* - * sftp_init - * - * Startup an SFTP session - */ -static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session) -{ - unsigned char *data, *s; - size_t data_len; - ssize_t rc; - LIBSSH2_SFTP *sftp_handle; - - if (session->sftpInit_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Initializing SFTP subsystem"); - - /* - * The 'sftpInit_sftp' and 'sftpInit_channel' struct fields within the - * session struct are only to be used during the setup phase. As soon - * as the SFTP session is created they are cleared and can thus be - * re-used again to allow any amount of SFTP handles per sessions. - * - * Note that you MUST NOT try to call libssh2_sftp_init() again to get - * another handle until the previous call has finished and either - * succesffully made a handle or failed and returned error (not - * including *EAGAIN). - */ - - assert(session->sftpInit_sftp == NULL); - session->sftpInit_sftp = NULL; - session->sftpInit_state = libssh2_NB_state_created; - } - - sftp_handle = session->sftpInit_sftp; - - if (session->sftpInit_state == libssh2_NB_state_created) { - session->sftpInit_channel = - _libssh2_channel_open(session, "session", sizeof("session") - 1, - LIBSSH2_CHANNEL_WINDOW_DEFAULT, - LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); - if (!session->sftpInit_channel) { - if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block starting up channel"); - } - else { - _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, - "Unable to startup channel"); - session->sftpInit_state = libssh2_NB_state_idle; - } - return NULL; - } - - session->sftpInit_state = libssh2_NB_state_sent; - } - - if (session->sftpInit_state == libssh2_NB_state_sent) { - int ret = _libssh2_channel_process_startup(session->sftpInit_channel, - "subsystem", - sizeof("subsystem") - 1, "sftp", - strlen("sftp")); - if (ret == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block to request SFTP subsystem"); - return NULL; - } else if (ret) { - _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, - "Unable to request SFTP subsystem"); - goto sftp_init_error; - } - - session->sftpInit_state = libssh2_NB_state_sent1; - } - - if (session->sftpInit_state == libssh2_NB_state_sent1) { - rc = _libssh2_channel_extended_data(session->sftpInit_channel, - LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting handle extended data"); - return NULL; - } - - sftp_handle = - session->sftpInit_sftp = - LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); - if (!sftp_handle) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate a new SFTP structure"); - goto sftp_init_error; - } - memset(sftp_handle, 0, sizeof(LIBSSH2_SFTP)); - sftp_handle->channel = session->sftpInit_channel; - sftp_handle->request_id = 0; - - _libssh2_htonu32(session->sftpInit_buffer, 5); - session->sftpInit_buffer[4] = SSH_FXP_INIT; - _libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION); - session->sftpInit_sent = 0; /* nothing's sent yet */ - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Sending FXP_INIT packet advertising version %d support", - (int) LIBSSH2_SFTP_VERSION); - - session->sftpInit_state = libssh2_NB_state_sent2; - } - - if (session->sftpInit_state == libssh2_NB_state_sent2) { - /* sent off what's left of the init buffer to send */ - rc = _libssh2_channel_write(session->sftpInit_channel, 0, - session->sftpInit_buffer + - session->sftpInit_sent, - 9 - session->sftpInit_sent); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending SSH_FXP_INIT"); - return NULL; - } - else if(rc < 0) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send SSH_FXP_INIT"); - goto sftp_init_error; - } - else { - /* add up the number of bytes sent */ - session->sftpInit_sent += rc; - - if(session->sftpInit_sent == 9) - /* move on */ - session->sftpInit_state = libssh2_NB_state_sent3; - - /* if less than 9, we remain in this state to send more later on */ - } - } - - rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION, - 0, &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) - return NULL; - else if (rc) { - _libssh2_error(session, rc, - "Timeout waiting for response from SFTP subsystem"); - goto sftp_init_error; - } - if (data_len < 5) { - _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Invalid SSH_FXP_VERSION response"); - goto sftp_init_error; - } - - s = data + 1; - sftp_handle->version = _libssh2_ntohu32(s); - s += 4; - if (sftp_handle->version > LIBSSH2_SFTP_VERSION) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Truncating remote SFTP version from %lu", - sftp_handle->version); - sftp_handle->version = LIBSSH2_SFTP_VERSION; - } - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Enabling SFTP version %lu compatability", - sftp_handle->version); - while (s < (data + data_len)) { - size_t extname_len, extdata_len; - - extname_len = _libssh2_ntohu32(s); - s += 4; - /* the extension name starts here */ - s += extname_len; - - extdata_len = _libssh2_ntohu32(s); - s += 4; - - /* TODO: Actually process extensions */ - s += extdata_len; - - } - LIBSSH2_FREE(session, data); - - /* Make sure that when the channel gets closed, the SFTP service is shut - down too */ - sftp_handle->channel->abstract = sftp_handle; - sftp_handle->channel->close_cb = libssh2_sftp_dtor; - - session->sftpInit_state = libssh2_NB_state_idle; - - /* clear the sftp and channel pointers in this session struct now */ - session->sftpInit_sftp = NULL; - session->sftpInit_channel = NULL; - - _libssh2_list_init(&sftp_handle->sftp_handles); - - return sftp_handle; - - sftp_init_error: - while (_libssh2_channel_free(session->sftpInit_channel) == - LIBSSH2_ERROR_EAGAIN); - session->sftpInit_channel = NULL; - if (session->sftpInit_sftp) { - LIBSSH2_FREE(session, session->sftpInit_sftp); - session->sftpInit_sftp = NULL; - } - session->sftpInit_state = libssh2_NB_state_idle; - return NULL; -} - -/* - * libssh2_sftp_init - * - * Startup an SFTP session - */ -LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session) -{ - LIBSSH2_SFTP *ptr; - - if(!session) - return NULL; - - if(!(session->state & LIBSSH2_STATE_AUTHENTICATED)) { - _libssh2_error(session, LIBSSH2_ERROR_INVAL, - "session not authenticated yet"); - return NULL; - } - - BLOCK_ADJUST_ERRNO(ptr, session, sftp_init(session)); - return ptr; -} - -/* - * sftp_shutdown - * - * Shutsdown the SFTP subsystem - */ -static int -sftp_shutdown(LIBSSH2_SFTP *sftp) -{ - int rc; - LIBSSH2_SESSION *session = sftp->channel->session; - /* - * Make sure all memory used in the state variables are free - */ - if (sftp->partial_packet) { - LIBSSH2_FREE(session, sftp->partial_packet); - sftp->partial_packet = NULL; - } - if (sftp->open_packet) { - LIBSSH2_FREE(session, sftp->open_packet); - sftp->open_packet = NULL; - } - if (sftp->readdir_packet) { - LIBSSH2_FREE(session, sftp->readdir_packet); - sftp->readdir_packet = NULL; - } - if (sftp->fstat_packet) { - LIBSSH2_FREE(session, sftp->fstat_packet); - sftp->fstat_packet = NULL; - } - if (sftp->unlink_packet) { - LIBSSH2_FREE(session, sftp->unlink_packet); - sftp->unlink_packet = NULL; - } - if (sftp->rename_packet) { - LIBSSH2_FREE(session, sftp->rename_packet); - sftp->rename_packet = NULL; - } - if (sftp->fstatvfs_packet) { - LIBSSH2_FREE(session, sftp->fstatvfs_packet); - sftp->fstatvfs_packet = NULL; - } - if (sftp->statvfs_packet) { - LIBSSH2_FREE(session, sftp->statvfs_packet); - sftp->statvfs_packet = NULL; - } - if (sftp->mkdir_packet) { - LIBSSH2_FREE(session, sftp->mkdir_packet); - sftp->mkdir_packet = NULL; - } - if (sftp->rmdir_packet) { - LIBSSH2_FREE(session, sftp->rmdir_packet); - sftp->rmdir_packet = NULL; - } - if (sftp->stat_packet) { - LIBSSH2_FREE(session, sftp->stat_packet); - sftp->stat_packet = NULL; - } - if (sftp->symlink_packet) { - LIBSSH2_FREE(session, sftp->symlink_packet); - sftp->symlink_packet = NULL; - } - - sftp_packet_flush(sftp); - - /* TODO: We should consider walking over the sftp_handles list and kill - * any remaining sftp handles ... */ - - rc = _libssh2_channel_free(sftp->channel); - - return rc; -} - -/* libssh2_sftp_shutdown - * Shutsdown the SFTP subsystem - */ -LIBSSH2_API int -libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, sftp_shutdown(sftp)); - return rc; -} - -/* ******************************* - * SFTP File and Directory Ops * - ******************************* */ - -/* sftp_open - */ -static LIBSSH2_SFTP_HANDLE * -sftp_open(LIBSSH2_SFTP *sftp, const char *filename, - size_t filename_len, uint32_t flags, long mode, - int open_type) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_HANDLE *fp; - LIBSSH2_SFTP_ATTRIBUTES attrs = { - LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 - }; - unsigned char *s; - ssize_t rc; - int open_file = (open_type == LIBSSH2_SFTP_OPENFILE)?1:0; - - if (sftp->open_state == libssh2_NB_state_idle) { - /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + - flags(4) */ - sftp->open_packet_len = filename_len + 13 + - (open_file? (4 + sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS)) : 0); - - /* surprise! this starts out with nothing sent */ - sftp->open_packet_sent = 0; - s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len); - if (!sftp->open_packet) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_OPEN or " - "FXP_OPENDIR packet"); - return NULL; - } - /* Filetype in SFTP 3 and earlier */ - attrs.permissions = mode | - (open_file ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : - LIBSSH2_SFTP_ATTR_PFILETYPE_DIR); - - _libssh2_store_u32(&s, sftp->open_packet_len - 4); - *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR; - sftp->open_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->open_request_id); - _libssh2_store_str(&s, filename, filename_len); - - if (open_file) { - _libssh2_store_u32(&s, flags); - s += sftp_attr2bin(s, &attrs); - } - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request", - open_file? "file" : "directory"); - - sftp->open_state = libssh2_NB_state_created; - } - - if (sftp->open_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, sftp->open_packet+ - sftp->open_packet_sent, - sftp->open_packet_len - - sftp->open_packet_sent); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block sending FXP_OPEN or FXP_OPENDIR command"); - return NULL; - } - else if(rc < 0) { - _libssh2_error(session, rc, "Unable to send FXP_OPEN*"); - LIBSSH2_FREE(session, sftp->open_packet); - sftp->open_packet = NULL; - sftp->open_state = libssh2_NB_state_idle; - return NULL; - } - - /* bump the sent counter and remain in this state until the whole - data is off */ - sftp->open_packet_sent += rc; - - if(sftp->open_packet_len == sftp->open_packet_sent) { - LIBSSH2_FREE(session, sftp->open_packet); - sftp->open_packet = NULL; - - sftp->open_state = libssh2_NB_state_sent; - } - } - - if (sftp->open_state == libssh2_NB_state_sent) { - size_t data_len; - unsigned char *data; - static const unsigned char fopen_responses[2] = - { SSH_FXP_HANDLE, SSH_FXP_STATUS }; - rc = sftp_packet_requirev(sftp, 2, fopen_responses, - sftp->open_request_id, &data, - &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting for status message"); - return NULL; - } - sftp->open_state = libssh2_NB_state_idle; - if (rc) { - _libssh2_error(session, rc, "Timeout waiting for status message"); - return NULL; - } - - /* OPEN can basically get STATUS or HANDLE back, where HANDLE implies - a fine response while STATUS means error. It seems though that at - times we get an SSH_FX_OK back in a STATUS, followed the "real" - HANDLE so we need to properly deal with that. */ - if (data[0] == SSH_FXP_STATUS) { - int badness = 1; - - if(data_len < 9) { - _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Too small FXP_STATUS"); - LIBSSH2_FREE(session, data); - return NULL; - } - - sftp->last_errno = _libssh2_ntohu32(data + 5); - - if(LIBSSH2_FX_OK == sftp->last_errno) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got HANDLE FXOK!"); - - LIBSSH2_FREE(session, data); - - /* silly situation, but check for a HANDLE */ - rc = sftp_packet_require(sftp, SSH_FXP_HANDLE, - sftp->open_request_id, &data, - &data_len); - if(rc == LIBSSH2_ERROR_EAGAIN) { - /* go back to sent state and wait for something else */ - sftp->open_state = libssh2_NB_state_sent; - return NULL; - } - else if(!rc) - /* we got the handle so this is not a bad situation */ - badness = 0; - } - - if(badness) { - _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Failed opening remote file"); - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got FXP_STATUS %d", - sftp->last_errno); - LIBSSH2_FREE(session, data); - return NULL; - } - } - - if(data_len < 10) { - _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Too small FXP_HANDLE"); - LIBSSH2_FREE(session, data); - return NULL; - } - - fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE)); - if (!fp) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate new SFTP handle structure"); - LIBSSH2_FREE(session, data); - return NULL; - } - memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE)); - fp->handle_type = open_file ? LIBSSH2_SFTP_HANDLE_FILE : - LIBSSH2_SFTP_HANDLE_DIR; - - fp->handle_len = _libssh2_ntohu32(data + 5); - if (fp->handle_len > SFTP_HANDLE_MAXLEN) - /* SFTP doesn't allow handles longer than 256 characters */ - fp->handle_len = SFTP_HANDLE_MAXLEN; - - if(fp->handle_len > (data_len - 9)) - /* do not reach beyond the end of the data we got */ - fp->handle_len = data_len - 9; - - memcpy(fp->handle, data + 9, fp->handle_len); - - LIBSSH2_FREE(session, data); - - /* add this file handle to the list kept in the sftp session */ - _libssh2_list_add(&sftp->sftp_handles, &fp->node); - - fp->sftp = sftp; /* point to the parent struct */ - - fp->u.file.offset = 0; - fp->u.file.offset_sent = 0; - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful"); - return fp; - } - return NULL; -} - -/* libssh2_sftp_open_ex - */ -LIBSSH2_API LIBSSH2_SFTP_HANDLE * -libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename, - unsigned int filename_len, unsigned long flags, long mode, - int open_type) -{ - LIBSSH2_SFTP_HANDLE *hnd; - - if(!sftp) - return NULL; - - BLOCK_ADJUST_ERRNO(hnd, sftp->channel->session, - sftp_open(sftp, filename, filename_len, flags, mode, - open_type)); - return hnd; -} - -/* - * sftp_read - * - * Read from an SFTP file handle - * - */ -static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer, - size_t buffer_size) -{ - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t count = 0; - struct sftp_pipeline_chunk *chunk; - struct sftp_pipeline_chunk *next; - ssize_t rc; - struct _libssh2_sftp_handle_file_data *filep = - &handle->u.file; - - /* This function can be interrupted in three different places where it - might need to wait for data from the network. It returns EAGAIN to - allow non-blocking clients to do other work but these client are - expected to call this function again (possibly many times) to finish - the operation. - - The tricky part is that if we previously aborted a sftp_read due to - EAGAIN, we must continue at the same spot to continue the previously - interrupted operation. This is done using a state machine to record - what phase of execution we were at. The state is stored in - sftp->read_state. - - libssh2_NB_state_idle: The first phase is where we prepare multiple - FXP_READ packets to do optimistic read-ahead. We send off as many as - possible in the second phase without waiting for a response to each - one; this is the key to fast reads. But we may have to adjust the - channel window size to do this which may interrupt this function while - waiting. The state machine saves the phase as libssh2_NB_state_idle so - it returns here on the next call. - - libssh2_NB_state_sent: The second phase is where we send the FXP_READ - packets. Writing them to the channel can be interrupted with EAGAIN - but the state machine ensures we skip the first phase on the next call - and resume sending. - - libssh2_NB_state_sent2: In the third phase (indicated by ) we read the - data from the responses that have arrived so far. Reading can be - interrupted with EAGAIN but the state machine ensures we skip the first - and second phases on the next call and resume sending. - */ - - switch (sftp->read_state) { - case libssh2_NB_state_idle: - - /* Some data may already have been read from the server in the - previous call but didn't fit in the buffer at the time. If so, we - return that now as we can't risk being interrupted later with data - partially written to the buffer. */ - if(filep->data_left) { - size_t copy = MIN(buffer_size, filep->data_left); - - memcpy(buffer, &filep->data[ filep->data_len - filep->data_left], - copy); - - filep->data_left -= copy; - filep->offset += copy; - - if(!filep->data_left) { - LIBSSH2_FREE(session, filep->data); - filep->data = NULL; - } - - return copy; - } - - /* We allow a number of bytes being requested at any given time - without having been acked - until we reach EOF. */ - if(!filep->eof) { - /* Number of bytes asked for that haven't been acked yet */ - size_t already = (filep->offset_sent - filep->offset); - - size_t max_read_ahead = buffer_size*4; - unsigned long recv_window; - - if(max_read_ahead > LIBSSH2_CHANNEL_WINDOW_DEFAULT*4) - max_read_ahead = LIBSSH2_CHANNEL_WINDOW_DEFAULT*4; - - /* if the buffer_size passed in now is smaller than what has - already been sent, we risk getting count become a very large - number */ - if(max_read_ahead > already) - count = max_read_ahead - already; - - /* 'count' is how much more data to ask for, and 'already' is how - much data that already has been asked for but not yet returned. - Specificly, 'count' means how much data that have or will be - asked for by the nodes that are already added to the linked - list. Some of those read requests may not actually have been - sent off successfully yet. - - If 'already' is very large it should be perfectly fine to have - count set to 0 as then we don't have to ask for more data - (right now). - - buffer_size*4 is just picked more or less out of the air. The - idea is that when reading SFTP from a remote server, we send - away multiple read requests guessing that the client will read - more than only this 'buffer_size' amount of memory. So we ask - for maximum buffer_size*4 amount of data so that we can return - them very fast in subsequent calls. - */ - - recv_window = libssh2_channel_window_read_ex(sftp->channel, - NULL, NULL); - if(max_read_ahead > recv_window) { - /* more data will be asked for than what the window currently - allows, expand it! */ - - rc = _libssh2_channel_receive_window_adjust(sftp->channel, - max_read_ahead*8, - 1, NULL); - /* if this returns EAGAIN, we will get back to this function - at next call */ - assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->data_left); - assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->eof); - if (rc) - return rc; - } - } - - while(count > 0) { - unsigned char *s; - uint32_t size = MIN(MAX_SFTP_READ_SIZE, count); - - /* 25 = packet_len(4) + packet_type(1) + request_id(4) + - handle_len(4) + offset(8) + count(4) */ - uint32_t packet_len = (uint32_t)handle->handle_len + 25; - uint32_t request_id; - - chunk = LIBSSH2_ALLOC(session, packet_len + - sizeof(struct sftp_pipeline_chunk)); - if (!chunk) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "malloc fail for FXP_WRITE"); - - chunk->len = size; - chunk->lefttosend = packet_len; - chunk->sent = 0; - - s = chunk->packet; - - _libssh2_store_u32(&s, packet_len - 4); - *s++ = SSH_FXP_READ; - request_id = sftp->request_id++; - chunk->request_id = request_id; - _libssh2_store_u32(&s, request_id); - _libssh2_store_str(&s, handle->handle, handle->handle_len); - _libssh2_store_u64(&s, filep->offset_sent); - filep->offset_sent += size; /* advance offset at once */ - _libssh2_store_u32(&s, size); - - /* add this new entry LAST in the list */ - _libssh2_list_add(&handle->packet_list, &chunk->node); - count -= size; /* deduct the size we used, as we might have - to create more packets */ - } - - case libssh2_NB_state_sent: - - sftp->read_state = libssh2_NB_state_idle; - - /* move through the READ packets that haven't been sent and send as - many as possible - remember that we don't block */ - chunk = _libssh2_list_first(&handle->packet_list); - - while(chunk) { - if(chunk->lefttosend) { - - rc = _libssh2_channel_write(channel, 0, - &chunk->packet[chunk->sent], - chunk->lefttosend); - if(rc < 0) { - sftp->read_state = libssh2_NB_state_sent; - return rc; - } - - /* remember where to continue sending the next time */ - chunk->lefttosend -= rc; - chunk->sent += rc; - - if(chunk->lefttosend) - /* data left to send, get out of loop */ - break; - } - - /* move on to the next chunk with data to send */ - chunk = _libssh2_list_next(&chunk->node); - } - - case libssh2_NB_state_sent2: - - sftp->read_state = libssh2_NB_state_idle; - - /* - * Count all ACKed packets and act on the contents of them. - */ - chunk = _libssh2_list_first(&handle->packet_list); - - while(chunk) { - unsigned char *data; - size_t data_len; - uint32_t rc32; - static const unsigned char read_responses[2] = { - SSH_FXP_DATA, SSH_FXP_STATUS - }; - - if(chunk->lefttosend) - /* if the chunk still has data left to send, we shouldn't wait - for an ACK for it just yet */ - break; - - rc = sftp_packet_requirev(sftp, 2, read_responses, - chunk->request_id, &data, &data_len); - if (rc < 0) { - sftp->read_state = libssh2_NB_state_sent2; - return rc; - } - - /* - * We get DATA or STATUS back. STATUS can be error, or it is - * FX_EOF when we reach the end of the file. - */ - - switch (data[0]) { - case SSH_FXP_STATUS: - /* remove the chunk we just processed keeping track of the - * next one in case we need it */ - next = _libssh2_list_next(&chunk->node); - _libssh2_list_remove(&chunk->node); - LIBSSH2_FREE(session, chunk); - - /* we must remove all outstanding READ requests, as either we - got an error or we're at end of file */ - sftp_packetlist_flush(handle); - - rc32 = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - if (rc32 == LIBSSH2_FX_EOF) { - filep->eof = TRUE; - return 0; - } - else { - sftp->last_errno = rc32; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP READ error"); - } - break; - - case SSH_FXP_DATA: - rc32 = _libssh2_ntohu32(data + 5); - if (rc32 > (data_len - 9)) - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol badness"); - - if(rc32 != chunk->len) { - /* a short read does not imply end of file, but we must - adjust the offset_sent since it was advanced with a - full chunk->len before */ - filep->offset_sent -= (chunk->len - rc32); - } - - if(rc32 > buffer_size) { - /* figure out the overlap amount */ - filep->data_left = rc32 - buffer_size; - - /* getting the full packet would overflow the buffer, so - only get the correct amount and keep the remainder */ - rc32 = (uint32_t)buffer_size; - - /* store data to keep for next call */ - filep->data = data; - filep->data_len = data_len; - } - else - filep->data_len = 0; - - /* copy the received data from the received FXP_DATA packet to - the buffer at the correct index */ - memcpy(buffer, data + 9, rc32); - filep->offset += rc32; - - if(filep->data_len == 0) - /* free the allocated data if not stored to keep */ - LIBSSH2_FREE(session, data); - - - /* remove the chunk we just processed keeping track of the - * next one in case we need it */ - next = _libssh2_list_next(&chunk->node); - _libssh2_list_remove(&chunk->node); - LIBSSH2_FREE(session, chunk); - chunk = NULL; - - if(rc32 > 0) { - /* we must return as we wrote some data to the buffer */ - return rc32; - } else { - /* A zero-byte read is not necessarily EOF so we must not - * return 0 (that would signal EOF to the caller) so - * instead we carry on to the next chunk */ - chunk = next; - } - - break; - default: - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol badness: unrecognised " - "read request response"); - } - } - - break; - - default: - assert(!"State machine error; unrecognised read state"); - } - - return 0; -} - -/* libssh2_sftp_read - * Read from an SFTP file handle - */ -LIBSSH2_API ssize_t -libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *hnd, char *buffer, - size_t buffer_maxlen) -{ - ssize_t rc; - if(!hnd) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, hnd->sftp->channel->session, - sftp_read(hnd, buffer, buffer_maxlen)); - return rc; -} - -/* sftp_readdir - * Read from an SFTP directory handle - */ -static ssize_t sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, - size_t buffer_maxlen, char *longentry, - size_t longentry_maxlen, - LIBSSH2_SFTP_ATTRIBUTES *attrs) -{ - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - uint32_t num_names; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - uint32_t packet_len = handle->handle_len + 13; - unsigned char *s, *data; - static const unsigned char read_responses[2] = { - SSH_FXP_NAME, SSH_FXP_STATUS }; - ssize_t retcode; - - if (sftp->readdir_state == libssh2_NB_state_idle) { - if (handle->u.dir.names_left) { - /* - * A prior request returned more than one directory entry, - * feed it back from the buffer - */ - LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; - size_t real_longentry_len; - size_t real_filename_len; - size_t filename_len; - size_t longentry_len; - - s = (unsigned char *) handle->u.dir.next_name; - real_filename_len = _libssh2_ntohu32(s); - - s += 4; - - filename_len = real_filename_len; - if (filename_len >= buffer_maxlen) { - filename_len = LIBSSH2_ERROR_BUFFER_TOO_SMALL; - goto end; - } - - memcpy(buffer, s, filename_len); - buffer[filename_len] = '\0'; /* zero terminate */ - s += real_filename_len; - - real_longentry_len = _libssh2_ntohu32(s); - s += 4; - - if (longentry && (longentry_maxlen>1)) { - longentry_len = real_longentry_len; - - if (longentry_len >= longentry_maxlen) { - filename_len = LIBSSH2_ERROR_BUFFER_TOO_SMALL; - goto end; - } - - memcpy(longentry, s, longentry_len); - longentry[longentry_len] = '\0'; /* zero terminate */ - } - s += real_longentry_len; - - if (attrs) - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - - s += sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); - - handle->u.dir.next_name = (char *) s; - end: - - if ((--handle->u.dir.names_left) == 0) - LIBSSH2_FREE(session, handle->u.dir.names_packet); - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "libssh2_sftp_readdir_ex() return %d", - filename_len); - return (ssize_t)filename_len; - } - - /* Request another entry(entries?) */ - - s = sftp->readdir_packet = LIBSSH2_ALLOC(session, packet_len); - if (!sftp->readdir_packet) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "FXP_READDIR packet"); - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_READDIR; - sftp->readdir_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->readdir_request_id); - _libssh2_store_str(&s, handle->handle, handle->handle_len); - - sftp->readdir_state = libssh2_NB_state_created; - } - - if (sftp->readdir_state == libssh2_NB_state_created) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Reading entries from directory handle"); - retcode = _libssh2_channel_write(channel, 0, sftp->readdir_packet, - packet_len); - if (retcode == LIBSSH2_ERROR_EAGAIN) { - return retcode; - } - else if ((ssize_t)packet_len != retcode) { - LIBSSH2_FREE(session, sftp->readdir_packet); - sftp->readdir_packet = NULL; - sftp->readdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "_libssh2_channel_write() failed"); - } - - LIBSSH2_FREE(session, sftp->readdir_packet); - sftp->readdir_packet = NULL; - - sftp->readdir_state = libssh2_NB_state_sent; - } - - retcode = sftp_packet_requirev(sftp, 2, read_responses, - sftp->readdir_request_id, &data, - &data_len); - if (retcode == LIBSSH2_ERROR_EAGAIN) - return retcode; - else if (retcode) { - sftp->readdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, retcode, - "Timeout waiting for status message"); - } - - if (data[0] == SSH_FXP_STATUS) { - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_EOF) { - sftp->readdir_state = libssh2_NB_state_idle; - return 0; - } - else { - sftp->last_errno = retcode; - sftp->readdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } - } - - sftp->readdir_state = libssh2_NB_state_idle; - - num_names = _libssh2_ntohu32(data + 5); - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%lu entries returned", - num_names); - if (!num_names) { - LIBSSH2_FREE(session, data); - return 0; - } - - handle->u.dir.names_left = num_names; - handle->u.dir.names_packet = data; - handle->u.dir.next_name = (char *) data + 9; - - /* use the name popping mechanism from the start of the function */ - return sftp_readdir(handle, buffer, buffer_maxlen, longentry, - longentry_maxlen, attrs); -} - -/* libssh2_sftp_readdir_ex - * Read from an SFTP directory handle - */ -LIBSSH2_API int -libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *hnd, char *buffer, - size_t buffer_maxlen, char *longentry, - size_t longentry_maxlen, - LIBSSH2_SFTP_ATTRIBUTES *attrs) -{ - int rc; - if(!hnd) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, hnd->sftp->channel->session, - sftp_readdir(hnd, buffer, buffer_maxlen, longentry, - longentry_maxlen, attrs)); - return rc; -} - -/* - * sftp_write - * - * Write data to an SFTP handle. Returns the number of bytes written, or - * a negative error code. - * - * We recommend sending very large data buffers to this function! - * - * Concept: - * - * - Detect how much of the given buffer that was already sent in a previous - * call by inspecting the linked list of outgoing chunks. Make sure to skip - * passed the data that has already been taken care of. - * - * - Split all (new) outgoing data in chunks no larger than N. - * - * - Each N bytes chunk gets created as a separate SFTP packet. - * - * - Add all created outgoing packets to the linked list. - * - * - Walk through the list and send the chunks that haven't been sent, - * as many as possible until EAGAIN. Some of the chunks may have been put - * in the list in a previous invoke. - * - * - For all the chunks in the list that have been completely sent off, check - * for ACKs. If a chunk has been ACKed, it is removed from the linked - * list and the "acked" counter gets increased with that data amount. - * - * - Return TOTAL bytes acked so far. - * - * Caveats: - * - be careful: we must not return a higher number than what was given! - * - * TODO: - * Introduce an option that disables this sort of "speculative" ahead writing - * as there's a risk that it will do harm to some app. - */ - -static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, - size_t count) -{ - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - uint32_t retcode; - uint32_t packet_len; - unsigned char *s, *data; - ssize_t rc; - struct sftp_pipeline_chunk *chunk; - struct sftp_pipeline_chunk *next; - size_t acked = 0; - size_t org_count = count; - size_t already; - - switch(sftp->write_state) { - default: - case libssh2_NB_state_idle: - - /* Number of bytes sent off that haven't been acked and therefor we - will get passed in here again. - - Also, add up the number of bytes that actually already have been - acked but we haven't been able to return as such yet, so we will - get that data as well passed in here again. - */ - already = (handle->u.file.offset_sent - handle->u.file.offset)+ - handle->u.file.acked; - - if(count >= already) { - /* skip the part already made into packets */ - buffer += already; - count -= already; - } - else - /* there is more data already fine than what we got in this call */ - count = 0; - - sftp->write_state = libssh2_NB_state_idle; - while(count) { - /* TODO: Possibly this should have some logic to prevent a very - very small fraction to be left but lets ignore that for now */ - uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count); - uint32_t request_id; - - /* 25 = packet_len(4) + packet_type(1) + request_id(4) + - handle_len(4) + offset(8) + count(4) */ - packet_len = handle->handle_len + size + 25; - - chunk = LIBSSH2_ALLOC(session, packet_len + - sizeof(struct sftp_pipeline_chunk)); - if (!chunk) - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "malloc fail for FXP_WRITE"); - - chunk->len = size; - chunk->sent = 0; - chunk->lefttosend = packet_len; - - s = chunk->packet; - _libssh2_store_u32(&s, packet_len - 4); - - *(s++) = SSH_FXP_WRITE; - request_id = sftp->request_id++; - chunk->request_id = request_id; - _libssh2_store_u32(&s, request_id); - _libssh2_store_str(&s, handle->handle, handle->handle_len); - _libssh2_store_u64(&s, handle->u.file.offset_sent); - handle->u.file.offset_sent += size; /* advance offset at once */ - _libssh2_store_str(&s, buffer, size); - - /* add this new entry LAST in the list */ - _libssh2_list_add(&handle->packet_list, &chunk->node); - - buffer += size; - count -= size; /* deduct the size we used, as we might have - to create more packets */ - } - - /* move through the WRITE packets that haven't been sent and send as many - as possible - remember that we don't block */ - chunk = _libssh2_list_first(&handle->packet_list); - - while(chunk) { - if(chunk->lefttosend) { - rc = _libssh2_channel_write(channel, 0, - &chunk->packet[chunk->sent], - chunk->lefttosend); - if(rc < 0) - /* remain in idle state */ - return rc; - - /* remember where to continue sending the next time */ - chunk->lefttosend -= rc; - chunk->sent += rc; - - if(chunk->lefttosend) - /* data left to send, get out of loop */ - break; - } - - /* move on to the next chunk with data to send */ - chunk = _libssh2_list_next(&chunk->node); - } - - /* fall-through */ - case libssh2_NB_state_sent: - - sftp->write_state = libssh2_NB_state_idle; - /* - * Count all ACKed packets - */ - chunk = _libssh2_list_first(&handle->packet_list); - - while(chunk) { - if(chunk->lefttosend) - /* if the chunk still has data left to send, we shouldn't wait - for an ACK for it just yet */ - break; - - else if(acked) - /* if we have sent data that is acked, we must return that - info before we call a function that might return EAGAIN */ - break; - - /* we check the packets in order */ - rc = sftp_packet_require(sftp, SSH_FXP_STATUS, - chunk->request_id, &data, &data_len); - if (rc < 0) { - if (rc == LIBSSH2_ERROR_EAGAIN) - sftp->write_state = libssh2_NB_state_sent; - return rc; - } - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - sftp->last_errno = retcode; - if (retcode == LIBSSH2_FX_OK) { - acked += chunk->len; /* number of payload data that was acked - here */ - - /* we increase the offset value for all acks */ - handle->u.file.offset += chunk->len; - - next = _libssh2_list_next(&chunk->node); - - _libssh2_list_remove(&chunk->node); /* remove from list */ - LIBSSH2_FREE(session, chunk); /* free memory */ - - chunk = next; - } - else { - /* flush all pending packets from the outgoing list */ - sftp_packetlist_flush(handle); - - /* since we return error now, the applicaton will not get any - outstanding data acked, so we need to rewind the offset to - where the application knows it has reached with acked data */ - handle->u.file.offset -= handle->u.file.acked; - - /* then reset the offset_sent to be the same as the offset */ - handle->u.file.offset_sent = handle->u.file.offset; - - /* clear the acked counter since we can have no pending data to - ack after an error */ - handle->u.file.acked = 0; - - /* the server returned an error for that written chunk, propagate - this back to our parent function */ - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "FXP write failed"); - } - } - break; - } - - /* if there were acked data in a previous call that wasn't returned then, - add that up and try to return it all now. This can happen if the app - first sends a huge buffer of data, and then in a second call it sends a - smaller one. */ - acked += handle->u.file.acked; - - if(acked) { - ssize_t ret = MIN(acked, org_count); - /* we got data acked so return that amount, but no more than what - was asked to get sent! */ - - /* store the remainder. 'ret' is always equal to or less than 'acked' - here */ - handle->u.file.acked = acked - ret; - - return ret; - } - - else - return 0; /* nothing was acked, and no EAGAIN was received! */ -} - -/* libssh2_sftp_write - * Write data to a file handle - */ -LIBSSH2_API ssize_t -libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *hnd, const char *buffer, - size_t count) -{ - ssize_t rc; - if(!hnd) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, hnd->sftp->channel->session, - sftp_write(hnd, buffer, count)); - return rc; - -} - -/* - * sftp_fstat - * - * Get or Set stat on a file - */ -static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle, - LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat) -{ - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - uint32_t packet_len = - handle->handle_len + 13 + (setstat ? sftp_attrsize(attrs->flags) : 0); - unsigned char *s, *data; - static const unsigned char fstat_responses[2] = - { SSH_FXP_ATTRS, SSH_FXP_STATUS }; - ssize_t rc; - - if (sftp->fstat_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Issuing %s command", - setstat ? "set-stat" : "stat"); - s = sftp->fstat_packet = LIBSSH2_ALLOC(session, packet_len); - if (!sftp->fstat_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "FSTAT/FSETSTAT packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT; - sftp->fstat_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->fstat_request_id); - _libssh2_store_str(&s, handle->handle, handle->handle_len); - - if (setstat) { - s += sftp_attr2bin(s, attrs); - } - - sftp->fstat_state = libssh2_NB_state_created; - } - - if (sftp->fstat_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, sftp->fstat_packet, - packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - else if ((ssize_t)packet_len != rc) { - LIBSSH2_FREE(session, sftp->fstat_packet); - sftp->fstat_packet = NULL; - sftp->fstat_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - (setstat ? "Unable to send FXP_FSETSTAT" - : "Unable to send FXP_FSTAT command")); - } - LIBSSH2_FREE(session, sftp->fstat_packet); - sftp->fstat_packet = NULL; - - sftp->fstat_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_requirev(sftp, 2, fstat_responses, - sftp->fstat_request_id, &data, - &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - else if (rc) { - sftp->fstat_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Timeout waiting for status message"); - } - - sftp->fstat_state = libssh2_NB_state_idle; - - if (data[0] == SSH_FXP_STATUS) { - uint32_t retcode; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } - } - - sftp_bin2attr(attrs, data + 5); - LIBSSH2_FREE(session, data); - - return 0; -} - -/* libssh2_sftp_fstat_ex - * Get or Set stat on a file - */ -LIBSSH2_API int -libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *hnd, - LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat) -{ - int rc; - if(!hnd || !attrs) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, hnd->sftp->channel->session, - sftp_fstat(hnd, attrs, setstat)); - return rc; -} - - -/* libssh2_sftp_seek64 - * Set the read/write pointer to an arbitrary position within the file - */ -LIBSSH2_API void -libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset) -{ - if(handle) { - handle->u.file.offset = handle->u.file.offset_sent = offset; - /* discard all pending requests and currently read data */ - sftp_packetlist_flush(handle); - - /* free the left received buffered data */ - if (handle->u.file.data_left) { - LIBSSH2_FREE(handle->sftp->channel->session, handle->u.file.data); - handle->u.file.data_left = handle->u.file.data_len = 0; - handle->u.file.data = NULL; - } - - /* reset EOF to False */ - handle->u.file.eof = FALSE; - } -} - -/* libssh2_sftp_seek - * Set the read/write pointer to an arbitrary position within the file - */ -LIBSSH2_API void -libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset) -{ - libssh2_sftp_seek64(handle, (libssh2_uint64_t)offset); -} - -/* libssh2_sftp_tell - * Return the current read/write pointer's offset - */ -LIBSSH2_API size_t -libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle) -{ - if(!handle) - return 0; /* no handle, no size */ - - /* NOTE: this may very well truncate the size if it is larger than what - size_t can hold, so libssh2_sftp_tell64() is really the function you - should use */ - return (size_t)(handle->u.file.offset); -} - -/* libssh2_sftp_tell64 - * Return the current read/write pointer's offset - */ -LIBSSH2_API libssh2_uint64_t -libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle) -{ - if(!handle) - return 0; /* no handle, no size */ - - return handle->u.file.offset; -} - -/* - * Flush all remaining incoming SFTP packets and zombies. - */ -static void sftp_packet_flush(LIBSSH2_SFTP *sftp) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets); - struct sftp_zombie_requests *zombie = - _libssh2_list_first(&sftp->zombie_requests); - - while(packet) { - LIBSSH2_SFTP_PACKET *next; - - /* check next struct in the list */ - next = _libssh2_list_next(&packet->node); - _libssh2_list_remove(&packet->node); - LIBSSH2_FREE(session, packet->data); - LIBSSH2_FREE(session, packet); - - packet = next; - } - - while(zombie) { - /* figure out the next node */ - struct sftp_zombie_requests *next = _libssh2_list_next(&zombie->node); - /* unlink the current one */ - _libssh2_list_remove(&zombie->node); - /* free the memory */ - LIBSSH2_FREE(session, zombie); - zombie = next; - } - -} - -/* sftp_close_handle - * - * Close a file or directory handle - * Also frees handle resource and unlinks it from the SFTP structure - */ -static int -sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle) -{ - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - int retcode; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - uint32_t packet_len = handle->handle_len + 13; - unsigned char *s, *data = NULL; - int rc; - - if (handle->close_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Closing handle"); - s = handle->close_packet = LIBSSH2_ALLOC(session, packet_len); - if (!handle->close_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_CLOSE " - "packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_CLOSE; - handle->close_request_id = sftp->request_id++; - _libssh2_store_u32(&s, handle->close_request_id); - _libssh2_store_str(&s, handle->handle, handle->handle_len); - handle->close_state = libssh2_NB_state_created; - } - - if (handle->close_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, handle->close_packet, - packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if ((ssize_t)packet_len != rc) { - LIBSSH2_FREE(session, handle->close_packet); - handle->close_packet = NULL; - handle->close_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send FXP_CLOSE command"); - } - LIBSSH2_FREE(session, handle->close_packet); - handle->close_packet = NULL; - - handle->close_state = libssh2_NB_state_sent; - } - - if (handle->close_state == libssh2_NB_state_sent) { - rc = sftp_packet_require(sftp, SSH_FXP_STATUS, - handle->close_request_id, &data, - &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - handle->close_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for status message"); - } - - handle->close_state = libssh2_NB_state_sent1; - } - - if(!data) - /* if it reaches this point with data unset, something unwanted - happened (like this function is called again when in - libssh2_NB_state_sent1 state) and we just bail out */ - return LIBSSH2_ERROR_INVAL; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - if (retcode != LIBSSH2_FX_OK) { - sftp->last_errno = retcode; - handle->close_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } - - /* remove this handle from the parent's list */ - _libssh2_list_remove(&handle->node); - - if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) - && handle->u.dir.names_left) { - LIBSSH2_FREE(session, handle->u.dir.names_packet); - } - else { - if(handle->u.file.data) - LIBSSH2_FREE(session, handle->u.file.data); - } - - sftp_packetlist_flush(handle); - sftp->read_state = libssh2_NB_state_idle; - - handle->close_state = libssh2_NB_state_idle; - - LIBSSH2_FREE(session, handle); - - return 0; -} - -/* libssh2_sftp_close_handle - * - * Close a file or directory handle - * Also frees handle resource and unlinks it from the SFTP structure - */ -LIBSSH2_API int -libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *hnd) -{ - int rc; - if(!hnd) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, hnd->sftp->channel->session, sftp_close_handle(hnd)); - return rc; -} - -/* sftp_unlink - * Delete a file from the remote server - */ -static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename, - size_t filename_len) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - int retcode; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ - uint32_t packet_len = filename_len + 13; - unsigned char *s, *data; - int rc; - - if (sftp->unlink_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Unlinking %s", filename); - s = sftp->unlink_packet = LIBSSH2_ALLOC(session, packet_len); - if (!sftp->unlink_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_REMOVE " - "packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_REMOVE; - sftp->unlink_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->unlink_request_id); - _libssh2_store_str(&s, filename, filename_len); - sftp->unlink_state = libssh2_NB_state_created; - } - - if (sftp->unlink_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, sftp->unlink_packet, - packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if ((ssize_t)packet_len != rc) { - LIBSSH2_FREE(session, sftp->unlink_packet); - sftp->unlink_packet = NULL; - sftp->unlink_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send FXP_REMOVE command"); - } - LIBSSH2_FREE(session, sftp->unlink_packet); - sftp->unlink_packet = NULL; - - sftp->unlink_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_require(sftp, SSH_FXP_STATUS, - sftp->unlink_request_id, &data, - &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } - else if (rc) { - sftp->unlink_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for FXP STATUS"); - } - - sftp->unlink_state = libssh2_NB_state_idle; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } -} - -/* libssh2_sftp_unlink_ex - * Delete a file from the remote server - */ -LIBSSH2_API int -libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename, - unsigned int filename_len) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, - sftp_unlink(sftp, filename, filename_len)); - return rc; -} - -/* - * sftp_rename - * - * Rename a file on the remote server - */ -static int sftp_rename(LIBSSH2_SFTP *sftp, const char *source_filename, - unsigned int source_filename_len, - const char *dest_filename, - unsigned int dest_filename_len, long flags) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - int retcode; - uint32_t packet_len = - source_filename_len + dest_filename_len + 17 + (sftp->version >= - 5 ? 4 : 0); - /* packet_len(4) + packet_type(1) + request_id(4) + - source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */ - unsigned char *data; - ssize_t rc; - - if (sftp->version < 2) { - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Server does not support RENAME"); - } - - if (sftp->rename_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Renaming %s to %s", - source_filename, dest_filename); - sftp->rename_s = sftp->rename_packet = - LIBSSH2_ALLOC(session, packet_len); - if (!sftp->rename_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_RENAME " - "packet"); - } - - _libssh2_store_u32(&sftp->rename_s, packet_len - 4); - *(sftp->rename_s++) = SSH_FXP_RENAME; - sftp->rename_request_id = sftp->request_id++; - _libssh2_store_u32(&sftp->rename_s, sftp->rename_request_id); - _libssh2_store_str(&sftp->rename_s, source_filename, - source_filename_len); - _libssh2_store_str(&sftp->rename_s, dest_filename, dest_filename_len); - - if (sftp->version >= 5) - _libssh2_store_u32(&sftp->rename_s, flags); - - sftp->rename_state = libssh2_NB_state_created; - } - - if (sftp->rename_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, sftp->rename_packet, - sftp->rename_s - sftp->rename_packet); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if ((ssize_t)packet_len != rc) { - LIBSSH2_FREE(session, sftp->rename_packet); - sftp->rename_packet = NULL; - sftp->rename_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send FXP_RENAME command"); - } - LIBSSH2_FREE(session, sftp->rename_packet); - sftp->rename_packet = NULL; - - sftp->rename_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_require(sftp, SSH_FXP_STATUS, - sftp->rename_request_id, &data, - &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - sftp->rename_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for FXP STATUS"); - } - - sftp->rename_state = libssh2_NB_state_idle; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - sftp->last_errno = retcode; - - /* now convert the SFTP error code to libssh2 return code or error - message */ - switch (retcode) { - case LIBSSH2_FX_OK: - retcode = LIBSSH2_ERROR_NONE; - break; - - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "File already exists and " - "SSH_FXP_RENAME_OVERWRITE not specified"); - break; - - case LIBSSH2_FX_OP_UNSUPPORTED: - retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Operation Not Supported"); - break; - - default: - retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - break; - } - - return retcode; -} - -/* libssh2_sftp_rename_ex - * Rename a file on the remote server - */ -LIBSSH2_API int -libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename, - unsigned int source_filename_len, - const char *dest_filename, - unsigned int dest_filename_len, long flags) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, - sftp_rename(sftp, source_filename, source_filename_len, - dest_filename, dest_filename_len, flags)); - return rc; -} - -/* - * sftp_fstatvfs - * - * Get file system statistics - */ -static int sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st) -{ - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - /* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4) - + handle_len (4) */ - /* 20 = strlen ("fstatvfs@openssh.com") */ - uint32_t packet_len = handle->handle_len + 20 + 17; - unsigned char *packet, *s, *data; - ssize_t rc; - unsigned int flag; - - if (sftp->fstatvfs_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Getting file system statistics"); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_EXTENDED " - "packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_EXTENDED; - sftp->fstatvfs_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->fstatvfs_request_id); - _libssh2_store_str(&s, "fstatvfs@openssh.com", 20); - _libssh2_store_str(&s, handle->handle, handle->handle_len); - - sftp->fstatvfs_state = libssh2_NB_state_created; - } - else { - packet = sftp->fstatvfs_packet; - } - - if (sftp->fstatvfs_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, packet, packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN || - (0 <= rc && rc < (ssize_t)packet_len)) { - sftp->fstatvfs_packet = packet; - return LIBSSH2_ERROR_EAGAIN; - } - - LIBSSH2_FREE(session, packet); - sftp->fstatvfs_packet = NULL; - - if (rc < 0) { - sftp->fstatvfs_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "_libssh2_channel_write() failed"); - } - sftp->fstatvfs_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_require(sftp, SSH_FXP_EXTENDED_REPLY, - sftp->fstatvfs_request_id, &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - sftp->fstatvfs_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for FXP EXTENDED REPLY"); - } else if (data_len < 93) { - LIBSSH2_FREE(session, data); - sftp->fstatvfs_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error: short response"); - } - - sftp->fstatvfs_state = libssh2_NB_state_idle; - - st->f_bsize = _libssh2_ntohu64(data + 5); - st->f_frsize = _libssh2_ntohu64(data + 13); - st->f_blocks = _libssh2_ntohu64(data + 21); - st->f_bfree = _libssh2_ntohu64(data + 29); - st->f_bavail = _libssh2_ntohu64(data + 37); - st->f_files = _libssh2_ntohu64(data + 45); - st->f_ffree = _libssh2_ntohu64(data + 53); - st->f_favail = _libssh2_ntohu64(data + 61); - st->f_fsid = _libssh2_ntohu64(data + 69); - flag = _libssh2_ntohu64(data + 77); - st->f_namemax = _libssh2_ntohu64(data + 85); - - st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY) - ? LIBSSH2_SFTP_ST_RDONLY : 0; - st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID) - ? LIBSSH2_SFTP_ST_NOSUID : 0; - - LIBSSH2_FREE(session, data); - return 0; -} - -/* libssh2_sftp_fstatvfs - * Get filesystem space and inode utilization (requires fstatvfs@openssh.com - * support on the server) - */ -LIBSSH2_API int -libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st) -{ - int rc; - if(!handle || !st) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, handle->sftp->channel->session, sftp_fstatvfs(handle, st)); - return rc; -} - -/* - * sftp_statvfs - * - * Get file system statistics - */ -static int sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, LIBSSH2_SFTP_STATVFS *st) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - /* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4) - + path_len (4) */ - /* 19 = strlen ("statvfs@openssh.com") */ - uint32_t packet_len = path_len + 19 + 17; - unsigned char *packet, *s, *data; - ssize_t rc; - unsigned int flag; - - if (sftp->statvfs_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Getting file system statistics of %s", path); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_EXTENDED " - "packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_EXTENDED; - sftp->statvfs_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->statvfs_request_id); - _libssh2_store_str(&s, "statvfs@openssh.com", 19); - _libssh2_store_str(&s, path, path_len); - - sftp->statvfs_state = libssh2_NB_state_created; - } - else { - packet = sftp->statvfs_packet; - } - - if (sftp->statvfs_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, packet, packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN || - (0 <= rc && rc < (ssize_t)packet_len)) { - sftp->statvfs_packet = packet; - return LIBSSH2_ERROR_EAGAIN; - } - - LIBSSH2_FREE(session, packet); - sftp->statvfs_packet = NULL; - - if (rc < 0) { - sftp->statvfs_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "_libssh2_channel_write() failed"); - } - sftp->statvfs_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_require(sftp, SSH_FXP_EXTENDED_REPLY, - sftp->statvfs_request_id, &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - sftp->statvfs_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for FXP EXTENDED REPLY"); - } else if (data_len < 93) { - LIBSSH2_FREE(session, data); - sftp->fstatvfs_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error: short response"); - } - - sftp->statvfs_state = libssh2_NB_state_idle; - - st->f_bsize = _libssh2_ntohu64(data + 5); - st->f_frsize = _libssh2_ntohu64(data + 13); - st->f_blocks = _libssh2_ntohu64(data + 21); - st->f_bfree = _libssh2_ntohu64(data + 29); - st->f_bavail = _libssh2_ntohu64(data + 37); - st->f_files = _libssh2_ntohu64(data + 45); - st->f_ffree = _libssh2_ntohu64(data + 53); - st->f_favail = _libssh2_ntohu64(data + 61); - st->f_fsid = _libssh2_ntohu64(data + 69); - flag = _libssh2_ntohu64(data + 77); - st->f_namemax = _libssh2_ntohu64(data + 85); - - st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY) - ? LIBSSH2_SFTP_ST_RDONLY : 0; - st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID) - ? LIBSSH2_SFTP_ST_NOSUID : 0; - - LIBSSH2_FREE(session, data); - return 0; -} - -/* libssh2_sftp_statvfs_ex - * Get filesystem space and inode utilization (requires statvfs@openssh.com - * support on the server) - */ -LIBSSH2_API int -libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path, - size_t path_len, LIBSSH2_SFTP_STATVFS *st) -{ - int rc; - if(!sftp || !st) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, sftp_statvfs(sftp, path, path_len, - st)); - return rc; -} - - -/* - * sftp_mkdir - * - * Create an SFTP directory - */ -static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, long mode) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_ATTRIBUTES attrs = { - LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 - }; - size_t data_len; - int retcode; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - ssize_t packet_len = path_len + 13 + - sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS); - unsigned char *packet, *s, *data; - int rc; - - if (sftp->mkdir_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, - "Creating directory %s with mode 0%lo", path, mode); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_MKDIR " - "packet"); - } - /* Filetype in SFTP 3 and earlier */ - attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR; - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_MKDIR; - sftp->mkdir_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->mkdir_request_id); - _libssh2_store_str(&s, path, path_len); - - s += sftp_attr2bin(s, &attrs); - - sftp->mkdir_state = libssh2_NB_state_created; - } - else { - packet = sftp->mkdir_packet; - } - - if (sftp->mkdir_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, packet, packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - sftp->mkdir_packet = packet; - return rc; - } - if (packet_len != rc) { - LIBSSH2_FREE(session, packet); - sftp->mkdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "_libssh2_channel_write() failed"); - } - LIBSSH2_FREE(session, packet); - sftp->mkdir_state = libssh2_NB_state_sent; - sftp->mkdir_packet = NULL; - } - - rc = sftp_packet_require(sftp, SSH_FXP_STATUS, sftp->mkdir_request_id, - &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - sftp->mkdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for FXP STATUS"); - } - - sftp->mkdir_state = libssh2_NB_state_idle; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - if (retcode == LIBSSH2_FX_OK) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "OK!"); - return 0; - } else { - sftp->last_errno = retcode; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } -} - -/* - * libssh2_sftp_mkdir_ex - * - * Create an SFTP directory - */ -LIBSSH2_API int -libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, long mode) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, - sftp_mkdir(sftp, path, path_len, mode)); - return rc; -} - -/* sftp_rmdir - * Remove a directory - */ -static int sftp_rmdir(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - int retcode; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - ssize_t packet_len = path_len + 13; - unsigned char *s, *data; - int rc; - - if (sftp->rmdir_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Removing directory: %s", - path); - s = sftp->rmdir_packet = LIBSSH2_ALLOC(session, packet_len); - if (!sftp->rmdir_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_RMDIR " - "packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - *(s++) = SSH_FXP_RMDIR; - sftp->rmdir_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->rmdir_request_id); - _libssh2_store_str(&s, path, path_len); - - sftp->rmdir_state = libssh2_NB_state_created; - } - - if (sftp->rmdir_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, sftp->rmdir_packet, - packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (packet_len != rc) { - LIBSSH2_FREE(session, sftp->rmdir_packet); - sftp->rmdir_packet = NULL; - sftp->rmdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send FXP_RMDIR command"); - } - LIBSSH2_FREE(session, sftp->rmdir_packet); - sftp->rmdir_packet = NULL; - - sftp->rmdir_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_require(sftp, SSH_FXP_STATUS, - sftp->rmdir_request_id, &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (rc) { - sftp->rmdir_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Error waiting for FXP STATUS"); - } - - sftp->rmdir_state = libssh2_NB_state_idle; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } -} - -/* libssh2_sftp_rmdir_ex - * Remove a directory - */ -LIBSSH2_API int -libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, - sftp_rmdir(sftp, path, path_len)); - return rc; -} - -/* sftp_stat - * Stat a file or symbolic link - */ -static int sftp_stat(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, int stat_type, - LIBSSH2_SFTP_ATTRIBUTES * attrs) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - ssize_t packet_len = - path_len + 13 + - ((stat_type == - LIBSSH2_SFTP_SETSTAT) ? sftp_attrsize(attrs->flags) : 0); - unsigned char *s, *data; - static const unsigned char stat_responses[2] = - { SSH_FXP_ATTRS, SSH_FXP_STATUS }; - int rc; - - if (sftp->stat_state == libssh2_NB_state_idle) { - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s", - (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : - (stat_type == - LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path); - s = sftp->stat_packet = LIBSSH2_ALLOC(session, packet_len); - if (!sftp->stat_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for FXP_*STAT " - "packet"); - } - - _libssh2_store_u32(&s, packet_len - 4); - - switch (stat_type) { - case LIBSSH2_SFTP_SETSTAT: - *(s++) = SSH_FXP_SETSTAT; - break; - - case LIBSSH2_SFTP_LSTAT: - *(s++) = SSH_FXP_LSTAT; - break; - - case LIBSSH2_SFTP_STAT: - default: - *(s++) = SSH_FXP_STAT; - } - sftp->stat_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->stat_request_id); - _libssh2_store_str(&s, path, path_len); - - if (stat_type == LIBSSH2_SFTP_SETSTAT) - s += sftp_attr2bin(s, attrs); - - sftp->stat_state = libssh2_NB_state_created; - } - - if (sftp->stat_state == libssh2_NB_state_created) { - rc = _libssh2_channel_write(channel, 0, sftp->stat_packet, packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return rc; - } else if (packet_len != rc) { - LIBSSH2_FREE(session, sftp->stat_packet); - sftp->stat_packet = NULL; - sftp->stat_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send STAT/LSTAT/SETSTAT command"); - } - LIBSSH2_FREE(session, sftp->stat_packet); - sftp->stat_packet = NULL; - - sftp->stat_state = libssh2_NB_state_sent; - } - - rc = sftp_packet_requirev(sftp, 2, stat_responses, - sftp->stat_request_id, &data, &data_len); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - else if (rc) { - sftp->stat_state = libssh2_NB_state_idle; - return _libssh2_error(session, rc, - "Timeout waiting for status message"); - } - - sftp->stat_state = libssh2_NB_state_idle; - - if (data[0] == SSH_FXP_STATUS) { - int retcode; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) { - return 0; - } else { - sftp->last_errno = retcode; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } - } - - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - sftp_bin2attr(attrs, data + 5); - LIBSSH2_FREE(session, data); - - return 0; -} - -/* libssh2_sftp_stat_ex - * Stat a file or symbolic link - */ -LIBSSH2_API int -libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, int stat_type, - LIBSSH2_SFTP_ATTRIBUTES *attrs) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, - sftp_stat(sftp, path, path_len, stat_type, attrs)); - return rc; -} - -/* sftp_symlink - * Read or set a symlink - */ -static int sftp_symlink(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, char *target, - unsigned int target_len, int link_type) -{ - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - size_t data_len, link_len; - /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ - ssize_t packet_len = - path_len + 13 + - ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0); - unsigned char *s, *data; - static const unsigned char link_responses[2] = - { SSH_FXP_NAME, SSH_FXP_STATUS }; - int retcode; - - if ((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) { - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Server does not support SYMLINK or READLINK"); - } - - if (sftp->symlink_state == libssh2_NB_state_idle) { - s = sftp->symlink_packet = LIBSSH2_ALLOC(session, packet_len); - if (!sftp->symlink_packet) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "SYMLINK/READLINK/REALPATH packet"); - } - - _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s on %s", - (link_type == - LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading", - (link_type == - LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path); - - _libssh2_store_u32(&s, packet_len - 4); - - switch (link_type) { - case LIBSSH2_SFTP_REALPATH: - *(s++) = SSH_FXP_REALPATH; - break; - - case LIBSSH2_SFTP_SYMLINK: - *(s++) = SSH_FXP_SYMLINK; - break; - - case LIBSSH2_SFTP_READLINK: - default: - *(s++) = SSH_FXP_READLINK; - } - sftp->symlink_request_id = sftp->request_id++; - _libssh2_store_u32(&s, sftp->symlink_request_id); - _libssh2_store_str(&s, path, path_len); - - if (link_type == LIBSSH2_SFTP_SYMLINK) - _libssh2_store_str(&s, target, target_len); - - sftp->symlink_state = libssh2_NB_state_created; - } - - if (sftp->symlink_state == libssh2_NB_state_created) { - ssize_t rc = _libssh2_channel_write(channel, 0, sftp->symlink_packet, - packet_len); - if (rc == LIBSSH2_ERROR_EAGAIN) - return rc; - else if (packet_len != rc) { - LIBSSH2_FREE(session, sftp->symlink_packet); - sftp->symlink_packet = NULL; - sftp->symlink_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send SYMLINK/READLINK command"); - } - LIBSSH2_FREE(session, sftp->symlink_packet); - sftp->symlink_packet = NULL; - - sftp->symlink_state = libssh2_NB_state_sent; - } - - retcode = sftp_packet_requirev(sftp, 2, link_responses, - sftp->symlink_request_id, &data, - &data_len); - if (retcode == LIBSSH2_ERROR_EAGAIN) - return retcode; - else if (retcode) { - sftp->symlink_state = libssh2_NB_state_idle; - return _libssh2_error(session, retcode, - "Error waiting for status message"); - } - - sftp->symlink_state = libssh2_NB_state_idle; - - if (data[0] == SSH_FXP_STATUS) { - int retcode; - - retcode = _libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_OK) - return LIBSSH2_ERROR_NONE; - else { - sftp->last_errno = retcode; - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "SFTP Protocol Error"); - } - } - - if (_libssh2_ntohu32(data + 5) < 1) { - LIBSSH2_FREE(session, data); - return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, - "Invalid READLINK/REALPATH response, " - "no name entries"); - } - - /* this reads a u32 and stores it into a signed 32bit value */ - link_len = _libssh2_ntohu32(data + 9); - if (link_len < target_len) { - memcpy(target, data + 13, link_len); - target[link_len] = 0; - retcode = (int)link_len; - } - else - retcode = LIBSSH2_ERROR_BUFFER_TOO_SMALL; - LIBSSH2_FREE(session, data); - - return retcode; -} - -/* libssh2_sftp_symlink_ex - * Read or set a symlink - */ -LIBSSH2_API int -libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, - unsigned int path_len, char *target, - unsigned int target_len, int link_type) -{ - int rc; - if(!sftp) - return LIBSSH2_ERROR_BAD_USE; - BLOCK_ADJUST(rc, sftp->channel->session, - sftp_symlink(sftp, path, path_len, target, target_len, - link_type)); - return rc; -} - -/* libssh2_sftp_last_error - * Returns the last error code reported by SFTP - */ -LIBSSH2_API unsigned long -libssh2_sftp_last_error(LIBSSH2_SFTP *sftp) -{ - if(!sftp) - return 0; - - return sftp->last_errno; -} - -/* libssh2_sftp_get_channel - * Return the channel of sftp, then caller can control the channel's behavior. - */ -LIBSSH2_API LIBSSH2_CHANNEL * -libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp) -{ - if (!sftp) - return NULL; - - return sftp->channel; -} diff --git a/vendor/libssh2-1.4.2/src/sftp.h b/vendor/libssh2-1.4.2/src/sftp.h deleted file mode 100644 index 55bdb460d..000000000 --- a/vendor/libssh2-1.4.2/src/sftp.h +++ /dev/null @@ -1,230 +0,0 @@ -#ifndef _LIBSSH2_SFTP_H -#define _LIBSSH2_SFTP_H -/* - * Copyright (C) 2010 - 2012 by Daniel Stenberg - * Author: Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - */ - -/* - * MAX_SFTP_OUTGOING_SIZE MUST not be larger than 32500 or so. This is the - * amount of data sent in each FXP_WRITE packet - */ -#define MAX_SFTP_OUTGOING_SIZE 30000 - -/* MAX_SFTP_READ_SIZE is how much data is asked for at max in each FXP_READ - * packets. - */ -#define MAX_SFTP_READ_SIZE 2000 - -struct sftp_pipeline_chunk { - struct list_node node; - size_t len; /* WRITE: size of the data to write - READ: how many bytes that was asked for */ - size_t sent; - ssize_t lefttosend; /* if 0, the entire packet has been sent off */ - uint32_t request_id; - unsigned char packet[1]; /* data */ -}; - -struct sftp_zombie_requests { - struct list_node node; - uint32_t request_id; -}; - -#ifndef MIN -#define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -struct _LIBSSH2_SFTP_PACKET -{ - struct list_node node; /* linked list header */ - uint32_t request_id; - unsigned char *data; - size_t data_len; /* payload size */ -}; - -typedef struct _LIBSSH2_SFTP_PACKET LIBSSH2_SFTP_PACKET; - -#define SFTP_HANDLE_MAXLEN 256 /* according to spec! */ - -struct _LIBSSH2_SFTP_HANDLE -{ - struct list_node node; - - LIBSSH2_SFTP *sftp; - - char handle[SFTP_HANDLE_MAXLEN]; - size_t handle_len; - - enum { - LIBSSH2_SFTP_HANDLE_FILE, - LIBSSH2_SFTP_HANDLE_DIR - } handle_type; - - union _libssh2_sftp_handle_data - { - struct _libssh2_sftp_handle_file_data - { - libssh2_uint64_t offset; - libssh2_uint64_t offset_sent; - size_t acked; /* container for acked data that hasn't been - returned to caller yet, used for sftp_write */ - - /* 'data' is used by sftp_read() and is allocated data that has - been received already from the server but wasn't returned to - the caller yet. It is of size 'data_len' and 'data_left is the - number of bytes not yet returned, counted from the end of the - buffer. */ - unsigned char *data; - size_t data_len; - size_t data_left; - - char eof; /* we have read to the end */ - } file; - struct _libssh2_sftp_handle_dir_data - { - uint32_t names_left; - void *names_packet; - char *next_name; - } dir; - } u; - - /* State variables used in libssh2_sftp_close_handle() */ - libssh2_nonblocking_states close_state; - uint32_t close_request_id; - unsigned char *close_packet; - - /* list of outstanding packets sent to server */ - struct list_head packet_list; - -}; - -struct _LIBSSH2_SFTP -{ - LIBSSH2_CHANNEL *channel; - - uint32_t request_id, version; - - struct list_head packets; - - /* List of FXP_READ responses to ignore because EOF already received. */ - struct list_head zombie_requests; - - /* a list of _LIBSSH2_SFTP_HANDLE structs */ - struct list_head sftp_handles; - - uint32_t last_errno; - - /* Holder for partial packet, use in libssh2_sftp_packet_read() */ - unsigned char partial_size[4]; /* buffer for size field */ - size_t partial_size_len; /* size field length */ - unsigned char *partial_packet; /* The data */ - uint32_t partial_len; /* Desired number of bytes */ - size_t partial_received; /* Bytes received so far */ - - /* Time that libssh2_sftp_packet_requirev() started reading */ - time_t requirev_start; - - /* State variables used in libssh2_sftp_open_ex() */ - libssh2_nonblocking_states open_state; - unsigned char *open_packet; - uint32_t open_packet_len; /* 32 bit on the wire */ - size_t open_packet_sent; - uint32_t open_request_id; - - /* State variable used in sftp_read() */ - libssh2_nonblocking_states read_state; - - /* State variable used in sftp_packet_read() */ - libssh2_nonblocking_states packet_state; - - /* State variable used in sftp_write() */ - libssh2_nonblocking_states write_state; - - /* State variables used in libssh2_sftp_readdir() */ - libssh2_nonblocking_states readdir_state; - unsigned char *readdir_packet; - uint32_t readdir_request_id; - - /* State variables used in libssh2_sftp_fstat_ex() */ - libssh2_nonblocking_states fstat_state; - unsigned char *fstat_packet; - uint32_t fstat_request_id; - - /* State variables used in libssh2_sftp_unlink_ex() */ - libssh2_nonblocking_states unlink_state; - unsigned char *unlink_packet; - uint32_t unlink_request_id; - - /* State variables used in libssh2_sftp_rename_ex() */ - libssh2_nonblocking_states rename_state; - unsigned char *rename_packet; - unsigned char *rename_s; - uint32_t rename_request_id; - - /* State variables used in libssh2_sftp_fstatvfs() */ - libssh2_nonblocking_states fstatvfs_state; - unsigned char *fstatvfs_packet; - uint32_t fstatvfs_request_id; - - /* State variables used in libssh2_sftp_statvfs() */ - libssh2_nonblocking_states statvfs_state; - unsigned char *statvfs_packet; - uint32_t statvfs_request_id; - - /* State variables used in libssh2_sftp_mkdir() */ - libssh2_nonblocking_states mkdir_state; - unsigned char *mkdir_packet; - uint32_t mkdir_request_id; - - /* State variables used in libssh2_sftp_rmdir() */ - libssh2_nonblocking_states rmdir_state; - unsigned char *rmdir_packet; - uint32_t rmdir_request_id; - - /* State variables used in libssh2_sftp_stat() */ - libssh2_nonblocking_states stat_state; - unsigned char *stat_packet; - uint32_t stat_request_id; - - /* State variables used in libssh2_sftp_symlink() */ - libssh2_nonblocking_states symlink_state; - unsigned char *symlink_packet; - uint32_t symlink_request_id; -}; - -#endif diff --git a/vendor/libssh2-1.4.2/src/transport.c b/vendor/libssh2-1.4.2/src/transport.c deleted file mode 100644 index 95b9a3adf..000000000 --- a/vendor/libssh2-1.4.2/src/transport.c +++ /dev/null @@ -1,873 +0,0 @@ -/* Copyright (C) 2007 The Written Word, Inc. All rights reserved. - * Copyright (C) 2009-2010 by Daniel Stenberg - * Author: Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - * This file handles reading and writing to the SECSH transport layer. RFC4253. - */ - -#include "libssh2_priv.h" -#include -#include -#include -#ifdef LIBSSH2DEBUG -#include -#endif - -#include - -#include "transport.h" -#include "mac.h" - -#define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */ -#define MAX_MACSIZE 20 /* MUST fit biggest MAC length we support */ - -#ifdef LIBSSH2DEBUG -#define UNPRINTABLE_CHAR '.' -static void -debugdump(LIBSSH2_SESSION * session, - const char *desc, const unsigned char *ptr, size_t size) -{ - size_t i; - size_t c; - unsigned int width = 0x10; - char buffer[256]; /* Must be enough for width*4 + about 30 or so */ - size_t used; - static const char* hex_chars = "0123456789ABCDEF"; - - if (!(session->showmask & LIBSSH2_TRACE_TRANS)) { - /* not asked for, bail out */ - return; - } - - used = snprintf(buffer, sizeof(buffer), "=> %s (%d bytes)\n", - desc, (int) size); - if (session->tracehandler) - (session->tracehandler)(session, session->tracehandler_context, - buffer, used); - else - fprintf(stderr, "%s", buffer); - - for(i = 0; i < size; i += width) { - - used = snprintf(buffer, sizeof(buffer), "%04lx: ", (long)i); - - /* hex not disabled, show it */ - for(c = 0; c < width; c++) { - if (i + c < size) { - buffer[used++] = hex_chars[(ptr[i+c] >> 4) & 0xF]; - buffer[used++] = hex_chars[ptr[i+c] & 0xF]; - } - else { - buffer[used++] = ' '; - buffer[used++] = ' '; - } - - buffer[used++] = ' '; - if ((width/2) - 1 == c) - buffer[used++] = ' '; - } - - buffer[used++] = ':'; - buffer[used++] = ' '; - - for(c = 0; (c < width) && (i + c < size); c++) { - buffer[used++] = isprint(ptr[i + c]) ? - ptr[i + c] : UNPRINTABLE_CHAR; - } - buffer[used++] = '\n'; - buffer[used] = 0; - - if (session->tracehandler) - (session->tracehandler)(session, session->tracehandler_context, - buffer, used); - else - fprintf(stderr, "%s", buffer); - } -} -#else -#define debugdump(a,x,y,z) -#endif - - -/* decrypt() decrypts 'len' bytes from 'source' to 'dest'. - * - * returns 0 on success and negative on failure - */ - -static int -decrypt(LIBSSH2_SESSION * session, unsigned char *source, - unsigned char *dest, int len) -{ - struct transportpacket *p = &session->packet; - int blocksize = session->remote.crypt->blocksize; - - /* if we get called with a len that isn't an even number of blocksizes - we risk losing those extra bytes */ - assert((len % blocksize) == 0); - - while (len >= blocksize) { - if (session->remote.crypt->crypt(session, source, - &session->remote.crypt_abstract)) { - LIBSSH2_FREE(session, p->payload); - return LIBSSH2_ERROR_DECRYPT; - } - - /* if the crypt() function would write to a given address it - wouldn't have to memcpy() and we could avoid this memcpy() - too */ - memcpy(dest, source, blocksize); - - len -= blocksize; /* less bytes left */ - dest += blocksize; /* advance write pointer */ - source += blocksize; /* advance read pointer */ - } - return LIBSSH2_ERROR_NONE; /* all is fine */ -} - -/* - * fullpacket() gets called when a full packet has been received and properly - * collected. - */ -static int -fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ ) -{ - unsigned char macbuf[MAX_MACSIZE]; - struct transportpacket *p = &session->packet; - int rc; - - if (session->fullpacket_state == libssh2_NB_state_idle) { - session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED; - session->fullpacket_payload_len = p->packet_length - 1; - - if (encrypted) { - - /* Calculate MAC hash */ - session->remote.mac->hash(session, macbuf, /* store hash here */ - session->remote.seqno, - p->init, 5, - p->payload, - session->fullpacket_payload_len, - &session->remote.mac_abstract); - - /* Compare the calculated hash with the MAC we just read from - * the network. The read one is at the very end of the payload - * buffer. Note that 'payload_len' here is the packet_length - * field which includes the padding but not the MAC. - */ - if (memcmp(macbuf, p->payload + session->fullpacket_payload_len, - session->remote.mac->mac_len)) { - session->fullpacket_macstate = LIBSSH2_MAC_INVALID; - } - } - - session->remote.seqno++; - - /* ignore the padding */ - session->fullpacket_payload_len -= p->padding_length; - - /* Check for and deal with decompression */ - if (session->remote.comp && - session->remote.comp->compress && - session->remote.comp_abstract) { - /* - * The buffer for the decompression (remote.comp_abstract) is - * initialised in time when it is needed so as long it is NULL we - * cannot decompress. - */ - - unsigned char *data; - size_t data_len; - rc = session->remote.comp->decomp(session, - &data, &data_len, - LIBSSH2_PACKET_MAXDECOMP, - p->payload, - session->fullpacket_payload_len, - &session->remote.comp_abstract); - LIBSSH2_FREE(session, p->payload); - if(rc) - return rc; - - p->payload = data; - session->fullpacket_payload_len = data_len; - } - - session->fullpacket_packet_type = p->payload[0]; - - debugdump(session, "libssh2_transport_read() plain", - p->payload, session->fullpacket_payload_len); - - session->fullpacket_state = libssh2_NB_state_created; - } - - if (session->fullpacket_state == libssh2_NB_state_created) { - rc = _libssh2_packet_add(session, p->payload, - session->fullpacket_payload_len, - session->fullpacket_macstate); - if (rc) - return rc; - } - - session->fullpacket_state = libssh2_NB_state_idle; - - return session->fullpacket_packet_type; -} - - -/* - * _libssh2_transport_read - * - * Collect a packet into the input queue. - * - * Returns packet type added to input queue (0 if nothing added), or a - * negative error number. - */ - -/* - * This function reads the binary stream as specified in chapter 6 of RFC4253 - * "The Secure Shell (SSH) Transport Layer Protocol" - * - * DOES NOT call _libssh2_error() for ANY error case. - */ -int _libssh2_transport_read(LIBSSH2_SESSION * session) -{ - int rc; - struct transportpacket *p = &session->packet; - int remainbuf; - int remainpack; - int numbytes; - int numdecrypt; - unsigned char block[MAX_BLOCKSIZE]; - int blocksize; - int encrypted = 1; - size_t total_num; - - /* default clear the bit */ - session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND; - - /* - * All channels, systems, subsystems, etc eventually make it down here - * when looking for more incoming data. If a key exchange is going on - * (LIBSSH2_STATE_EXCHANGING_KEYS bit is set) then the remote end will - * ONLY send key exchange related traffic. In non-blocking mode, there is - * a chance to break out of the kex_exchange function with an EAGAIN - * status, and never come back to it. If LIBSSH2_STATE_EXCHANGING_KEYS is - * active, then we must redirect to the key exchange. However, if - * kex_exchange is active (as in it is the one that calls this execution - * of packet_read, then don't redirect, as that would be an infinite loop! - */ - - if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS && - !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) { - - /* Whoever wants a packet won't get anything until the key re-exchange - * is done! - */ - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the" - " key re-exchange from _libssh2_transport_read"); - rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state); - if (rc) - return rc; - } - - /* - * =============================== NOTE =============================== - * I know this is very ugly and not a really good use of "goto", but - * this case statement would be even uglier to do it any other way - */ - if (session->readPack_state == libssh2_NB_state_jump1) { - session->readPack_state = libssh2_NB_state_idle; - encrypted = session->readPack_encrypted; - goto libssh2_transport_read_point1; - } - - do { - if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { - return LIBSSH2_ERROR_NONE; - } - - if (session->state & LIBSSH2_STATE_NEWKEYS) { - blocksize = session->remote.crypt->blocksize; - } else { - encrypted = 0; /* not encrypted */ - blocksize = 5; /* not strictly true, but we can use 5 here to - make the checks below work fine still */ - } - - /* read/use a whole big chunk into a temporary area stored in - the LIBSSH2_SESSION struct. We will decrypt data from that - buffer into the packet buffer so this temp one doesn't have - to be able to keep a whole SSH packet, just be large enough - so that we can read big chunks from the network layer. */ - - /* how much data there is remaining in the buffer to deal with - before we should read more from the network */ - remainbuf = p->writeidx - p->readidx; - - /* if remainbuf turns negative we have a bad internal error */ - assert(remainbuf >= 0); - - if (remainbuf < blocksize) { - /* If we have less than a blocksize left, it is too - little data to deal with, read more */ - ssize_t nread; - - /* move any remainder to the start of the buffer so - that we can do a full refill */ - if (remainbuf) { - memmove(p->buf, &p->buf[p->readidx], remainbuf); - p->readidx = 0; - p->writeidx = remainbuf; - } else { - /* nothing to move, just zero the indexes */ - p->readidx = p->writeidx = 0; - } - - /* now read a big chunk from the network into the temp buffer */ - nread = - LIBSSH2_RECV(session, &p->buf[remainbuf], - PACKETBUFSIZE - remainbuf, - LIBSSH2_SOCKET_RECV_FLAGS(session)); - if (nread <= 0) { - /* check if this is due to EAGAIN and return the special - return code if so, error out normally otherwise */ - if ((nread < 0) && (nread == -EAGAIN)) { - session->socket_block_directions |= - LIBSSH2_SESSION_BLOCK_INBOUND; - return LIBSSH2_ERROR_EAGAIN; - } - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Error recving %d bytes (got %d)", - PACKETBUFSIZE - remainbuf, -nread); - return LIBSSH2_ERROR_SOCKET_RECV; - } - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Recved %d/%d bytes to %p+%d", nread, - PACKETBUFSIZE - remainbuf, p->buf, remainbuf); - - debugdump(session, "libssh2_transport_read() raw", - &p->buf[remainbuf], nread); - /* advance write pointer */ - p->writeidx += nread; - - /* update remainbuf counter */ - remainbuf = p->writeidx - p->readidx; - } - - /* how much data to deal with from the buffer */ - numbytes = remainbuf; - - if (!p->total_num) { - /* No payload package area allocated yet. To know the - size of this payload, we need to decrypt the first - blocksize data. */ - - if (numbytes < blocksize) { - /* we can't act on anything less than blocksize, but this - check is only done for the initial block since once we have - got the start of a block we can in fact deal with fractions - */ - session->socket_block_directions |= - LIBSSH2_SESSION_BLOCK_INBOUND; - return LIBSSH2_ERROR_EAGAIN; - } - - if (encrypted) { - rc = decrypt(session, &p->buf[p->readidx], block, blocksize); - if (rc != LIBSSH2_ERROR_NONE) { - return rc; - } - /* save the first 5 bytes of the decrypted package, to be - used in the hash calculation later down. */ - memcpy(p->init, &p->buf[p->readidx], 5); - } else { - /* the data is plain, just copy it verbatim to - the working block buffer */ - memcpy(block, &p->buf[p->readidx], blocksize); - } - - /* advance the read pointer */ - p->readidx += blocksize; - - /* we now have the initial blocksize bytes decrypted, - * and we can extract packet and padding length from it - */ - p->packet_length = _libssh2_ntohu32(block); - if (p->packet_length < 1) - return LIBSSH2_ERROR_DECRYPT; - - p->padding_length = block[4]; - - /* total_num is the number of bytes following the initial - (5 bytes) packet length and padding length fields */ - total_num = - p->packet_length - 1 + - (encrypted ? session->remote.mac->mac_len : 0); - - /* RFC4253 section 6.1 Maximum Packet Length says: - * - * "All implementations MUST be able to process - * packets with uncompressed payload length of 32768 - * bytes or less and total packet size of 35000 bytes - * or less (including length, padding length, payload, - * padding, and MAC.)." - */ - if (total_num > LIBSSH2_PACKET_MAXPAYLOAD) { - return LIBSSH2_ERROR_OUT_OF_BOUNDARY; - } - - /* Get a packet handle put data into. We get one to - hold all data, including padding and MAC. */ - p->payload = LIBSSH2_ALLOC(session, total_num); - if (!p->payload) { - return LIBSSH2_ERROR_ALLOC; - } - p->total_num = total_num; - /* init write pointer to start of payload buffer */ - p->wptr = p->payload; - - if (blocksize > 5) { - /* copy the data from index 5 to the end of - the blocksize from the temporary buffer to - the start of the decrypted buffer */ - memcpy(p->wptr, &block[5], blocksize - 5); - p->wptr += blocksize - 5; /* advance write pointer */ - } - - /* init the data_num field to the number of bytes of - the package read so far */ - p->data_num = p->wptr - p->payload; - - /* we already dealt with a blocksize worth of data */ - numbytes -= blocksize; - } - - /* how much there is left to add to the current payload - package */ - remainpack = p->total_num - p->data_num; - - if (numbytes > remainpack) { - /* if we have more data in the buffer than what is going into this - particular packet, we limit this round to this packet only */ - numbytes = remainpack; - } - - if (encrypted) { - /* At the end of the incoming stream, there is a MAC, - and we don't want to decrypt that since we need it - "raw". We MUST however decrypt the padding data - since it is used for the hash later on. */ - int skip = session->remote.mac->mac_len; - - /* if what we have plus numbytes is bigger than the - total minus the skip margin, we should lower the - amount to decrypt even more */ - if ((p->data_num + numbytes) > (p->total_num - skip)) { - numdecrypt = (p->total_num - skip) - p->data_num; - } else { - int frac; - numdecrypt = numbytes; - frac = numdecrypt % blocksize; - if (frac) { - /* not an aligned amount of blocks, - align it */ - numdecrypt -= frac; - /* and make it no unencrypted data - after it */ - numbytes = 0; - } - } - } else { - /* unencrypted data should not be decrypted at all */ - numdecrypt = 0; - } - - /* if there are bytes to decrypt, do that */ - if (numdecrypt > 0) { - /* now decrypt the lot */ - rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt); - if (rc != LIBSSH2_ERROR_NONE) { - return rc; - } - - /* advance the read pointer */ - p->readidx += numdecrypt; - /* advance write pointer */ - p->wptr += numdecrypt; - /* increse data_num */ - p->data_num += numdecrypt; - - /* bytes left to take care of without decryption */ - numbytes -= numdecrypt; - } - - /* if there are bytes to copy that aren't decrypted, simply - copy them as-is to the target buffer */ - if (numbytes > 0) { - memcpy(p->wptr, &p->buf[p->readidx], numbytes); - - /* advance the read pointer */ - p->readidx += numbytes; - /* advance write pointer */ - p->wptr += numbytes; - /* increse data_num */ - p->data_num += numbytes; - } - - /* now check how much data there's left to read to finish the - current packet */ - remainpack = p->total_num - p->data_num; - - if (!remainpack) { - /* we have a full packet */ - libssh2_transport_read_point1: - rc = fullpacket(session, encrypted); - if (rc == LIBSSH2_ERROR_EAGAIN) { - - if (session->packAdd_state != libssh2_NB_state_idle) - { - /* fullpacket only returns LIBSSH2_ERROR_EAGAIN if - * libssh2_packet_add returns LIBSSH2_ERROR_EAGAIN. If that - * returns LIBSSH2_ERROR_EAGAIN but the packAdd_state is idle, - * then the packet has been added to the brigade, but some - * immediate action that was taken based on the packet - * type (such as key re-exchange) is not yet complete. - * Clear the way for a new packet to be read in. - */ - session->readPack_encrypted = encrypted; - session->readPack_state = libssh2_NB_state_jump1; - } - - return rc; - } - - p->total_num = 0; /* no packet buffer available */ - - return rc; - } - } while (1); /* loop */ - - return LIBSSH2_ERROR_SOCKET_RECV; /* we never reach this point */ -} - -static int -send_existing(LIBSSH2_SESSION *session, const unsigned char *data, - size_t data_len, ssize_t *ret) -{ - ssize_t rc; - ssize_t length; - struct transportpacket *p = &session->packet; - - if (!p->olen) { - *ret = 0; - return LIBSSH2_ERROR_NONE; - } - - /* send as much as possible of the existing packet */ - if ((data != p->odata) || (data_len != p->olen)) { - /* When we are about to complete the sending of a packet, it is vital - that the caller doesn't try to send a new/different packet since - we don't add this one up until the previous one has been sent. To - make the caller really notice his/hers flaw, we return error for - this case */ - return LIBSSH2_ERROR_BAD_USE; - } - - *ret = 1; /* set to make our parent return */ - - /* number of bytes left to send */ - length = p->ototal_num - p->osent; - - rc = LIBSSH2_SEND(session, &p->outbuf[p->osent], length, - LIBSSH2_SOCKET_SEND_FLAGS(session)); - if (rc < 0) - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Error sending %d bytes: %d", length, -rc); - else { - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Sent %d/%d bytes at %p+%d", rc, length, p->outbuf, - p->osent); - debugdump(session, "libssh2_transport_write send()", - &p->outbuf[p->osent], rc); - } - - if (rc == length) { - /* the remainder of the package was sent */ - p->ototal_num = 0; - p->olen = 0; - /* we leave *ret set so that the parent returns as we MUST return back - a send success now, so that we don't risk sending EAGAIN later - which then would confuse the parent function */ - return LIBSSH2_ERROR_NONE; - - } - else if (rc < 0) { - /* nothing was sent */ - if (rc != -EAGAIN) - /* send failure! */ - return LIBSSH2_ERROR_SOCKET_SEND; - - session->socket_block_directions |= LIBSSH2_SESSION_BLOCK_OUTBOUND; - return LIBSSH2_ERROR_EAGAIN; - } - - p->osent += rc; /* we sent away this much data */ - - return rc < length ? LIBSSH2_ERROR_EAGAIN : LIBSSH2_ERROR_NONE; -} - -/* - * libssh2_transport_send - * - * Send a packet, encrypting it and adding a MAC code if necessary - * Returns 0 on success, non-zero on failure. - * - * The data is provided as _two_ data areas that are combined by this - * function. The 'data' part is sent immediately before 'data2'. 'data2' may - * be set to NULL to only use a single part. - * - * Returns LIBSSH2_ERROR_EAGAIN if it would block or if the whole packet was - * not sent yet. If it does so, the caller should call this function again as - * soon as it is likely that more data can be sent, and this function MUST - * then be called with the same argument set (same data pointer and same - * data_len) until ERROR_NONE or failure is returned. - * - * This function DOES NOT call _libssh2_error() on any errors. - */ -int _libssh2_transport_send(LIBSSH2_SESSION *session, - const unsigned char *data, size_t data_len, - const unsigned char *data2, size_t data2_len) -{ - int blocksize = - (session->state & LIBSSH2_STATE_NEWKEYS) ? - session->local.crypt->blocksize : 8; - int padding_length; - size_t packet_length; - int total_length; -#ifdef RANDOM_PADDING - int rand_max; - int seed = data[0]; /* FIXME: make this random */ -#endif - struct transportpacket *p = &session->packet; - int encrypted; - ssize_t ret; - int rc; - const unsigned char *orgdata = data; - size_t orgdata_len = data_len; - - /* - * If the last read operation was interrupted in the middle of a key - * exchange, we must complete that key exchange before continuing to write - * further data. - * - * See the similar block in _libssh2_transport_read for more details. - */ - if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS && - !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) { - /* Don't write any new packets if we're still in the middle of a key - * exchange. */ - _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the" - " key re-exchange from _libssh2_transport_send"); - rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state); - if (rc) - return rc; - } - - debugdump(session, "libssh2_transport_write plain", data, data_len); - if(data2) - debugdump(session, "libssh2_transport_write plain2", data2, data2_len); - - /* FIRST, check if we have a pending write to complete. send_existing - only sanity-check data and data_len and not data2 and data2_len!! */ - rc = send_existing(session, data, data_len, &ret); - if (rc) - return rc; - - session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND; - - if (ret) - /* set by send_existing if data was sent */ - return rc; - - encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0; - - if (encrypted && session->local.comp->compress) { - /* the idea here is that these function must fail if the output gets - larger than what fits in the assigned buffer so thus they don't - check the input size as we don't know how much it compresses */ - size_t dest_len = MAX_SSH_PACKET_LEN-5-256; - size_t dest2_len = dest_len; - - /* compress directly to the target buffer */ - rc = session->local.comp->comp(session, - &p->outbuf[5], &dest_len, - data, data_len, - &session->local.comp_abstract); - if(rc) - return rc; /* compression failure */ - - if(data2 && data2_len) { - /* compress directly to the target buffer right after where the - previous call put data */ - dest2_len -= dest_len; - - rc = session->local.comp->comp(session, - &p->outbuf[5+dest_len], &dest2_len, - data2, data2_len, - &session->local.comp_abstract); - } - else - dest2_len = 0; - if(rc) - return rc; /* compression failure */ - - data_len = dest_len + dest2_len; /* use the combined length */ - } - else { - if((data_len + data2_len) >= (MAX_SSH_PACKET_LEN-0x100)) - /* too large packet, return error for this until we make this - function split it up and send multiple SSH packets */ - return LIBSSH2_ERROR_INVAL; - - /* copy the payload data */ - memcpy(&p->outbuf[5], data, data_len); - if(data2 && data2_len) - memcpy(&p->outbuf[5+data_len], data2, data2_len); - data_len += data2_len; /* use the combined length */ - } - - - /* RFC4253 says: Note that the length of the concatenation of - 'packet_length', 'padding_length', 'payload', and 'random padding' - MUST be a multiple of the cipher block size or 8, whichever is - larger. */ - - /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == 0 */ - - packet_length = data_len + 1 + 4; /* 1 is for padding_length field - 4 for the packet_length field */ - - /* at this point we have it all except the padding */ - - /* first figure out our minimum padding amount to make it an even - block size */ - padding_length = blocksize - (packet_length % blocksize); - - /* if the padding becomes too small we add another blocksize worth - of it (taken from the original libssh2 where it didn't have any - real explanation) */ - if (padding_length < 4) { - padding_length += blocksize; - } -#ifdef RANDOM_PADDING - /* FIXME: we can add padding here, but that also makes the packets - bigger etc */ - - /* now we can add 'blocksize' to the padding_length N number of times - (to "help thwart traffic analysis") but it must be less than 255 in - total */ - rand_max = (255 - padding_length) / blocksize + 1; - padding_length += blocksize * (seed % rand_max); -#endif - - packet_length += padding_length; - - /* append the MAC length to the total_length size */ - total_length = - packet_length + (encrypted ? session->local.mac->mac_len : 0); - - /* store packet_length, which is the size of the whole packet except - the MAC and the packet_length field itself */ - _libssh2_htonu32(p->outbuf, packet_length - 4); - /* store padding_length */ - p->outbuf[4] = padding_length; - - /* fill the padding area with random junk */ - _libssh2_random(p->outbuf + 5 + data_len, padding_length); - - if (encrypted) { - size_t i; - - /* Calculate MAC hash. Put the output at index packet_length, - since that size includes the whole packet. The MAC is - calculated on the entire unencrypted packet, including all - fields except the MAC field itself. */ - session->local.mac->hash(session, p->outbuf + packet_length, - session->local.seqno, p->outbuf, - packet_length, NULL, 0, - &session->local.mac_abstract); - - /* Encrypt the whole packet data, one block size at a time. - The MAC field is not encrypted. */ - for(i = 0; i < packet_length; i += session->local.crypt->blocksize) { - unsigned char *ptr = &p->outbuf[i]; - if (session->local.crypt->crypt(session, ptr, - &session->local.crypt_abstract)) - return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */ - } - } - - session->local.seqno++; - - ret = LIBSSH2_SEND(session, p->outbuf, total_length, - LIBSSH2_SOCKET_SEND_FLAGS(session)); - if (ret < 0) - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, - "Error sending %d bytes: %d", total_length, -ret); - else { - _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, "Sent %d/%d bytes at %p", - ret, total_length, p->outbuf); - debugdump(session, "libssh2_transport_write send()", p->outbuf, ret); - } - - if (ret != total_length) { - if (ret >= 0 || ret == -EAGAIN) { - /* the whole packet could not be sent, save the rest */ - session->socket_block_directions |= LIBSSH2_SESSION_BLOCK_OUTBOUND; - p->odata = orgdata; - p->olen = orgdata_len; - p->osent = ret <= 0 ? 0 : ret; - p->ototal_num = total_length; - return LIBSSH2_ERROR_EAGAIN; - } - return LIBSSH2_ERROR_SOCKET_SEND; - } - - /* the whole thing got sent away */ - p->odata = NULL; - p->olen = 0; - - return LIBSSH2_ERROR_NONE; /* all is good */ -} diff --git a/vendor/libssh2-1.4.2/src/transport.h b/vendor/libssh2-1.4.2/src/transport.h deleted file mode 100644 index 89982a67f..000000000 --- a/vendor/libssh2-1.4.2/src/transport.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __LIBSSH2_TRANSPORT_H -#define __LIBSSH2_TRANSPORT_H - -/* Copyright (C) 2007 The Written Word, Inc. All rights reserved. - * Copyright (C) 2009-2010 by Daniel Stenberg - * Author: Daniel Stenberg - * - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - * This file handles reading and writing to the SECSH transport layer. RFC4253. - */ - -#include "libssh2_priv.h" -#include "packet.h" - - -/* - * libssh2_transport_send - * - * Send a packet, encrypting it and adding a MAC code if necessary - * Returns 0 on success, non-zero on failure. - * - * The data is provided as _two_ data areas that are combined by this - * function. The 'data' part is sent immediately before 'data2'. 'data2' can - * be set to NULL (or data2_len to 0) to only use a single part. - * - * Returns LIBSSH2_ERROR_EAGAIN if it would block or if the whole packet was - * not sent yet. If it does so, the caller should call this function again as - * soon as it is likely that more data can be sent, and this function MUST - * then be called with the same argument set (same data pointer and same - * data_len) until ERROR_NONE or failure is returned. - * - * This function DOES NOT call _libssh2_error() on any errors. - */ -int _libssh2_transport_send(LIBSSH2_SESSION *session, - const unsigned char *data, size_t data_len, - const unsigned char *data2, size_t data2_len); - -/* - * _libssh2_transport_read - * - * Collect a packet into the input brigade block only controls whether or not - * to wait for a packet to start. - * - * Returns packet type added to input brigade (PACKET_NONE if nothing added), - * or PACKET_FAIL on failure and PACKET_EAGAIN if it couldn't process a full - * packet. - */ - -/* - * This function reads the binary stream as specified in chapter 6 of RFC4253 - * "The Secure Shell (SSH) Transport Layer Protocol" - */ -int _libssh2_transport_read(LIBSSH2_SESSION * session); - -#endif /* __LIBSSH2_TRANSPORT_H */ diff --git a/vendor/libssh2-1.4.2/src/userauth.c b/vendor/libssh2-1.4.2/src/userauth.c deleted file mode 100644 index a0733d5da..000000000 --- a/vendor/libssh2-1.4.2/src/userauth.c +++ /dev/null @@ -1,1687 +0,0 @@ -/* Copyright (c) 2004-2007, Sara Golemon - * Copyright (c) 2005 Mikhail Gusarov - * Copyright (c) 2009-2011 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -#include "libssh2_priv.h" - -#include -#include - -#include - -/* Needed for struct iovec on some platforms */ -#ifdef HAVE_SYS_UIO_H -#include -#endif - -#include "transport.h" -#include "session.h" -#include "userauth.h" - -/* libssh2_userauth_list - * - * List authentication methods - * Will yield successful login if "none" happens to be allowable for this user - * Not a common configuration for any SSH server though - * username should be NULL, or a null terminated string - */ -static char *userauth_list(LIBSSH2_SESSION *session, const char *username, - unsigned int username_len) -{ - static const unsigned char reply_codes[3] = - { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; - /* packet_type(1) + username_len(4) + service_len(4) + - service(14)"ssh-connection" + method_len(4) = 27 */ - unsigned long methods_len; - unsigned char *s; - int rc; - - if (session->userauth_list_state == libssh2_NB_state_idle) { - /* Zero the whole thing out */ - memset(&session->userauth_list_packet_requirev_state, 0, - sizeof(session->userauth_list_packet_requirev_state)); - - session->userauth_list_data_len = username_len + 27; - - s = session->userauth_list_data = - LIBSSH2_ALLOC(session, session->userauth_list_data_len); - if (!session->userauth_list_data) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for userauth_list"); - return NULL; - } - - *(s++) = SSH_MSG_USERAUTH_REQUEST; - _libssh2_store_str(&s, username, username_len); - _libssh2_store_str(&s, "ssh-connection", 14); - _libssh2_store_u32(&s, 4); /* send "none" separately */ - - session->userauth_list_state = libssh2_NB_state_created; - } - - if (session->userauth_list_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, session->userauth_list_data, - session->userauth_list_data_len, - (unsigned char *)"none", 4); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting userauth list"); - return NULL; - } - /* now free the packet that was sent */ - LIBSSH2_FREE(session, session->userauth_list_data); - session->userauth_list_data = NULL; - - if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-none request"); - session->userauth_list_state = libssh2_NB_state_idle; - return NULL; - } - - session->userauth_list_state = libssh2_NB_state_sent; - } - - if (session->userauth_list_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_list_data, - &session->userauth_list_data_len, 0, - NULL, 0, - &session->userauth_list_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting userauth list"); - return NULL; - } else if (rc) { - _libssh2_error(session, rc, "Failed getting response"); - session->userauth_list_state = libssh2_NB_state_idle; - return NULL; - } - - if (session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) { - /* Wow, who'dve thought... */ - _libssh2_error(session, LIBSSH2_ERROR_NONE, "No error"); - LIBSSH2_FREE(session, session->userauth_list_data); - session->userauth_list_data = NULL; - session->state |= LIBSSH2_STATE_AUTHENTICATED; - session->userauth_list_state = libssh2_NB_state_idle; - return NULL; - } - - methods_len = _libssh2_ntohu32(session->userauth_list_data + 1); - - /* Do note that the memory areas overlap! */ - memmove(session->userauth_list_data, session->userauth_list_data + 5, - methods_len); - session->userauth_list_data[methods_len] = '\0'; - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Permitted auth methods: %s", - session->userauth_list_data); - } - - session->userauth_list_state = libssh2_NB_state_idle; - return (char *) session->userauth_list_data; -} - -/* libssh2_userauth_list - * - * List authentication methods - * Will yield successful login if "none" happens to be allowable for this user - * Not a common configuration for any SSH server though - * username should be NULL, or a null terminated string - */ -LIBSSH2_API char * -libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user, - unsigned int user_len) -{ - char *ptr; - BLOCK_ADJUST_ERRNO(ptr, session, - userauth_list(session, user, user_len)); - return ptr; -} - -/* - * libssh2_userauth_authenticated - * - * Returns: 0 if not yet authenticated - * 1 if already authenticated - */ -LIBSSH2_API int -libssh2_userauth_authenticated(LIBSSH2_SESSION * session) -{ - return (session->state & LIBSSH2_STATE_AUTHENTICATED)?1:0; -} - - - -/* userauth_password - * Plain ol' login - */ -static int -userauth_password(LIBSSH2_SESSION *session, - const char *username, unsigned int username_len, - const unsigned char *password, unsigned int password_len, - LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) -{ - unsigned char *s; - static const unsigned char reply_codes[4] = - { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, - SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 - }; - int rc; - - if (session->userauth_pswd_state == libssh2_NB_state_idle) { - /* Zero the whole thing out */ - memset(&session->userauth_pswd_packet_requirev_state, 0, - sizeof(session->userauth_pswd_packet_requirev_state)); - - /* - * 40 = acket_type(1) + username_len(4) + service_len(4) + - * service(14)"ssh-connection" + method_len(4) + method(8)"password" + - * chgpwdbool(1) + password_len(4) */ - session->userauth_pswd_data_len = username_len + 40; - - session->userauth_pswd_data0 = ~SSH_MSG_USERAUTH_PASSWD_CHANGEREQ; - - /* TODO: remove this alloc with a fixed buffer in the session - struct */ - s = session->userauth_pswd_data = - LIBSSH2_ALLOC(session, session->userauth_pswd_data_len); - if (!session->userauth_pswd_data) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "userauth-password request"); - } - - *(s++) = SSH_MSG_USERAUTH_REQUEST; - _libssh2_store_str(&s, username, username_len); - _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1); - _libssh2_store_str(&s, "password", sizeof("password") - 1); - *s++ = '\0'; - _libssh2_store_u32(&s, password_len); - /* 'password' is sent separately */ - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Attempting to login using password authentication"); - - session->userauth_pswd_state = libssh2_NB_state_created; - } - - if (session->userauth_pswd_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, session->userauth_pswd_data, - session->userauth_pswd_data_len, - password, password_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block writing password request"); - } - - /* now free the sent packet */ - LIBSSH2_FREE(session, session->userauth_pswd_data); - session->userauth_pswd_data = NULL; - - if (rc) { - session->userauth_pswd_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-password request"); - } - - session->userauth_pswd_state = libssh2_NB_state_sent; - } - - password_response: - - if ((session->userauth_pswd_state == libssh2_NB_state_sent) - || (session->userauth_pswd_state == libssh2_NB_state_sent1) - || (session->userauth_pswd_state == libssh2_NB_state_sent2)) { - if (session->userauth_pswd_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_pswd_data, - &session->userauth_pswd_data_len, - 0, NULL, 0, - &session-> - userauth_pswd_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting"); - } else if (rc) { - session->userauth_pswd_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, - "Would block waiting"); - } - - if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Password authentication successful"); - LIBSSH2_FREE(session, session->userauth_pswd_data); - session->userauth_pswd_data = NULL; - session->state |= LIBSSH2_STATE_AUTHENTICATED; - session->userauth_pswd_state = libssh2_NB_state_idle; - return 0; - } else if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_FAILURE) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Password authentication failed"); - LIBSSH2_FREE(session, session->userauth_pswd_data); - session->userauth_pswd_data = NULL; - session->userauth_pswd_state = libssh2_NB_state_idle; - return _libssh2_error(session, - LIBSSH2_ERROR_AUTHENTICATION_FAILED, - "Authentication failed " - "(username/password)"); - } - - session->userauth_pswd_newpw = NULL; - session->userauth_pswd_newpw_len = 0; - - session->userauth_pswd_state = libssh2_NB_state_sent1; - } - - if ((session->userauth_pswd_data[0] == - SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) - || (session->userauth_pswd_data0 == - SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)) { - session->userauth_pswd_data0 = SSH_MSG_USERAUTH_PASSWD_CHANGEREQ; - - if ((session->userauth_pswd_state == libssh2_NB_state_sent1) || - (session->userauth_pswd_state == libssh2_NB_state_sent2)) { - if (session->userauth_pswd_state == libssh2_NB_state_sent1) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Password change required"); - LIBSSH2_FREE(session, session->userauth_pswd_data); - session->userauth_pswd_data = NULL; - } - if (passwd_change_cb) { - if (session->userauth_pswd_state == libssh2_NB_state_sent1) { - passwd_change_cb(session, - &session->userauth_pswd_newpw, - &session->userauth_pswd_newpw_len, - &session->abstract); - if (!session->userauth_pswd_newpw) { - return _libssh2_error(session, - LIBSSH2_ERROR_PASSWORD_EXPIRED, - "Password expired, and " - "callback failed"); - } - - /* basic data_len + newpw_len(4) */ - session->userauth_pswd_data_len = - username_len + password_len + 44; - - s = session->userauth_pswd_data = - LIBSSH2_ALLOC(session, - session->userauth_pswd_data_len); - if (!session->userauth_pswd_data) { - LIBSSH2_FREE(session, - session->userauth_pswd_newpw); - session->userauth_pswd_newpw = NULL; - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory " - "for userauth password " - "change request"); - } - - *(s++) = SSH_MSG_USERAUTH_REQUEST; - _libssh2_store_str(&s, username, username_len); - _libssh2_store_str(&s, "ssh-connection", - sizeof("ssh-connection") - 1); - _libssh2_store_str(&s, "password", - sizeof("password") - 1); - *s++ = 0x01; - _libssh2_store_str(&s, (char *)password, password_len); - _libssh2_store_u32(&s, - session->userauth_pswd_newpw_len); - /* send session->userauth_pswd_newpw separately */ - - session->userauth_pswd_state = libssh2_NB_state_sent2; - } - - if (session->userauth_pswd_state == libssh2_NB_state_sent2) { - rc = _libssh2_transport_send(session, - session->userauth_pswd_data, - session->userauth_pswd_data_len, - (unsigned char *) - session->userauth_pswd_newpw, - session->userauth_pswd_newpw_len); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block waiting"); - } - - /* free the allocated packets again */ - LIBSSH2_FREE(session, session->userauth_pswd_data); - session->userauth_pswd_data = NULL; - LIBSSH2_FREE(session, session->userauth_pswd_newpw); - session->userauth_pswd_newpw = NULL; - - if (rc) { - return _libssh2_error(session, - LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth " - "password-change request"); - } - - /* - * Ugliest use of goto ever. Blame it on the - * askN => requirev migration. - */ - session->userauth_pswd_state = libssh2_NB_state_sent; - goto password_response; - } - } - } else { - session->userauth_pswd_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, - "Password Expired, and no callback " - "specified"); - } - } - } - - /* FAILURE */ - LIBSSH2_FREE(session, session->userauth_pswd_data); - session->userauth_pswd_data = NULL; - session->userauth_pswd_state = libssh2_NB_state_idle; - - return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED, - "Authentication failed"); -} - -/* - * libssh2_userauth_password_ex - * - * Plain ol' login - */ - -LIBSSH2_API int -libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, - unsigned int username_len, const char *password, - unsigned int password_len, - LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) -{ - int rc; - BLOCK_ADJUST(rc, session, - userauth_password(session, username, username_len, - (unsigned char *)password, password_len, - passwd_change_cb)); - return rc; -} - -/* - * file_read_publickey - * - * Read a public key from an id_???.pub style file - * - * Returns an allocated string containing the decoded key in *pubkeydata - * on success. - * Returns an allocated string containing the key method (e.g. "ssh-dss") - * in method on success. - */ -static int -file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - const char *pubkeyfile) -{ - FILE *fd; - char c; - unsigned char *pubkey = NULL, *sp1, *sp2, *tmp; - size_t pubkey_len = 0; - unsigned int tmp_len; - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading public key file: %s", - pubkeyfile); - /* Read Public Key */ - fd = fopen(pubkeyfile, "r"); - if (!fd) { - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Unable to open public key file"); - } - while (!feof(fd) && 1 == fread(&c, 1, 1, fd) && c != '\r' && c != '\n') - pubkey_len++; - if (feof(fd)) { - /* the last character was EOF */ - pubkey_len--; - } - rewind(fd); - - if (pubkey_len <= 1) { - fclose(fd); - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Invalid data in public key file"); - } - - pubkey = LIBSSH2_ALLOC(session, pubkey_len); - if (!pubkey) { - fclose(fd); - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for public key data"); - } - if (fread(pubkey, 1, pubkey_len, fd) != pubkey_len) { - LIBSSH2_FREE(session, pubkey); - fclose(fd); - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Unable to read public key from file"); - } - fclose(fd); - /* - * Remove trailing whitespace - */ - while (pubkey_len && isspace(pubkey[pubkey_len - 1])) - pubkey_len--; - - if (!pubkey_len) { - LIBSSH2_FREE(session, pubkey); - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Missing public key data"); - } - - if ((sp1 = memchr(pubkey, ' ', pubkey_len)) == NULL) { - LIBSSH2_FREE(session, pubkey); - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Invalid public key data"); - } - - sp1++; - - if ((sp2 = memchr(sp1, ' ', pubkey_len - (sp1 - pubkey - 1))) == NULL) { - /* Assume that the id string is missing, but that it's okay */ - sp2 = pubkey + pubkey_len; - } - - if (libssh2_base64_decode(session, (char **) &tmp, &tmp_len, - (char *) sp1, sp2 - sp1)) { - LIBSSH2_FREE(session, pubkey); - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Invalid key data, not base64 encoded"); - } - - /* Wasting some bytes here (okay, more than some), but since it's likely - * to be freed soon anyway, we'll just avoid the extra free/alloc and call - * it a wash */ - *method = pubkey; - *method_len = sp1 - pubkey - 1; - - *pubkeydata = tmp; - *pubkeydata_len = tmp_len; - - return 0; -} - - - -/* libssh2_file_read_privatekey - * Read a PEM encoded private key from an id_??? style file - */ -static int -file_read_privatekey(LIBSSH2_SESSION * session, - const LIBSSH2_HOSTKEY_METHOD ** hostkey_method, - void **hostkey_abstract, - const unsigned char *method, int method_len, - const char *privkeyfile, const char *passphrase) -{ - const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = - libssh2_hostkey_methods(); - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading private key file: %s", - privkeyfile); - *hostkey_method = NULL; - *hostkey_abstract = NULL; - while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) { - if ((*hostkey_methods_avail)->initPEM - && strncmp((*hostkey_methods_avail)->name, (const char *) method, - method_len) == 0) { - *hostkey_method = *hostkey_methods_avail; - break; - } - hostkey_methods_avail++; - } - if (!*hostkey_method) { - return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, - "No handler for specified private key"); - } - - if ((*hostkey_method)-> - initPEM(session, privkeyfile, (unsigned char *) passphrase, - hostkey_abstract)) { - return _libssh2_error(session, LIBSSH2_ERROR_FILE, - "Unable to initialize private key from file"); - } - - return 0; -} - -struct privkey_file { - const char *filename; - const char *passphrase; -}; - -static int -sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, - const unsigned char *data, size_t data_len, void **abstract) -{ - struct privkey_file *privkey_file = (struct privkey_file *) (*abstract); - const LIBSSH2_HOSTKEY_METHOD *privkeyobj; - void *hostkey_abstract; - struct iovec datavec; - int rc; - - rc = file_read_privatekey(session, &privkeyobj, &hostkey_abstract, - session->userauth_pblc_method, - session->userauth_pblc_method_len, - privkey_file->filename, - privkey_file->passphrase); - if(rc) - return rc; - - datavec.iov_base = (void *)data; - datavec.iov_len = data_len; - - if (privkeyobj->signv(session, sig, sig_len, 1, &datavec, - &hostkey_abstract)) { - if (privkeyobj->dtor) { - privkeyobj->dtor(session, abstract); - } - return -1; - } - - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &hostkey_abstract); - } - return 0; -} - - - -/* userauth_hostbased_fromfile - * Authenticate using a keypair found in the named files - */ -static int -userauth_hostbased_fromfile(LIBSSH2_SESSION *session, - const char *username, size_t username_len, - const char *publickey, const char *privatekey, - const char *passphrase, const char *hostname, - size_t hostname_len, - const char *local_username, - size_t local_username_len) -{ - int rc; - - if (session->userauth_host_state == libssh2_NB_state_idle) { - const LIBSSH2_HOSTKEY_METHOD *privkeyobj; - unsigned char *pubkeydata, *sig; - size_t pubkeydata_len; - size_t sig_len; - void *abstract; - unsigned char buf[5]; - struct iovec datavec[4]; - - /* Zero the whole thing out */ - memset(&session->userauth_host_packet_requirev_state, 0, - sizeof(session->userauth_host_packet_requirev_state)); - - if (publickey) { - rc = file_read_publickey(session, &session->userauth_host_method, - &session->userauth_host_method_len, - &pubkeydata, &pubkeydata_len, publickey); - if(rc) - /* Note: file_read_publickey() calls _libssh2_error() */ - return rc; - } - else { - /* Compute public key from private key. */ - rc = _libssh2_pub_priv_keyfile(session, - &session->userauth_host_method, - &session->userauth_host_method_len, - &pubkeydata, &pubkeydata_len, - privatekey, passphrase); - if (rc) - /* libssh2_pub_priv_keyfile calls _libssh2_error() */ - return rc; - } - - /* - * 52 = packet_type(1) + username_len(4) + servicename_len(4) + - * service_name(14)"ssh-connection" + authmethod_len(4) + - * authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) + - * hostname_len(4) + local_username_len(4) - */ - session->userauth_host_packet_len = - username_len + session->userauth_host_method_len + hostname_len + - local_username_len + pubkeydata_len + 52; - - /* - * Preallocate space for an overall length, method name again, - * and the signature, which won't be any larger than the size of - * the publickeydata itself - */ - session->userauth_host_s = session->userauth_host_packet = - LIBSSH2_ALLOC(session, - session->userauth_host_packet_len + 4 + - (4 + session->userauth_host_method_len) + - (4 + pubkeydata_len)); - if (!session->userauth_host_packet) { - LIBSSH2_FREE(session, session->userauth_host_method); - session->userauth_host_method = NULL; - LIBSSH2_FREE(session, pubkeydata); - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Out of memory"); - } - - *(session->userauth_host_s++) = SSH_MSG_USERAUTH_REQUEST; - _libssh2_store_str(&session->userauth_host_s, username, username_len); - _libssh2_store_str(&session->userauth_host_s, "ssh-connection", 14); - _libssh2_store_str(&session->userauth_host_s, "hostbased", 9); - _libssh2_store_str(&session->userauth_host_s, - (const char *)session->userauth_host_method, - session->userauth_host_method_len); - _libssh2_store_str(&session->userauth_host_s, (const char *)pubkeydata, - pubkeydata_len); - LIBSSH2_FREE(session, pubkeydata); - _libssh2_store_str(&session->userauth_host_s, hostname, hostname_len); - _libssh2_store_str(&session->userauth_host_s, local_username, - local_username_len); - - rc = file_read_privatekey(session, &privkeyobj, &abstract, - session->userauth_host_method, - session->userauth_host_method_len, - privatekey, passphrase); - if(rc) { - /* Note: file_read_privatekey() calls _libssh2_error() */ - LIBSSH2_FREE(session, session->userauth_host_method); - session->userauth_host_method = NULL; - LIBSSH2_FREE(session, session->userauth_host_packet); - session->userauth_host_packet = NULL; - return rc; - } - - _libssh2_htonu32(buf, session->session_id_len); - datavec[0].iov_base = (void *)buf; - datavec[0].iov_len = 4; - datavec[1].iov_base = (void *)session->session_id; - datavec[1].iov_len = session->session_id_len; - datavec[2].iov_base = (void *)session->userauth_host_packet; - datavec[2].iov_len = session->userauth_host_packet_len; - - if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { - LIBSSH2_FREE(session, session->userauth_host_method); - session->userauth_host_method = NULL; - LIBSSH2_FREE(session, session->userauth_host_packet); - session->userauth_host_packet = NULL; - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &abstract); - } - return -1; - } - - if (privkeyobj->dtor) { - privkeyobj->dtor(session, &abstract); - } - - if (sig_len > pubkeydata_len) { - unsigned char *newpacket; - /* Should *NEVER* happen, but...well.. better safe than sorry */ - newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet, - session->userauth_host_packet_len + 4 + - (4 + session->userauth_host_method_len) - + (4 + sig_len)); /* PK sigblob */ - if (!newpacket) { - LIBSSH2_FREE(session, sig); - LIBSSH2_FREE(session, session->userauth_host_packet); - session->userauth_host_packet = NULL; - LIBSSH2_FREE(session, session->userauth_host_method); - session->userauth_host_method = NULL; - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Failed allocating additional space for " - "userauth-hostbased packet"); - } - session->userauth_host_packet = newpacket; - } - - session->userauth_host_s = - session->userauth_host_packet + session->userauth_host_packet_len; - - _libssh2_store_u32(&session->userauth_host_s, - 4 + session->userauth_host_method_len + 4 + sig_len); - _libssh2_store_str(&session->userauth_host_s, - (const char *)session->userauth_host_method, - session->userauth_host_method_len); - LIBSSH2_FREE(session, session->userauth_host_method); - session->userauth_host_method = NULL; - - _libssh2_store_str(&session->userauth_host_s, (const char *)sig, - sig_len); - LIBSSH2_FREE(session, sig); - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Attempting hostbased authentication"); - - session->userauth_host_state = libssh2_NB_state_created; - } - - if (session->userauth_host_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, session->userauth_host_packet, - session->userauth_host_s - - session->userauth_host_packet, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } - else if (rc) { - LIBSSH2_FREE(session, session->userauth_host_packet); - session->userauth_host_packet = NULL; - session->userauth_host_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-hostbased request"); - } - LIBSSH2_FREE(session, session->userauth_host_packet); - session->userauth_host_packet = NULL; - - session->userauth_host_state = libssh2_NB_state_sent; - } - - if (session->userauth_host_state == libssh2_NB_state_sent) { - static const unsigned char reply_codes[3] = - { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; - size_t data_len; - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_host_data, - &data_len, 0, NULL, 0, - &session-> - userauth_host_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } - - session->userauth_host_state = libssh2_NB_state_idle; - if (rc) { - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Auth failed"); - } - - if (session->userauth_host_data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Hostbased authentication successful"); - /* We are us and we've proved it. */ - LIBSSH2_FREE(session, session->userauth_host_data); - session->userauth_host_data = NULL; - session->state |= LIBSSH2_STATE_AUTHENTICATED; - return 0; - } - } - - /* This public key is not allowed for this user on this server */ - LIBSSH2_FREE(session, session->userauth_host_data); - session->userauth_host_data = NULL; - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid signature for supplied public key, or bad " - "username/public key combination"); -} - -/* libssh2_userauth_hostbased_fromfile_ex - * Authenticate using a keypair found in the named files - */ -LIBSSH2_API int -libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, - const char *user, - unsigned int user_len, - const char *publickey, - const char *privatekey, - const char *passphrase, - const char *host, - unsigned int host_len, - const char *localuser, - unsigned int localuser_len) -{ - int rc; - BLOCK_ADJUST(rc, session, - userauth_hostbased_fromfile(session, user, user_len, - publickey, privatekey, - passphrase, host, host_len, - localuser, localuser_len)); - return rc; -} - - - -int -_libssh2_userauth_publickey(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len, - const unsigned char *pubkeydata, - unsigned long pubkeydata_len, - LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), - void *abstract) -{ - unsigned char reply_codes[4] = - { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, - SSH_MSG_USERAUTH_PK_OK, 0 - }; - int rc; - unsigned char *s; - - if (session->userauth_pblc_state == libssh2_NB_state_idle) { - - /* - * The call to _libssh2_ntohu32 later relies on pubkeydata having at - * least 4 valid bytes containing the length of the method name. - */ - if (pubkeydata_len < 4) - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid public key, too short"); - - /* Zero the whole thing out */ - memset(&session->userauth_pblc_packet_requirev_state, 0, - sizeof(session->userauth_pblc_packet_requirev_state)); - - /* - * As an optimisation, userauth_publickey_fromfile reuses a - * previously allocated copy of the method name to avoid an extra - * allocation/free. - * For other uses, we allocate and populate it here. - */ - if (!session->userauth_pblc_method) { - session->userauth_pblc_method_len = _libssh2_ntohu32(pubkeydata); - - if(session->userauth_pblc_method_len > pubkeydata_len) - /* the method length simply cannot be longer than the entire - passed in data, so we use this to detect crazy input - data */ - return _libssh2_error(session, - LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid public key"); - - session->userauth_pblc_method = - LIBSSH2_ALLOC(session, session->userauth_pblc_method_len); - if (!session->userauth_pblc_method) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for public key " - "data"); - } - memcpy(session->userauth_pblc_method, pubkeydata + 4, - session->userauth_pblc_method_len); - } - /* - * The length of the method name read from plaintext prefix in the - * file must match length embedded in the key. - * TODO: The data should match too but we don't check that. Should we? - */ - else if (session->userauth_pblc_method_len != - _libssh2_ntohu32(pubkeydata)) - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid public key"); - - /* - * 45 = packet_type(1) + username_len(4) + servicename_len(4) + - * service_name(14)"ssh-connection" + authmethod_len(4) + - * authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) + - * publickey_len(4) - */ - session->userauth_pblc_packet_len = - username_len + session->userauth_pblc_method_len + pubkeydata_len + - 45; - - /* - * Preallocate space for an overall length, method name again, and the - * signature, which won't be any larger than the size of the - * publickeydata itself. - * - * Note that the 'pubkeydata_len' extra bytes allocated here will not - * be used in this first send, but will be used in the later one where - * this same allocation is re-used. - */ - s = session->userauth_pblc_packet = - LIBSSH2_ALLOC(session, - session->userauth_pblc_packet_len + 4 + - (4 + session->userauth_pblc_method_len) - + (4 + pubkeydata_len)); - if (!session->userauth_pblc_packet) { - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Out of memory"); - } - - *s++ = SSH_MSG_USERAUTH_REQUEST; - _libssh2_store_str(&s, username, username_len); - _libssh2_store_str(&s, "ssh-connection", 14); - _libssh2_store_str(&s, "publickey", 9); - - session->userauth_pblc_b = s; - /* Not sending signature with *this* packet */ - *s++ = 0; - - _libssh2_store_str(&s, (const char *)session->userauth_pblc_method, - session->userauth_pblc_method_len); - _libssh2_store_str(&s, (const char *)pubkeydata, pubkeydata_len); - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Attempting publickey authentication"); - - session->userauth_pblc_state = libssh2_NB_state_created; - } - - if (session->userauth_pblc_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, session->userauth_pblc_packet, - session->userauth_pblc_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - else if (rc) { - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-publickey request"); - } - - session->userauth_pblc_state = libssh2_NB_state_sent; - } - - if (session->userauth_pblc_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_pblc_data, - &session->userauth_pblc_data_len, 0, - NULL, 0, - &session-> - userauth_pblc_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } - else if (rc) { - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Waiting for USERAUTH response"); - } - - if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Pubkey authentication prematurely successful"); - /* - * God help any SSH server that allows an UNVERIFIED - * public key to validate the user - */ - LIBSSH2_FREE(session, session->userauth_pblc_data); - session->userauth_pblc_data = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - session->state |= LIBSSH2_STATE_AUTHENTICATED; - session->userauth_pblc_state = libssh2_NB_state_idle; - return 0; - } - - if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_FAILURE) { - /* This public key is not allowed for this user on this server */ - LIBSSH2_FREE(session, session->userauth_pblc_data); - session->userauth_pblc_data = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED, - "Username/PublicKey combination invalid"); - } - - /* Semi-Success! */ - LIBSSH2_FREE(session, session->userauth_pblc_data); - session->userauth_pblc_data = NULL; - - *session->userauth_pblc_b = 0x01; - session->userauth_pblc_state = libssh2_NB_state_sent1; - } - - if (session->userauth_pblc_state == libssh2_NB_state_sent1) { - unsigned char *buf; - unsigned char *sig; - size_t sig_len; - - s = buf = LIBSSH2_ALLOC(session, 4 + session->session_id_len - + session->userauth_pblc_packet_len); - if (!buf) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "userauth-publickey signed data"); - } - - _libssh2_store_str(&s, (const char *)session->session_id, - session->session_id_len); - - memcpy (s, session->userauth_pblc_packet, - session->userauth_pblc_packet_len); - s += session->userauth_pblc_packet_len; - - rc = sign_callback(session, &sig, &sig_len, buf, s - buf, abstract); - LIBSSH2_FREE(session, buf); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } else if (rc) { - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Callback returned error"); - } - - /* - * If this function was restarted, pubkeydata_len might still be 0 - * which will cause an unnecessary but harmless realloc here. - */ - if (sig_len > pubkeydata_len) { - unsigned char *newpacket; - /* Should *NEVER* happen, but...well.. better safe than sorry */ - newpacket = LIBSSH2_REALLOC(session, - session->userauth_pblc_packet, - session->userauth_pblc_packet_len + 4 + - (4 + session->userauth_pblc_method_len) - + (4 + sig_len)); /* PK sigblob */ - if (!newpacket) { - LIBSSH2_FREE(session, sig); - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Failed allocating additional space for " - "userauth-publickey packet"); - } - session->userauth_pblc_packet = newpacket; - } - - s = session->userauth_pblc_packet + session->userauth_pblc_packet_len; - session->userauth_pblc_b = NULL; - - _libssh2_store_u32(&s, - 4 + session->userauth_pblc_method_len + 4 + sig_len); - _libssh2_store_str(&s, (const char *)session->userauth_pblc_method, - session->userauth_pblc_method_len); - - LIBSSH2_FREE(session, session->userauth_pblc_method); - session->userauth_pblc_method = NULL; - - _libssh2_store_str(&s, (const char *)sig, sig_len); - LIBSSH2_FREE(session, sig); - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Attempting publickey authentication -- phase 2"); - - session->userauth_pblc_s = s; - session->userauth_pblc_state = libssh2_NB_state_sent2; - } - - if (session->userauth_pblc_state == libssh2_NB_state_sent2) { - rc = _libssh2_transport_send(session, session->userauth_pblc_packet, - session->userauth_pblc_s - - session->userauth_pblc_packet, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } else if (rc) { - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-publickey request"); - } - LIBSSH2_FREE(session, session->userauth_pblc_packet); - session->userauth_pblc_packet = NULL; - - session->userauth_pblc_state = libssh2_NB_state_sent3; - } - - /* PK_OK is no longer valid */ - reply_codes[2] = 0; - - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_pblc_data, - &session->userauth_pblc_data_len, 0, NULL, 0, - &session->userauth_pblc_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block requesting userauth list"); - } else if (rc) { - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Waiting for publickey USERAUTH response"); - } - - if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Publickey authentication successful"); - /* We are us and we've proved it. */ - LIBSSH2_FREE(session, session->userauth_pblc_data); - session->userauth_pblc_data = NULL; - session->state |= LIBSSH2_STATE_AUTHENTICATED; - session->userauth_pblc_state = libssh2_NB_state_idle; - return 0; - } - - /* This public key is not allowed for this user on this server */ - LIBSSH2_FREE(session, session->userauth_pblc_data); - session->userauth_pblc_data = NULL; - session->userauth_pblc_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, - "Invalid signature for supplied public key, or bad " - "username/public key combination"); -} - -/* - * userauth_publickey_fromfile - * Authenticate using a keypair found in the named files - */ -static int -userauth_publickey_fromfile(LIBSSH2_SESSION *session, - const char *username, - size_t username_len, - const char *publickey, - const char *privatekey, - const char *passphrase) -{ - unsigned char *pubkeydata = NULL; - size_t pubkeydata_len = 0; - struct privkey_file privkey_file; - void *abstract = &privkey_file; - int rc; - - privkey_file.filename = privatekey; - privkey_file.passphrase = passphrase; - - if (session->userauth_pblc_state == libssh2_NB_state_idle) { - if (publickey) { - rc = file_read_publickey(session, &session->userauth_pblc_method, - &session->userauth_pblc_method_len, - &pubkeydata, &pubkeydata_len,publickey); - if (rc) - return rc; - } - else { - /* Compute public key from private key. */ - rc = _libssh2_pub_priv_keyfile(session, - &session->userauth_pblc_method, - &session->userauth_pblc_method_len, - &pubkeydata, &pubkeydata_len, - privatekey, passphrase); - - /* _libssh2_pub_priv_keyfile calls _libssh2_error() */ - if (rc) - return rc; - } - } - - rc = _libssh2_userauth_publickey(session, username, username_len, - pubkeydata, pubkeydata_len, - sign_fromfile, &abstract); - if(pubkeydata) - LIBSSH2_FREE(session, pubkeydata); - - return rc; -} - -/* libssh2_userauth_publickey_fromfile_ex - * Authenticate using a keypair found in the named files - */ -LIBSSH2_API int -libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, - const char *user, - unsigned int user_len, - const char *publickey, - const char *privatekey, - const char *passphrase) -{ - int rc; - - if(NULL == passphrase) - /* if given a NULL pointer, make it point to a zero-length - string to save us from having to check this all over */ - passphrase=""; - - BLOCK_ADJUST(rc, session, - userauth_publickey_fromfile(session, user, user_len, - publickey, privatekey, - passphrase)); - return rc; -} - -/* libssh2_userauth_publickey_ex - * Authenticate using an external callback function - */ -LIBSSH2_API int -libssh2_userauth_publickey(LIBSSH2_SESSION *session, - const char *user, - const unsigned char *pubkeydata, - size_t pubkeydata_len, - LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), - void **abstract) -{ - int rc; - - if(!session) - return LIBSSH2_ERROR_BAD_USE; - - BLOCK_ADJUST(rc, session, - _libssh2_userauth_publickey(session, user, strlen(user), - pubkeydata, pubkeydata_len, - sign_callback, abstract)); - return rc; -} - - - -/* - * userauth_keyboard_interactive - * - * Authenticate using a challenge-response authentication - */ -static int -userauth_keyboard_interactive(LIBSSH2_SESSION * session, - const char *username, - unsigned int username_len, - LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) -{ - unsigned char *s; - int rc; - - static const unsigned char reply_codes[4] = { - SSH_MSG_USERAUTH_SUCCESS, - SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 - }; - unsigned int language_tag_len; - unsigned int i; - - if (session->userauth_kybd_state == libssh2_NB_state_idle) { - session->userauth_kybd_auth_name = NULL; - session->userauth_kybd_auth_instruction = NULL; - session->userauth_kybd_num_prompts = 0; - session->userauth_kybd_auth_failure = 1; - session->userauth_kybd_prompts = NULL; - session->userauth_kybd_responses = NULL; - - /* Zero the whole thing out */ - memset(&session->userauth_kybd_packet_requirev_state, 0, - sizeof(session->userauth_kybd_packet_requirev_state)); - - session->userauth_kybd_packet_len = - 1 /* byte SSH_MSG_USERAUTH_REQUEST */ - + 4 + username_len /* string user name (ISO-10646 UTF-8, as - defined in [RFC-3629]) */ - + 4 + 14 /* string service name (US-ASCII) */ - + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ - + 4 + 0 /* string language tag (as defined in - [RFC-3066]) */ - + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ - ; - - session->userauth_kybd_data = s = - LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len); - if (!s) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "keyboard-interactive authentication"); - } - - *s++ = SSH_MSG_USERAUTH_REQUEST; - - /* user name */ - _libssh2_store_str(&s, username, username_len); - - /* service name */ - _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1); - - /* "keyboard-interactive" */ - _libssh2_store_str(&s, "keyboard-interactive", - sizeof("keyboard-interactive") - 1); - /* language tag */ - _libssh2_store_u32(&s, 0); - - /* submethods */ - _libssh2_store_u32(&s, 0); - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Attempting keyboard-interactive authentication"); - - session->userauth_kybd_state = libssh2_NB_state_created; - } - - if (session->userauth_kybd_state == libssh2_NB_state_created) { - rc = _libssh2_transport_send(session, session->userauth_kybd_data, - session->userauth_kybd_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } else if (rc) { - LIBSSH2_FREE(session, session->userauth_kybd_data); - session->userauth_kybd_data = NULL; - session->userauth_kybd_state = libssh2_NB_state_idle; - return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send keyboard-interactive request"); - } - LIBSSH2_FREE(session, session->userauth_kybd_data); - session->userauth_kybd_data = NULL; - - session->userauth_kybd_state = libssh2_NB_state_sent; - } - - for(;;) { - if (session->userauth_kybd_state == libssh2_NB_state_sent) { - rc = _libssh2_packet_requirev(session, reply_codes, - &session->userauth_kybd_data, - &session->userauth_kybd_data_len, - 0, NULL, 0, - &session-> - userauth_kybd_packet_requirev_state); - if (rc == LIBSSH2_ERROR_EAGAIN) { - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block"); - } else if (rc) { - session->userauth_kybd_state = libssh2_NB_state_idle; - return _libssh2_error(session, - LIBSSH2_ERROR_AUTHENTICATION_FAILED, - "Waiting for keyboard USERAUTH response"); - } - - if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_SUCCESS) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Keyboard-interactive authentication successful"); - LIBSSH2_FREE(session, session->userauth_kybd_data); - session->userauth_kybd_data = NULL; - session->state |= LIBSSH2_STATE_AUTHENTICATED; - session->userauth_kybd_state = libssh2_NB_state_idle; - return 0; - } - - if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_FAILURE) { - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Keyboard-interactive authentication failed"); - LIBSSH2_FREE(session, session->userauth_kybd_data); - session->userauth_kybd_data = NULL; - session->userauth_kybd_state = libssh2_NB_state_idle; - return _libssh2_error(session, - LIBSSH2_ERROR_AUTHENTICATION_FAILED, - "Authentication failed " - "(keyboard-interactive)"); - } - - /* server requested PAM-like conversation */ - s = session->userauth_kybd_data + 1; - - /* string name (ISO-10646 UTF-8) */ - session->userauth_kybd_auth_name_len = _libssh2_ntohu32(s); - s += 4; - if(session->userauth_kybd_auth_name_len) { - session->userauth_kybd_auth_name = - LIBSSH2_ALLOC(session, - session->userauth_kybd_auth_name_len); - if (!session->userauth_kybd_auth_name) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "keyboard-interactive 'name' " - "request field"); - goto cleanup; - } - memcpy(session->userauth_kybd_auth_name, s, - session->userauth_kybd_auth_name_len); - s += session->userauth_kybd_auth_name_len; - } - - /* string instruction (ISO-10646 UTF-8) */ - session->userauth_kybd_auth_instruction_len = _libssh2_ntohu32(s); - s += 4; - if(session->userauth_kybd_auth_instruction_len) { - session->userauth_kybd_auth_instruction = - LIBSSH2_ALLOC(session, - session->userauth_kybd_auth_instruction_len); - if (!session->userauth_kybd_auth_instruction) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "keyboard-interactive 'instruction' " - "request field"); - goto cleanup; - } - memcpy(session->userauth_kybd_auth_instruction, s, - session->userauth_kybd_auth_instruction_len); - s += session->userauth_kybd_auth_instruction_len; - } - - /* string language tag (as defined in [RFC-3066]) */ - language_tag_len = _libssh2_ntohu32(s); - s += 4; - - /* ignoring this field as deprecated */ - s += language_tag_len; - - /* int num-prompts */ - session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); - s += 4; - - if(session->userauth_kybd_num_prompts) { - session->userauth_kybd_prompts = - LIBSSH2_ALLOC(session, - sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * - session->userauth_kybd_num_prompts); - if (!session->userauth_kybd_prompts) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "keyboard-interactive prompts array"); - goto cleanup; - } - memset(session->userauth_kybd_prompts, 0, - sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * - session->userauth_kybd_num_prompts); - - session->userauth_kybd_responses = - LIBSSH2_ALLOC(session, - sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * - session->userauth_kybd_num_prompts); - if (!session->userauth_kybd_responses) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "keyboard-interactive responses array"); - goto cleanup; - } - memset(session->userauth_kybd_responses, 0, - sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * - session->userauth_kybd_num_prompts); - - for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { - /* string prompt[1] (ISO-10646 UTF-8) */ - session->userauth_kybd_prompts[i].length = - _libssh2_ntohu32(s); - s += 4; - session->userauth_kybd_prompts[i].text = - LIBSSH2_ALLOC(session, - session->userauth_kybd_prompts[i].length); - if (!session->userauth_kybd_prompts[i].text) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for " - "keyboard-interactive prompt message"); - goto cleanup; - } - memcpy(session->userauth_kybd_prompts[i].text, s, - session->userauth_kybd_prompts[i].length); - s += session->userauth_kybd_prompts[i].length; - - /* boolean echo[1] */ - session->userauth_kybd_prompts[i].echo = *s++; - } - } - - response_callback(session->userauth_kybd_auth_name, - session->userauth_kybd_auth_name_len, - session->userauth_kybd_auth_instruction, - session->userauth_kybd_auth_instruction_len, - session->userauth_kybd_num_prompts, - session->userauth_kybd_prompts, - session->userauth_kybd_responses, - &session->abstract); - - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Keyboard-interactive response callback function" - " invoked"); - - session->userauth_kybd_packet_len = - 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ - + 4 /* int num-responses */ - ; - - for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { - /* string response[1] (ISO-10646 UTF-8) */ - session->userauth_kybd_packet_len += - 4 + session->userauth_kybd_responses[i].length; - } - - /* A new userauth_kybd_data area is to be allocated, free the - former one. */ - LIBSSH2_FREE(session, session->userauth_kybd_data); - - session->userauth_kybd_data = s = - LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len); - if (!s) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for keyboard-" - "interactive response packet"); - goto cleanup; - } - - *s = SSH_MSG_USERAUTH_INFO_RESPONSE; - s++; - _libssh2_store_u32(&s, session->userauth_kybd_num_prompts); - - for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { - _libssh2_store_str(&s, - session->userauth_kybd_responses[i].text, - session->userauth_kybd_responses[i].length); - } - - session->userauth_kybd_state = libssh2_NB_state_sent1; - } - - if (session->userauth_kybd_state == libssh2_NB_state_sent1) { - rc = _libssh2_transport_send(session, session->userauth_kybd_data, - session->userauth_kybd_packet_len, - NULL, 0); - if (rc == LIBSSH2_ERROR_EAGAIN) - return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, - "Would block"); - if (rc) { - _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, - "Unable to send userauth-keyboard-interactive" - " request"); - goto cleanup; - } - - session->userauth_kybd_auth_failure = 0; - } - - cleanup: - /* - * It's safe to clean all the data here, because unallocated pointers - * are filled by zeroes - */ - - LIBSSH2_FREE(session, session->userauth_kybd_data); - session->userauth_kybd_data = NULL; - - if (session->userauth_kybd_prompts) { - for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { - LIBSSH2_FREE(session, session->userauth_kybd_prompts[i].text); - session->userauth_kybd_prompts[i].text = NULL; - } - } - - if (session->userauth_kybd_responses) { - for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { - LIBSSH2_FREE(session, - session->userauth_kybd_responses[i].text); - session->userauth_kybd_responses[i].text = NULL; - } - } - - if(session->userauth_kybd_prompts) { - LIBSSH2_FREE(session, session->userauth_kybd_prompts); - session->userauth_kybd_prompts = NULL; - } - if(session->userauth_kybd_responses) { - LIBSSH2_FREE(session, session->userauth_kybd_responses); - session->userauth_kybd_responses = NULL; - } - if(session->userauth_kybd_auth_name) { - LIBSSH2_FREE(session, session->userauth_kybd_auth_name); - session->userauth_kybd_auth_name = NULL; - } - if(session->userauth_kybd_auth_instruction) { - LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction); - session->userauth_kybd_auth_instruction = NULL; - } - - if (session->userauth_kybd_auth_failure) { - session->userauth_kybd_state = libssh2_NB_state_idle; - return -1; - } - - session->userauth_kybd_state = libssh2_NB_state_sent; - } -} - -/* - * libssh2_userauth_keyboard_interactive_ex - * - * Authenticate using a challenge-response authentication - */ -LIBSSH2_API int -libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, - const char *user, - unsigned int user_len, - LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) -{ - int rc; - BLOCK_ADJUST(rc, session, - userauth_keyboard_interactive(session, user, user_len, - response_callback)); - return rc; -} diff --git a/vendor/libssh2-1.4.2/src/userauth.h b/vendor/libssh2-1.4.2/src/userauth.h deleted file mode 100644 index c0442ae15..000000000 --- a/vendor/libssh2-1.4.2/src/userauth.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef LIBSSH2_USERAUTH_H -#define LIBSSH2_USERAUTH_H -/* Copyright (c) 2004-2007, Sara Golemon - * Copyright (c) 2009-2010 by Daniel Stenberg - * 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - */ - -int -_libssh2_userauth_publickey(LIBSSH2_SESSION *session, - const char *username, - unsigned int username_len, - const unsigned char *pubkeydata, - unsigned long pubkeydata_len, - LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), - void *abstract); - -#endif /* LIBSSH2_USERAUTH_H */ diff --git a/vendor/libssh2-1.4.2/src/version.c b/vendor/libssh2-1.4.2/src/version.c deleted file mode 100644 index 408f83a39..000000000 --- a/vendor/libssh2-1.4.2/src/version.c +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (C) 2009 Daniel Stenberg. 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. - * - * 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. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * 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. - * - */ - -#include "libssh2_priv.h" - -/* - libssh2_version() can be used like this: - - if (!libssh2_version(LIBSSH2_VERSION_NUM)) { - fprintf (stderr, "Runtime libssh2 version too old!\n"); - exit(1); - } -*/ -LIBSSH2_API -const char *libssh2_version(int req_version_num) -{ - if(req_version_num <= LIBSSH2_VERSION_NUM) - return LIBSSH2_VERSION; - return NULL; /* this is not a suitable library! */ -} diff --git a/vendor/secp256k1-zkp b/vendor/secp256k1-zkp new file mode 160000 index 000000000..bd067945e --- /dev/null +++ b/vendor/secp256k1-zkp @@ -0,0 +1 @@ +Subproject commit bd067945ead3b514fba884abd0de95fc4b5db9ae diff --git a/vendor/sigar/CMakeLists.txt b/vendor/sigar/CMakeLists.txt deleted file mode 100644 index 671ac7220..000000000 --- a/vendor/sigar/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -INCLUDE_DIRECTORIES(include) - - -IF(WIN32) - INCLUDE_DIRECTORIES(src/os/win32) - SET( os_sources src/os/win32/peb.c src/os/win32/wmi.cpp src/os/win32/win32_sigar.c ) -ELSE(WIN32) - IF( APPLE ) - ADD_DEFINITIONS( -DDARWIN_HAS_LIBPROC_H ) - INCLUDE_DIRECTORIES(src/os/darwin) - SET( os_sources src/os/darwin/darwin_sigar.c ) - ELSE( APPLE ) - INCLUDE_DIRECTORIES(src/os/linux) - SET( os_sources src/os/linux/linux_sigar.c ) - ENDIF( APPLE ) - -ENDIF(WIN32) - - -SET( sources - ${os_sources} - src/sigar.c - src/sigar_cache.c - src/sigar_fileinfo.c - src/sigar_format.c - src/sigar_getline.c - src/sigar_ptql.c - src/sigar_signal.c - src/sigar_util.c - src/sigar_version_autoconf.c -) - -SETUP_LIBRARY( sigar SOURCES ${sources} LIBRARIES ${libraries} LIBRARY_TYPE STATIC ) diff --git a/vendor/sigar/include/sigar.h b/vendor/sigar/include/sigar.h deleted file mode 100644 index 3b26cb894..000000000 --- a/vendor/sigar/include/sigar.h +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (c) 2004-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_H -#define SIGAR_H - -/* System Information Gatherer And Reporter */ - -#include - -#ifndef MAX_INTERFACE_NAME_LEN -#define MAX_INTERFACE_NAME_LEN 256 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(_LP64) || \ - defined(__LP64__) || \ - defined(__64BIT__) || \ - defined(__powerpc64__) || \ - defined(__osf__) -#define SIGAR_64BIT -#endif - -/* for printf sigar_uint64_t */ -#ifdef SIGAR_64BIT -# define SIGAR_F_U64 "%lu" -#else -# define SIGAR_F_U64 "%Lu" -#endif - -#if defined(WIN32) - -typedef unsigned __int32 sigar_uint32_t; - -typedef unsigned __int64 sigar_uint64_t; - -typedef __int32 sigar_int32_t; - -typedef __int64 sigar_int64_t; - -#elif ULONG_MAX > 4294967295UL - -typedef unsigned int sigar_uint32_t; - -typedef unsigned long sigar_uint64_t; - -typedef int sigar_int32_t; - -typedef long sigar_int64_t; - -#else - -typedef unsigned int sigar_uint32_t; - -typedef unsigned long long sigar_uint64_t; - -typedef int sigar_int32_t; - -typedef long long sigar_int64_t; - -#endif - -#define SIGAR_FIELD_NOTIMPL -1 - -#define SIGAR_OK 0 -#define SIGAR_START_ERROR 20000 -#define SIGAR_ENOTIMPL (SIGAR_START_ERROR + 1) -#define SIGAR_OS_START_ERROR (SIGAR_START_ERROR*2) - -#ifdef WIN32 -# define SIGAR_ENOENT ERROR_FILE_NOT_FOUND -# define SIGAR_EACCES ERROR_ACCESS_DENIED -# define SIGAR_ENXIO ERROR_BAD_DRIVER_LEVEL -#else -# define SIGAR_ENOENT ENOENT -# define SIGAR_EACCES EACCES -# define SIGAR_ENXIO ENXIO -#endif - -#ifdef WIN32 -# define SIGAR_DECLARE(type) \ - __declspec(dllexport) type __stdcall -#else -# define SIGAR_DECLARE(type) type -#endif - -#if defined(PATH_MAX) -# define SIGAR_PATH_MAX PATH_MAX -#elif defined(MAXPATHLEN) -# define SIGAR_PATH_MAX MAXPATHLEN -#else -# define SIGAR_PATH_MAX 4096 -#endif - -#ifdef WIN32 -typedef sigar_uint64_t sigar_pid_t; -typedef unsigned long sigar_uid_t; -typedef unsigned long sigar_gid_t; -#else -#include -typedef pid_t sigar_pid_t; -typedef uid_t sigar_uid_t; -typedef gid_t sigar_gid_t; -#endif - -typedef struct sigar_t sigar_t; - -SIGAR_DECLARE(int) sigar_open(sigar_t **sigar); - -SIGAR_DECLARE(int) sigar_close(sigar_t *sigar); - -SIGAR_DECLARE(sigar_pid_t) sigar_pid_get(sigar_t *sigar); - -SIGAR_DECLARE(int) sigar_proc_kill(sigar_pid_t pid, int signum); - -SIGAR_DECLARE(int) sigar_signum_get(char *name); - -SIGAR_DECLARE(char *) sigar_strerror(sigar_t *sigar, int err); - -/* system memory info */ - -typedef struct { - sigar_uint64_t - ram, - total, - used, - free, - actual_used, - actual_free; - double used_percent; - double free_percent; -} sigar_mem_t; - -SIGAR_DECLARE(int) sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem); - -typedef struct { - sigar_uint64_t - total, - used, - free, - page_in, - page_out; -} sigar_swap_t; - -SIGAR_DECLARE(int) sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap); - -typedef struct { - sigar_uint64_t - user, - sys, - nice, - idle, - wait, - irq, - soft_irq, - stolen, - total; -} sigar_cpu_t; - -SIGAR_DECLARE(int) sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu); - -typedef struct { - unsigned long number; - unsigned long size; - sigar_cpu_t *data; -} sigar_cpu_list_t; - -SIGAR_DECLARE(int) sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist); - -SIGAR_DECLARE(int) sigar_cpu_list_destroy(sigar_t *sigar, - sigar_cpu_list_t *cpulist); - -typedef struct { - char vendor[128]; - char model[128]; - int mhz; - int mhz_max; - int mhz_min; - sigar_uint64_t cache_size; - int total_sockets; - int total_cores; - int cores_per_socket; -} sigar_cpu_info_t; - -typedef struct { - unsigned long number; - unsigned long size; - sigar_cpu_info_t *data; -} sigar_cpu_info_list_t; - -SIGAR_DECLARE(int) -sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos); - -SIGAR_DECLARE(int) -sigar_cpu_info_list_destroy(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos); - -typedef struct { - double uptime; -} sigar_uptime_t; - -SIGAR_DECLARE(int) sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime); - -typedef struct { - double loadavg[3]; -} sigar_loadavg_t; - -SIGAR_DECLARE(int) sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg); - -typedef struct { - unsigned long number; - unsigned long size; - sigar_pid_t *data; -} sigar_proc_list_t; - -typedef struct { - /* RLIMIT_CPU */ - sigar_uint64_t cpu_cur, cpu_max; - /* RLIMIT_FSIZE */ - sigar_uint64_t file_size_cur, file_size_max; - /* PIPE_BUF */ - sigar_uint64_t pipe_size_cur, pipe_size_max; - /* RLIMIT_DATA */ - sigar_uint64_t data_cur, data_max; - /* RLIMIT_STACK */ - sigar_uint64_t stack_cur, stack_max; - /* RLIMIT_CORE */ - sigar_uint64_t core_cur, core_max; - /* RLIMIT_RSS */ - sigar_uint64_t memory_cur, memory_max; - /* RLIMIT_NPROC */ - sigar_uint64_t processes_cur, processes_max; - /* RLIMIT_NOFILE */ - sigar_uint64_t open_files_cur, open_files_max; - /* RLIMIT_AS */ - sigar_uint64_t virtual_memory_cur, virtual_memory_max; -} sigar_resource_limit_t; - -SIGAR_DECLARE(int) sigar_resource_limit_get(sigar_t *sigar, - sigar_resource_limit_t *rlimit); - -SIGAR_DECLARE(int) sigar_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist); - -SIGAR_DECLARE(int) sigar_proc_list_destroy(sigar_t *sigar, - sigar_proc_list_t *proclist); - -typedef struct { - sigar_uint64_t total; - sigar_uint64_t sleeping; - sigar_uint64_t running; - sigar_uint64_t zombie; - sigar_uint64_t stopped; - sigar_uint64_t idle; - sigar_uint64_t threads; -} sigar_proc_stat_t; - -SIGAR_DECLARE(int) sigar_proc_stat_get(sigar_t *sigar, - sigar_proc_stat_t *procstat); - -typedef struct { - sigar_uint64_t - size, - resident, - share, - minor_faults, - major_faults, - page_faults; -} sigar_proc_mem_t; - -SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem); - -typedef struct { - sigar_uid_t uid; - sigar_gid_t gid; - sigar_uid_t euid; - sigar_gid_t egid; -} sigar_proc_cred_t; - -SIGAR_DECLARE(int) sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred); - -#define SIGAR_CRED_NAME_MAX 512 - -typedef struct { - char user[SIGAR_CRED_NAME_MAX]; - char group[SIGAR_CRED_NAME_MAX]; -} sigar_proc_cred_name_t; - -SIGAR_DECLARE(int) -sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_name_t *proccredname); - -typedef struct { - sigar_uint64_t - start_time, - user, - sys, - total; -} sigar_proc_time_t; - -SIGAR_DECLARE(int) sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime); - -typedef struct { - /* must match sigar_proc_time_t fields */ - sigar_uint64_t - start_time, - user, - sys, - total; - sigar_uint64_t last_time; - double percent; -} sigar_proc_cpu_t; - -SIGAR_DECLARE(int) sigar_proc_cpu_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cpu_t *proccpu); - -#define SIGAR_PROC_STATE_SLEEP 'S' -#define SIGAR_PROC_STATE_RUN 'R' -#define SIGAR_PROC_STATE_STOP 'T' -#define SIGAR_PROC_STATE_ZOMBIE 'Z' -#define SIGAR_PROC_STATE_IDLE 'D' - -#define SIGAR_PROC_NAME_LEN 128 - -typedef struct { - char name[SIGAR_PROC_NAME_LEN]; - char state; - sigar_pid_t ppid; - int tty; - int priority; - int nice; - int processor; - sigar_uint64_t threads; -} sigar_proc_state_t; - -SIGAR_DECLARE(int) sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate); - -typedef struct { - unsigned long number; - unsigned long size; - char **data; -} sigar_proc_args_t; - -SIGAR_DECLARE(int) sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs); - -SIGAR_DECLARE(int) sigar_proc_args_destroy(sigar_t *sigar, - sigar_proc_args_t *procargs); - -typedef struct { - void *data; /* user data */ - - enum { - SIGAR_PROC_ENV_ALL, - SIGAR_PROC_ENV_KEY - } type; - - /* used for SIGAR_PROC_ENV_KEY */ - const char *key; - int klen; - - int (*env_getter)(void *, const char *, int, char *, int); -} sigar_proc_env_t; - -SIGAR_DECLARE(int) sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv); - -typedef struct { - sigar_uint64_t total; - /* XXX - which are files, sockets, etc. */ -} sigar_proc_fd_t; - -SIGAR_DECLARE(int) sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd); - -typedef struct { - char name[SIGAR_PATH_MAX+1]; - char cwd[SIGAR_PATH_MAX+1]; - char root[SIGAR_PATH_MAX+1]; -} sigar_proc_exe_t; - -SIGAR_DECLARE(int) sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe); - -typedef struct { - void *data; /* user data */ - - int (*module_getter)(void *, char *, int); -} sigar_proc_modules_t; - -SIGAR_DECLARE(int) sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods); - -typedef struct { - sigar_uint64_t user; - sigar_uint64_t sys; - sigar_uint64_t total; -} sigar_thread_cpu_t; - -SIGAR_DECLARE(int) sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu); - -typedef enum { - SIGAR_FSTYPE_UNKNOWN, - SIGAR_FSTYPE_NONE, - SIGAR_FSTYPE_LOCAL_DISK, - SIGAR_FSTYPE_NETWORK, - SIGAR_FSTYPE_RAM_DISK, - SIGAR_FSTYPE_CDROM, - SIGAR_FSTYPE_SWAP, - SIGAR_FSTYPE_MAX -} sigar_file_system_type_e; - -#define SIGAR_FS_NAME_LEN SIGAR_PATH_MAX -#define SIGAR_FS_INFO_LEN 256 - -typedef struct { - char dir_name[SIGAR_FS_NAME_LEN]; - char dev_name[SIGAR_FS_NAME_LEN]; - char type_name[SIGAR_FS_INFO_LEN]; /* e.g. "local" */ - char sys_type_name[SIGAR_FS_INFO_LEN]; /* e.g. "ext3" */ - char options[SIGAR_FS_INFO_LEN]; - sigar_file_system_type_e type; - unsigned long flags; -} sigar_file_system_t; - -typedef struct { - unsigned long number; - unsigned long size; - sigar_file_system_t *data; -} sigar_file_system_list_t; - -SIGAR_DECLARE(int) -sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist); - -SIGAR_DECLARE(int) -sigar_file_system_list_destroy(sigar_t *sigar, - sigar_file_system_list_t *fslist); - -typedef struct { - sigar_uint64_t reads; - sigar_uint64_t writes; - sigar_uint64_t write_bytes; - sigar_uint64_t read_bytes; - sigar_uint64_t rtime; - sigar_uint64_t wtime; - sigar_uint64_t qtime; - sigar_uint64_t time; - sigar_uint64_t snaptime; - double service_time; - double queue; -} sigar_disk_usage_t; - -/* XXX for sigar_file_system_usage_t compat */ -#define disk_reads disk.reads -#define disk_writes disk.writes -#define disk_write_bytes disk.write_bytes -#define disk_read_bytes disk.read_bytes -#define disk_queue disk.queue -#define disk_service_time disk.service_time - -typedef struct { - sigar_disk_usage_t disk; - double use_percent; - sigar_uint64_t total; - sigar_uint64_t free; - sigar_uint64_t used; - sigar_uint64_t avail; - sigar_uint64_t files; - sigar_uint64_t free_files; -} sigar_file_system_usage_t; - -#undef SIGAR_DISK_USAGE_T - -SIGAR_DECLARE(int) -sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage); - -SIGAR_DECLARE(int) sigar_disk_usage_get(sigar_t *sigar, - const char *name, - sigar_disk_usage_t *disk); - -SIGAR_DECLARE(int) -sigar_file_system_ping(sigar_t *sigar, - sigar_file_system_t *fs); - -typedef struct { - enum { - SIGAR_AF_UNSPEC, - SIGAR_AF_INET, - SIGAR_AF_INET6, - SIGAR_AF_LINK - } family; - union { - sigar_uint32_t in; - sigar_uint32_t in6[4]; - unsigned char mac[8]; - } addr; -} sigar_net_address_t; - -#define SIGAR_INET6_ADDRSTRLEN 46 - -#define SIGAR_MAXDOMAINNAMELEN 256 -#define SIGAR_MAXHOSTNAMELEN 256 - -typedef struct { - char default_gateway[SIGAR_INET6_ADDRSTRLEN]; - char default_gateway_interface[MAX_INTERFACE_NAME_LEN]; - char host_name[SIGAR_MAXHOSTNAMELEN]; - char domain_name[SIGAR_MAXDOMAINNAMELEN]; - char primary_dns[SIGAR_INET6_ADDRSTRLEN]; - char secondary_dns[SIGAR_INET6_ADDRSTRLEN]; -} sigar_net_info_t; - -SIGAR_DECLARE(int) -sigar_net_info_get(sigar_t *sigar, - sigar_net_info_t *netinfo); - -#define SIGAR_RTF_UP 0x1 -#define SIGAR_RTF_GATEWAY 0x2 -#define SIGAR_RTF_HOST 0x4 - -typedef struct { - sigar_net_address_t destination; - sigar_net_address_t gateway; - sigar_net_address_t mask; - sigar_uint64_t - flags, - refcnt, - use, - metric, - mtu, - window, - irtt; - char ifname[MAX_INTERFACE_NAME_LEN]; -} sigar_net_route_t; - -typedef struct { - unsigned long number; - unsigned long size; - sigar_net_route_t *data; -} sigar_net_route_list_t; - -SIGAR_DECLARE(int) sigar_net_route_list_get(sigar_t *sigar, - sigar_net_route_list_t *routelist); - -SIGAR_DECLARE(int) sigar_net_route_list_destroy(sigar_t *sigar, - sigar_net_route_list_t *routelist); - -/* - * platforms define most of these "standard" flags, - * but of course, with different values in some cases. - */ -#define SIGAR_IFF_UP 0x1 -#define SIGAR_IFF_BROADCAST 0x2 -#define SIGAR_IFF_DEBUG 0x4 -#define SIGAR_IFF_LOOPBACK 0x8 -#define SIGAR_IFF_POINTOPOINT 0x10 -#define SIGAR_IFF_NOTRAILERS 0x20 -#define SIGAR_IFF_RUNNING 0x40 -#define SIGAR_IFF_NOARP 0x80 -#define SIGAR_IFF_PROMISC 0x100 -#define SIGAR_IFF_ALLMULTI 0x200 -#define SIGAR_IFF_MULTICAST 0x800 -#define SIGAR_IFF_SLAVE 0x1000 -#define SIGAR_IFF_MASTER 0x2000 -#define SIGAR_IFF_DYNAMIC 0x4000 - -#define SIGAR_NULL_HWADDR "00:00:00:00:00:00" - -/* scope values from linux-2.6/include/net/ipv6.h */ -#define SIGAR_IPV6_ADDR_ANY 0x0000 -#define SIGAR_IPV6_ADDR_UNICAST 0x0001 -#define SIGAR_IPV6_ADDR_MULTICAST 0x0002 -#define SIGAR_IPV6_ADDR_LOOPBACK 0x0010 -#define SIGAR_IPV6_ADDR_LINKLOCAL 0x0020 -#define SIGAR_IPV6_ADDR_SITELOCAL 0x0040 -#define SIGAR_IPV6_ADDR_COMPATv4 0x0080 - -typedef struct { - char name[MAX_INTERFACE_NAME_LEN]; - char type[64]; - char description[256]; - sigar_net_address_t hwaddr; - sigar_net_address_t address; - sigar_net_address_t destination; - sigar_net_address_t broadcast; - sigar_net_address_t netmask; - sigar_net_address_t address6; - int prefix6_length; - int scope6; - sigar_uint64_t - flags, - mtu, - metric; - int tx_queue_len; -} sigar_net_interface_config_t; - -SIGAR_DECLARE(int) -sigar_net_interface_config_get(sigar_t *sigar, - const char *name, - sigar_net_interface_config_t *ifconfig); - -SIGAR_DECLARE(int) -sigar_net_interface_config_primary_get(sigar_t *sigar, - sigar_net_interface_config_t *ifconfig); - -typedef struct { - sigar_uint64_t - /* received */ - rx_packets, - rx_bytes, - rx_errors, - rx_dropped, - rx_overruns, - rx_frame, - /* transmitted */ - tx_packets, - tx_bytes, - tx_errors, - tx_dropped, - tx_overruns, - tx_collisions, - tx_carrier, - speed; -} sigar_net_interface_stat_t; - -SIGAR_DECLARE(int) -sigar_net_interface_stat_get(sigar_t *sigar, - const char *name, - sigar_net_interface_stat_t *ifstat); - -typedef struct { - unsigned long number; - unsigned long size; - char **data; -} sigar_net_interface_list_t; - -SIGAR_DECLARE(int) -sigar_net_interface_list_get(sigar_t *sigar, - sigar_net_interface_list_t *iflist); - -SIGAR_DECLARE(int) -sigar_net_interface_list_destroy(sigar_t *sigar, - sigar_net_interface_list_t *iflist); - -#define SIGAR_NETCONN_CLIENT 0x01 -#define SIGAR_NETCONN_SERVER 0x02 - -#define SIGAR_NETCONN_TCP 0x10 -#define SIGAR_NETCONN_UDP 0x20 -#define SIGAR_NETCONN_RAW 0x40 -#define SIGAR_NETCONN_UNIX 0x80 - -enum { - SIGAR_TCP_ESTABLISHED = 1, - SIGAR_TCP_SYN_SENT, - SIGAR_TCP_SYN_RECV, - SIGAR_TCP_FIN_WAIT1, - SIGAR_TCP_FIN_WAIT2, - SIGAR_TCP_TIME_WAIT, - SIGAR_TCP_CLOSE, - SIGAR_TCP_CLOSE_WAIT, - SIGAR_TCP_LAST_ACK, - SIGAR_TCP_LISTEN, - SIGAR_TCP_CLOSING, - SIGAR_TCP_IDLE, - SIGAR_TCP_BOUND, - SIGAR_TCP_UNKNOWN -}; - -typedef struct { - unsigned long local_port; - sigar_net_address_t local_address; - unsigned long remote_port; - sigar_net_address_t remote_address; - sigar_uid_t uid; - unsigned long inode; - int type; - int state; - unsigned long send_queue; - unsigned long receive_queue; -} sigar_net_connection_t; - -typedef struct { - unsigned long number; - unsigned long size; - sigar_net_connection_t *data; -} sigar_net_connection_list_t; - -SIGAR_DECLARE(int) -sigar_net_connection_list_get(sigar_t *sigar, - sigar_net_connection_list_t *connlist, - int flags); - -SIGAR_DECLARE(int) -sigar_net_connection_list_destroy(sigar_t *sigar, - sigar_net_connection_list_t *connlist); - -typedef struct sigar_net_connection_walker_t sigar_net_connection_walker_t; - -/* alternative to sigar_net_connection_list_get */ -struct sigar_net_connection_walker_t { - sigar_t *sigar; - int flags; - void *data; /* user data */ - int (*add_connection)(sigar_net_connection_walker_t *walker, - sigar_net_connection_t *connection); -}; - -SIGAR_DECLARE(int) -sigar_net_connection_walk(sigar_net_connection_walker_t *walker); - -typedef struct { - int tcp_states[SIGAR_TCP_UNKNOWN]; - sigar_uint32_t tcp_inbound_total; - sigar_uint32_t tcp_outbound_total; - sigar_uint32_t all_inbound_total; - sigar_uint32_t all_outbound_total; -} sigar_net_stat_t; - -SIGAR_DECLARE(int) -sigar_net_stat_get(sigar_t *sigar, - sigar_net_stat_t *netstat, - int flags); - -SIGAR_DECLARE(int) -sigar_net_stat_port_get(sigar_t *sigar, - sigar_net_stat_t *netstat, - int flags, - sigar_net_address_t *address, - unsigned long port); - -/* TCP-MIB */ -typedef struct { - sigar_uint64_t active_opens; - sigar_uint64_t passive_opens; - sigar_uint64_t attempt_fails; - sigar_uint64_t estab_resets; - sigar_uint64_t curr_estab; - sigar_uint64_t in_segs; - sigar_uint64_t out_segs; - sigar_uint64_t retrans_segs; - sigar_uint64_t in_errs; - sigar_uint64_t out_rsts; -} sigar_tcp_t; - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp); - -typedef struct { - sigar_uint64_t null; - sigar_uint64_t getattr; - sigar_uint64_t setattr; - sigar_uint64_t root; - sigar_uint64_t lookup; - sigar_uint64_t readlink; - sigar_uint64_t read; - sigar_uint64_t writecache; - sigar_uint64_t write; - sigar_uint64_t create; - sigar_uint64_t remove; - sigar_uint64_t rename; - sigar_uint64_t link; - sigar_uint64_t symlink; - sigar_uint64_t mkdir; - sigar_uint64_t rmdir; - sigar_uint64_t readdir; - sigar_uint64_t fsstat; -} sigar_nfs_v2_t; - -typedef sigar_nfs_v2_t sigar_nfs_client_v2_t; -typedef sigar_nfs_v2_t sigar_nfs_server_v2_t; - -SIGAR_DECLARE(int) -sigar_nfs_client_v2_get(sigar_t *sigar, - sigar_nfs_client_v2_t *nfs); - -SIGAR_DECLARE(int) -sigar_nfs_server_v2_get(sigar_t *sigar, - sigar_nfs_server_v2_t *nfs); - -typedef struct { - sigar_uint64_t null; - sigar_uint64_t getattr; - sigar_uint64_t setattr; - sigar_uint64_t lookup; - sigar_uint64_t access; - sigar_uint64_t readlink; - sigar_uint64_t read; - sigar_uint64_t write; - sigar_uint64_t create; - sigar_uint64_t mkdir; - sigar_uint64_t symlink; - sigar_uint64_t mknod; - sigar_uint64_t remove; - sigar_uint64_t rmdir; - sigar_uint64_t rename; - sigar_uint64_t link; - sigar_uint64_t readdir; - sigar_uint64_t readdirplus; - sigar_uint64_t fsstat; - sigar_uint64_t fsinfo; - sigar_uint64_t pathconf; - sigar_uint64_t commit; -} sigar_nfs_v3_t; - -typedef sigar_nfs_v3_t sigar_nfs_client_v3_t; -typedef sigar_nfs_v3_t sigar_nfs_server_v3_t; - -SIGAR_DECLARE(int) -sigar_nfs_client_v3_get(sigar_t *sigar, - sigar_nfs_client_v3_t *nfs); - -SIGAR_DECLARE(int) -sigar_nfs_server_v3_get(sigar_t *sigar, - sigar_nfs_server_v3_t *nfs); - -SIGAR_DECLARE(int) -sigar_net_listen_address_get(sigar_t *sigar, - unsigned long port, - sigar_net_address_t *address); - -typedef struct { - char ifname[MAX_INTERFACE_NAME_LEN]; - char type[64]; - sigar_net_address_t hwaddr; - sigar_net_address_t address; - sigar_uint64_t flags; -} sigar_arp_t; - -typedef struct { - unsigned long number; - unsigned long size; - sigar_arp_t *data; -} sigar_arp_list_t; - -SIGAR_DECLARE(int) sigar_arp_list_get(sigar_t *sigar, - sigar_arp_list_t *arplist); - -SIGAR_DECLARE(int) sigar_arp_list_destroy(sigar_t *sigar, - sigar_arp_list_t *arplist); - -typedef struct { - char user[32]; - char device[32]; - char host[256]; - sigar_uint64_t time; -} sigar_who_t; - -typedef struct { - unsigned long number; - unsigned long size; - sigar_who_t *data; -} sigar_who_list_t; - -SIGAR_DECLARE(int) sigar_who_list_get(sigar_t *sigar, - sigar_who_list_t *wholist); - -SIGAR_DECLARE(int) sigar_who_list_destroy(sigar_t *sigar, - sigar_who_list_t *wholist); - -SIGAR_DECLARE(int) sigar_proc_port_get(sigar_t *sigar, - int protocol, unsigned long port, - sigar_pid_t *pid); - -typedef struct { - const char *build_date; - const char *scm_revision; - const char *version; - const char *archname; - const char *archlib; - const char *binname; - const char *description; - int major, minor, maint, build; -} sigar_version_t; - -SIGAR_DECLARE(sigar_version_t *) sigar_version_get(void); - -#define SIGAR_SYS_INFO_LEN SIGAR_MAXHOSTNAMELEN /* more than enough */ - -typedef struct { - char name[SIGAR_SYS_INFO_LEN]; /* canonicalized sysname */ - char version[SIGAR_SYS_INFO_LEN]; /* utsname.release */ - char arch[SIGAR_SYS_INFO_LEN]; - char machine[SIGAR_SYS_INFO_LEN]; - char description[SIGAR_SYS_INFO_LEN]; - char patch_level[SIGAR_SYS_INFO_LEN]; - char vendor[SIGAR_SYS_INFO_LEN]; - char vendor_version[SIGAR_SYS_INFO_LEN]; - char vendor_name[SIGAR_SYS_INFO_LEN]; /* utsname.sysname */ - char vendor_code_name[SIGAR_SYS_INFO_LEN]; -} sigar_sys_info_t; - -SIGAR_DECLARE(int) sigar_sys_info_get(sigar_t *sigar, sigar_sys_info_t *sysinfo); - -#define SIGAR_FQDN_LEN 512 - -SIGAR_DECLARE(int) sigar_fqdn_get(sigar_t *sigar, char *name, int namelen); - -SIGAR_DECLARE(int) sigar_rpc_ping(char *hostname, - int protocol, - unsigned long program, - unsigned long version); - -SIGAR_DECLARE(char *) sigar_rpc_strerror(int err); - -SIGAR_DECLARE(char *) sigar_password_get(const char *prompt); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/vendor/sigar/include/sigar_fileinfo.h b/vendor/sigar/include/sigar_fileinfo.h deleted file mode 100644 index f13a4e48a..000000000 --- a/vendor/sigar/include/sigar_fileinfo.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2004-2005 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2000-2003 The Apache Software Foundation. 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. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. - * - * 4. The names "Apache" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR - * ITS 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - */ - -#include "sigar.h" - -typedef enum { - SIGAR_FILETYPE_NOFILE = 0, /**< no file type determined */ - SIGAR_FILETYPE_REG, /**< a regular file */ - SIGAR_FILETYPE_DIR, /**< a directory */ - SIGAR_FILETYPE_CHR, /**< a character device */ - SIGAR_FILETYPE_BLK, /**< a block device */ - SIGAR_FILETYPE_PIPE, /**< a FIFO / pipe */ - SIGAR_FILETYPE_LNK, /**< a symbolic link */ - SIGAR_FILETYPE_SOCK, /**< a [unix domain] socket */ - SIGAR_FILETYPE_UNKFILE /**< a file of some other unknown type */ -} sigar_file_type_e; - -#define SIGAR_UREAD 0x0400 /**< Read by user */ -#define SIGAR_UWRITE 0x0200 /**< Write by user */ -#define SIGAR_UEXECUTE 0x0100 /**< Execute by user */ - -#define SIGAR_GREAD 0x0040 /**< Read by group */ -#define SIGAR_GWRITE 0x0020 /**< Write by group */ -#define SIGAR_GEXECUTE 0x0010 /**< Execute by group */ - -#define SIGAR_WREAD 0x0004 /**< Read by others */ -#define SIGAR_WWRITE 0x0002 /**< Write by others */ -#define SIGAR_WEXECUTE 0x0001 /**< Execute by others */ - -typedef struct { - /** The access permissions of the file. Mimics Unix access rights. */ - sigar_uint64_t permissions; - sigar_file_type_e type; - /** The user id that owns the file */ - sigar_uid_t uid; - /** The group id that owns the file */ - sigar_gid_t gid; - /** The inode of the file. */ - sigar_uint64_t inode; - /** The id of the device the file is on. */ - sigar_uint64_t device; - /** The number of hard links to the file. */ - sigar_uint64_t nlink; - /** The size of the file */ - sigar_uint64_t size; - /** The time the file was last accessed */ - sigar_uint64_t atime; - /** The time the file was last modified */ - sigar_uint64_t mtime; - /** The time the file was last changed */ - sigar_uint64_t ctime; -} sigar_file_attrs_t; - -typedef struct { - sigar_uint64_t total; - sigar_uint64_t files; - sigar_uint64_t subdirs; - sigar_uint64_t symlinks; - sigar_uint64_t chrdevs; - sigar_uint64_t blkdevs; - sigar_uint64_t sockets; - sigar_uint64_t disk_usage; -} sigar_dir_stat_t; - -typedef sigar_dir_stat_t sigar_dir_usage_t; - -SIGAR_DECLARE(const char *) -sigar_file_attrs_type_string_get(sigar_file_type_e type); - -SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs); - -SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs); - -SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions); - -SIGAR_DECLARE(char *) -sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions, - char *str); - -SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar, - const char *dir, - sigar_dir_stat_t *dirstats); - -SIGAR_DECLARE(int) sigar_dir_usage_get(sigar_t *sigar, - const char *dir, - sigar_dir_usage_t *dirusage); diff --git a/vendor/sigar/include/sigar_format.h b/vendor/sigar/include/sigar_format.h deleted file mode 100644 index 3bce29b9c..000000000 --- a/vendor/sigar/include/sigar_format.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2007-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_FORMAT_H -#define SIGAR_FORMAT_H - -typedef struct { - double user; - double sys; - double nice; - double idle; - double wait; - double irq; - double soft_irq; - double stolen; - double combined; -} sigar_cpu_perc_t; - -SIGAR_DECLARE(int) sigar_cpu_perc_calculate(sigar_cpu_t *prev, - sigar_cpu_t *curr, - sigar_cpu_perc_t *perc); - -SIGAR_DECLARE(int) sigar_uptime_string(sigar_t *sigar, - sigar_uptime_t *uptime, - char *buffer, - int buflen); - -SIGAR_DECLARE(char *) sigar_format_size(sigar_uint64_t size, char *buf); - -SIGAR_DECLARE(int) sigar_net_address_equals(sigar_net_address_t *addr1, - sigar_net_address_t *addr2); - -SIGAR_DECLARE(int) sigar_net_address_to_string(sigar_t *sigar, - sigar_net_address_t *address, - char *addr_str); - -SIGAR_DECLARE(const char *)sigar_net_scope_to_string(int type); - -SIGAR_DECLARE(sigar_uint32_t) sigar_net_address_hash(sigar_net_address_t *address); - -SIGAR_DECLARE(const char *)sigar_net_connection_type_get(int type); - -SIGAR_DECLARE(const char *)sigar_net_connection_state_get(int state); - -SIGAR_DECLARE(char *) sigar_net_interface_flags_to_string(sigar_uint64_t flags, char *buf); - -SIGAR_DECLARE(char *)sigar_net_services_name_get(sigar_t *sigar, - int protocol, unsigned long port); - -#endif - diff --git a/vendor/sigar/include/sigar_getline.h b/vendor/sigar/include/sigar_getline.h deleted file mode 100644 index f5bbc7c49..000000000 --- a/vendor/sigar/include/sigar_getline.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SIGAR_GETLINE_H -#define SIGAR_GETLINE_H - -#include "sigar.h" - -typedef int (*sigar_getline_completer_t)(char *, int, int *); - -SIGAR_DECLARE(char *) sigar_getline(char *prompt); -SIGAR_DECLARE(void) sigar_getline_setwidth(int width); -SIGAR_DECLARE(void) sigar_getline_redraw(void); -SIGAR_DECLARE(void) sigar_getline_reset(void); -SIGAR_DECLARE(void) sigar_getline_windowchanged(); -SIGAR_DECLARE(void) sigar_getline_histinit(char *file); -SIGAR_DECLARE(void) sigar_getline_histadd(char *buf); -SIGAR_DECLARE(int) sigar_getline_eof(); -SIGAR_DECLARE(void) sigar_getline_completer_set(sigar_getline_completer_t func); - -#endif /* SIGAR_GETLINE_H */ diff --git a/vendor/sigar/include/sigar_log.h b/vendor/sigar/include/sigar_log.h deleted file mode 100644 index cc32f9a40..000000000 --- a/vendor/sigar/include/sigar_log.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2004, 2006 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_LOG_H -#define SIGAR_LOG_H - -#include - -#define SIGAR_LOG_FATAL 0 -#define SIGAR_LOG_ERROR 1 -#define SIGAR_LOG_WARN 2 -#define SIGAR_LOG_INFO 3 -#define SIGAR_LOG_DEBUG 4 -#define SIGAR_LOG_TRACE 5 - -#define SIGAR_LOG_IS_FATAL(sigar) \ - (sigar->log_level >= SIGAR_LOG_FATAL) - -#define SIGAR_LOG_IS_ERROR(sigar) \ - (sigar->log_level >= SIGAR_LOG_ERROR) - -#define SIGAR_LOG_IS_WARN(sigar) \ - (sigar->log_level >= SIGAR_LOG_WARN) - -#define SIGAR_LOG_IS_INFO(sigar) \ - (sigar->log_level >= SIGAR_LOG_INFO) - -#define SIGAR_LOG_IS_DEBUG(sigar) \ - (sigar->log_level >= SIGAR_LOG_DEBUG) - -#define SIGAR_LOG_IS_TRACE(sigar) \ - (sigar->log_level >= SIGAR_LOG_TRACE) - -#define SIGAR_STRINGIFY(n) #n - -#define SIGAR_LOG_FILELINE \ - __FILE__ ":" SIGAR_STRINGIFY(__LINE__) - -#if defined(__GNUC__) -# if (__GNUC__ > 2) -# define SIGAR_FUNC __func__ -# else -# define SIGAR_FUNC __FUNCTION__ -# endif -#else -# define SIGAR_FUNC SIGAR_LOG_FILELINE -#endif - -typedef void (*sigar_log_impl_t)(sigar_t *, void *, int, char *); - -SIGAR_DECLARE(void) sigar_log_printf(sigar_t *sigar, int level, - const char *format, ...); - -SIGAR_DECLARE(void) sigar_log(sigar_t *sigar, int level, char *message); - -SIGAR_DECLARE(void) sigar_log_impl_set(sigar_t *sigar, void *data, - sigar_log_impl_t impl); - -SIGAR_DECLARE(void) sigar_log_impl_file(sigar_t *sigar, void *data, - int level, char *message); - -SIGAR_DECLARE(int) sigar_log_level_get(sigar_t *sigar); - -SIGAR_DECLARE(void) sigar_log_level_set(sigar_t *sigar, int level); - - -#endif /* SIGAR_LOG_H */ diff --git a/vendor/sigar/include/sigar_private.h b/vendor/sigar/include/sigar_private.h deleted file mode 100644 index cfc9aa2f6..000000000 --- a/vendor/sigar/include/sigar_private.h +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2004-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_PRIVATE_DOT_H -#define SIGAR_PRIVATE_DOT_H - -#include "sigar_log.h" -#include "sigar_ptql.h" - -#include -#include -#include - -#ifndef WIN32 -#include -#include -#ifndef DARWIN -#include -#endif -#endif - -#ifdef DMALLOC -#define _MEMORY_H /* exclude memory.h on solaris */ -#define DMALLOC_FUNC_CHECK -#include -#endif - -/* common to all os sigar_t's */ -/* XXX: this is ugly; but don't want the same stuffs - * duplicated on 4 platforms and am too lazy to change - * sigar_t to the way it was originally where sigar_t was - * common and contained a sigar_os_t. - * feel free trav ;-) - */ -#define SIGAR_T_BASE \ - int cpu_list_cores; \ - int log_level; \ - void *log_data; \ - sigar_log_impl_t log_impl; \ - void *ptql_re_data; \ - sigar_ptql_re_impl_t ptql_re_impl; \ - unsigned int ncpu; \ - unsigned long version; \ - unsigned long boot_time; \ - int ticks; \ - sigar_pid_t pid; \ - char errbuf[256]; \ - char *ifconf_buf; \ - int ifconf_len; \ - char *self_path; \ - sigar_proc_list_t *pids; \ - sigar_cache_t *fsdev; \ - sigar_cache_t *proc_cpu; \ - sigar_cache_t *net_listen; \ - sigar_cache_t *net_services_tcp; \ - sigar_cache_t *net_services_udp - -#if defined(WIN32) -# define SIGAR_INLINE __inline -#elif defined(__GNUC__) -# define SIGAR_INLINE inline -#else -# define SIGAR_INLINE -#endif - -#ifdef DMALLOC -/* linux has its own strdup macro, make sure we use dmalloc's */ -#define sigar_strdup(s) \ - dmalloc_strndup(__FILE__, __LINE__, (s), -1, 0) -#else -# ifdef WIN32 -# define sigar_strdup(s) _strdup(s) -# else -# define sigar_strdup(s) strdup(s) -# endif -#endif - -#define SIGAR_ZERO(s) \ - memset(s, '\0', sizeof(*(s))) - -#define SIGAR_STRNCPY(dest, src, len) \ - strncpy(dest, src, len); \ - dest[len-1] = '\0' - -/* we use fixed size buffers pretty much everywhere */ -/* this is strncpy + ensured \0 terminator */ -#define SIGAR_SSTRCPY(dest, src) \ - SIGAR_STRNCPY(dest, src, sizeof(dest)) - -#ifndef strEQ -#define strEQ(s1, s2) (strcmp(s1, s2) == 0) -#endif - -#ifndef strnEQ -#define strnEQ(s1, s2, n) (strncmp(s1, s2, n) == 0) -#endif - -#ifdef WIN32 -#define strcasecmp stricmp -#define strncasecmp strnicmp -#endif - -#ifndef strcaseEQ -#define strcaseEQ(s1, s2) (strcasecmp(s1, s2) == 0) -#endif - -#ifndef strncaseEQ -#define strncaseEQ(s1, s2, n) (strncasecmp(s1, s2, n) == 0) -#endif - -#ifdef offsetof -#define sigar_offsetof offsetof -#else -#define sigar_offsetof(type, field) ((size_t)(&((type *)0)->field)) -#endif - -#define SIGAR_MSEC 1000L -#define SIGAR_USEC 1000000L -#define SIGAR_NSEC 1000000000L - -#define SIGAR_SEC2NANO(s) \ - ((sigar_uint64_t)(s) * (sigar_uint64_t)SIGAR_NSEC) - -/* cpu ticks to milliseconds */ -#define SIGAR_TICK2MSEC(s) \ - ((sigar_uint64_t)(s) * ((sigar_uint64_t)SIGAR_MSEC / (double)sigar->ticks)) - -#define SIGAR_TICK2NSEC(s) \ - ((sigar_uint64_t)(s) * ((sigar_uint64_t)SIGAR_NSEC / (double)sigar->ticks)) - -/* nanoseconds to milliseconds */ -#define SIGAR_NSEC2MSEC(s) \ - ((sigar_uint64_t)(s) / ((sigar_uint64_t)1000000L)) - -#define IFTYPE_LO 2 -#define IFTYPE_ETH 3 - -#define SIGAR_LAST_PROC_EXPIRE 2 - -#define SIGAR_FS_MAX 10 - -#define SIGAR_CPU_INFO_MAX 4 - -#define SIGAR_CPU_LIST_MAX 4 - -#define SIGAR_PROC_LIST_MAX 256 - -#define SIGAR_PROC_ARGS_MAX 12 - -#define SIGAR_NET_ROUTE_LIST_MAX 6 - -#define SIGAR_NET_IFLIST_MAX 20 - -#define SIGAR_NET_CONNLIST_MAX 20 - -#define SIGAR_ARP_LIST_MAX 12 - -#define SIGAR_WHO_LIST_MAX 12 - -int sigar_os_open(sigar_t **sigar); - -int sigar_os_close(sigar_t *sigar); - -char *sigar_os_error_string(sigar_t *sigar, int err); - -char *sigar_strerror_get(int err, char *errbuf, int buflen); - -void sigar_strerror_set(sigar_t *sigar, char *msg); - -void sigar_strerror_printf(sigar_t *sigar, const char *format, ...); - -int sigar_sys_info_get_uname(sigar_sys_info_t *sysinfo); - -int sigar_os_sys_info_get(sigar_t *sigar, sigar_sys_info_t *sysinfo); - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist); - -int sigar_proc_list_create(sigar_proc_list_t *proclist); - -int sigar_proc_list_grow(sigar_proc_list_t *proclist); - -#define SIGAR_PROC_LIST_GROW(proclist) \ - if (proclist->number >= proclist->size) { \ - sigar_proc_list_grow(proclist); \ - } - -int sigar_proc_args_create(sigar_proc_args_t *proclist); - -int sigar_proc_args_grow(sigar_proc_args_t *procargs); - -#define SIGAR_PROC_ARGS_GROW(procargs) \ - if (procargs->number >= procargs->size) { \ - sigar_proc_args_grow(procargs); \ - } - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs); - -int sigar_file_system_list_create(sigar_file_system_list_t *fslist); - -int sigar_file_system_list_grow(sigar_file_system_list_t *fslist); - -#define SIGAR_FILE_SYSTEM_LIST_GROW(fslist) \ - if (fslist->number >= fslist->size) { \ - sigar_file_system_list_grow(fslist); \ - } - -int sigar_os_fs_type_get(sigar_file_system_t *fsp); - -/* os plugins that set fsp->type call fs_type_get directly */ -#define sigar_fs_type_init(fsp) \ - fsp->type = SIGAR_FSTYPE_UNKNOWN; \ - sigar_fs_type_get(fsp) - -void sigar_fs_type_get(sigar_file_system_t *fsp); - -int sigar_cpu_info_list_create(sigar_cpu_info_list_t *cpu_infos); - -int sigar_cpu_info_list_grow(sigar_cpu_info_list_t *cpu_infos); - -#define SIGAR_CPU_INFO_LIST_GROW(cpu_infos) \ - if (cpu_infos->number >= cpu_infos->size) { \ - sigar_cpu_info_list_grow(cpu_infos); \ - } - -int sigar_cpu_list_create(sigar_cpu_list_t *cpulist); - -int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist); - -#define SIGAR_CPU_LIST_GROW(cpulist) \ - if (cpulist->number >= cpulist->size) { \ - sigar_cpu_list_grow(cpulist); \ - } - -int sigar_net_route_list_create(sigar_net_route_list_t *routelist); - -int sigar_net_route_list_grow(sigar_net_route_list_t *net_routelist); - -#define SIGAR_NET_ROUTE_LIST_GROW(routelist) \ - if (routelist->number >= routelist->size) { \ - sigar_net_route_list_grow(routelist); \ - } - -int sigar_net_interface_list_create(sigar_net_interface_list_t *iflist); - -int sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist); - -#define SIGAR_NET_IFLIST_GROW(iflist) \ - if (iflist->number >= iflist->size) { \ - sigar_net_interface_list_grow(iflist); \ - } - -int sigar_net_connection_list_create(sigar_net_connection_list_t *connlist); - -int sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist); - -#define SIGAR_NET_CONNLIST_GROW(connlist) \ - if (connlist->number >= connlist->size) { \ - sigar_net_connection_list_grow(connlist); \ - } - -#define sigar_net_address_set(a, val) \ - (a).addr.in = val; \ - (a).family = SIGAR_AF_INET - -#define sigar_net_address6_set(a, val) \ - memcpy(&((a).addr.in6), val, sizeof((a).addr.in6)); \ - (a).family = SIGAR_AF_INET6 - -#define SIGAR_IFHWADDRLEN 6 - -#define sigar_net_address_mac_set(a, val, len) \ - memcpy(&((a).addr.mac), val, len); \ - (a).family = SIGAR_AF_LINK - -#define sigar_hwaddr_set_null(ifconfig) \ - SIGAR_ZERO(&ifconfig->hwaddr.addr.mac); \ - ifconfig->hwaddr.family = SIGAR_AF_LINK - -int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig); - -#define sigar_net_interface_ipv6_config_init(ifconfig) \ - ifconfig->address6.family = SIGAR_AF_INET6; \ - ifconfig->prefix6_length = 0; \ - ifconfig->scope6 = 0 - -#define SIGAR_SIN6(s) ((struct sockaddr_in6 *)(s)) - -#define SIGAR_SIN6_ADDR(s) &SIGAR_SIN6(s)->sin6_addr - -#define sigar_net_interface_scope6_set(ifconfig, addr) \ - if (IN6_IS_ADDR_LINKLOCAL(addr)) \ - ifconfig->scope6 = SIGAR_IPV6_ADDR_LINKLOCAL; \ - else if (IN6_IS_ADDR_SITELOCAL(addr)) \ - ifconfig->scope6 = SIGAR_IPV6_ADDR_SITELOCAL; \ - else if (IN6_IS_ADDR_V4COMPAT(addr)) \ - ifconfig->scope6 = SIGAR_IPV6_ADDR_COMPATv4; \ - else if (IN6_IS_ADDR_LOOPBACK(addr)) \ - ifconfig->scope6 = SIGAR_IPV6_ADDR_LOOPBACK; \ - else \ - ifconfig->scope6 = SIGAR_IPV6_ADDR_ANY - -int sigar_tcp_curr_estab(sigar_t *sigar, sigar_tcp_t *tcp); - -int sigar_arp_list_create(sigar_arp_list_t *arplist); - -int sigar_arp_list_grow(sigar_arp_list_t *arplist); - -#define SIGAR_ARP_LIST_GROW(arplist) \ - if (arplist->number >= arplist->size) { \ - sigar_arp_list_grow(arplist); \ - } - -int sigar_who_list_create(sigar_who_list_t *wholist); - -int sigar_who_list_grow(sigar_who_list_t *wholist); - -#define SIGAR_WHO_LIST_GROW(wholist) \ - if (wholist->number >= wholist->size) { \ - sigar_who_list_grow(wholist); \ - } - -int sigar_user_id_get(sigar_t *sigar, const char *name, int *uid); - -int sigar_user_name_get(sigar_t *sigar, int uid, char *buf, int buflen); - -int sigar_group_name_get(sigar_t *sigar, int gid, char *buf, int buflen); - -#define SIGAR_PROC_ENV_KEY_LOOKUP() \ - if ((procenv->type == SIGAR_PROC_ENV_KEY) && \ - (pid == sigar->pid)) \ - { \ - char *value = getenv(procenv->key); \ - if (value != NULL) { \ - procenv->env_getter(procenv->data, \ - procenv->key, \ - procenv->klen, \ - value, strlen(value)); \ - } \ - return SIGAR_OK; \ - } - -#define SIGAR_DISK_STATS_INIT(disk) \ - (disk)->reads = (disk)->writes = \ - (disk)->read_bytes = (disk)->write_bytes = \ - (disk)->rtime = (disk)->wtime = (disk)->qtime = (disk)->time = \ - (disk)->queue = (disk)->service_time = SIGAR_FIELD_NOTIMPL; \ - (disk)->snaptime = 0 - -/* key used for filesystem (/) -> device (/dev/hda1) mapping */ -/* and disk_usage cache for service_time */ -#define SIGAR_FSDEV_ID(sb) \ - (S_ISBLK((sb).st_mode) ? (sb).st_rdev : ((sb).st_ino + (sb).st_dev)) - -#if defined(WIN32) || defined(NETWARE) -int sigar_get_iftype(const char *name, int *type, int *inst); -#endif - -#define SIGAR_NIC_LOOPBACK "Local Loopback" -#define SIGAR_NIC_UNSPEC "UNSPEC" -#define SIGAR_NIC_SLIP "Serial Line IP" -#define SIGAR_NIC_CSLIP "VJ Serial Line IP" -#define SIGAR_NIC_SLIP6 "6-bit Serial Line IP" -#define SIGAR_NIC_CSLIP6 "VJ 6-bit Serial Line IP" -#define SIGAR_NIC_ADAPTIVE "Adaptive Serial Line IP" -#define SIGAR_NIC_ETHERNET "Ethernet" -#define SIGAR_NIC_ASH "Ash" -#define SIGAR_NIC_FDDI "Fiber Distributed Data Interface" -#define SIGAR_NIC_HIPPI "HIPPI" -#define SIGAR_NIC_AX25 "AMPR AX.25" -#define SIGAR_NIC_ROSE "AMPR ROSE" -#define SIGAR_NIC_NETROM "AMPR NET/ROM" -#define SIGAR_NIC_X25 "generic X.25" -#define SIGAR_NIC_TUNNEL "IPIP Tunnel" -#define SIGAR_NIC_PPP "Point-to-Point Protocol" -#define SIGAR_NIC_HDLC "(Cisco)-HDLC" -#define SIGAR_NIC_LAPB "LAPB" -#define SIGAR_NIC_ARCNET "ARCnet" -#define SIGAR_NIC_DLCI "Frame Relay DLCI" -#define SIGAR_NIC_FRAD "Frame Relay Access Device" -#define SIGAR_NIC_SIT "IPv6-in-IPv4" -#define SIGAR_NIC_IRDA "IrLAP" -#define SIGAR_NIC_EC "Econet" - -#ifndef WIN32 -#include -#endif - -#define SIGAR_HOSTENT_LEN 1024 -#if defined(_AIX) -#define SIGAR_HAS_HOSTENT_DATA -#endif - -typedef struct { - char buffer[SIGAR_HOSTENT_LEN]; - int error; -#ifndef WIN32 - struct hostent hs; -#endif -#ifdef SIGAR_HAS_HOSTENT_DATA - struct hostent_data hd; -#endif -} sigar_hostent_t; - -#endif diff --git a/vendor/sigar/include/sigar_ptql.h b/vendor/sigar/include/sigar_ptql.h deleted file mode 100644 index 53d28ecd9..000000000 --- a/vendor/sigar/include/sigar_ptql.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2006-2007 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_PTQL_H -#define SIGAR_PTQL_H - -#define SIGAR_PTQL_MALFORMED_QUERY -1 - -typedef struct sigar_ptql_query_t sigar_ptql_query_t; - -#define SIGAR_PTQL_ERRMSG_SIZE 1024 - -typedef struct { - char message[SIGAR_PTQL_ERRMSG_SIZE]; -} sigar_ptql_error_t; - -typedef int (*sigar_ptql_re_impl_t)(void *, char *, char *); - -SIGAR_DECLARE(void) sigar_ptql_re_impl_set(sigar_t *sigar, void *data, - sigar_ptql_re_impl_t impl); - -SIGAR_DECLARE(int) sigar_ptql_query_create(sigar_ptql_query_t **query, - char *ptql, - sigar_ptql_error_t *error); - -SIGAR_DECLARE(int) sigar_ptql_query_match(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_pid_t pid); - -SIGAR_DECLARE(int) sigar_ptql_query_destroy(sigar_ptql_query_t *query); - -SIGAR_DECLARE(int) sigar_ptql_query_find_process(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_pid_t *pid); - -SIGAR_DECLARE(int) sigar_ptql_query_find(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_proc_list_t *proclist); - -#endif /*SIGAR_PTQL_H*/ diff --git a/vendor/sigar/include/sigar_util.h b/vendor/sigar/include/sigar_util.h deleted file mode 100644 index bc605fc99..000000000 --- a/vendor/sigar/include/sigar_util.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2004-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_UTIL_H -#define SIGAR_UTIL_H - -/* most of this is crap for dealing with linux /proc */ -#define UITOA_BUFFER_SIZE \ - (sizeof(int) * 3 + 1) - -#define SSTRLEN(s) \ - (sizeof(s)-1) - -#define sigar_strtoul(ptr) \ - strtoul(ptr, &ptr, 10) - -#define sigar_strtoull(ptr) \ - strtoull(ptr, &ptr, 10) - -#define sigar_isspace(c) \ - (isspace(((unsigned char)(c)))) - -#define sigar_isdigit(c) \ - (isdigit(((unsigned char)(c)))) - -#define sigar_isalpha(c) \ - (isalpha(((unsigned char)(c)))) - -#define sigar_isupper(c) \ - (isupper(((unsigned char)(c)))) - -#define sigar_tolower(c) \ - (tolower(((unsigned char)(c)))) - -#ifdef WIN32 -#define sigar_fileno _fileno -#define sigar_isatty _isatty -#define sigar_write _write -#else -#define sigar_fileno fileno -#define sigar_isatty isatty -#define sigar_write write -#endif - -#ifndef PROC_FS_ROOT -#define PROC_FS_ROOT "/proc/" -#endif - -#ifndef PROCP_FS_ROOT -#define PROCP_FS_ROOT "/proc/" -#endif - -sigar_int64_t sigar_time_now_millis(void); - -char *sigar_uitoa(char *buf, unsigned int n, int *len); - -int sigar_inet_ntoa(sigar_t *sigar, - sigar_uint32_t address, - char *addr_str); - -struct hostent *sigar_gethostbyname(const char *name, - sigar_hostent_t *data); - -SIGAR_INLINE char *sigar_skip_line(char *buffer, int buflen); - -SIGAR_INLINE char *sigar_skip_token(char *p); - -SIGAR_INLINE char *sigar_skip_multiple_token(char *p, int count); - -char *sigar_getword(char **line, char stop); - -char *sigar_strcasestr(const char *s1, const char *s2); - -int sigar_file2str(const char *fname, char *buffer, int buflen); - -int sigar_proc_file2str(char *buffer, int buflen, - sigar_pid_t pid, - const char *fname, - int fname_len); - -#define SIGAR_PROC_FILE2STR(buffer, pid, fname) \ - sigar_proc_file2str(buffer, sizeof(buffer), \ - pid, fname, SSTRLEN(fname)) - -#define SIGAR_PROC_FILENAME(buffer, pid, fname) \ - sigar_proc_filename(buffer, sizeof(buffer), \ - pid, fname, SSTRLEN(fname)) - -#define SIGAR_SKIP_SPACE(ptr) \ - while (sigar_isspace(*ptr)) ++ptr - -char *sigar_proc_filename(char *buffer, int buflen, - sigar_pid_t pid, - const char *fname, int fname_len); - -int sigar_proc_list_procfs_get(sigar_t *sigar, - sigar_proc_list_t *proclist); - -int sigar_proc_fd_count(sigar_t *sigar, sigar_pid_t pid, - sigar_uint64_t *total); - -/* linux + freebsd */ -int sigar_procfs_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs); - -int sigar_mem_calc_ram(sigar_t *sigar, sigar_mem_t *mem); - -int sigar_statvfs(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage); - -double sigar_file_system_usage_calc_used(sigar_t *sigar, - sigar_file_system_usage_t *fs); - -#define SIGAR_DEV_PREFIX "/dev/" - -#define SIGAR_NAME_IS_DEV(dev) \ - strnEQ(dev, SIGAR_DEV_PREFIX, SSTRLEN(SIGAR_DEV_PREFIX)) - -typedef struct { - char name[256]; - int is_partition; - sigar_disk_usage_t disk; -} sigar_iodev_t; - -sigar_iodev_t *sigar_iodev_get(sigar_t *sigar, - const char *dirname); - -int sigar_cpu_core_count(sigar_t *sigar); - -/* e.g. VM guest may have 1 virtual ncpu on multicore hosts */ -#define sigar_cpu_socket_count(sigar) \ - (sigar->ncpu < sigar->lcpu) ? sigar->ncpu : \ - (sigar->ncpu / sigar->lcpu) - -int sigar_cpu_core_rollup(sigar_t *sigar); - -void sigar_cpu_model_adjust(sigar_t *sigar, sigar_cpu_info_t *info); - -int sigar_cpu_mhz_from_model(char *model); - -char *sigar_get_self_path(sigar_t *sigar); - -#if defined(__sun) || defined(__FreeBSD__) - -#define SIGAR_HAS_DLINFO_MODULES -#include -#include - -int sigar_dlinfo_modules(sigar_t *sigar, sigar_proc_modules_t *procmods); -#endif - -typedef struct sigar_cache_entry_t sigar_cache_entry_t; - -struct sigar_cache_entry_t { - sigar_cache_entry_t *next; - sigar_uint64_t id; - void *value; -}; - -typedef struct { - sigar_cache_entry_t **entries; - unsigned int count, size; - void (*free_value)(void *ptr); -} sigar_cache_t; - -sigar_cache_t *sigar_cache_new(int size); - -sigar_cache_entry_t *sigar_cache_get(sigar_cache_t *table, - sigar_uint64_t key); - -sigar_cache_entry_t *sigar_cache_find(sigar_cache_t *table, - sigar_uint64_t key); - -void sigar_cache_destroy(sigar_cache_t *table); - -#endif /* SIGAR_UTIL_H */ diff --git a/vendor/sigar/src/os/aix/aix_sigar.c b/vendor/sigar/src/os/aix/aix_sigar.c deleted file mode 100644 index a4c0a88db..000000000 --- a/vendor/sigar/src/os/aix/aix_sigar.c +++ /dev/null @@ -1,2151 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* pull in time.h before resource.h does w/ _KERNEL */ -#include -#define _KERNEL 1 -#include /* for struct file */ -#include /* for rlimit32 in 64-bit mode */ -#undef _KERNEL - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* for proc_port */ -#include -#include -#include -#include - -/* for net_connection_list */ -#include -#include -#include -#include - -/* for odm api */ -#include -#include -#include - -#include - -/* for net_interface_config ipv6 */ -#include -#include - -/* for getkerninfo */ -#include - -/* not defined in aix 4.3 */ -#ifndef SBITS -#define SBITS 16 -#endif - -#ifndef PTHRDSINFO_RUSAGE_START -#define PTHRDSINFO_RUSAGE_START 0x00000001 -#define PTHRDSINFO_RUSAGE_STOP 0x00000002 -#define PTHRDSINFO_RUSAGE_COLLECT 0x00000004 -#endif - -/* - * from libperfstat.h: - * "To calculate the load average, divide the numbers by (1<." - */ -#define FIXED_TO_DOUBLE(x) (((double)x) / (1<koffsets[i] = klist[i].n_value; - } - - return SIGAR_OK; -} - -static int kread(sigar_t *sigar, void *data, int size, long offset) -{ - if (sigar->kmem < 0) { - return SIGAR_EPERM_KMEM; - } - - if (lseek(sigar->kmem, offset, SEEK_SET) != offset) { - return errno; - } - - if (read(sigar->kmem, data, size) != size) { - return errno; - } - - return SIGAR_OK; -} - -static int sigar_thread_rusage(struct rusage *usage, int mode) -{ - return pthread_getrusage_np(pthread_self(), usage, mode); -} - -static int sigar_perfstat_memory(perfstat_memory_total_t *memory) -{ - return perfstat_memory_total(NULL, memory, sizeof(*memory), 1); -} - -static int sigar_perfstat_cpu(perfstat_cpu_total_t *cpu_total) -{ - return perfstat_cpu_total(NULL, cpu_total, sizeof(*cpu_total), 1); -} - -int sigar_os_open(sigar_t **sigar) -{ - int status, i; - int kmem = -1; - struct utsname name; - - kmem = open("/dev/kmem", O_RDONLY); - - *sigar = malloc(sizeof(**sigar)); - - (*sigar)->getprocfd = NULL; /*XXX*/ - (*sigar)->kmem = kmem; - (*sigar)->pagesize = 0; - (*sigar)->ticks = sysconf(_SC_CLK_TCK); - (*sigar)->boot_time = 0; - (*sigar)->last_pid = -1; - (*sigar)->pinfo = NULL; - (*sigar)->cpuinfo = NULL; - (*sigar)->cpuinfo_size = 0; - SIGAR_ZERO(&(*sigar)->swaps); - - i = getpagesize(); - while ((i >>= 1) > 0) { - (*sigar)->pagesize++; - } - - if (kmem > 0) { - if ((status = get_koffsets(*sigar)) != SIGAR_OK) { - /* libperfstat only mode (aix 6) */ - close((*sigar)->kmem); - (*sigar)->kmem = -1; - } - } - - (*sigar)->cpu_mhz = -1; - - (*sigar)->model[0] = '\0'; - - uname(&name); - - (*sigar)->aix_version = atoi(name.version); - - (*sigar)->thrusage = PTHRDSINFO_RUSAGE_STOP; - - (*sigar)->diskmap = NULL; - - return SIGAR_OK; -} - -static void swaps_free(swaps_t *swaps); - -int sigar_os_close(sigar_t *sigar) -{ - swaps_free(&sigar->swaps); - if (sigar->kmem > 0) { - close(sigar->kmem); - } - if (sigar->pinfo) { - free(sigar->pinfo); - } - if (sigar->cpuinfo) { - free(sigar->cpuinfo); - } - if (sigar->diskmap) { - sigar_cache_destroy(sigar->diskmap); - } - if (sigar->thrusage == PTHRDSINFO_RUSAGE_START) { - struct rusage usage; - sigar_thread_rusage(&usage, - PTHRDSINFO_RUSAGE_STOP); - } - free(sigar); - return SIGAR_OK; -} - -char *sigar_os_error_string(sigar_t *sigar, int err) -{ - switch (err) { - case SIGAR_EPERM_KMEM: - return "Failed to open /dev/kmem for reading"; - default: - return NULL; - } -} - -#define PAGESHIFT(v) \ - ((v) << sigar->pagesize) - -int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) -{ - int status; - perfstat_memory_total_t minfo; - sigar_uint64_t kern; - - if (sigar_perfstat_memory(&minfo) == 1) { - mem->total = PAGESHIFT(minfo.real_total); - mem->free = PAGESHIFT(minfo.real_free); - kern = PAGESHIFT(minfo.numperm); /* number of pages in file cache */ - } - else { - return errno; - } - - mem->used = mem->total - mem->free; - mem->actual_used = mem->used - kern; - mem->actual_free = mem->free + kern; - - sigar_mem_calc_ram(sigar, mem); - - return SIGAR_OK; -} - -static void swaps_free(swaps_t *swaps) -{ - if (swaps->num) { - int i; - - for (i=0; inum; i++) { - free(swaps->devs[i]); - } - - free(swaps->devs); - - swaps->num = 0; - } -} - -/* - * there is no public api for parsing this file. - * well, there is something, but its super ugly and requires - * linking 2 static libraries (libodm and something else) - * maybe will switch to that if it can add value elsewhere too. - */ -#define SWAPSPACES "/etc/swapspaces" - -static int swaps_get(swaps_t *swaps) -{ - FILE *fp; - char buf[512]; - char *ptr; - struct stat statbuf; - - if (stat(SWAPSPACES, &statbuf) < 0) { - return errno; - } - - /* only re-parse if file has changed */ - if (swaps->mtime == statbuf.st_mtime) { - return 0; - } - - swaps->mtime = statbuf.st_mtime; - - /* easier to just start from scratch */ - swaps_free(swaps); - - if (!(fp = fopen(SWAPSPACES, "r"))) { - return errno; - } - - while ((ptr = fgets(buf, sizeof(buf), fp))) { - if (!isalpha(*ptr)) { - continue; - } - - if (strchr(ptr, ':')) { - int len; - - ptr = fgets(buf, sizeof(buf), fp); - - while (isspace(*ptr)) { - ++ptr; - } - - if (strncmp(ptr, "dev", 3)) { - continue; - } - ptr += 3; - while (isspace(*ptr) || (*ptr == '=')) { - ++ptr; - } - - len = strlen(ptr); - ptr[len-1] = '\0'; /* -1 == chomp \n */ - - swaps->devs = realloc(swaps->devs, swaps->num+1 * sizeof(char *)); - swaps->devs[swaps->num] = malloc(len); - memcpy(swaps->devs[swaps->num], ptr, len); - - swaps->num++; - } - } - - fclose(fp); - - return 0; -} - -/* - * documented in aix tech ref, - * but this prototype is not in any friggin header file. - * struct pginfo is in sys/vminfo.h - */ - -int swapqry(char *path, struct pginfo *info); - -static int sigar_swap_get_swapqry(sigar_t *sigar, sigar_swap_t *swap) -{ - int status, i; - - if ((status = swaps_get(&sigar->swaps)) != SIGAR_OK) { - return status; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[swap] pagesize=%d, shift=%d", - getpagesize(), sigar->pagesize); - } - - swap->total = swap->free = 0; - - for (i=0; iswaps.num; i++) { - struct pginfo info; - - status = swapqry(sigar->swaps.devs[i], &info); - - if (status != 0) { - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[swap] swapqry(%s) failed: %s", - sigar->swaps.devs[i], - sigar_strerror(sigar, errno)); - } - continue; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[swap] %s total=%d/%d, free=%d/%d", - sigar->swaps.devs[i], - info.size, PAGESHIFT(info.size), - info.free, PAGESHIFT(info.free)); - } - - swap->total += PAGESHIFT(info.size); /* lsps -a */ - swap->free += PAGESHIFT(info.free); - } - - swap->used = swap->total - swap->free; - - return SIGAR_OK; -} - -#define SWAP_DEV(ps) \ - ((ps.type == LV_PAGING) ? \ - ps.u.lv_paging.vgname : \ - ps.u.nfs_paging.filename) - -#define SWAP_MB_TO_BYTES(v) ((v) * (1024 * 1024)) - -int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) -{ - perfstat_memory_total_t minfo; - perfstat_pagingspace_t ps; - perfstat_id_t id; - - id.name[0] = '\0'; - - SIGAR_ZERO(swap); - - do { - if (perfstat_pagingspace(&id, &ps, sizeof(ps), 1) != 1) { - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[swap] dev=%s query failed: %s", - SWAP_DEV(ps), - sigar_strerror(sigar, errno)); - } - continue; - } - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[swap] dev=%s: active=%s, " - "total=%lluMb, used=%lluMb", - SWAP_DEV(ps), - ((ps.active == 1) ? "yes" : "no"), - ps.mb_size, ps.mb_used); - } - if (ps.active != 1) { - continue; - } - /* convert MB sizes to bytes */ - swap->total += SWAP_MB_TO_BYTES(ps.mb_size); - swap->used += SWAP_MB_TO_BYTES(ps.mb_used); - } while (id.name[0] != '\0'); - - swap->free = swap->total - swap->used; - - if (sigar_perfstat_memory(&minfo) == 1) { - swap->page_in = minfo.pgins; - swap->page_out = minfo.pgouts; - } - else { - swap->page_in = swap->page_out = -1; - } - return SIGAR_OK; -} - -int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - int i, status; - struct sysinfo data; - perfstat_cpu_total_t cpu_data; - - if (sigar_perfstat_cpu(&cpu_data) == 1) { - cpu->user = SIGAR_TICK2MSEC(cpu_data.user); - cpu->nice = SIGAR_FIELD_NOTIMPL; /* N/A */ - cpu->sys = SIGAR_TICK2MSEC(cpu_data.sys); - cpu->idle = SIGAR_TICK2MSEC(cpu_data.idle); - cpu->wait = SIGAR_TICK2MSEC(cpu_data.wait); - cpu->irq = 0; /*N/A*/ - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait; - return SIGAR_OK; - } - else { - return errno; - } -} - -/* - * other possible metrics we could add: - * struct cpuinfo { - * long cpu[CPU_NTIMES]; - * long pswitch; - * long syscall; - * long sysread; - * long syswrite; - * long sysfork; - * long sysexec; - * long readch; - * long writech; - * long iget; - * long namei; - * long dirblk; - * long msg; - * long sema; - * long bread; - * long bwrite; - * long lread; - * long lwrite; - * long phread; - * long phwrite; - * }; - */ - -int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) -{ - perfstat_cpu_t data; - int i, ncpu = _system_configuration.ncpus; /* this can change */ - perfstat_id_t id; - - id.name[0] = '\0'; - - sigar_cpu_list_create(cpulist); - - for (i=0; idata[cpulist->number++]; - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "cpu%d perfstat_id='%s'", - i, id.name); - } - - if (perfstat_cpu(&id, &data, sizeof(data), 1) == 1) { - cpu->user = SIGAR_TICK2MSEC(data.user); - cpu->nice = SIGAR_FIELD_NOTIMPL; /* N/A */ - cpu->sys = SIGAR_TICK2MSEC(data.sys); - cpu->idle = SIGAR_TICK2MSEC(data.idle); - cpu->wait = SIGAR_TICK2MSEC(data.wait); - cpu->irq = 0; /*N/A*/ - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait; - } - else { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "cpu%d perfstat_cpu(%s) failed: %s", - i, id.name, sigar_strerror(sigar, errno)); - SIGAR_ZERO(cpu); - } - } - - return SIGAR_OK; -} - -static int boot_time(sigar_t *sigar, time_t *time) -{ - int fd; - struct utmp data; - - if ((fd = open(UTMP_FILE, O_RDONLY)) < 0) { - return errno; - } - - do { - if (read(fd, &data, sizeof(data)) != sizeof(data)) { - int status = errno; - close(fd); - return status; - } - } while (data.ut_type != BOOT_TIME); - - *time = data.ut_time; - - close(fd); - - return SIGAR_OK; -} - -int sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime) -{ - if (sigar->boot_time == 0) { - int status; - time_t time; - - if ((status = boot_time(sigar, &time)) != SIGAR_OK) { - return status; - } - - sigar->boot_time = time; - } - - uptime->uptime = time(NULL) - sigar->boot_time; - - return SIGAR_OK; -} - -#define WHOCPY(dest, src) \ - SIGAR_SSTRCPY(dest, src); \ - if (sizeof(src) < sizeof(dest)) \ - dest[sizeof(dest)-1] = '\0' - -static int sigar_who_utmp(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - struct utmp ut; - FILE *fp; - - if (!(fp = fopen(UTMP_FILE, "r"))) { - return errno; - } - - while (fread(&ut, sizeof(ut), 1, fp) == 1) { - sigar_who_t *who; - - if (*ut.ut_name == '\0') { - continue; - } - - if (ut.ut_type != USER_PROCESS) { - continue; - } - - SIGAR_WHO_LIST_GROW(wholist); - who = &wholist->data[wholist->number++]; - - WHOCPY(who->user, ut.ut_user); - WHOCPY(who->device, ut.ut_line); - WHOCPY(who->host, ut.ut_host); - - who->time = ut.ut_time; - } - - fclose(fp); - - return SIGAR_OK; -} - -int sigar_who_list_get(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - int status; - - sigar_who_list_create(wholist); - - status = sigar_who_utmp(sigar, wholist); - if (status != SIGAR_OK) { - sigar_who_list_destroy(sigar, wholist); - return status; - } - - return SIGAR_OK; -} - -int sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg) -{ - int status, i; - int data[3]; - perfstat_cpu_total_t cpu_data; - - if (sigar_perfstat_cpu(&cpu_data) == 1) { - for (i=0; i<3; i++) { - loadavg->loadavg[i] = FIXED_TO_DOUBLE(cpu_data.loadavg[i]); - } - return SIGAR_OK; - } - else { - return errno; - } -} - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - pid_t pid = 0; - struct procsinfo info; - - for (;;) { - int num = getprocs(&info, sizeof(info), - NULL, 0, &pid, 1); - - if (num == 0) { - break; - } - - SIGAR_PROC_LIST_GROW(proclist); - - proclist->data[proclist->number++] = info.pi_pid; - } - - return SIGAR_OK; -} - -static int sigar_getprocs(sigar_t *sigar, sigar_pid_t pid) -{ - int status, num; - time_t timenow = time(NULL); - - if (sigar->pinfo == NULL) { - sigar->pinfo = malloc(sizeof(*sigar->pinfo)); - } - - if (sigar->last_pid == pid) { - if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { - return SIGAR_OK; - } - } - - sigar->last_pid = pid; - sigar->last_getprocs = timenow; - - num = getprocs(sigar->pinfo, sizeof(*sigar->pinfo), - NULL, 0, &pid, 1); - - if (num != 1) { - return ESRCH; - } - - return SIGAR_OK; -} - -int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem) -{ - int status = sigar_getprocs(sigar, pid); - struct procsinfo64 *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = PAGESHIFT(pinfo->pi_size); /* XXX fold in pi_dvm ? */ - procmem->share = PAGESHIFT(pinfo->pi_sdsize); - procmem->resident = PAGESHIFT(pinfo->pi_drss + pinfo->pi_trss); - - procmem->minor_faults = pinfo->pi_minflt; - procmem->major_faults = pinfo->pi_majflt; - procmem->page_faults = - procmem->minor_faults + - procmem->major_faults; - - return SIGAR_OK; -} - -int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred) -{ - int status = sigar_getprocs(sigar, pid); - struct procsinfo64 *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - proccred->uid = pinfo->pi_cred.cr_ruid; - proccred->euid = pinfo->pi_cred.cr_uid; - if (proccred->uid == -1) { - /* - * aix 5.2 has a process named 'jfsz' - * where uid is '-1', getpwuid returns EPERM - */ - proccred->uid = proccred->euid = 0; - } - proccred->gid = pinfo->pi_cred.cr_rgid; - proccred->egid = pinfo->pi_cred.cr_gid; - - return SIGAR_OK; -} - -int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime) -{ - int status = sigar_getprocs(sigar, pid); - struct procsinfo64 *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - proctime->start_time = pinfo->pi_start; - proctime->start_time *= SIGAR_MSEC; /* convert to ms */ - proctime->user = pinfo->pi_utime * SIGAR_MSEC; - proctime->sys = pinfo->pi_stime * SIGAR_MSEC; - proctime->total = proctime->user + proctime->sys; - - return SIGAR_OK; -} - -int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - int status = sigar_getprocs(sigar, pid); - struct procsinfo64 *pinfo = sigar->pinfo; - tid_t tid = 0; - struct thrdsinfo64 thrinfo; - - if (status != SIGAR_OK) { - return status; - } - - if (getthrds(pid, &thrinfo, sizeof(thrinfo), &tid, 1) == 1) { - procstate->processor = thrinfo.ti_affinity; - } - else { - procstate->processor = SIGAR_FIELD_NOTIMPL; - } - - SIGAR_SSTRCPY(procstate->name, pinfo->pi_comm); - procstate->ppid = pinfo->pi_ppid; - procstate->nice = pinfo->pi_nice; - procstate->tty = pinfo->pi_ttyd; - procstate->priority = pinfo->pi_pri; - procstate->threads = pinfo->pi_thcount; - - switch (pinfo->pi_state) { - case SACTIVE: - procstate->state = 'R'; - break; - case SIDL: - procstate->state = 'D'; - break; - case SSTOP: - procstate->state = 'S'; - break; - case SZOMB: - procstate->state = 'Z'; - break; - case SSWAP: - procstate->state = 'S'; - break; - } - - return SIGAR_OK; -} - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - /* XXX if buffer is not large enough args are truncated */ - char buffer[8192], *ptr; - struct procsinfo pinfo; - - pinfo.pi_pid = pid; - - if (getargs(&pinfo, sizeof(pinfo), - buffer, sizeof(buffer)) != 0) - { - return errno; - } - - ptr = buffer; - - while (*ptr) { - int alen = strlen(ptr)+1; - char *arg = malloc(alen); - - SIGAR_PROC_ARGS_GROW(procargs); - memcpy(arg, ptr, alen); - - procargs->data[procargs->number++] = arg; - - ptr += alen; - } - - return SIGAR_OK; -} - -int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - /* XXX if buffer is not large enough args are truncated */ - char buffer[8192], *ptr; - struct procsinfo pinfo; - - pinfo.pi_pid = pid; - - if (getevars(&pinfo, sizeof(pinfo), - buffer, sizeof(buffer)) != 0) - { - return errno; - } - - ptr = buffer; - - while (*ptr) { - char *val = strchr(ptr, '='); - int klen, vlen, status; - char key[128]; /* XXX is there a max key size? */ - - if (val == NULL) { - /* not key=val format */ - procenv->env_getter(procenv->data, ptr, strlen(ptr), NULL, 0); - break; - } - - klen = val - ptr; - SIGAR_SSTRCPY(key, ptr); - key[klen] = '\0'; - ++val; - - vlen = strlen(val); - status = procenv->env_getter(procenv->data, - key, klen, val, vlen); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - - ptr += (klen + 1 + vlen + 1); - } - - return SIGAR_OK; -} - -int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd) -{ -#ifdef SIGAR_64BIT - /* XXX no getuser() in 64-bit mode */ - return SIGAR_ENOTIMPL; -#else - int i; - struct procsinfo pinfo; - struct user uinfo; - - procfd->total = 0; - pinfo.pi_pid = pid; - - if (getuser(&pinfo, sizeof(pinfo), - &uinfo, sizeof(uinfo)) != 0) { - if (errno == EINVAL) { - return SIGAR_ENOTIMPL; /*XXX 5.2+*/ - } - } - - /* see sys/user.h */ - for (i=0; itotal++; - } - } - - return SIGAR_OK; -#endif -} - -int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ - int len; - char buffer[8192]; - struct procsinfo pinfo; - - pinfo.pi_pid = pid; - - if (getargs(&pinfo, sizeof(pinfo), - buffer, sizeof(buffer)) != 0) - { - return errno; - } - /* XXX argv[0] might be relative */ - len = strlen(buffer); - SIGAR_SSTRCPY(procexe->name, buffer); - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/cwd"); - - if ((len = readlink(buffer, procexe->cwd, - sizeof(procexe->cwd)-1)) < 0) - { - return errno; - } - procexe->cwd[len] = '\0'; - - procexe->root[0] = '\0'; - - return SIGAR_OK; -} - -static int sigar_proc_modules_local_get(sigar_t *sigar, - sigar_proc_modules_t *procmods) -{ - struct ld_info *info; - char *buffer; - int size = 2048, status; - unsigned int offset; - - buffer = malloc(size); - while ((loadquery(L_GETINFO, buffer, size) == -1) && - (errno == ENOMEM)) - { - size += 2048; - buffer = realloc(buffer, size); - } - - info = (struct ld_info *)buffer; - - do { - char *name = info->ldinfo_filename; - - status = - procmods->module_getter(procmods->data, name, strlen(name)); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - free(buffer); - return status; - } - - offset = info->ldinfo_next; - info = (struct ld_info *)((char*)info + offset); - } while(offset); - - free(buffer); - - return SIGAR_OK; -} - -int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ - if (pid == sigar_pid_get(sigar)) { - return sigar_proc_modules_local_get(sigar, procmods); - } - else { - return SIGAR_ENOTIMPL; - } -} - -#define SIGAR_MICROSEC2NANO(s) \ - ((sigar_uint64_t)(s) * (sigar_uint64_t)1000) - -#define TIME_NSEC(t) \ - (SIGAR_SEC2NANO((t).tv_sec) + SIGAR_MICROSEC2NANO((t).tv_usec)) - -int sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu) -{ - struct rusage usage; - int retval; - - if (sigar->thrusage != PTHRDSINFO_RUSAGE_START) { - sigar->thrusage = PTHRDSINFO_RUSAGE_START; - retval = - sigar_thread_rusage(&usage, - PTHRDSINFO_RUSAGE_START); - if (retval != 0) { - return retval; - } - } - - retval = - sigar_thread_rusage(&usage, - PTHRDSINFO_RUSAGE_COLLECT); - if (retval != 0) { - return retval; - } - - cpu->user = TIME_NSEC(usage.ru_utime); - cpu->sys = TIME_NSEC(usage.ru_stime); - cpu->total = TIME_NSEC(usage.ru_utime) + TIME_NSEC(usage.ru_stime); - - return SIGAR_OK; -} - -int sigar_os_fs_type_get(sigar_file_system_t *fsp) -{ - return fsp->type; -} - -#ifndef MNT_NFS4 -/* another one documented in aix tech ref - * with no friggin prototype in any header file... - * ...but added in 5.2 - */ -int mntctl(int command, int size, char *buffer); -#endif - -int sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - int i, size, num; - char *buf, *mntlist; - - /* get required size */ - if (mntctl(MCTL_QUERY, sizeof(size), (char *)&size) < 0) { - return errno; - } - - mntlist = buf = malloc(size); - - if ((num = mntctl(MCTL_QUERY, size, buf)) < 0) { - free(buf); - return errno; - } - - sigar_file_system_list_create(fslist); - - for (i=0; ivmt_length; - - SIGAR_FILE_SYSTEM_LIST_GROW(fslist); - - fsp = &fslist->data[fslist->number++]; - - switch (ent->vmt_gfstype) { - case MNT_AIX: - typename = "aix"; - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - break; - case MNT_JFS: - typename = "jfs"; - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - break; - case MNT_NFS: - case MNT_NFS3: - typename = "nfs"; - fsp->type = SIGAR_FSTYPE_NETWORK; - break; - case MNT_CDROM: - fsp->type = SIGAR_FSTYPE_CDROM; - break; - case MNT_SFS: - case MNT_CACHEFS: - case MNT_AUTOFS: - default: - if (ent->vmt_flags & MNT_REMOTE) { - fsp->type = SIGAR_FSTYPE_NETWORK; - } - else { - fsp->type = SIGAR_FSTYPE_NONE; - } - } - - SIGAR_SSTRCPY(fsp->dir_name, vmt2dataptr(ent, VMT_STUB)); - SIGAR_SSTRCPY(fsp->options, vmt2dataptr(ent, VMT_ARGS)); - - devname = vmt2dataptr(ent, VMT_OBJECT); - - if (fsp->type == SIGAR_FSTYPE_NETWORK) { - char *hostname = vmt2dataptr(ent, VMT_HOSTNAME); -#if 0 - /* XXX: these do not seem reliable */ - int hostname_len = vmt2datasize(ent, VMT_HOSTNAME)-1; /* -1 == skip '\0' */ - int devname_len = vmt2datasize(ent, VMT_OBJECT); /* includes '\0' */ -#else - int hostname_len = strlen(hostname); - int devname_len = strlen(devname) + 1; -#endif - int total_len = hostname_len + devname_len + 1; /* 1 == strlen(":") */ - - if (total_len > sizeof(fsp->dev_name)) { - /* justincase - prevent overflow. chances: slim..none */ - SIGAR_SSTRCPY(fsp->dev_name, devname); - } - else { - /* sprintf(fsp->devname, "%s:%s", hostname, devname) */ - char *ptr = fsp->dev_name; - - memcpy(ptr, hostname, hostname_len); - ptr += hostname_len; - - *ptr++ = ':'; - - memcpy(ptr, devname, devname_len); - } - } - else { - SIGAR_SSTRCPY(fsp->dev_name, devname); - } - - /* we set fsp->type, just looking up sigar.c:fstype_names[type] */ - sigar_fs_type_get(fsp); - - if (typename == NULL) { - typename = fsp->type_name; - } - - SIGAR_SSTRCPY(fsp->sys_type_name, typename); - } - - free(buf); - - return SIGAR_OK; -} - -typedef struct { - char name[IDENTIFIER_LENGTH]; - long addr; -} aix_diskio_t; - -static int create_diskmap(sigar_t *sigar) -{ - int i, total, num; - perfstat_disk_t *disk; - perfstat_id_t id; - - total = perfstat_disk(NULL, NULL, sizeof(*disk), 0); - if (total < 1) { - return ENOENT; - } - - disk = malloc(total * sizeof(*disk)); - id.name[0] = '\0'; - - num = perfstat_disk(&id, disk, sizeof(*disk), total); - if (num < 1) { - free(disk); - return ENOENT; - } - - sigar->diskmap = sigar_cache_new(25); - - odm_initialize(); - - for (i=0; iname, "label", 0, &num))) { - retval = stat(attr->value, &sb); - - if (retval == 0) { - aix_diskio_t *diskio = malloc(sizeof(*diskio)); - SIGAR_SSTRCPY(diskio->name, disk[i].name); - diskio->addr = -1; - ent = sigar_cache_get(sigar->diskmap, SIGAR_FSDEV_ID(sb)); - ent->value = diskio; - } - - free(attr); - } - } - - odm_free_list(dv, &info); - } - - free(disk); - odm_terminate(); - - return SIGAR_OK; -} - -int sigar_disk_usage_get(sigar_t *sigar, const char *name, - sigar_disk_usage_t *usage) -{ - perfstat_disk_t disk; - perfstat_id_t id; - - SIGAR_SSTRCPY(id.name, name); - - if (perfstat_disk(&id, &disk, sizeof(disk), 1) != 1) { - return ENXIO; - } - - usage->reads = disk.rblks; - usage->writes = disk.wblks; - usage->read_bytes = disk.rblks * disk.bsize; - usage->write_bytes = disk.wblks * disk.bsize; - usage->queue = disk.qdepth; - usage->time = disk.time; - usage->rtime = SIGAR_FIELD_NOTIMPL; - usage->wtime = SIGAR_FIELD_NOTIMPL; - - return SIGAR_OK; -} - -int sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - sigar_cache_entry_t *ent; - struct stat sb; - int status; - - status = sigar_statvfs(sigar, dirname, fsusage); - - if (status != SIGAR_OK) { - return status; - } - - fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); - - SIGAR_DISK_STATS_INIT(&fsusage->disk); - - if (!sigar->diskmap) { - status = create_diskmap(sigar); - if (status != SIGAR_OK) { - return SIGAR_OK; - } - } - - status = stat(dirname, &sb); - if (status == 0) { - sigar_cache_entry_t *ent = - sigar_cache_get(sigar->diskmap, SIGAR_FSDEV_ID(sb)); - if (!ent->value) { - return SIGAR_OK; - } - sigar_disk_usage_get(sigar, ((aix_diskio_t *)ent->value)->name, &fsusage->disk); - } - - return SIGAR_OK; -} - -/* from sys/systemcfg.h, not defined in 4.3 headers */ -#ifndef POWER_4 -#define POWER_4 0x0800 -#endif -#ifndef POWER_MPC7450 -#define POWER_MPC7450 0x1000 -#endif -#ifndef POWER_5 -#define POWER_5 0x2000 -#endif - -static char *sigar_get_odm_model(sigar_t *sigar) -{ - if (sigar->model[0] == '\0') { - struct CuAt *odm_obj; - int num; - - odm_initialize(); - - if ((odm_obj = getattr("proc0", "type", 0, &num))) { - SIGAR_SSTRCPY(sigar->model, odm_obj->value); - free(odm_obj); - } - - odm_terminate(); - } - - return sigar->model; -} - -#define SIGAR_CPU_CACHE_SIZE \ - (_system_configuration.L2_cache_size / 1024) - -static int sigar_get_cpu_mhz(sigar_t *sigar) -{ - if (sigar->cpu_mhz == SIGAR_FIELD_NOTIMPL) { - perfstat_cpu_total_t data; - - if (sigar_perfstat_cpu(&data) == 1) { - sigar->cpu_mhz = data.processorHZ / 1000000; - } - else { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "perfstat_cpu_total failed: %s", - sigar_strerror(sigar, errno)); - } - } - - return sigar->cpu_mhz; -} - -static char *get_cpu_arch(void) -{ - switch (_system_configuration.architecture) { - case POWER_RS: - return "Power Classic"; - case POWER_PC: - return "PowerPC"; - case IA64: - return "IA64"; - default: - return "PowerPC"; /* what else could it be */ - } -} - -static char *get_ppc_cpu_model(void) -{ - switch (_system_configuration.implementation) { - case POWER_RS1: - return "RS1"; - case POWER_RSC: - return "RSC"; - case POWER_RS2: - return "RS2"; - case POWER_601: - return "601"; - case POWER_603: - return "603"; - case POWER_604: - return "604"; - case POWER_620: - return "620"; - case POWER_630: - return "630"; - case POWER_A35: - return "A35"; - case POWER_RS64II: - return "RS64-II"; - case POWER_RS64III: - return "RS64-III"; - case POWER_4: - return "POWER4"; - case POWER_MPC7450: - return "MPC7450"; - case POWER_5: - return "POWER5"; - default: - return "Unknown"; - } -} - -static char *get_ia64_cpu_model(void) -{ - switch (_system_configuration.implementation) { - case IA64_M1: - return "M1"; - case IA64_M2: - return "M2"; - default: - return "Unknown"; - } -} - -static char *get_cpu_model(void) -{ - if (_system_configuration.architecture == IA64) { - return get_ia64_cpu_model(); - } - else { - return get_ppc_cpu_model(); - } -} - -int sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - int i; - int ncpu = _system_configuration.ncpus; /* this can change */ - char *arch = get_cpu_arch(), *model = get_cpu_model(); - - /*XXX should only do this once*/ - sigar_cpu_info_list_create(cpu_infos); - - for (i=0; idata[cpu_infos->number++]; - - info->total_cores = ncpu; - info->cores_per_socket = 1; /*XXX*/ - info->total_sockets = ncpu; /*XXX*/ - - info->cache_size = SIGAR_CPU_CACHE_SIZE; - - info->mhz = sigar_get_cpu_mhz(sigar); - - if (*arch == 'P') { - SIGAR_SSTRCPY(info->vendor, "IBM"); - } - else if (*arch == 'I') { - SIGAR_SSTRCPY(info->vendor, "Intel"); - } - else { - SIGAR_SSTRCPY(info->vendor, "Unknown"); - } - - snprintf(info->model, sizeof(info->model), - "%s %s", arch, model); - } - - return SIGAR_OK; -} -/* XXX net_route_list copy-n-pasted from darwin_sigar.c; only diff is getkerninfo instead of sysctl */ -#define rt_s_addr(sa) ((struct sockaddr_in *)(sa))->sin_addr.s_addr - -#ifndef SA_SIZE -#define SA_SIZE(sa) \ - ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ - sizeof(long) : \ - 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) -#endif - -int sigar_net_route_list_get(sigar_t *sigar, - sigar_net_route_list_t *routelist) -{ - int needed; - int bit; - char *buf, *next, *lim; - struct rt_msghdr *rtm; - - needed = getkerninfo(KINFO_RT_DUMP, NULL, NULL, 0); - if (needed <= 0) { - return errno; - } - - buf = malloc(needed); - - if (getkerninfo(KINFO_RT_DUMP, buf, &needed, 0) < 0) { - return errno; - } - - sigar_net_route_list_create(routelist); - - lim = buf + needed; - - for (next = buf; next < lim; next += rtm->rtm_msglen) { - struct sockaddr *sa; - sigar_net_route_t *route; - rtm = (struct rt_msghdr *)next; - - if (rtm->rtm_type != RTM_GET) { - continue; - } - - sa = (struct sockaddr *)(rtm + 1); - - if (sa->sa_family != AF_INET) { - continue; - } - - SIGAR_NET_ROUTE_LIST_GROW(routelist); - route = &routelist->data[routelist->number++]; - SIGAR_ZERO(route); - - route->flags = rtm->rtm_flags; - if_indextoname(rtm->rtm_index, route->ifname); - - for (bit=RTA_DST; - bit && ((char *)sa < lim); - bit <<= 1) - { - if ((rtm->rtm_addrs & bit) == 0) { - continue; - } - switch (bit) { - case RTA_DST: - sigar_net_address_set(route->destination, - rt_s_addr(sa)); - break; - case RTA_GATEWAY: - if (sa->sa_family == AF_INET) { - sigar_net_address_set(route->gateway, - rt_s_addr(sa)); - } - break; - case RTA_NETMASK: - sigar_net_address_set(route->mask, - rt_s_addr(sa)); - break; - case RTA_IFA: - break; - } - - sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); - } - } - - free(buf); - - return SIGAR_OK; -} - -int sigar_net_interface_stat_get(sigar_t *sigar, - const char *name, - sigar_net_interface_stat_t *ifstat) -{ - perfstat_id_t id; - perfstat_netinterface_t data; - - sigar_log(sigar, SIGAR_LOG_DEBUG, "[ifstat] using libperfstat"); - - SIGAR_SSTRCPY(id.name, name); - - if (perfstat_netinterface(&id, &data, sizeof(data), 1) == 1) { - ifstat->rx_bytes = data.ibytes; - ifstat->rx_packets = data.ipackets; - ifstat->rx_errors = data.ierrors; - ifstat->rx_dropped = SIGAR_FIELD_NOTIMPL; - ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->rx_frame = SIGAR_FIELD_NOTIMPL; - - ifstat->tx_bytes = data.obytes; - ifstat->tx_packets = data.opackets; - ifstat->tx_errors = data.oerrors; - ifstat->tx_dropped = SIGAR_FIELD_NOTIMPL; - ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->tx_collisions = data.collisions; - ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL; - - ifstat->speed = data.bitrate; - - return SIGAR_OK; - } - else { - return errno; - } -} - -int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - int sock; - struct in6_ifreq ifr; - - if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - return errno; - } - - SIGAR_SSTRCPY(ifr.ifr_name, name); - - if (ioctl(sock, SIOCGIFADDR6, &ifr) == 0) { - struct in6_addr *addr = SIGAR_SIN6_ADDR(&ifr.ifr_Addr); - - sigar_net_address6_set(ifconfig->address6, addr); - sigar_net_interface_scope6_set(ifconfig, addr); - - if (ioctl(sock, SIOCGIFNETMASK6, &ifr) == 0) { - addr = SIGAR_SIN6_ADDR(&ifr.ifr_Addr); - ifconfig->prefix6_length = SIGAR_SIN6(&ifr.ifr_Addr)->sin6_len; /*XXX*/ - } - } - - close(sock); - return SIGAR_OK; -} - -#define IS_TCP_SERVER(state, flags) \ - ((flags & SIGAR_NETCONN_SERVER) && (state == TCPS_LISTEN)) - -#define IS_TCP_CLIENT(state, flags) \ - ((flags & SIGAR_NETCONN_CLIENT) && (state != TCPS_LISTEN)) - -static int net_conn_get_tcp(sigar_net_connection_walker_t *walker) -{ - sigar_t *sigar = walker->sigar; - int flags = walker->flags; - int status; - struct inpcb tcp_inpcb; - struct tcpcb tcpcb; - struct inpcb *entry; - - status = kread(sigar, &tcp_inpcb, sizeof(tcp_inpcb), - sigar->koffsets[KOFFSET_TCB]); - - if (status != SIGAR_OK) { - return status; - } - - entry = tcp_inpcb.inp_next; - while (entry) { - struct inpcb pcb; - int state; - - status = kread(sigar, &pcb, sizeof(pcb), (long)entry); - if (status != SIGAR_OK) { - return status; - } - status = kread(sigar, &tcpcb, sizeof(tcpcb), (long)pcb.inp_ppcb); - if (status != SIGAR_OK) { - return status; - } - - state = tcpcb.t_state; - if ((IS_TCP_SERVER(state, flags) || - IS_TCP_CLIENT(state, flags))) - { - sigar_net_connection_t conn; - - SIGAR_ZERO(&conn); - - conn.type = SIGAR_NETCONN_TCP; - - sigar_net_address_set(conn.local_address, - pcb.inp_laddr.s_addr); - - sigar_net_address_set(conn.remote_address, - pcb.inp_faddr.s_addr); - - conn.local_port = ntohs(pcb.inp_lport); - conn.remote_port = ntohs(pcb.inp_fport); - - conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL; - - switch (state) { - case TCPS_CLOSED: - conn.state = SIGAR_TCP_CLOSE; - break; - case TCPS_LISTEN: - conn.state = SIGAR_TCP_LISTEN; - break; - case TCPS_SYN_SENT: - conn.state = SIGAR_TCP_SYN_SENT; - break; - case TCPS_SYN_RECEIVED: - conn.state = SIGAR_TCP_SYN_RECV; - break; - case TCPS_ESTABLISHED: - conn.state = SIGAR_TCP_ESTABLISHED; - break; - case TCPS_CLOSE_WAIT: - conn.state = SIGAR_TCP_CLOSE_WAIT; - break; - case TCPS_FIN_WAIT_1: - conn.state = SIGAR_TCP_FIN_WAIT1; - break; - case TCPS_CLOSING: - conn.state = SIGAR_TCP_CLOSING; - break; - case TCPS_LAST_ACK: - conn.state = SIGAR_TCP_LAST_ACK; - break; - case TCPS_FIN_WAIT_2: - conn.state = SIGAR_TCP_FIN_WAIT2; - break; - case TCPS_TIME_WAIT: - conn.state = SIGAR_TCP_TIME_WAIT; - break; - default: - conn.state = SIGAR_TCP_UNKNOWN; - break; - } - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - - entry = pcb.inp_next; - if (entry == tcp_inpcb.inp_next) { - break; - } - } - - return SIGAR_OK; -} - -int sigar_net_connection_walk(sigar_net_connection_walker_t *walker) -{ - int status; - - if (walker->flags & SIGAR_NETCONN_TCP) { - status = net_conn_get_tcp(walker); - - if (status != SIGAR_OK) { - return status; - } - } -#if 0 - if (walker->flags & SIGAR_NETCONN_UDP) { - status = net_conn_get_udp(walker); - - if (status != SIGAR_OK) { - return status; - } - } -#endif - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp) -{ - perfstat_id_t id; - perfstat_protocol_t proto; - - SIGAR_SSTRCPY(id.name, "tcp"); - - if (perfstat_protocol(&id, &proto, sizeof(proto), 1) != 1) { - return ENOENT; - } - - tcp->active_opens = proto.u.tcp.initiated; - tcp->passive_opens = proto.u.tcp.accepted; - tcp->attempt_fails = proto.u.tcp.dropped; - tcp->estab_resets = proto.u.tcp.dropped; - tcp->curr_estab = proto.u.tcp.established; - tcp->in_segs = proto.u.tcp.ipackets; - tcp->out_segs = proto.u.tcp.opackets; - tcp->retrans_segs = 0; - tcp->in_errs = proto.u.tcp.ierrors; - tcp->out_rsts = 0; -} - -#define NFS_V2_STAT_SET(type) \ - nfs->null = proto.u.nfsv2.type.null; \ - nfs->getattr = proto.u.nfsv2.type.getattr; \ - nfs->setattr = proto.u.nfsv2.type.setattr; \ - nfs->root = proto.u.nfsv2.type.root; \ - nfs->lookup = proto.u.nfsv2.type.lookup; \ - nfs->readlink = proto.u.nfsv2.type.readlink; \ - nfs->read = proto.u.nfsv2.type.read; \ - nfs->writecache = proto.u.nfsv2.type.writecache; \ - nfs->write = proto.u.nfsv2.type.write; \ - nfs->create = proto.u.nfsv2.type.create; \ - nfs->remove = proto.u.nfsv2.type.remove; \ - nfs->rename = proto.u.nfsv2.type.rename; \ - nfs->link = proto.u.nfsv2.type.link; \ - nfs->symlink = proto.u.nfsv2.type.symlink; \ - nfs->mkdir = proto.u.nfsv2.type.mkdir; \ - nfs->rmdir = proto.u.nfsv2.type.rmdir; \ - nfs->readdir = proto.u.nfsv2.type.readdir; \ - nfs->fsstat = proto.u.nfsv2.type.statfs - -int sigar_nfs_client_v2_get(sigar_t *sigar, - sigar_nfs_client_v2_t *nfs) -{ - perfstat_id_t id; - perfstat_protocol_t proto; - - SIGAR_SSTRCPY(id.name, "nfsv2"); - - if (perfstat_protocol(&id, &proto, sizeof(proto), 1) != 1) { - return ENOENT; - } - - NFS_V2_STAT_SET(client); - - return SIGAR_OK; -} - -int sigar_nfs_server_v2_get(sigar_t *sigar, - sigar_nfs_server_v2_t *nfs) -{ - perfstat_id_t id; - perfstat_protocol_t proto; - - SIGAR_SSTRCPY(id.name, "nfsv2"); - - if (perfstat_protocol(&id, &proto, sizeof(proto), 1) != 1) { - return ENOENT; - } - - NFS_V2_STAT_SET(server); - - return SIGAR_OK; -} - -#define NFS_V3_STAT_SET(type) \ - nfs->null = proto.u.nfsv3.type.null; \ - nfs->getattr = proto.u.nfsv3.type.getattr; \ - nfs->setattr = proto.u.nfsv3.type.setattr; \ - nfs->lookup = proto.u.nfsv3.type.lookup; \ - nfs->access = proto.u.nfsv3.type.access; \ - nfs->readlink = proto.u.nfsv3.type.readlink; \ - nfs->read = proto.u.nfsv3.type.read; \ - nfs->write = proto.u.nfsv3.type.write; \ - nfs->create = proto.u.nfsv3.type.create; \ - nfs->mkdir = proto.u.nfsv3.type.mkdir; \ - nfs->symlink = proto.u.nfsv3.type.symlink; \ - nfs->mknod = proto.u.nfsv3.type.mknod; \ - nfs->remove = proto.u.nfsv3.type.remove; \ - nfs->rmdir = proto.u.nfsv3.type.rmdir; \ - nfs->rename = proto.u.nfsv3.type.rename; \ - nfs->link = proto.u.nfsv3.type.link; \ - nfs->readdir = proto.u.nfsv3.type.readdir; \ - nfs->readdirplus = proto.u.nfsv3.type.readdirplus; \ - nfs->fsstat = proto.u.nfsv3.type.fsstat; \ - nfs->fsinfo = proto.u.nfsv3.type.fsinfo; \ - nfs->pathconf = proto.u.nfsv3.type.pathconf; \ - nfs->commit = proto.u.nfsv3.type.commit - -int sigar_nfs_client_v3_get(sigar_t *sigar, - sigar_nfs_client_v3_t *nfs) -{ - perfstat_id_t id; - perfstat_protocol_t proto; - - SIGAR_SSTRCPY(id.name, "nfsv3"); - - if (perfstat_protocol(&id, &proto, sizeof(proto), 1) != 1) { - return ENOENT; - } - - NFS_V3_STAT_SET(client); - - return SIGAR_OK; -} - -int sigar_nfs_server_v3_get(sigar_t *sigar, - sigar_nfs_server_v3_t *nfs) -{ - perfstat_id_t id; - perfstat_protocol_t proto; - - SIGAR_SSTRCPY(id.name, "nfsv3"); - - if (perfstat_protocol(&id, &proto, sizeof(proto), 1) != 1) { - return ENOENT; - } - - NFS_V3_STAT_SET(server); - - return SIGAR_OK; -} - -#include -/* - * cannot find any related aix docs on reading the ARP table, - * this impl was gleaned from the above .h file and truss -f arp -an - */ -int sigar_arp_list_get(sigar_t *sigar, - sigar_arp_list_t *arplist) -{ - int status = SIGAR_OK; - long arptabsize; - int i, size, retval; - struct arptab *arptabp; - - size = sizeof(arptabsize); - retval = getkerninfo(KINFO_READ, &arptabsize, &size, - sigar->koffsets[KOFFSET_ARPTABSIZE]); - if (retval != sizeof(arptabsize)) { - return errno; - } - - size = sizeof(arptabp); - retval = getkerninfo(KINFO_READ, &arptabp, &size, - sigar->koffsets[KOFFSET_ARPTABP]); - if (retval != sizeof(arptabp)) { - return errno; - } - - sigar_arp_list_create(arplist); - status = SIGAR_OK; - - for (i=0; idata[arplist->number++]; - - sigar_net_address_set(arp->address, - ent.at_iaddr.s_addr); - - sigar_net_address_mac_set(arp->hwaddr, - ent.hwaddr, - sizeof(arp->hwaddr.addr.mac)); - - if_indextoname(ifp.if_index, arp->ifname); - - arp->flags = ent.at_flags; - SIGAR_SSTRCPY(arp->type, "ether"); /* XXX ifp.if_type */ - } - - if (status != SIGAR_OK) { - sigar_arp_list_destroy(sigar, arplist); - } - - return status; -} - -/* derived from pidentd's k_aix432.c */ -int sigar_proc_port_get(sigar_t *sigar, int protocol, - unsigned long port, sigar_pid_t *pidp) -{ - struct procsinfo pinfo; - struct fdsinfo finfo; - pid_t pid = 0; - int type; - - switch (protocol) { - case SIGAR_NETCONN_TCP: - type = IPPROTO_TCP; - break; - case SIGAR_NETCONN_UDP: - type = IPPROTO_UDP; - break; - default: - return SIGAR_ENOTIMPL; - } - - for (;;) { - int fd, status; - int num = getprocs(&pinfo, sizeof(pinfo), - &finfo, sizeof(finfo), - &pid, 1); - - if (num == 0) { - break; - } - - if ((pinfo.pi_state == 0) || (pinfo.pi_state == SZOMB)) { - continue; - } - - for (fd = 0; fd < pinfo.pi_maxofile; fd++) { - struct file file; - struct socket socket, *sockp; - struct protosw protosw; - struct domain domain; - struct inpcb inpcb; - long ptr; - - if (!(ptr = (long)finfo.pi_ufd[fd].fp)) { - continue; - } - - status = kread(sigar, &file, sizeof(file), ptr); - if (status != SIGAR_OK) { - continue; - } - - if (file.f_type != DTYPE_SOCKET) { - continue; - } - - if (!(sockp = (struct socket *)file.f_data)) { - continue; - } - - status = kread(sigar, &socket, sizeof(socket), (long)sockp); - if (status != SIGAR_OK) { - continue; - } - - if (!(ptr = (long)socket.so_proto)) { - continue; - } - - status = kread(sigar, &protosw, sizeof(protosw), ptr); - if (status != SIGAR_OK) { - continue; - } - - if (protosw.pr_protocol != type) { - continue; - } - - if (!(ptr = (long)protosw.pr_domain)) { - continue; - } - - status = kread(sigar, &domain, sizeof(domain), ptr); - if (status != SIGAR_OK) { - continue; - } - - if ((domain.dom_family != AF_INET) && - domain.dom_family != AF_INET6) - { - continue; - } - - if (!(ptr = (long)socket.so_pcb)) { - continue; - } - - status = kread(sigar, &inpcb, sizeof(inpcb), ptr); - if (status != SIGAR_OK) { - continue; - } - - if (sockp != inpcb.inp_socket) { - continue; - } - - if (inpcb.inp_lport != port) { - continue; - } - - *pidp = pinfo.pi_pid; - - return SIGAR_OK; - } - } - - return ENOENT; -} - -int sigar_os_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sysinfo) -{ - struct utsname name; - - uname(&name); - - SIGAR_SSTRCPY(sysinfo->vendor, "IBM"); - SIGAR_SSTRCPY(sysinfo->arch, get_cpu_arch()); - /* utsname.machine is a sequence number */ - /* XXX odm might have something better */ - snprintf(sysinfo->machine, - sizeof(sysinfo->machine), - "%s %s", - sysinfo->arch, get_cpu_model()); - - snprintf(sysinfo->version, - sizeof(sysinfo->version), - "%s.%s", - name.version, name.release); - - SIGAR_SSTRCPY(sysinfo->vendor_version, sysinfo->version); - - snprintf(sysinfo->description, - sizeof(sysinfo->description), - "%s %s", - sysinfo->name, sysinfo->version); - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/os/aix/sigar_os.h b/vendor/sigar/src/os/aix/sigar_os.h deleted file mode 100644 index 5ee7cf066..000000000 --- a/vendor/sigar/src/os/aix/sigar_os.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2004-2007, 2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_OS_H -#define SIGAR_OS_H - -#include -#include -#include -#include -#include - -enum { - KOFFSET_LOADAVG, - KOFFSET_VAR, - KOFFSET_SYSINFO, - KOFFSET_IFNET, - KOFFSET_VMINFO, - KOFFSET_CPUINFO, - KOFFSET_TCB, - KOFFSET_ARPTABSIZE, - KOFFSET_ARPTABP, - KOFFSET_MAX -}; - -typedef struct { - time_t mtime; - int num; - char **devs; -} swaps_t; - -typedef int (*proc_fd_func_t) (sigar_t *, sigar_pid_t, sigar_proc_fd_t *); - -struct sigar_t { - SIGAR_T_BASE; - int kmem; - /* offsets for seeking on kmem */ - long koffsets[KOFFSET_MAX]; - proc_fd_func_t getprocfd; - int pagesize; - swaps_t swaps; - time_t last_getprocs; - sigar_pid_t last_pid; - struct procsinfo64 *pinfo; - struct cpuinfo *cpuinfo; - int cpuinfo_size; - int cpu_mhz; - char model[128]; - int aix_version; - int thrusage; - sigar_cache_t *diskmap; -}; - -#define HAVE_STRERROR_R - -#define SIGAR_EPERM_KMEM (SIGAR_OS_START_ERROR+EACCES) - -#endif /* SIGAR_OS_H */ diff --git a/vendor/sigar/src/os/darwin/darwin_sigar.c b/vendor/sigar/src/os/darwin/darwin_sigar.c deleted file mode 100644 index 0e154b092..000000000 --- a/vendor/sigar/src/os/darwin/darwin_sigar.c +++ /dev/null @@ -1,3711 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#include -#include -#if !(defined(__FreeBSD__) && (__FreeBSD_version >= 800000)) -#include -#endif -#include - -#ifdef DARWIN -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if !defined(HAVE_SHARED_REGION_H) && defined(__MAC_10_5) /* see Availability.h */ -# define HAVE_SHARED_REGION_H /* suckit autoconf */ -#endif -#ifdef HAVE_SHARED_REGION_H -#include /* does not exist in 10.4 SDK */ -#else -#include /* deprecated in Leopard */ -#endif -#include -#define __OPENTRANSPORTPROVIDERS__ -#include -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#endif - -#if defined(__FreeBSD__) && (__FreeBSD_version >= 500013) -#define SIGAR_FREEBSD5_NFSSTAT -#include -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#ifdef __NetBSD__ -#include -#include -#include -#define SRUN LSRUN -#define SSLEEP LSSLEEP -#define SDEAD LSDEAD -#define SONPROC LSONPROC -#define SSUSPENDED LSSUSPENDED -#include -#endif -#include -#include - -#define NMIB(mib) (sizeof(mib)/sizeof(mib[0])) - -#ifdef __FreeBSD__ -# if (__FreeBSD_version >= 500013) -# define SIGAR_FREEBSD5 -# else -# define SIGAR_FREEBSD4 -# endif -#endif - -#if defined(SIGAR_FREEBSD5) - -#define KI_FD ki_fd -#define KI_PID ki_pid -#define KI_PPID ki_ppid -#define KI_PRI ki_pri.pri_user -#define KI_NICE ki_nice -#define KI_COMM ki_comm -#define KI_STAT ki_stat -#define KI_UID ki_ruid -#define KI_GID ki_rgid -#define KI_EUID ki_svuid -#define KI_EGID ki_svgid -#define KI_SIZE ki_size -#define KI_RSS ki_rssize -#define KI_TSZ ki_tsize -#define KI_DSZ ki_dsize -#define KI_SSZ ki_ssize -#define KI_FLAG ki_flag -#define KI_START ki_start - -#elif defined(DARWIN) || defined(SIGAR_FREEBSD4) || defined(__OpenBSD__) || defined(__NetBSD__) - -#define KI_FD kp_proc.p_fd -#define KI_PID kp_proc.p_pid -#define KI_PPID kp_eproc.e_ppid -#define KI_PRI kp_proc.p_priority -#define KI_NICE kp_proc.p_nice -#define KI_COMM kp_proc.p_comm -#define KI_STAT kp_proc.p_stat -#define KI_UID kp_eproc.e_pcred.p_ruid -#define KI_GID kp_eproc.e_pcred.p_rgid -#define KI_EUID kp_eproc.e_pcred.p_svuid -#define KI_EGID kp_eproc.e_pcred.p_svgid -#define KI_SIZE XXX -#define KI_RSS kp_eproc.e_vm.vm_rssize -#define KI_TSZ kp_eproc.e_vm.vm_tsize -#define KI_DSZ kp_eproc.e_vm.vm_dsize -#define KI_SSZ kp_eproc.e_vm.vm_ssize -#define KI_FLAG kp_eproc.e_flag -#define KI_START kp_proc.p_starttime - -#endif - -#ifndef DARWIN - -#define PROCFS_STATUS(status) \ - ((((status) != SIGAR_OK) && !sigar->proc_mounted) ? \ - SIGAR_ENOTIMPL : status) - -static int get_koffsets(sigar_t *sigar) -{ - int i; - struct nlist klist[] = { - { "_cp_time" }, - { "_cnt" }, -#if defined(__OpenBSD__) || defined(__NetBSD__) - { "_tcpstat" }, - { "_tcbtable" }, -#endif - { NULL } - }; - - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - kvm_nlist(sigar->kmem, klist); - - for (i=0; ikoffsets[i] = klist[i].n_value; - } - - return SIGAR_OK; -} - -static int kread(sigar_t *sigar, void *data, int size, long offset) -{ - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - if (kvm_read(sigar->kmem, offset, data, size) != size) { - return errno; - } - - return SIGAR_OK; -} -#endif - -int sigar_os_open(sigar_t **sigar) -{ - int mib[2]; - int ncpu; - size_t len; - struct timeval boottime; -#ifndef DARWIN - struct stat sb; -#endif - - len = sizeof(ncpu); - mib[0] = CTL_HW; - mib[1] = HW_NCPU; - if (sysctl(mib, NMIB(mib), &ncpu, &len, NULL, 0) < 0) { - return errno; - } - - len = sizeof(boottime); - mib[0] = CTL_KERN; - mib[1] = KERN_BOOTTIME; - if (sysctl(mib, NMIB(mib), &boottime, &len, NULL, 0) < 0) { - return errno; - } - - *sigar = malloc(sizeof(**sigar)); - -#ifdef DARWIN - (*sigar)->mach_port = mach_host_self(); -# ifdef DARWIN_HAS_LIBPROC_H - if (((*sigar)->libproc = dlopen("/usr/lib/libproc.dylib", 0))) { - (*sigar)->proc_pidinfo = dlsym((*sigar)->libproc, "proc_pidinfo"); - (*sigar)->proc_pidfdinfo = dlsym((*sigar)->libproc, "proc_pidfdinfo"); - } -# endif -#else - (*sigar)->kmem = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); - if (stat("/proc/curproc", &sb) < 0) { - (*sigar)->proc_mounted = 0; - } - else { - (*sigar)->proc_mounted = 1; - } -#endif - -#ifndef DARWIN - get_koffsets(*sigar); -#endif - - (*sigar)->ncpu = ncpu; - (*sigar)->lcpu = -1; - (*sigar)->argmax = 0; - (*sigar)->boot_time = boottime.tv_sec; /* XXX seems off a bit */ - - (*sigar)->pagesize = getpagesize(); -#ifdef __FreeBSD__ - (*sigar)->ticks = 100; /* sysconf(_SC_CLK_TCK) == 128 !? */ -#else - (*sigar)->ticks = sysconf(_SC_CLK_TCK); -#endif - (*sigar)->last_pid = -1; - - (*sigar)->pinfo = NULL; - - return SIGAR_OK; -} - -int sigar_os_close(sigar_t *sigar) -{ - if (sigar->pinfo) { - free(sigar->pinfo); - } -#ifndef DARWIN - if (sigar->kmem) { - kvm_close(sigar->kmem); - } -#endif - free(sigar); - return SIGAR_OK; -} - -char *sigar_os_error_string(sigar_t *sigar, int err) -{ - switch (err) { - case SIGAR_EPERM_KMEM: - return "Failed to open /dev/kmem for reading"; - case SIGAR_EPROC_NOENT: - return "/proc filesystem is not mounted"; - default: - return NULL; - } -} - -/* ARG_MAX in FreeBSD 6.0 == 262144, which blows up the stack */ -#define SIGAR_ARG_MAX 65536 - -#ifdef DARWIN -static size_t sigar_argmax_get(sigar_t *sigar) -{ -#ifdef KERN_ARGMAX - int mib[] = { CTL_KERN, KERN_ARGMAX }; - size_t size = sizeof(sigar->argmax); - - if (sigar->argmax != 0) { - return sigar->argmax; - } - if (sysctl(mib, NMIB(mib), &sigar->argmax, &size, NULL, 0) == 0) { - return sigar->argmax; - } -#endif - return SIGAR_ARG_MAX; -} -#endif /* DARWIN */ - -#if defined(DARWIN) -static int sigar_vmstat(sigar_t *sigar, vm_statistics_data_t *vmstat) -{ - kern_return_t status; - mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t); - - status = host_statistics(sigar->mach_port, HOST_VM_INFO, - (host_info_t)vmstat, &count); - - if (status == KERN_SUCCESS) { - return SIGAR_OK; - } - else { - return errno; - } -} -#elif defined(__FreeBSD__) -static int sigar_vmstat(sigar_t *sigar, struct vmmeter *vmstat) -{ - int status; - size_t size = sizeof(unsigned int); - - status = kread(sigar, vmstat, sizeof(*vmstat), - sigar->koffsets[KOFFSET_VMMETER]); - - if (status == SIGAR_OK) { - return SIGAR_OK; - } - - SIGAR_ZERO(vmstat); - - /* derived from src/usr.bin/vmstat/vmstat.c */ - /* only collect the ones we actually use */ -#define GET_VM_STATS(cat, name, used) \ - if (used) sysctlbyname("vm.stats." #cat "." #name, &vmstat->name, &size, NULL, 0) - - /* sys */ - GET_VM_STATS(sys, v_swtch, 0); - GET_VM_STATS(sys, v_trap, 0); - GET_VM_STATS(sys, v_syscall, 0); - GET_VM_STATS(sys, v_intr, 0); - GET_VM_STATS(sys, v_soft, 0); - - /* vm */ - GET_VM_STATS(vm, v_vm_faults, 0); - GET_VM_STATS(vm, v_cow_faults, 0); - GET_VM_STATS(vm, v_cow_optim, 0); - GET_VM_STATS(vm, v_zfod, 0); - GET_VM_STATS(vm, v_ozfod, 0); - GET_VM_STATS(vm, v_swapin, 1); - GET_VM_STATS(vm, v_swapout, 1); - GET_VM_STATS(vm, v_swappgsin, 0); - GET_VM_STATS(vm, v_swappgsout, 0); - GET_VM_STATS(vm, v_vnodein, 1); - GET_VM_STATS(vm, v_vnodeout, 1); - GET_VM_STATS(vm, v_vnodepgsin, 0); - GET_VM_STATS(vm, v_vnodepgsout, 0); - GET_VM_STATS(vm, v_intrans, 0); - GET_VM_STATS(vm, v_reactivated, 0); - GET_VM_STATS(vm, v_pdwakeups, 0); - GET_VM_STATS(vm, v_pdpages, 0); - GET_VM_STATS(vm, v_dfree, 0); - GET_VM_STATS(vm, v_pfree, 0); - GET_VM_STATS(vm, v_tfree, 0); - GET_VM_STATS(vm, v_page_size, 0); - GET_VM_STATS(vm, v_page_count, 0); - GET_VM_STATS(vm, v_free_reserved, 0); - GET_VM_STATS(vm, v_free_target, 0); - GET_VM_STATS(vm, v_free_min, 0); - GET_VM_STATS(vm, v_free_count, 1); - GET_VM_STATS(vm, v_wire_count, 0); - GET_VM_STATS(vm, v_active_count, 0); - GET_VM_STATS(vm, v_inactive_target, 0); - GET_VM_STATS(vm, v_inactive_count, 1); - GET_VM_STATS(vm, v_cache_count, 1); - GET_VM_STATS(vm, v_cache_min, 0); - GET_VM_STATS(vm, v_cache_max, 0); - GET_VM_STATS(vm, v_pageout_free_min, 0); - GET_VM_STATS(vm, v_interrupt_free_min, 0); - GET_VM_STATS(vm, v_forks, 0); - GET_VM_STATS(vm, v_vforks, 0); - GET_VM_STATS(vm, v_rforks, 0); - GET_VM_STATS(vm, v_kthreads, 0); - GET_VM_STATS(vm, v_forkpages, 0); - GET_VM_STATS(vm, v_vforkpages, 0); - GET_VM_STATS(vm, v_rforkpages, 0); - GET_VM_STATS(vm, v_kthreadpages, 0); -#undef GET_VM_STATS - - return SIGAR_OK; -} -#elif defined(__OpenBSD__) || defined(__NetBSD__) -static int sigar_vmstat(sigar_t *sigar, struct uvmexp *vmstat) -{ - size_t size = sizeof(*vmstat); - int mib[] = { CTL_VM, VM_UVMEXP }; - if (sysctl(mib, NMIB(mib), vmstat, &size, NULL, 0) < 0) { - return errno; - } - else { - return SIGAR_OK; - } -} -#endif - -int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) -{ - sigar_uint64_t kern = 0; -#ifdef DARWIN - vm_statistics_data_t vmstat; - uint64_t mem_total; -#else - unsigned long mem_total; -#endif -#if defined(__FreeBSD__) - struct vmmeter vmstat; -#elif defined(__OpenBSD__) || defined(__NetBSD__) - struct uvmexp vmstat; -#endif - int mib[2]; - size_t len; - int status; - - mib[0] = CTL_HW; - - mib[1] = HW_PAGESIZE; - len = sizeof(sigar->pagesize); - if (sysctl(mib, NMIB(mib), &sigar->pagesize, &len, NULL, 0) < 0) { - return errno; - } - -#ifdef DARWIN - mib[1] = HW_MEMSIZE; -#else - mib[1] = HW_PHYSMEM; -#endif - len = sizeof(mem_total); - if (sysctl(mib, NMIB(mib), &mem_total, &len, NULL, 0) < 0) { - return errno; - } - - mem->total = mem_total; - -#if defined(DARWIN) - if ((status = sigar_vmstat(sigar, &vmstat)) != SIGAR_OK) { - return status; - } - - mem->free = vmstat.free_count; - mem->free *= sigar->pagesize; - kern = vmstat.inactive_count; - kern *= sigar->pagesize; -#elif defined(__FreeBSD__) - if ((status = sigar_vmstat(sigar, &vmstat)) == SIGAR_OK) { - kern = vmstat.v_cache_count + vmstat.v_inactive_count; - kern *= sigar->pagesize; - mem->free = vmstat.v_free_count; - mem->free *= sigar->pagesize; - } -#elif defined(__OpenBSD__) || defined(__NetBSD__) - if ((status = sigar_vmstat(sigar, &vmstat)) != SIGAR_OK) { - return status; - } - mem->free = vmstat.free; - kern = vmstat.inactive; -# if defined(__OpenBSD__) - kern += vmstat.vnodepages + vmstat.vtextpages; -# elif defined(__NetBSD__) - kern += vmstat.filepages + vmstat.execpages; -# endif - kern *= sigar->pagesize; -#endif - - mem->used = mem->total - mem->free; - - mem->actual_free = mem->free + kern; - mem->actual_used = mem->used - kern; - - sigar_mem_calc_ram(sigar, mem); - - return SIGAR_OK; -} - -#define SWI_MAXMIB 3 - -#ifdef SIGAR_FREEBSD5 -/* code in this function is based on FreeBSD 5.3 kvm_getswapinfo.c */ -static int getswapinfo_sysctl(struct kvm_swap *swap_ary, - int swap_max) -{ - int ti, ttl; - size_t mibi, len, size; - int soid[SWI_MAXMIB]; - struct xswdev xsd; - struct kvm_swap tot; - int unswdev, dmmax; - - /* XXX this can be optimized by using os_open */ - size = sizeof(dmmax); - if (sysctlbyname("vm.dmmax", &dmmax, &size, NULL, 0) == -1) { - return errno; - } - - mibi = SWI_MAXMIB - 1; - if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) { - return errno; - } - - bzero(&tot, sizeof(tot)); - for (unswdev = 0;; unswdev++) { - soid[mibi] = unswdev; - len = sizeof(xsd); - if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) { - if (errno == ENOENT) { - break; - } - return errno; - } -#if 0 - if (len != sizeof(xsd)) { - _kvm_err(kd, kd->program, "struct xswdev has unexpected " - "size; kernel and libkvm out of sync?"); - return -1; - } - if (xsd.xsw_version != XSWDEV_VERSION) { - _kvm_err(kd, kd->program, "struct xswdev version " - "mismatch; kernel and libkvm out of sync?"); - return -1; - } -#endif - ttl = xsd.xsw_nblks - dmmax; - if (unswdev < swap_max - 1) { - bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev])); - swap_ary[unswdev].ksw_total = ttl; - swap_ary[unswdev].ksw_used = xsd.xsw_used; - swap_ary[unswdev].ksw_flags = xsd.xsw_flags; - } - tot.ksw_total += ttl; - tot.ksw_used += xsd.xsw_used; - } - - ti = unswdev; - if (ti >= swap_max) { - ti = swap_max - 1; - } - if (ti >= 0) { - swap_ary[ti] = tot; - } - - return SIGAR_OK; -} -#else -#define getswapinfo_sysctl(swap_ary, swap_max) SIGAR_ENOTIMPL -#endif - -#define SIGAR_FS_BLOCKS_TO_BYTES(val, bsize) ((val * bsize) >> 1) - -#ifdef DARWIN -#define VM_DIR "/private/var/vm" -#define SWAPFILE "swapfile" - -static int sigar_swap_fs_get(sigar_t *sigar, sigar_swap_t *swap) /* <= 10.3 */ -{ - DIR *dirp; - struct dirent *ent; - char swapfile[SSTRLEN(VM_DIR) + SSTRLEN("/") + SSTRLEN(SWAPFILE) + 12]; - struct stat swapstat; - struct statfs vmfs; - sigar_uint64_t val, bsize; - - swap->used = swap->total = swap->free = 0; - - if (!(dirp = opendir(VM_DIR))) { - return errno; - } - - /* looking for "swapfile0", "swapfile1", etc. */ - while ((ent = readdir(dirp))) { - char *ptr = swapfile; - - if ((ent->d_namlen < SSTRLEN(SWAPFILE)+1) || /* n/a, see comment above */ - (ent->d_namlen > SSTRLEN(SWAPFILE)+11)) /* ensure no overflow */ - { - continue; - } - - if (!strnEQ(ent->d_name, SWAPFILE, SSTRLEN(SWAPFILE))) { - continue; - } - - /* sprintf(swapfile, "%s/%s", VM_DIR, ent->d_name) */ - - memcpy(ptr, VM_DIR, SSTRLEN(VM_DIR)); - ptr += SSTRLEN(VM_DIR); - - *ptr++ = '/'; - - memcpy(ptr, ent->d_name, ent->d_namlen+1); - - if (stat(swapfile, &swapstat) < 0) { - continue; - } - - swap->used += swapstat.st_size; - } - - closedir(dirp); - - if (statfs(VM_DIR, &vmfs) < 0) { - return errno; - } - - bsize = vmfs.f_bsize / 512; - val = vmfs.f_bfree; - swap->total = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize) + swap->used; - - swap->free = swap->total - swap->used; - - return SIGAR_OK; -} - -static int sigar_swap_sysctl_get(sigar_t *sigar, sigar_swap_t *swap) - -{ -#ifdef VM_SWAPUSAGE /* => 10.4 */ - struct xsw_usage sw_usage; - size_t size = sizeof(sw_usage); - int mib[] = { CTL_VM, VM_SWAPUSAGE }; - - if (sysctl(mib, NMIB(mib), &sw_usage, &size, NULL, 0) != 0) { - return errno; - } - - swap->total = sw_usage.xsu_total; - swap->used = sw_usage.xsu_used; - swap->free = sw_usage.xsu_avail; - - return SIGAR_OK; -#else - return SIGAR_ENOTIMPL; /* <= 10.3 */ -#endif -} -#endif /* DARWIN */ - -int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) -{ - int status; -#if defined(DARWIN) - vm_statistics_data_t vmstat; - - if (sigar_swap_sysctl_get(sigar, swap) != SIGAR_OK) { - status = sigar_swap_fs_get(sigar, swap); /* <= 10.3 */ - if (status != SIGAR_OK) { - return status; - } - } - - if ((status = sigar_vmstat(sigar, &vmstat)) != SIGAR_OK) { - return status; - } - swap->page_in = vmstat.pageins; - swap->page_out = vmstat.pageouts; -#elif defined(__FreeBSD__) - struct kvm_swap kswap[1]; - struct vmmeter vmstat; - - if (getswapinfo_sysctl(kswap, 1) != SIGAR_OK) { - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - if (kvm_getswapinfo(sigar->kmem, kswap, 1, 0) < 0) { - return errno; - } - } - - if (kswap[0].ksw_total == 0) { - swap->total = 0; - swap->used = 0; - swap->free = 0; - return SIGAR_OK; - } - - swap->total = kswap[0].ksw_total * sigar->pagesize; - swap->used = kswap[0].ksw_used * sigar->pagesize; - swap->free = swap->total - swap->used; - - if ((status = sigar_vmstat(sigar, &vmstat)) == SIGAR_OK) { - swap->page_in = vmstat.v_swapin + vmstat.v_vnodein; - swap->page_out = vmstat.v_swapout + vmstat.v_vnodeout; - } - else { - swap->page_in = swap->page_out = -1; - } -#elif defined(__OpenBSD__) || defined(__NetBSD__) - struct uvmexp vmstat; - - if ((status = sigar_vmstat(sigar, &vmstat)) != SIGAR_OK) { - return status; - } - swap->total = vmstat.swpages * sigar->pagesize; - swap->used = vmstat.swpginuse * sigar->pagesize; - swap->free = swap->total - swap->used; - swap->page_in = vmstat.pageins; - swap->page_out = vmstat.pdpageouts; -#endif - - return SIGAR_OK; -} - -#ifndef KERN_CPTIME -#define KERN_CPTIME KERN_CP_TIME -#endif - -#if defined(__NetBSD__) -typedef uint64_t cp_time_t; -#else -typedef unsigned long cp_time_t; -#endif - -int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ -#if defined(DARWIN) - kern_return_t status; - mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; - host_cpu_load_info_data_t cpuload; - - status = host_statistics(sigar->mach_port, HOST_CPU_LOAD_INFO, - (host_info_t)&cpuload, &count); - - if (status != KERN_SUCCESS) { - return errno; - } - - cpu->user = SIGAR_TICK2MSEC(cpuload.cpu_ticks[CPU_STATE_USER]); - cpu->sys = SIGAR_TICK2MSEC(cpuload.cpu_ticks[CPU_STATE_SYSTEM]); - cpu->idle = SIGAR_TICK2MSEC(cpuload.cpu_ticks[CPU_STATE_IDLE]); - cpu->nice = SIGAR_TICK2MSEC(cpuload.cpu_ticks[CPU_STATE_NICE]); - cpu->wait = 0; /*N/A*/ - cpu->irq = 0; /*N/A*/ - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle; - -#elif defined(__FreeBSD__) || (__OpenBSD__) || defined(__NetBSD__) - int status; - cp_time_t cp_time[CPUSTATES]; - size_t size = sizeof(cp_time); - -# if defined(__OpenBSD__) || defined(__NetBSD__) - int mib[] = { CTL_KERN, KERN_CPTIME }; - if (sysctl(mib, NMIB(mib), &cp_time, &size, NULL, 0) == -1) { - status = errno; - } -# else - /* try sysctl first, does not require /dev/kmem perms */ - if (sysctlbyname("kern.cp_time", &cp_time, &size, NULL, 0) == -1) { - status = kread(sigar, &cp_time, sizeof(cp_time), - sigar->koffsets[KOFFSET_CPUINFO]); - } -# endif - else { - status = SIGAR_OK; - } - - if (status != SIGAR_OK) { - return status; - } - - cpu->user = SIGAR_TICK2MSEC(cp_time[CP_USER]); - cpu->nice = SIGAR_TICK2MSEC(cp_time[CP_NICE]); - cpu->sys = SIGAR_TICK2MSEC(cp_time[CP_SYS]); - cpu->idle = SIGAR_TICK2MSEC(cp_time[CP_IDLE]); - cpu->wait = 0; /*N/A*/ - cpu->irq = SIGAR_TICK2MSEC(cp_time[CP_INTR]); - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle + cpu->irq; -#endif - - return SIGAR_OK; -} - -#if defined(__FreeBSD__) && (__FreeBSD_version >= 700000) -#define HAVE_KERN_CP_TIMES /* kern.cp_times came later than 7.0, not sure exactly when */ -static int sigar_cp_times_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) -{ - int maxcpu, status; - size_t len = sizeof(maxcpu), size; - long *times; - - if (sysctlbyname("kern.smp.maxcpus", &maxcpu, &len, NULL, 0) == -1) { - return errno; - } - - size = sizeof(long) * maxcpu * CPUSTATES; - times = malloc(size); - if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1) { - status = errno; - } - else { - int i, maxid = (size / CPUSTATES / sizeof(long)); - long *cp_time = times; - status = SIGAR_OK; - - for (i=0; idata[cpulist->number++]; - cpu->user = SIGAR_TICK2MSEC(cp_time[CP_USER]); - cpu->nice = SIGAR_TICK2MSEC(cp_time[CP_NICE]); - cpu->sys = SIGAR_TICK2MSEC(cp_time[CP_SYS]); - cpu->idle = SIGAR_TICK2MSEC(cp_time[CP_IDLE]); - cpu->wait = 0; /*N/A*/ - cpu->irq = SIGAR_TICK2MSEC(cp_time[CP_INTR]); - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle + cpu->irq; - cp_time += CPUSTATES; - } - } - - free(times); - return status; -} -#endif - -int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) -{ -#ifdef DARWIN - kern_return_t status; - mach_msg_type_number_t count; - processor_cpu_load_info_data_t *cpuload; - natural_t i, ncpu; - - status = host_processor_info(sigar->mach_port, - PROCESSOR_CPU_LOAD_INFO, - &ncpu, - (processor_info_array_t*)&cpuload, - &count); - - if (status != KERN_SUCCESS) { - return errno; - } - - sigar_cpu_list_create(cpulist); - - for (i=0; idata[cpulist->number++]; - - cpu->user = SIGAR_TICK2MSEC(cpuload[i].cpu_ticks[CPU_STATE_USER]); - cpu->sys = SIGAR_TICK2MSEC(cpuload[i].cpu_ticks[CPU_STATE_SYSTEM]); - cpu->idle = SIGAR_TICK2MSEC(cpuload[i].cpu_ticks[CPU_STATE_IDLE]); - cpu->nice = SIGAR_TICK2MSEC(cpuload[i].cpu_ticks[CPU_STATE_NICE]); - cpu->wait = 0; /*N/A*/ - cpu->irq = 0; /*N/A*/ - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle; - } - - vm_deallocate(mach_task_self(), (vm_address_t)cpuload, count); - - return SIGAR_OK; -#else - int status, i; - sigar_cpu_t *cpu; - - sigar_cpu_list_create(cpulist); - -#ifdef HAVE_KERN_CP_TIMES - if ((status = sigar_cp_times_get(sigar, cpulist)) == SIGAR_OK) { - return SIGAR_OK; - } -#endif - /* XXX no multi cpu in freebsd < 7.0, howbout others? - * for now just report all metrics on the 1st cpu - * 0's for the rest - */ - cpu = &cpulist->data[cpulist->number++]; - - status = sigar_cpu_get(sigar, cpu); - if (status != SIGAR_OK) { - return status; - } - - for (i=1; incpu; i++) { - SIGAR_CPU_LIST_GROW(cpulist); - - cpu = &cpulist->data[cpulist->number++]; - SIGAR_ZERO(cpu); - } - - return SIGAR_OK; -#endif -} - -int sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime) -{ - uptime->uptime = time(NULL) - sigar->boot_time; - - return SIGAR_OK; -} - -int sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg) -{ - getloadavg(loadavg->loadavg, 3); - - return SIGAR_OK; -} - -#if defined(DARWIN) && defined(DARWIN_HAS_LIBPROC_H) - -static int proc_fdinfo_get(sigar_t *sigar, sigar_pid_t pid, int *num) -{ - int rsize; - const int init_size = PROC_PIDLISTFD_SIZE * 32; - - if (!sigar->libproc) { - return SIGAR_ENOTIMPL; - } - - if (sigar->ifconf_len == 0) { - sigar->ifconf_len = init_size; - sigar->ifconf_buf = malloc(sigar->ifconf_len); - } - - while (1) { - rsize = sigar->proc_pidinfo(pid, PROC_PIDLISTFDS, 0, - sigar->ifconf_buf, sigar->ifconf_len); - if (rsize <= 0) { - return errno; - } - if ((rsize + PROC_PIDLISTFD_SIZE) < sigar->ifconf_len) { - break; - } - - sigar->ifconf_len += init_size; - sigar->ifconf_buf = realloc(sigar->ifconf_buf, sigar->ifconf_len); - } - - *num = rsize / PROC_PIDLISTFD_SIZE; - - return SIGAR_OK; -} - -#endif - -#ifndef KERN_PROC_PROC -/* freebsd 4.x */ -#define KERN_PROC_PROC KERN_PROC_ALL -#endif - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ -#if defined(DARWIN) || defined(SIGAR_FREEBSD5) || defined(__OpenBSD__) || defined(__NetBSD__) - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; - int i, num; - size_t len; - struct kinfo_proc *proc; - - if (sysctl(mib, NMIB(mib), NULL, &len, NULL, 0) < 0) { - return errno; - } - - proc = malloc(len); - - if (sysctl(mib, NMIB(mib), proc, &len, NULL, 0) < 0) { - free(proc); - return errno; - } - - num = len/sizeof(*proc); - - for (i=0; idata[proclist->number++] = proc[i].KI_PID; - } - - free(proc); - - return SIGAR_OK; -#else - int i, num; - struct kinfo_proc *proc; - - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - proc = kvm_getprocs(sigar->kmem, KERN_PROC_PROC, 0, &num); - - for (i=0; idata[proclist->number++] = proc[i].KI_PID; - } -#endif - - return SIGAR_OK; -} - -static int sigar_get_pinfo(sigar_t *sigar, sigar_pid_t pid) -{ -#if defined(__OpenBSD__) || defined(__NetBSD__) - int mib[] = { CTL_KERN, KERN_PROC2, KERN_PROC_PID, 0, sizeof(*sigar->pinfo), 1 }; -#else - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; -#endif - size_t len = sizeof(*sigar->pinfo); - time_t timenow = time(NULL); - mib[3] = pid; - - if (sigar->pinfo == NULL) { - sigar->pinfo = malloc(len); - } - - if (sigar->last_pid == pid) { - if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { - return SIGAR_OK; - } - } - - sigar->last_pid = pid; - sigar->last_getprocs = timenow; - - if (sysctl(mib, NMIB(mib), sigar->pinfo, &len, NULL, 0) < 0) { - return errno; - } - - return SIGAR_OK; -} - -#if defined(SHARED_TEXT_REGION_SIZE) && defined(SHARED_DATA_REGION_SIZE) -# define GLOBAL_SHARED_SIZE (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE) /* 10.4 SDK */ -#endif - -#if defined(DARWIN) && defined(DARWIN_HAS_LIBPROC_H) && !defined(GLOBAL_SHARED_SIZE) -/* get the CPU type of the process for the given pid */ -static int sigar_proc_cpu_type(sigar_t *sigar, sigar_pid_t pid, cpu_type_t *type) -{ - int status; - int mib[CTL_MAXNAME]; - size_t len, miblen = NMIB(mib); - - status = sysctlnametomib("sysctl.proc_cputype", mib, &miblen); - if (status != SIGAR_OK) { - return status; - } - - mib[miblen] = pid; - len = sizeof(*type); - return sysctl(mib, miblen + 1, type, &len, NULL, 0); -} - -/* shared memory region size for the given cpu_type_t */ -static mach_vm_size_t sigar_shared_region_size(cpu_type_t type) -{ - switch (type) { - case CPU_TYPE_ARM: - return SHARED_REGION_SIZE_ARM; - case CPU_TYPE_POWERPC: - return SHARED_REGION_SIZE_PPC; - case CPU_TYPE_POWERPC64: - return SHARED_REGION_SIZE_PPC64; - case CPU_TYPE_I386: - return SHARED_REGION_SIZE_I386; - case CPU_TYPE_X86_64: - return SHARED_REGION_SIZE_X86_64; - default: - return SHARED_REGION_SIZE_I386; /* assume 32-bit x86|ppc */ - } -} -#endif /* DARWIN */ - -int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem) -{ -#if defined(DARWIN) - mach_port_t task, self = mach_task_self(); - kern_return_t status; - task_basic_info_data_t info; - task_events_info_data_t events; - mach_msg_type_number_t count; -# ifdef DARWIN_HAS_LIBPROC_H - struct proc_taskinfo pti; - struct proc_regioninfo pri; - - if (sigar->libproc) { - int sz = - sigar->proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)); - - if (sz == sizeof(pti)) { - procmem->size = pti.pti_virtual_size; - procmem->resident = pti.pti_resident_size; - procmem->page_faults = pti.pti_faults; - procmem->minor_faults = SIGAR_FIELD_NOTIMPL; - procmem->major_faults = SIGAR_FIELD_NOTIMPL; - procmem->share = SIGAR_FIELD_NOTIMPL; - - sz = sigar->proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, sizeof(pri)); - if (sz == sizeof(pri)) { - if (pri.pri_share_mode == SM_EMPTY) { - mach_vm_size_t shared_size; -#ifdef GLOBAL_SHARED_SIZE - shared_size = GLOBAL_SHARED_SIZE; /* 10.4 SDK */ -#else - cpu_type_t cpu_type; - - if (sigar_proc_cpu_type(sigar, pid, &cpu_type) == SIGAR_OK) { - shared_size = sigar_shared_region_size(cpu_type); - } - else { - shared_size = SHARED_REGION_SIZE_I386; /* assume 32-bit x86|ppc */ - } -#endif - if (procmem->size > shared_size) { - procmem->size -= shared_size; /* SIGAR-123 */ - } - } - } - return SIGAR_OK; - } - } -# endif - - status = task_for_pid(self, pid, &task); - - if (status != KERN_SUCCESS) { - return errno; - } - - count = TASK_BASIC_INFO_COUNT; - status = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count); - if (status != KERN_SUCCESS) { - return errno; - } - - count = TASK_EVENTS_INFO_COUNT; - status = task_info(task, TASK_EVENTS_INFO, (task_info_t)&events, &count); - if (status == KERN_SUCCESS) { - procmem->page_faults = events.faults; - } - else { - procmem->page_faults = SIGAR_FIELD_NOTIMPL; - } - - procmem->minor_faults = SIGAR_FIELD_NOTIMPL; - procmem->major_faults = SIGAR_FIELD_NOTIMPL; - - if (task != self) { - mach_port_deallocate(self, task); - } - - procmem->size = info.virtual_size; - procmem->resident = info.resident_size; - procmem->share = SIGAR_FIELD_NOTIMPL; - - return SIGAR_OK; -#elif defined(__FreeBSD__) - int status = sigar_get_pinfo(sigar, pid); - bsd_pinfo_t *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = - (pinfo->KI_TSZ + pinfo->KI_DSZ + pinfo->KI_SSZ) * sigar->pagesize; - - procmem->resident = pinfo->KI_RSS * sigar->pagesize; - - procmem->share = SIGAR_FIELD_NOTIMPL; - - procmem->page_faults = SIGAR_FIELD_NOTIMPL; - procmem->minor_faults = SIGAR_FIELD_NOTIMPL; - procmem->major_faults = SIGAR_FIELD_NOTIMPL; -#elif defined(__OpenBSD__) || defined(__NetBSD__) - int status = sigar_get_pinfo(sigar, pid); - bsd_pinfo_t *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = - (pinfo->p_vm_tsize + pinfo->p_vm_dsize + pinfo->p_vm_ssize) * sigar->pagesize; - - procmem->resident = pinfo->p_vm_rssize * sigar->pagesize; - - procmem->share = SIGAR_FIELD_NOTIMPL; - - procmem->minor_faults = pinfo->p_uru_minflt; - procmem->major_faults = pinfo->p_uru_majflt; - procmem->page_faults = procmem->minor_faults + procmem->major_faults; -#endif - return SIGAR_OK; -} - -int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred) -{ - int status = sigar_get_pinfo(sigar, pid); - bsd_pinfo_t *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - -#if defined(__OpenBSD__) || defined(__NetBSD__) - proccred->uid = pinfo->p_ruid; - proccred->gid = pinfo->p_rgid; - proccred->euid = pinfo->p_uid; - proccred->egid = pinfo->p_gid; -#else - proccred->uid = pinfo->KI_UID; - proccred->gid = pinfo->KI_GID; - proccred->euid = pinfo->KI_EUID; - proccred->egid = pinfo->KI_EGID; -#endif - - return SIGAR_OK; -} - -#define tv2msec(tv) \ - (((sigar_uint64_t)tv.tv_sec * SIGAR_MSEC) + (((sigar_uint64_t)tv.tv_usec) / 1000)) - -#ifdef DARWIN -#define tval2msec(tval) \ - ((tval.seconds * SIGAR_MSEC) + (tval.microseconds / 1000)) - -#define tval2nsec(tval) \ - (SIGAR_SEC2NANO((tval).seconds) + SIGAR_MICROSEC2NANO((tval).microseconds)) - -static int get_proc_times(sigar_t *sigar, sigar_pid_t pid, sigar_proc_time_t *time) -{ - unsigned int count; - time_value_t utime = {0, 0}, stime = {0, 0}; - task_basic_info_data_t ti; - task_thread_times_info_data_t tti; - task_port_t task, self; - kern_return_t status; -# ifdef DARWIN_HAS_LIBPROC_H - if (sigar->libproc) { - struct proc_taskinfo pti; - int sz = - sigar->proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)); - - if (sz == sizeof(pti)) { - time->user = SIGAR_NSEC2MSEC(pti.pti_total_user); - time->sys = SIGAR_NSEC2MSEC(pti.pti_total_system); - time->total = time->user + time->sys; - return SIGAR_OK; - } - } -# endif - - self = mach_task_self(); - status = task_for_pid(self, pid, &task); - if (status != KERN_SUCCESS) { - return errno; - } - - count = TASK_BASIC_INFO_COUNT; - status = task_info(task, TASK_BASIC_INFO, - (task_info_t)&ti, &count); - if (status != KERN_SUCCESS) { - if (task != self) { - mach_port_deallocate(self, task); - } - return errno; - } - - count = TASK_THREAD_TIMES_INFO_COUNT; - status = task_info(task, TASK_THREAD_TIMES_INFO, - (task_info_t)&tti, &count); - if (status != KERN_SUCCESS) { - if (task != self) { - mach_port_deallocate(self, task); - } - return errno; - } - - time_value_add(&utime, &ti.user_time); - time_value_add(&stime, &ti.system_time); - time_value_add(&utime, &tti.user_time); - time_value_add(&stime, &tti.system_time); - - time->user = tval2msec(utime); - time->sys = tval2msec(stime); - time->total = time->user + time->sys; - - return SIGAR_OK; -} -#endif - -int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime) -{ -#ifdef SIGAR_FREEBSD4 - struct user user; -#endif - int status = sigar_get_pinfo(sigar, pid); - bsd_pinfo_t *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - -#if defined(DARWIN) - if ((status = get_proc_times(sigar, pid, proctime)) != SIGAR_OK) { - return status; - } - proctime->start_time = tv2msec(pinfo->KI_START); -#elif defined(SIGAR_FREEBSD5) - proctime->user = tv2msec(pinfo->ki_rusage.ru_utime); - proctime->sys = tv2msec(pinfo->ki_rusage.ru_stime); - proctime->total = proctime->user + proctime->sys; - proctime->start_time = tv2msec(pinfo->KI_START); -#elif defined(SIGAR_FREEBSD4) - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - status = kread(sigar, &user, sizeof(user), - (u_long)pinfo->kp_proc.p_addr); - if (status != SIGAR_OK) { - return status; - } - - proctime->user = tv2msec(user.u_stats.p_ru.ru_utime); - proctime->sys = tv2msec(user.u_stats.p_ru.ru_stime); - proctime->total = proctime->user + proctime->sys; - proctime->start_time = tv2msec(user.u_stats.p_start); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - /* XXX *_usec */ - proctime->user = pinfo->p_uutime_sec * SIGAR_MSEC; - proctime->sys = pinfo->p_ustime_sec * SIGAR_MSEC; - proctime->total = proctime->user + proctime->sys; - proctime->start_time = pinfo->p_ustart_sec * SIGAR_MSEC; -#endif - - return SIGAR_OK; -} - -#ifdef DARWIN -/* thread state mapping derived from ps.tproj */ -static const char const thread_states[] = { - /*0*/ '-', - /*1*/ SIGAR_PROC_STATE_RUN, - /*2*/ SIGAR_PROC_STATE_ZOMBIE, - /*3*/ SIGAR_PROC_STATE_SLEEP, - /*4*/ SIGAR_PROC_STATE_IDLE, - /*5*/ SIGAR_PROC_STATE_STOP, - /*6*/ SIGAR_PROC_STATE_STOP, - /*7*/ '?' -}; - -static int thread_state_get(thread_basic_info_data_t *info) -{ - switch (info->run_state) { - case TH_STATE_RUNNING: - return 1; - case TH_STATE_UNINTERRUPTIBLE: - return 2; - case TH_STATE_WAITING: - return (info->sleep_time > 20) ? 4 : 3; - case TH_STATE_STOPPED: - return 5; - case TH_STATE_HALTED: - return 6; - default: - return 7; - } -} - -static int sigar_proc_threads_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - mach_port_t task, self = mach_task_self(); - kern_return_t status; - thread_array_t threads; - mach_msg_type_number_t count, i; - int state = TH_STATE_HALTED + 1; - - status = task_for_pid(self, pid, &task); - if (status != KERN_SUCCESS) { - return errno; - } - - status = task_threads(task, &threads, &count); - if (status != KERN_SUCCESS) { - return errno; - } - - procstate->threads = count; - - for (i=0; istate = thread_states[state]; - - return SIGAR_OK; -} -#endif - -int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - int status = sigar_get_pinfo(sigar, pid); - bsd_pinfo_t *pinfo = sigar->pinfo; -#if defined(__OpenBSD__) || defined(__NetBSD__) - int state = pinfo->p_stat; -#else - int state = pinfo->KI_STAT; -#endif - - if (status != SIGAR_OK) { - return status; - } - -#if defined(__OpenBSD__) || defined(__NetBSD__) - SIGAR_SSTRCPY(procstate->name, pinfo->p_comm); - procstate->ppid = pinfo->p_ppid; - procstate->priority = pinfo->p_priority; - procstate->nice = pinfo->p_nice; - procstate->tty = pinfo->p_tdev; - procstate->threads = SIGAR_FIELD_NOTIMPL; - procstate->processor = pinfo->p_cpuid; -#else - SIGAR_SSTRCPY(procstate->name, pinfo->KI_COMM); - procstate->ppid = pinfo->KI_PPID; - procstate->priority = pinfo->KI_PRI; - procstate->nice = pinfo->KI_NICE; - procstate->tty = SIGAR_FIELD_NOTIMPL; /*XXX*/ - procstate->threads = SIGAR_FIELD_NOTIMPL; - procstate->processor = SIGAR_FIELD_NOTIMPL; -#endif - -#ifdef DARWIN - status = sigar_proc_threads_get(sigar, pid, procstate); - if (status == SIGAR_OK) { - return status; - } -#endif - - switch (state) { - case SIDL: - procstate->state = 'D'; - break; - case SRUN: -#ifdef SONPROC - case SONPROC: -#endif - procstate->state = 'R'; - break; - case SSLEEP: - procstate->state = 'S'; - break; - case SSTOP: - procstate->state = 'T'; - break; - case SZOMB: - procstate->state = 'Z'; - break; - default: - procstate->state = '?'; - break; - } - - return SIGAR_OK; -} - -#if defined(DARWIN) -typedef struct { - char *buf, *ptr, *end; - int count; -} sigar_kern_proc_args_t; - -static void sigar_kern_proc_args_destroy(sigar_kern_proc_args_t *kargs) -{ - if (kargs->buf) { - free(kargs->buf); - kargs->buf = NULL; - } -} - -/* re-usable hack for use by proc_args and proc_env */ -static int sigar_kern_proc_args_get(sigar_t *sigar, - sigar_pid_t pid, - char *exe, - sigar_kern_proc_args_t *kargs) -{ - /* - * derived from: - * http://darwinsource.opendarwin.org/10.4.1/adv_cmds-79.1/ps.tproj/print.c - */ - int mib[3], len; - size_t size = sigar_argmax_get(sigar); - - kargs->buf = malloc(size); - - mib[0] = CTL_KERN; - mib[1] = KERN_PROCARGS2; - mib[2] = pid; - - if (sysctl(mib, NMIB(mib), kargs->buf, &size, NULL, 0) < 0) { - sigar_kern_proc_args_destroy(kargs); - return errno; - } - - kargs->end = &kargs->buf[size]; - - memcpy(&kargs->count, kargs->buf, sizeof(kargs->count)); - kargs->ptr = kargs->buf + sizeof(kargs->count); - - len = strlen(kargs->ptr); - if (exe) { - memcpy(exe, kargs->ptr, len+1); - } - kargs->ptr += len+1; - - if (kargs->ptr == kargs->end) { - sigar_kern_proc_args_destroy(kargs); - return exe ? SIGAR_OK : ENOENT; - } - - for (; kargs->ptr < kargs->end; kargs->ptr++) { - if (*kargs->ptr != '\0') { - break; /* start of argv[0] */ - } - } - - if (kargs->ptr == kargs->end) { - sigar_kern_proc_args_destroy(kargs); - return exe ? SIGAR_OK : ENOENT; - } - - return SIGAR_OK; -} - -static int kern_proc_args_skip_argv(sigar_kern_proc_args_t *kargs) -{ - char *ptr = kargs->ptr; - char *end = kargs->end; - int count = kargs->count; - - /* skip over argv */ - while ((ptr < end) && (count-- > 0)) { - int alen = strlen(ptr)+1; - - ptr += alen; - } - - kargs->ptr = ptr; - kargs->end = end; - kargs->count = 0; - - if (ptr >= end) { - return ENOENT; - } - - return SIGAR_OK; -} -#endif - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ -#if defined(DARWIN) - int status, count; - sigar_kern_proc_args_t kargs; - char *ptr, *end; - - status = sigar_kern_proc_args_get(sigar, pid, NULL, &kargs); - if (status != SIGAR_OK) { - return status; - } - - count = kargs.count; - ptr = kargs.ptr; - end = kargs.end; - - while ((ptr < end) && (count-- > 0)) { - int slen = strlen(ptr); - int alen = slen+1; - char *arg; - - /* - * trim trailing whitespace. - * seen w/ postgresql, probably related - * to messing with argv[0] - */ - while (*(ptr + (slen-1)) == ' ') { - if (--slen <= 0) { - break; - } - } - - arg = malloc(slen+1); - - SIGAR_PROC_ARGS_GROW(procargs); - memcpy(arg, ptr, slen); - *(arg+slen) = '\0'; - - procargs->data[procargs->number++] = arg; - - ptr += alen; - } - - sigar_kern_proc_args_destroy(&kargs); - return SIGAR_OK; -#elif defined(__FreeBSD__) || defined(__NetBSD__) - char buffer[SIGAR_ARG_MAX+1], *ptr=buffer; - size_t len = sizeof(buffer); -# ifdef __NetBSD__ - int mib[] = { CTL_KERN, KERN_PROC_ARGS, 0, KERN_PROC_ARGV }; - mib[2] = pid; -# else - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, 0 }; - mib[3] = pid; -# endif - - if (sysctl(mib, NMIB(mib), buffer, &len, NULL, 0) < 0) { - return errno; - } - - if (len == 0) { - procargs->number = 0; - return SIGAR_OK; - } - - buffer[len] = '\0'; - - while (len > 0) { - int alen = strlen(ptr)+1; - char *arg = malloc(alen); - - SIGAR_PROC_ARGS_GROW(procargs); - memcpy(arg, ptr, alen); - - procargs->data[procargs->number++] = arg; - - len -= alen; - if (len > 0) { - ptr += alen; - } - } - - return SIGAR_OK; -#elif defined(__OpenBSD__) - char buffer[SIGAR_ARG_MAX+1], **ptr=(char **)buffer; - size_t len = sizeof(buffer); - int mib[] = { CTL_KERN, KERN_PROC_ARGS, 0, KERN_PROC_ARGV }; - mib[2] = pid; - - if (sysctl(mib, NMIB(mib), buffer, &len, NULL, 0) < 0) { - return errno; - } - - if (len == 0) { - procargs->number = 0; - return SIGAR_OK; - } - - for (; *ptr; ptr++) { - int alen = strlen(*ptr)+1; - char *arg = malloc(alen); - - SIGAR_PROC_ARGS_GROW(procargs); - memcpy(arg, *ptr, alen); - - procargs->data[procargs->number++] = arg; - } - - return SIGAR_OK; -#else - return SIGAR_ENOTIMPL; -#endif -} - -int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ -#ifdef DARWIN - int status, count; - sigar_kern_proc_args_t kargs; - char *ptr, *end; - - status = sigar_kern_proc_args_get(sigar, pid, NULL, &kargs); - if (status != SIGAR_OK) { - return status; - } - - status = kern_proc_args_skip_argv(&kargs); - if (status != SIGAR_OK) { - sigar_kern_proc_args_destroy(&kargs); - return status; - } - - count = kargs.count; - ptr = kargs.ptr; - end = kargs.end; - - /* into environ */ - while (ptr < end) { - char *val = strchr(ptr, '='); - int klen, vlen, status; - char key[256]; /* XXX is there a max key size? */ - - if (val == NULL) { - /* not key=val format */ - break; - } - - klen = val - ptr; - SIGAR_SSTRCPY(key, ptr); - key[klen] = '\0'; - ++val; - - vlen = strlen(val); - status = procenv->env_getter(procenv->data, - key, klen, val, vlen); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - - ptr += (klen + 1 + vlen + 1); - - if (*ptr == '\0') { - break; - } - } - - sigar_kern_proc_args_destroy(&kargs); - return SIGAR_OK; -#else - char **env; - struct kinfo_proc *pinfo; - int num; - - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - pinfo = kvm_getprocs(sigar->kmem, KERN_PROC_PID, pid, &num); - if (!pinfo || (num < 1)) { - return errno; - } - - if (!(env = kvm_getenvv(sigar->kmem, pinfo, 9086))) { - return errno; - } - - while (*env) { - char *ptr = *env++; - char *val = strchr(ptr, '='); - int klen, vlen, status; - char key[128]; /* XXX is there a max key size? */ - - if (val == NULL) { - /* not key=val format */ - procenv->env_getter(procenv->data, ptr, strlen(ptr), NULL, 0); - break; - } - - klen = val - ptr; - SIGAR_SSTRCPY(key, ptr); - key[klen] = '\0'; - ++val; - - vlen = strlen(val); - status = procenv->env_getter(procenv->data, - key, klen, val, vlen); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - - ptr += (klen + 1 + vlen + 1); - } - - return SIGAR_OK; -#endif -} - -int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd) -{ -#ifdef __FreeBSD__ - int status; - bsd_pinfo_t *pinfo; - struct filedesc filed; -#if 0 - struct file **ofiles; - int nfiles, i; - size_t size; -#endif - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - if ((status = sigar_get_pinfo(sigar, pid)) != SIGAR_OK) { - return status; - } - pinfo = sigar->pinfo; - - status = kread(sigar, &filed, sizeof(filed), (u_long)pinfo->KI_FD); - if (status != SIGAR_OK) { - return status; - } -#if 0 - nfiles = filed.fd_lastfile+1; - size = sizeof(*ofiles) * nfiles; - ofiles = malloc(size); - status = kread(sigar, ofiles, size, (u_long)filed.fd_ofiles); - if (status != SIGAR_OK) { - free(ofiles); - return status; - } - - procfd->total = 0; - for (i=0; itotal++; - } - - free(ofiles); -#else - /* seems the same as the above */ - procfd->total = filed.fd_lastfile; -#endif - - return SIGAR_OK; -#else - return SIGAR_ENOTIMPL; -#endif -} - -int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ -#ifdef DARWIN - int status; - sigar_kern_proc_args_t kargs; - - status = sigar_kern_proc_args_get(sigar, pid, procexe->name, &kargs); - if (status != SIGAR_OK) { - return status; - } - - procexe->cwd[0] = '\0'; - procexe->root[0] = '\0'; - - /* attempt to determine cwd from $PWD */ - status = kern_proc_args_skip_argv(&kargs); - if (status == SIGAR_OK) { - char *ptr = kargs.ptr; - char *end = kargs.end; - - /* into environ */ - while (ptr < end) { - int len = strlen(ptr); - - if ((len > 4) && - (ptr[0] == 'P') && - (ptr[1] == 'W') && - (ptr[2] == 'D') && - (ptr[3] == '=')) - { - memcpy(procexe->cwd, ptr+4, len-3); - break; - } - - ptr += len+1; - } - } - - sigar_kern_proc_args_destroy(&kargs); - - return SIGAR_OK; -#else - int len; - char name[1024]; - - procexe->cwd[0] = '\0'; - procexe->root[0] = '\0'; - - (void)SIGAR_PROC_FILENAME(name, pid, "/file"); - - if ((len = readlink(name, procexe->name, - sizeof(procexe->name)-1)) < 0) - { - return PROCFS_STATUS(errno); - } - - procexe->name[len] = '\0'; - - return SIGAR_OK; -#endif -} - -#ifdef DARWIN -static int sigar_dlinfo_modules(sigar_t *sigar, sigar_proc_modules_t *procmods) -{ - uint32_t i, count = _dyld_image_count(); - - for (i=0; imodule_getter(procmods->data, - (char *)name, strlen(name)); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - } - return SIGAR_OK; -} -#endif /* DARWIN */ - -int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ -#if defined(SIGAR_HAS_DLINFO_MODULES) || defined(DARWIN) - if (pid == sigar_pid_get(sigar)) { - return sigar_dlinfo_modules(sigar, procmods); - } -#endif - return SIGAR_ENOTIMPL; -} - -#define SIGAR_MICROSEC2NANO(s) \ - ((sigar_uint64_t)(s) * (sigar_uint64_t)1000) - -#define TIME_NSEC(t) \ - (SIGAR_SEC2NANO((t).tv_sec) + SIGAR_MICROSEC2NANO((t).tv_usec)) - -int sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu) -{ -#if defined(DARWIN) - mach_port_t self = mach_thread_self(); - thread_basic_info_data_t info; - mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; - kern_return_t status; - - status = thread_info(self, THREAD_BASIC_INFO, - (thread_info_t)&info, &count); - if (status != KERN_SUCCESS) { - return errno; - } - - mach_port_deallocate(mach_task_self(), self); - - cpu->user = tval2nsec(info.user_time); - cpu->sys = tval2nsec(info.system_time); - cpu->total = cpu->user + cpu->sys; -#elif defined(__NetBSD__) - return SIGAR_ENOTIMPL; /* http://tinyurl.com/chbvln */ -#else - /* XXX this is not per-thread, it is for the whole-process. - * just want to use for the shell time command at the moment. - */ - struct rusage usage; - getrusage(RUSAGE_SELF, &usage); - - cpu->user = TIME_NSEC(usage.ru_utime); - cpu->sys = TIME_NSEC(usage.ru_stime); - cpu->total = TIME_NSEC(usage.ru_utime) + TIME_NSEC(usage.ru_stime); -#endif - - return SIGAR_OK; -} - -int sigar_os_fs_type_get(sigar_file_system_t *fsp) -{ - char *type = fsp->sys_type_name; - - /* see sys/disklabel.h */ - switch (*type) { - case 'f': - if (strEQ(type, "ffs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'h': - if (strEQ(type, "hfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'u': - if (strEQ(type, "ufs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - } - - return fsp->type; -} - -static void get_fs_options(char *opts, int osize, long flags) -{ - *opts = '\0'; - if (flags & MNT_RDONLY) strncat(opts, "ro", osize); - else strncat(opts, "rw", osize); - if (flags & MNT_SYNCHRONOUS) strncat(opts, ",sync", osize); - if (flags & MNT_NOEXEC) strncat(opts, ",noexec", osize); - if (flags & MNT_NOSUID) strncat(opts, ",nosuid", osize); -#ifdef MNT_NODEV - if (flags & MNT_NODEV) strncat(opts, ",nodev", osize); -#endif -#ifdef MNT_UNION - if (flags & MNT_UNION) strncat(opts, ",union", osize); -#endif - if (flags & MNT_ASYNC) strncat(opts, ",async", osize); -#ifdef MNT_NOATIME - if (flags & MNT_NOATIME) strncat(opts, ",noatime", osize); -#endif -#ifdef MNT_NOCLUSTERR - if (flags & MNT_NOCLUSTERR) strncat(opts, ",noclusterr", osize); -#endif -#ifdef MNT_NOCLUSTERW - if (flags & MNT_NOCLUSTERW) strncat(opts, ",noclusterw", osize); -#endif -#ifdef MNT_NOSYMFOLLOW - if (flags & MNT_NOSYMFOLLOW) strncat(opts, ",nosymfollow", osize); -#endif -#ifdef MNT_SUIDDIR - if (flags & MNT_SUIDDIR) strncat(opts, ",suiddir", osize); -#endif -#ifdef MNT_SOFTDEP - if (flags & MNT_SOFTDEP) strncat(opts, ",soft-updates", osize); -#endif - if (flags & MNT_LOCAL) strncat(opts, ",local", osize); - if (flags & MNT_QUOTA) strncat(opts, ",quota", osize); - if (flags & MNT_ROOTFS) strncat(opts, ",rootfs", osize); -#ifdef MNT_USER - if (flags & MNT_USER) strncat(opts, ",user", osize); -#endif -#ifdef MNT_IGNORE - if (flags & MNT_IGNORE) strncat(opts, ",ignore", osize); -#endif - if (flags & MNT_EXPORTED) strncat(opts, ",nfs", osize); -} - -#ifdef __NetBSD__ -#define sigar_statfs statvfs -#define sigar_getfsstat getvfsstat -#define sigar_f_flags f_flag -#else -#define sigar_statfs statfs -#define sigar_getfsstat getfsstat -#define sigar_f_flags f_flags -#endif - -int sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - struct sigar_statfs *fs; - int num, i; - int is_debug = SIGAR_LOG_IS_DEBUG(sigar); - long len; - - if ((num = sigar_getfsstat(NULL, 0, MNT_NOWAIT)) < 0) { - return errno; - } - - len = sizeof(*fs) * num; - fs = malloc(len); - - if ((num = sigar_getfsstat(fs, len, MNT_NOWAIT)) < 0) { - free(fs); - return errno; - } - - sigar_file_system_list_create(fslist); - - for (i=0; idata[fslist->number++]; - - SIGAR_SSTRCPY(fsp->dir_name, fs[i].f_mntonname); - SIGAR_SSTRCPY(fsp->dev_name, fs[i].f_mntfromname); - SIGAR_SSTRCPY(fsp->sys_type_name, fs[i].f_fstypename); - get_fs_options(fsp->options, sizeof(fsp->options)-1, fs[i].sigar_f_flags); - - sigar_fs_type_init(fsp); - } - - free(fs); - return SIGAR_OK; -} - -#ifdef DARWIN -#define IoStatGetValue(key, val) \ - if ((number = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatistics##key)))) \ - CFNumberGetValue(number, kCFNumberSInt64Type, &val) -#endif - -int sigar_disk_usage_get(sigar_t *sigar, const char *name, - sigar_disk_usage_t *disk) -{ -#if defined(DARWIN) - kern_return_t status; - io_registry_entry_t parent; - io_service_t service; - CFDictionaryRef props; - CFNumberRef number; - sigar_iodev_t *iodev = sigar_iodev_get(sigar, name); - char dname[256], *ptr; - - SIGAR_DISK_STATS_INIT(disk); - - if (!iodev) { - return ENXIO; - } - - /* "/dev/disk0s1" -> "disk0" */ /* XXX better way? */ - ptr = &iodev->name[SSTRLEN(SIGAR_DEV_PREFIX)]; - SIGAR_SSTRCPY(dname, ptr); - ptr = dname; - if (strnEQ(ptr, "disk", 4)) { - ptr += 4; - if ((ptr = strchr(ptr, 's')) && isdigit(*(ptr+1))) { - *ptr = '\0'; - } - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[disk_usage] map %s -> %s", - iodev->name, dname); - } - - /* e.g. name == "disk0" */ - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOBSDNameMatching(kIOMasterPortDefault, 0, dname)); - - if (!service) { - return errno; - } - - status = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); - if (status != KERN_SUCCESS) { - IOObjectRelease(service); - return status; - } - - status = IORegistryEntryCreateCFProperties(parent, - (CFMutableDictionaryRef *)&props, - kCFAllocatorDefault, - kNilOptions); - if (props) { - CFDictionaryRef stats = - (CFDictionaryRef)CFDictionaryGetValue(props, - CFSTR(kIOBlockStorageDriverStatisticsKey)); - - if (stats) { - IoStatGetValue(ReadsKey, disk->reads); - IoStatGetValue(BytesReadKey, disk->read_bytes); - IoStatGetValue(TotalReadTimeKey, disk->rtime); - IoStatGetValue(WritesKey, disk->writes); - IoStatGetValue(BytesWrittenKey, disk->write_bytes); - IoStatGetValue(TotalWriteTimeKey, disk->wtime); - disk->time = disk->rtime + disk->wtime; - } - - CFRelease(props); - } - - IOObjectRelease(service); - IOObjectRelease(parent); - - return SIGAR_OK; -#elif defined(__FreeBSD__) - /* XXX incomplete */ - struct sigar_statfs buf; - - if (sigar_statfs(name, &buf) < 0) { - return errno; - } - - SIGAR_DISK_STATS_INIT(disk); - - disk->reads = buf.f_syncreads + buf.f_asyncreads; - disk->writes = buf.f_syncwrites + buf.f_asyncwrites; - return SIGAR_OK; -#else - SIGAR_DISK_STATS_INIT(disk); - return SIGAR_ENOTIMPL; -#endif -} - -int sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - int status = sigar_statvfs(sigar, dirname, fsusage); - - if (status != SIGAR_OK) { - return status; - } - - fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); - - sigar_disk_usage_get(sigar, dirname, &fsusage->disk); - - return SIGAR_OK; -} - -#ifdef DARWIN -#define CTL_HW_FREQ_MAX "hw.cpufrequency_max" -#define CTL_HW_FREQ_MIN "hw.cpufrequency_min" -#else -/* XXX FreeBSD 5.x+ only? */ -#define CTL_HW_FREQ "machdep.tsc_freq" -#endif - -int sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - int i; - unsigned int mhz, mhz_min, mhz_max; - int cache_size=SIGAR_FIELD_NOTIMPL; - size_t size; - char model[128], vendor[128], *ptr; - - size = sizeof(mhz); - - (void)sigar_cpu_core_count(sigar); - -#if defined(DARWIN) - { - int mib[] = { CTL_HW, HW_CPU_FREQ }; - size = sizeof(mhz); - if (sysctl(mib, NMIB(mib), &mhz, &size, NULL, 0) < 0) { - mhz = SIGAR_FIELD_NOTIMPL; - } - } - if (sysctlbyname(CTL_HW_FREQ_MAX, &mhz_max, &size, NULL, 0) < 0) { - mhz_max = SIGAR_FIELD_NOTIMPL; - } - if (sysctlbyname(CTL_HW_FREQ_MIN, &mhz_min, &size, NULL, 0) < 0) { - mhz_min = SIGAR_FIELD_NOTIMPL; - } -#elif defined(__FreeBSD__) - if (sysctlbyname(CTL_HW_FREQ, &mhz, &size, NULL, 0) < 0) { - mhz = SIGAR_FIELD_NOTIMPL; - } - /* TODO */ - mhz_max = SIGAR_FIELD_NOTIMPL; - mhz_min = SIGAR_FIELD_NOTIMPL; -#else - /*XXX OpenBSD*/ - mhz = SIGAR_FIELD_NOTIMPL; - mhz_max = SIGAR_FIELD_NOTIMPL; - mhz_min = SIGAR_FIELD_NOTIMPL; -#endif - - if (mhz != SIGAR_FIELD_NOTIMPL) { - mhz /= 1000000; - } - if (mhz_max != SIGAR_FIELD_NOTIMPL) { - mhz_max /= 1000000; - } - if (mhz_min != SIGAR_FIELD_NOTIMPL) { - mhz_min /= 1000000; - } - - size = sizeof(model); -#ifdef __OpenBSD__ - if (1) { -#else - if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { -#endif - int mib[] = { CTL_HW, HW_MODEL }; - size = sizeof(model); - if (sysctl(mib, NMIB(mib), &model[0], &size, NULL, 0) < 0) { -#ifdef DARWIN - strcpy(model, "powerpc"); -#else - strcpy(model, "Unknown"); -#endif - } - } - - if (mhz == SIGAR_FIELD_NOTIMPL) { - /* freebsd4 */ - mhz = sigar_cpu_mhz_from_model(model); - } - /* XXX not sure */ - if (mhz_max == SIGAR_FIELD_NOTIMPL) { - mhz_max = 0; - } - if (mhz_min == SIGAR_FIELD_NOTIMPL) { - mhz_min = 0; - } - - -#ifdef DARWIN - size = sizeof(vendor); - if (sysctlbyname("machdep.cpu.vendor", &vendor, &size, NULL, 0) < 0) { - SIGAR_SSTRCPY(vendor, "Apple"); - } - else { - /* GenuineIntel -> Intel */ - if (strstr(vendor, "Intel")) { - SIGAR_SSTRCPY(vendor, "Intel"); - } - } -#endif - - if ((ptr = strchr(model, ' '))) { - if (strstr(model, "Intel")) { - SIGAR_SSTRCPY(vendor, "Intel"); - } - else if (strstr(model, "AMD")) { - SIGAR_SSTRCPY(vendor, "AMD"); - } - else { - SIGAR_SSTRCPY(vendor, "Unknown"); - } - SIGAR_SSTRCPY(model, ptr+1); - } - -#ifdef DARWIN - { - int mib[] = { CTL_HW, HW_L2CACHESIZE }; /* in bytes */ - size = sizeof(cache_size); - if (sysctl(mib, NMIB(mib), &cache_size, &size, NULL, 0) < 0) { - cache_size = SIGAR_FIELD_NOTIMPL; - } - else { - cache_size /= 1024; /* convert to KB */ - } - } -#endif - - sigar_cpu_info_list_create(cpu_infos); - - for (i=0; incpu; i++) { - sigar_cpu_info_t *info; - - SIGAR_CPU_INFO_LIST_GROW(cpu_infos); - - info = &cpu_infos->data[cpu_infos->number++]; - - SIGAR_SSTRCPY(info->vendor, vendor); - SIGAR_SSTRCPY(info->model, model); - sigar_cpu_model_adjust(sigar, info); - - info->mhz = mhz; - info->mhz_max = mhz_max; - info->mhz_min = mhz_min; - info->cache_size = cache_size; - info->total_cores = sigar->ncpu; - info->cores_per_socket = sigar->lcpu; - info->total_sockets = sigar_cpu_socket_count(sigar); - } - - return SIGAR_OK; -} - -#define rt_s_addr(sa) ((struct sockaddr_in *)(sa))->sin_addr.s_addr - -#ifndef SA_SIZE -#define SA_SIZE(sa) \ - ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ - sizeof(long) : \ - 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) -#endif - -int sigar_net_route_list_get(sigar_t *sigar, - sigar_net_route_list_t *routelist) -{ - size_t needed; - int bit; - char *buf, *next, *lim; - struct rt_msghdr *rtm; - int mib[6] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_DUMP, 0 }; - - if (sysctl(mib, NMIB(mib), NULL, &needed, NULL, 0) < 0) { - return errno; - } -#if __FreeBSD_version >= 800000 - if (needed == 0) { - return SIGAR_ENOTIMPL; /*XXX hoping this is an 8.0beta bug*/ - } -#endif - buf = malloc(needed); - - if (sysctl(mib, NMIB(mib), buf, &needed, NULL, 0) < 0) { - free(buf); - return errno; - } - - sigar_net_route_list_create(routelist); - - lim = buf + needed; - for (next = buf; next < lim; next += rtm->rtm_msglen) { - struct sockaddr *sa; - sigar_net_route_t *route; - rtm = (struct rt_msghdr *)next; - - if (rtm->rtm_type != RTM_GET) { - continue; - } - - sa = (struct sockaddr *)(rtm + 1); - - if (sa->sa_family != AF_INET) { - continue; - } - - SIGAR_NET_ROUTE_LIST_GROW(routelist); - route = &routelist->data[routelist->number++]; - SIGAR_ZERO(route); - - route->flags = rtm->rtm_flags; - if_indextoname(rtm->rtm_index, route->ifname); - - for (bit=RTA_DST; - bit && ((char *)sa < lim); - bit <<= 1) - { - if ((rtm->rtm_addrs & bit) == 0) { - continue; - } - switch (bit) { - case RTA_DST: - sigar_net_address_set(route->destination, - rt_s_addr(sa)); - break; - case RTA_GATEWAY: - if (sa->sa_family == AF_INET) { - sigar_net_address_set(route->gateway, - rt_s_addr(sa)); - } - break; - case RTA_NETMASK: - sigar_net_address_set(route->mask, - rt_s_addr(sa)); - break; - case RTA_IFA: - break; - } - - sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); - } - } - - free(buf); - - return SIGAR_OK; -} - -typedef enum { - IFMSG_ITER_LIST, - IFMSG_ITER_GET -} ifmsg_iter_e; - -typedef struct { - const char *name; - ifmsg_iter_e type; - union { - sigar_net_interface_list_t *iflist; - struct if_msghdr *ifm; - } data; -} ifmsg_iter_t; - -static int sigar_ifmsg_init(sigar_t *sigar) -{ - int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0 }; - size_t len; - - if (sysctl(mib, NMIB(mib), NULL, &len, NULL, 0) < 0) { - return errno; - } - - if (sigar->ifconf_len < len) { - sigar->ifconf_buf = realloc(sigar->ifconf_buf, len); - sigar->ifconf_len = len; - } - - if (sysctl(mib, NMIB(mib), sigar->ifconf_buf, &len, NULL, 0) < 0) { - return errno; - } - - return SIGAR_OK; -} - -/** - * @param name name of the interface - * @param name_len length of name (w/o \0) - */ -static int has_ifaddr(char *name, size_t name_len) -{ - int sock, status; - struct ifreq ifr; - - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - return errno; - } - strncpy(ifr.ifr_name, name, MIN(sizeof(ifr.ifr_name) - 1, name_len)); - ifr.ifr_name[MIN(sizeof(ifr.ifr_name) - 1, name_len)] = '\0'; - if (ioctl(sock, SIOCGIFADDR, &ifr) == 0) { - status = SIGAR_OK; - } - else { - status = errno; - } - - close(sock); - return status; -} - -static int sigar_ifmsg_iter(sigar_t *sigar, ifmsg_iter_t *iter) -{ - char *end = sigar->ifconf_buf + sigar->ifconf_len; - char *ptr = sigar->ifconf_buf; - - if (iter->type == IFMSG_ITER_LIST) { - sigar_net_interface_list_create(iter->data.iflist); - } - - while (ptr < end) { - char *name; - struct sockaddr_dl *sdl; - struct if_msghdr *ifm = (struct if_msghdr *)ptr; - - if (ifm->ifm_type != RTM_IFINFO) { - break; - } - - ptr += ifm->ifm_msglen; - - while (ptr < end) { - struct if_msghdr *next = (struct if_msghdr *)ptr; - - if (next->ifm_type != RTM_NEWADDR) { - break; - } - - ptr += next->ifm_msglen; - } - - sdl = (struct sockaddr_dl *)(ifm + 1); - if (sdl->sdl_family != AF_LINK) { - continue; - } - - switch (iter->type) { - case IFMSG_ITER_LIST: - if (sdl->sdl_type == IFT_OTHER) { - if (has_ifaddr(sdl->sdl_data, sdl->sdl_nlen) != SIGAR_OK) { - break; - } - } - else if (!((sdl->sdl_type == IFT_ETHER) || - (sdl->sdl_type == IFT_LOOP))) - { - break; /* XXX deal w/ other weirdo interfaces */ - } - - SIGAR_NET_IFLIST_GROW(iter->data.iflist); - - /* sdl_data doesn't include a trailing \0, it is only sdl_nlen long */ - name = malloc(sdl->sdl_nlen+1); - memcpy(name, sdl->sdl_data, sdl->sdl_nlen); - name[sdl->sdl_nlen] = '\0'; /* add the missing \0 */ - - iter->data.iflist->data[iter->data.iflist->number++] = name; - break; - - case IFMSG_ITER_GET: - if (strlen(iter->name) == sdl->sdl_nlen && 0 == memcmp(iter->name, sdl->sdl_data, sdl->sdl_nlen)) { - iter->data.ifm = ifm; - return SIGAR_OK; - } - } - } - - switch (iter->type) { - case IFMSG_ITER_LIST: - return SIGAR_OK; - - case IFMSG_ITER_GET: - default: - return ENXIO; - } -} - -int sigar_net_interface_list_get(sigar_t *sigar, - sigar_net_interface_list_t *iflist) -{ - int status; - ifmsg_iter_t iter; - - if ((status = sigar_ifmsg_init(sigar)) != SIGAR_OK) { - return status; - } - - iter.type = IFMSG_ITER_LIST; - iter.data.iflist = iflist; - - return sigar_ifmsg_iter(sigar, &iter); -} - -#include - -/* in6_prefixlen derived from freebsd/sbin/ifconfig/af_inet6.c */ -static int sigar_in6_prefixlen(struct sockaddr *netmask) -{ - struct in6_addr *addr = SIGAR_SIN6_ADDR(netmask); - u_char *name = (u_char *)addr; - int size = sizeof(*addr); - int byte, bit, plen = 0; - - for (byte = 0; byte < size; byte++, plen += 8) { - if (name[byte] != 0xff) { - break; - } - } - if (byte == size) { - return plen; - } - for (bit = 7; bit != 0; bit--, plen++) { - if (!(name[byte] & (1 << bit))) { - break; - } - } - for (; bit != 0; bit--) { - if (name[byte] & (1 << bit)) { - return 0; - } - } - byte++; - for (; byte < size; byte++) { - if (name[byte]) { - return 0; - } - } - return plen; -} - -int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - int status = SIGAR_ENOENT; - struct ifaddrs *addrs, *ifa; - - if (getifaddrs(&addrs) != 0) { - return errno; - } - - for (ifa=addrs; ifa; ifa=ifa->ifa_next) { - if (ifa->ifa_addr && - (ifa->ifa_addr->sa_family == AF_INET6) && - strEQ(ifa->ifa_name, name)) - { - status = SIGAR_OK; - break; - } - } - - if (status == SIGAR_OK) { - struct in6_addr *addr = SIGAR_SIN6_ADDR(ifa->ifa_addr); - - sigar_net_address6_set(ifconfig->address6, addr); - sigar_net_interface_scope6_set(ifconfig, addr); - ifconfig->prefix6_length = sigar_in6_prefixlen(ifa->ifa_netmask); - } - - freeifaddrs(addrs); - - return status; -} - -int sigar_net_interface_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - int sock; - int status; - ifmsg_iter_t iter; - struct if_msghdr *ifm; - struct sockaddr_dl *sdl; - struct ifreq ifr; - - if (!name) { - return sigar_net_interface_config_primary_get(sigar, ifconfig); - } - - if (sigar->ifconf_len == 0) { - if ((status = sigar_ifmsg_init(sigar)) != SIGAR_OK) { - return status; - } - } - - SIGAR_ZERO(ifconfig); - - iter.type = IFMSG_ITER_GET; - iter.name = name; - - if ((status = sigar_ifmsg_iter(sigar, &iter)) != SIGAR_OK) { - return status; - } - - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - return errno; - } - - ifm = iter.data.ifm; - - SIGAR_SSTRCPY(ifconfig->name, name); - - sdl = (struct sockaddr_dl *)(ifm + 1); - - sigar_net_address_mac_set(ifconfig->hwaddr, - LLADDR(sdl), - sdl->sdl_alen); - - ifconfig->flags = ifm->ifm_flags; - ifconfig->mtu = ifm->ifm_data.ifi_mtu; - ifconfig->metric = ifm->ifm_data.ifi_metric; - - SIGAR_SSTRCPY(ifr.ifr_name, name); - -#define ifr_s_addr(ifr) \ - ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr - - if (!ioctl(sock, SIOCGIFADDR, &ifr)) { - sigar_net_address_set(ifconfig->address, - ifr_s_addr(ifr)); - } - - if (!ioctl(sock, SIOCGIFNETMASK, &ifr)) { - sigar_net_address_set(ifconfig->netmask, - ifr_s_addr(ifr)); - } - - if (ifconfig->flags & IFF_LOOPBACK) { - sigar_net_address_set(ifconfig->destination, - ifconfig->address.addr.in); - sigar_net_address_set(ifconfig->broadcast, 0); - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_LOOPBACK); - } - else { - if (!ioctl(sock, SIOCGIFDSTADDR, &ifr)) { - sigar_net_address_set(ifconfig->destination, - ifr_s_addr(ifr)); - } - - if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) { - sigar_net_address_set(ifconfig->broadcast, - ifr_s_addr(ifr)); - } - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_ETHERNET); - } - - close(sock); - - /* XXX can we get a better description like win32? */ - SIGAR_SSTRCPY(ifconfig->description, - ifconfig->name); - - sigar_net_interface_ipv6_config_init(ifconfig); - sigar_net_interface_ipv6_config_get(sigar, name, ifconfig); - - return SIGAR_OK; -} - -int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - int status; - ifmsg_iter_t iter; - struct if_msghdr *ifm; - - if ((status = sigar_ifmsg_init(sigar)) != SIGAR_OK) { - return status; - } - - iter.type = IFMSG_ITER_GET; - iter.name = name; - - if ((status = sigar_ifmsg_iter(sigar, &iter)) != SIGAR_OK) { - return status; - } - - ifm = iter.data.ifm; - - ifstat->rx_bytes = ifm->ifm_data.ifi_ibytes; - ifstat->rx_packets = ifm->ifm_data.ifi_ipackets; - ifstat->rx_errors = ifm->ifm_data.ifi_ierrors; - ifstat->rx_dropped = ifm->ifm_data.ifi_iqdrops; - ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->rx_frame = SIGAR_FIELD_NOTIMPL; - - ifstat->tx_bytes = ifm->ifm_data.ifi_obytes; - ifstat->tx_packets = ifm->ifm_data.ifi_opackets; - ifstat->tx_errors = ifm->ifm_data.ifi_oerrors; - ifstat->tx_collisions = ifm->ifm_data.ifi_collisions; - ifstat->tx_dropped = SIGAR_FIELD_NOTIMPL; - ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL; - - ifstat->speed = ifm->ifm_data.ifi_baudrate; - - return SIGAR_OK; -} - -static int net_connection_state_get(int state) -{ - switch (state) { - case TCPS_CLOSED: - return SIGAR_TCP_CLOSE; - case TCPS_LISTEN: - return SIGAR_TCP_LISTEN; - case TCPS_SYN_SENT: - return SIGAR_TCP_SYN_SENT; - case TCPS_SYN_RECEIVED: - return SIGAR_TCP_SYN_RECV; - case TCPS_ESTABLISHED: - return SIGAR_TCP_ESTABLISHED; - case TCPS_CLOSE_WAIT: - return SIGAR_TCP_CLOSE_WAIT; - case TCPS_FIN_WAIT_1: - return SIGAR_TCP_FIN_WAIT1; - case TCPS_CLOSING: - return SIGAR_TCP_CLOSING; - case TCPS_LAST_ACK: - return SIGAR_TCP_LAST_ACK; - case TCPS_FIN_WAIT_2: - return SIGAR_TCP_FIN_WAIT2; - case TCPS_TIME_WAIT: - return SIGAR_TCP_TIME_WAIT; - default: - return SIGAR_TCP_UNKNOWN; - } -} - -#if defined(__OpenBSD__) || defined(__NetBSD__) -static int net_connection_get(sigar_net_connection_walker_t *walker, int proto) -{ - int status; - int istcp = 0, type; - int flags = walker->flags; - struct inpcbtable table; - struct inpcb *head, *next, *prev; - sigar_t *sigar = walker->sigar; - u_long offset; - - switch (proto) { - case IPPROTO_TCP: - offset = sigar->koffsets[KOFFSET_TCBTABLE]; - istcp = 1; - type = SIGAR_NETCONN_TCP; - break; - case IPPROTO_UDP: - default: - return SIGAR_ENOTIMPL; - } - - - status = kread(sigar, &table, sizeof(table), offset); - - if (status != SIGAR_OK) { - return status; - } - - prev = head = - (struct inpcb *)&CIRCLEQ_FIRST(&((struct inpcbtable *)offset)->inpt_queue); - - next = (struct inpcb *)CIRCLEQ_FIRST(&table.inpt_queue); - - while (next != head) { - struct inpcb inpcb; - struct tcpcb tcpcb; - struct socket socket; - - status = kread(sigar, &inpcb, sizeof(inpcb), (long)next); - prev = next; - next = (struct inpcb *)CIRCLEQ_NEXT(&inpcb, inp_queue); - - kread(sigar, &socket, sizeof(socket), (u_long)inpcb.inp_socket); - - if ((((flags & SIGAR_NETCONN_SERVER) && socket.so_qlimit) || - ((flags & SIGAR_NETCONN_CLIENT) && !socket.so_qlimit))) - { - sigar_net_connection_t conn; - - SIGAR_ZERO(&conn); - - if (istcp) { - kread(sigar, &tcpcb, sizeof(tcpcb), (u_long)inpcb.inp_ppcb); - } - -#ifdef __NetBSD__ - if (inpcb.inp_af == AF_INET6) { - /*XXX*/ - continue; - } -#else - if (inpcb.inp_flags & INP_IPV6) { - sigar_net_address6_set(conn.local_address, - &inpcb.inp_laddr6.s6_addr); - - sigar_net_address6_set(conn.remote_address, - &inpcb.inp_faddr6.s6_addr); - } -#endif - else { - sigar_net_address_set(conn.local_address, - inpcb.inp_laddr.s_addr); - - sigar_net_address_set(conn.remote_address, - inpcb.inp_faddr.s_addr); - } - - conn.local_port = ntohs(inpcb.inp_lport); - conn.remote_port = ntohs(inpcb.inp_fport); - conn.receive_queue = socket.so_rcv.sb_cc; - conn.send_queue = socket.so_snd.sb_cc; - conn.uid = socket.so_pgid; - conn.type = type; - - if (!istcp) { - conn.state = SIGAR_TCP_UNKNOWN; - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - continue; - } - - conn.state = net_connection_state_get(tcpcb.t_state); - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - } - - return SIGAR_OK; -} -#else -static int net_connection_get(sigar_net_connection_walker_t *walker, int proto) -{ - int flags = walker->flags; - int type, istcp = 0; - char *buf; - const char *mibvar; - struct tcpcb *tp = NULL; - struct inpcb *inp; - struct xinpgen *xig, *oxig; - struct xsocket *so; - size_t len; - - switch (proto) { - case IPPROTO_TCP: - mibvar = "net.inet.tcp.pcblist"; - istcp = 1; - type = SIGAR_NETCONN_TCP; - break; - case IPPROTO_UDP: - mibvar = "net.inet.udp.pcblist"; - type = SIGAR_NETCONN_UDP; - break; - default: - mibvar = "net.inet.raw.pcblist"; - type = SIGAR_NETCONN_RAW; - break; - } - - len = 0; - if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { - return errno; - } - if ((buf = malloc(len)) == 0) { - return errno; - } - if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { - free(buf); - return errno; - } - - oxig = xig = (struct xinpgen *)buf; - for (xig = (struct xinpgen *)((char *)xig + xig->xig_len); - xig->xig_len > sizeof(struct xinpgen); - xig = (struct xinpgen *)((char *)xig + xig->xig_len)) - { - if (istcp) { - struct xtcpcb *cb = (struct xtcpcb *)xig; - tp = &cb->xt_tp; - inp = &cb->xt_inp; - so = &cb->xt_socket; - } - else { - struct xinpcb *cb = (struct xinpcb *)xig; - inp = &cb->xi_inp; - so = &cb->xi_socket; - } - - if (so->xso_protocol != proto) { - continue; - } - - if (inp->inp_gencnt > oxig->xig_gen) { - continue; - } - - if ((((flags & SIGAR_NETCONN_SERVER) && so->so_qlimit) || - ((flags & SIGAR_NETCONN_CLIENT) && !so->so_qlimit))) - { - sigar_net_connection_t conn; - - SIGAR_ZERO(&conn); - - if (inp->inp_vflag & INP_IPV6) { - sigar_net_address6_set(conn.local_address, - &inp->in6p_laddr.s6_addr); - - sigar_net_address6_set(conn.remote_address, - &inp->in6p_faddr.s6_addr); - } - else { - sigar_net_address_set(conn.local_address, - inp->inp_laddr.s_addr); - - sigar_net_address_set(conn.remote_address, - inp->inp_faddr.s_addr); - } - - conn.local_port = ntohs(inp->inp_lport); - conn.remote_port = ntohs(inp->inp_fport); - conn.receive_queue = so->so_rcv.sb_cc; - conn.send_queue = so->so_snd.sb_cc; - conn.uid = so->so_pgid; - conn.type = type; - - if (!istcp) { - conn.state = SIGAR_TCP_UNKNOWN; - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - continue; - } - - conn.state = net_connection_state_get(tp->t_state); - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - } - - free(buf); - - return SIGAR_OK; -} -#endif - -int sigar_net_connection_walk(sigar_net_connection_walker_t *walker) -{ - int flags = walker->flags; - int status; - - if (flags & SIGAR_NETCONN_TCP) { - status = net_connection_get(walker, IPPROTO_TCP); - if (status != SIGAR_OK) { - return status; - } - } - if (flags & SIGAR_NETCONN_UDP) { - status = net_connection_get(walker, IPPROTO_UDP); - if (status != SIGAR_OK) { - return status; - } - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp) -{ - struct tcpstat mib; -#if !defined(TCPCTL_STATS) && (defined(__OpenBSD__) || defined(__NetBSD__)) - int status = - kread(sigar, &mib, sizeof(mib), - sigar->koffsets[KOFFSET_TCPSTAT]); - if (status != SIGAR_OK) { - return status; - } -#else - int var[4] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS }; - size_t len = sizeof(mib); - - if (sysctl(var, NMIB(var), &mib, &len, NULL, 0) < 0) { - return errno; - } -#endif - - tcp->active_opens = mib.tcps_connattempt; - tcp->passive_opens = mib.tcps_accepts; - tcp->attempt_fails = mib.tcps_conndrops; - tcp->estab_resets = mib.tcps_drops; - if (sigar_tcp_curr_estab(sigar, tcp) != SIGAR_OK) { - tcp->curr_estab = -1; - } - tcp->in_segs = mib.tcps_rcvtotal; - tcp->out_segs = mib.tcps_sndtotal - mib.tcps_sndrexmitpack; - tcp->retrans_segs = mib.tcps_sndrexmitpack; - tcp->in_errs = - mib.tcps_rcvbadsum + - mib.tcps_rcvbadoff + - mib.tcps_rcvmemdrop + - mib.tcps_rcvshort; - tcp->out_rsts = -1; /* XXX mib.tcps_sndctrl - mib.tcps_closed; ? */ - - return SIGAR_OK; -} - -#ifndef SIGAR_FREEBSD5_NFSSTAT -static int get_nfsstats(struct nfsstats *stats) -{ - size_t len = sizeof(*stats); - int mib[] = { CTL_VFS, 2, NFS_NFSSTATS }; - - if (sysctl(mib, NMIB(mib), stats, &len, NULL, 0) < 0) { - return errno; - } - else { - return SIGAR_OK; - } -} -#endif - -#if defined(__OpenBSD__) -typedef uint64_t rpc_cnt_t; -#else -typedef int rpc_cnt_t; -#endif - -static void map_nfs_stats(sigar_nfs_v3_t *nfs, rpc_cnt_t *rpc) -{ - nfs->null = rpc[NFSPROC_NULL]; - nfs->getattr = rpc[NFSPROC_GETATTR]; - nfs->setattr = rpc[NFSPROC_SETATTR]; - nfs->lookup = rpc[NFSPROC_LOOKUP]; - nfs->access = rpc[NFSPROC_ACCESS]; - nfs->readlink = rpc[NFSPROC_READLINK]; - nfs->read = rpc[NFSPROC_READ]; - nfs->write = rpc[NFSPROC_WRITE]; - nfs->create = rpc[NFSPROC_CREATE]; - nfs->mkdir = rpc[NFSPROC_MKDIR]; - nfs->symlink = rpc[NFSPROC_SYMLINK]; - nfs->mknod = rpc[NFSPROC_MKNOD]; - nfs->remove = rpc[NFSPROC_REMOVE]; - nfs->rmdir = rpc[NFSPROC_RMDIR]; - nfs->rename = rpc[NFSPROC_RENAME]; - nfs->link = rpc[NFSPROC_LINK]; - nfs->readdir = rpc[NFSPROC_READDIR]; - nfs->readdirplus = rpc[NFSPROC_READDIRPLUS]; - nfs->fsstat = rpc[NFSPROC_FSSTAT]; - nfs->fsinfo = rpc[NFSPROC_FSINFO]; - nfs->pathconf = rpc[NFSPROC_PATHCONF]; - nfs->commit = rpc[NFSPROC_COMMIT]; -} - -int sigar_nfs_client_v2_get(sigar_t *sigar, - sigar_nfs_client_v2_t *nfs) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_nfs_server_v2_get(sigar_t *sigar, - sigar_nfs_server_v2_t *nfs) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_nfs_client_v3_get(sigar_t *sigar, - sigar_nfs_client_v3_t *nfs) -{ -#ifdef SIGAR_FREEBSD5_NFSSTAT - struct nfsstats stats; - size_t size = sizeof(stats); - - if (sysctlbyname("vfs.nfs.nfsstats", &stats, &size, NULL, 0) == -1) { - return errno; - } - - map_nfs_stats((sigar_nfs_v3_t *)nfs, &stats.rpccnt[0]); -#else - int status; - struct nfsstats stats; - - if ((status = get_nfsstats(&stats)) != SIGAR_OK) { - return status; - } - - map_nfs_stats((sigar_nfs_v3_t *)nfs, &stats.rpccnt[0]); -#endif - - return SIGAR_OK; -} - -int sigar_nfs_server_v3_get(sigar_t *sigar, - sigar_nfs_server_v3_t *nfs) -{ -#ifdef SIGAR_FREEBSD5_NFSSTAT - struct nfsrvstats stats; - size_t size = sizeof(stats); - - if (sysctlbyname("vfs.nfsrv.nfsrvstats", &stats, &size, NULL, 0) == -1) { - return errno; - } - - map_nfs_stats((sigar_nfs_v3_t *)nfs, &stats.srvrpccnt[0]); -#else - int status; - struct nfsstats stats; - - if ((status = get_nfsstats(&stats)) != SIGAR_OK) { - return status; - } - - map_nfs_stats((sigar_nfs_v3_t *)nfs, &stats.srvrpccnt[0]); -#endif - - return SIGAR_OK; -} - -static char *get_hw_type(int type) -{ - switch (type) { - case IFT_ETHER: - return "ether"; - case IFT_ISO88025: - return "tr"; - case IFT_FDDI: - return "fddi"; - case IFT_ATM: - return "atm"; - case IFT_L2VLAN: - return "vlan"; - case IFT_IEEE1394: - return "firewire"; -#ifdef IFT_BRIDGE - case IFT_BRIDGE: - return "bridge"; -#endif - default: - return "unknown"; - } -} - -int sigar_arp_list_get(sigar_t *sigar, - sigar_arp_list_t *arplist) -{ - size_t needed; - char *lim, *buf, *next; - struct rt_msghdr *rtm; - struct sockaddr_inarp *sin; - struct sockaddr_dl *sdl; - int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO }; - - if (sysctl(mib, NMIB(mib), NULL, &needed, NULL, 0) < 0) { - return errno; - } - - if (needed == 0) { /* empty cache */ - return 0; - } - - buf = malloc(needed); - - if (sysctl(mib, NMIB(mib), buf, &needed, NULL, 0) < 0) { - free(buf); - return errno; - } - - sigar_arp_list_create(arplist); - - lim = buf + needed; - for (next = buf; next < lim; next += rtm->rtm_msglen) { - sigar_arp_t *arp; - - SIGAR_ARP_LIST_GROW(arplist); - arp = &arplist->data[arplist->number++]; - - rtm = (struct rt_msghdr *)next; - sin = (struct sockaddr_inarp *)(rtm + 1); - sdl = (struct sockaddr_dl *)((char *)sin + SA_SIZE(sin)); - - sigar_net_address_set(arp->address, sin->sin_addr.s_addr); - sigar_net_address_mac_set(arp->hwaddr, LLADDR(sdl), sdl->sdl_alen); - if_indextoname(sdl->sdl_index, arp->ifname); - arp->flags = rtm->rtm_flags; - - SIGAR_SSTRCPY(arp->type, get_hw_type(sdl->sdl_type)); - } - - free(buf); - - return SIGAR_OK; -} - -#if defined(__FreeBSD__) && /*XXX*/ (__FreeBSD_version < 800000) - -#define _KERNEL -#include -#undef _KERNEL - -/* derived from - * /usr/ports/security/pidentd/work/pidentd-3.0.16/src/k_freebsd2.c - */ -int sigar_proc_port_get(sigar_t *sigar, int protocol, - unsigned long port, sigar_pid_t *pid) -{ - struct nlist nl[2]; - struct inpcbhead tcb; - struct socket *sockp = NULL; - struct kinfo_proc *pinfo; - struct inpcb *head, pcbp; - int i, nentries, status; - - if (protocol != SIGAR_NETCONN_TCP) { - return SIGAR_ENOTIMPL; - } - - if (!sigar->kmem) { - return SIGAR_EPERM_KMEM; - } - - nl[0].n_name = "_tcb"; /* XXX cache */ - nl[1].n_name = ""; - if (kvm_nlist(sigar->kmem, nl) < 0) { - return errno; - } - - status = kread(sigar, &tcb, sizeof(tcb), nl[0].n_value); - if (status != SIGAR_OK) { - return status; - } - - for (head = tcb.lh_first; head != NULL; - head = pcbp.inp_list.le_next) - { - status = kread(sigar, &pcbp, sizeof(pcbp), (long)head); - if (status != SIGAR_OK) { - return status; - } - if (!(pcbp.inp_vflag & INP_IPV4)) { - continue; - } - if (pcbp.inp_fport != 0) { - continue; - } - if (ntohs(pcbp.inp_lport) == port) { - sockp = pcbp.inp_socket; - break; - } - } - - if (!sockp) { - return ENOENT; - } - - pinfo = kvm_getprocs(sigar->kmem, KERN_PROC_PROC, 0, &nentries); - if (!pinfo) { - return errno; - } - - for (i=0; ilibproc) { - return SIGAR_ENOTIMPL; - } - - status = sigar_proc_list_get(sigar, &pids); - if (status != SIGAR_OK) { - return status; - } - - for (i=0; iifconf_buf; - - for (n=0; nproc_fdtype != PROX_FDTYPE_SOCKET) { - continue; - } - rsize = sigar->proc_pidfdinfo(pids.data[i], fdp->proc_fd, - PROC_PIDFDSOCKETINFO, &si, sizeof(si)); - if (rsize != sizeof(si)) { - continue; - } - if (si.psi.soi_kind != SOCKINFO_TCP) { - continue; - } - if (si.psi.soi_proto.pri_tcp.tcpsi_state != TSI_S_LISTEN) { - continue; - } - family = si.psi.soi_family; - if (!((family == AF_INET) || (family == AF_INET6))) { - continue; - } - lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); - if (lport == port) { - *pid = pids.data[i]; - found = 1; - break; - } - } - } - - sigar_proc_list_destroy(sigar, &pids); - - return found ? SIGAR_OK : ENOENT; -} - -#else - -int sigar_proc_port_get(sigar_t *sigar, int protocol, - unsigned long port, sigar_pid_t *pid) -{ - return SIGAR_ENOTIMPL; -} - -#endif - -int sigar_os_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sysinfo) -{ -#ifdef DARWIN - char *codename = NULL; - SInt32 version, version_major, version_minor, version_fix; - - SIGAR_SSTRCPY(sysinfo->name, "MacOSX"); - SIGAR_SSTRCPY(sysinfo->vendor_name, "Mac OS X"); - SIGAR_SSTRCPY(sysinfo->vendor, "Apple"); - - if (Gestalt(gestaltSystemVersion, &version) == noErr) { - if (version >= 0x00001040) { - Gestalt('sys1' /*gestaltSystemVersionMajor*/, &version_major); - Gestalt('sys2' /*gestaltSystemVersionMinor*/, &version_minor); - Gestalt('sys3' /*gestaltSystemVersionBugFix*/, &version_fix); - } - else { - version_fix = version & 0xf; - version >>= 4; - version_minor = version & 0xf; - version >>= 4; - version_major = version - (version >> 4) * 6; - } - } - else { - return SIGAR_ENOTIMPL; - } - - snprintf(sysinfo->vendor_version, - sizeof(sysinfo->vendor_version), - "%d.%d", - (int)version_major, (int)version_minor); - - snprintf(sysinfo->version, - sizeof(sysinfo->version), - "%s.%d", - sysinfo->vendor_version, (int)version_fix); - - if (version_major == 10) { - switch (version_minor) { - case 2: - codename = "Jaguar"; - break; - case 3: - codename = "Panther"; - break; - case 4: - codename = "Tiger"; - break; - case 5: - codename = "Leopard"; - break; - case 6: - codename = "Snow Leopard"; - break; - case 7: - codename = "Lion"; - break; - default: - codename = "Unknown"; - break; - } - } - else { - return SIGAR_ENOTIMPL; - } - - SIGAR_SSTRCPY(sysinfo->vendor_code_name, codename); - - snprintf(sysinfo->description, - sizeof(sysinfo->description), - "%s %s", - sysinfo->vendor_name, sysinfo->vendor_code_name); -#else - char *ptr; - -#if defined(__FreeBSD__) - SIGAR_SSTRCPY(sysinfo->name, "FreeBSD"); -#elif defined(__OpenBSD__) - SIGAR_SSTRCPY(sysinfo->name, "OpenBSD"); -#elif defined(__NetBSD__) - SIGAR_SSTRCPY(sysinfo->name, "NetBSD"); -#else - SIGAR_SSTRCPY(sysinfo->name, "Unknown"); -#endif - SIGAR_SSTRCPY(sysinfo->vendor_name, sysinfo->name); - SIGAR_SSTRCPY(sysinfo->vendor, sysinfo->name); - SIGAR_SSTRCPY(sysinfo->vendor_version, - sysinfo->version); - - if ((ptr = strstr(sysinfo->vendor_version, "-"))) { - /* STABLE, RELEASE, CURRENT */ - *ptr++ = '\0'; - SIGAR_SSTRCPY(sysinfo->vendor_code_name, ptr); - } - - snprintf(sysinfo->description, - sizeof(sysinfo->description), - "%s %s", - sysinfo->name, sysinfo->version); -#endif - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/os/darwin/sigar_os.h b/vendor/sigar/src/os/darwin/sigar_os.h deleted file mode 100644 index ee0010085..000000000 --- a/vendor/sigar/src/os/darwin/sigar_os.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2004-2006, 2008 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_OS_H -#define SIGAR_OS_H -#ifdef __APPLE__ -#define DARWIN -#endif - -#ifdef DARWIN -/// Added to allow this code to compile with gcc4.7 vs Apple's built in compiler. -#define __private_extern__ extern - - - - -#include -#include -#ifdef DARWIN_HAS_LIBPROC_H -#include -#include -typedef int (*proc_pidinfo_func_t)(int, int, uint64_t, void *, int); -typedef int (*proc_pidfdinfo_func_t)(int, int, int, void *, int); -#endif -#else -#include -#endif - -#ifdef __NetBSD__ -#include -#endif -#include - -enum { - KOFFSET_CPUINFO, - KOFFSET_VMMETER, -#if defined(__OpenBSD__) || defined(__NetBSD__) - KOFFSET_TCPSTAT, - KOFFSET_TCBTABLE, -#endif - KOFFSET_MAX -}; - -#if defined(__OpenBSD__) || defined(__NetBSD__) -typedef struct kinfo_proc2 bsd_pinfo_t; -#else -typedef struct kinfo_proc bsd_pinfo_t; -#endif - -struct sigar_t { - SIGAR_T_BASE; - int pagesize; - time_t last_getprocs; - sigar_pid_t last_pid; - bsd_pinfo_t *pinfo; - int lcpu; - size_t argmax; -#ifdef DARWIN - mach_port_t mach_port; -# ifdef DARWIN_HAS_LIBPROC_H - void *libproc; - proc_pidinfo_func_t proc_pidinfo; - proc_pidfdinfo_func_t proc_pidfdinfo; -# endif -#else - kvm_t *kmem; - /* offsets for seeking on kmem */ - unsigned long koffsets[KOFFSET_MAX]; - int proc_mounted; -#endif -}; - -#define SIGAR_EPERM_KMEM (SIGAR_OS_START_ERROR+EACCES) -#define SIGAR_EPROC_NOENT (SIGAR_OS_START_ERROR+2) - -#endif /* SIGAR_OS_H */ diff --git a/vendor/sigar/src/os/hpux/hpux_sigar.c b/vendor/sigar/src/os/hpux/hpux_sigar.c deleted file mode 100644 index f7a7adce7..000000000 --- a/vendor/sigar/src/os/hpux/hpux_sigar.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#include -#include -#ifndef __ia64__ -#include -#endif -#include -#include -#include - -#ifdef _PSTAT64 -typedef int64_t pstat_int_t; -#else -typedef int32_t pstat_int_t; -#endif - -int sigar_os_open(sigar_t **sigar) -{ - *sigar = malloc(sizeof(**sigar)); - - /* does not change while system is running */ - pstat_getstatic(&(*sigar)->pstatic, - sizeof((*sigar)->pstatic), - 1, 0); - - (*sigar)->ticks = sysconf(_SC_CLK_TCK); - - (*sigar)->last_pid = -1; - - (*sigar)->pinfo = NULL; - - (*sigar)->mib = -1; - - return SIGAR_OK; - -} - -int sigar_os_close(sigar_t *sigar) -{ - if (sigar->pinfo) { - free(sigar->pinfo); - } - if (sigar->mib >= 0) { - close_mib(sigar->mib); - } - free(sigar); - return SIGAR_OK; -} - -char *sigar_os_error_string(sigar_t *sigar, int err) -{ - return NULL; -} - -int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) -{ - struct pst_dynamic stats; - struct pst_vminfo vminfo; - sigar_uint64_t pagesize = sigar->pstatic.page_size; - sigar_uint64_t kern; - - mem->total = sigar->pstatic.physical_memory * pagesize; - - pstat_getdynamic(&stats, sizeof(stats), 1, 0); - - mem->free = stats.psd_free * pagesize; - mem->used = mem->total - mem->free; - - pstat_getvminfo(&vminfo, sizeof(vminfo), 1, 0); - - /* "kernel dynamic memory" */ - kern = vminfo.psv_kern_dynmem * pagesize; - mem->actual_free = mem->free + kern; - mem->actual_used = mem->used - kern; - - sigar_mem_calc_ram(sigar, mem); - - return SIGAR_OK; -} - -int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) -{ - struct pst_swapinfo swapinfo; - struct pst_vminfo vminfo; - int i=0; - - swap->total = swap->free = 0; - - while (pstat_getswap(&swapinfo, sizeof(swapinfo), 1, i++) > 0) { - swapinfo.pss_nfpgs *= 4; /* nfpgs is in 512 byte blocks */ - - if (swapinfo.pss_nblksenabled == 0) { - swapinfo.pss_nblksenabled = swapinfo.pss_nfpgs; - } - - swap->total += swapinfo.pss_nblksenabled; - swap->free += swapinfo.pss_nfpgs; - } - - swap->used = swap->total - swap->free; - - pstat_getvminfo(&vminfo, sizeof(vminfo), 1, 0); - - swap->page_in = vminfo.psv_spgin; - swap->page_out = vminfo.psv_spgout; - - return SIGAR_OK; -} - -static void get_cpu_metrics(sigar_t *sigar, - sigar_cpu_t *cpu, - pstat_int_t *cpu_time) -{ - cpu->user = SIGAR_TICK2MSEC(cpu_time[CP_USER]); - - cpu->sys = SIGAR_TICK2MSEC(cpu_time[CP_SYS] + - cpu_time[CP_SSYS]); - - cpu->nice = SIGAR_TICK2MSEC(cpu_time[CP_NICE]); - - cpu->idle = SIGAR_TICK2MSEC(cpu_time[CP_IDLE]); - - cpu->wait = SIGAR_TICK2MSEC(cpu_time[CP_SWAIT] + - cpu_time[CP_BLOCK]); - - cpu->irq = SIGAR_TICK2MSEC(cpu_time[CP_INTR]); - cpu->soft_irq = 0; /*N/A*/ - cpu->stolen = 0; /*N/A*/ - - cpu->total = - cpu->user + cpu->sys + cpu->nice + cpu->idle + cpu->wait + cpu->irq; -} - -int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - struct pst_dynamic stats; - - pstat_getdynamic(&stats, sizeof(stats), 1, 0); - sigar->ncpu = stats.psd_proc_cnt; - - get_cpu_metrics(sigar, cpu, stats.psd_cpu_time); - - return SIGAR_OK; -} - -int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) -{ - int i; - struct pst_dynamic stats; - - pstat_getdynamic(&stats, sizeof(stats), 1, 0); - sigar->ncpu = stats.psd_proc_cnt; - - sigar_cpu_list_create(cpulist); - - for (i=0; incpu; i++) { - sigar_cpu_t *cpu; - struct pst_processor proc; - - if (pstat_getprocessor(&proc, sizeof(proc), 1, i) < 0) { - continue; - } - - SIGAR_CPU_LIST_GROW(cpulist); - - cpu = &cpulist->data[cpulist->number++]; - - get_cpu_metrics(sigar, cpu, proc.psp_cpu_time); - } - - return SIGAR_OK; -} - -int sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime) -{ - uptime->uptime = time(NULL) - sigar->pstatic.boot_time; - - return SIGAR_OK; -} - -int sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg) -{ - struct pst_dynamic stats; - - pstat_getdynamic(&stats, sizeof(stats), 1, 0); - - loadavg->loadavg[0] = stats.psd_avg_1_min; - loadavg->loadavg[1] = stats.psd_avg_5_min; - loadavg->loadavg[2] = stats.psd_avg_15_min; - - return SIGAR_OK; -} - -#define PROC_ELTS 16 - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - int num, idx=0; - struct pst_status proctab[PROC_ELTS]; - - while ((num = pstat_getproc(proctab, sizeof(proctab[0]), - PROC_ELTS, idx)) > 0) - { - int i; - - for (i=0; idata[proclist->number++] = - proctab[i].pst_pid; - } - - idx = proctab[num-1].pst_idx + 1; - } - - if (proclist->number == 0) { - return errno; - } - - return SIGAR_OK; -} - -static int sigar_pstat_getproc(sigar_t *sigar, sigar_pid_t pid) -{ - int status, num; - time_t timenow = time(NULL); - - if (sigar->pinfo == NULL) { - sigar->pinfo = malloc(sizeof(*sigar->pinfo)); - } - - if (sigar->last_pid == pid) { - if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { - return SIGAR_OK; - } - } - - sigar->last_pid = pid; - sigar->last_getprocs = timenow; - - if (pstat_getproc(sigar->pinfo, - sizeof(*sigar->pinfo), - 0, pid) == -1) - { - return errno; - } - - return SIGAR_OK; -} - -int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem) -{ - int pagesize = sigar->pstatic.page_size; - int status = sigar_pstat_getproc(sigar, pid); - struct pst_status *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = - pinfo->pst_vtsize + /* text */ - pinfo->pst_vdsize + /* data */ - pinfo->pst_vssize + /* stack */ - pinfo->pst_vshmsize + /* shared memory */ - pinfo->pst_vmmsize + /* mem-mapped files */ - pinfo->pst_vusize + /* U-Area & K-Stack */ - pinfo->pst_viosize; /* I/O dev mapping */ - - procmem->size *= pagesize; - - procmem->resident = pinfo->pst_rssize * pagesize; - - procmem->share = pinfo->pst_vshmsize * pagesize; - - procmem->minor_faults = pinfo->pst_minorfaults; - procmem->major_faults = pinfo->pst_majorfaults; - procmem->page_faults = - procmem->minor_faults + - procmem->major_faults; - - return SIGAR_OK; -} - -int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred) -{ - int status = sigar_pstat_getproc(sigar, pid); - struct pst_status *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - proccred->uid = pinfo->pst_uid; - proccred->gid = pinfo->pst_gid; - proccred->euid = pinfo->pst_euid; - proccred->egid = pinfo->pst_egid; - - return SIGAR_OK; -} - -int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime) -{ - int status = sigar_pstat_getproc(sigar, pid); - struct pst_status *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - proctime->start_time = pinfo->pst_start; - proctime->start_time *= SIGAR_MSEC; - proctime->user = pinfo->pst_utime * SIGAR_MSEC; - proctime->sys = pinfo->pst_stime * SIGAR_MSEC; - proctime->total = proctime->user + proctime->sys; - - return SIGAR_OK; -} - -int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - int status = sigar_pstat_getproc(sigar, pid); - struct pst_status *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - - SIGAR_SSTRCPY(procstate->name, pinfo->pst_ucomm); - procstate->ppid = pinfo->pst_ppid; - procstate->tty = makedev(pinfo->pst_term.psd_major, - pinfo->pst_term.psd_minor); - procstate->priority = pinfo->pst_pri; - procstate->nice = pinfo->pst_nice; - procstate->threads = pinfo->pst_nlwps; - procstate->processor = pinfo->pst_procnum; - - /* cast to prevent compiler warning: */ - /* Case label too big for the type of the switch expression */ - switch ((int32_t)pinfo->pst_stat) { - case PS_SLEEP: - procstate->state = 'S'; - break; - case PS_RUN: - procstate->state = 'R'; - break; - case PS_STOP: - procstate->state = 'T'; - break; - case PS_ZOMBIE: - procstate->state = 'Z'; - break; - case PS_IDLE: - procstate->state = 'D'; - break; - } - - return SIGAR_OK; -} - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - char *args, *arg; -#ifdef PSTAT_GETCOMMANDLINE - char buf[1024]; /* kernel limit */ - -# ifdef pstat_getcommandline /* 11i v2 + */ - if (pstat_getcommandline(buf, sizeof(buf), sizeof(buf[0]), pid) == -1) { - return errno; - } -# else - union pstun pu; - - pu.pst_command = buf; - if (pstat(PSTAT_GETCOMMANDLINE, pu, sizeof(buf), sizeof(buf[0]), pid) == -1) { - return errno; - } -# endif /* pstat_getcommandline */ - - args = buf; -#else - struct pst_status status; - - if (pstat_getproc(&status, sizeof(status), 0, pid) == -1) { - return errno; - } - - args = status.pst_cmd; -#endif - - while (*args && (arg = sigar_getword(&args, ' '))) { - SIGAR_PROC_ARGS_GROW(procargs); - procargs->data[procargs->number++] = arg; - } - - return SIGAR_OK; -} - -int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd) -{ - struct pst_status status; - int idx=0, n; - struct pst_fileinfo2 psf[16]; - - procfd->total = 0; - - if (pstat_getproc(&status, sizeof(status), 0, pid) == -1) { - return errno; - } - - /* HPUX 11.31 removed the deprecated pstat_getfile call */ - while ((n = pstat_getfile2(psf, sizeof(psf[0]), - sizeof(psf)/sizeof(psf[0]), - idx, pid)) > 0) - { - procfd->total += n; - idx = psf[n-1].psf_fd + 1; - } - - if (n == -1) { - return errno; - } - - return SIGAR_OK; -} - -int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ -#ifdef __pst_fid /* 11.11+ */ - int rc; - struct pst_status status; - - if (pstat_getproc(&status, sizeof(status), 0, pid) == -1) { - return errno; - } - - rc = pstat_getpathname(procexe->cwd, - sizeof(procexe->cwd), - &status.pst_fid_cdir); - if (rc == -1) { - return errno; - } - - rc = pstat_getpathname(procexe->name, - sizeof(procexe->name), - &status.pst_fid_text); - if (rc == -1) { - return errno; - } - - rc = pstat_getpathname(procexe->root, - sizeof(procexe->root), - &status.pst_fid_rdir); - if (rc == -1) { - return errno; - } - - return SIGAR_OK; -#else - return SIGAR_ENOTIMPL; /* 11.00 */ -#endif -} - -int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ - return SIGAR_ENOTIMPL; -} - -#define TIME_NSEC(t) \ - (SIGAR_SEC2NANO((t).tv_sec) + (sigar_uint64_t)(t).tv_nsec) - -int sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu) -{ -#ifdef __ia64__ - /* XXX seems _lwp funcs were for solaris compat and dont exist - * on itanium. hp docs claim that have equiv functions, - * but wtf is it for _lwp_info? - */ - return SIGAR_ENOTIMPL; -#else - struct lwpinfo info; - - if (id != 0) { - return SIGAR_ENOTIMPL; - } - - _lwp_info(&info); - - cpu->user = TIME_NSEC(info.lwp_utime); - cpu->sys = TIME_NSEC(info.lwp_stime); - cpu->total = TIME_NSEC(info.lwp_utime) + TIME_NSEC(info.lwp_stime); - - return SIGAR_OK; -#endif -} - -#include - -int sigar_os_fs_type_get(sigar_file_system_t *fsp) -{ - char *type = fsp->sys_type_name; - - switch (*type) { - case 'h': - if (strEQ(type, "hfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'c': - if (strEQ(type, "cdfs")) { - fsp->type = SIGAR_FSTYPE_CDROM; - } - break; - } - - return fsp->type; -} - -int sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - struct mntent *ent; - - FILE *fp; - sigar_file_system_t *fsp; - - if (!(fp = setmntent(MNT_MNTTAB, "r"))) { - return errno; - } - - sigar_file_system_list_create(fslist); - - while ((ent = getmntent(fp))) { - if ((*(ent->mnt_type) == 's') && - strEQ(ent->mnt_type, "swap")) - { - /* - * in this case, devname == "...", for - * which statfs chokes on. so skip it. - * also notice hpux df command has no swap info. - */ - continue; - } - - SIGAR_FILE_SYSTEM_LIST_GROW(fslist); - - fsp = &fslist->data[fslist->number++]; - - SIGAR_SSTRCPY(fsp->dir_name, ent->mnt_dir); - SIGAR_SSTRCPY(fsp->dev_name, ent->mnt_fsname); - SIGAR_SSTRCPY(fsp->sys_type_name, ent->mnt_type); - SIGAR_SSTRCPY(fsp->options, ent->mnt_opts); - sigar_fs_type_init(fsp); - } - - endmntent(fp); - - return SIGAR_OK; -} - -static int create_fsdev_cache(sigar_t *sigar) -{ - sigar_file_system_list_t fslist; - int i; - int status = - sigar_file_system_list_get(sigar, &fslist); - - if (status != SIGAR_OK) { - return status; - } - - sigar->fsdev = sigar_cache_new(15); - - for (i=0; itype == SIGAR_FSTYPE_LOCAL_DISK) { - sigar_cache_entry_t *ent; - struct stat sb; - - if (stat(fsp->dir_name, &sb) < 0) { - continue; - } - - ent = sigar_cache_get(sigar->fsdev, SIGAR_FSDEV_ID(sb)); - ent->value = strdup(fsp->dev_name); - } - } - - return SIGAR_OK; -} - -int sigar_disk_usage_get(sigar_t *sigar, const char *name, - sigar_disk_usage_t *usage) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - struct stat sb; - int status = sigar_statvfs(sigar, dirname, fsusage); - - if (status != SIGAR_OK) { - return status; - } - - fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); - - SIGAR_DISK_STATS_INIT(&fsusage->disk); - - if (!sigar->fsdev) { - if (create_fsdev_cache(sigar) != SIGAR_OK) { - return SIGAR_OK; - } - } - - if (stat(dirname, &sb) == 0) { - sigar_cache_entry_t *ent; - struct pst_lvinfo lv; - struct stat devsb; - char *devname; - int retval; - - ent = sigar_cache_get(sigar->fsdev, SIGAR_FSDEV_ID(sb)); - if (ent->value == NULL) { - return SIGAR_OK; - } - - if (stat((char *)ent->value, &devsb) < 0) { - return SIGAR_OK; - } - - retval = pstat_getlv(&lv, sizeof(lv), 0, (int)devsb.st_rdev); - - if (retval == 1) { - fsusage->disk.reads = lv.psl_rxfer; - fsusage->disk.writes = lv.psl_wxfer; - fsusage->disk.read_bytes = lv.psl_rcount; - fsusage->disk.write_bytes = lv.psl_wcount; - fsusage->disk.queue = SIGAR_FIELD_NOTIMPL; - } - } - - return SIGAR_OK; -} - -int sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - int i; - struct pst_dynamic stats; - - pstat_getdynamic(&stats, sizeof(stats), 1, 0); - sigar->ncpu = stats.psd_proc_cnt; - - sigar_cpu_info_list_create(cpu_infos); - - for (i=0; incpu; i++) { - sigar_cpu_info_t *info; - struct pst_processor proc; - - if (pstat_getprocessor(&proc, sizeof(proc), 1, i) < 0) { - perror("pstat_getprocessor"); - continue; - } - - SIGAR_CPU_INFO_LIST_GROW(cpu_infos); - - info = &cpu_infos->data[cpu_infos->number++]; - - info->total_cores = sigar->ncpu; - info->cores_per_socket = 1; /*XXX*/ - info->total_sockets = sigar->ncpu; /*XXX*/ - -#ifdef __ia64__ - SIGAR_SSTRCPY(info->vendor, "Intel"); /*XXX*/ - SIGAR_SSTRCPY(info->model, "Itanium"); /*XXX*/ -#else - SIGAR_SSTRCPY(info->vendor, "HP"); /*XXX*/ - SIGAR_SSTRCPY(info->model, "PA RISC"); /*XXX*/ -#endif -#ifdef PSP_MAX_CACHE_LEVELS /* 11.31+; see SIGAR-196 */ - info->mhz = proc.psp_cpu_frequency / 1000000; -#else - info->mhz = sigar->ticks * proc.psp_iticksperclktick / 1000000; -#endif - info->cache_size = SIGAR_FIELD_NOTIMPL; /*XXX*/ - } - - return SIGAR_OK; -} - -static int sigar_get_mib_info(sigar_t *sigar, - struct nmparms *parms) -{ - if (sigar->mib < 0) { - if ((sigar->mib = open_mib("/dev/ip", O_RDONLY, 0, 0)) < 0) { - return errno; - } - } - return get_mib_info(sigar->mib, parms); -} - -/* wrapper around get_physical_stat() */ -static int sigar_get_physical_stat(sigar_t *sigar, int *count) -{ - int status; - unsigned int len; - struct nmparms parms; - - len = sizeof(*count); - parms.objid = ID_ifNumber; - parms.buffer = count; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - return status; - } - - len = sizeof(nmapi_phystat) * *count; - - if (sigar->ifconf_len < len) { - sigar->ifconf_buf = realloc(sigar->ifconf_buf, len); - sigar->ifconf_len = len; - } - - if (get_physical_stat(sigar->ifconf_buf, &len) < 0) { - return errno; - } - else { - return SIGAR_OK; - } -} - -#define SIGAR_IF_NAMESIZE 16 -/* hpux if_indextoname() does not work as advertised in 11.11 */ -static int sigar_if_indextoname(sigar_t *sigar, - char *name, - int index) -{ - int i, status, count; - nmapi_phystat *stat; - - if ((status = sigar_get_physical_stat(sigar, &count) != SIGAR_OK)) { - return status; - } - - for (i=0, stat = (nmapi_phystat *)sigar->ifconf_buf; - iif_entry.ifIndex == index) { - strncpy(name, stat->nm_device, SIGAR_IF_NAMESIZE); - return SIGAR_OK; - } - } - - return ENXIO; -} - -int sigar_net_route_list_get(sigar_t *sigar, - sigar_net_route_list_t *routelist) -{ - int status, count, i; - unsigned int len; - struct nmparms parms; - mib_ipRouteEnt *routes; - sigar_net_route_t *route; - - len = sizeof(count); - parms.objid = ID_ipRouteNumEnt; - parms.buffer = &count; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - return status; - } - - len = count * sizeof(*routes); - routes = malloc(len); - - parms.objid = ID_ipRouteTable; - parms.buffer = routes; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - free(routes); - return status; - } - - routelist->size = routelist->number = 0; - - sigar_net_route_list_create(routelist); - - for (i=0; idata[routelist->number++]; - SIGAR_ZERO(route); /* XXX: other fields */ - - sigar_net_address_set(route->destination, - ent->Dest); - - sigar_net_address_set(route->mask, - ent->Mask); - - sigar_net_address_set(route->gateway, - ent->NextHop); - - sigar_if_indextoname(sigar, route->ifname, ent->IfIndex); - - route->flags = SIGAR_RTF_UP; - if ((ent->Dest == 0) && - (ent->Mask == 0)) - { - route->flags |= SIGAR_RTF_GATEWAY; - } - } - - free(routes); - - return SIGAR_OK; -} - -static int get_mib_ifstat(sigar_t *sigar, - const char *name, - mib_ifEntry *mib) -{ - int i, status, count; - nmapi_phystat *stat; - - if ((status = sigar_get_physical_stat(sigar, &count) != SIGAR_OK)) { - return status; - } - - for (i=0, stat = (nmapi_phystat *)sigar->ifconf_buf; - inm_device, name)) { - memcpy(mib, &stat->if_entry, sizeof(*mib)); - return SIGAR_OK; - } - } - - return ENXIO; -} - -int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - int status; - mib_ifEntry mib; - - status = get_mib_ifstat(sigar, name, &mib); - - if (status != SIGAR_OK) { - return status; - } - - ifstat->rx_bytes = mib.ifInOctets; - ifstat->rx_packets = mib.ifInUcastPkts + mib.ifInNUcastPkts; - ifstat->rx_errors = mib.ifInErrors; - ifstat->rx_dropped = mib.ifInDiscards; - ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->rx_frame = SIGAR_FIELD_NOTIMPL; - - ifstat->tx_bytes = mib.ifOutOctets; - ifstat->tx_packets = mib.ifOutUcastPkts + mib.ifOutNUcastPkts; - ifstat->tx_errors = mib.ifOutErrors; - ifstat->tx_dropped = mib.ifOutDiscards; - ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->tx_collisions = SIGAR_FIELD_NOTIMPL; - ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL; - - ifstat->speed = mib.ifSpeed; - - return SIGAR_OK; -} - -int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - int sock; - struct if_laddrreq iflr; - - if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - return errno; - } - - SIGAR_SSTRCPY(iflr.iflr_name, name); - - if (ioctl(sock, SIOCGLIFADDR, &iflr) == 0) { - struct in6_addr *addr = SIGAR_SIN6_ADDR(&iflr.iflr_addr); - - sigar_net_address6_set(ifconfig->address6, addr); - sigar_net_interface_scope6_set(ifconfig, addr); - - if (ioctl(sock, SIOCGLIFNETMASK, &iflr) == 0) { - addr = SIGAR_SIN6_ADDR(&iflr.iflr_addr); - ifconfig->prefix6_length = 10; /*XXX*/ - } - } - - close(sock); - return SIGAR_OK; -} - -static int net_conn_get_udp_listen(sigar_net_connection_walker_t *walker) -{ - sigar_t *sigar = walker->sigar; - int flags = walker->flags; - int status, count, i; - unsigned int len; - mib_udpLsnEnt *entries; - struct nmparms parms; - - len = sizeof(count); - parms.objid = ID_udpLsnNumEnt; - parms.buffer = &count; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - return status; - } - - if (count <= 0) { - return ENOENT; - } - - len = count * sizeof(*entries); - entries = malloc(len); - parms.objid = ID_udpLsnTable; - parms.buffer = entries; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - free(entries); - return status; - } - - for (i=0; iLocalPort; - conn.remote_port = 0; - - sigar_net_address_set(conn.local_address, - entry->LocalAddress); - - sigar_net_address_set(conn.remote_address, 0); - - conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL; - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - - free(entries); - return SIGAR_OK; -} - -static int net_conn_get_udp(sigar_net_connection_walker_t *walker) -{ - int status = SIGAR_OK; - - if (walker->flags & SIGAR_NETCONN_SERVER) { - status = net_conn_get_udp_listen(walker); - } - - return status; -} - -#define IS_TCP_SERVER(state, flags) \ - ((flags & SIGAR_NETCONN_SERVER) && (state == TCLISTEN)) - -#define IS_TCP_CLIENT(state, flags) \ - ((flags & SIGAR_NETCONN_CLIENT) && (state != TCLISTEN)) - -static int net_conn_get_tcp(sigar_net_connection_walker_t *walker) -{ - sigar_t *sigar = walker->sigar; - int flags = walker->flags; - int status, count, i; - unsigned int len; - mib_tcpConnEnt *entries; - struct nmparms parms; - - len = sizeof(count); - parms.objid = ID_tcpConnNumEnt; - parms.buffer = &count; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - return status; - } - - if (count <= 0) { - return ENOENT; - } - - len = count * sizeof(*entries); - entries = malloc(len); - parms.objid = ID_tcpConnTable; - parms.buffer = entries; - parms.len = &len; - - if ((status = sigar_get_mib_info(sigar, &parms)) != SIGAR_OK) { - free(entries); - return status; - } - - for (i=0; iState; - - if (!(IS_TCP_SERVER(state, flags) || - IS_TCP_CLIENT(state, flags))) - { - continue; - } - - SIGAR_ZERO(&conn); - - switch (state) { - case TCCLOSED: - conn.state = SIGAR_TCP_CLOSE; - break; - case TCLISTEN: - conn.state = SIGAR_TCP_LISTEN; - break; - case TCSYNSENT: - conn.state = SIGAR_TCP_SYN_SENT; - break; - case TCSYNRECEIVE: - conn.state = SIGAR_TCP_SYN_RECV; - break; - case TCESTABLISED: - conn.state = SIGAR_TCP_ESTABLISHED; - break; - case TCFINWAIT1: - conn.state = SIGAR_TCP_FIN_WAIT1; - break; - case TCFINWAIT2: - conn.state = SIGAR_TCP_FIN_WAIT2; - break; - case TCCLOSEWAIT: - conn.state = SIGAR_TCP_CLOSE_WAIT; - break; - case TCCLOSING: - conn.state = SIGAR_TCP_CLOSING; - break; - case TCLASTACK: - conn.state = SIGAR_TCP_LAST_ACK; - break; - case TCTIMEWAIT: - conn.state = SIGAR_TCP_TIME_WAIT; - break; - case TCDELETETCB: - default: - conn.state = SIGAR_TCP_UNKNOWN; - break; - } - - conn.local_port = (unsigned short)entry->LocalPort; - conn.remote_port = (unsigned short)entry->RemPort; - conn.type = SIGAR_NETCONN_TCP; - - sigar_net_address_set(conn.local_address, entry->LocalAddress); - sigar_net_address_set(conn.remote_address, entry->RemAddress); - - conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL; - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - - free(entries); - - return SIGAR_OK; -} - -int sigar_net_connection_walk(sigar_net_connection_walker_t *walker) -{ - int status; - - if (walker->flags & SIGAR_NETCONN_TCP) { - status = net_conn_get_tcp(walker); - - if (status != SIGAR_OK) { - return status; - } - } - - if (walker->flags & SIGAR_NETCONN_UDP) { - status = net_conn_get_udp(walker); - - if (status != SIGAR_OK) { - return status; - } - } - - return SIGAR_OK; -} - -#define tcpsoff(x) sigar_offsetof(sigar_tcp_t, x) - -static struct { - unsigned int id; - size_t offset; -} tcps_lu[] = { -#if 0 - { ID_tcpRtoAlgorithm, tcpsoff(xxx) }, - { ID_tcpRtoMin, tcpsoff(xxx) }, - { ID_tcpRtoMax, tcpsoff(xxx) }, - { ID_tcpMaxConn, tcpsoff(max_conn) }, -#endif - { ID_tcpActiveOpens, tcpsoff(active_opens) }, - { ID_tcpPassiveOpens, tcpsoff(passive_opens) }, - { ID_tcpAttemptFails, tcpsoff(attempt_fails) }, - { ID_tcpEstabResets, tcpsoff(estab_resets) }, - { ID_tcpCurrEstab, tcpsoff(curr_estab) }, - { ID_tcpInSegs, tcpsoff(in_segs) }, - { ID_tcpOutSegs, tcpsoff(out_segs) }, - { ID_tcpRetransSegs, tcpsoff(retrans_segs) }, - { ID_tcpInErrs, tcpsoff(in_errs) }, - { ID_tcpOutRsts, tcpsoff(out_rsts) } -}; - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp) -{ - int i; - - for (i=0; idata[arplist->number++]; - - sigar_net_address_set(arp->address, - ent->NetAddr); - - sigar_net_address_mac_set(arp->hwaddr, - ent->PhysAddr.o_bytes, - ent->PhysAddr.o_length); - - sigar_if_indextoname(sigar, arp->ifname, ent->IfIndex); - - SIGAR_SSTRCPY(arp->type, "ether"); /*XXX*/ - arp->flags = 0; /*XXX*/ - } - - free(entries); - - return SIGAR_OK; -} - -int sigar_proc_port_get(sigar_t *sigar, int protocol, - unsigned long port, sigar_pid_t *pid) -{ - return SIGAR_ENOTIMPL; -} - - -int sigar_os_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sysinfo) -{ - char *vendor_version, *arch; - long cpu = sysconf(_SC_CPU_VERSION); - - switch (cpu) { - case CPU_PA_RISC1_0: - arch = "PA_RISC1.0"; - break; - case CPU_PA_RISC1_1: - arch = "PA_RISC1.1"; - break; - case CPU_PA_RISC2_0: - arch = "PA_RISC2.0"; - break; -#ifdef CPU_IA64_ARCHREV_0 - case CPU_IA64_ARCHREV_0: - arch = "ia64"; - break; -#endif - default: - arch = "unknown"; - break; - } - - SIGAR_SSTRCPY(sysinfo->arch, arch); - - SIGAR_SSTRCPY(sysinfo->name, "HPUX"); - SIGAR_SSTRCPY(sysinfo->vendor, "Hewlett-Packard"); - - if (strstr(sysinfo->version, ".11.")) { - vendor_version = "11"; - } - else { - vendor_version = sysinfo->version; - } - - SIGAR_SSTRCPY(sysinfo->vendor_version, vendor_version); - - snprintf(sysinfo->description, - sizeof(sysinfo->description), - "%s %s", - sysinfo->vendor_name, sysinfo->vendor_version); - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/os/hpux/sigar_os.h b/vendor/sigar/src/os/hpux/sigar_os.h deleted file mode 100644 index ee2434a3e..000000000 --- a/vendor/sigar/src/os/hpux/sigar_os.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2004-2007 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_OS_H -#define SIGAR_OS_H - -#if defined(__ia64) && !defined(__ia64__) -#define __ia64__ -#endif - -#ifdef __ia64__ -#ifndef _LP64 -#define _LP64 -#endif -#endif - -#define _PSTAT64 - -#include -#include -#include -#include - -struct sigar_t { - SIGAR_T_BASE; - struct pst_static pstatic; - time_t last_getprocs; - sigar_pid_t last_pid; - struct pst_status *pinfo; - - int mib; -}; - -int hpux_get_mib_ifentry(int ppa, mib_ifEntry *mib); - -#endif /* SIGAR_OS_H */ diff --git a/vendor/sigar/src/os/linux/linux_sigar.c b/vendor/sigar/src/os/linux/linux_sigar.c deleted file mode 100644 index 0e4283fac..000000000 --- a/vendor/sigar/src/os/linux/linux_sigar.c +++ /dev/null @@ -1,2782 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#define pageshift(x) ((x) << sigar->pagesize) - -#define PROC_MEMINFO PROC_FS_ROOT "meminfo" -#define PROC_VMSTAT PROC_FS_ROOT "vmstat" -#define PROC_MTRR PROC_FS_ROOT "mtrr" -#define PROC_STAT PROC_FS_ROOT "stat" -#define PROC_UPTIME PROC_FS_ROOT "uptime" -#define PROC_LOADAVG PROC_FS_ROOT "loadavg" - -#define PROC_PSTAT "/stat" -#define PROC_PSTATUS "/status" - -#define SYS_BLOCK "/sys/block" -#define PROC_PARTITIONS PROC_FS_ROOT "partitions" -#define PROC_DISKSTATS PROC_FS_ROOT "diskstats" - -/* - * /proc/self/stat fields: - * 1 - pid - * 2 - comm - * 3 - state - * 4 - ppid - * 5 - pgrp - * 6 - session - * 7 - tty_nr - * 8 - tpgid - * 9 - flags - * 10 - minflt - * 11 - cminflt - * 12 - majflt - * 13 - cmajflt - * 14 - utime - * 15 - stime - * 16 - cutime - * 17 - cstime - * 18 - priority - * 19 - nice - * 20 - 0 (removed field) - * 21 - itrealvalue - * 22 - starttime - * 23 - vsize - * 24 - rss - * 25 - rlim - * 26 - startcode - * 27 - endcode - * 28 - startstack - * 29 - kstkesp - * 30 - kstkeip - * 31 - signal - * 32 - blocked - * 33 - sigignore - * 34 - sigcache - * 35 - wchan - * 36 - nswap - * 37 - cnswap - * 38 - exit_signal <-- looking for this. - * 39 - processor - * ... more for newer RH - */ - -#define PROC_SIGNAL_IX 38 - -static int get_proc_signal_offset(void) -{ - char buffer[BUFSIZ], *ptr=buffer; - int fields = 0; - int status = sigar_file2str(PROCP_FS_ROOT "self/stat", - buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return 1; - } - - while (*ptr) { - if (*ptr++ == ' ') { - fields++; - } - } - - return (fields - PROC_SIGNAL_IX) + 1; -} - -sigar_pid_t sigar_pid_get(sigar_t *sigar) -{ - /* XXX cannot safely cache getpid unless using nptl */ - /* we can however, cache it for optimizations in the - * case of proc_env_get for example. - */ - sigar->pid = getpid(); - return sigar->pid; -} - -static int sigar_boot_time_get(sigar_t *sigar) -{ - FILE *fp; - char buffer[BUFSIZ], *ptr; - int found = 0; - - if (!(fp = fopen(PROC_STAT, "r"))) { - return errno; - } - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - if (strnEQ(ptr, "btime", 5)) { - if ((ptr = sigar_skip_token(ptr))) { - sigar->boot_time = sigar_strtoul(ptr); - found = 1; - } - break; - } - } - - fclose(fp); - - if (!found) { - /* should never happen */ - sigar->boot_time = time(NULL); - } - - return SIGAR_OK; -} - -int sigar_os_open(sigar_t **sigar) -{ - int i, status; - int kernel_rev, has_nptl; - struct stat sb; - struct utsname name; - - *sigar = malloc(sizeof(**sigar)); - - (*sigar)->pagesize = 0; - i = getpagesize(); - while ((i >>= 1) > 0) { - (*sigar)->pagesize++; - } - - status = sigar_boot_time_get(*sigar); - if (status != SIGAR_OK) { - return status; - } - - (*sigar)->ticks = sysconf(_SC_CLK_TCK); - - (*sigar)->ram = -1; - - (*sigar)->proc_signal_offset = -1; - - (*sigar)->last_proc_stat.pid = -1; - - (*sigar)->lcpu = -1; - - if (stat(PROC_DISKSTATS, &sb) == 0) { - (*sigar)->iostat = IOSTAT_DISKSTATS; - } - else if (stat(SYS_BLOCK, &sb) == 0) { - (*sigar)->iostat = IOSTAT_SYS; - } - else if (stat(PROC_PARTITIONS, &sb) == 0) { - /* XXX file exists does not mean is has the fields */ - (*sigar)->iostat = IOSTAT_PARTITIONS; - } - else { - (*sigar)->iostat = IOSTAT_NONE; - } - - /* hook for using mirrored /proc/net/tcp file */ - (*sigar)->proc_net = getenv("SIGAR_PROC_NET"); - - uname(&name); - /* 2.X.y.z -> just need X (unless there is ever a kernel version 3!) */ - kernel_rev = atoi(&name.release[2]); - if (kernel_rev >= 6) { - has_nptl = 1; - } - else { - has_nptl = getenv("SIGAR_HAS_NPTL") ? 1 : 0; - } - (*sigar)->has_nptl = has_nptl; - - return SIGAR_OK; -} - -int sigar_os_close(sigar_t *sigar) -{ - free(sigar); - return SIGAR_OK; -} - -char *sigar_os_error_string(sigar_t *sigar, int err) -{ - return NULL; -} - -static int sigar_cpu_total_count(sigar_t *sigar) -{ - sigar->ncpu = (int)sysconf(_SC_NPROCESSORS_CONF); - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[cpu] ncpu=%d\n", - sigar->ncpu); - return sigar->ncpu; -} - -static int get_ram(sigar_t *sigar, sigar_mem_t *mem) -{ - char buffer[BUFSIZ], *ptr; - FILE *fp; - int total = 0; - sigar_uint64_t sys_total = (mem->total / (1024 * 1024)); - - if (sigar->ram > 0) { - /* return cached value */ - mem->ram = sigar->ram; - return SIGAR_OK; - } - - if (sigar->ram == 0) { - return ENOENT; - } - - /* - * Memory Type Range Registers - * write-back registers add up to the total. - * Well, they are supposed to add up, but seen - * at least one configuration where that is not the - * case. - */ - if (!(fp = fopen(PROC_MTRR, "r"))) { - return errno; - } - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - if (!(ptr = strstr(ptr, "size="))) { - continue; - } - - if (!strstr(ptr, "write-back")) { - continue; - } - - ptr += 5; - while (sigar_isspace(*ptr)) { - ++ptr; - } - - total += atoi(ptr); - } - - fclose(fp); - - if ((total - sys_total) > 256) { - /* mtrr write-back registers are way off - * kernel should not be using more that 256MB of mem - */ - total = 0; /* punt */ - } - - if (total == 0) { - return ENOENT; - } - - mem->ram = sigar->ram = total; - - return SIGAR_OK; -} - -#define MEMINFO_PARAM(a) a ":", SSTRLEN(a ":") - -static SIGAR_INLINE sigar_uint64_t sigar_meminfo(char *buffer, - char *attr, int len) -{ - sigar_uint64_t val = 0; - char *ptr, *tok; - - if ((ptr = strstr(buffer, attr))) { - ptr += len; - val = strtoull(ptr, &tok, 0); - while (*tok == ' ') { - ++tok; - } - if (*tok == 'k') { - val *= 1024; - } - else if (*tok == 'M') { - val *= (1024 * 1024); - } - } - - return val; -} - -int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) -{ - sigar_uint64_t buffers, cached, kern; - char buffer[BUFSIZ]; - - int status = sigar_file2str(PROC_MEMINFO, - buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - mem->total = sigar_meminfo(buffer, MEMINFO_PARAM("MemTotal")); - mem->free = sigar_meminfo(buffer, MEMINFO_PARAM("MemFree")); - mem->used = mem->total - mem->free; - - buffers = sigar_meminfo(buffer, MEMINFO_PARAM("Buffers")); - cached = sigar_meminfo(buffer, MEMINFO_PARAM("Cached")); - - kern = buffers + cached; - mem->actual_free = mem->free + kern; - mem->actual_used = mem->used - kern; - - sigar_mem_calc_ram(sigar, mem); - - if (get_ram(sigar, mem) != SIGAR_OK) { - /* XXX other options on failure? */ - } - - return SIGAR_OK; -} - -int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) -{ - char buffer[BUFSIZ], *ptr; - - /* XXX: we open/parse the same file here as sigar_mem_get */ - int status = sigar_file2str(PROC_MEMINFO, - buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - swap->total = sigar_meminfo(buffer, MEMINFO_PARAM("SwapTotal")); - swap->free = sigar_meminfo(buffer, MEMINFO_PARAM("SwapFree")); - swap->used = swap->total - swap->free; - - swap->page_in = swap->page_out = -1; - - status = sigar_file2str(PROC_VMSTAT, - buffer, sizeof(buffer)); - - if (status == SIGAR_OK) { - /* 2.6+ kernel */ - if ((ptr = strstr(buffer, "\npswpin"))) { - ptr = sigar_skip_token(ptr); - swap->page_in = sigar_strtoull(ptr); - ptr = sigar_skip_token(ptr); - swap->page_out = sigar_strtoull(ptr); - } - } - else { - /* 2.2, 2.4 kernels */ - status = sigar_file2str(PROC_STAT, - buffer, sizeof(buffer)); - if (status != SIGAR_OK) { - return status; - } - - if ((ptr = strstr(buffer, "\nswap"))) { - ptr = sigar_skip_token(ptr); - swap->page_in = sigar_strtoull(ptr); - swap->page_out = sigar_strtoull(ptr); - } - } - - return SIGAR_OK; -} - -static void get_cpu_metrics(sigar_t *sigar, sigar_cpu_t *cpu, char *line) -{ - char *ptr = sigar_skip_token(line); /* "cpu%d" */ - - cpu->user += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - cpu->nice += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - cpu->sys += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - cpu->idle += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - if (*ptr == ' ') { - /* 2.6+ kernels only */ - cpu->wait += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - cpu->irq += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - cpu->soft_irq += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - } - if (*ptr == ' ') { - /* 2.6.11+ kernels only */ - cpu->stolen += SIGAR_TICK2MSEC(sigar_strtoull(ptr)); - } - cpu->total = - cpu->user + cpu->nice + cpu->sys + cpu->idle + - cpu->wait + cpu->irq + cpu->soft_irq + cpu->stolen; -} - -int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - char buffer[BUFSIZ]; - int status = sigar_file2str(PROC_STAT, buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - SIGAR_ZERO(cpu); - get_cpu_metrics(sigar, cpu, buffer); - - return SIGAR_OK; -} - -int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) -{ - FILE *fp; - char buffer[BUFSIZ], cpu_total[BUFSIZ], *ptr; - int core_rollup = sigar_cpu_core_rollup(sigar), i=0; - sigar_cpu_t *cpu; - - if (!(fp = fopen(PROC_STAT, "r"))) { - return errno; - } - - /* skip first line */ - (void)fgets(cpu_total, sizeof(cpu_total), fp); - - sigar_cpu_list_create(cpulist); - - /* XXX: merge times of logical processors if hyperthreading */ - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - if (!strnEQ(ptr, "cpu", 3)) { - break; - } - - if (core_rollup && (i % sigar->lcpu)) { - /* merge times of logical processors */ - cpu = &cpulist->data[cpulist->number-1]; - } - else { - SIGAR_CPU_LIST_GROW(cpulist); - cpu = &cpulist->data[cpulist->number++]; - SIGAR_ZERO(cpu); - } - - get_cpu_metrics(sigar, cpu, ptr); - - i++; - } - - fclose(fp); - - if (cpulist->number == 0) { - /* likely older kernel where cpu\d is not present */ - cpu = &cpulist->data[cpulist->number++]; - SIGAR_ZERO(cpu); - get_cpu_metrics(sigar, cpu, cpu_total); - } - - return SIGAR_OK; -} - -int sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime) -{ - char buffer[BUFSIZ], *ptr = buffer; - int status = sigar_file2str(PROC_UPTIME, buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - uptime->uptime = strtod(buffer, &ptr); - - return SIGAR_OK; -} - -int sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg) -{ - char buffer[BUFSIZ], *ptr = buffer; - int status = sigar_file2str(PROC_LOADAVG, buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - loadavg->loadavg[0] = strtod(buffer, &ptr); - loadavg->loadavg[1] = strtod(ptr, &ptr); - loadavg->loadavg[2] = strtod(ptr, &ptr); - - return SIGAR_OK; -} - -/* - * seems the easiest/fastest way to tell if a process listed in /proc - * is a thread is to check the "exit signal" flag in /proc/num/stat. - * any value other than SIGCHLD seems to be a thread. this make hulk mad. - * redhat's procps patch (named "threadbadhack.pat") does not use - * this flag to filter out threads. instead does much more expensive - * comparisions. their patch also bubbles up thread cpu times to the main - * process. functionality we currently lack. - * when nptl is in use, this is not the case and all threads spawned from - * a process have the same pid. however, it seems both old-style linux - * threads and nptl threads can be run on the same machine. - * there is also the "Tgid" field in /proc/self/status which could be used - * to detect threads, but this is not available in older kernels. - */ -static SIGAR_INLINE int proc_isthread(sigar_t *sigar, char *pidstr, int len) -{ - char buffer[BUFSIZ], *ptr=buffer; - int fd, n, offset=sigar->proc_signal_offset; - - /* sprintf(buffer, "/proc/%s/stat", pidstr) */ - memcpy(ptr, PROCP_FS_ROOT, SSTRLEN(PROCP_FS_ROOT)); - ptr += SSTRLEN(PROCP_FS_ROOT); - - memcpy(ptr, pidstr, len); - ptr += len; - - memcpy(ptr, PROC_PSTAT, SSTRLEN(PROC_PSTAT)); - ptr += SSTRLEN(PROC_PSTAT); - - *ptr = '\0'; - - if ((fd = open(buffer, O_RDONLY)) < 0) { - /* unlikely if pid was from readdir proc */ - return 0; - } - - n = read(fd, buffer, sizeof(buffer)); - close(fd); - - if (n < 0) { - return 0; /* chances: slim..none */ - } - - buffer[n--] = '\0'; - - /* exit_signal is the second to last field so we look backwards. - * XXX if newer kernels drop more turds in this file we'll need - * to go the other way. luckily linux has no real api for this shit. - */ - - /* skip trailing crap */ - while ((n > 0) && !isdigit(buffer[n--])) ; - - while (offset-- > 0) { - /* skip last field */ - while ((n > 0) && isdigit(buffer[n--])) ; - - /* skip whitespace */ - while ((n > 0) && !isdigit(buffer[n--])) ; - } - - if (n < 3) { - return 0; /* hulk smashed /proc? */ - } - - ptr = &buffer[n]; - /* - * '17' == SIGCHLD == real process. - * '33' and '0' are threads - */ - if ((*ptr++ == '1') && - (*ptr++ == '7') && - (*ptr++ == ' ')) - { - return 0; - } - - return 1; -} - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - DIR *dirp = opendir(PROCP_FS_ROOT); - struct dirent *ent, dbuf; - register const int threadbadhack = !sigar->has_nptl; - - if (!dirp) { - return errno; - } - - if (threadbadhack && (sigar->proc_signal_offset == -1)) { - sigar->proc_signal_offset = get_proc_signal_offset(); - } - - while (readdir_r(dirp, &dbuf, &ent) == 0) { - if (!ent) { - break; - } - - if (!sigar_isdigit(*ent->d_name)) { - continue; - } - - if (threadbadhack && - proc_isthread(sigar, ent->d_name, strlen(ent->d_name))) - { - continue; - } - - /* XXX: more sanity checking */ - - SIGAR_PROC_LIST_GROW(proclist); - - proclist->data[proclist->number++] = - strtoul(ent->d_name, NULL, 10); - } - - closedir(dirp); - - return SIGAR_OK; -} - -static int proc_stat_read(sigar_t *sigar, sigar_pid_t pid) -{ - char buffer[BUFSIZ], *ptr=buffer, *tmp; - unsigned int len; - linux_proc_stat_t *pstat = &sigar->last_proc_stat; - int status; - - time_t timenow = time(NULL); - - /* - * short-lived cache read/parse of last /proc/pid/stat - * as this info is spread out across a few functions. - */ - if (pstat->pid == pid) { - if ((timenow - pstat->mtime) < SIGAR_LAST_PROC_EXPIRE) { - return SIGAR_OK; - } - } - - pstat->pid = pid; - pstat->mtime = timenow; - - status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTAT); - - if (status != SIGAR_OK) { - return status; - } - - if (!(ptr = strchr(ptr, '('))) { - return EINVAL; - } - if (!(tmp = strrchr(++ptr, ')'))) { - return EINVAL; - } - len = tmp-ptr; - - if (len >= sizeof(pstat->name)) { - len = sizeof(pstat->name)-1; - } - - /* (1,2) */ - memcpy(pstat->name, ptr, len); - pstat->name[len] = '\0'; - ptr = tmp+1; - - SIGAR_SKIP_SPACE(ptr); - pstat->state = *ptr++; /* (3) */ - SIGAR_SKIP_SPACE(ptr); - - pstat->ppid = sigar_strtoul(ptr); /* (4) */ - ptr = sigar_skip_token(ptr); /* (5) pgrp */ - ptr = sigar_skip_token(ptr); /* (6) session */ - pstat->tty = sigar_strtoul(ptr); /* (7) */ - ptr = sigar_skip_token(ptr); /* (8) tty pgrp */ - - ptr = sigar_skip_token(ptr); /* (9) flags */ - pstat->minor_faults = sigar_strtoull(ptr); /* (10) */ - ptr = sigar_skip_token(ptr); /* (11) cmin flt */ - pstat->major_faults = sigar_strtoull(ptr); /* (12) */ - ptr = sigar_skip_token(ptr); /* (13) cmaj flt */ - - pstat->utime = SIGAR_TICK2MSEC(sigar_strtoull(ptr)); /* (14) */ - pstat->stime = SIGAR_TICK2MSEC(sigar_strtoull(ptr)); /* (15) */ - - ptr = sigar_skip_token(ptr); /* (16) cutime */ - ptr = sigar_skip_token(ptr); /* (17) cstime */ - - pstat->priority = sigar_strtoul(ptr); /* (18) */ - pstat->nice = sigar_strtoul(ptr); /* (19) */ - - ptr = sigar_skip_token(ptr); /* (20) timeout */ - ptr = sigar_skip_token(ptr); /* (21) it_real_value */ - - pstat->start_time = sigar_strtoul(ptr); /* (22) */ - pstat->start_time /= sigar->ticks; - pstat->start_time += sigar->boot_time; /* seconds */ - pstat->start_time *= 1000; /* milliseconds */ - - pstat->vsize = sigar_strtoull(ptr); /* (23) */ - pstat->rss = pageshift(sigar_strtoull(ptr)); /* (24) */ - - ptr = sigar_skip_token(ptr); /* (25) rlim */ - ptr = sigar_skip_token(ptr); /* (26) startcode */ - ptr = sigar_skip_token(ptr); /* (27) endcode */ - ptr = sigar_skip_token(ptr); /* (28) startstack */ - ptr = sigar_skip_token(ptr); /* (29) kstkesp */ - ptr = sigar_skip_token(ptr); /* (30) kstkeip */ - ptr = sigar_skip_token(ptr); /* (31) signal */ - ptr = sigar_skip_token(ptr); /* (32) blocked */ - ptr = sigar_skip_token(ptr); /* (33) sigignore */ - ptr = sigar_skip_token(ptr); /* (34) sigcache */ - ptr = sigar_skip_token(ptr); /* (35) wchan */ - ptr = sigar_skip_token(ptr); /* (36) nswap */ - ptr = sigar_skip_token(ptr); /* (37) cnswap */ - ptr = sigar_skip_token(ptr); /* (38) exit_signal */ - - pstat->processor = sigar_strtoul(ptr); /* (39) */ - - return SIGAR_OK; -} - -int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem) -{ - char buffer[BUFSIZ], *ptr=buffer; - int status = proc_stat_read(sigar, pid); - linux_proc_stat_t *pstat = &sigar->last_proc_stat; - - procmem->minor_faults = pstat->minor_faults; - procmem->major_faults = pstat->major_faults; - procmem->page_faults = - procmem->minor_faults + procmem->major_faults; - - status = SIGAR_PROC_FILE2STR(buffer, pid, "/statm"); - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = pageshift(sigar_strtoull(ptr)); - procmem->resident = pageshift(sigar_strtoull(ptr)); - procmem->share = pageshift(sigar_strtoull(ptr)); - - return SIGAR_OK; -} - -#define NO_ID_MSG "[proc_cred] /proc/%lu" PROC_PSTATUS " missing " - -int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred) -{ - char buffer[BUFSIZ], *ptr; - int status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTATUS); - - if (status != SIGAR_OK) { - return status; - } - - if ((ptr = strstr(buffer, "\nUid:"))) { - ptr = sigar_skip_token(ptr); - - proccred->uid = sigar_strtoul(ptr); - proccred->euid = sigar_strtoul(ptr); - } - else { - sigar_log_printf(sigar, SIGAR_LOG_WARN, - NO_ID_MSG "Uid", pid); - return ENOENT; - } - - if ((ptr = strstr(ptr, "\nGid:"))) { - ptr = sigar_skip_token(ptr); - - proccred->gid = sigar_strtoul(ptr); - proccred->egid = sigar_strtoul(ptr); - } - else { - sigar_log_printf(sigar, SIGAR_LOG_WARN, - NO_ID_MSG "Gid", pid); - return ENOENT; - } - - return SIGAR_OK; -} - -int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime) -{ - int status = proc_stat_read(sigar, pid); - linux_proc_stat_t *pstat = &sigar->last_proc_stat; - - if (status != SIGAR_OK) { - return status; - } - - proctime->user = pstat->utime; - proctime->sys = pstat->stime; - proctime->total = proctime->user + proctime->sys; - proctime->start_time = pstat->start_time; - - return SIGAR_OK; -} - -static int proc_status_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - char buffer[BUFSIZ], *ptr; - int status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTATUS); - - if (status != SIGAR_OK) { - return status; - } - - ptr = strstr(buffer, "\nThreads:"); - if (ptr) { - /* 2.6+ kernel only */ - ptr = sigar_skip_token(ptr); - procstate->threads = sigar_strtoul(ptr); - } - else { - procstate->threads = SIGAR_FIELD_NOTIMPL; - } - - return SIGAR_OK; -} - -int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - int status = proc_stat_read(sigar, pid); - linux_proc_stat_t *pstat = &sigar->last_proc_stat; - - if (status != SIGAR_OK) { - return status; - } - - memcpy(procstate->name, pstat->name, sizeof(procstate->name)); - procstate->state = pstat->state; - - procstate->ppid = pstat->ppid; - procstate->tty = pstat->tty; - procstate->priority = pstat->priority; - procstate->nice = pstat->nice; - procstate->processor = pstat->processor; - - if (sigar_cpu_core_rollup(sigar)) { - procstate->processor /= sigar->lcpu; - } - - proc_status_get(sigar, pid, procstate); - - return SIGAR_OK; -} - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - return sigar_procfs_args_get(sigar, pid, procargs); -} - -/* glibc 2.8 XXX use sysconf(_SC_ARG_MAX) */ -#ifndef ARG_MAX -#define ARG_MAX 131072 -#endif - -int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - int fd; - char buffer[ARG_MAX]; /* XXX: ARG_MAX == 130k */ - char name[BUFSIZ]; - size_t len; - char *ptr, *end; - - /* optimize if pid == $$ and type == ENV_KEY */ - SIGAR_PROC_ENV_KEY_LOOKUP(); - - (void)SIGAR_PROC_FILENAME(name, pid, "/environ"); - - if ((fd = open(name, O_RDONLY)) < 0) { - if (errno == ENOENT) { - return ESRCH; - } - return errno; - } - - len = read(fd, buffer, sizeof(buffer)); - - close(fd); - - buffer[len] = '\0'; - ptr = buffer; - - end = buffer + len; - while (ptr < end) { - char *val = strchr(ptr, '='); - int klen, vlen, status; - char key[128]; /* XXX is there a max key size? */ - - if (val == NULL) { - /* not key=val format */ - break; - } - - klen = val - ptr; - SIGAR_SSTRCPY(key, ptr); - key[klen] = '\0'; - ++val; - - vlen = strlen(val); - status = procenv->env_getter(procenv->data, - key, klen, val, vlen); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - - ptr += (klen + 1 + vlen + 1); - } - - return SIGAR_OK; -} - -int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd) -{ - int status = - sigar_proc_fd_count(sigar, pid, &procfd->total); - - return status; -} - -int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ - int len; - char name[BUFSIZ]; - - (void)SIGAR_PROC_FILENAME(name, pid, "/cwd"); - - if ((len = readlink(name, procexe->cwd, - sizeof(procexe->cwd)-1)) < 0) - { - return errno; - } - - procexe->cwd[len] = '\0'; - - (void)SIGAR_PROC_FILENAME(name, pid, "/exe"); - - if ((len = readlink(name, procexe->name, - sizeof(procexe->name)-1)) < 0) - { - return errno; - } - - procexe->name[len] = '\0'; - - (void)SIGAR_PROC_FILENAME(name, pid, "/root"); - - if ((len = readlink(name, procexe->root, - sizeof(procexe->root)-1)) < 0) - { - return errno; - } - - procexe->root[len] = '\0'; - - return SIGAR_OK; -} - -int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ - FILE *fp; - char buffer[BUFSIZ], *ptr; - unsigned long inode, last_inode = 0; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/maps"); - - if (!(fp = fopen(buffer, "r"))) { - return errno; - } - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - int len, status; - /* skip region, flags, offset, dev */ - ptr = sigar_skip_multiple_token(ptr, 4); - inode = sigar_strtoul(ptr); - - if ((inode == 0) || (inode == last_inode)) { - last_inode = 0; - continue; - } - - last_inode = inode; - SIGAR_SKIP_SPACE(ptr); - len = strlen(ptr); - ptr[len-1] = '\0'; /* chop \n */ - - status = - procmods->module_getter(procmods->data, - ptr, len-1); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - } - - fclose(fp); - - return SIGAR_OK; -} - -int sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu) -{ - struct tms now; - - if (id != 0) { - return SIGAR_ENOTIMPL; - } - - times(&now); - - cpu->user = SIGAR_TICK2NSEC(now.tms_utime); - cpu->sys = SIGAR_TICK2NSEC(now.tms_stime); - cpu->total = SIGAR_TICK2NSEC(now.tms_utime + now.tms_stime); - - return SIGAR_OK; -} - -#include - -int sigar_os_fs_type_get(sigar_file_system_t *fsp) -{ - char *type = fsp->sys_type_name; - - switch (*type) { - case 'e': - if (strnEQ(type, "ext", 3)) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'g': - if (strEQ(type, "gfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'h': - if (strEQ(type, "hpfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'j': - if (strnEQ(type, "jfs", 3)) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'o': - if (strnEQ(type, "ocfs", 4)) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'p': - if (strnEQ(type, "psfs", 4)) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'r': - if (strEQ(type, "reiserfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'v': - if (strEQ(type, "vzfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'x': - if (strEQ(type, "xfs") || strEQ(type, "xiafs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - } - - return fsp->type; -} - -int sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - struct mntent ent; - char buf[1025]; /* buffer for strings within ent */ - FILE *fp; - sigar_file_system_t *fsp; - - if (!(fp = setmntent(MOUNTED, "r"))) { - return errno; - } - - sigar_file_system_list_create(fslist); - - while (getmntent_r(fp, &ent, buf, sizeof(buf))) { - SIGAR_FILE_SYSTEM_LIST_GROW(fslist); - - fsp = &fslist->data[fslist->number++]; - - fsp->type = SIGAR_FSTYPE_UNKNOWN; /* unknown, will be set later */ - SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_dir); - SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_fsname); - SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_type); - SIGAR_SSTRCPY(fsp->options, ent.mnt_opts); - sigar_fs_type_get(fsp); - } - - endmntent(fp); - - return SIGAR_OK; -} - -#define ST_MAJOR(sb) major((sb).st_rdev) -#define ST_MINOR(sb) minor((sb).st_rdev) - -static int get_iostat_sys(sigar_t *sigar, - const char *dirname, - sigar_disk_usage_t *disk, - sigar_iodev_t **iodev) -{ - char stat[1025], dev[1025]; - char *name, *ptr, *fsdev; - int partition, status; - - if (!(*iodev = sigar_iodev_get(sigar, dirname))) { - return ENXIO; - } - - name = fsdev = (*iodev)->name; - - if (SIGAR_NAME_IS_DEV(name)) { - name += SSTRLEN(SIGAR_DEV_PREFIX); /* strip "/dev/" */ - } - - while (!sigar_isdigit(*fsdev)) { - fsdev++; - } - - partition = strtoul(fsdev, NULL, 0); - *fsdev = '\0'; - - snprintf(stat, sizeof(stat), - SYS_BLOCK "/%s/%s%d/stat", name, name, partition); - - status = sigar_file2str(stat, dev, sizeof(dev)); - if (status != SIGAR_OK) { - return status; - } - - ptr = dev; - ptr = sigar_skip_token(ptr); - disk->reads = sigar_strtoull(ptr); - ptr = sigar_skip_token(ptr); - disk->writes = sigar_strtoull(ptr); - - disk->read_bytes = SIGAR_FIELD_NOTIMPL; - disk->write_bytes = SIGAR_FIELD_NOTIMPL; - disk->queue = SIGAR_FIELD_NOTIMPL; - - return SIGAR_OK; -} - -static int get_iostat_proc_dstat(sigar_t *sigar, - const char *dirname, - sigar_disk_usage_t *disk, - sigar_iodev_t **iodev, - sigar_disk_usage_t *device_usage) -{ - FILE *fp; - char buffer[1025]; - char *ptr; - struct stat sb; - int status=ENOENT; - - SIGAR_DISK_STATS_INIT(device_usage); - - if (!(*iodev = sigar_iodev_get(sigar, dirname))) { - return ENXIO; - } - - if (stat((*iodev)->name, &sb) < 0) { - return errno; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - PROC_DISKSTATS " %s -> %s [%d,%d]", - dirname, (*iodev)->name, - ST_MAJOR(sb), ST_MINOR(sb)); - } - - if (!(fp = fopen(PROC_DISKSTATS, "r"))) { - return errno; - } - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - unsigned long major, minor; - - major = sigar_strtoul(ptr); - minor = sigar_strtoul(ptr); - - if ((major == ST_MAJOR(sb)) && - ((minor == ST_MINOR(sb)) || (minor == 0))) - { - int num; - unsigned long - rio, rmerge, rsect, ruse, - wio, wmerge, wsect, wuse, - running, use, aveq; - - ptr = sigar_skip_token(ptr); /* name */ - - num = sscanf(ptr, - "%lu %lu %lu %lu " - "%lu %lu %lu %lu " - "%lu %lu %lu", - &rio, /* 1 # reads issued */ - &rmerge, /* 2 # reads merged */ - &rsect, /* 3 # sectors read */ - &ruse, /* 4 # millis spent reading */ - &wio, /* 5 # writes completed */ - &wmerge, /* 6 # writes merged */ - &wsect, /* 7 # sectors written */ - &wuse, /* 8 # millis spent writing */ - &running, /* 9 # I/Os currently in progress */ - &use, /* 10 # millis spent doing I/Os */ - &aveq); /* 11 # of millis spent doing I/Os (weighted) */ - - if (num == 11) { - disk->rtime = ruse; - disk->wtime = wuse; - disk->time = use; - disk->qtime = aveq; - } - else if (num == 4) { - wio = rsect; - rsect = rmerge; - wsect = ruse; - disk->time = disk->qtime = SIGAR_FIELD_NOTIMPL; - } - else { - status = ENOENT; - } - - disk->reads = rio; - disk->writes = wio; - disk->read_bytes = rsect; - disk->write_bytes = wsect; - - /* convert sectors to bytes (512 is fixed size in 2.6 kernels) */ - disk->read_bytes *= 512; - disk->write_bytes *= 512; - - if (minor == ST_MINOR(sb)) { - status = SIGAR_OK; - break; - } - else if (minor == 0) { - memcpy(device_usage, disk, sizeof(*device_usage)); - } - } - } - - fclose(fp); - - return status; -} - -static int get_iostat_procp(sigar_t *sigar, - const char *dirname, - sigar_disk_usage_t *disk, - sigar_iodev_t **iodev) -{ - FILE *fp; - char buffer[1025]; - char *ptr; - struct stat sb; - - if (!(*iodev = sigar_iodev_get(sigar, dirname))) { - return ENXIO; - } - - if (stat((*iodev)->name, &sb) < 0) { - return errno; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - PROC_PARTITIONS " %s -> %s [%d,%d]", - dirname, (*iodev)->name, - ST_MAJOR(sb), ST_MINOR(sb)); - } - - if (!(fp = fopen(PROC_PARTITIONS, "r"))) { - return errno; - } - - (void)fgets(buffer, sizeof(buffer), fp); /* skip header */ - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - unsigned long major, minor; - - major = sigar_strtoul(ptr); - minor = sigar_strtoul(ptr); - - if ((major == ST_MAJOR(sb)) && (minor == ST_MINOR(sb))) { - ptr = sigar_skip_token(ptr); /* blocks */ - ptr = sigar_skip_token(ptr); /* name */ - disk->reads = sigar_strtoull(ptr); /* rio */ - ptr = sigar_skip_token(ptr); /* rmerge */ - disk->read_bytes = sigar_strtoull(ptr); /* rsect */ - disk->rtime = sigar_strtoull(ptr); /* ruse */ - disk->writes = sigar_strtoull(ptr); /* wio */ - ptr = sigar_skip_token(ptr); /* wmerge */ - disk->write_bytes = sigar_strtoull(ptr); /* wsect */ - disk->wtime = sigar_strtoull(ptr); /* wuse */ - ptr = sigar_skip_token(ptr); /* running */ - disk->time = sigar_strtoull(ptr); /* use */ - disk->qtime = sigar_strtoull(ptr); /* aveq */ - - /* convert sectors to bytes (512 is fixed size in 2.6 kernels) */ - disk->read_bytes *= 512; - disk->write_bytes *= 512; - - fclose(fp); - return SIGAR_OK; - } - } - - fclose(fp); - - return ENOENT; -} - -int sigar_disk_usage_get(sigar_t *sigar, const char *name, - sigar_disk_usage_t *disk) -{ - int status; - sigar_iodev_t *iodev = NULL; - sigar_disk_usage_t device_usage; - SIGAR_DISK_STATS_INIT(disk); - - /* - * 2.2 has metrics /proc/stat, but wtf is the device mapping? - * 2.4 has /proc/partitions w/ the metrics. - * 2.6 has /proc/partitions w/o the metrics. - * instead the metrics are within the /proc-like /sys filesystem. - * also has /proc/diskstats - */ - switch (sigar->iostat) { - case IOSTAT_SYS: - status = get_iostat_sys(sigar, name, disk, &iodev); - break; - case IOSTAT_DISKSTATS: - status = get_iostat_proc_dstat(sigar, name, disk, &iodev, &device_usage); - break; - case IOSTAT_PARTITIONS: - status = get_iostat_procp(sigar, name, disk, &iodev); - break; - /* - * case IOSTAT_SOME_OTHER_WIERD_THING: - * break; - */ - case IOSTAT_NONE: - default: - status = ENOENT; - break; - } - - if ((status == SIGAR_OK) && iodev) { - sigar_uptime_t uptime; - sigar_uint64_t interval, ios; - double tput, util; - sigar_disk_usage_t *partition_usage=NULL; - - sigar_uptime_get(sigar, &uptime); - - if (iodev->is_partition && - (sigar->iostat == IOSTAT_DISKSTATS)) - { - /* 2.6 kernels do not have per-partition times */ - partition_usage = disk; - disk = &device_usage; - } - - disk->snaptime = uptime.uptime; - - if (iodev->disk.snaptime) { - interval = disk->snaptime - iodev->disk.snaptime; - } - else { - interval = disk->snaptime; - } - - ios = - (disk->reads - iodev->disk.reads) + - (disk->writes - iodev->disk.writes); - - if (disk->time == SIGAR_FIELD_NOTIMPL) { - disk->service_time = SIGAR_FIELD_NOTIMPL; - } - else { - tput = ((double)ios) * HZ / interval; - util = ((double)(disk->time - iodev->disk.time)) / interval * HZ; - disk->service_time = tput ? util / tput : 0.0; - } - if (disk->qtime == SIGAR_FIELD_NOTIMPL) { - disk->queue = SIGAR_FIELD_NOTIMPL; - } - else { - util = ((double)(disk->qtime - iodev->disk.qtime)) / interval; - disk->queue = util / 1000.0; - } - - memcpy(&iodev->disk, disk, sizeof(iodev->disk)); - if (partition_usage) { - partition_usage->service_time = disk->service_time; - partition_usage->queue = disk->queue; - } - } - - return status; -} - -int sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - int status = sigar_statvfs(sigar, dirname, fsusage); - - if (status != SIGAR_OK) { - return status; - } - - fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); - - (void)sigar_disk_usage_get(sigar, dirname, &fsusage->disk); - - return SIGAR_OK; -} - -static SIGAR_INLINE char *cpu_info_strval(char *ptr) -{ - if ((ptr = strchr(ptr, ':'))) { - ptr++; - while (isspace (*ptr)) ptr++; - return ptr; - } - return NULL; -} - -static SIGAR_INLINE void cpu_info_strcpy(char *ptr, char *buf, int len) -{ - int slen; - ptr = cpu_info_strval(ptr); - if (!ptr) { - return; - } - slen = strlen(ptr); - strncpy(buf, ptr, len); - buf[len] = '\0'; - if (slen < len) { - buf[slen-1] = '\0'; /* rid \n */ - } -} - -static int get_cpu_info(sigar_t *sigar, sigar_cpu_info_t *info, - FILE *fp) -{ - char buffer[BUFSIZ], *ptr; - - int found = 0; - - /* UML vm wont have "cpu MHz" or "cache size" fields */ - info->mhz = 0; - info->cache_size = 0; - -#ifdef __powerpc64__ - SIGAR_SSTRCPY(info->vendor, "IBM"); -#endif - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - switch (*ptr) { - case 'p': /* processor : 0 */ - if (strnEQ(ptr, "processor", 9)) { - found = 1; - } - break; - case 'v': - /* "vendor_id" or "vendor" */ - if (strnEQ(ptr, "vendor", 6)) { - cpu_info_strcpy(ptr, info->vendor, sizeof(info->vendor)); - if (strEQ(info->vendor, "GenuineIntel")) { - SIGAR_SSTRCPY(info->vendor, "Intel"); - } - else if (strEQ(info->vendor, "AuthenticAMD")) { - SIGAR_SSTRCPY(info->vendor, "AMD"); - } - } - break; - case 'f': - if (strnEQ(ptr, "family", 6)) { - /* IA64 version of "model name" */ - cpu_info_strcpy(ptr, info->model, sizeof(info->model)); - sigar_cpu_model_adjust(sigar, info); - } - break; - case 'm': - if (strnEQ(ptr, "model name", 10)) { - cpu_info_strcpy(ptr, info->model, sizeof(info->model)); - sigar_cpu_model_adjust(sigar, info); - } - break; - case 'c': - if (strnEQ(ptr, "cpu MHz", 7)) { - ptr = cpu_info_strval(ptr); - info->mhz = atoi(ptr); - } - else if (strnEQ(ptr, "cache size", 10)) { - ptr = cpu_info_strval(ptr); - info->cache_size = sigar_strtoul(ptr); - } -#ifdef __powerpc64__ - /* each /proc/cpuinfo entry looks like so: - * processor : 0 - * cpu : POWER5 (gr) - * clock : 1656.392000MHz - * revision : 2.2 - */ - else if (strnEQ(ptr, "clock", 5)) { - ptr = cpu_info_strval(ptr); - info->mhz = atoi(ptr); - } - else if (strnEQ(ptr, "cpu", 3)) { - cpu_info_strcpy(ptr, info->model, sizeof(info->model)); - - if ((ptr = strchr(info->model, ' '))) { - /* "POWER5 (gr)" -> "POWER5" */ - *ptr = '\0'; - } - } -#endif - break; - /* lone \n means end of info for this processor */ - case '\n': - return found; - } - } - - return found; -} - -/* /proc/cpuinfo MHz will change w/ AMD + PowerNow */ -static void get_cpuinfo_max_freq(sigar_cpu_info_t *cpu_info, int num) -{ - int status; - char max_freq[PATH_MAX]; - snprintf(max_freq, sizeof(max_freq), - "/sys/devices/system/cpu/cpu%d" - "/cpufreq/cpuinfo_max_freq", num); - - status = - sigar_file2str(max_freq, max_freq, sizeof(max_freq)-1); - - if (status == SIGAR_OK) { - cpu_info->mhz_max = atoi(max_freq) / 1000; - } -} - -static void get_cpuinfo_min_freq(sigar_cpu_info_t *cpu_info, int num) -{ - int status; - char min_freq[PATH_MAX]; - snprintf(min_freq, sizeof(min_freq), - "/sys/devices/system/cpu/cpu%d" - "/cpufreq/cpuinfo_min_freq", num); - - status = - sigar_file2str(min_freq, min_freq, sizeof(min_freq)-1); - - if (status == SIGAR_OK) { - cpu_info->mhz_min = atoi(min_freq) / 1000; - } -} - -int sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - FILE *fp; - int core_rollup = sigar_cpu_core_rollup(sigar), i=0; - - if (!(fp = fopen(PROC_FS_ROOT "cpuinfo", "r"))) { - return errno; - } - - (void)sigar_cpu_total_count(sigar); - sigar_cpu_info_list_create(cpu_infos); - - while (get_cpu_info(sigar, &cpu_infos->data[cpu_infos->number], fp)) { - sigar_cpu_info_t *info; - - if (core_rollup && (i++ % sigar->lcpu)) { - continue; /* fold logical processors */ - } - - info = &cpu_infos->data[cpu_infos->number]; - get_cpuinfo_max_freq(info, cpu_infos->number); - get_cpuinfo_min_freq(info, cpu_infos->number); - - info->total_cores = sigar->ncpu; - info->cores_per_socket = sigar->lcpu; - info->total_sockets = sigar_cpu_socket_count(sigar); - - ++cpu_infos->number; - SIGAR_CPU_INFO_LIST_GROW(cpu_infos); - } - - fclose(fp); - - return SIGAR_OK; -} - -static SIGAR_INLINE unsigned int hex2int(const char *x, int len) -{ - int i; - unsigned int j; - - for (i=0, j=0; isize = routelist->number = 0; - - if (!(fp = fopen(PROC_FS_ROOT "net/route", "r"))) { - return errno; - } - - sigar_net_route_list_create(routelist); - - (void)fgets(buffer, sizeof(buffer), fp); /* skip header */ - while (fgets(buffer, sizeof(buffer), fp)) { - int num; - - SIGAR_NET_ROUTE_LIST_GROW(routelist); - route = &routelist->data[routelist->number++]; - - /* XXX rid sscanf */ - num = sscanf(buffer, ROUTE_FMT, - route->ifname, net_addr, gate_addr, - &flags, &route->refcnt, &route->use, - &route->metric, mask_addr, - &route->mtu, &route->window, &route->irtt); - - if ((num < 10) || !(flags & RTF_UP)) { - --routelist->number; - continue; - } - - route->flags = flags; - - sigar_net_address_set(route->destination, hex2int(net_addr, HEX_ENT_LEN)); - sigar_net_address_set(route->gateway, hex2int(gate_addr, HEX_ENT_LEN)); - sigar_net_address_set(route->mask, hex2int(mask_addr, HEX_ENT_LEN)); - } - - fclose(fp); - - return SIGAR_OK; -} - -int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - int found = 0; - char buffer[BUFSIZ]; - FILE *fp = fopen(PROC_FS_ROOT "net/dev", "r"); - - if (!fp) { - return errno; - } - - /* skip header */ - fgets(buffer, sizeof(buffer), fp); - fgets(buffer, sizeof(buffer), fp); - - while (fgets(buffer, sizeof(buffer), fp)) { - char *ptr, *dev; - - dev = buffer; - while (isspace(*dev)) { - dev++; - } - - if (!(ptr = strchr(dev, ':'))) { - continue; - } - - *ptr++ = 0; - - if (!strEQ(dev, name)) { - continue; - } - - found = 1; - ifstat->rx_bytes = sigar_strtoull(ptr); - ifstat->rx_packets = sigar_strtoull(ptr); - ifstat->rx_errors = sigar_strtoull(ptr); - ifstat->rx_dropped = sigar_strtoull(ptr); - ifstat->rx_overruns = sigar_strtoull(ptr); - ifstat->rx_frame = sigar_strtoull(ptr); - - /* skip: compressed multicast */ - ptr = sigar_skip_multiple_token(ptr, 2); - - ifstat->tx_bytes = sigar_strtoull(ptr); - ifstat->tx_packets = sigar_strtoull(ptr); - ifstat->tx_errors = sigar_strtoull(ptr); - ifstat->tx_dropped = sigar_strtoull(ptr); - ifstat->tx_overruns = sigar_strtoull(ptr); - ifstat->tx_collisions = sigar_strtoull(ptr); - ifstat->tx_carrier = sigar_strtoull(ptr); - - ifstat->speed = SIGAR_FIELD_NOTIMPL; - - break; - } - - fclose(fp); - - return found ? SIGAR_OK : ENXIO; -} - -static SIGAR_INLINE void convert_hex_address(sigar_net_address_t *address, - char *ptr, int len) -{ - if (len > HEX_ENT_LEN) { - int i; - for (i=0; i<=3; i++, ptr+=HEX_ENT_LEN) { - address->addr.in6[i] = hex2int(ptr, HEX_ENT_LEN); - } - - address->family = SIGAR_AF_INET6; - } - else { - address->addr.in = - (len == HEX_ENT_LEN) ? hex2int(ptr, HEX_ENT_LEN) : 0; - - address->family = SIGAR_AF_INET; - } -} - -typedef struct { - sigar_net_connection_list_t *connlist; - sigar_net_connection_t *conn; - unsigned long port; -} net_conn_getter_t; - -static int proc_net_walker(sigar_net_connection_walker_t *walker, - sigar_net_connection_t *conn) -{ - net_conn_getter_t *getter = - (net_conn_getter_t *)walker->data; - - if (getter->connlist) { - SIGAR_NET_CONNLIST_GROW(getter->connlist); - memcpy(&getter->connlist->data[getter->connlist->number++], - conn, sizeof(*conn)); - } - else { - if ((getter->port == conn->local_port) && - (conn->remote_port == 0)) - { - memcpy(getter->conn, conn, sizeof(*conn)); - return !SIGAR_OK; /* break loop */ - } - } - - return SIGAR_OK; /* continue loop */ -} - -#define SKIP_WHILE(p, c) while (*p == c) p++ -#define SKIP_PAST(p, c) \ - while(*p && (*p != c)) p++; \ - SKIP_WHILE(p, c) - -typedef struct { - FILE *fp; - int (*close)(FILE *); -} xproc_t; - -static FILE *xproc_open(const char *command, xproc_t *xproc) -{ - struct stat sb; - if (stat(command, &sb) == 0) { - if (sb.st_mode & S_IXUSR) { - /* executable script for testing large - * conn table where we can sleep() to better - * simulate /proc/net/tcp behavior - */ - xproc->fp = popen(command, "r"); - xproc->close = pclose; - } - else { - xproc->fp = fopen(command, "r"); - xproc->close = fclose; - } - return xproc->fp; - } - else { - return NULL; - } -} - -static int proc_net_read(sigar_net_connection_walker_t *walker, - const char *fname, - int type) -{ - FILE *fp = NULL; - char buffer[8192]; - sigar_t *sigar = walker->sigar; - char *ptr = sigar->proc_net; - int flags = walker->flags; - xproc_t xproc = { NULL, fclose }; - - if (ptr) { - snprintf(buffer, sizeof(buffer), - "%s/%s", ptr, - fname + sizeof(PROC_FS_ROOT)-1); - - if ((fp = xproc_open(buffer, &xproc))) { - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[proc_net] using %s", - buffer); - } - } - else if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[proc_net] cannot open %s", - buffer); - } - } - - if (!(fp || (fp = fopen(fname, "r")))) { - return errno; - } - - fgets(buffer, sizeof(buffer), fp); /* skip header */ - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - sigar_net_connection_t conn; - char *laddr, *raddr; - int laddr_len=0, raddr_len=0; - int more; - - /* skip leading space */ - SKIP_WHILE(ptr, ' '); - - /* skip "%d: " */ - SKIP_PAST(ptr, ' '); - - laddr = ptr; - while (*ptr && (*ptr != ':')) { - laddr_len++; - ptr++; - } - SKIP_WHILE(ptr, ':'); - - conn.local_port = (strtoul(ptr, &ptr, 16) & 0xffff); - - SKIP_WHILE(ptr, ' '); - - raddr = ptr; - while (*ptr && (*ptr != ':')) { - raddr_len++; - ptr++; - } - SKIP_WHILE(ptr, ':'); - - conn.remote_port = (strtoul(ptr, &ptr, 16) & 0xffff); - - SKIP_WHILE(ptr, ' '); - - if (!((conn.remote_port && (flags & SIGAR_NETCONN_CLIENT)) || - (!conn.remote_port && (flags & SIGAR_NETCONN_SERVER)))) - { - continue; - } - - conn.type = type; - - convert_hex_address(&conn.local_address, - laddr, laddr_len); - - convert_hex_address(&conn.remote_address, - raddr, raddr_len); - - /* SIGAR_TCP_* currently matches TCP_* in linux/tcp.h */ - conn.state = hex2int(ptr, 2); - ptr += 2; - SKIP_WHILE(ptr, ' '); - - conn.send_queue = hex2int(ptr, HEX_ENT_LEN); - ptr += HEX_ENT_LEN+1; /* tx + ':' */; - - conn.receive_queue = hex2int(ptr, HEX_ENT_LEN); - ptr += HEX_ENT_LEN; - SKIP_WHILE(ptr, ' '); - - SKIP_PAST(ptr, ' '); /* tr:tm->whem */ - SKIP_PAST(ptr, ' '); /* retrnsmt */ - - conn.uid = sigar_strtoul(ptr); - - SKIP_WHILE(ptr, ' '); - SKIP_PAST(ptr, ' '); /* timeout */ - - conn.inode = sigar_strtoul(ptr); - - more = walker->add_connection(walker, &conn); - if (more != SIGAR_OK) { - xproc.close(fp); - return SIGAR_OK; - } - } - - xproc.close(fp); - - return SIGAR_OK; -} - -int sigar_net_connection_walk(sigar_net_connection_walker_t *walker) -{ - int flags = walker->flags; - int status; - - if (flags & SIGAR_NETCONN_TCP) { - status = proc_net_read(walker, - PROC_FS_ROOT "net/tcp", - SIGAR_NETCONN_TCP); - - if (status != SIGAR_OK) { - return status; - } - - status = proc_net_read(walker, - PROC_FS_ROOT "net/tcp6", - SIGAR_NETCONN_TCP); - - if (!((status == SIGAR_OK) || (status == ENOENT))) { - return status; - } - } - - if (flags & SIGAR_NETCONN_UDP) { - status = proc_net_read(walker, - PROC_FS_ROOT "net/udp", - SIGAR_NETCONN_UDP); - - if (status != SIGAR_OK) { - return status; - } - - status = proc_net_read(walker, - PROC_FS_ROOT "net/udp6", - SIGAR_NETCONN_UDP); - - if (!((status == SIGAR_OK) || (status == ENOENT))) { - return status; - } - } - - if (flags & SIGAR_NETCONN_RAW) { - status = proc_net_read(walker, - PROC_FS_ROOT "net/raw", - SIGAR_NETCONN_RAW); - - if (status != SIGAR_OK) { - return status; - } - - status = proc_net_read(walker, - PROC_FS_ROOT "net/raw6", - SIGAR_NETCONN_RAW); - - if (!((status == SIGAR_OK) || (status == ENOENT))) { - return status; - } - } - - /* XXX /proc/net/unix */ - - return SIGAR_OK; -} - -int sigar_net_connection_list_get(sigar_t *sigar, - sigar_net_connection_list_t *connlist, - int flags) -{ - int status; - sigar_net_connection_walker_t walker; - net_conn_getter_t getter; - - sigar_net_connection_list_create(connlist); - - getter.conn = NULL; - getter.connlist = connlist; - - walker.sigar = sigar; - walker.flags = flags; - walker.data = &getter; - walker.add_connection = proc_net_walker; - - status = sigar_net_connection_walk(&walker); - - if (status != SIGAR_OK) { - sigar_net_connection_list_destroy(sigar, connlist); - } - - return status; -} - -static int sigar_net_connection_get(sigar_t *sigar, - sigar_net_connection_t *netconn, - unsigned long port, - int flags) -{ - int status; - sigar_net_connection_walker_t walker; - net_conn_getter_t getter; - - getter.conn = netconn; - getter.connlist = NULL; - getter.port = port; - - walker.sigar = sigar; - walker.flags = flags; - walker.data = &getter; - walker.add_connection = proc_net_walker; - - status = sigar_net_connection_walk(&walker); - - return status; -} - -int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - FILE *fp; - char addr[32+1], ifname[8+1]; - int status = SIGAR_ENOENT; - int idx, prefix, scope, flags; - - if (!(fp = fopen(PROC_FS_ROOT "net/if_inet6", "r"))) { - return errno; - } - - while (fscanf(fp, "%32s %02x %02x %02x %02x %8s\n", - addr, &idx, &prefix, &scope, &flags, ifname) != EOF) - { - if (strEQ(name, ifname)) { - status = SIGAR_OK; - break; - } - } - - fclose(fp); - - if (status == SIGAR_OK) { - int i=0; - unsigned char *addr6 = (unsigned char *)&(ifconfig->address6.addr.in6); - char *ptr = addr; - - for (i=0; i<16; i++, ptr+=2) { - addr6[i] = (unsigned char)hex2int(ptr, 2); - } - - ifconfig->prefix6_length = prefix; - ifconfig->scope6 = scope; - } - - return status; -} - -#define SNMP_TCP_PREFIX "Tcp: " - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp) -{ - FILE *fp; - char buffer[1024], *ptr=buffer; - int status = SIGAR_ENOENT; - - if (!(fp = fopen(PROC_FS_ROOT "net/snmp", "r"))) { - return errno; - } - - while (fgets(buffer, sizeof(buffer), fp)) { - if (strnEQ(buffer, SNMP_TCP_PREFIX, sizeof(SNMP_TCP_PREFIX)-1)) { - if (fgets(buffer, sizeof(buffer), fp)) { - status = SIGAR_OK; - break; - } - } - } - - fclose(fp); - - if (status == SIGAR_OK) { - /* assuming field order, same in 2.2, 2.4 and 2.6 kernels */ - /* Tcp: RtoAlgorithm RtoMin RtoMax MaxConn */ - ptr = sigar_skip_multiple_token(ptr, 5); - tcp->active_opens = sigar_strtoull(ptr); - tcp->passive_opens = sigar_strtoull(ptr); - tcp->attempt_fails = sigar_strtoull(ptr); - tcp->estab_resets = sigar_strtoull(ptr); - tcp->curr_estab = sigar_strtoull(ptr); - tcp->in_segs = sigar_strtoull(ptr); - tcp->out_segs = sigar_strtoull(ptr); - tcp->retrans_segs = sigar_strtoull(ptr); - tcp->in_errs = sigar_strtoull(ptr); - tcp->out_rsts = sigar_strtoull(ptr); - } - - return status; -} - -static int sigar_proc_nfs_gets(char *file, char *tok, - char *buffer, size_t size) -{ - int status = ENOENT; - int len = strlen(tok); - FILE *fp = fopen(file, "r"); - - if (!fp) { - return SIGAR_ENOTIMPL; - } - - while (fgets(buffer, size, fp)) { - if (strnEQ(buffer, tok, len)) { - status = SIGAR_OK; - break; - } - } - - fclose(fp); - - return status; -} - -static int sigar_nfs_v2_get(char *file, sigar_nfs_v2_t *nfs) -{ - char buffer[BUFSIZ], *ptr=buffer; - int status = - sigar_proc_nfs_gets(file, - "proc2", buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - ptr = sigar_skip_multiple_token(ptr, 2); - - nfs->null = sigar_strtoull(ptr); - nfs->getattr = sigar_strtoull(ptr); - nfs->setattr = sigar_strtoull(ptr); - nfs->root = sigar_strtoull(ptr); - nfs->lookup = sigar_strtoull(ptr); - nfs->readlink = sigar_strtoull(ptr); - nfs->read = sigar_strtoull(ptr); - nfs->writecache = sigar_strtoull(ptr); - nfs->write = sigar_strtoull(ptr); - nfs->create = sigar_strtoull(ptr); - nfs->remove = sigar_strtoull(ptr); - nfs->rename = sigar_strtoull(ptr); - nfs->link = sigar_strtoull(ptr); - nfs->symlink = sigar_strtoull(ptr); - nfs->mkdir = sigar_strtoull(ptr); - nfs->rmdir = sigar_strtoull(ptr); - nfs->readdir = sigar_strtoull(ptr); - nfs->fsstat = sigar_strtoull(ptr); - - return SIGAR_OK; -} - -int sigar_nfs_client_v2_get(sigar_t *sigar, - sigar_nfs_client_v2_t *nfs) -{ - return sigar_nfs_v2_get(PROC_FS_ROOT "net/rpc/nfs", - (sigar_nfs_v2_t *)nfs); -} - -int sigar_nfs_server_v2_get(sigar_t *sigar, - sigar_nfs_server_v2_t *nfs) -{ - return sigar_nfs_v2_get(PROC_FS_ROOT "net/rpc/nfsd", - (sigar_nfs_v2_t *)nfs); -} - -static int sigar_nfs_v3_get(char *file, sigar_nfs_v3_t *nfs) -{ - char buffer[BUFSIZ], *ptr=buffer; - int status = - sigar_proc_nfs_gets(file, - "proc3", buffer, sizeof(buffer)); - - if (status != SIGAR_OK) { - return status; - } - - ptr = sigar_skip_multiple_token(ptr, 2); - - nfs->null = sigar_strtoull(ptr); - nfs->getattr = sigar_strtoull(ptr); - nfs->setattr = sigar_strtoull(ptr); - nfs->lookup = sigar_strtoull(ptr); - nfs->access = sigar_strtoull(ptr); - nfs->readlink = sigar_strtoull(ptr); - nfs->read = sigar_strtoull(ptr); - nfs->write = sigar_strtoull(ptr); - nfs->create = sigar_strtoull(ptr); - nfs->mkdir = sigar_strtoull(ptr); - nfs->symlink = sigar_strtoull(ptr); - nfs->mknod = sigar_strtoull(ptr); - nfs->remove = sigar_strtoull(ptr); - nfs->rmdir = sigar_strtoull(ptr); - nfs->rename = sigar_strtoull(ptr); - nfs->link = sigar_strtoull(ptr); - nfs->readdir = sigar_strtoull(ptr); - nfs->readdirplus = sigar_strtoull(ptr); - nfs->fsstat = sigar_strtoull(ptr); - nfs->fsinfo = sigar_strtoull(ptr); - nfs->pathconf = sigar_strtoull(ptr); - nfs->commit = sigar_strtoull(ptr); - - return SIGAR_OK; -} - -int sigar_nfs_client_v3_get(sigar_t *sigar, - sigar_nfs_client_v3_t *nfs) -{ - return sigar_nfs_v3_get(PROC_FS_ROOT "net/rpc/nfs", - (sigar_nfs_v3_t *)nfs); -} - -int sigar_nfs_server_v3_get(sigar_t *sigar, - sigar_nfs_server_v3_t *nfs) -{ - return sigar_nfs_v3_get(PROC_FS_ROOT "net/rpc/nfsd", - (sigar_nfs_v3_t *)nfs); -} - -#include - -static char *get_hw_type(int type) -{ - switch (type) { - case ARPHRD_AX25: - return "ax25"; - case ARPHRD_ECONET: - return "ec"; - case ARPHRD_ETHER: - return "ether"; - case ARPHRD_FDDI: - return "fddi"; - case ARPHRD_DLCI: - return "dlci"; - case ARPHRD_FRAD: - return "frad"; - case ARPHRD_HDLC: - return "hdlc"; - case ARPHRD_LAPB: - return "lapb"; - case ARPHRD_HIPPI: - return "hippi"; - case ARPHRD_IRDA: - return "irda"; - case ARPHRD_LOOPBACK: - return "loop"; - case ARPHRD_NETROM: - return "netrom"; - case ARPHRD_PPP: - return "ppp"; - case ARPHRD_ROSE: - return "rose"; - case ARPHRD_SIT: - return "sit"; - case ARPHRD_SLIP: - return "slip"; - case ARPHRD_CSLIP: - return "cslip"; - case ARPHRD_SLIP6: - return "slip6"; - case ARPHRD_CSLIP6: - return "cslip6"; - case ARPHRD_ADAPT: - return "adaptive"; - case ARPHRD_IEEE802: - return "tr"; - case ARPHRD_IEEE802_TR: - return "tr"; - case ARPHRD_TUNNEL: - return "tunnel"; - case ARPHRD_X25: - return "x25"; - default: - return "unknown"; - } -} - -int sigar_arp_list_get(sigar_t *sigar, - sigar_arp_list_t *arplist) -{ - FILE *fp; - char buffer[1024]; - char net_addr[128], hwaddr[128], mask_addr[128]; - int flags, type, status; - sigar_arp_t *arp; - - arplist->size = arplist->number = 0; - - if (!(fp = fopen(PROC_FS_ROOT "net/arp", "r"))) { - return errno; - } - - sigar_arp_list_create(arplist); - - (void)fgets(buffer, sizeof(buffer), fp); /* skip header */ - while (fgets(buffer, sizeof(buffer), fp)) { - int num; - - SIGAR_ARP_LIST_GROW(arplist); - arp = &arplist->data[arplist->number++]; - - /* XXX rid sscanf */ - num = sscanf(buffer, "%128s 0x%x 0x%x %128s %128s %16s", - net_addr, &type, &flags, - hwaddr, mask_addr, arp->ifname); - - if (num < 6) { - --arplist->number; - continue; - } - - arp->flags = flags; - status = inet_pton(AF_INET, net_addr, &arp->address.addr); - if (status > 0) { - arp->address.family = SIGAR_AF_INET; - } - else if ((status = inet_pton(AF_INET6, net_addr, &arp->address.addr)) > 0) { - arp->address.family = SIGAR_AF_INET6; - } - else { - sigar_log_printf(sigar, SIGAR_LOG_WARN, - "[arp] failed to parse address='%s' (%s)\n", net_addr, - ((status == 0) ? "Invalid format" : sigar_strerror(sigar, errno))); - --arplist->number; - continue; - } - - num = sscanf(hwaddr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &arp->hwaddr.addr.mac[0], - &arp->hwaddr.addr.mac[1], - &arp->hwaddr.addr.mac[2], - &arp->hwaddr.addr.mac[3], - &arp->hwaddr.addr.mac[4], - &arp->hwaddr.addr.mac[5]); - if (num < 6) { - sigar_log_printf(sigar, SIGAR_LOG_WARN, - "[arp] failed to parse hwaddr='%s' (%s)\n", hwaddr); - --arplist->number; - continue; - } - arp->hwaddr.family = SIGAR_AF_LINK; - - SIGAR_SSTRCPY(arp->type, get_hw_type(type)); - } - - fclose(fp); - - return SIGAR_OK; -} - -int sigar_proc_port_get(sigar_t *sigar, int protocol, - unsigned long port, sigar_pid_t *pid) -{ - int status; - sigar_net_connection_t netconn; - DIR *dirp; - struct dirent *ent, dbuf; - - SIGAR_ZERO(&netconn); - *pid = 0; - - status = sigar_net_connection_get(sigar, &netconn, port, - SIGAR_NETCONN_SERVER|protocol); - - if (status != SIGAR_OK) { - return status; - } - - if (netconn.local_port != port) { - return SIGAR_OK; /* XXX or ENOENT? */ - } - - if (!(dirp = opendir(PROCP_FS_ROOT))) { - return errno; - } - - while (readdir_r(dirp, &dbuf, &ent) == 0) { - DIR *fd_dirp; - struct dirent *fd_ent, fd_dbuf; - struct stat sb; - char fd_name[BUFSIZ], pid_name[BUFSIZ]; - int len, slen; - - if (ent == NULL) { - break; - } - - if (!sigar_isdigit(*ent->d_name)) { - continue; - } - - /* sprintf(pid_name, "/proc/%s", ent->d_name) */ - memcpy(&pid_name[0], PROCP_FS_ROOT, SSTRLEN(PROCP_FS_ROOT)); - len = SSTRLEN(PROCP_FS_ROOT); - pid_name[len++] = '/'; - - slen = strlen(ent->d_name); - memcpy(&pid_name[len], ent->d_name, slen); - len += slen; - pid_name[len] = '\0'; - - if (stat(pid_name, &sb) < 0) { - continue; - } - if (sb.st_uid != netconn.uid) { - continue; - } - - /* sprintf(fd_name, "%s/fd", pid_name) */ - memcpy(&fd_name[0], pid_name, len); - memcpy(&fd_name[len], "/fd", 3); - fd_name[len+=3] = '\0'; - - if (!(fd_dirp = opendir(fd_name))) { - continue; - } - - while (readdir_r(fd_dirp, &fd_dbuf, &fd_ent) == 0) { - char fd_ent_name[BUFSIZ]; - - if (fd_ent == NULL) { - break; - } - - if (!sigar_isdigit(*fd_ent->d_name)) { - continue; - } - - /* sprintf(fd_ent_name, "%s/%s", fd_name, fd_ent->d_name) */ - slen = strlen(fd_ent->d_name); - memcpy(&fd_ent_name[0], fd_name, len); - fd_ent_name[len] = '/'; - memcpy(&fd_ent_name[len+1], fd_ent->d_name, slen); - fd_ent_name[len+1+slen] = '\0'; - - if (stat(fd_ent_name, &sb) < 0) { - continue; - } - - if (sb.st_ino == netconn.inode) { - closedir(fd_dirp); - closedir(dirp); - *pid = strtoul(ent->d_name, NULL, 10); - return SIGAR_OK; - } - - } - - closedir(fd_dirp); - } - - closedir(dirp); - - return SIGAR_OK; -} - -static void generic_vendor_parse(char *line, sigar_sys_info_t *info) -{ - char *ptr; - int len = 0; - - while (*line) { - SIGAR_SKIP_SPACE(line); - if (!isdigit(*line)) { - ++line; - continue; - } - - ptr = line; - while ((isdigit(*ptr) || (*ptr == '.'))) { - ++ptr; - ++len; - } - - if (len) { - /* sanity check */ - if (len > sizeof(info->vendor_version)) { - continue; - } - memcpy(info->vendor_version, line, len);/*XXX*/ - info->vendor_version[len] = '\0'; - return; - } - } -} - -static void redhat_vendor_parse(char *line, sigar_sys_info_t *info) -{ - char *start, *end; - - generic_vendor_parse(line, info); /* super.parse */ - - if ((start = strchr(line, '('))) { - ++start; - if ((end = strchr(start, ')'))) { - int len = end-start; - memcpy(info->vendor_code_name, start, len);/*XXX*/ - info->vendor_code_name[len] = '\0'; - } - } - -#define RHEL_PREFIX "Red Hat Enterprise Linux " -#define CENTOS_VENDOR "CentOS" -#define SL_VENDOR "Scientific Linux" - - if (strnEQ(line, RHEL_PREFIX, sizeof(RHEL_PREFIX)-1)) { - snprintf(info->vendor_version, - sizeof(info->vendor_version), - "Enterprise Linux %c", - info->vendor_version[0]); - } - else if (strnEQ(line, CENTOS_VENDOR, sizeof(CENTOS_VENDOR)-1)) { - SIGAR_SSTRCPY(info->vendor, CENTOS_VENDOR); - } - else if (strnEQ(line, SL_VENDOR, sizeof(SL_VENDOR)-1)) { - SIGAR_SSTRCPY(info->vendor, SL_VENDOR); - } -} - -#define is_quote(c) ((c == '\'') || (c == '"')) - -static void kv_parse(char *data, sigar_sys_info_t *info, - void (*func)(sigar_sys_info_t *, char *, char *)) -{ - char *ptr = data; - int len = strlen(data); - char *end = data+len; - - while (ptr < end) { - char *val = strchr(ptr, '='); - int klen, vlen; - char key[256], *ix; - - if (!val) { - continue; - } - klen = val - ptr; - SIGAR_SSTRCPY(key, ptr); - key[klen] = '\0'; - ++val; - - if ((ix = strchr(val, '\n'))) { - *ix = '\0'; - } - vlen = strlen(val); - if (is_quote(*val)) { - if (is_quote(val[vlen-1])) { - val[vlen-1] = '\0'; - } - ++val; - } - - func(info, key, val); - - ptr += (klen + 1 + vlen + 1); - } -} - -static void lsb_parse(sigar_sys_info_t *info, - char *key, char *val) -{ - if (strEQ(key, "DISTRIB_ID")) { - SIGAR_SSTRCPY(info->vendor, val); - } - else if (strEQ(key, "DISTRIB_RELEASE")) { - SIGAR_SSTRCPY(info->vendor_version, val); - } - else if (strEQ(key, "DISTRIB_CODENAME")) { - SIGAR_SSTRCPY(info->vendor_code_name, val); - } -} - -static void lsb_vendor_parse(char *data, sigar_sys_info_t *info) -{ - kv_parse(data, info, lsb_parse); -} - -static void xen_parse(sigar_sys_info_t *info, - char *key, char *val) -{ - if (strEQ(key, "PRODUCT_VERSION")) { - SIGAR_SSTRCPY(info->vendor_version, val); - } - else if (strEQ(key, "KERNEL_VERSION")) { - SIGAR_SSTRCPY(info->version, val); - } -} - -static void xen_vendor_parse(char *data, sigar_sys_info_t *info) -{ - kv_parse(data, info, xen_parse); - - snprintf(info->description, - sizeof(info->description), - "XenServer %s", - info->vendor_version); -} - -typedef struct { - const char *name; - const char *file; - void (*parse)(char *, sigar_sys_info_t *); -} linux_vendor_info_t; - -static linux_vendor_info_t linux_vendors[] = { - { "Fedora", "/etc/fedora-release", NULL }, - { "SuSE", "/etc/SuSE-release", NULL }, - { "Gentoo", "/etc/gentoo-release", NULL }, - { "Slackware", "/etc/slackware-version", NULL }, - { "Mandrake", "/etc/mandrake-release", NULL }, - { "VMware", "/proc/vmware/version", NULL }, - { "XenSource", "/etc/xensource-inventory", xen_vendor_parse }, - { "Red Hat", "/etc/redhat-release", redhat_vendor_parse }, - { "lsb", "/etc/lsb-release", lsb_vendor_parse }, - { "Debian", "/etc/debian_version", NULL }, - { NULL } -}; - -static int get_linux_vendor_info(sigar_sys_info_t *info) -{ - int i, status = ENOENT; - /* env vars for testing */ - const char *release_file = getenv("SIGAR_OS_RELEASE_FILE"); - const char *vendor_name = getenv("SIGAR_OS_VENDOR_NAME"); - char buffer[8192], *data; - linux_vendor_info_t *vendor = NULL; - - for (i=0; linux_vendors[i].name; i++) { - struct stat sb; - vendor = &linux_vendors[i]; - - if (release_file && vendor_name) { - if (!strEQ(vendor->name, vendor_name)) { - continue; - } - } - else { - if (stat(vendor->file, &sb) < 0) { - continue; - } - release_file = vendor->file; - } - - status = - sigar_file2str(release_file, buffer, sizeof(buffer)-1); - - break; - } - - if (status != SIGAR_OK) { - return status; - } - - data = buffer; - - SIGAR_SSTRCPY(info->vendor, vendor->name); - - if (vendor->parse) { - vendor->parse(data, info); - } - else { - generic_vendor_parse(data, info); - } - - if (info->description[0] == '\0') { - snprintf(info->description, - sizeof(info->description), - "%s %s", - info->vendor, info->vendor_version); - } - - return SIGAR_OK; -} - -int sigar_os_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sysinfo) -{ - - get_linux_vendor_info(sysinfo); - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/os/linux/sigar_os.h b/vendor/sigar/src/os/linux/sigar_os.h deleted file mode 100644 index 29a2ba323..000000000 --- a/vendor/sigar/src/os/linux/sigar_os.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2004-2008 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_OS_H -#define SIGAR_OS_H - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -typedef struct { - sigar_pid_t pid; - time_t mtime; - sigar_uint64_t vsize; - sigar_uint64_t rss; - sigar_uint64_t minor_faults; - sigar_uint64_t major_faults; - sigar_uint64_t ppid; - int tty; - int priority; - int nice; - sigar_uint64_t start_time; - sigar_uint64_t utime; - sigar_uint64_t stime; - char name[SIGAR_PROC_NAME_LEN]; - char state; - int processor; -} linux_proc_stat_t; - -typedef enum { - IOSTAT_NONE, - IOSTAT_PARTITIONS, /* 2.4 */ - IOSTAT_DISKSTATS, /* 2.6 */ - IOSTAT_SYS /* 2.6 */ -} linux_iostat_e; - -struct sigar_t { - SIGAR_T_BASE; - int pagesize; - int ram; - int proc_signal_offset; - linux_proc_stat_t last_proc_stat; - int lcpu; - linux_iostat_e iostat; - char *proc_net; - /* Native POSIX Thread Library 2.6+ kernel */ - int has_nptl; -}; - -#define HAVE_STRERROR_R -#ifndef __USE_XOPEN2K -/* use gnu version of strerror_r */ -#define HAVE_STRERROR_R_GLIBC -#endif -#define HAVE_READDIR_R -#define HAVE_GETPWNAM_R -#define HAVE_GETPWUID_R -#define HAVE_GETGRGID_R - -#endif /* SIGAR_OS_H */ diff --git a/vendor/sigar/src/os/solaris/get_mib2.c b/vendor/sigar/src/os/solaris/get_mib2.c deleted file mode 100644 index fbcadc632..000000000 --- a/vendor/sigar/src/os/solaris/get_mib2.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * get_mib2() -- get MIB2 information from Solaris 2.[3-7] kernel - * - * V. Abell - * Purdue University Computing Center - */ - - -/* - * Copyright 1995 Purdue Research Foundation, West Lafayette, Indiana - * 47907. All rights reserved. - * - * Written by Victor A. Abell - * - * This software is not subject to any license of the American Telephone - * and Telegraph Company or the Regents of the University of California. - * - * Permission is granted to anyone to use this software for any purpose on - * any computer system, and to alter it and redistribute it freely, subject - * to the following restrictions: - * - * 1. Neither Victor A Abell nor Purdue University are responsible for - * any consequences of the use of this software. - * - * 2. The origin of this software must not be misrepresented, either by - * explicit claim or by omission. Credit to Victor A. Abell and Purdue - * University must appear in documentation and sources. - * - * 3. Altered versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 4. This notice may not be removed or altered. - */ - -/* - * Altered for sigar: - * - remove static stuff to make thread-safe by Doug MacEachern (3/11/05) - */ - -#if 0 /*ndef lint -Wall -Werror*/ -static char copyright[] = -"@(#) Copyright 1995 Purdue Research Foundation.\nAll rights reserved.\n"; -#endif - -#include "get_mib2.h" - -#include -#include -#include -#include -#include -#include - -#ifdef DMALLOC -#include -#endif - -/* - * close_mib2() - close MIB2 access - * - * return: - * - * exit = GET_MIB2_OK if close succeeded - * GET_MIB2_* is the error code. - */ - -int -close_mib2(solaris_mib2_t *mib2) -{ - if (mib2->sd < 0) { - (void) strcpy(mib2->errmsg, "close_mib2: socket not open"); - return(GET_MIB2_ERR_NOTOPEN); - } - if (close(mib2->sd)) { - (void) sprintf(mib2->errmsg, "close_mib2: %s", strerror(errno)); - return(GET_MIB2_ERR_CLOSE); - } - mib2->sd = -1; - if (mib2->db_len && mib2->db) { - mib2->db_len = 0; - free((void *)mib2->db); - mib2->db = NULL; - } - if (mib2->smb_len && mib2->smb) { - mib2->smb_len = 0; - free((void *)mib2->smb); - mib2->smb = NULL; - } - return(GET_MIB2_OK); -} - - -/* - * get_mib2() - get MIB2 data - * - * return: - * - * exit = GET_MIB2_OK if get succeeded, and: - * *opt = opthdr structure address - * *data = data buffer address - * *datalen = size of data buffer - * GET_MIB2_* is the error code for failure. - */ - -int -get_mib2(solaris_mib2_t *mib2, - struct opthdr **opt, - char **data, - int *datalen) -{ - struct strbuf d; /* streams data buffer */ - int err; /* error code */ - int f; /* flags */ - int rc; /* reply code */ - - /* - * If MIB2 access isn't open, open it and issue the preliminary stream - * messages. - */ - if (mib2->sd < 0) { - /* - * Open access. Return on error. - */ - if ((err = open_mib2(mib2))) { - return(err); - } - /* - * Set up message request and option. - */ - mib2->req = (struct T_optmgmt_req *)mib2->smb; - mib2->op = (struct opthdr *)&mib2->smb[sizeof(struct T_optmgmt_req)]; - mib2->req->PRIM_type = T_OPTMGMT_REQ; - mib2->req->OPT_offset = sizeof(struct T_optmgmt_req); - mib2->req->OPT_length = sizeof(struct opthdr); - -#if defined(MI_T_CURRENT) - mib2->req->MGMT_flags = MI_T_CURRENT; -#else /* !defined(MI_T_CURRENT) */ -# if defined(T_CURRENT) - mib2->req->MGMT_flags = T_CURRENT; -# else /* !defined(T_CURRENT) */ -#error "Neither MI_T_CURRENT nor T_CURRENT are defined." -# endif /* defined(T_CURRENT) */ -#endif /* defined(MI_T_CURRENT) */ - - mib2->op->level = MIB2_IP; - mib2->op->name = mib2->op->len = 0; - mib2->ctlbuf.buf = mib2->smb; - mib2->ctlbuf.len = mib2->req->OPT_offset + mib2->req->OPT_length; - /* - * Put the message. - */ - if (putmsg(mib2->sd, &mib2->ctlbuf, (struct strbuf *)NULL, 0) == -1) { - (void) sprintf(mib2->errmsg, - "get_mib2: putmsg request: %s", strerror(errno)); - return(GET_MIB2_ERR_PUTMSG); - } - /* - * Set up to process replies. - */ - mib2->op_ack = (struct T_optmgmt_ack *)mib2->smb; - mib2->ctlbuf.maxlen = mib2->smb_len; - mib2->err_ack = (struct T_error_ack *)mib2->smb; - mib2->op = (struct opthdr *)&mib2->smb[sizeof(struct T_optmgmt_ack)]; - } - /* - * Get the next (first) reply message. - */ - f = 0; - if ((rc = getmsg(mib2->sd, &mib2->ctlbuf, NULL, &f)) < 0) { - (void) sprintf(mib2->errmsg, "get_mib2: getmsg(reply): %s", - strerror(errno)); - return(GET_MIB2_ERR_GETMSGR); - } - /* - * Check for end of data. - */ - if (rc == 0 - && mib2->ctlbuf.len >= sizeof(struct T_optmgmt_ack) - && mib2->op_ack->PRIM_type == T_OPTMGMT_ACK - && mib2->op_ack->MGMT_flags == T_SUCCESS - && mib2->op->len == 0) - { - err = close_mib2(mib2); - if (err) { - return(err); - } - return(GET_MIB2_EOD); - } - /* - * Check for error. - */ - if (mib2->ctlbuf.len >= sizeof(struct T_error_ack) - && mib2->err_ack->PRIM_type == T_ERROR_ACK) - { - (void) sprintf(mib2->errmsg, - "get_mib2: T_ERROR_ACK: len=%d, TLI=%#x, UNIX=%#x", - mib2->ctlbuf.len, - (int)mib2->err_ack->TLI_error, - (int)mib2->err_ack->UNIX_error); - return(GET_MIB2_ERR_ACK); - } - /* - * Check for no data. - */ - if (rc != MOREDATA - || mib2->ctlbuf.len < sizeof(struct T_optmgmt_ack) - || mib2->op_ack->PRIM_type != T_OPTMGMT_ACK - || mib2->op_ack->MGMT_flags != T_SUCCESS) - { - (void) sprintf(mib2->errmsg, - "get_mib2: T_OPTMGMT_ACK: " - "rc=%d len=%d type=%#x flags=%#x", - rc, mib2->ctlbuf.len, - (int)mib2->op_ack->PRIM_type, - (int)mib2->op_ack->MGMT_flags); - return(GET_MIB2_ERR_NODATA); - } - /* - * Allocate (or enlarge) the data buffer. - */ - if (mib2->op->len >= mib2->db_len) { - mib2->db_len = mib2->op->len; - if (mib2->db == NULL) { - mib2->db = (char *)malloc(mib2->db_len); - } - else { - mib2->db = (char *)realloc(mib2->db, mib2->db_len); - } - if (mib2->db == NULL) { - (void) sprintf(mib2->errmsg, - "get_mib2: no space for %d byte data buffer", - mib2->db_len); - return(GET_MIB2_ERR_NOSPC); - } - } - /* - * Get the data part of the message -- the MIB2 part. - */ - d.maxlen = mib2->op->len; - d.buf = mib2->db; - d.len = 0; - f = 0; - if ((rc = getmsg(mib2->sd, NULL, &d, &f)) < 0) { - (void) sprintf(mib2->errmsg, "get_mib2: getmsg(data): %s", - strerror(errno)); - return(GET_MIB2_ERR_GETMSGD); - } - if (rc) { - (void) sprintf(mib2->errmsg, - "get_mib2: getmsg(data): rc=%d maxlen=%d len=%d: %s", - rc, d.maxlen, d.len, strerror(errno)); - return(GET_MIB2_ERR_GETMSGD); - } - /* - * Compose a successful return. - */ - *opt = mib2->op; - *data = mib2->db; - *datalen = d.len; - return(GET_MIB2_OK); -} - - -/* - * open_mib2() - open access to MIB2 data - * - * return: - * - * exit = GET_MIB2_OK if open succeeded - * GET_MIB2_* is the error code for failure. - */ - -int -open_mib2(solaris_mib2_t *mib2) -{ - /* - * It's an error if the stream device is already open. - */ - if (mib2->sd >= 0) { - (void) strcpy(mib2->errmsg, "open_mib2: MIB2 access already open"); - return(GET_MIB2_ERR_OPEN); - } - /* - * Open the ARP stream device, push TCP and UDP on it. - */ - if ((mib2->sd = open(GET_MIB2_ARPDEV, O_RDWR, 0600)) < 0) { - (void) sprintf(mib2->errmsg, "open_mib2: %s: %s", GET_MIB2_ARPDEV, - strerror(errno)); - return(GET_MIB2_ERR_ARPOPEN); - } - if (ioctl(mib2->sd, I_PUSH, GET_MIB2_TCPSTREAM) == -1) { - (void) sprintf(mib2->errmsg, "open_mib2: push %s: %s", - GET_MIB2_TCPSTREAM, strerror(errno)); - return(GET_MIB2_ERR_TCPPUSH); - } - if (ioctl(mib2->sd, I_PUSH, GET_MIB2_UDPSTREAM) == -1) { - (void) sprintf(mib2->errmsg, "open_mib2: push %s: %s", - GET_MIB2_UDPSTREAM, strerror(errno)); - return(GET_MIB2_ERR_UDPPUSH); - } - /* - * Allocate a stream message buffer. - */ - mib2->smb_len = sizeof(struct opthdr) + sizeof(struct T_optmgmt_req); - if (mib2->smb_len < (sizeof (struct opthdr) + sizeof(struct T_optmgmt_ack))) { - mib2->smb_len = sizeof (struct opthdr) + sizeof(struct T_optmgmt_ack); - } - if (mib2->smb_len < sizeof(struct T_error_ack)) { - mib2->smb_len = sizeof(struct T_error_ack); - } - if ((mib2->smb = (char *)malloc(mib2->smb_len)) == NULL) { - (void) strcpy(mib2->errmsg, - "open_mib2: no space for stream message buffer"); - return(GET_MIB2_ERR_NOSPC); - } - /* - * All is OK. Return that indication. - */ - return(GET_MIB2_OK); -} diff --git a/vendor/sigar/src/os/solaris/get_mib2.h b/vendor/sigar/src/os/solaris/get_mib2.h deleted file mode 100644 index 53116c5c4..000000000 --- a/vendor/sigar/src/os/solaris/get_mib2.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * get_mib2.h -- definitions for the get_mib2() function - * - * V. Abell - * Purdue University Computing Center - */ - - -/* - * Copyright 1995 Purdue Research Foundation, West Lafayette, Indiana - * 47907. All rights reserved. - * - * Written by Victor A. Abell - * - * This software is not subject to any license of the American Telephone - * and Telegraph Company or the Regents of the University of California. - * - * Permission is granted to anyone to use this software for any purpose on - * any computer system, and to alter it and redistribute it freely, subject - * to the following restrictions: - * - * 1. Neither Victor A Abell nor Purdue University are responsible for - * any consequences of the use of this software. - * - * 2. The origin of this software must not be misrepresented, either by - * explicit claim or by omission. Credit to Victor A. Abell and Purdue - * University must appear in documentation and sources. - * - * 3. Altered versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 4. This notice may not be removed or altered. - */ - -/* - * Altered for sigar: - * - remove static stuff to make thread-safe by Doug MacEachern (3/11/05) - */ - -#if !defined(GET_MIB2_H) -#define GET_MIB2_H - - -/* - * Required header files - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * Miscellaneous definitions - */ - -#define GET_MIB2_ARPDEV "/dev/arp" /* ARP stream devi9ce */ -#define GET_MIB2_ERRMSGL 1024 /* ErrMsg buffer length */ -#define GET_MIB2_TCPSTREAM "tcp" /* TCP stream name */ -#define GET_MIB2_UDPSTREAM "udp" /* UDP stream name */ - - -/* - * get_mib2() response codes - * - * -1 End of MIB2 information - * 0 Next MIB2 structure returned - * >0 Error code - */ - -#define GET_MIB2_EOD -1 /* end of data */ -#define GET_MIB2_OK 0 /* function succeeded */ -#define GET_MIB2_ERR_ACK 1 /* getmsg() ACK error received */ -#define GET_MIB2_ERR_ARPOPEN 2 /* error opening ARPDEV */ -#define GET_MIB2_ERR_CLOSE 3 /* MIB2 access close error */ -#define GET_MIB2_ERR_GETMSGD 4 /* error getting message data */ -#define GET_MIB2_ERR_GETMSGR 5 /* error getting message reply */ -#define GET_MIB2_ERR_NODATA 6 /* data expected; not received */ -#define GET_MIB2_ERR_NOSPC 7 /* no malloc() space */ -#define GET_MIB2_ERR_NOTOPEN 8 /* MIB2 access not open */ -#define GET_MIB2_ERR_OPEN 9 /* MIB2 access open error */ -#define GET_MIB2_ERR_PUTMSG 10 /* error putting request message */ -#define GET_MIB2_ERR_TCPPUSH 11 /* error pushing TCPSTREAM */ -#define GET_MIB2_ERR_UDPPUSH 12 /* error pushing UDPSTREAM */ - -#define GET_MIB2_ERR_MAX 13 /* maximum error number + 1 */ - - -typedef struct { - char *db; /* data buffer */ - int db_len; /* data buffer length */ - char *smb; /* stream message buffer */ - size_t smb_len; /* size of Smb[] */ - int sd; /* stream device descriptor */ - char errmsg[GET_MIB2_ERRMSGL]; /* error message buffer */ - struct T_optmgmt_ack *op_ack; /* message ACK pointer */ - struct strbuf ctlbuf; /* streams control buffer */ - struct T_error_ack *err_ack; /* message error pointer */ - struct opthdr *op; /* message option pointer */ - struct T_optmgmt_req *req; /* message request pointer */ -} solaris_mib2_t; - -/* - * Function prototypes - */ - -int close_mib2( /* close acccess to MIB2 information */ - solaris_mib2_t *mib2 - ); -int get_mib2( /* get MIB2 information */ - solaris_mib2_t *mib2, - struct opthdr **opt, /* opthdr pointer return (see - * */ - char **data, /* data buffer return address */ - int *datalen /* data buffer length return - * address */ - ); -int open_mib2( /* open acccess to MIB2 information */ - solaris_mib2_t *mib2 - ); - -#endif /* !defined(GET_MIB2_H) */ diff --git a/vendor/sigar/src/os/solaris/kstats.c b/vendor/sigar/src/os/solaris/kstats.c deleted file mode 100644 index a04ff09aa..000000000 --- a/vendor/sigar/src/os/solaris/kstats.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2004-2007 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -int sigar_get_kstats(sigar_t *sigar) -{ - kstat_ctl_t *kc = sigar->kc; - unsigned int i, id, ncpu = sysconf(_SC_NPROCESSORS_CONF); - int is_debug = SIGAR_LOG_IS_DEBUG(sigar); - - if (ncpu != sigar->ncpu) { - if (!sigar->ks.lcpu) { - /* init */ - sigar->ks.lcpu = ncpu; - sigar->ks.cpu = malloc(sizeof(*(sigar->ks.cpu)) * ncpu); - sigar->ks.cpu_info = malloc(sizeof(*(sigar->ks.cpu_info)) * ncpu); - sigar->ks.cpuid = malloc(sizeof(*(sigar->ks.cpuid)) * ncpu); - } - else { - sigar_log_printf(sigar, SIGAR_LOG_INFO, - "ncpu changed from %d to %d", - sigar->ncpu, ncpu); - if (ncpu > sigar->ks.lcpu) { - /* one or more cpus have been added */ - sigar->ks.cpu = realloc(sigar->ks.cpu, - sizeof(*(sigar->ks.cpu)) * ncpu); - sigar->ks.cpu_info = realloc(sigar->ks.cpu_info, - sizeof(*(sigar->ks.cpu_info)) * ncpu); - sigar->ks.cpuid = realloc(sigar->ks.cpuid, - sizeof(*(sigar->ks.cpuid)) * ncpu); - sigar->ks.lcpu = ncpu; - } - /* else or more cpus have been removed */ - } - - sigar->ncpu = ncpu; - - /* from man p_online: - * ``Processor numbers are integers, - * greater than or equal to 0, - * and are defined by the hardware platform. - * Processor numbers are not necessarily contiguous, - * but "not too sparse."`` - * so we maintain our own mapping in ks.cpuid[] - */ - - /* lookup in order, which kstat chain may not be in */ - for (i=0, id=0; iks.cpu_info[i] = cpu_info; - sigar->ks.cpu[i] = cpu_stat; - sigar->ks.cpuid[i] = id; - - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "cpu %d id=%d", i, sigar->ks.cpuid[i]); - } - i++; - } - } - - sigar->ks.system = kstat_lookup(kc, "unix", -1, "system_misc"); - sigar->ks.syspages = kstat_lookup(kc, "unix", -1, "system_pages"); - sigar->ks.mempages = kstat_lookup(kc, "bunyip", -1, "mempages"); - - return SIGAR_OK; -} - -SIGAR_INLINE kid_t sigar_kstat_update(sigar_t *sigar) -{ - kid_t id = kstat_chain_update(sigar->kc); - - switch (id) { - case -1: - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "kstat_chain_update error: %s", - sigar_strerror(sigar, errno)); - break; - case 0: - /* up-to-date */ - break; - default: - sigar_get_kstats(sigar); - sigar_log(sigar, SIGAR_LOG_DEBUG, - "kstat chain updated"); - break; - } - - return id; -} - -/* - * bincompat is not possible with certain kstat data structures between - * solaris 2.6, 2.7, 2.8, etc. alternative is to use kstat_data_lookup() - * which means everytime we want a stat, must do a linear search - * of ksp->ks_data. eek. so we meet half way and do the search for - * each key once per sigar_t instance. once the initial search has - * been done, we have a table of offsets to quickly access the stats via - * ksp->ks_data + offset. this gives us bincompat without the overhead - * of many kstat_data_lookup calls. - */ -static SIGAR_INLINE int kstat_named_offset(kstat_t *ksp, const char *name) -{ - unsigned int i; - kstat_named_t *kn; - - for (i=0, kn=ksp->ks_data; - iks_ndata; - i++, kn++) - { - if (strEQ(kn->name, name)) { - return i; - } - } - - return -2; /* not found */ -} - -static char *kstat_keys_system[] = { - "boot_time", - "avenrun_1min", - "avenrun_5min", - "avenrun_15min", - NULL -}; - -static char *kstat_keys_mempages[] = { - "pages_anon", - "pages_exec", - "pages_vnode", - NULL -}; - -static char *kstat_keys_syspages[] = { - "pagesfree", - NULL -}; - -static char **kstat_keys[] = { - kstat_keys_system, - kstat_keys_mempages, - kstat_keys_syspages, -}; - -void sigar_koffsets_lookup(kstat_t *ksp, int *offsets, int kidx) -{ - int i; - char **keys = kstat_keys[kidx]; - - for (i=0; keys[i]; i++) { - offsets[i] = kstat_named_offset(ksp, keys[i]); - } -} - diff --git a/vendor/sigar/src/os/solaris/procfs.c b/vendor/sigar/src/os/solaris/procfs.c deleted file mode 100644 index 743bd5b04..000000000 --- a/vendor/sigar/src/os/solaris/procfs.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2004, 2006 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#define my_pread(fd, ptr, type, offset) \ - (pread(fd, ptr, sizeof(type), offset) == sizeof(type)) - -int sigar_proc_psinfo_get(sigar_t *sigar, sigar_pid_t pid) -{ - int fd, retval = SIGAR_OK; - char buffer[BUFSIZ]; - time_t timenow = time(NULL); - - if (sigar->pinfo == NULL) { - sigar->pinfo = malloc(sizeof(*sigar->pinfo)); - } - - if (sigar->last_pid == pid) { - if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { - return SIGAR_OK; - } - } - - sigar->last_pid = pid; - sigar->last_getprocs = timenow; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/psinfo"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - return ESRCH; - } - - if (!my_pread(fd, sigar->pinfo, psinfo_t, 0)) { - retval = errno; - } - - close(fd); - - return retval; -} - -int sigar_proc_usage_get(sigar_t *sigar, prusage_t *prusage, sigar_pid_t pid) -{ - int fd, retval = SIGAR_OK; - char buffer[BUFSIZ]; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/usage"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - return ESRCH; - } - - if (!my_pread(fd, prusage, prusage_t, 0)) { - retval = errno; - } - - close(fd); - - return retval; -} - -int sigar_proc_status_get(sigar_t *sigar, pstatus_t *pstatus, sigar_pid_t pid) -{ - int fd, retval = SIGAR_OK; - char buffer[BUFSIZ]; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/status"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - return ESRCH; - } - - if (!my_pread(fd, pstatus, pstatus_t, 0)) { - retval = errno; - } - - close(fd); - - return retval; -} diff --git a/vendor/sigar/src/os/solaris/sigar_os.h b/vendor/sigar/src/os/solaris/sigar_os.h deleted file mode 100644 index 73115d846..000000000 --- a/vendor/sigar/src/os/solaris/sigar_os.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2004-2007 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_OS_H -#define SIGAR_OS_H - -#ifndef _POSIX_PTHREAD_SEMANTICS -#define _POSIX_PTHREAD_SEMANTICS -#endif - -typedef unsigned long long int u_int64_t; - -#include -#include -#ifndef DMALLOC -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "get_mib2.h" - -/* avoid -Wall warning since solaris doesnt have a prototype for this */ -int getdomainname(char *, int); - -typedef struct { - kstat_t **ks; - int num; - char *name; - int nlen; -} kstat_list_t; - -SIGAR_INLINE kid_t sigar_kstat_update(sigar_t *sigar); - -int sigar_get_kstats(sigar_t *sigar); - -void sigar_init_multi_kstats(sigar_t *sigar); - -void sigar_free_multi_kstats(sigar_t *sigar); - -int sigar_get_multi_kstats(sigar_t *sigar, - kstat_list_t *kl, - const char *name, - kstat_t **retval); - -void sigar_koffsets_lookup(kstat_t *ksp, int *offsets, int kidx); - -int sigar_proc_psinfo_get(sigar_t *sigar, sigar_pid_t pid); - -int sigar_proc_usage_get(sigar_t *sigar, prusage_t *prusage, sigar_pid_t pid); - -int sigar_proc_status_get(sigar_t *sigar, pstatus_t *pstatus, sigar_pid_t pid); - -#define CPU_ONLINE(n) \ - (p_online(n, P_STATUS) == P_ONLINE) - -typedef enum { - KSTAT_SYSTEM_BOOT_TIME, - KSTAT_SYSTEM_LOADAVG_1, - KSTAT_SYSTEM_LOADAVG_2, - KSTAT_SYSTEM_LOADAVG_3, - KSTAT_SYSTEM_MAX -} kstat_system_off_e; - -typedef enum { - KSTAT_MEMPAGES_ANON, - KSTAT_MEMPAGES_EXEC, - KSTAT_MEMPAGES_VNODE, - KSTAT_MEMPAGES_MAX -} kstat_mempages_off_e; - -typedef enum { - KSTAT_SYSPAGES_FREE, - KSTAT_SYSPAGES_MAX -} kstat_syspages_off_e; - -enum { - KSTAT_KEYS_system, - KSTAT_KEYS_mempages, - KSTAT_KEYS_syspages, -} kstat_keys_e; - -typedef struct ps_prochandle * (*proc_grab_func_t)(pid_t, int, int *); - -typedef void (*proc_free_func_t)(struct ps_prochandle *); - -typedef int (*proc_create_agent_func_t)(struct ps_prochandle *); - -typedef void (*proc_destroy_agent_func_t)(struct ps_prochandle *); - -typedef void (*proc_objname_func_t)(struct ps_prochandle *, - uintptr_t, const char *, size_t); - -typedef char * (*proc_dirname_func_t)(const char *, char *, size_t); - -typedef char * (*proc_exename_func_t)(struct ps_prochandle *, char *, size_t); - -typedef int (*proc_fstat64_func_t)(struct ps_prochandle *, int, void *); - -typedef int (*proc_getsockopt_func_t)(struct ps_prochandle *, - int, int, int, void *, int *); - -typedef int (*proc_getsockname_func_t)(struct ps_prochandle *, - int, struct sockaddr *, socklen_t *); - -struct sigar_t { - SIGAR_T_BASE; - - int solaris_version; - int use_ucb_ps; - - kstat_ctl_t *kc; - - /* kstat_lookup() as needed */ - struct { - kstat_t **cpu; - kstat_t **cpu_info; - processorid_t *cpuid; - unsigned int lcpu; /* number malloced slots in the cpu array above */ - kstat_t *system; - kstat_t *syspages; - kstat_t *mempages; - } ks; - - struct { - int system[KSTAT_SYSTEM_MAX]; - int mempages[KSTAT_MEMPAGES_MAX]; - int syspages[KSTAT_SYSPAGES_MAX]; - } koffsets; - - int pagesize; - - time_t last_getprocs; - sigar_pid_t last_pid; - psinfo_t *pinfo; - sigar_cpu_list_t cpulist; - - /* libproc.so interface */ - void *plib; - proc_grab_func_t pgrab; - proc_free_func_t pfree; - proc_create_agent_func_t pcreate_agent; - proc_destroy_agent_func_t pdestroy_agent; - proc_objname_func_t pobjname; - proc_dirname_func_t pdirname; - proc_exename_func_t pexename; - proc_fstat64_func_t pfstat64; - proc_getsockopt_func_t pgetsockopt; - proc_getsockname_func_t pgetsockname; - - sigar_cache_t *pargs; - - solaris_mib2_t mib2; -}; - -#ifdef SIGAR_64BIT -#define KSTAT_UINT ui64 -#else -#define KSTAT_UINT ui32 -#endif - -#define kSTAT_exists(v, type) \ - (sigar->koffsets.type[v] != -2) - -#define kSTAT_ptr(v, type) \ - ((kstat_named_t *)ksp->ks_data + sigar->koffsets.type[v]) - -#define kSTAT_uint(v, type) \ - (kSTAT_exists(v, type) ? kSTAT_ptr(v, type)->value.KSTAT_UINT : 0) - -#define kSTAT_ui32(v, type) \ - (kSTAT_exists(v, type) ? kSTAT_ptr(v, type)->value.ui32 : 0) - -#define kSYSTEM(v) kSTAT_ui32(v, system) - -#define kMEMPAGES(v) kSTAT_uint(v, mempages) - -#define kSYSPAGES(v) kSTAT_uint(v, syspages) - -#define sigar_koffsets_init(sigar, ksp, type) \ - if (sigar->koffsets.type[0] == -1) \ - sigar_koffsets_lookup(ksp, sigar->koffsets.type, KSTAT_KEYS_##type) - -#define sigar_koffsets_init_system(sigar, ksp) \ - sigar_koffsets_init(sigar, ksp, system) - -#define sigar_koffsets_init_mempages(sigar, ksp) \ - sigar_koffsets_init(sigar, ksp, mempages) - -#define sigar_koffsets_init_syspages(sigar, ksp) \ - sigar_koffsets_init(sigar, ksp, syspages) - -#define HAVE_READDIR_R -#define HAVE_GETPWNAM_R -#define HAVE_GETPWUID_R - -#define SIGAR_EMIB2 (SIGAR_OS_START_ERROR+1) - -#endif /* SIGAR_OS_H */ - diff --git a/vendor/sigar/src/os/solaris/solaris_sigar.c b/vendor/sigar/src/os/solaris/solaris_sigar.c deleted file mode 100644 index da9a2b635..000000000 --- a/vendor/sigar/src/os/solaris/solaris_sigar.c +++ /dev/null @@ -1,2717 +0,0 @@ -/* - * Copyright (c) 2004-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PROC_ERRNO ((errno == ENOENT) ? ESRCH : errno) -#define SIGAR_USR_UCB_PS "/usr/ucb/ps" - - -/* like kstat_lookup but start w/ ksp->ks_next instead of kc->kc_chain */ -static kstat_t * -kstat_next(kstat_t *ksp, char *ks_module, int ks_instance, char *ks_name) -{ - if (ksp) { - ksp = ksp->ks_next; - } - for (; ksp; ksp = ksp->ks_next) { - if ((ks_module == NULL || - strcmp(ksp->ks_module, ks_module) == 0) && - (ks_instance == -1 || ksp->ks_instance == ks_instance) && - (ks_name == NULL || strcmp(ksp->ks_name, ks_name) == 0)) - return ksp; - } - - errno = ENOENT; - return NULL; -} - -int sigar_os_open(sigar_t **sig) -{ - kstat_ctl_t *kc; - kstat_t *ksp; - sigar_t *sigar; - int i, status; - struct utsname name; - char *ptr; - - sigar = malloc(sizeof(*sigar)); - *sig = sigar; - - sigar->log_level = -1; /* log nothing by default */ - sigar->log_impl = NULL; - sigar->log_data = NULL; - - uname(&name); - if ((ptr = strchr(name.release, '.'))) { - ptr++; - sigar->solaris_version = atoi(ptr); - } - else { - sigar->solaris_version = 6; - } - - if ((ptr = getenv("SIGAR_USE_UCB_PS"))) { - sigar->use_ucb_ps = strEQ(ptr, "true"); - } - else { - struct stat sb; - if (stat(SIGAR_USR_UCB_PS, &sb) < 0) { - sigar->use_ucb_ps = 0; - } - else { - sigar->use_ucb_ps = 1; - } - } - - sigar->pagesize = 0; - i = sysconf(_SC_PAGESIZE); - while ((i >>= 1) > 0) { - sigar->pagesize++; - } - - sigar->ticks = sysconf(_SC_CLK_TCK); - sigar->kc = kc = kstat_open(); - - if (!kc) { - return errno; - } - - sigar->cpulist.size = 0; - sigar->ncpu = 0; - sigar->ks.cpu = NULL; - sigar->ks.cpu_info = NULL; - sigar->ks.cpuid = NULL; - sigar->ks.lcpu = 0; - - sigar->koffsets.system[0] = -1; - sigar->koffsets.mempages[0] = -1; - sigar->koffsets.syspages[0] = -1; - - if ((status = sigar_get_kstats(sigar)) != SIGAR_OK) { - fprintf(stderr, "status=%d\n", status); - } - - sigar->boot_time = 0; - - if ((ksp = sigar->ks.system) && - (kstat_read(kc, ksp, NULL) >= 0)) - { - sigar_koffsets_init_system(sigar, ksp); - - sigar->boot_time = kSYSTEM(KSTAT_SYSTEM_BOOT_TIME); - } - - sigar->last_pid = -1; - sigar->pinfo = NULL; - - sigar->plib = NULL; - sigar->pgrab = NULL; - sigar->pfree = NULL; - sigar->pobjname = NULL; - - sigar->pargs = NULL; - - SIGAR_ZERO(&sigar->mib2); - sigar->mib2.sd = -1; - - return SIGAR_OK; -} - -int sigar_os_close(sigar_t *sigar) -{ - kstat_close(sigar->kc); - if (sigar->mib2.sd != -1) { - close_mib2(&sigar->mib2); - } - - if (sigar->ks.lcpu) { - free(sigar->ks.cpu); - free(sigar->ks.cpu_info); - free(sigar->ks.cpuid); - } - if (sigar->pinfo) { - free(sigar->pinfo); - } - if (sigar->cpulist.size != 0) { - sigar_cpu_list_destroy(sigar, &sigar->cpulist); - } - if (sigar->plib) { - dlclose(sigar->plib); - } - if (sigar->pargs) { - sigar_cache_destroy(sigar->pargs); - } - free(sigar); - return SIGAR_OK; -} - -char *sigar_os_error_string(sigar_t *sigar, int err) -{ - switch (err) { - case SIGAR_EMIB2: - return sigar->mib2.errmsg; - default: - return NULL; - } -} - -int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) -{ - kstat_ctl_t *kc = sigar->kc; - kstat_t *ksp; - sigar_uint64_t kern = 0; - - SIGAR_ZERO(mem); - - /* XXX: is mem hot swappable or can we just do this during open ? */ - mem->total = sysconf(_SC_PHYS_PAGES); - mem->total <<= sigar->pagesize; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - - if ((ksp = sigar->ks.syspages) && kstat_read(kc, ksp, NULL) >= 0) { - sigar_koffsets_init_syspages(sigar, ksp); - - mem->free = kSYSPAGES(KSTAT_SYSPAGES_FREE); - mem->free <<= sigar->pagesize; - - mem->used = mem->total - mem->free; - } - - if ((ksp = sigar->ks.mempages) && kstat_read(kc, ksp, NULL) >= 0) { - sigar_koffsets_init_mempages(sigar, ksp); - } - - /* XXX mdb ::memstat cachelist/freelist not available to kstat, see: */ - /* http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6821980 */ - - /* ZFS ARC cache. see: http://opensolaris.org/jive/thread.jspa?messageID=393695 */ - if ((ksp = kstat_lookup(sigar->kc, "zfs", 0, "arcstats")) && - (kstat_read(sigar->kc, ksp, NULL) != -1)) - { - kstat_named_t *kn; - - if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "size"))) { - kern = kn->value.i64; - } - if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "c_min"))) { - /* c_min cannot be reclaimed they say */ - if (kern > kn->value.i64) { - kern -= kn->value.i64; - } - } - } - - mem->actual_free = mem->free + kern; - mem->actual_used = mem->used - kern; - - sigar_mem_calc_ram(sigar, mem); - - return SIGAR_OK; -} - -int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) -{ - kstat_t *ksp; - kstat_named_t *kn; - swaptbl_t *stab; - int num, i; - char path[PATH_MAX+1]; /* {un,re}used */ - - /* see: man swapctl(2) */ - if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { - return errno; - } - - stab = malloc(num * sizeof(stab->swt_ent[0]) + sizeof(*stab)); - - stab->swt_n = num; - for (i=0; iswt_ent[i].ste_path = path; - } - - if ((num = swapctl(SC_LIST, stab)) == -1) { - free(stab); - return errno; - } - - num = num < stab->swt_n ? num : stab->swt_n; - swap->total = swap->free = 0; - for (i=0; iswt_ent[i].ste_flags & ST_INDEL) { - continue; /* swap file is being deleted */ - } - swap->total += stab->swt_ent[i].ste_pages; - swap->free += stab->swt_ent[i].ste_free; - } - free(stab); - - swap->total <<= sigar->pagesize; - swap->free <<= sigar->pagesize; - swap->used = swap->total - swap->free; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - if (!(ksp = kstat_lookup(sigar->kc, "cpu", -1, "vm"))) { - swap->page_in = swap->page_out = SIGAR_FIELD_NOTIMPL; - return SIGAR_OK; - } - - swap->page_in = swap->page_out = 0; - - /* XXX: these stats do not exist in this form on solaris 8 or 9. - * they are in the raw cpu_stat struct, but thats not - * binary compatible - */ - do { - if (kstat_read(sigar->kc, ksp, NULL) < 0) { - break; - } - - if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "pgin"))) { - swap->page_in += kn->value.i64; /* vmstat -s | grep "page ins" */ - } - if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "pgout"))) { - swap->page_out += kn->value.i64; /* vmstat -s | grep "page outs" */ - } - } while ((ksp = kstat_next(ksp, "cpu", -1, "vm"))); - - return SIGAR_OK; -} - -#ifndef KSTAT_NAMED_STR_PTR -/* same offset as KSTAT_NAMED_STR_PTR(brand) */ -#define KSTAT_NAMED_STR_PTR(n) (char *)((n)->value.i32) -#endif - -static int get_chip_brand(sigar_t *sigar, int processor, - sigar_cpu_info_t *info) -{ - kstat_t *ksp = sigar->ks.cpu_info[processor]; - kstat_named_t *brand; - - if (sigar->solaris_version < 10) { - /* don't bother; doesn't exist. */ - return 0; - } - - if (ksp && - (kstat_read(sigar->kc, ksp, NULL) != -1) && - (brand = (kstat_named_t *)kstat_data_lookup(ksp, "brand"))) - { - char *name = KSTAT_NAMED_STR_PTR(brand); - - char *vendor = "Sun"; - char *vendors[] = { - "Intel", "AMD", NULL - }; - int i; - - if (!name) { - return 0; - } - - for (i=0; vendors[i]; i++) { - if (strstr(name, vendors[i])) { - vendor = vendors[i]; - break; - } - } - - SIGAR_SSTRCPY(info->vendor, vendor); -#if 0 - SIGAR_SSTRCPY(info->model, name); - sigar_cpu_model_adjust(sigar, info); -#endif - return 1; - } - else { - return 0; - } -} - -static void free_chip_id(void *ptr) -{ - /*noop*/ -} - -static int get_chip_id(sigar_t *sigar, int processor) -{ - kstat_t *ksp = sigar->ks.cpu_info[processor]; - kstat_named_t *chipid; - - if (ksp && - (kstat_read(sigar->kc, ksp, NULL) != -1) && - (chipid = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id"))) - { - return chipid->value.i32; - } - else { - return -1; - } -} - -int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - int status, i; - - status = sigar_cpu_list_get(sigar, &sigar->cpulist); - - if (status != SIGAR_OK) { - return status; - } - - SIGAR_ZERO(cpu); - - for (i=0; icpulist.number; i++) { - sigar_cpu_t *xcpu = &sigar->cpulist.data[i]; - - cpu->user += xcpu->user; - cpu->sys += xcpu->sys; - cpu->idle += xcpu->idle; - cpu->nice += xcpu->nice; - cpu->wait += xcpu->wait; - cpu->total = xcpu->total; - } - - return SIGAR_OK; -} - -int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) -{ - kstat_ctl_t *kc = sigar->kc; - kstat_t *ksp; - uint_t cpuinfo[CPU_STATES]; - unsigned int i; - int is_debug = SIGAR_LOG_IS_DEBUG(sigar); - sigar_cache_t *chips; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - - if (cpulist == &sigar->cpulist) { - if (sigar->cpulist.size == 0) { - /* create once */ - sigar_cpu_list_create(cpulist); - } - else { - /* reset, re-using cpulist.data */ - sigar->cpulist.number = 0; - } - } - else { - sigar_cpu_list_create(cpulist); - } - - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[cpu_list] OS reports %d CPUs", - sigar->ncpu); - } - - chips = sigar_cache_new(16); - chips->free_value = free_chip_id; - - for (i=0; incpu; i++) { - sigar_cpu_t *cpu; - char *buf; - int chip_id; - sigar_cache_entry_t *ent; - - if (!CPU_ONLINE(sigar->ks.cpuid[i])) { - sigar_log_printf(sigar, SIGAR_LOG_INFO, - "cpu %d (id=%d) is offline", - i, sigar->ks.cpuid[i]); - continue; - } - - if (!(ksp = sigar->ks.cpu[i])) { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "NULL ksp for cpu %d (id=%d)", - i, sigar->ks.cpuid[i]); - continue; /* shouldnot happen */ - } - - if (kstat_read(kc, ksp, NULL) < 0) { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "kstat_read failed for cpu %d (id=%d): %s", - i, sigar->ks.cpuid[i], - sigar_strerror(sigar, errno)); - continue; /* shouldnot happen */ - } - - /* - * cpu_stat_t is not binary compatible between solaris versions. - * since cpu_stat is a 'raw' kstat and not 'named' we cannot - * use name based lookups as we do for others. - * the start of the cpu_stat_t structure is binary compatible, - * which looks like so: - * typedef struct cpu_stat { - * kmutex_t cpu_stat_lock; - * cpu_sysinfo_t cpu_sysinfo; - * ... - * typedef struct cpu_sysinfo { - * ulong cpu[CPU_STATES]; - * ... - * we just copy the piece we need below: - */ - buf = ksp->ks_data; - buf += sizeof(kmutex_t); - memcpy(&cpuinfo[0], buf, sizeof(cpuinfo)); - chip_id = sigar->cpu_list_cores ? -1 : get_chip_id(sigar, i); - - if (chip_id == -1) { - SIGAR_CPU_LIST_GROW(cpulist); - cpu = &cpulist->data[cpulist->number++]; - SIGAR_ZERO(cpu); - } - else { - /* merge times of logical processors */ - ent = sigar_cache_get(chips, chip_id); - if (ent->value) { - cpu = &cpulist->data[(long)ent->value-1]; - } - else { - SIGAR_CPU_LIST_GROW(cpulist); - cpu = &cpulist->data[cpulist->number++]; - ent->value = (void *)(long)cpulist->number; - SIGAR_ZERO(cpu); - - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[cpu_list] Merging times of" - " logical processors for chip_id=%d", - chip_id); - } - } - } - - cpu->user += SIGAR_TICK2MSEC(cpuinfo[CPU_USER]); - cpu->sys += SIGAR_TICK2MSEC(cpuinfo[CPU_KERNEL]); - cpu->idle += SIGAR_TICK2MSEC(cpuinfo[CPU_IDLE]); - cpu->wait += SIGAR_TICK2MSEC(cpuinfo[CPU_WAIT]); - cpu->nice += 0; /* no cpu->nice */ - cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait; - } - - sigar_cache_destroy(chips); - - return SIGAR_OK; -} - -int sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime) -{ - if (sigar->boot_time) { - uptime->uptime = time(NULL) - sigar->boot_time; - } - else { - uptime->uptime = 0; /* XXX: shouldn't happen */ - } - - return SIGAR_OK; -} - -static int loadavg_keys[] = { - KSTAT_SYSTEM_LOADAVG_1, - KSTAT_SYSTEM_LOADAVG_2, - KSTAT_SYSTEM_LOADAVG_3 -}; - -int sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg) -{ - kstat_t *ksp; - int i; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - - if (!(ksp = sigar->ks.system)) { - return -1; - } - - if (kstat_read(sigar->kc, ksp, NULL) < 0) { - return -1; - } - - sigar_koffsets_init_system(sigar, ksp); - - for (i=0; i<3; i++) { - loadavg->loadavg[i] = (double)kSYSTEM(loadavg_keys[i]) / FSCALE; - } - - return SIGAR_OK; -} - -#define LIBPROC "/usr/lib/libproc.so" - -#define CHECK_PSYM(s) \ - if (!sigar->s) { \ - sigar_log_printf(sigar, SIGAR_LOG_WARN, \ - "[%s] Symbol not found: %s", \ - SIGAR_FUNC, #s); \ - dlclose(sigar->plib); \ - sigar->plib = NULL; \ - return SIGAR_ENOTIMPL; \ - } - -static char *proc_readlink(const char *name, char *buffer, size_t size) -{ - int len; - - if ((len = readlink(name, buffer, size-1)) < 0) { - return NULL; - } - - buffer[len] = '\0'; - return buffer; -} - -static int sigar_init_libproc(sigar_t *sigar) -{ - if (sigar->plib) { - return SIGAR_OK; - } - - /* libproc.so ships with 5.8+ */ - /* interface is undocumented, see libproc.h in the sun jdk sources */ - sigar->plib = dlopen(LIBPROC, RTLD_LAZY); - - if (!sigar->plib) { - sigar_log_printf(sigar, SIGAR_LOG_WARN, - "[%s] dlopen(%s) = %s", - SIGAR_FUNC, LIBPROC, dlerror()); - return SIGAR_ENOTIMPL; - } - - sigar->pgrab = (proc_grab_func_t)dlsym(sigar->plib, "Pgrab"); - sigar->pfree = (proc_free_func_t)dlsym(sigar->plib, "Pfree"); - sigar->pcreate_agent = (proc_create_agent_func_t)dlsym(sigar->plib, "Pcreate_agent"); - sigar->pdestroy_agent = (proc_destroy_agent_func_t)dlsym(sigar->plib, "Pdestroy_agent"); - sigar->pobjname = (proc_objname_func_t)dlsym(sigar->plib, "Pobjname"); - sigar->pexename = (proc_exename_func_t)dlsym(sigar->plib, "Pexecname"); - sigar->pdirname = (proc_dirname_func_t)dlsym(sigar->plib, "proc_dirname"); - sigar->pfstat64 = (proc_fstat64_func_t)dlsym(sigar->plib, "pr_fstat64"); - sigar->pgetsockopt = (proc_getsockopt_func_t)dlsym(sigar->plib, "pr_getsockopt"); - sigar->pgetsockname = (proc_getsockname_func_t)dlsym(sigar->plib, "pr_getsockname"); - - CHECK_PSYM(pgrab); - CHECK_PSYM(pfree); - CHECK_PSYM(pobjname); - - return SIGAR_OK; -} - -/* from libproc.h, not included w/ solaris distro */ -/* Error codes from Pgrab(), Pfgrab_core(), and Pgrab_core() */ -#define G_STRANGE -1 /* Unanticipated error, errno is meaningful */ -#define G_NOPROC 1 /* No such process */ -#define G_NOCORE 2 /* No such core file */ -#define G_NOPROCORCORE 3 /* No such proc or core (for proc_arg_grab) */ -#define G_NOEXEC 4 /* Cannot locate executable file */ -#define G_ZOMB 5 /* Zombie process */ -#define G_PERM 6 /* No permission */ -#define G_BUSY 7 /* Another process has control */ -#define G_SYS 8 /* System process */ -#define G_SELF 9 /* Process is self */ -#define G_INTR 10 /* Interrupt received while grabbing */ -#define G_LP64 11 /* Process is _LP64, self is ILP32 */ -#define G_FORMAT 12 /* File is not an ELF format core file */ -#define G_ELF 13 /* Libelf error, elf_errno() is meaningful */ -#define G_NOTE 14 /* Required PT_NOTE Phdr not present in core */ - -static int sigar_pgrab(sigar_t *sigar, sigar_pid_t pid, - const char *func, - struct ps_prochandle **phandle) -{ - int pstatus; - - if (!(*phandle = sigar->pgrab(pid, 0x01, &pstatus))) { - switch (pstatus) { - case G_NOPROC: - return ESRCH; - case G_PERM: - return EACCES; - default: - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "[%s] Pgrab error=%d", - func, pstatus); - return ENOTSUP; /*XXX*/ - } - } - - return SIGAR_OK; -} - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - return sigar_proc_list_procfs_get(sigar, proclist); -} - -int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem) -{ - int status = sigar_proc_psinfo_get(sigar, pid); - psinfo_t *pinfo = sigar->pinfo; - prusage_t usage; - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = pinfo->pr_size << 10; - procmem->resident = pinfo->pr_rssize << 10; - procmem->share = SIGAR_FIELD_NOTIMPL; - - if (sigar_proc_usage_get(sigar, &usage, pid) == SIGAR_OK) { - procmem->minor_faults = usage.pr_minf; - procmem->major_faults = usage.pr_majf; - procmem->page_faults = - procmem->minor_faults + - procmem->major_faults; - } - else { - procmem->minor_faults = SIGAR_FIELD_NOTIMPL; - procmem->major_faults = SIGAR_FIELD_NOTIMPL; - procmem->page_faults = SIGAR_FIELD_NOTIMPL; - } - - return SIGAR_OK; -} - -int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred) -{ - int status = sigar_proc_psinfo_get(sigar, pid); - psinfo_t *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - proccred->uid = pinfo->pr_uid; - proccred->gid = pinfo->pr_gid; - proccred->euid = pinfo->pr_euid; - proccred->egid = pinfo->pr_egid; - - return SIGAR_OK; -} - -#define TIMESTRUCT_2MSEC(t) \ - ((t.tv_sec * MILLISEC) + (t.tv_nsec / (NANOSEC/MILLISEC))) - -int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime) -{ - prusage_t usage; - int status; - - if ((status = sigar_proc_usage_get(sigar, &usage, pid)) != SIGAR_OK) { - return status; - } - - proctime->start_time = usage.pr_create.tv_sec + sigar->boot_time; - proctime->start_time *= MILLISEC; - - if (usage.pr_utime.tv_sec < 0) { - /* XXX wtf? seen on solaris 10, only for the self process */ - pstatus_t pstatus; - - status = sigar_proc_status_get(sigar, &pstatus, pid); - if (status != SIGAR_OK) { - return status; - } - - usage.pr_utime.tv_sec = pstatus.pr_utime.tv_sec; - usage.pr_utime.tv_nsec = pstatus.pr_utime.tv_nsec; - usage.pr_stime.tv_sec = pstatus.pr_stime.tv_sec; - usage.pr_stime.tv_nsec = pstatus.pr_stime.tv_nsec; - } - - proctime->user = TIMESTRUCT_2MSEC(usage.pr_utime); - proctime->sys = TIMESTRUCT_2MSEC(usage.pr_stime); - proctime->total = proctime->user + proctime->sys; - - return SIGAR_OK; -} - -int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - int status = sigar_proc_psinfo_get(sigar, pid); - psinfo_t *pinfo = sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - SIGAR_SSTRCPY(procstate->name, pinfo->pr_fname); - procstate->ppid = pinfo->pr_ppid; - procstate->tty = pinfo->pr_ttydev; - procstate->priority = pinfo->pr_lwp.pr_pri; - procstate->nice = pinfo->pr_lwp.pr_nice - NZERO; - procstate->threads = pinfo->pr_nlwp; - procstate->processor = pinfo->pr_lwp.pr_onpro; - - switch (pinfo->pr_lwp.pr_state) { - case SONPROC: - case SRUN: - procstate->state = 'R'; - break; - case SZOMB: - procstate->state = 'Z'; - break; - case SSLEEP: - procstate->state = 'S'; - break; - case SSTOP: - procstate->state = 'T'; - break; - case SIDL: - procstate->state = 'D'; - break; - } - - return SIGAR_OK; -} - -typedef struct { - int timestamp; - char *args; -} pargs_t; - -static void pargs_free(void *value) -{ - pargs_t *pargs = (pargs_t *)value; - if (pargs->args != NULL) { - free(pargs->args); - } - free(pargs); -} - -static int ucb_ps_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs, - int timestamp) -{ - char buffer[9086], *args=NULL, *arg; - sigar_cache_entry_t *ent; - FILE *fp; - pargs_t *pargs; - - if (!sigar->pargs) { - sigar->pargs = sigar_cache_new(15); - sigar->pargs->free_value = pargs_free; - } - - ent = sigar_cache_get(sigar->pargs, pid); - if (ent->value) { - pargs = (pargs_t *)ent->value; - if (pargs->timestamp != timestamp) { - if (pargs->args) { - free(pargs->args); - pargs->args = NULL; - } - } - } - else { - pargs = malloc(sizeof(*pargs)); - pargs->args = NULL; - ent->value = pargs; - } - - pargs->timestamp = timestamp; - - if (pargs->args) { - args = pargs->args; - } - else { - snprintf(buffer, sizeof(buffer), - SIGAR_USR_UCB_PS " -ww %ld", (long)pid); - - if (!(fp = popen(buffer, "r"))) { - return errno; - } - /* skip header */ - (void)fgets(buffer, sizeof(buffer), fp); - if ((args = fgets(buffer, sizeof(buffer), fp))) { - int len; - - /* skip PID,TT,S,TIME */ - args = sigar_skip_multiple_token(args, 4); - SIGAR_SKIP_SPACE(args); - len = strlen(args); - if (len > 0) { - args[len-1] = '\0'; /* chop \n */ - } - - pargs->args = malloc(len+1); - memcpy(pargs->args, args, len); - } - - pclose(fp); - - if (!args) { - return ESRCH; - } - } - - while (*args && (arg = sigar_getword(&args, ' '))) { - SIGAR_PROC_ARGS_GROW(procargs); - procargs->data[procargs->number++] = arg; - } - - return SIGAR_OK; -} - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - psinfo_t *pinfo; - int fd, status; - char buffer[9086]; - char *argvb[56]; - char **argvp = argvb; - - int n; - size_t nread = 0; - unsigned int argv_size; - - if ((status = sigar_proc_psinfo_get(sigar, pid)) != SIGAR_OK) { - return status; - } - pinfo = sigar->pinfo; - - if (pinfo->pr_argc == 0) { - procargs->number = 0; - return SIGAR_OK; - } - else if (pinfo->pr_dmodel != PR_MODEL_NATIVE) { - /* we are compiled in 32bit mode - * punt any 64bit native process, - * sizeof our structures can't handle. - */ - if (sigar->use_ucb_ps) { - return ucb_ps_args_get(sigar, pid, procargs, - pinfo->pr_start.tv_sec); - } - else { - return ENOTSUP; - } - } - - argv_size = sizeof(*argvp) * pinfo->pr_argc; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/as"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - if ((errno == EACCES) && sigar->use_ucb_ps) { - return ucb_ps_args_get(sigar, pid, procargs, - pinfo->pr_start.tv_sec); - } - else { - return PROC_ERRNO; - } - } - - if (argv_size > sizeof(argvb)) { - argvp = malloc(argv_size); - } - - if ((nread = pread(fd, argvp, argv_size, pinfo->pr_argv)) <= 0) { - close(fd); - if (argvp != argvb) { - free(argvp); - } - return errno; - } - - for (n = 0; n < pinfo->pr_argc; n++) { - int alen; - char *arg; - - if ((nread = pread(fd, buffer, sizeof(buffer)-1, (off_t)argvp[n])) <= 0) { - close(fd); - if (argvp != argvb) { - free(argvp); - } - return errno; - } - - buffer[nread] = '\0'; - alen = strlen(buffer)+1; - arg = malloc(alen); - memcpy(arg, buffer, alen); - - SIGAR_PROC_ARGS_GROW(procargs); - procargs->data[procargs->number++] = arg; - } - - if (argvp != argvb) { - free(argvp); - } - - close(fd); - - return SIGAR_OK; -} - -int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - psinfo_t *pinfo; - int fd, status; - char buffer[BUFSIZ], *offsets[512]; - size_t nread; - int n=0, max=sizeof(offsets)/sizeof(char *); - - if ((status = sigar_proc_psinfo_get(sigar, pid)) != SIGAR_OK) { - return status; - } - pinfo = sigar->pinfo; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/as"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - return PROC_ERRNO; - } - - if ((nread = pread(fd, offsets, sizeof(offsets), - pinfo->pr_envp)) <= 0) - { - close(fd); - return errno; - } - - while ((n < max) && offsets[n]) { - char *val; - int klen, vlen, status; - char key[128]; /* XXX is there a max key size? */ - - if ((nread = pread(fd, buffer, sizeof(buffer), - (off_t)offsets[n++])) <= 0) - { - close(fd); - return errno; - } - - val = strchr(buffer, '='); - - if (val == NULL) { - break; /*XXX*/ - } - - klen = val - buffer; - SIGAR_SSTRCPY(key, buffer); - key[klen] = '\0'; - ++val; - - vlen = strlen(val); - - status = procenv->env_getter(procenv->data, - key, klen, val, vlen); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - } - - close(fd); - - return SIGAR_OK; -} - -int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd) -{ - int status = - sigar_proc_fd_count(sigar, pid, &procfd->total); - - return status; -} - -static int sigar_proc_path_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ - /* solaris 10+ */ - char buffer[BUFSIZ]; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/path/a.out"); - if (!proc_readlink(buffer, procexe->name, sizeof(procexe->name))) { - procexe->name[0] = '\0'; - } - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/path/cwd"); - if (!proc_readlink(buffer, procexe->cwd, sizeof(procexe->cwd))) { - procexe->cwd[0] = '\0'; - } - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/path/root"); - if (!proc_readlink(buffer, procexe->root, sizeof(procexe->root))) { - procexe->root[0] = '\0'; - } - - return SIGAR_OK; -} - -static int proc_module_get_exe(void *data, char *name, int len) -{ - sigar_proc_exe_t *procexe = (sigar_proc_exe_t *)data; - SIGAR_STRNCPY(procexe->name, name, sizeof(procexe->name)); - return !SIGAR_OK; /* break loop */ -} - -static int sigar_which_exe_get(sigar_t *sigar, sigar_proc_exe_t *procexe) -{ - char *path = getenv("PATH"); - char exe[PATH_MAX]; - if (path == NULL) { - return EINVAL; - } - - while (path) { - char *ptr = strchr(path, ':'); - if (!ptr) { - break; - } - exe[0] = '\0'; - strncat(exe, path, ptr-path); - strncat(exe, "/", 1); - strcat(exe, procexe->name); - if (access(exe, X_OK) == 0) { - SIGAR_STRNCPY(procexe->name, exe, sizeof(procexe->name)); - break; - } - path = ptr+1; - } - - return ENOENT; -} - -int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ - int status; - char buffer[BUFSIZ]; - struct ps_prochandle *phandle; - - if (sigar->solaris_version >= 10) { - return sigar_proc_path_exe_get(sigar, pid, procexe); - } - - if ((status = sigar_init_libproc(sigar)) != SIGAR_OK) { - return status; - } - - procexe->name[0] = '\0'; - - /* Pgrab would return G_SELF error */ - if (pid == sigar_pid_get(sigar)) { - sigar_proc_modules_t procmods; - procmods.module_getter = proc_module_get_exe; - procmods.data = procexe; - - status = - sigar_dlinfo_modules(sigar, &procmods); - if (status == SIGAR_OK) { - if (procexe->name[0] != '/') { - sigar_which_exe_get(sigar, procexe); - } - } - } - else { - status = sigar_pgrab(sigar, pid, SIGAR_FUNC, &phandle); - - if (status == SIGAR_OK) { - sigar->pexename(phandle, procexe->name, sizeof(procexe->name)); - sigar->pfree(phandle); - } - } - - if (procexe->name[0] == '\0') { - /*XXX*/ - } - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/cwd"); - - if (!sigar->pdirname(buffer, procexe->cwd, sizeof(procexe->cwd))) { - procexe->cwd[0] = '\0'; - } - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/root"); - - if (!(sigar->pdirname(buffer, procexe->root, sizeof(procexe->root)))) { - procexe->root[0] = '\0'; - } - - return SIGAR_OK; -} - -static int sigar_read_xmaps(sigar_t *sigar, - prxmap_t *xmaps, int total, - unsigned long *last_inode, - struct ps_prochandle *phandle, - sigar_proc_modules_t *procmods) -{ - int status, i; - unsigned long inode; - char buffer[BUFSIZ]; - - for (i=0; ipobjname(phandle, xmaps[i].pr_vaddr, buffer, sizeof(buffer)); - - status = - procmods->module_getter(procmods->data, buffer, strlen(buffer)); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - return status; - } - } - - return SIGAR_OK; -} - -static int sigar_pgrab_modules(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ - int fd, pstatus; - off_t map_size, nread; - unsigned long last_inode = 0; - prxmap_t xmaps[15]; /* ~2K */ - struct ps_prochandle *phandle; - struct stat statbuf; - char buffer[BUFSIZ]; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/xmap"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - return errno; - } - - if (fstat(fd, &statbuf) < 0) { - close(fd); - return errno; - } - - map_size = statbuf.st_size; - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[%s] pid=%d, size=%d", - SIGAR_FUNC, pid, map_size); - } - - if ((pstatus = sigar_init_libproc(sigar)) != SIGAR_OK) { - close(fd); - return pstatus; - } - - pstatus = sigar_pgrab(sigar, pid, SIGAR_FUNC, &phandle); - - if (pstatus != SIGAR_OK) { - close(fd); - return pstatus; - } - - for (nread=0; nread sizeof(xmaps) ? sizeof(xmaps) : map_size; - int total = wanted / sizeof(prxmap_t); - - if (pread(fd, xmaps, wanted, nread) != wanted) { - close(fd); - sigar->pfree(phandle); - return errno; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[%s] nread=%d, map_size=%d, wanted=%d, total=%d", - SIGAR_FUNC, - nread, map_size, wanted, total); - } - - if (sigar_read_xmaps(sigar, xmaps, total, - &last_inode, - phandle, procmods) != SIGAR_OK) - { - break; - } - - nread += wanted; - map_size -= wanted; - } - - close(fd); - - sigar->pfree(phandle); - - return SIGAR_OK; -} - -int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ - if (pid == sigar_pid_get(sigar)) { - /* Pgrab would return G_SELF, this is faster anyhow */ - /* XXX one difference to Pgrab, first entry is not the exe name */ - return sigar_dlinfo_modules(sigar, procmods); - } - else { - return sigar_pgrab_modules(sigar, pid, procmods); - } -} - -#define TIME_NSEC(t) \ - (SIGAR_SEC2NANO((t).tv_sec) + (sigar_uint64_t)(t).tv_nsec) - -int sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu) -{ - struct lwpinfo info; - - if (id != 0) { - return SIGAR_ENOTIMPL; - } - - _lwp_info(&info); - - cpu->user = TIME_NSEC(info.lwp_utime); - cpu->sys = TIME_NSEC(info.lwp_stime); - cpu->total = TIME_NSEC(info.lwp_utime) + TIME_NSEC(info.lwp_stime); - - return SIGAR_OK; -} - -#include - -int sigar_os_fs_type_get(sigar_file_system_t *fsp) -{ - char *type = fsp->sys_type_name; - - switch (*type) { - case 'u': - if (strEQ(type, "ufs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - /* XXX */ - } - - return fsp->type; -} - -int sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - struct mnttab ent; - sigar_file_system_t *fsp; - FILE *fp = fopen(MNTTAB, "r"); - - if (!fp) { - return errno; - } - - sigar_file_system_list_create(fslist); - - while (getmntent(fp, &ent) == 0) { - if (strstr(ent.mnt_mntopts, "ignore")) { - continue; /* e.g. vold */ - } - - SIGAR_FILE_SYSTEM_LIST_GROW(fslist); - - fsp = &fslist->data[fslist->number++]; - - SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_mountp); - SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_special); - SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_fstype); - SIGAR_SSTRCPY(fsp->options, ent.mnt_mntopts); - sigar_fs_type_init(fsp); - } - - fclose(fp); - - return SIGAR_OK; -} - -typedef struct { - char device[PATH_MAX]; - char name[8]; - int instance; -} fsdev_path_t; - -typedef struct { - char name[256]; - int is_partition; - sigar_disk_usage_t disk; -} iodev_t; - -static fsdev_path_t *get_fsdev_paths(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - int i, ndisk, size; - char buffer[BUFSIZ], *ptr; - char *dev, *inst, *drv; - fsdev_path_t *paths, *mapping; - FILE *fp = fopen("/etc/path_to_inst", "r"); - - if (!fp) { - return NULL; - } - - for (i=0, ndisk=0; inumber; i++) { - sigar_file_system_t *fsp = &fslist->data[i]; - if (fsp->type == SIGAR_FSTYPE_LOCAL_DISK) { - ndisk++; - } - } - - size = sizeof(*paths) * (ndisk+1); - mapping = paths = malloc(size); - memset(mapping, '\0', size); - - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - /* eat dust java */ - char *q; - - SIGAR_SKIP_SPACE(ptr); - if (*ptr == '#') { - continue; - } - if (*ptr == '"') { - ptr++; - } - dev = ptr; - if (!(q = strchr(ptr, '"'))) { - continue; - } - ptr = q+1; - *q = '\0'; - SIGAR_SKIP_SPACE(ptr); - inst = ptr; - while (sigar_isdigit(*ptr)) { - ptr++; - } - *ptr = '\0'; - ptr++; - SIGAR_SKIP_SPACE(ptr); - if (*ptr == '"') { - ptr++; - } - drv = ptr; - if (!(q = strchr(ptr, '"'))) { - continue; - } - *q = '\0'; - - if (!(strEQ(drv, "sd") || - strEQ(drv, "ssd") || - strEQ(drv, "st") || - strEQ(drv, "dad") || - strEQ(drv, "cmdk"))) - { - continue; - } - - paths->instance = atoi(inst); - if (!kstat_lookup(sigar->kc, drv, paths->instance, NULL)) { - continue; - } - - SIGAR_SSTRCPY(paths->device, dev); - SIGAR_SSTRCPY(paths->name, drv); - - if (--ndisk < 0) { - /* XXX prevent overflow */ - break; - } - paths++; - } - fclose(fp); - - return mapping; -} - -static int create_fsdev_cache(sigar_t *sigar) -{ - fsdev_path_t *paths, *mapping; - sigar_file_system_list_t fslist; - int i, j; - int status; - int debug = SIGAR_LOG_IS_DEBUG(sigar); - - sigar->fsdev = sigar_cache_new(15); - - status = sigar_file_system_list_get(sigar, &fslist); - - if (status != SIGAR_OK) { - return status; - } - - if (!(mapping = get_fsdev_paths(sigar, &fslist))) { - sigar_file_system_list_destroy(sigar, &fslist); - return ENOENT; - } - - for (i=0; itype == SIGAR_FSTYPE_LOCAL_DISK) { - char device[PATH_MAX+1], *ptr=device; - int len = readlink(fsp->dev_name, device, sizeof(device)-1); - char *s; - char partition; - - if (len < 0) { - continue; - } - device[len] = '\0'; - - if (debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[fsdev] name=%s, dev=%s", - fsp->dev_name, device); - } - - while (strnEQ(ptr, "../", 3)) { - ptr += 3; - } - if (strnEQ(ptr, "devices", 7)) { - ptr += 7; - } - if ((s = strchr(ptr, ':'))) { - partition = *(s+1); - } - else { - continue; - } - - for (j=0, paths=mapping; paths->name[0]; j++) { - if (strnEQ(paths->device, ptr, strlen(paths->device))) { - sigar_cache_entry_t *ent; - struct stat sb; - int retval = stat(fsp->dir_name, &sb); - iodev_t *iodev; - - if (retval == 0) { - iodev = malloc(sizeof(*iodev)); - - SIGAR_DISK_STATS_INIT(&iodev->disk); - /* e.g. sd9,g - * module == sd - * instance == 9 - * partition == 8 - */ - snprintf(iodev->name, sizeof(iodev->name), "%s%d,%c", - paths->name, paths->instance, partition); - - ent = sigar_cache_get(sigar->fsdev, SIGAR_FSDEV_ID(sb)); - ent->value = iodev; - - if (debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fsdev] map %s -> %s", - fsp->dir_name, iodev->name); - } - } - break; - } - paths++; - } - } - } - - free(mapping); - sigar_file_system_list_destroy(sigar, &fslist); - - return SIGAR_OK; -} - -static int io_kstat_read(sigar_t *sigar, - sigar_disk_usage_t *disk, - kstat_t *ksp) -{ - kstat_io_t *io; - - kstat_read(sigar->kc, ksp, NULL); - - io = (kstat_io_t *)ksp->ks_data; - - disk->reads = io->reads; - disk->writes = io->writes; - disk->read_bytes = io->nread; - disk->write_bytes = io->nwritten; - disk->qtime = io->wlentime; - disk->rtime = io->rlentime; - disk->wtime = io->wlentime; - disk->time = disk->rtime + disk->wtime; - disk->snaptime = ksp->ks_snaptime; - - return SIGAR_OK; -} - - -static int sigar_kstat_disk_usage_get(sigar_t *sigar, const char *name, - sigar_disk_usage_t *disk, - kstat_t **kio) -{ - kstat_t *ksp; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - - for (ksp = sigar->kc->kc_chain; - ksp; - ksp = ksp->ks_next) - { - if (ksp->ks_type != KSTAT_TYPE_IO) { - continue; - } - if (strEQ(ksp->ks_name, name)) { - int status = io_kstat_read(sigar, disk, ksp); - *kio = ksp; - return status; - } - } - - return ENXIO; -} - -static int simple_hash(const char *s) -{ - int hash = 0; - while (*s) { - hash = 31*hash + *s++; - } - return hash; -} - -int sigar_disk_usage_get(sigar_t *sigar, const char *name, - sigar_disk_usage_t *disk) -{ - kstat_t *ksp; - int status; - iodev_t *iodev = NULL; - sigar_cache_entry_t *ent; - sigar_uint64_t id; - - SIGAR_DISK_STATS_INIT(disk); - - if (!sigar->fsdev) { - if (create_fsdev_cache(sigar) != SIGAR_OK) { - return SIGAR_OK; - } - } - - if (*name == '/') { - struct stat sb; - - if (stat(name, &sb) < 0) { - return errno; - } - - id = SIGAR_FSDEV_ID(sb); - ent = sigar_cache_get(sigar->fsdev, id); - if (ent->value == NULL) { - return ENXIO; - } - iodev = (iodev_t *)ent->value; - - status = sigar_kstat_disk_usage_get(sigar, iodev->name, disk, &ksp); - } - else { - status = sigar_kstat_disk_usage_get(sigar, name, disk, &ksp); - if (status != SIGAR_OK) { - return status; - } - id = simple_hash(name); /*XXX*/ - ent = sigar_cache_get(sigar->fsdev, id); - if (ent->value) { - iodev = (iodev_t *)ent->value; - } - else { - ent->value = iodev = malloc(sizeof(*iodev)); - SIGAR_SSTRCPY(iodev->name, name); - SIGAR_DISK_STATS_INIT(&iodev->disk); - } - } - - /* service_time formula derived from opensolaris.org:iostat.c */ - if ((status == SIGAR_OK) && iodev) { - sigar_uint64_t delta; - double avw, avr, tps, mtps; - double etime, hr_etime; - - if (iodev->disk.snaptime) { - delta = disk->snaptime - iodev->disk.snaptime; - } - else { - delta = ksp->ks_crtime - ksp->ks_snaptime; - } - - hr_etime = (double)delta; - if (hr_etime == 0.0) { - hr_etime = (double)NANOSEC; - } - etime = hr_etime / (double)NANOSEC; - - tps = - (((double)(disk->reads - iodev->disk.reads)) / etime) + - (((double)(disk->writes - iodev->disk.writes)) / etime); - - delta = disk->wtime - iodev->disk.wtime; - if (delta) { - avw = (double)delta; - avw /= hr_etime; - } - else { - avw = 0.0; - } - - delta = disk->rtime - iodev->disk.rtime; - if (delta) { - avr = (double)delta; - avr /= hr_etime; - } - else { - avr = 0.0; - } - - disk->queue = avw; - disk->service_time = 0.0; - - if (tps && (avw != 0.0 || avr != 0.0)) { - mtps = 1000.0 / tps; - if (avw != 0.0) { - disk->service_time += avw * mtps; - } - if (avr != 0.0) { - disk->service_time += avr * mtps; - } - } - - memcpy(&iodev->disk, disk, sizeof(iodev->disk)); - } - - return status; -} - -int sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - int status = sigar_statvfs(sigar, dirname, fsusage); - - if (status != SIGAR_OK) { - return status; - } - - fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); - - sigar_disk_usage_get(sigar, dirname, &fsusage->disk); - - return SIGAR_OK; -} - -int sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - processor_info_t stats; - unsigned int i; - int status = SIGAR_OK; - int brand = -1; - sigar_cache_t *chips; - int is_debug = SIGAR_LOG_IS_DEBUG(sigar); - int nsockets = 0; - - if (sigar_kstat_update(sigar) == -1) { /* for sigar->ncpu */ - return errno; - } - - /* - * stats we care about will be the same for each - * online processor, so just grab the first. - */ - for (i=0; incpu; i++) { - processorid_t id = sigar->ks.cpuid[i]; - - if ((status = processor_info(id, &stats)) < 0) { - continue; - } - else { - status = SIGAR_OK; - break; - } - } - - if (status != SIGAR_OK) { - /* should never happen */ - return ENOENT; - } - - sigar_cpu_info_list_create(cpu_infos); - chips = sigar_cache_new(16); - chips->free_value = free_chip_id; - - for (i=0; incpu; i++) { - sigar_cpu_info_t *info; - int chip_id = get_chip_id(sigar, i); - - if (chip_id != -1) { - sigar_cache_entry_t *ent = - sigar_cache_get(chips, chip_id); - - if (ent->value) { - if (!sigar->cpu_list_cores) { - continue; - } - } - else { - ++nsockets; - ent->value = chips; /*anything non-NULL*/ - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[cpu_list] Merging info of" - " logical processors for chip_id=%d", - chip_id); - } - } - } - else { - ++nsockets; - } - - SIGAR_CPU_INFO_LIST_GROW(cpu_infos); - - info = &cpu_infos->data[cpu_infos->number++]; - - SIGAR_SSTRCPY(info->model, stats.pi_processor_type); - - if (brand == -1) { - brand = get_chip_brand(sigar, i, info); - } - - if (strEQ(info->model, "i386")) { - if (!brand) { - /* assume Intel on x86 */ - SIGAR_SSTRCPY(info->vendor, "Intel"); - } - SIGAR_SSTRCPY(info->model, "x86"); - } - else { - if (!brand) { - /* assume Sun */ - SIGAR_SSTRCPY(info->vendor, "Sun"); - } - /* s/sparc/Sparc/ */ - info->model[0] = toupper(info->model[0]); - } - - if (brand) { - SIGAR_SSTRCPY(info->vendor, cpu_infos->data[0].vendor); - } - - info->mhz = stats.pi_clock; - info->cache_size = SIGAR_FIELD_NOTIMPL; /*XXX*/ - } - - sigar_cache_destroy(chips); - - for (i=0; inumber; i++) { - sigar_cpu_info_t *info = &cpu_infos->data[i]; - info->total_sockets = nsockets; - info->total_cores = sigar->ncpu; - info->cores_per_socket = sigar->ncpu / nsockets; - } - - return SIGAR_OK; -} - -int sigar_net_route_list_get(sigar_t *sigar, - sigar_net_route_list_t *routelist) - -{ - char *data; - int len, rc; - struct opthdr *op; - size_t nread=0, size=0; - const char *size_from; - - sigar_net_route_list_create(routelist); - - while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) { - mib2_ipRouteEntry_t *entry; - char *end; - - if (op->level != MIB2_IP) { - continue; - } - - if (op->name == 0) { - /* we want to use this size for bincompat */ - size = ((mib2_ip_t *)data)->ipRouteEntrySize; - continue; - } - else if (op->name != MIB2_IP_21) { - continue; - } - - if (size == 0) { - size_from = "sizeof"; - size = sizeof(*entry); - } - else { - size_from = "mib2_ip"; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[route_list] ipRouteEntrySize=%d (from %s)", - size, size_from); - } - - for (entry = (mib2_ipRouteEntry_t *)data, end = data + len; - (char *)entry < end; - nread+=size, entry = (mib2_ipRouteEntry_t *)((char *)data+nread)) - { - sigar_net_route_t *route; - int type = entry->ipRouteInfo.re_ire_type; - - /* filter same as netstat -r */ - if ((type == IRE_CACHE) || - (type == IRE_BROADCAST) || - (type == IRE_LOCAL)) - { - continue; - } - - SIGAR_NET_ROUTE_LIST_GROW(routelist); - route = &routelist->data[routelist->number++]; - - sigar_net_address_set(route->destination, - entry->ipRouteDest); - - sigar_net_address_set(route->gateway, - entry->ipRouteNextHop); - - sigar_net_address_set(route->mask, - entry->ipRouteMask); - - route->refcnt = entry->ipRouteInfo.re_ref; - route->irtt = entry->ipRouteInfo.re_rtt; - route->metric = entry->ipRouteMetric1; - - SIGAR_SSTRCPY(route->ifname, entry->ipRouteIfIndex.o_bytes); - - route->flags = RTF_UP; - if ((route->destination.addr.in == 0) && - (route->mask.addr.in == 0)) - { - route->flags |= RTF_GATEWAY; - } - - route->use = route->window = route->mtu = - SIGAR_FIELD_NOTIMPL; /*XXX*/ - } - } - - if (rc != GET_MIB2_EOD) { - close_mib2(&sigar->mib2); - return SIGAR_EMIB2; - } - - return SIGAR_OK; -} - -static void ifstat_kstat_common(sigar_net_interface_stat_t *ifstat, - kstat_named_t *data, int ndata) -{ - int i; - - for (i=0; itx_collisions = value; - } - break; - case 'd': - if (strEQ(ptr, "drop")) { - ifstat->rx_dropped = value; - ifstat->tx_dropped = value; - } - break; - case 'i': - if (strEQ(ptr, "ipackets")) { - if (ifstat->rx_packets == 0) { - ifstat->rx_packets = value; - } - } - else if (strEQ(ptr, "ipackets64")) { - ifstat->rx_packets = data[i].value.ui64; - } - else if (strEQ(ptr, "ierrors")) { - ifstat->rx_errors = value; - } - else if (strEQ(ptr, "ifspeed")) { - ifstat->speed = value; - } - break; - case 'f': - if (strEQ(ptr, "framing")) { - ifstat->rx_frame = value; - } - break; - case 'm': - if (strEQ(ptr, "missed")) { - ifstat->rx_dropped = value; - ifstat->tx_dropped = value; - } - break; - case 'n': - if (strEQ(ptr, "nocarrier")) { - ifstat->tx_carrier = value; - } - break; - case 'o': - if (strEQ(ptr, "obytes")) { - if (ifstat->tx_bytes == 0) { - ifstat->tx_bytes = value; - } - } - else if (strEQ(ptr, "obytes64")) { - ifstat->tx_bytes = data[i].value.ui64; - } - else if (strEQ(ptr, "oerrors")) { - ifstat->tx_errors = value; - } - else if (strEQ(ptr, "oflo")) { - ifstat->tx_overruns = value; - } - else if (strEQ(ptr, "opackets")) { - if (ifstat->tx_packets == 0) { - ifstat->tx_packets = value; - } - } - else if (strEQ(ptr, "opackets64")) { - ifstat->tx_packets = data[i].value.ui64; - } - else if (strEQ(ptr, "toolong_errors")) { - ifstat->tx_overruns = value; - } - break; - case 'r': - if (strEQ(ptr, "rbytes")) { - if (ifstat->rx_bytes == 0) { - ifstat->rx_bytes = value; - } - } - else if (strEQ(ptr, "rbytes64")) { - ifstat->rx_bytes = data[i].value.ui64; - } - else if (strEQ(ptr, "rx_overflow")) { - ifstat->rx_overruns = value; - } - break; - default: - break; - } - } -} - -static int sigar_net_ifstat_get_any(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - kstat_ctl_t *kc = sigar->kc; - kstat_t *ksp; - kstat_named_t *data; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - - if (!(ksp = kstat_lookup(kc, NULL, -1, (char *)name))) { - return ENXIO; - } - - if (kstat_read(kc, ksp, NULL) < 0) { - return ENOENT; - } - - data = (kstat_named_t *)ksp->ks_data; - - ifstat_kstat_common(ifstat, data, ksp->ks_ndata); - - return SIGAR_OK; -} - -/* loopback interface only has rx/tx packets */ -static int sigar_net_ifstat_get_lo(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - ifstat->rx_packets = 0; - ifstat->rx_bytes = SIGAR_FIELD_NOTIMPL; - ifstat->rx_errors = SIGAR_FIELD_NOTIMPL; - ifstat->rx_dropped = SIGAR_FIELD_NOTIMPL; - ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->rx_frame = SIGAR_FIELD_NOTIMPL; - - ifstat->tx_packets = 0; - ifstat->tx_bytes = SIGAR_FIELD_NOTIMPL; - ifstat->tx_errors = SIGAR_FIELD_NOTIMPL; - ifstat->tx_dropped = SIGAR_FIELD_NOTIMPL; - ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->tx_collisions = SIGAR_FIELD_NOTIMPL; - ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL; - - ifstat->speed = SIGAR_FIELD_NOTIMPL; - - return sigar_net_ifstat_get_any(sigar, name, ifstat); -} - -int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - ifstat->speed = SIGAR_FIELD_NOTIMPL; - - if (strnEQ(name, "lo", 2)) { - return sigar_net_ifstat_get_lo(sigar, name, ifstat); - } - else { - SIGAR_ZERO(ifstat); - return sigar_net_ifstat_get_any(sigar, name, ifstat); - } -} - -int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - int sock; - struct lifreq lifr; - - if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - return errno; - } - - SIGAR_SSTRCPY(lifr.lifr_name, name); - - if (ioctl(sock, SIOCGLIFADDR, &lifr) == 0) { - struct in6_addr *addr = SIGAR_SIN6_ADDR(&lifr.lifr_addr); - - sigar_net_address6_set(ifconfig->address6, addr); - sigar_net_interface_scope6_set(ifconfig, addr); - ifconfig->prefix6_length = lifr.lifr_addrlen; - } - - close(sock); - return SIGAR_OK; -} - -#define TCPQ_SIZE(s) ((s) >= 0 ? (s) : 0) - -static int tcp_connection_get(sigar_net_connection_walker_t *walker, - struct mib2_tcpConnEntry *entry, - int len) -{ - int flags = walker->flags; - int status; - char *end = (char *)entry + len; - - while ((char *)entry < end) { - int state = entry->tcpConnEntryInfo.ce_state; - - if (((flags & SIGAR_NETCONN_SERVER) && (state == TCPS_LISTEN)) || - ((flags & SIGAR_NETCONN_CLIENT) && (state != TCPS_LISTEN))) - { - sigar_net_connection_t conn; - - SIGAR_ZERO(&conn); - - sigar_net_address_set(conn.local_address, entry->tcpConnLocalAddress); - sigar_net_address_set(conn.remote_address, entry->tcpConnRemAddress); - - conn.local_port = entry->tcpConnLocalPort; - conn.remote_port = entry->tcpConnRemPort; - conn.type = SIGAR_NETCONN_TCP; - conn.send_queue = - TCPQ_SIZE(entry->tcpConnEntryInfo.ce_snxt - - entry->tcpConnEntryInfo.ce_suna - 1); - conn.receive_queue = - TCPQ_SIZE(entry->tcpConnEntryInfo.ce_rnxt - - entry->tcpConnEntryInfo.ce_rack); - - switch (state) { - case TCPS_CLOSED: - conn.state = SIGAR_TCP_CLOSE; - break; - case TCPS_IDLE: - conn.state = SIGAR_TCP_IDLE; - break; - case TCPS_BOUND: - conn.state = SIGAR_TCP_BOUND; - break; - case TCPS_LISTEN: - conn.state = SIGAR_TCP_LISTEN; - break; - case TCPS_SYN_SENT: - conn.state = SIGAR_TCP_SYN_SENT; - break; - case TCPS_SYN_RCVD: - conn.state = SIGAR_TCP_SYN_RECV; - break; - case TCPS_ESTABLISHED: - conn.state = SIGAR_TCP_ESTABLISHED; - break; - case TCPS_CLOSE_WAIT: - conn.state = SIGAR_TCP_CLOSE_WAIT; - break; - case TCPS_FIN_WAIT_1: - conn.state = SIGAR_TCP_FIN_WAIT1; - break; - case TCPS_CLOSING: - conn.state = SIGAR_TCP_CLOSING; - break; - case TCPS_LAST_ACK: - conn.state = SIGAR_TCP_LAST_ACK; - break; - case TCPS_FIN_WAIT_2: - conn.state = SIGAR_TCP_FIN_WAIT2; - break; - case TCPS_TIME_WAIT: - conn.state = SIGAR_TCP_TIME_WAIT; - break; - default: - conn.state = SIGAR_TCP_UNKNOWN; - break; - } - - status = walker->add_connection(walker, &conn); - if (status != SIGAR_OK) { - return status; - } - } - - entry++; - } - - return SIGAR_OK; -} - -static int udp_connection_get(sigar_net_connection_walker_t *walker, - struct mib2_udpEntry *entry, - int len) -{ - int flags = walker->flags; - int status; - char *end = (char *)entry + len; - - while ((char *)entry < end) { - int state = entry->udpEntryInfo.ue_state; - - /* XXX dunno if this state check is right */ - if (((flags & SIGAR_NETCONN_SERVER) && (state == MIB2_UDP_idle)) || - ((flags & SIGAR_NETCONN_CLIENT) && (state != MIB2_UDP_idle))) - { - sigar_net_connection_t conn; - - SIGAR_ZERO(&conn); - - sigar_net_address_set(conn.local_address, entry->udpLocalAddress); - sigar_net_address_set(conn.remote_address, 0); - - conn.local_port = entry->udpLocalPort; - conn.remote_port = 0; - conn.type = SIGAR_NETCONN_UDP; - - status = walker->add_connection(walker, &conn); - if (status != SIGAR_OK) { - return status; - } - } - - entry++; - } - - return SIGAR_OK; -} - -int sigar_net_connection_walk(sigar_net_connection_walker_t *walker) -{ - sigar_t *sigar = walker->sigar; - int flags = walker->flags; - int status; - int want_tcp = flags & SIGAR_NETCONN_TCP; - int want_udp = flags & SIGAR_NETCONN_UDP; - char *data; - int len; - int rc; - struct opthdr *op; - - while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) { - if ((op->level == MIB2_TCP) && - (op->name == MIB2_TCP_13) && - want_tcp) - { - status = - tcp_connection_get(walker, - (struct mib2_tcpConnEntry *)data, - len); - } - else if ((op->level == MIB2_UDP) && - (op->name == MIB2_UDP_5) && - want_udp) - { - status = - udp_connection_get(walker, - (struct mib2_udpEntry *)data, - len); - } - else { - status = SIGAR_OK; - } - - if (status != SIGAR_OK) { - break; - } - } - - if (rc != GET_MIB2_EOD) { - close_mib2(&sigar->mib2); - return SIGAR_EMIB2; - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp) -{ - char *data; - int len; - int rc; - struct opthdr *op; - mib2_tcp_t *mib = NULL; - - while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) { - if ((op->level == MIB2_TCP) && (op->name == 0)) { - mib = (mib2_tcp_t *)data; - break; - } - } - - if (mib) { - tcp->active_opens = mib->tcpActiveOpens; - tcp->passive_opens = mib->tcpPassiveOpens; - tcp->attempt_fails = mib->tcpAttemptFails; - tcp->estab_resets = mib->tcpEstabResets; - tcp->curr_estab = mib->tcpCurrEstab; - tcp->in_segs = mib->tcpInSegs; - tcp->out_segs = mib->tcpOutSegs; - tcp->retrans_segs = mib->tcpRetransSegs; - tcp->in_errs = SIGAR_FIELD_NOTIMPL; /* XXX mib2_ip_t.tcpInErrs */ - tcp->out_rsts = mib->tcpOutRsts; - return SIGAR_OK; - } - else { - return SIGAR_ENOTIMPL; - } -} - -static int sigar_nfs_get(sigar_t *sigar, - char *type, - char **names, - char *nfs) -{ - size_t offset; - kstat_t *ksp; - int i; - - if (sigar_kstat_update(sigar) == -1) { - return errno; - } - - if (!(ksp = kstat_lookup(sigar->kc, "nfs", 0, type))) { - return SIGAR_ENOTIMPL; - } - - if (kstat_read(sigar->kc, ksp, NULL) < 0) { - return errno; - } - - for (i=0, offset=0; - names[i]; - i++, offset+=sizeof(sigar_uint64_t)) - { - sigar_uint64_t val; - kstat_named_t *kv = - kstat_data_lookup(ksp, names[i]); - - if (kv) { - val = kv->value.ui64; - } - else { - val = -1; - } - - *(sigar_uint64_t *)((char *)nfs + offset) = val; - } - - return SIGAR_OK; -} - -static char *nfs_v2_names[] = { - "null", - "getattr", - "setattr", - "root", - "lookup", - "readlink", - "read", - "wrcache", - "write", - "create", - "remove", - "rename", - "link", - "symlink", - "mkdir", - "rmdir", - "readdir", - "statfs", - NULL -}; - -int sigar_nfs_client_v2_get(sigar_t *sigar, - sigar_nfs_client_v2_t *nfs) -{ - return sigar_nfs_get(sigar, "rfsreqcnt_v2", nfs_v2_names, (char *)nfs); -} - -int sigar_nfs_server_v2_get(sigar_t *sigar, - sigar_nfs_server_v2_t *nfs) -{ - return sigar_nfs_get(sigar, "rfsproccnt_v2", nfs_v2_names, (char *)nfs); -} - -static char *nfs_v3_names[] = { - "null", - "getattr", - "setattr", - "lookup", - "access", - "readlink", - "read", - "write", - "create", - "mkdir", - "symlink", - "mknod", - "remove", - "rmdir", - "rename", - "link", - "readdir", - "readdirplus", - "fsstat", - "fsinfo", - "pathconf", - "commit", - NULL -}; - -int sigar_nfs_client_v3_get(sigar_t *sigar, - sigar_nfs_client_v3_t *nfs) -{ - return sigar_nfs_get(sigar, "rfsreqcnt_v3", nfs_v3_names, (char *)nfs); -} - -int sigar_nfs_server_v3_get(sigar_t *sigar, - sigar_nfs_server_v3_t *nfs) -{ - return sigar_nfs_get(sigar, "rfsproccnt_v3", nfs_v3_names, (char *)nfs); -} - -int sigar_arp_list_get(sigar_t *sigar, - sigar_arp_list_t *arplist) -{ - char *data; - int len, rc; - struct opthdr *op; - size_t nread=0, size=0; - const char *size_from; - - sigar_arp_list_create(arplist); - - while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) { - mib2_ipNetToMediaEntry_t *entry; - char *end; - - if (op->level != MIB2_IP) { - continue; - } - - if (op->name == 0) { - /* we want to use this size for bincompat */ - size = ((mib2_ip_t *)data)->ipNetToMediaEntrySize; - continue; - } - else if (op->name != MIB2_IP_MEDIA) { - continue; - } - - if (size == 0) { - size_from = "sizeof"; - size = sizeof(*entry); - } - else { - size_from = "mib2_ip"; - } - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[arp_list] ipNetToMediaEntrySize=%d (from %s)", - size, size_from); - } - - for (entry = (mib2_ipNetToMediaEntry_t *)data, end = data + len; - (char *)entry < end; - nread+=size, entry = (mib2_ipNetToMediaEntry_t *)((char *)data+nread)) - { - sigar_arp_t *arp; - - SIGAR_ARP_LIST_GROW(arplist); - arp = &arplist->data[arplist->number++]; - - sigar_net_address_set(arp->address, - entry->ipNetToMediaNetAddress); - - sigar_net_address_mac_set(arp->hwaddr, - entry->ipNetToMediaPhysAddress.o_bytes, - entry->ipNetToMediaPhysAddress.o_length); - - SIGAR_SSTRCPY(arp->ifname, entry->ipNetToMediaIfIndex.o_bytes); - - arp->flags = entry->ipNetToMediaInfo.ntm_flags; - SIGAR_SSTRCPY(arp->type, "ether"); /*XXX*/ - } - } - - if (rc != GET_MIB2_EOD) { - close_mib2(&sigar->mib2); - return SIGAR_EMIB2; - } - - return SIGAR_OK; -} - -static int find_port(sigar_t *sigar, struct ps_prochandle *phandle, - sigar_pid_t pid, unsigned long port) -{ - DIR *dirp; - struct dirent *ent; - char pname[PATH_MAX]; - struct stat64 statb; - int found=0; - - sprintf(pname, "/proc/%d/fd", (int)pid); - - if (!(dirp = opendir(pname))) { - return 0; - } - - while ((ent = readdir(dirp))) { - int fd; - - if (!sigar_isdigit(ent->d_name[0])) { - continue; - } - fd = atoi(ent->d_name); - - if (sigar->pfstat64(phandle, fd, &statb) == -1) { - continue; - } - - if ((statb.st_mode & S_IFMT) == S_IFSOCK) { - struct sockaddr_in sin; - struct sockaddr *sa = (struct sockaddr *)&sin; - socklen_t len = sizeof(sin); - int opt, optsz, rc; - - optsz = sizeof(opt); - rc = sigar->pgetsockopt(phandle, fd, SOL_SOCKET, SO_TYPE, &opt, &optsz); - if (rc != 0) { - continue; - } - if (opt != SOCK_STREAM) { - continue; - } - optsz = sizeof(opt); - rc = sigar->pgetsockopt(phandle, fd, SOL_SOCKET, SO_ACCEPTCONN, &opt, &optsz); - if (rc != 0) { - continue; - } - if (opt != SO_ACCEPTCONN) { - continue; - } - - rc = sigar->pgetsockname(phandle, fd, sa, &len); - if (rc != 0) { - continue; - } - - if ((sa->sa_family == AF_INET) || - (sa->sa_family == AF_INET6)) - { - if (ntohs(sin.sin_port) == port) { - found = 1; - break; - } - } - } - } - - closedir(dirp); - - return found; -} - -/* derived from /usr/bin/pfiles.c */ -int sigar_proc_port_get(sigar_t *sigar, int protocol, - unsigned long port, sigar_pid_t *pid) -{ - sigar_proc_list_t pids; - int i, status, found=0; - - if (sigar->solaris_version < 10) { - return SIGAR_ENOTIMPL; - } - - if ((status = sigar_init_libproc(sigar)) != SIGAR_OK) { - return SIGAR_ENOTIMPL; - } - status = sigar_proc_list_get(sigar, &pids); - if (status != SIGAR_OK) { - return status; - } - - for (i=0; ipcreate_agent(phandle) == 0) { - found = find_port(sigar, phandle, ps_id, port); - sigar->pdestroy_agent(phandle); - } - - sigar->pfree(phandle); - if (found) { - *pid = ps_id; - break; - } - } - - sigar_proc_list_destroy(sigar, &pids); - - return found ? SIGAR_OK : ENOENT; -} - -int sigar_os_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sys_info) -{ - char *vendor_version; - - sysinfo(SI_ARCHITECTURE, sys_info->arch, sizeof(sys_info->arch)); - - SIGAR_SSTRCPY(sys_info->name, "Solaris"); - SIGAR_SSTRCPY(sys_info->vendor, "Sun Microsystems"); - - if (strEQ(sys_info->version, "5.6")) { - vendor_version = "2.6"; - } - else { - if ((vendor_version = strchr(sys_info->version, '.'))) { - ++vendor_version; - } - else { - vendor_version = sys_info->version; - } - } - - SIGAR_SSTRCPY(sys_info->vendor_version, vendor_version); - - snprintf(sys_info->description, - sizeof(sys_info->description), - "%s %s", - sys_info->name, sys_info->vendor_version); - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/os/win32/peb.c b/vendor/sigar/src/os/win32/peb.c deleted file mode 100644 index d8b2eca73..000000000 --- a/vendor/sigar/src/os/win32/peb.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2004, 2006-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * functions for getting info from the Process Environment Block - */ -#define UNICODE -#define _UNICODE - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_os.h" -#include - -void dllmod_init_ntdll(sigar_t *sigar); - -#define sigar_NtQueryInformationProcess \ - sigar->ntdll.query_proc_info.func - -static int sigar_pbi_get(sigar_t *sigar, HANDLE proc, PEB *peb) -{ - int status; - PROCESS_BASIC_INFORMATION pbi; - DWORD size=sizeof(pbi); - - dllmod_init_ntdll(sigar); - - if (!sigar_NtQueryInformationProcess) { - return SIGAR_ENOTIMPL; - } - - SIGAR_ZERO(&pbi); - status = - sigar_NtQueryInformationProcess(proc, - ProcessBasicInformation, - &pbi, - size, NULL); - if (status != ERROR_SUCCESS) { - return status; - } - - if (!pbi.PebBaseAddress) { - /* likely we are 32-bit, pid process is 64-bit */ - return ERROR_DATATYPE_MISMATCH; - } - - size = sizeof(*peb); - - if (ReadProcessMemory(proc, pbi.PebBaseAddress, peb, size, NULL)) { - return SIGAR_OK; - } - else { - return GetLastError(); - } -} - -static int sigar_rtl_get(sigar_t *sigar, HANDLE proc, - RTL_USER_PROCESS_PARAMETERS *rtl) -{ - PEB peb; - int status = sigar_pbi_get(sigar, proc, &peb); - DWORD size=sizeof(*rtl); - - if (status != SIGAR_OK) { - return status; - } - - if (ReadProcessMemory(proc, peb.ProcessParameters, rtl, size, NULL)) { - return SIGAR_OK; - } - else { - return GetLastError(); - } -} - -#define rtl_bufsize(buf, uc) \ - ((sizeof(buf) < uc.Length) ? sizeof(buf) : uc.Length) - -int sigar_proc_exe_peb_get(sigar_t *sigar, HANDLE proc, - sigar_proc_exe_t *procexe) -{ - int status; - WCHAR buf[MAX_PATH+1]; - RTL_USER_PROCESS_PARAMETERS rtl; - DWORD size; - - procexe->name[0] = '\0'; - procexe->cwd[0] = '\0'; - - if ((status = sigar_rtl_get(sigar, proc, &rtl)) != SIGAR_OK) { - return status; - } - - size = rtl_bufsize(buf, rtl.ImagePathName); - memset(buf, '\0', sizeof(buf)); - - if ((size > 0) && - ReadProcessMemory(proc, rtl.ImagePathName.Buffer, buf, size, NULL)) - { - SIGAR_W2A(buf, procexe->name, sizeof(procexe->name)); - } - - size = rtl_bufsize(buf, rtl.CurrentDirectoryName); - memset(buf, '\0', sizeof(buf)); - - if ((size > 0) && - ReadProcessMemory(proc, rtl.CurrentDirectoryName.Buffer, buf, size, NULL)) - { - SIGAR_W2A(buf, procexe->cwd, sizeof(procexe->cwd)); - } - - return SIGAR_OK; -} - -int sigar_parse_proc_args(sigar_t *sigar, WCHAR *buf, - sigar_proc_args_t *procargs) -{ - char arg[SIGAR_CMDLINE_MAX]; - LPWSTR *args; - int num, i; - - if (!buf) { - buf = GetCommandLine(); - } - - args = CommandLineToArgvW(buf, &num); - - if (args == NULL) { - return SIGAR_OK; - } - - for (i=0; idata[procargs->number++] = sigar_strdup(arg); - } - - GlobalFree(args); - - return SIGAR_OK; -} - -int sigar_proc_args_peb_get(sigar_t *sigar, HANDLE proc, - sigar_proc_args_t *procargs) -{ - int status; - WCHAR buf[SIGAR_CMDLINE_MAX]; - RTL_USER_PROCESS_PARAMETERS rtl; - DWORD size; - - if ((status = sigar_rtl_get(sigar, proc, &rtl)) != SIGAR_OK) { - return status; - } - - size = rtl_bufsize(buf, rtl.CommandLine); - if (size <= 0) { - return ERROR_DATATYPE_MISMATCH; /* fallback to wmi */ - } - memset(buf, '\0', sizeof(buf)); - - if (ReadProcessMemory(proc, rtl.CommandLine.Buffer, buf, size, NULL)) { - return sigar_parse_proc_args(sigar, buf, procargs); - } - else { - return GetLastError(); - } -} - -int sigar_proc_env_peb_get(sigar_t *sigar, HANDLE proc, - WCHAR *buf, DWORD size) -{ - int status; - RTL_USER_PROCESS_PARAMETERS rtl; - MEMORY_BASIC_INFORMATION info; - - if ((status = sigar_rtl_get(sigar, proc, &rtl)) != SIGAR_OK) { - return status; - } - - memset(buf, '\0', size); - /* -2 to ensure \0\0 terminator */ - size -= 2; - - if (VirtualQueryEx(proc, rtl.Environment, &info, sizeof(info))) { - if (size > info.RegionSize) { - /* ReadProcessMemory beyond region would fail */ - size = info.RegionSize; - } - } - - if (ReadProcessMemory(proc, rtl.Environment, buf, size, NULL)) { - return SIGAR_OK; - } - else { - return GetLastError(); - } -} diff --git a/vendor/sigar/src/os/win32/sigar_os.h b/vendor/sigar/src/os/win32/sigar_os.h deleted file mode 100755 index cf61c25f4..000000000 --- a/vendor/sigar/src/os/win32/sigar_os.h +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_OS_H -#define SIGAR_OS_H - -#if !defined(MSVC) && defined(_MSC_VER) -#define MSVC -#endif - -#ifdef MSVC -#define WIN32_LEAN_AND_MEAN -#define snprintf _snprintf -#if _MSC_VER <= 1200 -#define SIGAR_USING_MSC6 /* Visual Studio version 6 */ -#define HAVE_MIB_IPADDRROW_WTYPE 0 -#else -#define HAVE_MIB_IPADDRROW_WTYPE 1 -#endif -#else -/* Cross compiling */ -#define _WIN32_WINNT 0x0501 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sigar_util.h" - -#ifdef MSVC -# define INT64_C(val) val##i64 -# define SIGAR_DLLFUNC(api, name) \ - struct { \ - const char *name; \ - ##api##_##name func; \ - } ##name -#else -/* The GCC compiler doesn't require/accept the ## prefix */ -# define INT64_C(val) val##L -# define SIGAR_DLLFUNC(api, name) \ - struct { \ - const char *name; \ - api##_##name func; \ - } name -#endif - -/* see apr/include/arch/win32/atime.h */ -#define EPOCH_DELTA INT64_C(11644473600000000) - -#define SIGAR_CMDLINE_MAX 4096<<2 - -/* XXX: support CP_UTF8 ? */ - -#define SIGAR_A2W(lpa, lpw, bytes) \ - (lpw[0] = 0, MultiByteToWideChar(CP_ACP, 0, \ - lpa, -1, lpw, (bytes/sizeof(WCHAR)))) - -#define SIGAR_W2A(lpw, lpa, chars) \ - (lpa[0] = '\0', WideCharToMultiByte(CP_ACP, 0, \ - lpw, -1, (LPSTR)lpa, chars, \ - NULL, NULL)) - -/* iptypes.h from vc7, not available in vc6 */ -/* copy from PSDK if using vc6 */ -#include "iptypes.h" - -/* from wtsapi32.h not in vs6.0 */ -typedef enum { - WTSInitialProgram, - WTSApplicationName, - WTSWorkingDirectory, - WTSOEMId, - WTSSessionId, - WTSUserName, - WTSWinStationName, - WTSDomainName, - WTSConnectState, - WTSClientBuildNumber, - WTSClientName, - WTSClientDirectory, - WTSClientProductId, - WTSClientHardwareId, - WTSClientAddress, - WTSClientDisplay, - WTSClientProtocolType, -} WTS_INFO_CLASS; - -typedef enum _WTS_CONNECTSTATE_CLASS { - WTSActive, - WTSConnected, - WTSConnectQuery, - WTSShadow, - WTSDisconnected, - WTSIdle, - WTSListen, - WTSReset, - WTSDown, - WTSInit -} WTS_CONNECTSTATE_CLASS; - -#define WTS_PROTOCOL_TYPE_CONSOLE 0 -#define WTS_PROTOCOL_TYPE_ICA 1 -#define WTS_PROTOCOL_TYPE_RDP 2 - -typedef struct _WTS_SESSION_INFO { - DWORD SessionId; - LPTSTR pWinStationName; - DWORD State; -} WTS_SESSION_INFO, *PWTS_SESSION_INFO; - -typedef struct _WTS_PROCESS_INFO { - DWORD SessionId; - DWORD ProcessId; - LPSTR pProcessName; - PSID pUserSid; -} WTS_PROCESS_INFO, *PWTS_PROCESS_INFO; - -typedef struct _WTS_CLIENT_ADDRESS { - DWORD AddressFamily; - BYTE Address[20]; -} WTS_CLIENT_ADDRESS, *PWTS_CLIENT_ADDRESS; - -/* the WINSTATION_INFO stuff here is undocumented - * got the howto from google groups: - * http://redirx.com/?31gy - */ -typedef enum _WINSTATION_INFO_CLASS { - WinStationInformation = 8 -} WINSTATION_INFO_CLASS; - -typedef struct _WINSTATION_INFO { - BYTE Reserved1[72]; - ULONG SessionId; - BYTE Reserved2[4]; - FILETIME ConnectTime; - FILETIME DisconnectTime; - FILETIME LastInputTime; - FILETIME LoginTime; - BYTE Reserved3[1096]; - FILETIME CurrentTime; -} WINSTATION_INFO, *PWINSTATION_INFO; - -/* end wtsapi32.h */ - -#ifdef SIGAR_USING_MSC6 - -/* from winbase.h not in vs6.0 */ -typedef struct { - DWORD dwLength; - DWORD dwMemoryLoad; - DWORDLONG ullTotalPhys; - DWORDLONG ullAvailPhys; - DWORDLONG ullTotalPageFile; - DWORDLONG ullAvailPageFile; - DWORDLONG ullTotalVirtual; - DWORDLONG ullAvailVirtual; - DWORDLONG ullAvailExtendedVirtual; -} MEMORYSTATUSEX; - -/* service manager stuff not in vs6.0 */ -typedef struct _SERVICE_STATUS_PROCESS { - DWORD dwServiceType; - DWORD dwCurrentState; - DWORD dwControlsAccepted; - DWORD dwWin32ExitCode; - DWORD dwServiceSpecificExitCode; - DWORD dwCheckPoint; - DWORD dwWaitHint; - DWORD dwProcessId; - DWORD dwServiceFlags; -} SERVICE_STATUS_PROCESS; - -typedef enum { - SC_STATUS_PROCESS_INFO = 0 -} SC_STATUS_TYPE; - -#ifndef ERROR_DATATYPE_MISMATCH -#define ERROR_DATATYPE_MISMATCH 1629L -#endif - -#endif /* _MSC_VER */ - -#include - -/* undocumented structures */ -typedef struct { - DWORD dwState; - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwRemoteAddr; - DWORD dwRemotePort; - DWORD dwProcessId; -} MIB_TCPEXROW, *PMIB_TCPEXROW; - -typedef struct { - DWORD dwNumEntries; - MIB_TCPEXROW table[ANY_SIZE]; -} MIB_TCPEXTABLE, *PMIB_TCPEXTABLE; - -typedef struct { - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwProcessId; -} MIB_UDPEXROW, *PMIB_UDPEXROW; - -typedef struct { - DWORD dwNumEntries; - MIB_UDPEXROW table[ANY_SIZE]; -} MIB_UDPEXTABLE, *PMIB_UDPEXTABLE; - -/* end undocumented structures */ - -/* no longer in the standard header files */ -typedef struct { - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER DpcTime; - LARGE_INTEGER InterruptTime; - ULONG InterruptCount; -} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - -#define SystemProcessorPerformanceInformation 8 - -/* PEB decls from msdn docs w/ slight mods */ -#define ProcessBasicInformation 0 - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - -typedef struct _PEB_LDR_DATA { - BYTE Reserved1[8]; - PVOID Reserved2[3]; - LIST_ENTRY InMemoryOrderModuleList; -} PEB_LDR_DATA, *PPEB_LDR_DATA; - -typedef struct RTL_DRIVE_LETTER_CURDIR { - USHORT Flags; - USHORT Length; - ULONG TimeStamp; - UNICODE_STRING DosPath; -} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; - -/* from: http://source.winehq.org/source/include/winternl.h */ -typedef struct _RTL_USER_PROCESS_PARAMETERS { - ULONG AllocationSize; - ULONG Size; - ULONG Flags; - ULONG DebugFlags; - HANDLE hConsole; - ULONG ProcessGroup; - HANDLE hStdInput; - HANDLE hStdOutput; - HANDLE hStdError; - UNICODE_STRING CurrentDirectoryName; - HANDLE CurrentDirectoryHandle; - UNICODE_STRING DllPath; - UNICODE_STRING ImagePathName; - UNICODE_STRING CommandLine; - PWSTR Environment; - ULONG dwX; - ULONG dwY; - ULONG dwXSize; - ULONG dwYSize; - ULONG dwXCountChars; - ULONG dwYCountChars; - ULONG dwFillAttribute; - ULONG dwFlags; - ULONG wShowWindow; - UNICODE_STRING WindowTitle; - UNICODE_STRING Desktop; - UNICODE_STRING ShellInfo; - UNICODE_STRING RuntimeInfo; - RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; -} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; - -/* from msdn docs -typedef struct _RTL_USER_PROCESS_PARAMETERS { - BYTE Reserved1[16]; - PVOID Reserved2[10]; - UNICODE_STRING ImagePathName; - UNICODE_STRING CommandLine; -} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; -*/ - -typedef struct _PEB { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PPEB_LDR_DATA Ldr; - PRTL_USER_PROCESS_PARAMETERS ProcessParameters; - BYTE Reserved4[104]; - PVOID Reserved5[52]; - /*PPS_POST_PROCESS_INIT_ROUTINE*/ PVOID PostProcessInitRoutine; - BYTE Reserved6[128]; - PVOID Reserved7[1]; - ULONG SessionId; -} PEB, *PPEB; - -typedef struct _PROCESS_BASIC_INFORMATION { - PVOID Reserved1; - PPEB PebBaseAddress; - PVOID Reserved2[2]; - /*ULONG_PTR*/ UINT_PTR UniqueProcessId; - PVOID Reserved3; -} PROCESS_BASIC_INFORMATION; - -typedef struct { - sigar_pid_t pid; - int ppid; - int priority; - time_t mtime; - sigar_uint64_t size; - sigar_uint64_t resident; - char name[SIGAR_PROC_NAME_LEN]; - char state; - sigar_uint64_t handles; - sigar_uint64_t threads; - sigar_uint64_t page_faults; -} sigar_win32_pinfo_t; - -typedef struct { - const char *name; - HINSTANCE handle; -} sigar_dll_handle_t; - -typedef struct { - const char *name; - FARPROC func; -} sigar_dll_func_t; - -typedef struct { - const char *name; - HINSTANCE handle; - sigar_dll_func_t funcs[12]; -} sigar_dll_module_t; - -/* wtsapi.dll */ -typedef BOOL (CALLBACK *wtsapi_enum_sessions)(HANDLE, - DWORD, - DWORD, - PWTS_SESSION_INFO *, - DWORD *); - -typedef void (CALLBACK *wtsapi_free_mem)(PVOID); - -typedef BOOL (CALLBACK *wtsapi_query_session)(HANDLE, - DWORD, - WTS_INFO_CLASS, - LPSTR *, DWORD *); -/* iphlpapi.dll */ - -typedef DWORD (CALLBACK *iphlpapi_get_ipforward_table)(PMIB_IPFORWARDTABLE, - PULONG, - BOOL); - -typedef DWORD (CALLBACK *iphlpapi_get_ipaddr_table)(PMIB_IPADDRTABLE, - PULONG, - BOOL); - -typedef DWORD (CALLBACK *iphlpapi_get_if_table)(PMIB_IFTABLE, - PULONG, - BOOL); - -typedef DWORD (CALLBACK *iphlpapi_get_if_entry)(PMIB_IFROW); - -typedef DWORD (CALLBACK *iphlpapi_get_num_if)(PDWORD); - -typedef DWORD (CALLBACK *iphlpapi_get_tcp_table)(PMIB_TCPTABLE, - PDWORD, - BOOL); - -typedef DWORD (CALLBACK *iphlpapi_get_udp_table)(PMIB_UDPTABLE, - PDWORD, - BOOL); - -typedef DWORD (CALLBACK *iphlpapi_get_tcpx_table)(PMIB_TCPEXTABLE *, - BOOL, - HANDLE, - DWORD, - DWORD); - -typedef DWORD (CALLBACK *iphlpapi_get_udpx_table)(PMIB_UDPEXTABLE *, - BOOL, - HANDLE, - DWORD, - DWORD); - -typedef DWORD (CALLBACK *iphlpapi_get_tcp_stats)(PMIB_TCPSTATS); - -typedef DWORD (CALLBACK *iphlpapi_get_net_params)(PFIXED_INFO, - PULONG); - -typedef DWORD (CALLBACK *iphlpapi_get_adapters_info)(PIP_ADAPTER_INFO, - PULONG); - -typedef ULONG (CALLBACK *iphlpapi_get_adapters_addrs)(ULONG, - ULONG, - PVOID, - PIP_ADAPTER_ADDRESSES, - PULONG); - -/* advapi32.dll */ -typedef BOOL (CALLBACK *advapi_convert_string_sid)(LPCSTR, - PSID *); - -typedef BOOL (CALLBACK *advapi_query_service_status)(SC_HANDLE, - SC_STATUS_TYPE, - LPBYTE, - DWORD, - LPDWORD); - -typedef DWORD (CALLBACK *iphlpapi_get_ipnet_table)(PMIB_IPNETTABLE, - PDWORD, - BOOL); - -/* ntdll.dll */ -typedef DWORD (CALLBACK *ntdll_query_sys_info)(DWORD, - PVOID, - ULONG, - PULONG); - -typedef DWORD (CALLBACK *ntdll_query_proc_info)(HANDLE, - DWORD, - PVOID, - ULONG, - PULONG); - -/* psapi.dll */ -typedef BOOL (CALLBACK *psapi_enum_modules)(HANDLE, - HMODULE *, - DWORD, - LPDWORD); - -typedef DWORD (CALLBACK *psapi_get_module_name)(HANDLE, - HMODULE, - LPTSTR, - DWORD); - -typedef BOOL (CALLBACK *psapi_enum_processes)(DWORD *, - DWORD, - DWORD *); - -/* winsta.dll */ -typedef BOOLEAN (CALLBACK *winsta_query_info)(HANDLE, - ULONG, - WINSTATION_INFO_CLASS, - PVOID, - ULONG, - PULONG); - -/* kernel32.dll */ -typedef BOOL (CALLBACK *kernel_memory_status)(MEMORYSTATUSEX *); - -/* mpr.dll */ -typedef BOOL (CALLBACK *mpr_get_net_connection)(LPCTSTR, - LPTSTR, - LPDWORD); - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(wtsapi, enum_sessions); - SIGAR_DLLFUNC(wtsapi, free_mem); - SIGAR_DLLFUNC(wtsapi, query_session); - - sigar_dll_func_t end; -} sigar_wtsapi_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(iphlpapi, get_ipforward_table); - SIGAR_DLLFUNC(iphlpapi, get_ipaddr_table); - SIGAR_DLLFUNC(iphlpapi, get_if_table); - SIGAR_DLLFUNC(iphlpapi, get_if_entry); - SIGAR_DLLFUNC(iphlpapi, get_num_if); - SIGAR_DLLFUNC(iphlpapi, get_tcp_table); - SIGAR_DLLFUNC(iphlpapi, get_udp_table); - SIGAR_DLLFUNC(iphlpapi, get_tcpx_table); - SIGAR_DLLFUNC(iphlpapi, get_udpx_table); - SIGAR_DLLFUNC(iphlpapi, get_tcp_stats); - SIGAR_DLLFUNC(iphlpapi, get_net_params); - SIGAR_DLLFUNC(iphlpapi, get_adapters_info); - SIGAR_DLLFUNC(iphlpapi, get_adapters_addrs); - SIGAR_DLLFUNC(iphlpapi, get_ipnet_table); - - sigar_dll_func_t end; -} sigar_iphlpapi_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(advapi, convert_string_sid); - SIGAR_DLLFUNC(advapi, query_service_status); - - sigar_dll_func_t end; -} sigar_advapi_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(ntdll, query_sys_info); - SIGAR_DLLFUNC(ntdll, query_proc_info); - - sigar_dll_func_t end; -} sigar_ntdll_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(psapi, enum_modules); - SIGAR_DLLFUNC(psapi, enum_processes); - SIGAR_DLLFUNC(psapi, get_module_name); - - sigar_dll_func_t end; -} sigar_psapi_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(winsta, query_info); - - sigar_dll_func_t end; -} sigar_winsta_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(kernel, memory_status); - - sigar_dll_func_t end; -} sigar_kernel_t; - -typedef struct { - sigar_dll_handle_t handle; - - SIGAR_DLLFUNC(mpr, get_net_connection); - - sigar_dll_func_t end; -} sigar_mpr_t; - -struct sigar_t { - SIGAR_T_BASE; - char *machine; - int using_wide; - long pagesize; - HKEY handle; - char *perfbuf; - DWORD perfbuf_size; - sigar_wtsapi_t wtsapi; - sigar_iphlpapi_t iphlpapi; - sigar_advapi_t advapi; - sigar_ntdll_t ntdll; - sigar_psapi_t psapi; - sigar_winsta_t winsta; - sigar_kernel_t kernel; - sigar_mpr_t mpr; - sigar_win32_pinfo_t pinfo; - sigar_cache_t *netif_adapters; - sigar_cache_t *netif_mib_rows; - sigar_cache_t *netif_addr_rows; - sigar_cache_t *netif_names; /* dwIndex -> net_interface_config.name */ - int netif_name_short; - - WORD ws_version; - int ws_error; - int ht_enabled; - int lcpu; //number of logical cpus - int winnt; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft); - -int sigar_wsa_init(sigar_t *sigar); - -int sigar_proc_exe_peb_get(sigar_t *sigar, HANDLE proc, - sigar_proc_exe_t *procexe); - -int sigar_proc_args_peb_get(sigar_t *sigar, HANDLE proc, - sigar_proc_args_t *procargs); - -int sigar_proc_env_peb_get(sigar_t *sigar, HANDLE proc, - WCHAR *env, DWORD envlen); - -int sigar_proc_args_wmi_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs); - -int sigar_proc_exe_wmi_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe); - -int sigar_parse_proc_args(sigar_t *sigar, WCHAR *buf, - sigar_proc_args_t *procargs); - -int sigar_service_pid_get(sigar_t *sigar, char *name, sigar_pid_t *pid); - -typedef struct { - DWORD size; - DWORD count; - ENUM_SERVICE_STATUS *services; - SC_HANDLE handle; -} sigar_services_status_t; - -int sigar_services_status_get(sigar_services_status_t *ss, DWORD state); - -void sigar_services_status_close(sigar_services_status_t *ss); - -typedef struct sigar_services_walker_t sigar_services_walker_t; - -struct sigar_services_walker_t { - sigar_t *sigar; - int flags; - void *data; /* user data */ - int (*add_service)(sigar_services_walker_t *walker, char *name); -}; - -int sigar_services_query(char *ptql, - sigar_ptql_error_t *error, - sigar_services_walker_t *walker); - -char *sigar_service_exe_get(char *path, char *buffer, int basename); - -typedef struct { - WORD product_major; - WORD product_minor; - WORD product_build; - WORD product_revision; - WORD file_major; - WORD file_minor; - WORD file_build; - WORD file_revision; -} sigar_file_version_t; - -int sigar_file_version_get(sigar_file_version_t *version, - char *name, - sigar_proc_env_t *infocb); - -#ifdef __cplusplus -} -#endif - -#define SIGAR_NO_SUCH_PROCESS (SIGAR_OS_START_ERROR+1) - -#endif /* SIGAR_OS_H */ diff --git a/vendor/sigar/src/os/win32/sigar_pdh.h b/vendor/sigar/src/os/win32/sigar_pdh.h deleted file mode 100644 index 86a481a3d..000000000 --- a/vendor/sigar/src/os/win32/sigar_pdh.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2004, 2006 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIGAR_PDH_H -#define SIGAR_PDH_H - -/* performance data helpers */ - -#define PdhFirstObject(block) \ - ((PERF_OBJECT_TYPE *)((BYTE *) block + block->HeaderLength)) - -#define PdhNextObject(object) \ - ((PERF_OBJECT_TYPE *)((BYTE *) object + object->TotalByteLength)) - -#define PdhFirstCounter(object) \ - ((PERF_COUNTER_DEFINITION *)((BYTE *) object + object->HeaderLength)) - -#define PdhNextCounter(counter) \ - ((PERF_COUNTER_DEFINITION *)((BYTE *) counter + counter->ByteLength)) - -#define PdhGetCounterBlock(inst) \ - ((PERF_COUNTER_BLOCK *)((BYTE *) inst + inst->ByteLength)) - -#define PdhFirstInstance(object) \ - ((PERF_INSTANCE_DEFINITION *)((BYTE *) object + object->DefinitionLength)) - -#define PdhNextInstance(inst) \ - ((PERF_INSTANCE_DEFINITION *)((BYTE *)inst + inst->ByteLength + \ - PdhGetCounterBlock(inst)->ByteLength)) - -#define PdhInstanceName(inst) \ - ((wchar_t *)((BYTE *)inst + inst->NameOffset)) - -#endif /* SIGAR_PDH_H */ diff --git a/vendor/sigar/src/os/win32/win32_sigar.c b/vendor/sigar/src/os/win32/win32_sigar.c deleted file mode 100755 index 4288f08fe..000000000 --- a/vendor/sigar/src/os/win32/win32_sigar.c +++ /dev/null @@ -1,3992 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_pdh.h" -#include "sigar_os.h" -#include "sigar_util.h" -#include "sigar_format.h" -#include -#ifndef MSVC -#include -#endif - -#define USING_WIDE_S(s) (s)->using_wide -#define USING_WIDE() USING_WIDE_S(sigar) - -#define PERFBUF_SIZE 8192 - -#define PERF_TITLE_PROC 230 -#define PERF_TITLE_SYS_KEY "2" -#define PERF_TITLE_MEM_KEY "4" -#define PERF_TITLE_PROC_KEY "230" -#define PERF_TITLE_CPU_KEY "238" -#define PERF_TITLE_DISK_KEY "236" - -#define PERF_TITLE_CPU_USER 142 -#define PERF_TITLE_CPU_IDLE 1746 -#define PERF_TITLE_CPU_SYS 144 -#define PERF_TITLE_CPU_IRQ 698 - -typedef enum { - PERF_IX_CPU_USER, - PERF_IX_CPU_IDLE, - PERF_IX_CPU_SYS, - PERF_IX_CPU_IRQ, - PERF_IX_CPU_MAX -} perf_cpu_offsets_t; - -#define PERF_TITLE_CPUTIME 6 -#define PERF_TITLE_PAGE_FAULTS 28 -#define PERF_TITLE_MEM_VSIZE 174 -#define PERF_TITLE_MEM_SIZE 180 -#define PERF_TITLE_THREAD_CNT 680 -#define PERF_TITLE_HANDLE_CNT 952 -#define PERF_TITLE_PID 784 -#define PERF_TITLE_PPID 1410 -#define PERF_TITLE_PRIORITY 682 -#define PERF_TITLE_START_TIME 684 - -typedef enum { - PERF_IX_CPUTIME, - PERF_IX_PAGE_FAULTS, - PERF_IX_MEM_VSIZE, - PERF_IX_MEM_SIZE, - PERF_IX_THREAD_CNT, - PERF_IX_HANDLE_CNT, - PERF_IX_PID, - PERF_IX_PPID, - PERF_IX_PRIORITY, - PERF_IX_START_TIME, - PERF_IX_MAX -} perf_proc_offsets_t; - -typedef enum { - PERF_IX_DISK_TIME, - PERF_IX_DISK_READ_TIME, - PERF_IX_DISK_WRITE_TIME, - PERF_IX_DISK_READ, - PERF_IX_DISK_WRITE, - PERF_IX_DISK_READ_BYTES, - PERF_IX_DISK_WRITE_BYTES, - PERF_IX_DISK_QUEUE, - PERF_IX_DISK_MAX -} perf_disk_offsets_t; - -#define PERF_TITLE_DISK_TIME 200 /* % Disk Time */ -#define PERF_TITLE_DISK_READ_TIME 202 /* % Disk Read Time */ -#define PERF_TITLE_DISK_WRITE_TIME 204 /* % Disk Write Time */ -#define PERF_TITLE_DISK_READ 214 /* Disk Reads/sec */ -#define PERF_TITLE_DISK_WRITE 216 /* Disk Writes/sec */ -#define PERF_TITLE_DISK_READ_BYTES 220 /* Disk Read Bytes/sec */ -#define PERF_TITLE_DISK_WRITE_BYTES 222 /* Disk Write Bytes/sec */ -#define PERF_TITLE_DISK_QUEUE 198 /* Current Disk Queue Length */ - -/* - * diff is: - * ExW -> ExA - * wcounter -> counter - */ -#define MyRegQueryValue() \ - (USING_WIDE() ? \ - RegQueryValueExW(sigar->handle, \ - wcounter_key, NULL, &type, \ - sigar->perfbuf, \ - &bytes) : \ - RegQueryValueExA(sigar->handle, \ - counter_key, NULL, &type, \ - sigar->perfbuf, \ - &bytes)) - -#define PERF_VAL(ix) \ - perf_offsets[ix] ? \ - *((DWORD *)((BYTE *)counter_block + perf_offsets[ix])) : 0 - -/* 1/100ns units to milliseconds */ -#define NS100_2MSEC(t) ((t) / 10000) - -#define PERF_VAL_CPU(ix) \ - NS100_2MSEC(PERF_VAL(ix)) - -#define MS_LOOPBACK_ADAPTER "Microsoft Loopback Adapter" -#define NETIF_LA "la" - -static int get_proc_info(sigar_t *sigar, sigar_pid_t pid); -static int netif_hash(char *s); - -sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft) -{ - sigar_uint64_t time; - time = ft->dwHighDateTime; - time = time << 32; - time |= ft->dwLowDateTime; - time /= 10; - time -= EPOCH_DELTA; - return time; -} - -static DWORD perfbuf_init(sigar_t *sigar) -{ - if (!sigar->perfbuf) { - sigar->perfbuf = malloc(PERFBUF_SIZE); - sigar->perfbuf_size = PERFBUF_SIZE; - } - - return sigar->perfbuf_size; -} - -static DWORD perfbuf_grow(sigar_t *sigar) -{ - sigar->perfbuf_size += PERFBUF_SIZE; - - sigar->perfbuf = - realloc(sigar->perfbuf, sigar->perfbuf_size); - - return sigar->perfbuf_size; -} - -static char *get_counter_name(char *key) -{ - if (strEQ(key, PERF_TITLE_MEM_KEY)) { - return "Memory"; - } - else if (strEQ(key, PERF_TITLE_PROC_KEY)) { - return "Process"; - } - else if (strEQ(key, PERF_TITLE_CPU_KEY)) { - return "Processor"; - } - else if (strEQ(key, PERF_TITLE_DISK_KEY)) { - return "LogicalDisk"; - } - else { - return key; - } -} - -static PERF_OBJECT_TYPE *get_perf_object_inst(sigar_t *sigar, - char *counter_key, - DWORD inst, DWORD *err) -{ - DWORD retval, type, bytes; - WCHAR wcounter_key[MAX_PATH+1]; - PERF_DATA_BLOCK *block; - PERF_OBJECT_TYPE *object; - - *err = SIGAR_OK; - - if (USING_WIDE()) { - SIGAR_A2W(counter_key, wcounter_key, sizeof(wcounter_key)); - } - - bytes = perfbuf_init(sigar); - - while ((retval = MyRegQueryValue()) != ERROR_SUCCESS) { - if (retval == ERROR_MORE_DATA) { - bytes = perfbuf_grow(sigar); - } - else { - *err = retval; - return NULL; - } - } - - block = (PERF_DATA_BLOCK *)sigar->perfbuf; - if (block->NumObjectTypes == 0) { - counter_key = get_counter_name(counter_key); - sigar_strerror_printf(sigar, "No %s counters defined (disabled?)", - counter_key); - *err = -1; - return NULL; - } - object = PdhFirstObject(block); - - /* - * only seen on windows 2003 server when pdh.dll - * functions are in use by the same process. - * confucius say what the fuck. - */ - if (inst && (object->NumInstances == PERF_NO_INSTANCES)) { - int i; - - for (i=0; iNumObjectTypes; i++) { - if (object->NumInstances != PERF_NO_INSTANCES) { - return object; - } - object = PdhNextObject(object); - } - return NULL; - } - else { - return object; - } -} - -#define get_perf_object(sigar, counter_key, err) \ - get_perf_object_inst(sigar, counter_key, 1, err) - -static int get_mem_counters(sigar_t *sigar, sigar_swap_t *swap, sigar_mem_t *mem) -{ - int status; - PERF_OBJECT_TYPE *object = - get_perf_object_inst(sigar, PERF_TITLE_MEM_KEY, 0, &status); - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - BYTE *data; - DWORD i; - - if (!object) { - return status; - } - - data = (BYTE *)((BYTE *)object + object->DefinitionLength); - - for (i=0, counter = PdhFirstCounter(object); - iNumCounters; - i++, counter = PdhNextCounter(counter)) - { - DWORD offset = counter->CounterOffset; - - switch (counter->CounterNameTitleIndex) { - case 48: /* "Pages Output/sec" */ - if (swap) swap->page_out = *((DWORD *)(data + offset)); - break; - case 76: /* "System Cache Resident Bytes" aka file cache */ - if (mem) { - sigar_uint64_t kern = *((DWORD *)(data + offset)); - mem->actual_free = mem->free + kern; - mem->actual_used = mem->used - kern; - return SIGAR_OK; - } - case 822: /* "Pages Input/sec" */ - if (swap) swap->page_in = *((DWORD *)(data + offset)); - break; - default: - continue; - } - } - - return SIGAR_OK; -} - -static void get_sysinfo(sigar_t *sigar) -{ - SYSTEM_INFO sysinfo; - - GetSystemInfo(&sysinfo); - - sigar->ncpu = sysinfo.dwNumberOfProcessors; - sigar->pagesize = sysinfo.dwPageSize; -} - -/* for C# bindings */ -SIGAR_DECLARE(sigar_t *) sigar_new(void) -{ - sigar_t *sigar; - if (sigar_open(&sigar) != SIGAR_OK) { - return NULL; - } - return sigar; -} - -static sigar_wtsapi_t sigar_wtsapi = { - "wtsapi32.dll", - NULL, - { "WTSEnumerateSessionsA", NULL }, - { "WTSFreeMemory", NULL }, - { "WTSQuerySessionInformationA", NULL }, - { NULL, NULL } -}; - -static sigar_iphlpapi_t sigar_iphlpapi = { - "iphlpapi.dll", - NULL, - { "GetIpForwardTable", NULL }, - { "GetIpAddrTable", NULL }, - { "GetIfTable", NULL }, - { "GetIfEntry", NULL }, - { "GetNumberOfInterfaces", NULL }, - { "GetTcpTable", NULL }, - { "GetUdpTable", NULL }, - { "AllocateAndGetTcpExTableFromStack", NULL }, - { "AllocateAndGetUdpExTableFromStack", NULL }, - { "GetTcpStatistics", NULL }, - { "GetNetworkParams", NULL }, - { "GetAdaptersInfo", NULL }, - { "GetAdaptersAddresses", NULL }, - { "GetIpNetTable", NULL }, - { NULL, NULL } -}; - -static sigar_advapi_t sigar_advapi = { - "advapi32.dll", - NULL, - { "ConvertStringSidToSidA", NULL }, - { "QueryServiceStatusEx", NULL }, - { NULL, NULL } -}; - -static sigar_ntdll_t sigar_ntdll = { - "ntdll.dll", - NULL, - { "NtQuerySystemInformation", NULL }, - { "NtQueryInformationProcess", NULL }, - { NULL, NULL } -}; - -static sigar_psapi_t sigar_psapi = { - "psapi.dll", - NULL, - { "EnumProcessModules", NULL }, - { "EnumProcesses", NULL }, - { "GetModuleFileNameExA", NULL }, - { NULL, NULL } -}; - -static sigar_psapi_t sigar_winsta = { - "winsta.dll", - NULL, - { "WinStationQueryInformationW", NULL }, - { NULL, NULL } -}; - -static sigar_psapi_t sigar_kernel = { - "kernel32.dll", - NULL, - { "GlobalMemoryStatusEx", NULL }, - { NULL, NULL } -}; - -static sigar_mpr_t sigar_mpr = { - "mpr.dll", - NULL, - { "WNetGetConnectionA", NULL }, - { NULL, NULL } -}; - -#define DLLMOD_COPY(name) \ - memcpy(&(sigar->name), &sigar_##name, sizeof(sigar_##name)) - -#define DLLMOD_INIT(name, all) \ - sigar_dllmod_init(sigar, (sigar_dll_module_t *)&(sigar->name), all) - -#define DLLMOD_FREE(name) \ - sigar_dllmod_free((sigar_dll_module_t *)&(sigar->name)) - -static void sigar_dllmod_free(sigar_dll_module_t *module) -{ - if (module->handle) { - FreeLibrary(module->handle); - module->handle = NULL; - } -} - -static int sigar_dllmod_init(sigar_t *sigar, - sigar_dll_module_t *module, - int all) -{ - sigar_dll_func_t *funcs = &module->funcs[0]; - int is_debug = SIGAR_LOG_IS_DEBUG(sigar); - int rc, success; - - if (module->handle == INVALID_HANDLE_VALUE) { - return ENOENT; /* XXX better rc */ - } - - if (module->handle) { - return SIGAR_OK; - } - - module->handle = LoadLibrary(module->name); - if (!(success = (module->handle ? TRUE : FALSE))) { - rc = GetLastError(); - /* dont try again */ - module->handle = INVALID_HANDLE_VALUE; - } - - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "LoadLibrary(%s): %s", - module->name, - success ? - "OK" : - sigar_strerror(sigar, rc)); - } - - if (!success) { - return rc; - } - - while (funcs->name) { - funcs->func = GetProcAddress(module->handle, funcs->name); - - if (!(success = (funcs->func ? TRUE : FALSE))) { - rc = GetLastError(); - } - - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetProcAddress(%s:%s): %s", - module->name, funcs->name, - success ? - "OK" : - sigar_strerror(sigar, rc)); - } - - if (all && !success) { - return rc; - } - - funcs++; - } - - return SIGAR_OK; -} - -int sigar_wsa_init(sigar_t *sigar) -{ - if (sigar->ws_version == 0) { - WSADATA data; - - if (WSAStartup(MAKEWORD(2, 0), &data)) { - sigar->ws_error = WSAGetLastError(); - WSACleanup(); - return sigar->ws_error; - } - - sigar->ws_version = data.wVersion; - } - - return SIGAR_OK; -} - -static int sigar_enable_privilege(char *name) -{ - int status; - HANDLE handle; - TOKEN_PRIVILEGES tok; - - SIGAR_ZERO(&tok); - - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, - &handle)) - { - return GetLastError(); - } - - if (LookupPrivilegeValue(NULL, name, - &tok.Privileges[0].Luid)) - { - tok.PrivilegeCount = 1; - tok.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - if (AdjustTokenPrivileges(handle, FALSE, &tok, 0, NULL, 0)) { - status = SIGAR_OK; - } - else { - status = GetLastError(); - } - } - else { - status = GetLastError(); - } - - CloseHandle(handle); - - return status; -} - -static int netif_name_short(void) -{ - char value[32767]; /* max size from msdn docs */ - DWORD retval = - GetEnvironmentVariable("SIGAR_NETIF_NAME_SHORT", value, sizeof(value)); - if ((retval > 0) && (strEQ(value, "1") || (strEQ(value, "true")))) { - return 1; - } - else { - return 0; - } -} - -int sigar_os_open(sigar_t **sigar_ptr) -{ - LONG result; - HINSTANCE h; - OSVERSIONINFO version; - int i; - sigar_t *sigar; - - *sigar_ptr = sigar = malloc(sizeof(*sigar)); - sigar->machine = ""; /* local machine */ - sigar->using_wide = 0; /*XXX*/ - - sigar->perfbuf = NULL; - sigar->perfbuf_size = 0; - - version.dwOSVersionInfoSize = sizeof(version); - GetVersionEx(&version); - - /* - * 4 == NT 4.0 - * 5 == 2000, XP, 2003 Server - */ - sigar->winnt = (version.dwMajorVersion == 4); - - if (USING_WIDE_S(sigar)) { - WCHAR wmachine[MAX_PATH+1]; - - SIGAR_A2W(sigar->machine, wmachine, sizeof(wmachine)); - - result = RegConnectRegistryW(wmachine, - HKEY_PERFORMANCE_DATA, - &sigar->handle); - } - else { - result = RegConnectRegistryA(sigar->machine, - HKEY_PERFORMANCE_DATA, - &sigar->handle); - } - - get_sysinfo(sigar); - - DLLMOD_COPY(wtsapi); - DLLMOD_COPY(iphlpapi); - DLLMOD_COPY(advapi); - DLLMOD_COPY(ntdll); - DLLMOD_COPY(psapi); - DLLMOD_COPY(winsta); - DLLMOD_COPY(kernel); - DLLMOD_COPY(mpr); - - sigar->log_level = -1; /* else below segfaults */ - /* XXX init early for use by javasigar.c */ - sigar_dllmod_init(sigar, - (sigar_dll_module_t *)&sigar->advapi, - FALSE); - - sigar->netif_mib_rows = NULL; - sigar->netif_addr_rows = NULL; - sigar->netif_adapters = NULL; - sigar->netif_names = NULL; - sigar->netif_name_short = netif_name_short(); - - sigar->pinfo.pid = -1; - sigar->ws_version = 0; - sigar->lcpu = -1; - - /* increase process visibility */ - sigar_enable_privilege(SE_DEBUG_NAME); - - return result; -} - -void dllmod_init_ntdll(sigar_t *sigar) -{ - DLLMOD_INIT(ntdll, FALSE); -} - -int sigar_os_close(sigar_t *sigar) -{ - int retval; - - DLLMOD_FREE(wtsapi); - DLLMOD_FREE(iphlpapi); - DLLMOD_FREE(advapi); - DLLMOD_FREE(ntdll); - DLLMOD_FREE(psapi); - DLLMOD_FREE(winsta); - DLLMOD_FREE(kernel); - DLLMOD_FREE(mpr); - - if (sigar->perfbuf) { - free(sigar->perfbuf); - } - - retval = RegCloseKey(sigar->handle); - - if (sigar->ws_version != 0) { - WSACleanup(); - } - - if (sigar->netif_mib_rows) { - sigar_cache_destroy(sigar->netif_mib_rows); - } - - if (sigar->netif_addr_rows) { - sigar_cache_destroy(sigar->netif_addr_rows); - } - - if (sigar->netif_adapters) { - sigar_cache_destroy(sigar->netif_adapters); - } - - if (sigar->netif_names) { - sigar_cache_destroy(sigar->netif_names); - } - - free(sigar); - - return retval; -} - -char *sigar_os_error_string(sigar_t *sigar, int err) -{ - switch (err) { - case SIGAR_NO_SUCH_PROCESS: - return "No such process"; - break; - } - return NULL; -} - -#define sigar_GlobalMemoryStatusEx \ - sigar->kernel.memory_status.func - -SIGAR_DECLARE(int) sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) -{ - DLLMOD_INIT(kernel, TRUE); - - if (sigar_GlobalMemoryStatusEx) { - MEMORYSTATUSEX memstat; - - memstat.dwLength = sizeof(memstat); - - if (!sigar_GlobalMemoryStatusEx(&memstat)) { - return GetLastError(); - } - - mem->total = memstat.ullTotalPhys; - mem->free = memstat.ullAvailPhys; - } - else { - MEMORYSTATUS memstat; - GlobalMemoryStatus(&memstat); - mem->total = memstat.dwTotalPhys; - mem->free = memstat.dwAvailPhys; - } - - mem->used = mem->total - mem->free; - - mem->actual_free = mem->free; - mem->actual_used = mem->used; - /* set actual_{free,used} */ - get_mem_counters(sigar, NULL, mem); - - sigar_mem_calc_ram(sigar, mem); - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) -{ - int status; - DLLMOD_INIT(kernel, TRUE); - - if (sigar_GlobalMemoryStatusEx) { - MEMORYSTATUSEX memstat; - - memstat.dwLength = sizeof(memstat); - - if (!sigar_GlobalMemoryStatusEx(&memstat)) { - return GetLastError(); - } - - swap->total = memstat.ullTotalPageFile; - swap->free = memstat.ullAvailPageFile; - } - else { - MEMORYSTATUS memstat; - GlobalMemoryStatus(&memstat); - swap->total = memstat.dwTotalPageFile; - swap->free = memstat.dwAvailPageFile; - } - - swap->used = swap->total - swap->free; - - if (get_mem_counters(sigar, swap, NULL) != SIGAR_OK) { - swap->page_in = SIGAR_FIELD_NOTIMPL; - swap->page_out = SIGAR_FIELD_NOTIMPL; - } - - return SIGAR_OK; -} - -static PERF_INSTANCE_DEFINITION *get_cpu_instance(sigar_t *sigar, - DWORD *perf_offsets, - DWORD *num, DWORD *err) -{ - PERF_OBJECT_TYPE *object = get_perf_object(sigar, PERF_TITLE_CPU_KEY, err); - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - DWORD i; - - if (!object) { - return NULL; - } - - for (i=0, counter = PdhFirstCounter(object); - iNumCounters; - i++, counter = PdhNextCounter(counter)) - { - DWORD offset = counter->CounterOffset; - - switch (counter->CounterNameTitleIndex) { - case PERF_TITLE_CPU_SYS: - perf_offsets[PERF_IX_CPU_SYS] = offset; - break; - case PERF_TITLE_CPU_USER: - perf_offsets[PERF_IX_CPU_USER] = offset; - break; - case PERF_TITLE_CPU_IDLE: - perf_offsets[PERF_IX_CPU_IDLE] = offset; - break; - case PERF_TITLE_CPU_IRQ: - perf_offsets[PERF_IX_CPU_IRQ] = offset; - break; - } - } - - if (num) { - *num = object->NumInstances; - } - - return PdhFirstInstance(object); -} - -#define SPPI_MAX 128 /* XXX unhardcode; should move off this api anyhow */ - -#define sigar_NtQuerySystemInformation \ - sigar->ntdll.query_sys_info.func - -static int get_idle_cpu(sigar_t *sigar, sigar_cpu_t *cpu, - DWORD idx, - PERF_COUNTER_BLOCK *counter_block, - DWORD *perf_offsets) -{ - cpu->idle = 0; - - if (perf_offsets[PERF_IX_CPU_IDLE]) { - cpu->idle = PERF_VAL_CPU(PERF_IX_CPU_IDLE); - } - else { - /* windows NT and 2000 do not have an Idle counter */ - DLLMOD_INIT(ntdll, FALSE); - if (sigar_NtQuerySystemInformation) { - DWORD retval, num; - SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX]; - /* into the lungs of hell */ - sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation, - &info, sizeof(info), &retval); - - if (!retval) { - return GetLastError(); - } - num = retval/sizeof(info[0]); - - if (idx == -1) { - int i; - for (i=0; iidle += NS100_2MSEC(info[i].IdleTime.QuadPart); - } - } - else if (idx < num) { - cpu->idle = NS100_2MSEC(info[idx].IdleTime.QuadPart); - } - else { - return ERROR_INVALID_DATA; - } - } - else { - return ERROR_INVALID_FUNCTION; - } - } - - return SIGAR_OK; -} - -static int sigar_cpu_perflib_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - int status; - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_BLOCK *counter_block; - DWORD perf_offsets[PERF_IX_CPU_MAX], err; - - SIGAR_ZERO(cpu); - memset(&perf_offsets, 0, sizeof(perf_offsets)); - - inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, 0, &err); - - if (!inst) { - return err; - } - - /* first instance is total, rest are per-cpu */ - counter_block = PdhGetCounterBlock(inst); - - cpu->sys = PERF_VAL_CPU(PERF_IX_CPU_SYS); - cpu->user = PERF_VAL_CPU(PERF_IX_CPU_USER); - status = get_idle_cpu(sigar, cpu, -1, counter_block, perf_offsets); - cpu->irq = PERF_VAL_CPU(PERF_IX_CPU_IRQ); - cpu->nice = 0; /* no nice here */ - cpu->wait = 0; /*N/A?*/ - cpu->total = cpu->sys + cpu->user + cpu->idle + cpu->wait + cpu->irq; - - if (status != SIGAR_OK) { - sigar_log_printf(sigar, SIGAR_LOG_WARN, - "unable to determine idle cpu time: %s", - sigar_strerror(sigar, status)); - } - - return SIGAR_OK; -} - -static int sigar_cpu_ntsys_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - DWORD retval, num; - int i; - SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX]; - /* into the lungs of hell */ - sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation, - &info, sizeof(info), &retval); - - if (!retval) { - return GetLastError(); - } - num = retval/sizeof(info[0]); - SIGAR_ZERO(cpu); - - for (i=0; iidle += NS100_2MSEC(info[i].IdleTime.QuadPart); - cpu->user += NS100_2MSEC(info[i].UserTime.QuadPart); - cpu->sys += NS100_2MSEC(info[i].KernelTime.QuadPart - - info[i].IdleTime.QuadPart); - cpu->irq += NS100_2MSEC(info[i].InterruptTime.QuadPart); - cpu->total += cpu->idle + cpu->user + cpu->sys; - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) -{ - DLLMOD_INIT(ntdll, FALSE); - if (sigar_NtQuerySystemInformation) { - return sigar_cpu_ntsys_get(sigar, cpu); - } - else { - return sigar_cpu_perflib_get(sigar, cpu); - } -} - -static int sigar_cpu_list_perflib_get(sigar_t *sigar, - sigar_cpu_list_t *cpulist) -{ - int status, i, j; - PERF_INSTANCE_DEFINITION *inst; - DWORD perf_offsets[PERF_IX_CPU_MAX], num, err; - int core_rollup = sigar_cpu_core_rollup(sigar); - - memset(&perf_offsets, 0, sizeof(perf_offsets)); - - /* first instance is total, rest are per-cpu */ - inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, &num, &err); - - if (!inst) { - return err; - } - - if (!sigar->winnt) { - /* skip Processor _Total instance (NT doesnt have one) */ - --num; - inst = PdhNextInstance(inst); - } - - sigar_cpu_list_create(cpulist); - - /* verify there's a counter for each logical cpu */ - if (core_rollup && (sigar->ncpu != num)) { - core_rollup = 0; - } - - for (i=0; ilcpu)) { - /* merge times of logical processors */ - cpu = &cpulist->data[cpulist->number-1]; - } - else { - SIGAR_CPU_LIST_GROW(cpulist); - cpu = &cpulist->data[cpulist->number++]; - SIGAR_ZERO(cpu); - } - - counter_block = PdhGetCounterBlock(inst); - - cpu->sys += PERF_VAL_CPU(PERF_IX_CPU_SYS); - cpu->user += PERF_VAL_CPU(PERF_IX_CPU_USER); - cpu->irq += PERF_VAL_CPU(PERF_IX_CPU_IRQ); - get_idle_cpu(sigar, cpu, i, counter_block, perf_offsets); - cpu->nice = cpu->wait = 0; /*N/A*/ - - /*XXX adding up too much here if xeon, but not using this atm*/ - cpu->total += cpu->sys + cpu->user + cpu->idle + cpu->irq; - - inst = PdhNextInstance(inst); - } - - return SIGAR_OK; -} - -static int sigar_cpu_list_ntsys_get(sigar_t *sigar, - sigar_cpu_list_t *cpulist) -{ - DWORD retval, num; - int status, i, j; - int core_rollup = sigar_cpu_core_rollup(sigar); - - SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX]; - /* into the lungs of hell */ - sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation, - &info, sizeof(info), &retval); - - if (!retval) { - return GetLastError(); - } - num = retval/sizeof(info[0]); - - sigar_cpu_list_create(cpulist); - - /* verify there's a counter for each logical cpu */ - if (core_rollup && (sigar->ncpu != num)) { - core_rollup = 0; - } - - for (i=0; ilcpu)) { - /* merge times of logical processors */ - cpu = &cpulist->data[cpulist->number-1]; - } - else { - SIGAR_CPU_LIST_GROW(cpulist); - cpu = &cpulist->data[cpulist->number++]; - SIGAR_ZERO(cpu); - } - - idle = NS100_2MSEC(info[i].IdleTime.QuadPart); - user = NS100_2MSEC(info[i].UserTime.QuadPart); - sys = NS100_2MSEC(info[i].KernelTime.QuadPart - - info[i].IdleTime.QuadPart); - cpu->idle += idle; - cpu->user += user; - cpu->sys += sys; - cpu->nice = cpu->wait = 0; /*N/A*/ - cpu->total += idle + user + sys; - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_cpu_list_get(sigar_t *sigar, - sigar_cpu_list_t *cpulist) -{ - DLLMOD_INIT(ntdll, FALSE); - if (sigar_NtQuerySystemInformation) { - return sigar_cpu_list_ntsys_get(sigar, cpulist); - } - else { - return sigar_cpu_list_perflib_get(sigar, cpulist); - } -} - -#define PERF_TITLE_UPTIME_KEY 674 /* System Up Time */ - -SIGAR_DECLARE(int) sigar_uptime_get(sigar_t *sigar, - sigar_uptime_t *uptime) -{ - int status; - PERF_OBJECT_TYPE *object = - get_perf_object_inst(sigar, PERF_TITLE_SYS_KEY, 0, &status); - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - BYTE *data; - DWORD i; - - if (!object) { - return status; - } - - data = (BYTE *)((BYTE *)object + object->DefinitionLength); - - for (i=0, counter = PdhFirstCounter(object); - iNumCounters; - i++, counter = PdhNextCounter(counter)) - { - if (counter->CounterNameTitleIndex == PERF_TITLE_UPTIME_KEY) { - DWORD offset = counter->CounterOffset; - LONGLONG time = object->PerfTime.QuadPart; - LONGLONG freq = object->PerfFreq.QuadPart; - LONGLONG counter = *((LONGLONG *)(data + offset)); - uptime->uptime = (time - counter) / freq; - return SIGAR_OK; - } - } - - /* http://msdn.microsoft.com/en-us/library/ms724408.aspx */ - return GetTickCount() / 1000; -} - -/* - * there is no api for this info. - * closest i've seen is enumerating the entire process table - * and calculating an average based on process times. - */ -SIGAR_DECLARE(int) sigar_loadavg_get(sigar_t *sigar, - sigar_loadavg_t *loadavg) -{ - return SIGAR_ENOTIMPL; -} - -#define get_process_object(sigar, err) \ - get_perf_object(sigar, PERF_TITLE_PROC_KEY, err) - -static int sigar_proc_list_get_perf(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - - PERF_OBJECT_TYPE *object; - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - DWORD i, err; - DWORD perf_offsets[PERF_IX_MAX]; - - perf_offsets[PERF_IX_PID] = 0; - - object = get_process_object(sigar, &err); - - if (!object) { - return err; - } - - /* - * note we assume here: - * block->NumObjectTypes == 1 - * object->ObjectNameTitleIndex == PERF_TITLE_PROC - * - * which should always be the case. - */ - - for (i=0, counter = PdhFirstCounter(object); - iNumCounters; - i++, counter = PdhNextCounter(counter)) - { - DWORD offset = counter->CounterOffset; - - switch (counter->CounterNameTitleIndex) { - case PERF_TITLE_PID: - perf_offsets[PERF_IX_PID] = offset; - break; - } - } - - for (i=0, inst = PdhFirstInstance(object); - iNumInstances; - i++, inst = PdhNextInstance(inst)) - { - PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst); - DWORD pid = PERF_VAL(PERF_IX_PID); - - if (pid == 0) { - continue; /* dont include the system Idle process */ - } - - SIGAR_PROC_LIST_GROW(proclist); - - proclist->data[proclist->number++] = pid; - } - - return SIGAR_OK; -} - -#define sigar_EnumProcesses \ - sigar->psapi.enum_processes.func - -int sigar_os_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - DLLMOD_INIT(psapi, FALSE); - - if (sigar_EnumProcesses) { - DWORD retval, *pids; - DWORD size = 0, i; - - do { - /* re-use the perfbuf */ - if (size == 0) { - size = perfbuf_init(sigar); - } - else { - size = perfbuf_grow(sigar); - } - - if (!sigar_EnumProcesses((DWORD *)sigar->perfbuf, - sigar->perfbuf_size, - &retval)) - { - return GetLastError(); - } - } while (retval == sigar->perfbuf_size); //unlikely - - pids = (DWORD *)sigar->perfbuf; - - size = retval / sizeof(DWORD); - - for (i=0; idata[proclist->number++] = pid; - } - - return SIGAR_OK; - } - else { - return sigar_proc_list_get_perf(sigar, proclist); - } -} - -#define PROCESS_DAC (PROCESS_QUERY_INFORMATION|PROCESS_VM_READ) - -static HANDLE open_process(sigar_pid_t pid) -{ - return OpenProcess(PROCESS_DAC, 0, (DWORD)pid); -} - -/* - * Pretty good explanation of counters: - * http://www.semack.net/wiki/default.asp?db=SemackNetWiki&o=VirtualMemory - */ -SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_mem_t *procmem) -{ - int status = get_proc_info(sigar, pid); - sigar_win32_pinfo_t *pinfo = &sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - procmem->size = pinfo->size; /* "Virtual Bytes" */ - procmem->resident = pinfo->resident; /* "Working Set" */ - procmem->share = SIGAR_FIELD_NOTIMPL; - procmem->page_faults = pinfo->page_faults; - procmem->minor_faults = SIGAR_FIELD_NOTIMPL; - procmem->major_faults = SIGAR_FIELD_NOTIMPL; - - return SIGAR_OK; -} - -#define TOKEN_DAC (STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY) - -SIGAR_DECLARE(int) -sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_name_t *proccredname) -{ - HANDLE proc, token; - DWORD len; - int success; - TOKEN_USER *user = NULL; - TOKEN_PRIMARY_GROUP *group = NULL; - SID_NAME_USE type; - char domain[SIGAR_CRED_NAME_MAX]; - - /* XXX cache lookup */ - - if (!(proc = open_process(pid))) { - return GetLastError(); - } - - if (!OpenProcessToken(proc, TOKEN_DAC, &token)) { - CloseHandle(proc); - return GetLastError(); - } - - CloseHandle(proc); - - success = - !GetTokenInformation(token, TokenUser, NULL, 0, &len) && - (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && - (user = malloc(len)) && - GetTokenInformation(token, TokenUser, user, len, &len); - - if (success) { - DWORD domain_len = sizeof(domain); - DWORD user_len = sizeof(proccredname->user); - - success = LookupAccountSid(NULL, user->User.Sid, - proccredname->user, &user_len, - domain, &domain_len, &type); - } - - if (user != NULL) { - free(user); - } - if (!success) { - CloseHandle(token); - return GetLastError(); - } - - success = - !GetTokenInformation(token, TokenPrimaryGroup, NULL, 0, &len) && - (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && - (group = malloc(len)) && - GetTokenInformation(token, TokenPrimaryGroup, group, len, &len); - - if (success) { - DWORD domain_len = sizeof(domain); - DWORD group_len = sizeof(proccredname->group); - - success = LookupAccountSid(NULL, group->PrimaryGroup, - proccredname->group, &group_len, - domain, &domain_len, &type); - } - - if (group != NULL) { - free(group); - } - - CloseHandle(token); - - if (!success) { - return GetLastError(); - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_t *proccred) -{ - return SIGAR_ENOTIMPL; -} - -#define FILETIME2MSEC(ft) \ - NS100_2MSEC(((ft.dwHighDateTime << 32) | ft.dwLowDateTime)) - -sigar_int64_t sigar_time_now_millis(void) -{ - SYSTEMTIME st; - FILETIME time; - - GetSystemTime(&st); - SystemTimeToFileTime(&st, &time); - - return sigar_FileTimeToTime(&time) / 1000; -} - -SIGAR_DECLARE(int) sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_time_t *proctime) -{ - HANDLE proc = open_process(pid); - FILETIME start_time, exit_time, system_time, user_time; - int status = ERROR_SUCCESS; - - if (!proc) { - return GetLastError(); - } - - if (!GetProcessTimes(proc, - &start_time, &exit_time, - &system_time, &user_time)) - { - status = GetLastError(); - } - - CloseHandle(proc); - - if (status != ERROR_SUCCESS) { - return status; - } - - if (start_time.dwHighDateTime) { - proctime->start_time = - sigar_FileTimeToTime(&start_time) / 1000; - } - else { - proctime->start_time = 0; - } - - proctime->user = FILETIME2MSEC(user_time); - proctime->sys = FILETIME2MSEC(system_time); - proctime->total = proctime->user + proctime->sys; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_state_t *procstate) -{ - int status = get_proc_info(sigar, pid); - sigar_win32_pinfo_t *pinfo = &sigar->pinfo; - - if (status != SIGAR_OK) { - return status; - } - - memcpy(procstate->name, pinfo->name, sizeof(procstate->name)); - procstate->state = pinfo->state; - procstate->ppid = pinfo->ppid; - procstate->priority = pinfo->priority; - procstate->nice = SIGAR_FIELD_NOTIMPL; - procstate->tty = SIGAR_FIELD_NOTIMPL; - procstate->threads = pinfo->threads; - procstate->processor = SIGAR_FIELD_NOTIMPL; - - return SIGAR_OK; -} - -static int get_proc_info(sigar_t *sigar, sigar_pid_t pid) -{ - PERF_OBJECT_TYPE *object; - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - DWORD i, err; - DWORD perf_offsets[PERF_IX_MAX]; - sigar_win32_pinfo_t *pinfo = &sigar->pinfo; - time_t timenow = time(NULL); - - if (pinfo->pid == pid) { - if ((timenow - pinfo->mtime) < SIGAR_LAST_PROC_EXPIRE) { - return SIGAR_OK; - } - } - - memset(&perf_offsets, 0, sizeof(perf_offsets)); - - object = get_process_object(sigar, &err); - - if (object == NULL) { - return err; - } - - pinfo->pid = pid; - pinfo->mtime = timenow; - - /* - * note we assume here: - * block->NumObjectTypes == 1 - * object->ObjectNameTitleIndex == PERF_TITLE_PROC - * - * which should always be the case. - */ - - for (i=0, counter = PdhFirstCounter(object); - iNumCounters; - i++, counter = PdhNextCounter(counter)) - { - DWORD offset = counter->CounterOffset; - - switch (counter->CounterNameTitleIndex) { - case PERF_TITLE_CPUTIME: - perf_offsets[PERF_IX_CPUTIME] = offset; - break; - case PERF_TITLE_PAGE_FAULTS: - perf_offsets[PERF_IX_PAGE_FAULTS] = offset; - break; - case PERF_TITLE_MEM_VSIZE: - perf_offsets[PERF_IX_MEM_VSIZE] = offset; - break; - case PERF_TITLE_MEM_SIZE: - perf_offsets[PERF_IX_MEM_SIZE] = offset; - break; - case PERF_TITLE_THREAD_CNT: - perf_offsets[PERF_IX_THREAD_CNT] = offset; - break; - case PERF_TITLE_HANDLE_CNT: - perf_offsets[PERF_IX_HANDLE_CNT] = offset; - break; - case PERF_TITLE_PID: - perf_offsets[PERF_IX_PID] = offset; - break; - case PERF_TITLE_PPID: - perf_offsets[PERF_IX_PPID] = offset; - break; - case PERF_TITLE_PRIORITY: - perf_offsets[PERF_IX_PRIORITY] = offset; - break; - case PERF_TITLE_START_TIME: - perf_offsets[PERF_IX_START_TIME] = offset; - break; - } - } - - for (i=0, inst = PdhFirstInstance(object); - iNumInstances; - i++, inst = PdhNextInstance(inst)) - { - PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst); - sigar_pid_t this_pid = PERF_VAL(PERF_IX_PID); - - if (this_pid != pid) { - continue; - } - - pinfo->state = 'R'; /* XXX? */ - SIGAR_W2A(PdhInstanceName(inst), - pinfo->name, sizeof(pinfo->name)); - - pinfo->size = PERF_VAL(PERF_IX_MEM_VSIZE); - pinfo->resident = PERF_VAL(PERF_IX_MEM_SIZE); - pinfo->ppid = PERF_VAL(PERF_IX_PPID); - pinfo->priority = PERF_VAL(PERF_IX_PRIORITY); - pinfo->handles = PERF_VAL(PERF_IX_HANDLE_CNT); - pinfo->threads = PERF_VAL(PERF_IX_THREAD_CNT); - pinfo->page_faults = PERF_VAL(PERF_IX_PAGE_FAULTS); - - return SIGAR_OK; - } - - return SIGAR_NO_SUCH_PROCESS; -} - -static int sigar_remote_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - int status; - char cmdline[SIGAR_CMDLINE_MAX], *ptr = cmdline, *arg; - HANDLE proc = open_process(pid); - - if (proc) { - status = sigar_proc_args_peb_get(sigar, proc, procargs); - - CloseHandle(proc); - - if (status == SIGAR_OK) { - return status; - } - } - - /* likely we are 32-bit, pid process is 64-bit */ -#ifdef MSVC - status = sigar_proc_args_wmi_get(sigar, pid, procargs); -#endif - if (status == ERROR_NOT_FOUND) { - status = SIGAR_NO_SUCH_PROCESS; - } - return status; -} - -int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - if (pid == sigar->pid) { - return sigar_parse_proc_args(sigar, NULL, procargs); - } - else { - return sigar_remote_proc_args_get(sigar, pid, procargs); - } -} - -static int sigar_proc_env_parse(UCHAR *ptr, sigar_proc_env_t *procenv, - int multi) -{ - while (*ptr) { - char *val; - int klen, vlen, status; - char key[128]; /* XXX is there a max key size? */ - - if (*ptr == '=') { - ptr += strlen(ptr)+1; - continue; - } - - val = strchr(ptr, '='); - - if (val == NULL) { - break; /*XXX*/ - } - - klen = val - (char*)ptr; - SIGAR_SSTRCPY(key, ptr); - key[klen] = '\0'; - ++val; - - vlen = strlen(val); - - status = procenv->env_getter(procenv->data, - key, klen, val, vlen); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - return status; - } - - if (!multi) { - break; /* caller only provided 1 key=val pair */ - } - - ptr += klen + 1 + vlen + 1; - } - - return SIGAR_OK; -} - -static int sigar_local_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - UCHAR *env = (UCHAR*)GetEnvironmentStrings(); - - sigar_proc_env_parse(env, procenv, TRUE); - - FreeEnvironmentStrings(env); - - return SIGAR_OK; -} - -static int sigar_remote_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - int status; - HANDLE proc = open_process(pid); - WCHAR env[4096]; - - if (!proc) { - return GetLastError(); - } - - status = sigar_proc_env_peb_get(sigar, proc, env, sizeof(env)); - - CloseHandle(proc); - - if (status == SIGAR_OK) { - LPBYTE ptr = (LPBYTE)env; - DWORD size = sizeof(env); - UCHAR ent[4096]; - - while ((size > 0) && (*ptr != L'\0')) { - DWORD len = (wcslen((LPWSTR)ptr) + 1) * sizeof(WCHAR); - /* multi=FALSE so no need to: memset(ent, '\0', sizeof(ent)) */ - SIGAR_W2A((WCHAR *)ptr, ent, sizeof(ent)); - if (sigar_proc_env_parse(ent, procenv, FALSE) != SIGAR_OK) { - break; - } - size -= len; - ptr += len; - } - } - - return status; -} - -SIGAR_DECLARE(int) sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_env_t *procenv) -{ - if (pid == sigar->pid) { - if (procenv->type == SIGAR_PROC_ENV_KEY) { - char value[32767]; /* max size from msdn docs */ - DWORD retval = - GetEnvironmentVariable(procenv->key, value, sizeof(value)); - - if (retval == 0) { - if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) { - return SIGAR_OK; - } - return GetLastError(); - } - else if (retval > sizeof(value)) { - /* XXX shouldnt happen */ - return GetLastError(); - } - - procenv->env_getter(procenv->data, - procenv->key, procenv->klen, - value, retval); - return SIGAR_OK; - } - else { - return sigar_local_proc_env_get(sigar, pid, procenv); - } - } - else { - return sigar_remote_proc_env_get(sigar, pid, procenv); - } -} - -SIGAR_DECLARE(int) sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_fd_t *procfd) -{ - int status; - sigar_win32_pinfo_t *pinfo = &sigar->pinfo; - - pinfo->pid = -1; /* force update */ - if ((status = get_proc_info(sigar, pid)) != SIGAR_OK) { - return status; - } - - procfd->total = pinfo->handles; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ - int status = SIGAR_OK; - HANDLE proc = open_process(pid); - - if (!proc) { - return GetLastError(); - } - - status = sigar_proc_exe_peb_get(sigar, proc, procexe); -#ifdef MSVC - if (procexe->name[0] == '\0') { - /* likely we are 32-bit, pid process is 64-bit */ - /* procexe->cwd[0] = XXX where else can we try? */ - status = sigar_proc_exe_wmi_get(sigar, pid, procexe); - if (status == ERROR_NOT_FOUND) { - status = SIGAR_NO_SUCH_PROCESS; - } - } -#endif - if (procexe->cwd[0] != '\0') { - /* strip trailing '\' */ - int len = strlen(procexe->cwd); - if (procexe->cwd[len-1] == '\\') { - procexe->cwd[len-1] = '\0'; - } - /* uppercase driver letter */ - procexe->cwd[0] = toupper(procexe->cwd[0]); - /* e.g. C:\ */ - strncpy(procexe->root, procexe->cwd, 3); - procexe->root[3] = '\0'; - } - else { - procexe->root[0] = '\0'; - } - - if (procexe->name[0] != '\0') { - /* uppercase driver letter */ - procexe->name[0] = toupper(procexe->name[0]); - } - - CloseHandle(proc); - - return status; -} - -#define sigar_EnumProcessModules \ - sigar->psapi.enum_modules.func - -#define sigar_GetModuleFileNameEx \ - sigar->psapi.get_module_name.func - -SIGAR_DECLARE(int) sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_modules_t *procmods) -{ - HANDLE proc; - HMODULE modules[1024]; - DWORD size = 0; - unsigned int i; - - if (DLLMOD_INIT(psapi, TRUE) != SIGAR_OK) { - return SIGAR_ENOTIMPL; - } - - if (!(proc = open_process(pid))) { - return GetLastError(); - } - - if (!sigar_EnumProcessModules(proc, modules, sizeof(modules), &size)) { - CloseHandle(proc); - return GetLastError(); - } - - for (i=0; i<(size/sizeof(HMODULE)); i++) { - int status; - char name[MAX_PATH]; - - if (!sigar_GetModuleFileNameEx(proc, modules[i], - name, sizeof(name))) - { - continue; - } - - status = procmods->module_getter(procmods->data, - name, strlen(name)); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - } - - CloseHandle(proc); - - return SIGAR_OK; -} - -#define FT2INT64(ft) \ - ((__int64)((__int64)(ft).dwHighDateTime << 32 | \ - (__int64)(ft).dwLowDateTime)) - -SIGAR_DECLARE(int) sigar_thread_cpu_get(sigar_t *sigar, - sigar_uint64_t id, - sigar_thread_cpu_t *cpu) -{ - FILETIME start, exit, sys, user; - DWORD retval; - - if (id != 0) { - return SIGAR_ENOTIMPL; - } - - retval = GetThreadTimes(GetCurrentThread(), - &start, &exit, &sys, &user); - - if (retval == 0) { - return GetLastError(); - } - - cpu->user = FT2INT64(user) * 100; - cpu->sys = FT2INT64(sys) * 100; - cpu->total = (FT2INT64(user) + FT2INT64(sys)) * 100; - - return SIGAR_OK; -} - -int sigar_os_fs_type_get(sigar_file_system_t *fsp) -{ - return fsp->type; -} - -#ifndef FILE_READ_ONLY_VOLUME -#define FILE_READ_ONLY_VOLUME 0x00080000 -#endif -#ifndef FILE_NAMED_STREAMS -#define FILE_NAMED_STREAMS 0x00040000 -#endif -#ifndef FILE_SEQUENTIAL_WRITE_ONCE -#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 -#endif -#ifndef FILE_SUPPORTS_TRANSACTIONS -#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 -#endif - -static void get_fs_options(char *opts, int osize, long flags) -{ - *opts = '\0'; - if (flags & FILE_READ_ONLY_VOLUME) strncat(opts, "ro", osize); - else strncat(opts, "rw", osize); -#if 0 /*XXX*/ - if (flags & FILE_CASE_PRESERVED_NAMES) strncat(opts, ",casepn", osize); - if (flags & FILE_CASE_SENSITIVE_SEARCH) strncat(opts, ",casess", osize); - if (flags & FILE_FILE_COMPRESSION) strncat(opts, ",fcomp", osize); - if (flags & FILE_NAMED_STREAMS) strncat(opts, ",streams", osize); - if (flags & FILE_PERSISTENT_ACLS) strncat(opts, ",acls", osize); - if (flags & FILE_SEQUENTIAL_WRITE_ONCE) strncat(opts, ",wronce", osize); - if (flags & FILE_SUPPORTS_ENCRYPTION) strncat(opts, ",efs", osize); - if (flags & FILE_SUPPORTS_OBJECT_IDS) strncat(opts, ",oids", osize); - if (flags & FILE_SUPPORTS_REPARSE_POINTS) strncat(opts, ",reparse", osize); - if (flags & FILE_SUPPORTS_SPARSE_FILES) strncat(opts, ",sparse", osize); - if (flags & FILE_SUPPORTS_TRANSACTIONS) strncat(opts, ",trans", osize); - if (flags & FILE_UNICODE_ON_DISK) strncat(opts, ",unicode", osize); - if (flags & FILE_VOLUME_IS_COMPRESSED) strncat(opts, ",vcomp", osize); - if (flags & FILE_VOLUME_QUOTAS) strncat(opts, ",quota", osize); -#endif -} - -#define sigar_WNetGetConnection \ - sigar->mpr.get_net_connection.func - -SIGAR_DECLARE(int) sigar_file_system_list_get(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - char name[256]; - char *ptr = name; - /* XXX: hmm, Find{First,Next}Volume not available in my sdk */ - DWORD len = GetLogicalDriveStringsA(sizeof(name), name); - - DLLMOD_INIT(mpr, TRUE); - - if (len == 0) { - return GetLastError(); - } - - sigar_file_system_list_create(fslist); - - while (*ptr) { - sigar_file_system_t *fsp; - DWORD flags, serialnum=0; - char fsname[1024]; - UINT drive_type = GetDriveType(ptr); - int type; - - switch (drive_type) { - case DRIVE_FIXED: - type = SIGAR_FSTYPE_LOCAL_DISK; - break; - case DRIVE_REMOTE: - type = SIGAR_FSTYPE_NETWORK; - break; - case DRIVE_CDROM: - type = SIGAR_FSTYPE_CDROM; - break; - case DRIVE_RAMDISK: - type = SIGAR_FSTYPE_RAM_DISK; - break; - case DRIVE_REMOVABLE: - /* skip floppy, usb, etc. drives */ - ptr += strlen(ptr)+1; - continue; - default: - type = SIGAR_FSTYPE_NONE; - break; - } - - fsname[0] = '\0'; - - GetVolumeInformation(ptr, NULL, 0, &serialnum, NULL, - &flags, fsname, sizeof(fsname)); - - if (!serialnum && (drive_type == DRIVE_FIXED)) { - ptr += strlen(ptr)+1; - continue; /* ignore unformatted partitions */ - } - - SIGAR_FILE_SYSTEM_LIST_GROW(fslist); - - fsp = &fslist->data[fslist->number++]; - - fsp->type = type; - SIGAR_SSTRCPY(fsp->dir_name, ptr); - SIGAR_SSTRCPY(fsp->dev_name, ptr); - - if ((drive_type == DRIVE_REMOTE) && sigar_WNetGetConnection) { - DWORD len = sizeof(fsp->dev_name); - char drive[3] = {'\0', ':', '\0'}; /* e.g. "X:" w/o trailing "\" */ - drive[0] = fsp->dir_name[0]; - sigar_WNetGetConnection(drive, fsp->dev_name, &len); - /* ignoring failure, leaving dev_name as dir_name */ - } - - /* we set fsp->type, just looking up sigar.c:fstype_names[type] */ - sigar_fs_type_get(fsp); - - if (*fsname == '\0') { - SIGAR_SSTRCPY(fsp->sys_type_name, fsp->type_name); - } - else { - SIGAR_SSTRCPY(fsp->sys_type_name, fsname); /* CDFS, NTFS, etc */ - } - - get_fs_options(fsp->options, sizeof(fsp->options)-1, flags); - - ptr += strlen(ptr)+1; - } - - return SIGAR_OK; -} - -static PERF_INSTANCE_DEFINITION *get_disk_instance(sigar_t *sigar, - DWORD *perf_offsets, - DWORD *num, DWORD *err) -{ - PERF_OBJECT_TYPE *object = - get_perf_object(sigar, PERF_TITLE_DISK_KEY, err); - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - DWORD i, found=0; - - if (!object) { - return NULL; - } - - for (i=0, counter = PdhFirstCounter(object); - iNumCounters; - i++, counter = PdhNextCounter(counter)) - { - DWORD offset = counter->CounterOffset; - - switch (counter->CounterNameTitleIndex) { - case PERF_TITLE_DISK_TIME: - perf_offsets[PERF_IX_DISK_TIME] = offset; - found = 1; - break; - case PERF_TITLE_DISK_READ_TIME: - perf_offsets[PERF_IX_DISK_READ_TIME] = offset; - found = 1; - break; - case PERF_TITLE_DISK_WRITE_TIME: - perf_offsets[PERF_IX_DISK_WRITE_TIME] = offset; - found = 1; - break; - case PERF_TITLE_DISK_READ: - perf_offsets[PERF_IX_DISK_READ] = offset; - found = 1; - break; - case PERF_TITLE_DISK_WRITE: - perf_offsets[PERF_IX_DISK_WRITE] = offset; - found = 1; - break; - case PERF_TITLE_DISK_READ_BYTES: - perf_offsets[PERF_IX_DISK_READ_BYTES] = offset; - found = 1; - break; - case PERF_TITLE_DISK_WRITE_BYTES: - perf_offsets[PERF_IX_DISK_WRITE_BYTES] = offset; - found = 1; - break; - case PERF_TITLE_DISK_QUEUE: - perf_offsets[PERF_IX_DISK_QUEUE] = offset; - found = 1; - break; - } - } - - if (!found) { - *err = ENOENT; - return NULL; - } - - if (num) { - *num = object->NumInstances; - } - - return PdhFirstInstance(object); -} - -SIGAR_DECLARE(int) sigar_disk_usage_get(sigar_t *sigar, - const char *dirname, - sigar_disk_usage_t *disk) -{ - DWORD i, err; - PERF_OBJECT_TYPE *object = - get_perf_object(sigar, PERF_TITLE_DISK_KEY, &err); - PERF_INSTANCE_DEFINITION *inst; - PERF_COUNTER_DEFINITION *counter; - DWORD perf_offsets[PERF_IX_DISK_MAX]; - - SIGAR_DISK_STATS_INIT(disk); - - if (!object) { - return err; - } - - memset(&perf_offsets, 0, sizeof(perf_offsets)); - inst = get_disk_instance(sigar, (DWORD*)&perf_offsets, 0, &err); - - if (!inst) { - return err; - } - - for (i=0, inst = PdhFirstInstance(object); - iNumInstances; - i++, inst = PdhNextInstance(inst)) - { - char drive[MAX_PATH]; - PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst); - wchar_t *name = (wchar_t *)((BYTE *)inst + inst->NameOffset); - - SIGAR_W2A(name, drive, sizeof(drive)); - - if (sigar_isdigit(*name)) { - char *ptr = strchr(drive, ' '); /* 2000 Server "0 C:" */ - - if (ptr) { - ++ptr; - SIGAR_SSTRCPY(drive, ptr); - } - else { - /* XXX NT is a number only "0", how to map? */ - } - } - - if (strnEQ(drive, dirname, 2)) { - disk->time = PERF_VAL(PERF_IX_DISK_TIME); - disk->rtime = PERF_VAL(PERF_IX_DISK_READ_TIME); - disk->wtime = PERF_VAL(PERF_IX_DISK_WRITE_TIME); - disk->reads = PERF_VAL(PERF_IX_DISK_READ); - disk->writes = PERF_VAL(PERF_IX_DISK_WRITE); - disk->read_bytes = PERF_VAL(PERF_IX_DISK_READ_BYTES); - disk->write_bytes = PERF_VAL(PERF_IX_DISK_WRITE_BYTES); - disk->queue = PERF_VAL(PERF_IX_DISK_QUEUE); - return SIGAR_OK; - } - } - - return ENXIO; -} - -SIGAR_DECLARE(int) -sigar_file_system_usage_get(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - BOOL retval; - ULARGE_INTEGER avail, total, free; - int status; - - /* prevent dialog box if A:\ drive is empty */ - UINT errmode = SetErrorMode(SEM_FAILCRITICALERRORS); - - retval = GetDiskFreeSpaceEx(dirname, - &avail, &total, &free); - - /* restore previous error mode */ - SetErrorMode(errmode); - - if (!retval) { - return GetLastError(); - } - - fsusage->total = total.QuadPart / 1024; - fsusage->free = free.QuadPart / 1024; - fsusage->avail = avail.QuadPart / 1024; - fsusage->used = fsusage->total - fsusage->free; - fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); - - /* N/A */ - fsusage->files = SIGAR_FIELD_NOTIMPL; - fsusage->free_files = SIGAR_FIELD_NOTIMPL; - - status = sigar_disk_usage_get(sigar, dirname, &fsusage->disk); - - return SIGAR_OK; -} - -static int sigar_cpu_info_get(sigar_t *sigar, sigar_cpu_info_t *info) -{ - HKEY key, cpu; - int i = 0; - char id[MAX_PATH + 1]; - DWORD size = 0, rc; - - RegOpenKey(HKEY_LOCAL_MACHINE, - "HARDWARE\\DESCRIPTION\\System\\CentralProcessor", &key); - - //just lookup the first id, then assume all cpus are the same. - rc = RegEnumKey(key, 0, id, sizeof(id)); - if (rc != ERROR_SUCCESS) { - RegCloseKey(key); - return rc; - } - - rc = RegOpenKey(key, id, &cpu); - if (rc != ERROR_SUCCESS) { - RegCloseKey(key); - return rc; - } - - size = sizeof(info->vendor); - if (RegQueryValueEx(cpu, "VendorIdentifier", NULL, NULL, - (LPVOID)&info->vendor, &size) || - strEQ(info->vendor, "GenuineIntel")) - { - SIGAR_SSTRCPY(info->vendor, "Intel"); - } - else { - if (strEQ(info->vendor, "AuthenticAMD")) { - SIGAR_SSTRCPY(info->vendor, "AMD"); - } - } - - size = sizeof(info->model); - if (RegQueryValueEx(cpu, "ProcessorNameString", NULL, NULL, - (LPVOID)&info->model, &size)) - { - size = sizeof(info->model); - if (RegQueryValueEx(cpu, "Identifier", NULL, NULL, - (LPVOID)&info->model, &size)) - { - SIGAR_SSTRCPY(info->model, "x86"); - } - } - else { - sigar_cpu_model_adjust(sigar, info); - } - - size = sizeof(info->mhz); // == sizeof(DWORD) - if (RegQueryValueEx(cpu, "~MHz", NULL, NULL, - (LPVOID)&info->mhz, &size)) - { - info->mhz = -1; - } - - info->cache_size = -1; //XXX - RegCloseKey(key); - RegCloseKey(cpu); - - info->total_cores = sigar->ncpu; - info->cores_per_socket = sigar->lcpu; - info->total_sockets = sigar_cpu_socket_count(sigar); - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_cpu_info_list_get(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - int i, status; - sigar_cpu_info_t info; - int core_rollup = sigar_cpu_core_rollup(sigar); - - sigar_cpu_info_list_create(cpu_infos); - - status = sigar_cpu_info_get(sigar, &info); - - if (status != SIGAR_OK) { - return status; - } - - for (i=0; incpu; i++) { - SIGAR_CPU_INFO_LIST_GROW(cpu_infos); - - if (core_rollup && (i % sigar->lcpu)) { - continue; /* fold logical processors */ - } - - memcpy(&cpu_infos->data[cpu_infos->number++], - &info, sizeof(info)); - } - - return SIGAR_OK; -} - -#define sigar_GetNetworkParams \ - sigar->iphlpapi.get_net_params.func - -#define sigar_GetAdaptersInfo \ - sigar->iphlpapi.get_adapters_info.func - -#define sigar_GetAdaptersAddresses \ - sigar->iphlpapi.get_adapters_addrs.func - -#define sigar_GetNumberOfInterfaces \ - sigar->iphlpapi.get_num_if.func - -static sigar_cache_t *sigar_netif_cache_new(sigar_t *sigar) -{ - DWORD num = 0; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (sigar_GetNumberOfInterfaces) { - DWORD rc = sigar_GetNumberOfInterfaces(&num); - - if (rc == NO_ERROR) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetNumberOfInterfaces=%d", - num); - } - else { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetNumberOfInterfaces failed: %s", - sigar_strerror(sigar, rc)); - } - } - - if (num == 0) { - num = 10; /* reasonable default */ - } - - return sigar_cache_new(num); -} - -static int sigar_get_adapters_info(sigar_t *sigar, - PIP_ADAPTER_INFO *adapter) -{ - ULONG size = sigar->ifconf_len; - DWORD rc; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetAdaptersInfo) { - return SIGAR_ENOTIMPL; - } - - *adapter = (PIP_ADAPTER_INFO)sigar->ifconf_buf; - rc = sigar_GetAdaptersInfo(*adapter, &size); - - if (rc == ERROR_BUFFER_OVERFLOW) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetAdaptersInfo " - "realloc ifconf_buf old=%d, new=%d", - sigar->ifconf_len, size); - sigar->ifconf_len = size; - sigar->ifconf_buf = realloc(sigar->ifconf_buf, - sigar->ifconf_len); - - *adapter = (PIP_ADAPTER_INFO)sigar->ifconf_buf; - rc = sigar_GetAdaptersInfo(*adapter, &size); - } - - if (rc != NO_ERROR) { - return rc; - } - else { - return SIGAR_OK; - } -} - -static int sigar_get_adapter_info(sigar_t *sigar, - DWORD index, - IP_ADAPTER_INFO **adapter) -{ - sigar_cache_entry_t *entry; - *adapter = NULL; - - if (sigar->netif_adapters) { - entry = sigar_cache_get(sigar->netif_adapters, index); - if (entry->value) { - *adapter = (IP_ADAPTER_INFO *)entry->value; - } - } - else { - int status; - IP_ADAPTER_INFO *info; - - sigar->netif_adapters = - sigar_netif_cache_new(sigar); - - status = sigar_get_adapters_info(sigar, &info); - if (status != SIGAR_OK) { - return status; - } - - while (info) { - entry = sigar_cache_get(sigar->netif_adapters, - info->Index); - if (!entry->value) { - entry->value = malloc(sizeof(*info)); - } - memcpy(entry->value, info, sizeof(*info)); - if (info->Index == index) { - *adapter = info; - } - - info = info->Next; - } - } - - if (*adapter) { - return SIGAR_OK; - } - else { - return ENOENT; - } -} - -static int sigar_get_adapters_addresses(sigar_t *sigar, - ULONG family, ULONG flags, - PIP_ADAPTER_ADDRESSES *addrs, - ULONG *size) -{ - ULONG rc; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetAdaptersAddresses) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetAdaptersAddresses(family, - flags, - NULL, - *addrs, - size); - - if (rc == ERROR_BUFFER_OVERFLOW) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetAdaptersAddresses realloc to %d", size); - - *addrs = realloc(*addrs, *size); - - rc = sigar_GetAdaptersAddresses(family, - flags, - NULL, - (PIP_ADAPTER_ADDRESSES)*addrs, - size); - } - - if (rc != ERROR_SUCCESS) { - return rc; - } - else { - return SIGAR_OK; - } -} - -#define sigar_GetIpAddrTable \ - sigar->iphlpapi.get_ipaddr_table.func - -static int sigar_get_ipaddr_table(sigar_t *sigar, - PMIB_IPADDRTABLE *ipaddr) -{ - ULONG size = sigar->ifconf_len; - DWORD rc; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetIpAddrTable) { - return SIGAR_ENOTIMPL; - } - - *ipaddr = (PMIB_IPADDRTABLE)sigar->ifconf_buf; - rc = sigar_GetIpAddrTable(*ipaddr, &size, FALSE); - - if (rc == ERROR_INSUFFICIENT_BUFFER) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetIpAddrTable " - "realloc ifconf_buf old=%d, new=%d", - sigar->ifconf_len, size); - sigar->ifconf_len = size; - sigar->ifconf_buf = realloc(sigar->ifconf_buf, - sigar->ifconf_len); - - *ipaddr = (PMIB_IPADDRTABLE)sigar->ifconf_buf; - rc = sigar_GetIpAddrTable(*ipaddr, &size, FALSE); - } - - if (rc != NO_ERROR) { - return rc; - } - else { - return SIGAR_OK; - } -} - -#ifndef MIB_IPADDR_PRIMARY -#define MIB_IPADDR_PRIMARY 0x0001 -#endif - -static int sigar_get_netif_ipaddr(sigar_t *sigar, - DWORD index, - MIB_IPADDRROW **ipaddr) -{ - sigar_cache_entry_t *entry; - *ipaddr = NULL; - - if (sigar->netif_addr_rows) { - entry = sigar_cache_get(sigar->netif_addr_rows, index); - if (entry->value) { - *ipaddr = (MIB_IPADDRROW *)entry->value; - } - } - else { - int status, i; - MIB_IPADDRTABLE *mib; - - sigar->netif_addr_rows = - sigar_netif_cache_new(sigar); - - status = sigar_get_ipaddr_table(sigar, &mib); - if (status != SIGAR_OK) { - return status; - } - - for (i=0; idwNumEntries; i++) { - MIB_IPADDRROW *row = &mib->table[i]; - short type; - -#if HAVE_MIB_IPADDRROW_WTYPE - type = row->wType; -#else - type = row->unused2; -#endif - if (!(type & MIB_IPADDR_PRIMARY)) { - continue; - } - - entry = sigar_cache_get(sigar->netif_addr_rows, - row->dwIndex); - if (!entry->value) { - entry->value = malloc(sizeof(*row)); - } - memcpy(entry->value, row, sizeof(*row)); - - if (row->dwIndex == index) { - *ipaddr = row; - } - } - } - - if (*ipaddr) { - return SIGAR_OK; - } - else { - return ENOENT; - } -} - -SIGAR_DECLARE(int) sigar_net_info_get(sigar_t *sigar, - sigar_net_info_t *netinfo) -{ - PIP_ADAPTER_INFO adapter; - FIXED_INFO *info; - ULONG len = 0; - IP_ADDR_STRING *ip; - DWORD rc; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetNetworkParams) { - return SIGAR_ENOTIMPL; - } - - SIGAR_ZERO(netinfo); - - rc = sigar_GetNetworkParams(NULL, &len); - if (rc != ERROR_BUFFER_OVERFLOW) { - return rc; - } - - info = malloc(len); - rc = sigar_GetNetworkParams(info, &len); - if (rc != NO_ERROR) { - free(info); - return rc; - } - - SIGAR_SSTRCPY(netinfo->host_name, info->HostName); - SIGAR_SSTRCPY(netinfo->domain_name, info->DomainName); - SIGAR_SSTRCPY(netinfo->primary_dns, - info->DnsServerList.IpAddress.String); - - if ((ip = info->DnsServerList.Next)) { - SIGAR_SSTRCPY(netinfo->secondary_dns, - ip->IpAddress.String); - } - - free(info); - - if (sigar_get_adapters_info(sigar, &adapter) != SIGAR_OK) { - return SIGAR_OK; - } - - while (adapter) { - /* should only be 1 */ - if (adapter->GatewayList.IpAddress.String[0]) { - SIGAR_SSTRCPY(netinfo->default_gateway, - adapter->GatewayList.IpAddress.String); - } -#if 0 - if (apapters->DhcpEnabled) { - SIGAR_SSTRCPY(netinfo->dhcp_server, - apdaters->DhcpServer.IpAddress.String); - } -#endif - adapter = adapter->Next; - } - - return SIGAR_OK; -} - -#define sigar_GetIpForwardTable \ - sigar->iphlpapi.get_ipforward_table.func - -SIGAR_DECLARE(int) sigar_net_route_list_get(sigar_t *sigar, - sigar_net_route_list_t *routelist) -{ - PMIB_IPFORWARDTABLE buffer = NULL; - ULONG bufsize = 0; - DWORD rc, i; - MIB_IPFORWARDTABLE *ipt; - sigar_net_route_t *route; - - DLLMOD_INIT(iphlpapi, FALSE); - if (!sigar_GetIpForwardTable) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetIpForwardTable(buffer, &bufsize, FALSE); - if (rc != ERROR_INSUFFICIENT_BUFFER) { - return GetLastError(); - } - - buffer = malloc(bufsize); - rc = sigar_GetIpForwardTable(buffer, &bufsize, FALSE); - if (rc != NO_ERROR) { - free(buffer); - return GetLastError(); - } - - if (!sigar->netif_names) { - sigar_net_interface_list_get(sigar, NULL); - } - - sigar_net_route_list_create(routelist); - routelist->size = routelist->number = 0; - - ipt = buffer; - - for (i=0; idwNumEntries; i++) { - MIB_IPFORWARDROW *ipr = ipt->table + i; - sigar_cache_entry_t *entry; - - SIGAR_NET_ROUTE_LIST_GROW(routelist); - - route = &routelist->data[routelist->number++]; - SIGAR_ZERO(route); /* XXX: other fields */ - - sigar_net_address_set(route->destination, - ipr->dwForwardDest); - - sigar_net_address_set(route->mask, - ipr->dwForwardMask); - - sigar_net_address_set(route->gateway, - ipr->dwForwardNextHop); - - route->metric = ipr->dwForwardMetric1; - - route->flags = SIGAR_RTF_UP; - if ((ipr->dwForwardDest == 0) && - (ipr->dwForwardMask == 0)) - { - route->flags |= SIGAR_RTF_GATEWAY; - } - - entry = sigar_cache_get(sigar->netif_names, ipr->dwForwardIfIndex); - if (entry->value) { - SIGAR_SSTRCPY(route->ifname, (char *)entry->value); - } - } - - free(buffer); - - return SIGAR_OK; -} - -#define sigar_GetIfTable \ - sigar->iphlpapi.get_if_table.func - -#define sigar_GetIfEntry \ - sigar->iphlpapi.get_if_entry.func - -static int sigar_get_if_table(sigar_t *sigar, PMIB_IFTABLE *iftable) -{ - ULONG size = sigar->ifconf_len; - DWORD rc; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetIfTable) { - return SIGAR_ENOTIMPL; - } - - *iftable = (PMIB_IFTABLE)sigar->ifconf_buf; - rc = sigar_GetIfTable(*iftable, &size, FALSE); - - if (rc == ERROR_INSUFFICIENT_BUFFER) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "GetIfTable " - "realloc ifconf_buf old=%d, new=%d", - sigar->ifconf_len, size); - sigar->ifconf_len = size; - sigar->ifconf_buf = realloc(sigar->ifconf_buf, - sigar->ifconf_len); - - *iftable = (PMIB_IFTABLE)sigar->ifconf_buf; - rc = sigar_GetIfTable(*iftable, &size, FALSE); - } - - if (rc != NO_ERROR) { - return rc; - } - else { - return SIGAR_OK; - } -} - -static int get_mib_ifrow(sigar_t *sigar, - const char *name, - MIB_IFROW **ifrp) -{ - int status, key, cached=0; - sigar_cache_entry_t *entry; - - if (sigar->netif_mib_rows) { - cached = 1; - } - else { - status = sigar_net_interface_list_get(sigar, NULL); - if (status != SIGAR_OK) { - return status; - } - } - key = netif_hash(name); - entry = sigar_cache_get(sigar->netif_mib_rows, key); - if (!entry->value) { - return ENOENT; - } - - *ifrp = (MIB_IFROW *)entry->value; - if (cached) { - /* refresh */ - if ((status = sigar_GetIfEntry(*ifrp)) != NO_ERROR) { - return status; - } - } - - return SIGAR_OK; -} - -static int netif_hash(char *s) -{ - int hash = 0; - while (*s) { - hash = 31*hash + *s++; - } - return hash; -} - -/* Vista and later, wireless network cards are reported as IF_TYPE_IEEE80211 */ -#ifndef IF_TYPE_IEEE80211 -#define IF_TYPE_IEEE80211 71 -#endif - -static int -sigar_net_interface_name_get(sigar_t *sigar, MIB_IFROW *ifr, PIP_ADAPTER_ADDRESSES address_list, char *name) -{ - PIP_ADAPTER_ADDRESSES iter; - int lpc = 0; - - if (address_list == NULL) { - return SIGAR_ENOTIMPL; - } - - for (iter = address_list; iter != NULL; iter = iter->Next) { - for (lpc = 0; lpc < iter->PhysicalAddressLength; lpc++) { - if (iter->PhysicalAddress[lpc] != ifr->bPhysAddr[lpc]) { - break; - } - } - - if (lpc == iter->PhysicalAddressLength) { - wcstombs(name, iter->FriendlyName, MAX_INTERFACE_NAME_LEN); - name[MAX_INTERFACE_NAME_LEN-1] = '\0'; - return SIGAR_OK; - } - } - - return SIGAR_ENOENT; -} - -SIGAR_DECLARE(int) -sigar_net_interface_list_get(sigar_t *sigar, - sigar_net_interface_list_t *iflist) -{ - MIB_IFTABLE *ift; - int i, status; - int lo=0, eth=0, la=0; - PIP_ADAPTER_ADDRESSES address_list = NULL; - ULONG size = 0; - - status = sigar_get_adapters_addresses(sigar, AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, &address_list, &size); - - if (status != SIGAR_OK) { - address_list = NULL; - } - - if (!sigar->netif_mib_rows) { - sigar->netif_mib_rows = - sigar_netif_cache_new(sigar); - } - - if (!sigar->netif_names) { - sigar->netif_names = - sigar_netif_cache_new(sigar); - } - - if ((status = sigar_get_if_table(sigar, &ift)) != SIGAR_OK) { - if (address_list) { - free(address_list); - } - return status; - } - - if (iflist) { - iflist->number = 0; - iflist->size = ift->dwNumEntries; - iflist->data = - malloc(sizeof(*(iflist->data)) * iflist->size); - } - - for (i=0; idwNumEntries; i++) { - char name[MAX_INTERFACE_NAME_LEN]; - int key; - MIB_IFROW *ifr = ift->table + i; - sigar_cache_entry_t *entry; - - status = SIGAR_ENOENT; - - if (strEQ(ifr->bDescr, MS_LOOPBACK_ADAPTER)) { - /* special-case */ - sprintf(name, NETIF_LA "%d", la++); - } - else if (ifr->dwType == MIB_IF_TYPE_LOOPBACK) { - if (!sigar->netif_name_short) { - status = sigar_net_interface_name_get(sigar, ifr, address_list, name); - } - if (status != SIGAR_OK) { - sprintf(name, "lo%d", lo++); - } - } - else if ((ifr->dwType == MIB_IF_TYPE_ETHERNET) || - (ifr->dwType == IF_TYPE_IEEE80211)) - { - if (!sigar->netif_name_short && - (strstr(ifr->bDescr, "Scheduler") == NULL) && - (strstr(ifr->bDescr, "Filter") == NULL)) - { - status = sigar_net_interface_name_get(sigar, ifr, address_list, name); - } - - if (status != SIGAR_OK) { - if (sigar->netif_name_short) { - sprintf(name, "eth%d", eth++); - } - else { - snprintf(name, ifr->dwDescrLen, "%s", ifr->bDescr); - } - } - } - else { - continue; /*XXX*/ - } - - if (iflist) { - iflist->data[iflist->number++] = sigar_strdup(name); - } - - key = netif_hash(name); - entry = sigar_cache_get(sigar->netif_mib_rows, key); - if (!entry->value) { - entry->value = malloc(sizeof(*ifr)); - } - memcpy(entry->value, ifr, sizeof(*ifr)); - - /* save dwIndex -> name mapping for use by route_list */ - entry = sigar_cache_get(sigar->netif_names, ifr->dwIndex); - if (!entry->value) { - entry->value = sigar_strdup(name); - } - } - - if (address_list != NULL) { - free(address_list); - } - - return SIGAR_OK; -} - -static int sigar_net_interface_ipv6_config_find(sigar_t *sigar, int index, - sigar_net_interface_config_t *ifconfig) -{ -#ifdef SIGAR_USING_MSC6 - return SIGAR_ENOTIMPL; -#else - int status; - PIP_ADAPTER_ADDRESSES aa = (PIP_ADAPTER_ADDRESSES)sigar->ifconf_buf, addrs; - ULONG flags = GAA_FLAG_INCLUDE_PREFIX; - status = sigar_get_adapters_addresses(sigar, AF_UNSPEC, flags, &aa, &sigar->ifconf_len); - - if (status != SIGAR_OK) { - return status; - } - - for (addrs = aa; addrs; addrs = addrs->Next) { - PIP_ADAPTER_UNICAST_ADDRESS addr; - if (addrs->IfIndex != index) { - continue; - } - for (addr = addrs->FirstUnicastAddress; addr; addr = addr->Next) { - struct sockaddr *sa = addr->Address.lpSockaddr; - - if (sa->sa_family == AF_INET6) { - struct in6_addr *inet6 = SIGAR_SIN6_ADDR(sa); - - sigar_net_address6_set(ifconfig->address6, inet6); - sigar_net_interface_scope6_set(ifconfig, inet6); - if (addrs->FirstPrefix) { - ifconfig->prefix6_length = addrs->FirstPrefix->PrefixLength; - } - return SIGAR_OK; - } - } - } - return SIGAR_ENOENT; -#endif -} - -SIGAR_DECLARE(int) -sigar_net_interface_config_get(sigar_t *sigar, - const char *name, - sigar_net_interface_config_t *ifconfig) -{ - MIB_IFROW *ifr; - MIB_IPADDRROW *ipaddr; - int status; - - if (!name) { - return sigar_net_interface_config_primary_get(sigar, ifconfig); - } - - status = get_mib_ifrow(sigar, name, &ifr); - if (status != SIGAR_OK) { - return status; - } - - SIGAR_ZERO(ifconfig); - - SIGAR_SSTRCPY(ifconfig->name, name); - - ifconfig->mtu = ifr->dwMtu; - - sigar_net_address_mac_set(ifconfig->hwaddr, - ifr->bPhysAddr, - SIGAR_IFHWADDRLEN); - - SIGAR_SSTRCPY(ifconfig->description, - ifr->bDescr); - - if (ifr->dwOperStatus & MIB_IF_OPER_STATUS_OPERATIONAL) { - ifconfig->flags |= SIGAR_IFF_UP|SIGAR_IFF_RUNNING; - } - - status = sigar_get_netif_ipaddr(sigar, - ifr->dwIndex, - &ipaddr); - - if (status == SIGAR_OK) { - sigar_net_address_set(ifconfig->address, - ipaddr->dwAddr); - - sigar_net_address_set(ifconfig->netmask, - ipaddr->dwMask); - - if (ifr->dwType != MIB_IF_TYPE_LOOPBACK) { - if (ipaddr->dwBCastAddr) { - long bcast = - ipaddr->dwAddr & ipaddr->dwMask; - - bcast |= ~ipaddr->dwMask; - ifconfig->flags |= SIGAR_IFF_BROADCAST; - - sigar_net_address_set(ifconfig->broadcast, - bcast); - } - } - } - - /* hack for MS_LOOPBACK_ADAPTER */ - if (strnEQ(name, NETIF_LA, sizeof(NETIF_LA)-1)) { - ifr->dwType = MIB_IF_TYPE_LOOPBACK; - } - - if (ifr->dwType == MIB_IF_TYPE_LOOPBACK) { - ifconfig->flags |= SIGAR_IFF_LOOPBACK; - - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_LOOPBACK); - } - else { - if (ipaddr) { - ifconfig->flags |= SIGAR_IFF_MULTICAST; - } - - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_ETHERNET); - } - - sigar_net_interface_ipv6_config_init(ifconfig); - sigar_net_interface_ipv6_config_find(sigar, ifr->dwIndex, ifconfig); - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_net_interface_stat_get(sigar_t *sigar, const char *name, - sigar_net_interface_stat_t *ifstat) -{ - MIB_IFROW *ifr; - int status; - - status = get_mib_ifrow(sigar, name, &ifr); - if (status != SIGAR_OK) { - return status; - } - - ifstat->rx_bytes = ifr->dwInOctets; - ifstat->rx_packets = ifr->dwInUcastPkts + ifr->dwInNUcastPkts; - ifstat->rx_errors = ifr->dwInErrors; - ifstat->rx_dropped = ifr->dwInDiscards; - ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->rx_frame = SIGAR_FIELD_NOTIMPL; - - ifstat->tx_bytes = ifr->dwOutOctets; - ifstat->tx_packets = ifr->dwOutUcastPkts + ifr->dwOutNUcastPkts; - ifstat->tx_errors = ifr->dwOutErrors; - ifstat->tx_dropped = ifr->dwOutDiscards; - ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL; - ifstat->tx_collisions = SIGAR_FIELD_NOTIMPL; - ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL; - - ifstat->speed = ifr->dwSpeed; - - return SIGAR_OK; -} - -#define IS_TCP_SERVER(state, flags) \ - ((flags & SIGAR_NETCONN_SERVER) && (state == MIB_TCP_STATE_LISTEN)) - -#define IS_TCP_CLIENT(state, flags) \ - ((flags & SIGAR_NETCONN_CLIENT) && (state != MIB_TCP_STATE_LISTEN)) - -#define sigar_GetTcpTable \ - sigar->iphlpapi.get_tcp_table.func - -static int net_conn_get_tcp(sigar_net_connection_walker_t *walker) -{ - sigar_t *sigar = walker->sigar; - int flags = walker->flags; - int status, i; - DWORD rc, size=0; - PMIB_TCPTABLE tcp; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetTcpTable) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetTcpTable(NULL, &size, FALSE); - if (rc != ERROR_INSUFFICIENT_BUFFER) { - return GetLastError(); - } - tcp = malloc(size); - rc = sigar_GetTcpTable(tcp, &size, FALSE); - if (rc) { - free(tcp); - return GetLastError(); - } - - /* go in reverse to get LISTEN states first */ - for (i = (tcp->dwNumEntries-1); i >= 0; i--) { - sigar_net_connection_t conn; - DWORD state = tcp->table[i].dwState; - - if (!(IS_TCP_SERVER(state, flags) || - IS_TCP_CLIENT(state, flags))) - { - continue; - } - - conn.local_port = htons((WORD)tcp->table[i].dwLocalPort); - conn.remote_port = htons((WORD)tcp->table[i].dwRemotePort); - - conn.type = SIGAR_NETCONN_TCP; - - sigar_net_address_set(conn.local_address, - tcp->table[i].dwLocalAddr); - - sigar_net_address_set(conn.remote_address, - tcp->table[i].dwRemoteAddr); - - conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL; - - switch (state) { - case MIB_TCP_STATE_CLOSED: - conn.state = SIGAR_TCP_CLOSE; - break; - case MIB_TCP_STATE_LISTEN: - conn.state = SIGAR_TCP_LISTEN; - break; - case MIB_TCP_STATE_SYN_SENT: - conn.state = SIGAR_TCP_SYN_SENT; - break; - case MIB_TCP_STATE_SYN_RCVD: - conn.state = SIGAR_TCP_SYN_RECV; - break; - case MIB_TCP_STATE_ESTAB: - conn.state = SIGAR_TCP_ESTABLISHED; - break; - case MIB_TCP_STATE_FIN_WAIT1: - conn.state = SIGAR_TCP_FIN_WAIT1; - break; - case MIB_TCP_STATE_FIN_WAIT2: - conn.state = SIGAR_TCP_FIN_WAIT2; - break; - case MIB_TCP_STATE_CLOSE_WAIT: - conn.state = SIGAR_TCP_CLOSE_WAIT; - break; - case MIB_TCP_STATE_CLOSING: - conn.state = SIGAR_TCP_CLOSING; - break; - case MIB_TCP_STATE_LAST_ACK: - conn.state = SIGAR_TCP_LAST_ACK; - break; - case MIB_TCP_STATE_TIME_WAIT: - conn.state = SIGAR_TCP_TIME_WAIT; - break; - case MIB_TCP_STATE_DELETE_TCB: - default: - conn.state = SIGAR_TCP_UNKNOWN; - break; - } - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - - free(tcp); - return SIGAR_OK; -} - -#define IS_UDP_SERVER(conn, flags) \ - ((flags & SIGAR_NETCONN_SERVER) && !conn.remote_port) - -#define IS_UDP_CLIENT(state, flags) \ - ((flags & SIGAR_NETCONN_CLIENT) && conn.remote_port) - -#define sigar_GetUdpTable \ - sigar->iphlpapi.get_udp_table.func - -static int net_conn_get_udp(sigar_net_connection_walker_t *walker) -{ - sigar_t *sigar = walker->sigar; - int flags = walker->flags; - int status; - DWORD rc, size=0, i; - PMIB_UDPTABLE udp; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetUdpTable) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetUdpTable(NULL, &size, FALSE); - if (rc != ERROR_INSUFFICIENT_BUFFER) { - return GetLastError(); - } - udp = malloc(size); - rc = sigar_GetUdpTable(udp, &size, FALSE); - if (rc) { - free(udp); - return GetLastError(); - } - - for (i = 0; i < udp->dwNumEntries; i++) { - sigar_net_connection_t conn; - - if (!(IS_UDP_SERVER(conn, flags) || - IS_UDP_CLIENT(conn, flags))) - { - continue; - } - - conn.local_port = htons((WORD)udp->table[i].dwLocalPort); - conn.remote_port = 0; - - conn.type = SIGAR_NETCONN_UDP; - - sigar_net_address_set(conn.local_address, - udp->table[i].dwLocalAddr); - - sigar_net_address_set(conn.remote_address, 0); - - conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL; - - if (walker->add_connection(walker, &conn) != SIGAR_OK) { - break; - } - } - - free(udp); - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_net_connection_walk(sigar_net_connection_walker_t *walker) -{ - int status; - - if (walker->flags & SIGAR_NETCONN_TCP) { - status = net_conn_get_tcp(walker); - - if (status != SIGAR_OK) { - return status; - } - } - - if (walker->flags & SIGAR_NETCONN_UDP) { - status = net_conn_get_udp(walker); - - if (status != SIGAR_OK) { - return status; - } - } - - return SIGAR_OK; -} - -#define sigar_GetTcpStatistics \ - sigar->iphlpapi.get_tcp_stats.func - -SIGAR_DECLARE(int) -sigar_tcp_get(sigar_t *sigar, - sigar_tcp_t *tcp) -{ - MIB_TCPSTATS mib; - int status; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetTcpStatistics) { - return SIGAR_ENOTIMPL; - } - - status = sigar_GetTcpStatistics(&mib); - - if (status != NO_ERROR) { - return status; - } - - tcp->active_opens = mib.dwActiveOpens; - tcp->passive_opens = mib.dwPassiveOpens; - tcp->attempt_fails = mib.dwAttemptFails; - tcp->estab_resets = mib.dwEstabResets; - tcp->curr_estab = mib.dwCurrEstab; - tcp->in_segs = mib.dwInSegs; - tcp->out_segs = mib.dwOutSegs; - tcp->retrans_segs = mib.dwRetransSegs; - tcp->in_errs = mib.dwInErrs; - tcp->out_rsts = mib.dwOutRsts; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_nfs_client_v2_get(sigar_t *sigar, - sigar_nfs_client_v2_t *nfs) -{ - return SIGAR_ENOTIMPL; -} - -SIGAR_DECLARE(int) -sigar_nfs_server_v2_get(sigar_t *sigar, - sigar_nfs_server_v2_t *nfs) -{ - return SIGAR_ENOTIMPL; -} - -SIGAR_DECLARE(int) -sigar_nfs_client_v3_get(sigar_t *sigar, - sigar_nfs_client_v3_t *nfs) -{ - return SIGAR_ENOTIMPL; -} - -SIGAR_DECLARE(int) -sigar_nfs_server_v3_get(sigar_t *sigar, - sigar_nfs_server_v3_t *nfs) -{ - return SIGAR_ENOTIMPL; -} - -#define sigar_GetTcpExTable \ - sigar->iphlpapi.get_tcpx_table.func - -#define sigar_GetUdpExTable \ - sigar->iphlpapi.get_udpx_table.func - -SIGAR_DECLARE(int) sigar_proc_port_get(sigar_t *sigar, - int protocol, - unsigned long port, - sigar_pid_t *pid) -{ - DWORD rc, i; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (protocol == SIGAR_NETCONN_TCP) { - PMIB_TCPEXTABLE tcp; - - if (!sigar_GetTcpExTable) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetTcpExTable(&tcp, FALSE, GetProcessHeap(), - 2, 2); - - if (rc) { - return GetLastError(); - } - - for (i=0; idwNumEntries; i++) { - if (tcp->table[i].dwState != MIB_TCP_STATE_LISTEN) { - continue; - } - - if (htons((WORD)tcp->table[i].dwLocalPort) != port) { - continue; - } - - *pid = tcp->table[i].dwProcessId; - - return SIGAR_OK; - } - } - else if (protocol == SIGAR_NETCONN_UDP) { - PMIB_UDPEXTABLE udp; - - if (!sigar_GetUdpExTable) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetUdpExTable(&udp, FALSE, GetProcessHeap(), - 2, 2); - - if (rc) { - return GetLastError(); - } - - for (i=0; idwNumEntries; i++) { - if (htons((WORD)udp->table[i].dwLocalPort) != port) { - continue; - } - - *pid = udp->table[i].dwProcessId; - - return SIGAR_OK; - } - } - else { - return SIGAR_ENOTIMPL; - } - - return ENOENT; -} - -#define sigar_GetIpNetTable \ - sigar->iphlpapi.get_ipnet_table.func - -SIGAR_DECLARE(int) sigar_arp_list_get(sigar_t *sigar, - sigar_arp_list_t *arplist) -{ - int status; - DWORD rc, size=0, i; - PMIB_IPNETTABLE ipnet; - - DLLMOD_INIT(iphlpapi, FALSE); - - if (!sigar_GetIpNetTable) { - return SIGAR_ENOTIMPL; - } - - rc = sigar_GetIpNetTable(NULL, &size, FALSE); - if (rc != ERROR_INSUFFICIENT_BUFFER) { - return GetLastError(); - } - ipnet = malloc(size); - rc = sigar_GetIpNetTable(ipnet, &size, FALSE); - if (rc) { - free(ipnet); - return GetLastError(); - } - - sigar_arp_list_create(arplist); - - if (!sigar->netif_names) { - /* dwIndex -> name map */ - sigar_net_interface_list_get(sigar, NULL); - } - - for (i = 0; i < ipnet->dwNumEntries; i++) { - sigar_arp_t *arp; - PMIB_IPNETROW entry; - sigar_cache_entry_t *ifname; - - entry = &ipnet->table[i]; - SIGAR_ARP_LIST_GROW(arplist); - arp = &arplist->data[arplist->number++]; - - sigar_net_address_set(arp->address, - entry->dwAddr); - - sigar_net_address_mac_set(arp->hwaddr, - entry->bPhysAddr, - entry->dwPhysAddrLen); - - ifname = sigar_cache_get(sigar->netif_names, entry->dwIndex); - if (ifname->value) { - SIGAR_SSTRCPY(arp->ifname, (char *)ifname->value); - } - - arp->flags = 0; /*XXX*/ - SIGAR_SSTRCPY(arp->type, "ether"); /*XXX*/ - } - - free(ipnet); - - return SIGAR_OK; -} - -#include - -static int sigar_who_net_sessions(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - NET_API_STATUS status; - LPSESSION_INFO_10 buffer=NULL, ptr; - DWORD entries=0, total_entries=0; - DWORD resume_handle=0; - DWORD i; - - do { - status = NetSessionEnum(NULL, /* server name */ - NULL, /* client name */ - NULL, /* user name */ - 10, /* level */ - (LPBYTE*)&buffer, - MAX_PREFERRED_LENGTH, - &entries, - &total_entries, - &resume_handle); - - if ((status == NERR_Success) || (status == ERROR_MORE_DATA)) { - if ((ptr = buffer)) { - for (i=0; idata[wholist->number++]; - - who->time = (time(NULL) - ptr->sesi10_time); - SIGAR_W2A((LPCWSTR)ptr->sesi10_username, - who->user, sizeof(who->user)); - SIGAR_W2A((LPCWSTR)ptr->sesi10_cname, - who->host, sizeof(who->host)); - SIGAR_SSTRCPY(who->device, "network share"); - - ptr++; - } - } - } - else { - break; - } - - if (buffer) { - NetApiBufferFree(buffer); - buffer = NULL; - } - } while (status == ERROR_MORE_DATA); - - if (buffer) { - NetApiBufferFree(buffer); - } - - return SIGAR_OK; -} - -static int get_logon_info(HKEY users, - char *username, - sigar_who_t *who) -{ - DWORD status, size, type; - HKEY key; - char key_name[MAX_PATH]; - char value[256]; - FILETIME wtime; - - who->time = 0; - - sprintf(key_name, "%s\\Volatile Environment", username); - if (RegOpenKey(users, key_name, &key) != ERROR_SUCCESS) { - return ENOENT; - } - - status = RegQueryInfoKey(key, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &wtime); - - if (status == ERROR_SUCCESS) { - FileTimeToLocalFileTime(&wtime, &wtime); - who->time = sigar_FileTimeToTime(&wtime) / 1000000; - } - - size = sizeof(value); - status = RegQueryValueEx(key, "CLIENTNAME", - NULL, &type, value, &size); - if (status == ERROR_SUCCESS) { - if ((value[0] != '\0') && !strEQ(value, "Console")) { - SIGAR_SSTRCPY(who->host, value); - } - } - - size = sizeof(value); - status = RegQueryValueEx(key, "SESSIONNAME", - NULL, &type, value, &size); - if (status == ERROR_SUCCESS) { - SIGAR_SSTRCPY(who->device, value); - } - - RegCloseKey(key); - - return SIGAR_OK; -} - -#define sigar_ConvertStringSidToSid \ - sigar->advapi.convert_string_sid.func - -static int sigar_who_registry(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - HKEY users; - DWORD index=0, status; - - if (!sigar_ConvertStringSidToSid) { - return ENOENT; - } - - status = RegOpenKey(HKEY_USERS, NULL, &users); - if (status != ERROR_SUCCESS) { - return status; - } - - while (1) { - char subkey[MAX_PATH]; - char username[SIGAR_CRED_NAME_MAX]; - char domain[SIGAR_CRED_NAME_MAX]; - DWORD subkey_len = sizeof(subkey); - DWORD username_len = sizeof(username); - DWORD domain_len = sizeof(domain); - PSID sid; - SID_NAME_USE type; - - status = RegEnumKeyEx(users, index, subkey, &subkey_len, - NULL, NULL, NULL, NULL); - - if (status != ERROR_SUCCESS) { - break; - } - - index++; - - if ((subkey[0] == '.') || strstr(subkey, "_Classes")) { - continue; - } - - if (!sigar_ConvertStringSidToSid(subkey, &sid)) { - continue; - } - - if (LookupAccountSid(NULL, /* server */ - sid, - username, &username_len, - domain, &domain_len, - &type)) - { - sigar_who_t *who; - - SIGAR_WHO_LIST_GROW(wholist); - who = &wholist->data[wholist->number++]; - - SIGAR_SSTRCPY(who->user, username); - SIGAR_SSTRCPY(who->host, domain); - SIGAR_SSTRCPY(who->device, "console"); - - get_logon_info(users, subkey, who); - } - - LocalFree(sid); - } - - RegCloseKey(users); - - return SIGAR_OK; -} - -#define sigar_WTSEnumerateSessions \ - sigar->wtsapi.enum_sessions.func - -#define sigar_WTSFreeMemory \ - sigar->wtsapi.free_mem.func - -#define sigar_WTSQuerySessionInformation \ - sigar->wtsapi.query_session.func - -#define sigar_WinStationQueryInformation \ - sigar->winsta.query_info.func - -static int sigar_who_wts(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - DWORD count=0, i; - WTS_SESSION_INFO *sessions = NULL; - - if (DLLMOD_INIT(wtsapi, TRUE) != SIGAR_OK) { - sigar_log(sigar, SIGAR_LOG_DEBUG, - "Terminal Services api functions not available"); - return ENOENT; - } - - DLLMOD_INIT(winsta, FALSE); - - if (!sigar_WTSEnumerateSessions(0, 0, 1, &sessions, &count)) { - return GetLastError(); - } - - for (i=0; idata[wholist->number++]; - - SIGAR_SSTRCPY(who->device, sessions[i].pWinStationName); - - buffer = NULL; - bytes = 0; - if (sigar_WTSQuerySessionInformation(0, - sessionId, - WTSClientAddress, - &buffer, - &bytes)) - { - PWTS_CLIENT_ADDRESS client = - (PWTS_CLIENT_ADDRESS)buffer; - - sprintf(who->host, "%u.%u.%u.%u", - client->Address[2], - client->Address[3], - client->Address[4], - client->Address[5]); - - sigar_WTSFreeMemory(buffer); - } - else { - SIGAR_SSTRCPY(who->host, "unknown"); - } - - buffer = NULL; - bytes = 0; - if (sigar_WTSQuerySessionInformation(0, - sessionId, - WTSUserName, - &buffer, - &bytes)) - { - SIGAR_SSTRCPY(who->user, buffer); - sigar_WTSFreeMemory(buffer); - } - else { - SIGAR_SSTRCPY(who->user, "unknown"); - } - - buffer = NULL; - bytes = 0; - if (sigar_WinStationQueryInformation && - sigar_WinStationQueryInformation(0, - sessionId, - WinStationInformation, - &station_info, - sizeof(station_info), - &bytes)) - { - who->time = - sigar_FileTimeToTime(&station_info.ConnectTime) / 1000000; - } - else { - who->time = 0; - } - } - - sigar_WTSFreeMemory(sessions); - - return SIGAR_OK; -} - -int sigar_who_list_get_win32(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - sigar_who_net_sessions(sigar, wholist); - - sigar_who_registry(sigar, wholist); - - sigar_who_wts(sigar, wholist); - - return SIGAR_OK; -} - -/* see: http://msdn2.microsoft.com/en-us/library/ms724833.aspx */ -#ifndef VER_NT_WORKSTATION -#define VER_NT_WORKSTATION 0x0000001 -#endif - -#ifdef SIGAR_USING_MSC6 -#define sigar_wProductType wReserved[1] -#else -#define sigar_wProductType wProductType -#endif -#ifdef _M_X64 -#define SIGAR_ARCH "x64" -#else -#define SIGAR_ARCH "x86" -#endif - -int sigar_os_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sysinfo) -{ - OSVERSIONINFOEX version; - char *vendor_name, *vendor_version, *code_name=NULL; - - version.dwOSVersionInfoSize = sizeof(version); - GetVersionEx((OSVERSIONINFO *)&version); - - if (version.dwMajorVersion == 4) { - vendor_name = "Windows NT"; - vendor_version = "NT"; - } - else if (version.dwMajorVersion == 5) { - switch (version.dwMinorVersion) { - case 0: - vendor_name = "Windows 2000"; - vendor_version = "2000"; - break; - case 1: - vendor_name = "Windows XP"; - vendor_version = "XP"; - code_name = "Whistler"; - break; - case 2: - vendor_name = "Windows 2003"; - vendor_version = "2003"; - code_name = "Whistler Server"; - break; - default: - vendor_name = "Windows Unknown"; - break; - } - } - else if (version.dwMajorVersion == 6) { - if (version.sigar_wProductType == VER_NT_WORKSTATION) { - if (version.dwMinorVersion == 0) { - vendor_name = "Windows Vista"; - vendor_version = "Vista"; - code_name = "Longhorn"; - } - else { - vendor_name = "Windows 7"; - vendor_version = "7"; - code_name = "Vienna"; - } - } - else { - vendor_name = "Windows 2008"; - vendor_version = "2008"; - code_name = "Longhorn Server"; - } - } - - SIGAR_SSTRCPY(sysinfo->name, "Win32"); - SIGAR_SSTRCPY(sysinfo->vendor, "Microsoft"); - SIGAR_SSTRCPY(sysinfo->vendor_name, vendor_name); - SIGAR_SSTRCPY(sysinfo->vendor_version, vendor_version); - if (code_name) { - SIGAR_SSTRCPY(sysinfo->vendor_code_name, code_name); - } - - SIGAR_SSTRCPY(sysinfo->arch, SIGAR_ARCH); - - sprintf(sysinfo->version, "%d.%d", - version.dwMajorVersion, - version.dwMinorVersion); - - SIGAR_SSTRCPY(sysinfo->patch_level, - version.szCSDVersion); - - sprintf(sysinfo->description, "%s %s", - sysinfo->vendor, sysinfo->vendor_name); - - return SIGAR_OK; -} - -#define sigar_QueryServiceStatusEx \ - sigar->advapi.query_service_status.func - -int sigar_service_pid_get(sigar_t *sigar, char *name, sigar_pid_t *pid) -{ - DWORD rc = ERROR_SUCCESS, len; - SC_HANDLE mgr; - HANDLE svc; - SERVICE_STATUS_PROCESS status; - - if (!sigar_QueryServiceStatusEx) { - return SIGAR_ENOTIMPL; - } - - mgr = OpenSCManager(NULL, - SERVICES_ACTIVE_DATABASE, - SC_MANAGER_ALL_ACCESS); - - if (!mgr) { - return GetLastError(); - } - - if (!(svc = OpenService(mgr, name, SERVICE_ALL_ACCESS))) { - CloseServiceHandle(mgr); - return GetLastError(); - } - - if (sigar_QueryServiceStatusEx(svc, - SC_STATUS_PROCESS_INFO, - (LPBYTE)&status, - sizeof(status), &len)) - { - *pid = status.dwProcessId; - } - else { - *pid = -1; - rc = GetLastError(); - } - - CloseServiceHandle(svc); - CloseServiceHandle(mgr); - - return rc; -} - -int sigar_services_status_get(sigar_services_status_t *ss, DWORD state) -{ - DWORD bytes, resume=0; - BOOL retval; - - if (!ss->handle) { - ss->handle = - OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); - if (!ss->handle) { - return GetLastError(); - } - } - - retval = EnumServicesStatus(ss->handle, - SERVICE_WIN32, state, - ss->services, ss->size, - &bytes, &ss->count, &resume); - if (retval == FALSE) { - DWORD err = GetLastError(); - if (err != ERROR_MORE_DATA) { - return err; - } - - ss->services = realloc(ss->services, bytes); - ss->size = bytes; - - retval = EnumServicesStatus(ss->handle, - SERVICE_WIN32, state, - ss->services, ss->size, - &bytes, &ss->count, &resume); - - if (retval == FALSE) { - return GetLastError(); - } - } - - return SIGAR_OK; -} - -void sigar_services_status_close(sigar_services_status_t *ss) -{ - if (ss->handle) { - CloseServiceHandle(ss->handle); - } - if (ss->size) { - free(ss->services); - } - SIGAR_ZERO(ss); -} - -/* extract exe from QUERY_SERVICE_CONFIG.lpBinaryPathName - * leaves behind command-line arguments and quotes (if any) - */ -char *sigar_service_exe_get(char *path, char *buffer, int basename) -{ - char *ptr; - - if (path) { - strncpy(buffer, path, SIGAR_CMDLINE_MAX); - } - path = buffer; - - if (*path == '"') { - ++path; - if ((ptr = strchr(path, '"'))) { - *ptr = '\0'; - } - } - else { - ptr = sigar_strcasestr(path, ".exe"); - - if (ptr) { - *(ptr+4) = '\0'; - } - else { - if ((ptr = strchr(path, ' '))) { - *ptr = '\0'; - } - } - } - - if (basename && (ptr = strrchr(path, '\\'))) { - path = ++ptr; - } - - return path; -} - -static char *string_file_info_keys[] = { - "Comments", - "CompanyName", - "FileDescription", - "FileVersion", - "InternalName", - "LegalCopyright", - "LegalTrademarks", - "OriginalFilename", - "ProductName", - "ProductVersion", - "PrivateBuild", - "SpecialBuild", - NULL -}; - -int sigar_file_version_get(sigar_file_version_t *version, - char *name, - sigar_proc_env_t *infocb) -{ - DWORD handle, len; - LPTSTR data; - VS_FIXEDFILEINFO *info; - int status; - - if (!(len = GetFileVersionInfoSize(name, &handle))) { - return GetLastError(); - } - - if (len == 0) { - return !SIGAR_OK; - } - data = malloc(len); - - if (GetFileVersionInfo(name, handle, len, data)) { - if (VerQueryValue(data, "\\", &info, &len)) { - version->product_major = HIWORD(info->dwProductVersionMS); - version->product_minor = LOWORD(info->dwProductVersionMS); - version->product_build = HIWORD(info->dwProductVersionLS); - version->product_revision = LOWORD(info->dwProductVersionLS); - version->file_major = HIWORD(info->dwFileVersionMS); - version->file_minor = LOWORD(info->dwFileVersionMS); - version->file_build = HIWORD(info->dwFileVersionLS); - version->file_revision = LOWORD(info->dwFileVersionLS); - status = SIGAR_OK; - } - else { - status = GetLastError(); - } - } - else { - status = GetLastError(); - } - - if (infocb && (status == SIGAR_OK)) { - struct { - WORD lang; - WORD code_page; - } *trans; - - if (VerQueryValue(data, "\\VarFileInfo\\Translation", - &trans, &len)) - { - int i; - char buf[1024]; - void *ptr; - - for (i=0; string_file_info_keys[i]; i++) { - char *key = string_file_info_keys[i]; - sprintf(buf, "\\StringFileInfo\\%04x%04x\\%s", - trans[0].lang, trans[0].code_page, - key); - if (VerQueryValue(data, buf, &ptr, &len)) { - if (len == 0) { - continue; - } - infocb->env_getter(infocb->data, - key, strlen(key), - (char *)ptr, len); - } - } - } - } - - free(data); - return status; -} diff --git a/vendor/sigar/src/os/win32/wmi.cpp b/vendor/sigar/src/os/win32/wmi.cpp deleted file mode 100644 index bc1019d66..000000000 --- a/vendor/sigar/src/os/win32/wmi.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define UNICODE -#define _UNICODE -#define _WIN32_DCOM - -#include -#include -#include -#include -#include "sigar.h" - -#pragma comment(lib, "wbemuuid.lib") - -#ifndef SIGAR_CMDLINE_MAX -#define SIGAR_CMDLINE_MAX 4096<<2 -#endif - -class WMI { - - public: - WMI(); - ~WMI(); - HRESULT Open(LPCTSTR machine=NULL, LPCTSTR user=NULL, LPCTSTR pass=NULL); - void Close(); - HRESULT GetProcStringProperty(DWORD pid, TCHAR *name, TCHAR *value, DWORD len); - HRESULT GetProcExecutablePath(DWORD pid, TCHAR *value); - HRESULT GetProcCommandLine(DWORD pid, TCHAR *value); - int GetLastError(); - - private: - IWbemServices *wbem; - HRESULT result; - BSTR GetProcQuery(DWORD pid); -}; - -WMI::WMI() -{ - wbem = NULL; - result = S_OK; - CoInitializeEx(NULL, COINIT_MULTITHREADED); -} - -WMI::~WMI() -{ - Close(); - CoUninitialize(); -} - -/* XXX must be a better way to map HRESULT */ -int WMI::GetLastError() -{ - switch (result) { - case S_OK: - return ERROR_SUCCESS; - case WBEM_E_NOT_FOUND: - return ERROR_NOT_FOUND; - case WBEM_E_ACCESS_DENIED: - return ERROR_ACCESS_DENIED; - case WBEM_E_NOT_SUPPORTED: - return SIGAR_ENOTIMPL; - default: - return ERROR_INVALID_FUNCTION; - } -} - -HRESULT WMI::Open(LPCTSTR machine, LPCTSTR user, LPCTSTR pass) -{ - IWbemLocator *locator; - wchar_t path[MAX_PATH]; - - if (wbem) { - result = S_OK; - return result; - } - - result = - CoInitializeSecurity(NULL, //Security Descriptor - -1, //COM authentication - NULL, //Authentication services - NULL, //Reserved - RPC_C_AUTHN_LEVEL_DEFAULT, //Default authentication - RPC_C_IMP_LEVEL_IMPERSONATE, //Default Impersonation - NULL, //Authentication info - EOAC_NONE, //Additional capabilities - NULL); //Reserved - - result = CoCreateInstance(CLSID_WbemLocator, - NULL, /* IUnknown */ - CLSCTX_INPROC_SERVER, - IID_IWbemLocator, - (LPVOID *)&locator); - - if (FAILED(result)) { - return result; - } - - if (machine == NULL) { - machine = L"."; - } - - wsprintf(path, L"\\\\%S\\ROOT\\CIMV2", machine); - - result = locator->ConnectServer(bstr_t(path), //Object path of WMI namespace - bstr_t(user), //User name. NULL = current user - bstr_t(pass), //User password. NULL = current - NULL, //Locale. NULL indicates current - 0, //Security flags - NULL, //Authority (e.g. Kerberos) - NULL, //Context object - &wbem); //pointer to IWbemServices proxy - - locator->Release(); - - return result; -} - -void WMI::Close() -{ - if (wbem) { - wbem->Release(); - wbem = NULL; - result = S_OK; - } -} - -BSTR WMI::GetProcQuery(DWORD pid) -{ - wchar_t query[56]; - wsprintf(query, L"Win32_Process.Handle=%d", pid); - return bstr_t(query); -} - -HRESULT WMI::GetProcStringProperty(DWORD pid, TCHAR *name, TCHAR *value, DWORD len) -{ - IWbemClassObject *obj; - VARIANT var; - - result = wbem->GetObject(GetProcQuery(pid), 0, 0, &obj, 0); - - if (FAILED(result)) { - return result; - } - - result = obj->Get(name, 0, &var, 0, 0); - - if (SUCCEEDED(result)) { - if (var.vt == VT_NULL) { - result = E_INVALIDARG; - } - else { - lstrcpyn(value, var.bstrVal, len); - } - VariantClear(&var); - } - - obj->Release(); - - return result; -} - -HRESULT WMI::GetProcExecutablePath(DWORD pid, TCHAR *value) -{ - return GetProcStringProperty(pid, L"ExecutablePath", value, MAX_PATH); -} - -HRESULT WMI::GetProcCommandLine(DWORD pid, TCHAR *value) -{ - return GetProcStringProperty(pid, L"CommandLine", value, SIGAR_CMDLINE_MAX); -} - -/* in peb.c */ -extern "C" int sigar_parse_proc_args(sigar_t *sigar, WCHAR *buf, - sigar_proc_args_t *procargs); - -extern "C" int sigar_proc_args_wmi_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - int status; - TCHAR buf[SIGAR_CMDLINE_MAX]; - WMI *wmi = new WMI(); - - if (FAILED(wmi->Open())) { - return wmi->GetLastError(); - } - - if (FAILED(wmi->GetProcCommandLine(pid, buf))) { - status = wmi->GetLastError(); - } - else { - status = sigar_parse_proc_args(sigar, buf, procargs); - } - - wmi->Close(); - delete wmi; - - return status; -} - -extern "C" int sigar_proc_exe_wmi_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_exe_t *procexe) -{ - int status; - TCHAR buf[MAX_PATH+1]; - WMI *wmi = new WMI(); - - if (FAILED(wmi->Open())) { - return wmi->GetLastError(); - } - - procexe->name[0] = '\0'; - - if (FAILED(wmi->GetProcExecutablePath(pid, buf))) { - status = wmi->GetLastError(); - } - else { - status = SIGAR_OK; - /* SIGAR_W2A(buf, procexe->name, sizeof(procexe->name)); */ - WideCharToMultiByte(CP_ACP, 0, buf, -1, - (LPSTR)procexe->name, sizeof(procexe->name), - NULL, NULL); - } - - wmi->Close(); - delete wmi; - - return status; -} diff --git a/vendor/sigar/src/sigar.c b/vendor/sigar/src/sigar.c deleted file mode 100644 index 8bd7e9191..000000000 --- a/vendor/sigar/src/sigar.c +++ /dev/null @@ -1,2428 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2009-2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#ifndef WIN32 -#include -#include -#include -#endif -#if defined(__OpenBSD__) || defined(__FreeBSD__) -#include -#endif -#ifndef WIN32 -#include -#endif - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" -#include "sigar_format.h" - -SIGAR_DECLARE(int) sigar_open(sigar_t **sigar) -{ - int status = sigar_os_open(sigar); - - if (status == SIGAR_OK) { - /* use env to revert to old behavior */ - (*sigar)->cpu_list_cores = getenv("SIGAR_CPU_LIST_SOCKETS") ? 0 : 1; - (*sigar)->pid = 0; - (*sigar)->ifconf_buf = NULL; - (*sigar)->ifconf_len = 0; - (*sigar)->log_level = -1; /* log nothing by default */ - (*sigar)->log_impl = NULL; - (*sigar)->log_data = NULL; - (*sigar)->ptql_re_impl = NULL; - (*sigar)->ptql_re_data = NULL; - (*sigar)->self_path = NULL; - (*sigar)->fsdev = NULL; - (*sigar)->pids = NULL; - (*sigar)->proc_cpu = NULL; - (*sigar)->net_listen = NULL; - (*sigar)->net_services_tcp = NULL; - (*sigar)->net_services_udp = NULL; - } - - return status; -} - -SIGAR_DECLARE(int) sigar_close(sigar_t *sigar) -{ - if (sigar->ifconf_buf) { - free(sigar->ifconf_buf); - } - if (sigar->self_path) { - free(sigar->self_path); - } - if (sigar->pids) { - sigar_proc_list_destroy(sigar, sigar->pids); - free(sigar->pids); - } - if (sigar->fsdev) { - sigar_cache_destroy(sigar->fsdev); - } - if (sigar->proc_cpu) { - sigar_cache_destroy(sigar->proc_cpu); - } - if (sigar->net_listen) { - sigar_cache_destroy(sigar->net_listen); - } - if (sigar->net_services_tcp) { - sigar_cache_destroy(sigar->net_services_tcp); - } - if (sigar->net_services_udp) { - sigar_cache_destroy(sigar->net_services_udp); - } - - return sigar_os_close(sigar); -} - -#ifndef __linux__ /* linux has a special case */ -SIGAR_DECLARE(sigar_pid_t) sigar_pid_get(sigar_t *sigar) -{ - if (!sigar->pid) { - sigar->pid = getpid(); - } - - return sigar->pid; -} -#endif - -/* XXX: add clear() function */ -/* XXX: check for stale-ness using start_time */ -SIGAR_DECLARE(int) sigar_proc_cpu_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cpu_t *proccpu) -{ - sigar_cache_entry_t *entry; - sigar_proc_cpu_t *prev; - sigar_uint64_t otime, time_now = sigar_time_now_millis(); - sigar_uint64_t time_diff, total_diff; - int status; - - if (!sigar->proc_cpu) { - sigar->proc_cpu = sigar_cache_new(128); - } - - entry = sigar_cache_get(sigar->proc_cpu, pid); - if (entry->value) { - prev = (sigar_proc_cpu_t *)entry->value; - } - else { - prev = entry->value = malloc(sizeof(*prev)); - SIGAR_ZERO(prev); - } - - time_diff = time_now - prev->last_time; - proccpu->last_time = prev->last_time = time_now; - - if (time_diff == 0) { - /* we were just called within < 1 second ago. */ - memcpy(proccpu, prev, sizeof(*proccpu)); - return SIGAR_OK; - } - - otime = prev->total; - - status = - sigar_proc_time_get(sigar, pid, - (sigar_proc_time_t *)proccpu); - - if (status != SIGAR_OK) { - return status; - } - - memcpy(prev, proccpu, sizeof(*prev)); - - if (proccpu->total < otime) { - /* XXX this should not happen */ - otime = 0; - } - - if (otime == 0) { - proccpu->percent = 0.0; - /* first time called */ - return SIGAR_OK; - } - - total_diff = proccpu->total - otime; - proccpu->percent = total_diff / (double)time_diff; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_stat_get(sigar_t *sigar, - sigar_proc_stat_t *procstat) -{ - int status, i; - sigar_proc_list_t *pids; - - SIGAR_ZERO(procstat); - procstat->threads = SIGAR_FIELD_NOTIMPL; - - if ((status = sigar_proc_list_get(sigar, NULL)) != SIGAR_OK) { - return status; - } - - pids = sigar->pids; - procstat->total = pids->number; - - for (i=0; inumber; i++) { - sigar_proc_state_t state; - - status = sigar_proc_state_get(sigar, pids->data[i], &state); - if (status != SIGAR_OK) { - continue; - } - - if (state.threads != SIGAR_FIELD_NOTIMPL) { - procstat->threads += state.threads; - } - - switch (state.state) { - case SIGAR_PROC_STATE_IDLE: - procstat->idle++; - break; - case SIGAR_PROC_STATE_RUN: - procstat->running++; - break; - case SIGAR_PROC_STATE_SLEEP: - procstat->sleeping++; - break; - case SIGAR_PROC_STATE_STOP: - procstat->stopped++; - break; - case SIGAR_PROC_STATE_ZOMBIE: - procstat->zombie++; - break; - default: - break; - } - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_sys_info_get(sigar_t *sigar, - sigar_sys_info_t *sysinfo) -{ - SIGAR_ZERO(sysinfo); - -#ifndef WIN32 - sigar_sys_info_get_uname(sysinfo); -#endif - - sigar_os_sys_info_get(sigar, sysinfo); - - return SIGAR_OK; -} - -#ifndef WIN32 - -#include - -int sigar_sys_info_get_uname(sigar_sys_info_t *sysinfo) -{ - struct utsname name; - - uname(&name); - - SIGAR_SSTRCPY(sysinfo->version, name.release); - SIGAR_SSTRCPY(sysinfo->vendor_name, name.sysname); - SIGAR_SSTRCPY(sysinfo->name, name.sysname); - SIGAR_SSTRCPY(sysinfo->machine, name.machine); - SIGAR_SSTRCPY(sysinfo->arch, name.machine); - SIGAR_SSTRCPY(sysinfo->patch_level, "unknown"); - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_cred_name_t *proccredname) -{ - sigar_proc_cred_t cred; - - int status = sigar_proc_cred_get(sigar, pid, &cred); - - if (status != SIGAR_OK) { - return status; - } - - status = sigar_user_name_get(sigar, cred.uid, - proccredname->user, - sizeof(proccredname->user)); - - if (status != SIGAR_OK) { - return status; - } - - status = sigar_group_name_get(sigar, cred.gid, - proccredname->group, - sizeof(proccredname->group)); - - return status; -} - -#endif /* WIN32 */ - -int sigar_proc_list_create(sigar_proc_list_t *proclist) -{ - proclist->number = 0; - proclist->size = SIGAR_PROC_LIST_MAX; - proclist->data = malloc(sizeof(*(proclist->data)) * - proclist->size); - return SIGAR_OK; -} - -int sigar_proc_list_grow(sigar_proc_list_t *proclist) -{ - proclist->data = realloc(proclist->data, - sizeof(*(proclist->data)) * - (proclist->size + SIGAR_PROC_LIST_MAX)); - proclist->size += SIGAR_PROC_LIST_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_list_destroy(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - if (proclist->size) { - free(proclist->data); - proclist->number = proclist->size = 0; - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_list_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - if (proclist == NULL) { - /* internal re-use */ - if (sigar->pids == NULL) { - sigar->pids = malloc(sizeof(*sigar->pids)); - sigar_proc_list_create(sigar->pids); - } - else { - sigar->pids->number = 0; - } - proclist = sigar->pids; - } - else { - sigar_proc_list_create(proclist); - } - - return sigar_os_proc_list_get(sigar, proclist); -} - -int sigar_proc_args_create(sigar_proc_args_t *procargs) -{ - procargs->number = 0; - procargs->size = SIGAR_PROC_ARGS_MAX; - procargs->data = malloc(sizeof(*(procargs->data)) * - procargs->size); - return SIGAR_OK; -} - -int sigar_proc_args_grow(sigar_proc_args_t *procargs) -{ - procargs->data = realloc(procargs->data, - sizeof(*(procargs->data)) * - (procargs->size + SIGAR_PROC_ARGS_MAX)); - procargs->size += SIGAR_PROC_ARGS_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_args_destroy(sigar_t *sigar, - sigar_proc_args_t *procargs) -{ - unsigned int i; - - if (procargs->size) { - for (i=0; inumber; i++) { - free(procargs->data[i]); - } - free(procargs->data); - procargs->number = procargs->size = 0; - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_proc_args_get(sigar_t *sigar, - sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - int status; - sigar_proc_args_create(procargs); - status = sigar_os_proc_args_get(sigar, pid, procargs); - if (status != SIGAR_OK) { - sigar_proc_args_destroy(sigar, procargs); - } - return status; -} - -int sigar_file_system_list_create(sigar_file_system_list_t *fslist) -{ - fslist->number = 0; - fslist->size = SIGAR_FS_MAX; - fslist->data = malloc(sizeof(*(fslist->data)) * - fslist->size); - return SIGAR_OK; -} - -int sigar_file_system_list_grow(sigar_file_system_list_t *fslist) -{ - fslist->data = realloc(fslist->data, - sizeof(*(fslist->data)) * - (fslist->size + SIGAR_FS_MAX)); - fslist->size += SIGAR_FS_MAX; - - return SIGAR_OK; -} - -/* indexed with sigar_file_system_type_e */ -static const char *fstype_names[] = { - "unknown", "none", "local", "remote", "ram", "cdrom", "swap" -}; - -static int sigar_common_fs_type_get(sigar_file_system_t *fsp) -{ - char *type = fsp->sys_type_name; - - switch (*type) { - case 'n': - if (strnEQ(type, "nfs", 3)) { - fsp->type = SIGAR_FSTYPE_NETWORK; - } - break; - case 's': - if (strEQ(type, "smbfs")) { /* samba */ - fsp->type = SIGAR_FSTYPE_NETWORK; - } - else if (strEQ(type, "swap")) { - fsp->type = SIGAR_FSTYPE_SWAP; - } - break; - case 'a': - if (strEQ(type, "afs")) { - fsp->type = SIGAR_FSTYPE_NETWORK; - } - break; - case 'i': - if (strEQ(type, "iso9660")) { - fsp->type = SIGAR_FSTYPE_CDROM; - } - break; - case 'c': - if (strEQ(type, "cvfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - else if (strEQ(type, "cifs")) { - fsp->type = SIGAR_FSTYPE_NETWORK; - } - break; - case 'm': - if (strEQ(type, "msdos") || strEQ(type, "minix")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'h': - if (strEQ(type, "hpfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'v': - if (strEQ(type, "vxfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - else if (strEQ(type, "vfat")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - case 'z': - if (strEQ(type, "zfs")) { - fsp->type = SIGAR_FSTYPE_LOCAL_DISK; - } - break; - } - - return fsp->type; -} - -void sigar_fs_type_get(sigar_file_system_t *fsp) -{ - if (!(fsp->type || /* already set */ - sigar_os_fs_type_get(fsp) || /* try os specifics first */ - sigar_common_fs_type_get(fsp))) /* try common ones last */ - { - fsp->type = SIGAR_FSTYPE_NONE; - } - - if (fsp->type >= SIGAR_FSTYPE_MAX) { - fsp->type = SIGAR_FSTYPE_NONE; - } - - strcpy(fsp->type_name, fstype_names[fsp->type]); -} - - -SIGAR_DECLARE(int) -sigar_file_system_list_destroy(sigar_t *sigar, - sigar_file_system_list_t *fslist) -{ - if (fslist->size) { - free(fslist->data); - fslist->number = fslist->size = 0; - } - - return SIGAR_OK; -} - -#ifndef NFS_PROGRAM -#define NFS_PROGRAM 100003 -#endif - -#ifndef NFS_VERSION -#define NFS_VERSION 2 -#endif - -SIGAR_DECLARE(int) -sigar_file_system_ping(sigar_t *sigar, - sigar_file_system_t *fs) -{ - int status = SIGAR_OK; -#ifndef WIN32 - char *ptr; - - if ((fs->type == SIGAR_FSTYPE_NETWORK) && - strEQ(fs->sys_type_name, "nfs") && - (ptr = strchr(fs->dev_name, ':'))) - { - *ptr = '\0'; /* "hostname:/mount" -> "hostname" */ - - status = sigar_rpc_ping(fs->dev_name, - SIGAR_NETCONN_UDP, - NFS_PROGRAM, NFS_VERSION); - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fs_ping] %s -> %s: %s", - fs->dir_name, fs->dev_name, - ((status == SIGAR_OK) ? - "OK" : sigar_rpc_strerror(status))); - } - - *ptr = ':'; /* "hostname" -> "hostname:/mount" */ - } -#endif - return status; -} - -int sigar_cpu_info_list_create(sigar_cpu_info_list_t *cpu_infos) -{ - cpu_infos->number = 0; - cpu_infos->size = SIGAR_CPU_INFO_MAX; - cpu_infos->data = malloc(sizeof(*(cpu_infos->data)) * - cpu_infos->size); - return SIGAR_OK; -} - -int sigar_cpu_info_list_grow(sigar_cpu_info_list_t *cpu_infos) -{ - cpu_infos->data = realloc(cpu_infos->data, - sizeof(*(cpu_infos->data)) * - (cpu_infos->size + SIGAR_CPU_INFO_MAX)); - cpu_infos->size += SIGAR_CPU_INFO_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_cpu_info_list_destroy(sigar_t *sigar, - sigar_cpu_info_list_t *cpu_infos) -{ - if (cpu_infos->size) { - free(cpu_infos->data); - cpu_infos->number = cpu_infos->size = 0; - } - - return SIGAR_OK; -} - -int sigar_cpu_list_create(sigar_cpu_list_t *cpulist) -{ - cpulist->number = 0; - cpulist->size = SIGAR_CPU_INFO_MAX; - cpulist->data = malloc(sizeof(*(cpulist->data)) * - cpulist->size); - return SIGAR_OK; -} - -int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist) -{ - cpulist->data = realloc(cpulist->data, - sizeof(*(cpulist->data)) * - (cpulist->size + SIGAR_CPU_INFO_MAX)); - cpulist->size += SIGAR_CPU_INFO_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_cpu_list_destroy(sigar_t *sigar, - sigar_cpu_list_t *cpulist) -{ - if (cpulist->size) { - free(cpulist->data); - cpulist->number = cpulist->size = 0; - } - - return SIGAR_OK; -} - -int sigar_net_route_list_create(sigar_net_route_list_t *routelist) -{ - routelist->number = 0; - routelist->size = SIGAR_NET_ROUTE_LIST_MAX; - routelist->data = malloc(sizeof(*(routelist->data)) * - routelist->size); - return SIGAR_OK; -} - -int sigar_net_route_list_grow(sigar_net_route_list_t *routelist) -{ - routelist->data = - realloc(routelist->data, - sizeof(*(routelist->data)) * - (routelist->size + SIGAR_NET_ROUTE_LIST_MAX)); - routelist->size += SIGAR_NET_ROUTE_LIST_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_net_route_list_destroy(sigar_t *sigar, - sigar_net_route_list_t *routelist) -{ - if (routelist->size) { - free(routelist->data); - routelist->number = routelist->size = 0; - } - - return SIGAR_OK; -} - -int sigar_net_interface_list_create(sigar_net_interface_list_t *iflist) -{ - iflist->number = 0; - iflist->size = SIGAR_NET_IFLIST_MAX; - iflist->data = malloc(sizeof(*(iflist->data)) * - iflist->size); - return SIGAR_OK; -} - -int sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist) -{ - iflist->data = realloc(iflist->data, - sizeof(*(iflist->data)) * - (iflist->size + SIGAR_NET_IFLIST_MAX)); - iflist->size += SIGAR_NET_IFLIST_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_net_interface_list_destroy(sigar_t *sigar, - sigar_net_interface_list_t *iflist) -{ - unsigned int i; - - if (iflist->size) { - for (i=0; inumber; i++) { - free(iflist->data[i]); - } - free(iflist->data); - iflist->number = iflist->size = 0; - } - - return SIGAR_OK; -} - -int sigar_net_connection_list_create(sigar_net_connection_list_t *connlist) -{ - connlist->number = 0; - connlist->size = SIGAR_NET_CONNLIST_MAX; - connlist->data = malloc(sizeof(*(connlist->data)) * - connlist->size); - return SIGAR_OK; -} - -int sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist) -{ - connlist->data = - realloc(connlist->data, - sizeof(*(connlist->data)) * - (connlist->size + SIGAR_NET_CONNLIST_MAX)); - connlist->size += SIGAR_NET_CONNLIST_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_net_connection_list_destroy(sigar_t *sigar, - sigar_net_connection_list_t *connlist) -{ - if (connlist->size) { - free(connlist->data); - connlist->number = connlist->size = 0; - } - - return SIGAR_OK; -} - -#if !defined(__linux__) -/* - * implement sigar_net_connection_list_get using sigar_net_connection_walk - * linux has its own list_get impl. - */ -static int net_connection_list_walker(sigar_net_connection_walker_t *walker, - sigar_net_connection_t *conn) -{ - sigar_net_connection_list_t *connlist = - (sigar_net_connection_list_t *)walker->data; - - SIGAR_NET_CONNLIST_GROW(connlist); - memcpy(&connlist->data[connlist->number++], - conn, sizeof(*conn)); - - return SIGAR_OK; /* continue loop */ -} - -SIGAR_DECLARE(int) -sigar_net_connection_list_get(sigar_t *sigar, - sigar_net_connection_list_t *connlist, - int flags) -{ - int status; - sigar_net_connection_walker_t walker; - - sigar_net_connection_list_create(connlist); - - walker.sigar = sigar; - walker.flags = flags; - walker.data = connlist; - walker.add_connection = net_connection_list_walker; - - status = sigar_net_connection_walk(&walker); - - if (status != SIGAR_OK) { - sigar_net_connection_list_destroy(sigar, connlist); - } - - return status; -} -#endif - -static void sigar_net_listen_address_add(sigar_t *sigar, - sigar_net_connection_t *conn) -{ - sigar_cache_entry_t *entry = - sigar_cache_get(sigar->net_listen, conn->local_port); - - if (entry->value) { - if (conn->local_address.family == SIGAR_AF_INET6) { - return; /* prefer ipv4 */ - } - } - else { - entry->value = malloc(sizeof(conn->local_address)); - } - - memcpy(entry->value, &conn->local_address, - sizeof(conn->local_address)); -} - -SIGAR_DECLARE(int) -sigar_net_listen_address_get(sigar_t *sigar, - unsigned long port, - sigar_net_address_t *address) -{ - if (!sigar->net_listen || - !sigar_cache_find(sigar->net_listen, port)) - { - sigar_net_stat_t netstat; - int status = - sigar_net_stat_get(sigar, &netstat, - SIGAR_NETCONN_SERVER|SIGAR_NETCONN_TCP); - - if (status != SIGAR_OK) { - return status; - } - } - - if (sigar_cache_find(sigar->net_listen, port)) { - void *value = sigar_cache_get(sigar->net_listen, port)->value; - memcpy(address, value, sizeof(*address)); - return SIGAR_OK; - } - else { - return ENOENT; - } -} - -typedef struct { - sigar_net_stat_t *netstat; - sigar_net_connection_list_t *connlist; -} net_stat_getter_t; - -static int net_stat_walker(sigar_net_connection_walker_t *walker, - sigar_net_connection_t *conn) -{ - int state = conn->state; - sigar_cache_t *listen_ports = walker->sigar->net_listen; - net_stat_getter_t *getter = - (net_stat_getter_t *)walker->data; - - if (conn->type == SIGAR_NETCONN_TCP) { - getter->netstat->tcp_states[state]++; - - /* XXX listen_ports may get stale */ - if (state == SIGAR_TCP_LISTEN) { - sigar_net_listen_address_add(walker->sigar, conn); - } - else { - if (sigar_cache_find(listen_ports, - conn->local_port)) - { - getter->netstat->tcp_inbound_total++; - } - else { - getter->netstat->tcp_outbound_total++; - } - } - } - else if (conn->type == SIGAR_NETCONN_UDP) { - /*XXX*/ - } - - getter->netstat->all_inbound_total = - getter->netstat->tcp_inbound_total; - - getter->netstat->all_outbound_total = - getter->netstat->tcp_outbound_total; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_net_stat_get(sigar_t *sigar, - sigar_net_stat_t *netstat, - int flags) -{ - sigar_net_connection_walker_t walker; - net_stat_getter_t getter; - - if (!sigar->net_listen) { - sigar->net_listen = sigar_cache_new(32); - } - - SIGAR_ZERO(netstat); - - getter.netstat = netstat; - - walker.sigar = sigar; - walker.data = &getter; - walker.add_connection = net_stat_walker; - - walker.flags = flags; - - return sigar_net_connection_walk(&walker); -} - -typedef struct { - sigar_net_stat_t *netstat; - sigar_net_address_t *address; - unsigned long port; -} net_stat_port_getter_t; - -static int net_stat_port_walker(sigar_net_connection_walker_t *walker, - sigar_net_connection_t *conn) -{ - net_stat_port_getter_t *getter = - (net_stat_port_getter_t *)walker->data; - sigar_net_stat_t *netstat = getter->netstat; - - if (conn->type == SIGAR_NETCONN_TCP) { - if (conn->local_port == getter->port) { - netstat->all_inbound_total++; - - if (sigar_net_address_equals(getter->address, - &conn->local_address) == SIGAR_OK) - { - netstat->tcp_inbound_total++; - } - } - else if (conn->remote_port == getter->port) { - netstat->all_outbound_total++; - - if (sigar_net_address_equals(getter->address, - &conn->remote_address) == SIGAR_OK) - { - netstat->tcp_outbound_total++; - } - } - else { - return SIGAR_OK; - } - - netstat->tcp_states[conn->state]++; - } - else if (conn->type == SIGAR_NETCONN_UDP) { - /*XXX*/ - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) -sigar_net_stat_port_get(sigar_t *sigar, - sigar_net_stat_t *netstat, - int flags, - sigar_net_address_t *address, - unsigned long port) -{ - sigar_net_connection_walker_t walker; - net_stat_port_getter_t getter; - - SIGAR_ZERO(netstat); - - getter.netstat = netstat; - getter.address = address; - getter.port = port; - - walker.sigar = sigar; - walker.data = &getter; - walker.add_connection = net_stat_port_walker; - - walker.flags = flags; - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - char name[SIGAR_FQDN_LEN]; - sigar_net_address_to_string(sigar, address, name); - - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[net_stat_port] using address '%s:%d'", - name, port); - } - - return sigar_net_connection_walk(&walker); -} - -static int tcp_curr_estab_count(sigar_net_connection_walker_t *walker, - sigar_net_connection_t *conn) -{ - if ((conn->state == SIGAR_TCP_ESTABLISHED) || - (conn->state == SIGAR_TCP_CLOSE_WAIT)) - { - ((sigar_tcp_t *)walker->data)->curr_estab++; - } - - return SIGAR_OK; -} - -/* TCP-MIB::tcpCurrEstab */ -int sigar_tcp_curr_estab(sigar_t *sigar, sigar_tcp_t *tcp) -{ - sigar_net_connection_walker_t walker; - - walker.sigar = sigar; - walker.data = tcp; - walker.add_connection = tcp_curr_estab_count; - walker.flags = SIGAR_NETCONN_CLIENT|SIGAR_NETCONN_TCP; - - tcp->curr_estab = 0; - - return sigar_net_connection_walk(&walker); -} - -int sigar_arp_list_create(sigar_arp_list_t *arplist) -{ - arplist->number = 0; - arplist->size = SIGAR_ARP_LIST_MAX; - arplist->data = malloc(sizeof(*(arplist->data)) * - arplist->size); - return SIGAR_OK; -} - -int sigar_arp_list_grow(sigar_arp_list_t *arplist) -{ - arplist->data = realloc(arplist->data, - sizeof(*(arplist->data)) * - (arplist->size + SIGAR_ARP_LIST_MAX)); - arplist->size += SIGAR_ARP_LIST_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_arp_list_destroy(sigar_t *sigar, - sigar_arp_list_t *arplist) -{ - if (arplist->size) { - free(arplist->data); - arplist->number = arplist->size = 0; - } - - return SIGAR_OK; -} - -int sigar_who_list_create(sigar_who_list_t *wholist) -{ - wholist->number = 0; - wholist->size = SIGAR_WHO_LIST_MAX; - wholist->data = malloc(sizeof(*(wholist->data)) * - wholist->size); - return SIGAR_OK; -} - -int sigar_who_list_grow(sigar_who_list_t *wholist) -{ - wholist->data = realloc(wholist->data, - sizeof(*(wholist->data)) * - (wholist->size + SIGAR_WHO_LIST_MAX)); - wholist->size += SIGAR_WHO_LIST_MAX; - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_who_list_destroy(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - if (wholist->size) { - free(wholist->data); - wholist->number = wholist->size = 0; - } - - return SIGAR_OK; -} - -#ifdef DARWIN -#include -#endif -#ifdef MAC_OS_X_VERSION_10_5 -# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 -# define SIGAR_NO_UTMP -# endif -/* else 10.4 and earlier or compiled with -mmacosx-version-min=10.3 */ -#endif - -#if defined(__sun) -# include -# define SIGAR_UTMP_FILE _UTMPX_FILE -# define ut_time ut_tv.tv_sec -#elif defined(WIN32) -/* XXX may not be the default */ -#define SIGAR_UTMP_FILE "C:\\cygwin\\var\\run\\utmp" -#define UT_LINESIZE 16 -#define UT_NAMESIZE 16 -#define UT_HOSTSIZE 256 -#define UT_IDLEN 2 -#define ut_name ut_user - -struct utmp { - short ut_type; - int ut_pid; - char ut_line[UT_LINESIZE]; - char ut_id[UT_IDLEN]; - time_t ut_time; - char ut_user[UT_NAMESIZE]; - char ut_host[UT_HOSTSIZE]; - long ut_addr; -}; -#elif defined(NETWARE) -static char *getpass(const char *prompt) -{ - static char password[BUFSIZ]; - - fputs(prompt, stderr); - fgets((char *)&password, sizeof(password), stdin); - - return (char *)&password; -} -#elif !defined(SIGAR_NO_UTMP) -# include -# ifdef UTMP_FILE -# define SIGAR_UTMP_FILE UTMP_FILE -# else -# define SIGAR_UTMP_FILE _PATH_UTMP -# endif -#endif - -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(DARWIN) -# define ut_user ut_name -#endif - -#ifdef DARWIN -/* XXX from utmpx.h; sizeof changed in 10.5 */ -/* additionally, utmpx does not work on 10.4 */ -#define SIGAR_HAS_UTMPX -#define _PATH_UTMPX "/var/run/utmpx" -#define _UTX_USERSIZE 256 /* matches MAXLOGNAME */ -#define _UTX_LINESIZE 32 -#define _UTX_IDSIZE 4 -#define _UTX_HOSTSIZE 256 -struct utmpx { - char ut_user[_UTX_USERSIZE]; /* login name */ - char ut_id[_UTX_IDSIZE]; /* id */ - char ut_line[_UTX_LINESIZE]; /* tty name */ - pid_t ut_pid; /* process id creating the entry */ - short ut_type; /* type of this entry */ - struct timeval ut_tv; /* time entry was created */ - char ut_host[_UTX_HOSTSIZE]; /* host name */ - __uint32_t ut_pad[16]; /* reserved for future use */ -}; -#define ut_xtime ut_tv.tv_sec -#define UTMPX_USER_PROCESS 7 -/* end utmpx.h */ -#define SIGAR_UTMPX_FILE _PATH_UTMPX -#endif - -#if !defined(NETWARE) && !defined(_AIX) - -#define WHOCPY(dest, src) \ - SIGAR_SSTRCPY(dest, src); \ - if (sizeof(src) < sizeof(dest)) \ - dest[sizeof(src)] = '\0' - -#ifdef SIGAR_HAS_UTMPX -static int sigar_who_utmpx(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - FILE *fp; - struct utmpx ut; - - if (!(fp = fopen(SIGAR_UTMPX_FILE, "r"))) { - return errno; - } - - while (fread(&ut, sizeof(ut), 1, fp) == 1) { - sigar_who_t *who; - - if (*ut.ut_user == '\0') { - continue; - } - -#ifdef UTMPX_USER_PROCESS - if (ut.ut_type != UTMPX_USER_PROCESS) { - continue; - } -#endif - - SIGAR_WHO_LIST_GROW(wholist); - who = &wholist->data[wholist->number++]; - - WHOCPY(who->user, ut.ut_user); - WHOCPY(who->device, ut.ut_line); - WHOCPY(who->host, ut.ut_host); - - who->time = ut.ut_xtime; - } - - fclose(fp); - - return SIGAR_OK; -} -#endif - -#if defined(SIGAR_NO_UTMP) && defined(SIGAR_HAS_UTMPX) -#define sigar_who_utmp sigar_who_utmpx -#else -static int sigar_who_utmp(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - FILE *fp; -#ifdef __sun - /* use futmpx w/ pid32_t for sparc64 */ - struct futmpx ut; -#else - struct utmp ut; -#endif - if (!(fp = fopen(SIGAR_UTMP_FILE, "r"))) { -#ifdef SIGAR_HAS_UTMPX - /* Darwin 10.5 */ - return sigar_who_utmpx(sigar, wholist); -#endif - return errno; - } - - while (fread(&ut, sizeof(ut), 1, fp) == 1) { - sigar_who_t *who; - - if (*ut.ut_name == '\0') { - continue; - } - -#ifdef USER_PROCESS - if (ut.ut_type != USER_PROCESS) { - continue; - } -#endif - - SIGAR_WHO_LIST_GROW(wholist); - who = &wholist->data[wholist->number++]; - - WHOCPY(who->user, ut.ut_user); - WHOCPY(who->device, ut.ut_line); - WHOCPY(who->host, ut.ut_host); - - who->time = ut.ut_time; - } - - fclose(fp); - - return SIGAR_OK; -} -#endif /* SIGAR_NO_UTMP */ -#endif /* NETWARE */ - -#if defined(WIN32) - -int sigar_who_list_get_win32(sigar_t *sigar, - sigar_who_list_t *wholist); - -SIGAR_DECLARE(int) sigar_who_list_get(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - sigar_who_list_create(wholist); - - /* cygwin ssh */ - sigar_who_utmp(sigar, wholist); - - sigar_who_list_get_win32(sigar, wholist); - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_resource_limit_get(sigar_t *sigar, - sigar_resource_limit_t *rlimit) -{ - MEMORY_BASIC_INFORMATION meminfo; - memset(rlimit, 0x7fffffff, sizeof(*rlimit)); - - if (VirtualQuery((LPCVOID)&meminfo, &meminfo, sizeof(meminfo))) { - rlimit->stack_cur = - (DWORD)&meminfo - (DWORD)meminfo.AllocationBase; - rlimit->stack_max = - ((DWORD)meminfo.BaseAddress + meminfo.RegionSize) - - (DWORD)meminfo.AllocationBase; - } - - rlimit->virtual_memory_max = rlimit->virtual_memory_cur = - 0x80000000UL; - - return SIGAR_OK; -} - -#elif defined(NETWARE) -int sigar_resource_limit_get(sigar_t *sigar, - sigar_resource_limit_t *rlimit) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_who_list_get(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - return SIGAR_ENOTIMPL; -} -#else - -#ifndef _AIX -int sigar_who_list_get(sigar_t *sigar, - sigar_who_list_t *wholist) -{ - int status; - - sigar_who_list_create(wholist); - - status = sigar_who_utmp(sigar, wholist); - if (status != SIGAR_OK) { - sigar_who_list_destroy(sigar, wholist); - return status; - } - - return SIGAR_OK; -} -#endif - -static int sigar_get_default_gateway(sigar_t *sigar, - sigar_net_info_t *netinfo) -{ - int status, i; - sigar_net_route_list_t routelist; - - status = sigar_net_route_list_get(sigar, &routelist); - if (status != SIGAR_OK) { - return status; - } - - for (i=0; idefault_gateway); - - SIGAR_STRNCPY(netinfo->default_gateway_interface, - routelist.data[i].ifname, - sizeof(netinfo->default_gateway_interface)); - break; - } - } - - sigar_net_route_list_destroy(sigar, &routelist); - - return SIGAR_OK; -} - -int sigar_net_info_get(sigar_t *sigar, - sigar_net_info_t *netinfo) -{ - int size; - char buffer[BUFSIZ], *ptr; - FILE *fp; - - SIGAR_ZERO(netinfo); - - if ((fp = fopen("/etc/resolv.conf", "r"))) { - while ((ptr = fgets(buffer, sizeof(buffer), fp))) { - int len; - - SIGAR_SKIP_SPACE(ptr); - if ((*ptr == '#') || - !(ptr = strstr(ptr, "nameserver"))) - { - continue; - } - ptr += 10; - SIGAR_SKIP_SPACE(ptr); - - len = strlen(ptr); - ptr[len-1] = '\0'; /* chop \n */ - - if (!netinfo->primary_dns[0]) { - SIGAR_SSTRCPY(netinfo->primary_dns, ptr); - } - else if (!netinfo->secondary_dns[0]) { - SIGAR_SSTRCPY(netinfo->secondary_dns, ptr); - } - else { - break; - } - } - fclose(fp); - } /* else /etc/resolv.conf may not exist if unplugged (MacOSX) */ - - size = sizeof(netinfo->host_name)-1; - if (gethostname(netinfo->host_name, size) == 0) { - netinfo->host_name[size] = '\0'; - } - else { - netinfo->host_name[0] = '\0'; - } - - size = sizeof(netinfo->domain_name)-1; - if (getdomainname(netinfo->domain_name, size) == 0) { - netinfo->domain_name[size] = '\0'; - } - else { - netinfo->domain_name[0] = '\0'; - } - - sigar_get_default_gateway(sigar, netinfo); - - return SIGAR_OK; -} - -#include - -#define OffsetOf(structure, field) \ - (size_t)(&((structure *)NULL)->field) - -#define RlimitOffsets(field) \ - OffsetOf(sigar_resource_limit_t, field##_cur), \ - OffsetOf(sigar_resource_limit_t, field##_max) - -#define RlimitSet(structure, ptr, val) \ - *(sigar_uint64_t *)((char *)structure + (int)(long)ptr) = val - -typedef struct { - int resource; - int factor; - size_t cur; - size_t max; -} rlimit_field_t; - -#ifndef RLIMIT_RSS -#define RLIMIT_RSS (RLIM_NLIMITS+1) -#endif - -#ifndef RLIMIT_NPROC -#define RLIMIT_NPROC (RLIM_NLIMITS+2) -#endif - -#define RLIMIT_PSIZE (RLIM_NLIMITS+3) - -#ifndef RLIMIT_AS -# if defined(RLIMIT_VMEM) -# define RLIMIT_AS RLIMIT_VMEM -# elif defined(RLIMIT_RSS) -# define RLIMIT_AS RLIMIT_RSS -# endif -#endif - -static rlimit_field_t sigar_rlimits[] = { - { RLIMIT_CPU, 1, RlimitOffsets(cpu) }, - { RLIMIT_FSIZE, 1024, RlimitOffsets(file_size) }, - { RLIMIT_DATA, 1024, RlimitOffsets(data) }, - { RLIMIT_STACK, 1024, RlimitOffsets(stack) }, - { RLIMIT_PSIZE, 512, RlimitOffsets(pipe_size) }, - { RLIMIT_CORE, 1024, RlimitOffsets(core) }, - { RLIMIT_RSS, 1024, RlimitOffsets(memory) }, - { RLIMIT_NPROC, 1, RlimitOffsets(processes) }, - { RLIMIT_NOFILE, 1, RlimitOffsets(open_files) }, - { RLIMIT_AS, 1024, RlimitOffsets(virtual_memory) }, - { -1 } -}; - -#define RlimitScale(val) \ - if (val != RLIM_INFINITY) val /= r->factor - -#define RlimitHS(val) \ - rl.rlim_cur = rl.rlim_max = (val) - -int sigar_resource_limit_get(sigar_t *sigar, - sigar_resource_limit_t *rlimit) -{ - int i; - - for (i=0; sigar_rlimits[i].resource != -1; i++) { - struct rlimit rl; - rlimit_field_t *r = &sigar_rlimits[i]; - - if (r->resource > RLIM_NLIMITS) { - switch (r->resource) { - case RLIMIT_NPROC: - RlimitHS(sysconf(_SC_CHILD_MAX)); - break; - case RLIMIT_PSIZE: - RlimitHS(PIPE_BUF/512); - break; - default: - RlimitHS(RLIM_INFINITY); - break; - } - } - else if (getrlimit(r->resource, &rl) != 0) { - RlimitHS(RLIM_INFINITY); - } - else { - RlimitScale(rl.rlim_cur); - RlimitScale(rl.rlim_max); - } - - RlimitSet(rlimit, r->cur, rl.rlim_cur); - RlimitSet(rlimit, r->max, rl.rlim_max); - } - - return SIGAR_OK; -} -#endif - -#if !defined(WIN32) && !defined(NETWARE) && !defined(DARWIN) && \ - !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) - -/* XXX: prolly will be moving these stuffs into os_net.c */ -#include -#include - -#ifndef SIOCGIFCONF -#include -#endif - -#if defined(_AIX) || defined(__osf__) /* good buddies */ - -#include - -static void hwaddr_aix_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig) -{ - char *ent, *end; - struct ifreq *ifr; - - /* XXX: assumes sigar_net_interface_list_get has been called */ - end = sigar->ifconf_buf + sigar->ifconf_len; - - for (ent = sigar->ifconf_buf; - ent < end; - ent += sizeof(*ifr)) - { - ifr = (struct ifreq *)ent; - - if (ifr->ifr_addr.sa_family != AF_LINK) { - continue; - } - - if (strEQ(ifr->ifr_name, ifconfig->name)) { - struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr; - - sigar_net_address_mac_set(ifconfig->hwaddr, - LLADDR(sdl), - sdl->sdl_alen); - return; - } - } - - sigar_hwaddr_set_null(ifconfig); -} - -#elif !defined(SIOCGIFHWADDR) - -#include - -static void hwaddr_arp_lookup(sigar_net_interface_config_t *ifconfig, int sock) -{ - struct arpreq areq; - struct sockaddr_in *sa; - - memset(&areq, 0, sizeof(areq)); - sa = (struct sockaddr_in *)&areq.arp_pa; - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = ifconfig->address.addr.in; - - if (ioctl(sock, SIOCGARP, &areq) < 0) { - /* ho-hum */ - sigar_hwaddr_set_null(ifconfig); - } - else { - sigar_net_address_mac_set(ifconfig->hwaddr, - areq.arp_ha.sa_data, - SIGAR_IFHWADDRLEN); - } -} - -#endif - -#ifdef __linux__ - -#include - -#ifndef ARPHRD_CISCO /* not in 2.2 kernel headers */ -#define ARPHRD_CISCO 513 /* Cisco HDLC. */ -#endif - -static void get_interface_type(sigar_net_interface_config_t *ifconfig, - int family) -{ - char *type; - - switch (family) { - case ARPHRD_SLIP: - type = SIGAR_NIC_SLIP; - break; - case ARPHRD_CSLIP: - type = SIGAR_NIC_CSLIP; - break; - case ARPHRD_SLIP6: - type = SIGAR_NIC_SLIP6; - break; - case ARPHRD_CSLIP6: - type = SIGAR_NIC_CSLIP6; - break; - case ARPHRD_ADAPT: - type = SIGAR_NIC_ADAPTIVE; - break; - case ARPHRD_ETHER: - type = SIGAR_NIC_ETHERNET; - break; - case ARPHRD_ASH: - type = SIGAR_NIC_ASH; - break; - case ARPHRD_FDDI: - type = SIGAR_NIC_FDDI; - break; - case ARPHRD_HIPPI: - type = SIGAR_NIC_HIPPI; - break; - case ARPHRD_AX25: - type = SIGAR_NIC_AX25; - break; - case ARPHRD_ROSE: - type = SIGAR_NIC_ROSE; - break; - case ARPHRD_NETROM: - type = SIGAR_NIC_NETROM; - break; - case ARPHRD_X25: - type = SIGAR_NIC_X25; - break; - case ARPHRD_TUNNEL: - type = SIGAR_NIC_TUNNEL; - break; - case ARPHRD_PPP: - type = SIGAR_NIC_PPP; - break; - case ARPHRD_CISCO: - type = SIGAR_NIC_HDLC; - break; - case ARPHRD_LAPB: - type = SIGAR_NIC_LAPB; - break; - case ARPHRD_ARCNET: - type = SIGAR_NIC_ARCNET; - break; - case ARPHRD_DLCI: - type = SIGAR_NIC_DLCI; - break; - case ARPHRD_FRAD: - type = SIGAR_NIC_FRAD; - break; - case ARPHRD_SIT: - type = SIGAR_NIC_SIT; - break; - case ARPHRD_IRDA: - type = SIGAR_NIC_IRDA; - break; - case ARPHRD_ECONET: - type = SIGAR_NIC_EC; - break; - default: - type = SIGAR_NIC_UNSPEC; - break; - } - - SIGAR_SSTRCPY(ifconfig->type, type); -} - -#endif - -int sigar_net_interface_config_get(sigar_t *sigar, const char *name, - sigar_net_interface_config_t *ifconfig) -{ - int sock; - struct ifreq ifr; - - if (!name) { - return sigar_net_interface_config_primary_get(sigar, ifconfig); - } - - SIGAR_ZERO(ifconfig); - - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - return errno; - } - - SIGAR_SSTRCPY(ifconfig->name, name); - SIGAR_SSTRCPY(ifr.ifr_name, name); - -#define ifr_s_addr(ifr) \ - ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr - - if (!ioctl(sock, SIOCGIFADDR, &ifr)) { - sigar_net_address_set(ifconfig->address, - ifr_s_addr(ifr)); - } - - if (!ioctl(sock, SIOCGIFNETMASK, &ifr)) { - sigar_net_address_set(ifconfig->netmask, - ifr_s_addr(ifr)); - } - - if (!ioctl(sock, SIOCGIFFLAGS, &ifr)) { - sigar_uint64_t flags = ifr.ifr_flags; -#ifdef __linux__ -# ifndef IFF_DYNAMIC -# define IFF_DYNAMIC 0x8000 /* not in 2.2 kernel */ -# endif /* IFF_DYNAMIC */ - int is_mcast = flags & IFF_MULTICAST; - int is_slave = flags & IFF_SLAVE; - int is_master = flags & IFF_MASTER; - int is_dynamic = flags & IFF_DYNAMIC; - /* - * XXX: should just define SIGAR_IFF_* - * and test IFF_* bits on given platform. - * this is the only diff between solaris/hpux/linux - * for the flags we care about. - * - */ - flags &= ~(IFF_MULTICAST|IFF_SLAVE|IFF_MASTER); - if (is_mcast) { - flags |= SIGAR_IFF_MULTICAST; - } - if (is_slave) { - flags |= SIGAR_IFF_SLAVE; - } - if (is_master) { - flags |= SIGAR_IFF_MASTER; - } - if (is_dynamic) { - flags |= SIGAR_IFF_DYNAMIC; - } -#endif - ifconfig->flags = flags; - } - else { - /* should always be able to get flags for existing device */ - /* other ioctls may fail if device is not enabled: ok */ - close(sock); - return errno; - } - - if (ifconfig->flags & IFF_LOOPBACK) { - sigar_net_address_set(ifconfig->destination, - ifconfig->address.addr.in); - sigar_net_address_set(ifconfig->broadcast, 0); - sigar_hwaddr_set_null(ifconfig); - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_LOOPBACK); - } - else { - if (!ioctl(sock, SIOCGIFDSTADDR, &ifr)) { - sigar_net_address_set(ifconfig->destination, - ifr_s_addr(ifr)); - } - - if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) { - sigar_net_address_set(ifconfig->broadcast, - ifr_s_addr(ifr)); - } - -#if defined(SIOCGIFHWADDR) - if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) { - get_interface_type(ifconfig, - ifr.ifr_hwaddr.sa_family); - sigar_net_address_mac_set(ifconfig->hwaddr, - ifr.ifr_hwaddr.sa_data, - IFHWADDRLEN); - } -#elif defined(_AIX) || defined(__osf__) - hwaddr_aix_lookup(sigar, ifconfig); - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_ETHERNET); -#else - hwaddr_arp_lookup(ifconfig, sock); - SIGAR_SSTRCPY(ifconfig->type, - SIGAR_NIC_ETHERNET); -#endif - } - -#if defined(SIOCGLIFMTU) && !defined(__hpux) - { - struct lifreq lifr; - SIGAR_SSTRCPY(lifr.lifr_name, name); - if(!ioctl(sock, SIOCGLIFMTU, &lifr)) { - ifconfig->mtu = lifr.lifr_mtu; - } - } -#elif defined(SIOCGIFMTU) - if (!ioctl(sock, SIOCGIFMTU, &ifr)) { -# if defined(__hpux) - ifconfig->mtu = ifr.ifr_metric; -# else - ifconfig->mtu = ifr.ifr_mtu; -#endif - } -#else - ifconfig->mtu = 0; /*XXX*/ -#endif - - if (!ioctl(sock, SIOCGIFMETRIC, &ifr)) { - ifconfig->metric = ifr.ifr_metric ? ifr.ifr_metric : 1; - } - -#if defined(SIOCGIFTXQLEN) - if (!ioctl(sock, SIOCGIFTXQLEN, &ifr)) { - ifconfig->tx_queue_len = ifr.ifr_qlen; - } - else { - ifconfig->tx_queue_len = -1; /* net-tools behaviour */ - } -#else - ifconfig->tx_queue_len = -1; -#endif - - close(sock); - - /* XXX can we get a better description like win32? */ - SIGAR_SSTRCPY(ifconfig->description, - ifconfig->name); - - sigar_net_interface_ipv6_config_init(ifconfig); - sigar_net_interface_ipv6_config_get(sigar, name, ifconfig); - - return SIGAR_OK; -} - -#ifdef _AIX -# define MY_SIOCGIFCONF CSIOCGIFCONF -#else -# define MY_SIOCGIFCONF SIOCGIFCONF -#endif - -#ifdef __osf__ -static int sigar_netif_configured(sigar_t *sigar, char *name) -{ - int status; - sigar_net_interface_config_t ifconfig; - - status = sigar_net_interface_config_get(sigar, name, &ifconfig); - - return status == SIGAR_OK; -} -#endif - -#ifdef __linux__ -static SIGAR_INLINE int has_interface(sigar_net_interface_list_t *iflist, - char *name) -{ - register int i; - register int num = iflist->number; - register char **data = iflist->data; - for (i=0; idata[iflist->number++] = - sigar_strdup(dev); - } - - fclose(fp); - - return SIGAR_OK; -} -#endif - -int sigar_net_interface_list_get(sigar_t *sigar, - sigar_net_interface_list_t *iflist) -{ - int n, lastlen=0; - struct ifreq *ifr; - struct ifconf ifc; - int sock = socket(AF_INET, SOCK_DGRAM, 0); - - if (sock < 0) { - return errno; - } - - for (;;) { - if (!sigar->ifconf_buf || lastlen) { - sigar->ifconf_len += sizeof(struct ifreq) * SIGAR_NET_IFLIST_MAX; - sigar->ifconf_buf = realloc(sigar->ifconf_buf, sigar->ifconf_len); - } - - ifc.ifc_len = sigar->ifconf_len; - ifc.ifc_buf = sigar->ifconf_buf; - - if (ioctl(sock, MY_SIOCGIFCONF, &ifc) < 0) { - /* EINVAL should mean num_interfaces > ifc.ifc_len */ - if ((errno != EINVAL) || - (lastlen == ifc.ifc_len)) - { - free(ifc.ifc_buf); - return errno; - } - } - - if (ifc.ifc_len < sigar->ifconf_len) { - break; /* got em all */ - } - - if (ifc.ifc_len != lastlen) { - /* might be more */ - lastlen = ifc.ifc_len; - continue; - } - - break; - } - - close(sock); - - iflist->number = 0; - iflist->size = ifc.ifc_len; - iflist->data = malloc(sizeof(*(iflist->data)) * - iflist->size); - - ifr = ifc.ifc_req; - for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq), ifr++) { -#if defined(_AIX) || defined(__osf__) /* pass the bourbon */ - if (ifr->ifr_addr.sa_family != AF_LINK) { - /* XXX: dunno if this is right. - * otherwise end up with two 'en0' and three 'lo0' - * with the same ip address. - */ - continue; - } -# ifdef __osf__ - /* weed out "sl0", "tun0" and the like */ - /* XXX must be a better way to check this */ - if (!sigar_netif_configured(sigar, ifr->ifr_name)) { - continue; - } -# endif -#endif - iflist->data[iflist->number++] = - sigar_strdup(ifr->ifr_name); - } - -#ifdef __linux__ - proc_net_interface_list_get(sigar, iflist); -#endif - - return SIGAR_OK; -} - -#endif /* WIN32 */ - -SIGAR_DECLARE(int) -sigar_net_interface_config_primary_get(sigar_t *sigar, - sigar_net_interface_config_t *ifconfig) -{ - int i, status, found=0; - sigar_net_interface_list_t iflist; - sigar_net_interface_config_t possible_config; - - possible_config.flags = 0; - - if ((status = sigar_net_interface_list_get(sigar, &iflist)) != SIGAR_OK) { - return status; - } - - for (i=0; iflags & SIGAR_IFF_LOOPBACK) || - !ifconfig->hwaddr.addr.in) /* no mac address */ - { - continue; - } - - if (!possible_config.flags) { - /* save for later for use if we're not connected to the net - * or all interfaces are aliases (e.g. solaris zone) - */ - memcpy(&possible_config, ifconfig, sizeof(*ifconfig)); - } - if (!ifconfig->address.addr.in) { - continue; /* no ip address */ - } - if (strchr(iflist.data[i], ':')) { - continue; /* alias */ - } - - found = 1; - break; - } - - sigar_net_interface_list_destroy(sigar, &iflist); - - if (found) { - return SIGAR_OK; - } - else if (possible_config.flags) { - memcpy(ifconfig, &possible_config, sizeof(*ifconfig)); - return SIGAR_OK; - } - else { - return SIGAR_ENXIO; - } -} - -static int fqdn_ip_get(sigar_t *sigar, char *name) -{ - sigar_net_interface_config_t ifconfig; - int status; - - status = sigar_net_interface_config_primary_get(sigar, &ifconfig); - - if (status != SIGAR_OK) { - return status; - } - if (!ifconfig.address.addr.in) { - return SIGAR_ENXIO; - } - - sigar_net_address_to_string(sigar, &ifconfig.address, name); - - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] using ip address '%s' for fqdn", - name); - - return SIGAR_OK; -} - -struct hostent *sigar_gethostbyname(const char *name, - sigar_hostent_t *data) -{ - struct hostent *hp = NULL; - -#if defined(__linux__) - gethostbyname_r(name, &data->hs, - data->buffer, sizeof(data->buffer), - &hp, &data->error); -#elif defined(__sun) - hp = gethostbyname_r(name, &data->hs, - data->buffer, sizeof(data->buffer), - &data->error); -#elif defined(SIGAR_HAS_HOSTENT_DATA) - if (gethostbyname_r(name, &data->hs, &data->hd) == 0) { - hp = &data->hs; - } - else { - data->error = h_errno; - } -#else - hp = gethostbyname(name); -#endif - - return hp; -} - -static struct hostent *sigar_gethostbyaddr(const char *addr, - int len, int type, - sigar_hostent_t *data) -{ - struct hostent *hp = NULL; - -#if defined(__linux__) - gethostbyaddr_r(addr, len, type, - &data->hs, - data->buffer, sizeof(data->buffer), - &hp, &data->error); -#elif defined(__sun) - hp = gethostbyaddr_r(addr, len, type, - &data->hs, - data->buffer, sizeof(data->buffer), - &data->error); -#elif defined(SIGAR_HAS_HOSTENT_DATA) - if (gethostbyaddr_r((char *)addr, len, type, - &data->hs, &data->hd) == 0) - { - hp = &data->hs; - } - else { - data->error = h_errno; - } -#else - if (!(hp = gethostbyaddr(addr, len, type))) { - data->error = h_errno; - } -#endif - - return hp; -} -#define IS_FQDN(name) \ - (name && strchr(name, '.')) - -#define IS_FQDN_MATCH(lookup, name) \ - (IS_FQDN(lookup) && strnEQ(lookup, name, strlen(name))) - -#define FQDN_SET(fqdn) \ - SIGAR_STRNCPY(name, fqdn, namelen) - -SIGAR_DECLARE(int) sigar_fqdn_get(sigar_t *sigar, char *name, int namelen) -{ - register int is_debug = SIGAR_LOG_IS_DEBUG(sigar); - sigar_hostent_t data; - struct hostent *p; - char domain[SIGAR_FQDN_LEN + 1]; -#ifdef WIN32 - int status = sigar_wsa_init(sigar); - - if (status != SIGAR_OK) { - return status; - } -#endif - - if (gethostname(name, namelen - 1) != 0) { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "[fqdn] gethostname failed: %s", - sigar_strerror(sigar, errno)); - return errno; - } - else { - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] gethostname()=='%s'", - name); - } - } - - if (!(p = sigar_gethostbyname(name, &data))) { - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] gethostbyname(%s) failed: %s", - name, sigar_strerror(sigar, errno)); - } - - if (!IS_FQDN(name)) { - fqdn_ip_get(sigar, name); - } - - return SIGAR_OK; - } - - if (IS_FQDN_MATCH(p->h_name, name)) { - FQDN_SET(p->h_name); - - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] resolved using gethostbyname.h_name"); - - return SIGAR_OK; - } - else { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] unresolved using gethostbyname.h_name"); - } - - if (p->h_aliases) { - int i; - - for (i=0; p->h_aliases[i]; i++) { - if (IS_FQDN_MATCH(p->h_aliases[i], name)) { - FQDN_SET(p->h_aliases[i]); - - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] resolved using gethostbyname.h_aliases"); - - return SIGAR_OK; - } - else if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] gethostbyname(%s).alias[%d]=='%s'", - name, i, p->h_aliases[i]); - } - } - } - - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] unresolved using gethostbyname.h_aliases"); - - if (p->h_addr_list) { - int i,j; - - for (i=0; p->h_addr_list[i]; i++) { - char addr[SIGAR_INET6_ADDRSTRLEN]; - struct in_addr *in = - (struct in_addr *)p->h_addr_list[i]; - - struct hostent *q = - sigar_gethostbyaddr(p->h_addr_list[i], - p->h_length, - p->h_addrtype, - &data); - - if (is_debug) { - sigar_inet_ntoa(sigar, in->s_addr, addr); - } - - if (!q) { - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] gethostbyaddr(%s) failed: %s", - addr, - sigar_strerror(sigar, errno)); - } - continue; - } - - if (IS_FQDN_MATCH(q->h_name, name)) { - FQDN_SET(q->h_name); - - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] resolved using gethostbyaddr.h_name"); - - return SIGAR_OK; - } - else { - if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] gethostbyaddr(%s)=='%s'", - addr, q->h_name); - } - - for (j=0; q->h_aliases[j]; j++) { - if (IS_FQDN_MATCH(q->h_aliases[j], name)) { - FQDN_SET(q->h_aliases[j]); - - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] resolved using " - "gethostbyaddr.h_aliases"); - - return SIGAR_OK; - } - else if (is_debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[fqdn] gethostbyaddr(%s).alias[%d]=='%s'", - addr, j, q->h_aliases[j]); - } - } - } - } - } - - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] unresolved using gethostbyname.h_addr_list"); - -#if !defined(WIN32) && !defined(NETWARE) - if (!IS_FQDN(name) && /* e.g. aix gethostname is already fqdn */ - (getdomainname(domain, sizeof(domain) - 1) == 0) && - (domain[0] != '\0') && - (domain[0] != '(')) /* linux default is "(none)" */ - { - /* sprintf(name, "%s.%s", name, domain); */ - char *ptr = name; - int len = strlen(name); - ptr += len; - *ptr++ = '.'; - namelen -= (len+1); - SIGAR_STRNCPY(ptr, domain, namelen); - - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] resolved using getdomainname"); - } - else { - sigar_log(sigar, SIGAR_LOG_DEBUG, - "[fqdn] getdomainname failed"); - } -#endif - - if (!IS_FQDN(name)) { - fqdn_ip_get(sigar, name); - } - - return SIGAR_OK; -} - -#ifndef MAX_STRING_LEN -#define MAX_STRING_LEN 8192 -#endif - -#ifdef WIN32 -/* The windows version of getPasswordNative was lifted from apr */ -SIGAR_DECLARE(char *) sigar_password_get(const char *prompt) -{ - static char password[MAX_STRING_LEN]; - int n = 0; - int ch; - - fputs(prompt, stderr); - fflush(stderr); - - while ((ch = _getch()) != '\r') { - if (ch == EOF) /* EOF */ { - return NULL; - } - else if (ch == 0 || ch == 0xE0) { - /* FN Keys (0 or E0) are a sentinal for a FN code */ - ch = (ch << 4) | _getch(); - /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ - if ((ch == 0xE53 || ch == 0xE4B || ch == 0x053 || ch == 0x04b) && n) { - password[--n] = '\0'; - fputs("\b \b", stderr); - fflush(stderr); - } - else { - fputc('\a', stderr); - fflush(stderr); - } - } - else if ((ch == '\b' || ch == 127) && n) /* BS/DEL */ { - password[--n] = '\0'; - fputs("\b \b", stderr); - fflush(stderr); - } - else if (ch == 3) /* CTRL+C */ { - /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ - fputs("^C\n", stderr); - fflush(stderr); - exit(-1); - } - else if (ch == 26) /* CTRL+Z */ { - fputs("^Z\n", stderr); - fflush(stderr); - return NULL; - } - else if (ch == 27) /* ESC */ { - fputc('\n', stderr); - fputs(prompt, stderr); - fflush(stderr); - n = 0; - } - else if ((n < sizeof(password) - 1) && !iscntrl(ch)) { - password[n++] = ch; - fputc(' ', stderr); - fflush(stderr); - } - else { - fputc('\a', stderr); - fflush(stderr); - } - } - - fputc('\n', stderr); - fflush(stderr); - password[n] = '\0'; - - return password; -} - -#else - -/* linux/hpux/solaris getpass() prototype lives here */ -#include - -#include - -/* from apr_getpass.c */ - -#if defined(SIGAR_HPUX) -# define getpass termios_getpass -#elif defined(SIGAR_SOLARIS) -# define getpass getpassphrase -#endif - -#ifdef SIGAR_HPUX -static char *termios_getpass(const char *prompt) -{ - struct termios attr; - static char password[MAX_STRING_LEN]; - unsigned int n=0; - - fputs(prompt, stderr); - fflush(stderr); - - if (tcgetattr(STDIN_FILENO, &attr) != 0) { - return NULL; - } - - attr.c_lflag &= ~(ECHO); - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) != 0) { - return NULL; - } - - while ((password[n] = getchar()) != '\n') { - if (n < (sizeof(password) - 1) && - (password[n] >= ' ') && - (password[n] <= '~')) - { - n++; - } - else { - fprintf(stderr, "\n"); - fputs(prompt, stderr); - fflush(stderr); - n = 0; - } - } - - password[n] = '\0'; - printf("\n"); - - if (n > (MAX_STRING_LEN - 1)) { - password[MAX_STRING_LEN - 1] = '\0'; - } - - attr.c_lflag |= ECHO; - tcsetattr(STDIN_FILENO, TCSANOW, &attr); - - return (char *)&password; -} -#endif - -SIGAR_DECLARE(char *) sigar_password_get(const char *prompt) -{ - char *buf = NULL; - - /* the linux version of getpass prints the prompt to the tty; ok. - * the solaris version prints the prompt to stderr; not ok. - * so print the prompt to /dev/tty ourselves if possible (always should be) - */ - - FILE *tty = NULL; - - if ((tty = fopen("/dev/tty", "w"))) { - fprintf(tty, "%s", prompt); - fflush(tty); - - buf = getpass(tty ? "" : prompt); - fclose(tty); - } - - return buf; -} - -#endif /* WIN32 */ diff --git a/vendor/sigar/src/sigar_cache.c b/vendor/sigar/src/sigar_cache.c deleted file mode 100644 index 8d640167a..000000000 --- a/vendor/sigar/src/sigar_cache.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2004-2006 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include -/* - * hash table to cache values where key is a unique number - * such as: - * pid -> some process data - * uid -> user name - * gid -> group name - */ - -#define ENTRIES_SIZE(n) \ - (sizeof(sigar_cache_entry_t *) * (n)) - -/* wrap free() for use w/ dmalloc */ -static void free_value(void *ptr) -{ - free(ptr); -} - -sigar_cache_t *sigar_cache_new(int size) -{ - sigar_cache_t *table = malloc(sizeof(*table)); - table->count = 0; - table->size = size; - table->entries = malloc(ENTRIES_SIZE(size)); - memset(table->entries, '\0', ENTRIES_SIZE(size)); - table->free_value = free_value; - return table; -} - -#ifdef DEBUG_CACHE -/* see how well entries are distributed */ -static void sigar_cache_dump(sigar_cache_t *table) -{ - int i; - sigar_cache_entry_t **entries = table->entries; - - for (i=0; isize; i++) { - sigar_cache_entry_t *entry = *entries++; - - printf("|"); - while (entry) { - printf("%lld", entry->id); - if (entry->next) { - printf(","); - } - entry = entry->next; - } - } - printf("\n"); - fflush(stdout); -} -#endif - -static void sigar_cache_rehash(sigar_cache_t *table) -{ - int i; - unsigned int new_size = table->size * 2 + 1; - sigar_cache_entry_t **entries = table->entries; - sigar_cache_entry_t **new_entries = - malloc(ENTRIES_SIZE(new_size)); - - memset(new_entries, '\0', ENTRIES_SIZE(new_size)); - - for (i=0; isize; i++) { - sigar_cache_entry_t *entry = *entries++; - - while (entry) { - sigar_cache_entry_t *next = entry->next; - sigar_uint64_t hash = entry->id % new_size; - - entry->next = new_entries[hash]; - new_entries[hash] = entry; - entry = next; - } - } - - free(table->entries); - table->entries = new_entries; - table->size = new_size; -} - -#define SIGAR_CACHE_IX(t, k) \ - t->entries + (k % t->size) - -sigar_cache_entry_t *sigar_cache_find(sigar_cache_t *table, - sigar_uint64_t key) -{ - sigar_cache_entry_t *entry, **ptr; - - for (ptr = SIGAR_CACHE_IX(table, key), entry = *ptr; - entry; - ptr = &entry->next, entry = *ptr) - { - if (entry->id == key) { - return entry; - } - } - - return NULL; -} - -/* create entry if it does not exist */ -sigar_cache_entry_t *sigar_cache_get(sigar_cache_t *table, - sigar_uint64_t key) -{ - sigar_cache_entry_t *entry, **ptr; - - for (ptr = SIGAR_CACHE_IX(table, key), entry = *ptr; - entry; - ptr = &entry->next, entry = *ptr) - { - if (entry->id == key) { - return entry; - } - } - - if (table->count++ > table->size) { - sigar_cache_rehash(table); - - for (ptr = SIGAR_CACHE_IX(table, key), entry = *ptr; - entry; - ptr = &entry->next, entry = *ptr) - { - } - } - - *ptr = entry = malloc(sizeof(*entry)); - entry->id = key; - entry->value = NULL; - entry->next = NULL; - - return entry; -} - -void sigar_cache_destroy(sigar_cache_t *table) -{ - int i; - sigar_cache_entry_t **entries = table->entries; - -#ifdef DEBUG_CACHE - sigar_cache_dump(table); -#endif - - for (i=0; isize; i++) { - sigar_cache_entry_t *entry, *ptr; - entry = *entries++; - - while (entry) { - if (entry->value) { - table->free_value(entry->value); - } - ptr = entry->next; - free(entry); - entry = ptr; - } - } - - free(table->entries); - free(table); -} diff --git a/vendor/sigar/src/sigar_fileinfo.c b/vendor/sigar/src/sigar_fileinfo.c deleted file mode 100644 index adde8c079..000000000 --- a/vendor/sigar/src/sigar_fileinfo.c +++ /dev/null @@ -1,815 +0,0 @@ -/* - * Copyright (c) 2004-2005, 2007-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2000-2003 The Apache Software Foundation. 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. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. - * - * 4. The names "Apache" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR - * ITS 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - */ - -#ifndef WIN32 -# ifdef _AIX -# define _LARGE_FILES -# else -# define _FILE_OFFSET_BITS 64 -# define _LARGEFILE64_SOURCE -# endif -#endif - -#include "sigar.h" - -#ifndef WIN32 -#if defined(__FreeBSD__) || defined(__OpenBSD__) -# include -# include -#else -# include -# define HAVE_STATVFS -#endif -#include - -#define SIGAR_FS_BLOCKS_TO_BYTES(val, bsize) ((val * bsize) >> 1) - -int sigar_statvfs(sigar_t *sigar, - const char *dirname, - sigar_file_system_usage_t *fsusage) -{ - sigar_uint64_t val, bsize; -#ifdef HAVE_STATVFS - struct statvfs buf; - int status = -# if defined(__sun) && !defined(_LP64) - /* http://bugs.opensolaris.org/view_bug.do?bug_id=4462986 */ - statvfs(dirname, (void *)&buf); -# else - statvfs(dirname, &buf); -# endif -#else - struct statfs buf; - int status = statfs(dirname, &buf); -#endif - - if (status != 0) { - return errno; - } - -#ifdef HAVE_STATVFS - bsize = buf.f_frsize / 512; -#else - bsize = buf.f_bsize / 512; -#endif - val = buf.f_blocks; - fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize); - val = buf.f_bfree; - fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize); - val = buf.f_bavail; - fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize); - fsusage->used = fsusage->total - fsusage->free; - fsusage->files = buf.f_files; - fsusage->free_files = buf.f_ffree; - - return SIGAR_OK; -} -#endif - -/* - * whittled down version of apr/file_info/{unix,win32}/filestat.c - * to fillin sigar_fileattrs_t - */ -#include "sigar_fileinfo.h" -#include "sigar_log.h" - -#ifndef SIGAR_ZERO -#define SIGAR_ZERO(s) \ - memset(s, '\0', sizeof(*(s))) -#endif - -#ifdef WIN32 -#include -sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft); -#else -#include -#endif - -static const char* types[] = { - "none", - "regular", - "directory", - "character device", - "block device", - "pipe", - "symbolic link", - "socket", - "unknown" -}; - -SIGAR_DECLARE(const char *) -sigar_file_attrs_type_string_get(sigar_file_type_e type) -{ - if ((type < SIGAR_FILETYPE_NOFILE) || - (type > SIGAR_FILETYPE_UNKFILE)) - { - type = SIGAR_FILETYPE_UNKFILE; - } - - return types[type]; -} - -static const sigar_uint64_t perm_modes[] = { - SIGAR_UREAD, SIGAR_UWRITE, SIGAR_UEXECUTE, - SIGAR_GREAD, SIGAR_GWRITE, SIGAR_GEXECUTE, - SIGAR_WREAD, SIGAR_WWRITE, SIGAR_WEXECUTE -}; - -static const char perm_chars[] = "rwx"; - -SIGAR_DECLARE(char *) -sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions, - char *str) -{ - char *ptr = str; - int i=0, j=0; - - for (i=0; i<9; i+=3) { - for (j=0; j<3; j++) { - if (permissions & perm_modes[i+j]) { - *ptr = perm_chars[j]; - } - else { - *ptr = '-'; - } - ptr++; - } - } - - *ptr = '\0'; - return str; -} - -static const int perm_int[] = { - 400, 200, 100, - 40, 20, 10, - 4, 2, 1 -}; - -SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions) -{ - int i=0; - int perms = 0; - - /* no doubt there is some fancy bitshifting - * to convert, but this works fine. - */ - for (i=0; i<9; i++) { - if (permissions & perm_modes[i]) { - perms += perm_int[i]; - } - } - - return perms; -} - -#define IS_DOTDIR(dir) \ - ((dir[0] == '.') && (!dir[1] || ((dir[1] == '.') && !dir[2]))) - -#define DIR_STAT_WARN() \ - sigar_log_printf(sigar, SIGAR_LOG_WARN, \ - "dir_stat: cannot stat `%s': %s", \ - name, \ - sigar_strerror(sigar, status)) - -#if defined(NETWARE) - -int sigar_dir_stat_get(sigar_t *sigar, - const char *dir, - sigar_dir_stat_t *dirstats) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_file_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - return SIGAR_ENOTIMPL; -} - -int sigar_link_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - return SIGAR_ENOTIMPL; -} - -#elif defined(WIN32) - -#include -#include - -static void fillin_fileattrs(sigar_file_attrs_t *finfo, - WIN32_FILE_ATTRIBUTE_DATA *wininfo, - int linkinfo) -{ - DWORD *sizes = &wininfo->nFileSizeHigh; - - finfo->atime = sigar_FileTimeToTime(&wininfo->ftLastAccessTime) / 1000; - finfo->ctime = sigar_FileTimeToTime(&wininfo->ftCreationTime) / 1000; - finfo->mtime = sigar_FileTimeToTime(&wininfo->ftLastWriteTime) / 1000; - - finfo->size = - (sigar_uint64_t)sizes[1] | ((sigar_uint64_t)sizes[0] << 32); - - if (linkinfo && - (wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - finfo->type = SIGAR_FILETYPE_LNK; - } - else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - finfo->type = SIGAR_FILETYPE_DIR; - } - else { - finfo->type = SIGAR_FILETYPE_REG; - } -} - -static sigar_uint64_t convert_perms(ACCESS_MASK acc, sigar_uint64_t scope) -{ - sigar_uint64_t perms = 0; - if (acc & FILE_EXECUTE) { - perms |= SIGAR_WEXECUTE; - } - if (acc & FILE_WRITE_DATA) { - perms |= SIGAR_WWRITE; - } - if (acc & FILE_READ_DATA) { - perms |= SIGAR_WREAD; - } - - return (perms << scope); -} - -static int get_security_info(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - DWORD retval; - PSID user = NULL, group = NULL, world = NULL; - PACL dacl = NULL; - PSECURITY_DESCRIPTOR pdesc = NULL; - SECURITY_INFORMATION sinfo = - OWNER_SECURITY_INFORMATION | - GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION; - TRUSTEE ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID}; - ACCESS_MASK acc; - SID_IDENTIFIER_AUTHORITY auth = SECURITY_WORLD_SID_AUTHORITY; - - retval = GetNamedSecurityInfo((char *)file, - SE_FILE_OBJECT, - sinfo, - &user, - &group, - &dacl, - NULL, - &pdesc); - - if (retval != ERROR_SUCCESS) { - return retval; - } - - if (!AllocateAndInitializeSid(&auth, 1, SECURITY_WORLD_RID, - 0, 0, 0, 0, 0, 0, 0, &world)) - { - world = NULL; - } - - ident.TrusteeType = TRUSTEE_IS_USER; - ident.ptstrName = user; - if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) { - fileattrs->permissions |= convert_perms(acc, 8); - } - - ident.TrusteeType = TRUSTEE_IS_GROUP; - ident.ptstrName = group; - if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) { - fileattrs->permissions |= convert_perms(acc, 4); - } - - if (world) { - ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; - ident.ptstrName = world; - if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) { - fileattrs->permissions |= convert_perms(acc, 0); - } - } - - if (world) { - FreeSid(world); - } - - LocalFree(pdesc); - - return SIGAR_OK; -} - -static int fileattrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs, - int linkinfo) -{ - BY_HANDLE_FILE_INFORMATION info; - WIN32_FILE_ATTRIBUTE_DATA attrs; - HANDLE handle; - DWORD flags; - - SIGAR_ZERO(fileattrs); - - if (!GetFileAttributesExA(file, - GetFileExInfoStandard, - &attrs)) - { - return GetLastError(); - } - - fillin_fileattrs(fileattrs, &attrs, linkinfo); - - flags = fileattrs->type == SIGAR_FILETYPE_DIR ? - FILE_FLAG_BACKUP_SEMANTICS : - FILE_ATTRIBUTE_NORMAL; - - /** - * We need to set dwDesiredAccess to 0 to work in cases where GENERIC_READ can fail. - * - * see: http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx - */ - handle = CreateFile(file, - 0, - 0, - NULL, - OPEN_EXISTING, - flags, - NULL); - - if (handle != INVALID_HANDLE_VALUE) { - if (GetFileInformationByHandle(handle, &info)) { - fileattrs->inode = - info.nFileIndexLow | - (info.nFileIndexHigh << 32); - fileattrs->device = info.dwVolumeSerialNumber; - fileattrs->nlink = info.nNumberOfLinks; - } - CloseHandle(handle); - } - - get_security_info(sigar, file, fileattrs); - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - return fileattrs_get(sigar, file, fileattrs, 0); -} - -SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - return fileattrs_get(sigar, file, fileattrs, 1); -} - -static __inline int file_type(char *file) -{ - WIN32_FILE_ATTRIBUTE_DATA attrs; - - if (!GetFileAttributesExA(file, - GetFileExInfoStandard, - &attrs)) - { - return -1; - } - - if (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - return SIGAR_FILETYPE_LNK; - } - else if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - return SIGAR_FILETYPE_DIR; - } - else { - return SIGAR_FILETYPE_REG; - } -} - -static int dir_stat_get(sigar_t *sigar, - const char *dir, - sigar_dir_stat_t *dirstats, - int recurse) -{ - int status; - char name[SIGAR_PATH_MAX+1]; - int len = strlen(dir); - int max = sizeof(name)-len-1; - char *ptr = name; - WIN32_FIND_DATA data; - HANDLE handle; - DWORD error; - char delim; - - if (file_type((char *)dir) != SIGAR_FILETYPE_DIR) { - return ERROR_NO_MORE_FILES; - } - - strncpy(name, dir, sizeof(name)); - ptr += len; - if (strchr(dir, '/')) { - delim = '/'; - } - else { - delim = '\\'; - } - if (name[len] != delim) { - *ptr++ = delim; - len++; - max--; - } - - /* e.g. "C:\sigar\*" */ - name[len] = '*'; - name[len+1] = '\0'; - - handle = FindFirstFile(name, &data); - if (handle == INVALID_HANDLE_VALUE) { - return GetLastError(); - } - - do { - /* skip '.' and '..' */ - if (IS_DOTDIR(data.cFileName)) { - continue; - } - - dirstats->disk_usage += - (data.nFileSizeHigh * (MAXDWORD+1)) + - data.nFileSizeLow; - - /* e.g. "C:\sigar\lib" */ - strncpy(ptr, data.cFileName, max); - ptr[max] = '\0'; - - switch (file_type(name)) { - case -1: - break; - case SIGAR_FILETYPE_REG: - ++dirstats->files; - break; - case SIGAR_FILETYPE_DIR: - ++dirstats->subdirs; - if (recurse) { - status = - dir_stat_get(sigar, name, - dirstats, recurse); - if (status != SIGAR_OK) { - DIR_STAT_WARN(); - } - } - break; - case SIGAR_FILETYPE_LNK: - ++dirstats->symlinks; - break; - case SIGAR_FILETYPE_CHR: - ++dirstats->chrdevs; - break; - case SIGAR_FILETYPE_BLK: - ++dirstats->blkdevs; - break; - case SIGAR_FILETYPE_SOCK: - ++dirstats->sockets; - break; - default: - ++dirstats->total; - } - } while (FindNextFile(handle, &data)); - - error = GetLastError(); - - FindClose(handle); - - if (error != ERROR_NO_MORE_FILES) { - return error; - } - - dirstats->total = - dirstats->files + - dirstats->subdirs + - dirstats->symlinks + - dirstats->chrdevs + - dirstats->blkdevs + - dirstats->sockets; - - return SIGAR_OK; -} - -#else - -#include -#include -#include -#include - -static sigar_file_type_e filetype_from_mode(mode_t mode) -{ - sigar_file_type_e type; - - switch (mode & S_IFMT) { - case S_IFREG: - type = SIGAR_FILETYPE_REG; break; - case S_IFDIR: - type = SIGAR_FILETYPE_DIR; break; - case S_IFLNK: - type = SIGAR_FILETYPE_LNK; break; - case S_IFCHR: - type = SIGAR_FILETYPE_CHR; break; - case S_IFBLK: - type = SIGAR_FILETYPE_BLK; break; -#if defined(S_IFFIFO) - case S_IFFIFO: - type = SIGAR_FILETYPE_PIPE; break; -#endif -#if !defined(BEOS) && defined(S_IFSOCK) - case S_IFSOCK: - type = SIGAR_FILETYPE_SOCK; break; -#endif - - default: - /* Work around missing S_IFxxx values above - * for Linux et al. - */ -#if !defined(S_IFFIFO) && defined(S_ISFIFO) - if (S_ISFIFO(mode)) { - type = SIGAR_FILETYPE_PIPE; - } else -#endif -#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK) - if (S_ISSOCK(mode)) { - type = SIGAR_FILETYPE_SOCK; - } else -#endif - type = SIGAR_FILETYPE_UNKFILE; - } - return type; -} - -static sigar_uint64_t sigar_unix_mode2perms(mode_t mode) -{ - sigar_uint64_t perms = 0; - - if (mode & S_IRUSR) - perms |= SIGAR_UREAD; - if (mode & S_IWUSR) - perms |= SIGAR_UWRITE; - if (mode & S_IXUSR) - perms |= SIGAR_UEXECUTE; - - if (mode & S_IRGRP) - perms |= SIGAR_GREAD; - if (mode & S_IWGRP) - perms |= SIGAR_GWRITE; - if (mode & S_IXGRP) - perms |= SIGAR_GEXECUTE; - - if (mode & S_IROTH) - perms |= SIGAR_WREAD; - if (mode & S_IWOTH) - perms |= SIGAR_WWRITE; - if (mode & S_IXOTH) - perms |= SIGAR_WEXECUTE; - - return perms; -} - -static void copy_stat_info(sigar_file_attrs_t *fileattrs, - struct stat *info) -{ - fileattrs->permissions = sigar_unix_mode2perms(info->st_mode); - fileattrs->type = filetype_from_mode(info->st_mode); - fileattrs->uid = info->st_uid; - fileattrs->gid = info->st_gid; - fileattrs->size = info->st_size; - fileattrs->inode = info->st_ino; - fileattrs->device = info->st_dev; - fileattrs->nlink = info->st_nlink; - fileattrs->atime = info->st_atime; - fileattrs->mtime = info->st_mtime; - fileattrs->ctime = info->st_ctime; - fileattrs->atime *= 1000; - fileattrs->mtime *= 1000; - fileattrs->ctime *= 1000; -} - -int sigar_file_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - struct stat info; - - if (stat(file, &info) == 0) { - copy_stat_info(fileattrs, &info); - return SIGAR_OK; - } - else { - return errno; - } -} - -int sigar_link_attrs_get(sigar_t *sigar, - const char *file, - sigar_file_attrs_t *fileattrs) -{ - struct stat info; - - if (lstat(file, &info) == 0) { - copy_stat_info(fileattrs, &info); - return SIGAR_OK; - } - else { - return errno; - } -} - -static int dir_stat_get(sigar_t *sigar, - const char *dir, - sigar_dir_stat_t *dirstats, - int recurse) -{ - int status; - char name[SIGAR_PATH_MAX+1]; - int len = strlen(dir); - int max = sizeof(name)-len-1; - char *ptr = name; - DIR *dirp = opendir(dir); - struct dirent *ent; - struct stat info; -#ifdef HAVE_READDIR_R - struct dirent dbuf; -#endif - - if (!dirp) { - return errno; - } - - strncpy(name, dir, sizeof(name)); - ptr += len; - if (name[len] != '/') { - *ptr++ = '/'; - len++; - max--; - } - -#ifdef HAVE_READDIR_R - while (readdir_r(dirp, &dbuf, &ent) == 0) { - if (ent == NULL) { - break; - } -#else - while ((ent = readdir(dirp))) { -#endif - /* skip '.' and '..' */ - if (IS_DOTDIR(ent->d_name)) { - continue; - } - - strncpy(ptr, ent->d_name, max); - ptr[max] = '\0'; - - if (lstat(name, &info) != 0) { - continue; - } - - dirstats->disk_usage += info.st_size; - - switch (filetype_from_mode(info.st_mode)) { - case SIGAR_FILETYPE_REG: - ++dirstats->files; - break; - case SIGAR_FILETYPE_DIR: - ++dirstats->subdirs; - if (recurse) { - status = - dir_stat_get(sigar, name, - dirstats, recurse); - if (status != SIGAR_OK) { - DIR_STAT_WARN(); - } - } - break; - case SIGAR_FILETYPE_LNK: - ++dirstats->symlinks; - break; - case SIGAR_FILETYPE_CHR: - ++dirstats->chrdevs; - break; - case SIGAR_FILETYPE_BLK: - ++dirstats->blkdevs; - break; - case SIGAR_FILETYPE_SOCK: - ++dirstats->sockets; - break; - default: - ++dirstats->total; - } - } - - dirstats->total = - dirstats->files + - dirstats->subdirs + - dirstats->symlinks + - dirstats->chrdevs + - dirstats->blkdevs + - dirstats->sockets; - - closedir(dirp); - - return SIGAR_OK; -} - -#endif - -SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar, - const char *dir, - sigar_dir_stat_t *dirstats) -{ - SIGAR_ZERO(dirstats); - return dir_stat_get(sigar, dir, dirstats, 0); -} - -SIGAR_DECLARE(int) sigar_dir_usage_get(sigar_t *sigar, - const char *dir, - sigar_dir_usage_t *dirusage) -{ - SIGAR_ZERO(dirusage); - return dir_stat_get(sigar, dir, dirusage, 1); -} diff --git a/vendor/sigar/src/sigar_format.c b/vendor/sigar/src/sigar_format.c deleted file mode 100644 index 8062ddd14..000000000 --- a/vendor/sigar/src/sigar_format.c +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright (c) 2007-2008 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * Copyright (c) 2010 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Utility functions to provide string formatting of SIGAR data */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" -#include "sigar_format.h" - -#include -#include - -#ifndef WIN32 -#include -#include -#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_AIX) -#include -#endif -#include -#include - -/* sysconf(_SC_GET{PW,GR}_R_SIZE_MAX) */ -#define R_SIZE_MAX 2048 - -int sigar_user_name_get(sigar_t *sigar, int uid, char *buf, int buflen) -{ - struct passwd *pw = NULL; - /* XXX cache lookup */ - -# ifdef HAVE_GETPWUID_R - struct passwd pwbuf; - char buffer[R_SIZE_MAX]; - - if (getpwuid_r(uid, &pwbuf, buffer, sizeof(buffer), &pw) != 0) { - return errno; - } - if (!pw) { - return ENOENT; - } -# else - if ((pw = getpwuid(uid)) == NULL) { - return errno; - } -# endif - - strncpy(buf, pw->pw_name, buflen); - buf[buflen-1] = '\0'; - - return SIGAR_OK; -} - -int sigar_group_name_get(sigar_t *sigar, int gid, char *buf, int buflen) -{ - struct group *gr; - /* XXX cache lookup */ - -# ifdef HAVE_GETGRGID_R - struct group grbuf; - char buffer[R_SIZE_MAX]; - - if (getgrgid_r(gid, &grbuf, buffer, sizeof(buffer), &gr) != 0) { - return errno; - } -# else - if ((gr = getgrgid(gid)) == NULL) { - return errno; - } -# endif - - if (gr && gr->gr_name) { - strncpy(buf, gr->gr_name, buflen); - } - else { - /* seen on linux.. apache httpd.conf has: - * Group #-1 - * results in uid == -1 and gr == NULL. - * wtf getgrgid_r doesnt fail instead? - */ - sprintf(buf, "%d", gid); - } - buf[buflen-1] = '\0'; - - return SIGAR_OK; -} - -int sigar_user_id_get(sigar_t *sigar, const char *name, int *uid) -{ - /* XXX cache lookup */ - struct passwd *pw; - -# ifdef HAVE_GETPWNAM_R - struct passwd pwbuf; - char buf[R_SIZE_MAX]; - - if (getpwnam_r(name, &pwbuf, buf, sizeof(buf), &pw) != 0) { - return errno; - } -# else - if (!(pw = getpwnam(name))) { - return errno; - } -# endif - - *uid = (int)pw->pw_uid; - return SIGAR_OK; -} - -#endif /* WIN32 */ - -static char *sigar_error_string(int err) -{ - switch (err) { - case SIGAR_ENOTIMPL: - return "This function has not been implemented on this platform"; - default: - return "Error string not specified yet"; - } -} - -SIGAR_DECLARE(char *) sigar_strerror(sigar_t *sigar, int err) -{ - char *buf; - - if (err < 0) { - return sigar->errbuf; - } - - if (err > SIGAR_OS_START_ERROR) { - if ((buf = sigar_os_error_string(sigar, err)) != NULL) { - return buf; - } - return "Unknown OS Error"; /* should never happen */ - } - - if (err > SIGAR_START_ERROR) { - return sigar_error_string(err); - } - - return sigar_strerror_get(err, sigar->errbuf, sizeof(sigar->errbuf)); -} - -char *sigar_strerror_get(int err, char *errbuf, int buflen) -{ - char *buf = NULL; -#ifdef WIN32 - DWORD len; - - len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - err, - MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), /* force english */ - (LPTSTR)errbuf, - (DWORD)buflen, - NULL); -#else - -#if defined(HAVE_STRERROR_R) && defined(HAVE_STRERROR_R_GLIBC) - /* - * strerror_r man page says: - * "The GNU version may, but need not, use the user supplied buffer" - */ - buf = strerror_r(err, errbuf, buflen); -#elif defined(HAVE_STRERROR_R) - if (strerror_r(err, errbuf, buflen) < 0) { - buf = "Unknown Error"; - } -#else - /* strerror() is thread safe on solaris and hpux */ - buf = strerror(err); -#endif - - if (buf != NULL) { - SIGAR_STRNCPY(errbuf, buf, buflen); - } - -#endif - return errbuf; -} - -void sigar_strerror_set(sigar_t *sigar, char *msg) -{ - SIGAR_SSTRCPY(sigar->errbuf, msg); -} - -#ifdef WIN32 -#define vsnprintf _vsnprintf -#endif - -void sigar_strerror_printf(sigar_t *sigar, const char *format, ...) -{ - va_list args; - - va_start(args, format); - vsnprintf(sigar->errbuf, sizeof(sigar->errbuf), format, args); - va_end(args); -} - -/* copy apr_strfsize */ -SIGAR_DECLARE(char *) sigar_format_size(sigar_uint64_t size, char *buf) -{ - const char ord[] = "KMGTPE"; - const char *o = ord; - int remain; - - if (size == SIGAR_FIELD_NOTIMPL) { - buf[0] = '-'; - buf[1] = '\0'; - return buf; - } - - if (size < 973) { - sprintf(buf, "%3d ", (int) size); - return buf; - } - - do { - remain = (int)(size & 1023); - size >>= 10; - - if (size >= 973) { - ++o; - continue; - } - - if (size < 9 || (size == 9 && remain < 973)) { - if ((remain = ((remain * 5) + 256) / 512) >= 10) { - ++size; - remain = 0; - } - sprintf(buf, "%d.%d%c", (int) size, remain, *o); - return buf; - } - - if (remain >= 512) { - ++size; - } - - sprintf(buf, "%3d%c", (int) size, *o); - - return buf; - } while (1); -} - - -SIGAR_DECLARE(int) sigar_uptime_string(sigar_t *sigar, - sigar_uptime_t *uptime, - char *buffer, - int buflen) -{ - char *ptr = buffer; - int time = (int)uptime->uptime; - int minutes, hours, days, offset = 0; - - /* XXX: get rid of sprintf and/or check for overflow */ - days = time / (60*60*24); - - if (days) { - offset += sprintf(ptr + offset, "%d day%s, ", - days, (days > 1) ? "s" : ""); - } - - minutes = time / 60; - hours = minutes / 60; - hours = hours % 24; - minutes = minutes % 60; - - if (hours) { - offset += sprintf(ptr + offset, "%2d:%02d", - hours, minutes); - } - else { - offset += sprintf(ptr + offset, "%d min", minutes); - } - - return SIGAR_OK; -} - -/* threadsafe alternative to inet_ntoa (inet_ntop4 from apr) */ -int sigar_inet_ntoa(sigar_t *sigar, - sigar_uint32_t address, - char *addr_str) -{ - char *next=addr_str; - int n=0; - const unsigned char *src = - (const unsigned char *)&address; - - do { - unsigned char u = *src++; - if (u > 99) { - *next++ = '0' + u/100; - u %= 100; - *next++ = '0' + u/10; - u %= 10; - } - else if (u > 9) { - *next++ = '0' + u/10; - u %= 10; - } - *next++ = '0' + u; - *next++ = '.'; - n++; - } while (n < 4); - - *--next = 0; - - return SIGAR_OK; -} - -static int sigar_ether_ntoa(char *buff, unsigned char *ptr) -{ - sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", - (ptr[0] & 0xff), (ptr[1] & 0xff), (ptr[2] & 0xff), - (ptr[3] & 0xff), (ptr[4] & 0xff), (ptr[5] & 0xff)); - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_net_address_equals(sigar_net_address_t *addr1, - sigar_net_address_t *addr2) - -{ - if (addr1->family != addr2->family) { - return EINVAL; - } - - switch (addr1->family) { - case SIGAR_AF_INET: - return memcmp(&addr1->addr.in, &addr2->addr.in, sizeof(addr1->addr.in)); - case SIGAR_AF_INET6: - return memcmp(&addr1->addr.in6, &addr2->addr.in6, sizeof(addr1->addr.in6)); - case SIGAR_AF_LINK: - return memcmp(&addr1->addr.mac, &addr2->addr.mac, sizeof(addr1->addr.mac)); - default: - return EINVAL; - } -} - -#if defined(SIGAR_USING_MSC6) -#define sigar_inet_ntop(af, src, dst, size) NULL -#define sigar_inet_ntop_errno SIGAR_ENOTIMPL -#elif defined(WIN32) -static char *sigar_inet_ntop(int af, const void *src, char *dst, int cnt) -{ - struct sockaddr_in6 sa; /* note only using this for AF_INET6 */ - - memset(&sa, '\0', sizeof(sa)); - sa.sin6_family = af; - memcpy(&sa.sin6_addr, src, sizeof(sa.sin6_addr)); - - if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), - dst, cnt, NULL, 0, NI_NUMERICHOST)) - { - return NULL; - } - else { - return dst; - } -} -#define sigar_inet_ntop_errno GetLastError() -#else -#define sigar_inet_ntop inet_ntop -#define sigar_inet_ntop_errno errno -#endif - -SIGAR_DECLARE(int) sigar_net_address_to_string(sigar_t *sigar, - sigar_net_address_t *address, - char *addr_str) -{ - *addr_str = '\0'; - switch (address->family) { - case SIGAR_AF_INET6: - if (sigar_inet_ntop(AF_INET6, (const void *)&address->addr.in6, - addr_str, SIGAR_INET6_ADDRSTRLEN)) - { - return SIGAR_OK; - } - else { - return sigar_inet_ntop_errno; - } - case SIGAR_AF_INET: - return sigar_inet_ntoa(sigar, address->addr.in, addr_str); - case SIGAR_AF_UNSPEC: - return sigar_inet_ntoa(sigar, 0, addr_str); /*XXX*/ - case SIGAR_AF_LINK: - return sigar_ether_ntoa(addr_str, &address->addr.mac[0]); - default: - return EINVAL; - } -} - -SIGAR_DECLARE(const char *)sigar_net_scope_to_string(int type) -{ - switch (type) { - case SIGAR_IPV6_ADDR_ANY: - return "Global"; - case SIGAR_IPV6_ADDR_LOOPBACK: - return "Host"; - case SIGAR_IPV6_ADDR_LINKLOCAL: - return "Link"; - case SIGAR_IPV6_ADDR_SITELOCAL: - return "Site"; - case SIGAR_IPV6_ADDR_COMPATv4: - return "Compat"; - default: - return "Unknown"; - } -} - -SIGAR_DECLARE(sigar_uint32_t) sigar_net_address_hash(sigar_net_address_t *address) -{ - sigar_uint32_t hash = 0; - unsigned char *data; - int i=0, size, elts; - - switch (address->family) { - case SIGAR_AF_UNSPEC: - case SIGAR_AF_INET: - return address->addr.in; - case SIGAR_AF_INET6: - data = (unsigned char *)&address->addr.in6; - size = sizeof(address->addr.in6); - elts = 4; - break; - case SIGAR_AF_LINK: - data = (unsigned char *)&address->addr.mac; - size = sizeof(address->addr.mac); - elts = 2; - break; - default: - return -1; - } - - while (ivalue) { - entry->value = strdup(name); - } - } - - fclose(fp); - return SIGAR_OK; -} - -SIGAR_DECLARE(char *)sigar_net_services_name_get(sigar_t *sigar, - int protocol, unsigned long port) -{ - sigar_cache_entry_t *entry; - sigar_cache_t **names; - char *pname; - - switch (protocol) { - case SIGAR_NETCONN_TCP: - names = &sigar->net_services_tcp; - pname = "tcp"; - break; - case SIGAR_NETCONN_UDP: - names = &sigar->net_services_udp; - pname = "udp"; - break; - default: - return NULL; - } - - if (*names == NULL) { - *names = sigar_cache_new(1024); - net_services_parse(*names, pname); - } - - if ((entry = sigar_cache_find(*names, port))) { - return (char *)entry->value; - } - else { - return NULL; - } -} - -SIGAR_DECLARE(int) sigar_cpu_perc_calculate(sigar_cpu_t *prev, - sigar_cpu_t *curr, - sigar_cpu_perc_t *perc) -{ - double diff_user, diff_sys, diff_nice, diff_idle; - double diff_wait, diff_irq, diff_soft_irq, diff_stolen; - double diff_total; - - diff_user = curr->user - prev->user; - diff_sys = curr->sys - prev->sys; - diff_nice = curr->nice - prev->nice; - diff_idle = curr->idle - prev->idle; - diff_wait = curr->wait - prev->wait; - diff_irq = curr->irq - prev->irq; - diff_soft_irq = curr->soft_irq - prev->soft_irq; - diff_stolen = curr->stolen - prev->stolen; - - diff_user = diff_user < 0 ? 0 : diff_user; - diff_sys = diff_sys < 0 ? 0 : diff_sys; - diff_nice = diff_nice < 0 ? 0 : diff_nice; - diff_idle = diff_idle < 0 ? 0 : diff_idle; - diff_wait = diff_wait < 0 ? 0 : diff_wait; - diff_irq = diff_irq < 0 ? 0 : diff_irq; - diff_soft_irq = diff_soft_irq < 0 ? 0 : diff_soft_irq; - diff_stolen = diff_stolen < 0 ? 0 : diff_stolen; - - diff_total = - diff_user + diff_sys + diff_nice + diff_idle + - diff_wait + diff_irq + diff_soft_irq + - diff_stolen; - - perc->user = diff_user / diff_total; - perc->sys = diff_sys / diff_total; - perc->nice = diff_nice / diff_total; - perc->idle = diff_idle / diff_total; - perc->wait = diff_wait / diff_total; - perc->irq = diff_irq / diff_total; - perc->soft_irq = diff_soft_irq / diff_total; - perc->stolen = diff_stolen / diff_total; - - perc->combined = - perc->user + perc->sys + perc->nice + perc->wait; - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/sigar_getline.c b/vendor/sigar/src/sigar_getline.c deleted file mode 100644 index 0a8946bc8..000000000 --- a/vendor/sigar/src/sigar_getline.c +++ /dev/null @@ -1,1849 +0,0 @@ -/* - * Copyright (C) 1991, 1992 by Chris Thewalt (thewalt@ce.berkeley.edu) - * - * Permission to use, copy, modify, and distribute this software - * for any purpose and without fee is hereby granted, provided - * that the above copyright notices appear in all copies and that both the - * copyright notice and this permission notice appear in supporting - * documentation. This software is provided "as is" without express or - * implied warranty. - */ -/* -*************************** Motivation ********************************** - -Many interactive programs read input line by line, but would like to -provide line editing and history functionality to the end-user that -runs the program. - -The input-edit package provides that functionality. As far as the -programmer is concerned, the program only asks for the next line -of input. However, until the user presses the RETURN key they can use -emacs-style line editing commands and can traverse the history of lines -previously typed. - -Other packages, such as GNU's readline, have greater capability but are -also substantially larger. Input-edit is small, since it uses neither -stdio nor any termcap features, and is also quite portable. It only uses -\b to backspace and \007 to ring the bell on errors. Since it cannot -edit multiple lines it scrolls long lines left and right on the same line. - -Input edit uses classic (not ANSI) C, and should run on any Unix -system (BSD or SYSV), PC's with the MSC compiler, or Vax/VMS (untested by me). -Porting the package to new systems basicaly requires code to read a -character when it is typed without echoing it, everything else should be OK. - -I have run the package on: - - DECstation 5000, Ultrix 4.2 with cc and gcc - Sun Sparc 2, SunOS 4.1.1, with cc - SGI Iris, IRIX System V.3, with cc - PC, DRDOS 5.0, with MSC 6.0 - -The description below is broken into two parts, the end-user (editing) -interface and the programmer interface. Send bug reports, fixes and -enhancements to: - -Chris Thewalt (thewalt@ce.berkeley.edu) -2/4/92 - -PS: I don't have, and don't want to add, a vi mode, sorry. - -************************** End-User Interface *************************** - -Entering printable keys generally inserts new text into the buffer (unless -in overwrite mode, see below). Other special keys can be used to modify -the text in the buffer. In the description of the keys below, ^n means -Control-n, or holding the CONTROL key down while pressing "n". M-B means -Meta-B (or Alt-B). Errors will ring the terminal bell. - -^A/^E : Move cursor to beginning/end of the line. -^F/^B : Move cursor forward/backward one character. -^D : Delete the character under the cursor. -^H, DEL : Delete the character to the left of the cursor. -^K : Kill from the cursor to the end of line. -^L : Redraw current line. -^O : Toggle overwrite/insert mode. Initially in insert mode. Text - added in overwrite mode (including yanks) overwrite - existing text, while insert mode does not overwrite. -^P/^N : Move to previous/next item on history list. -^R/^S : Perform incremental reverse/forward search for string on - the history list. Typing normal characters adds to the current - search string and searches for a match. Typing ^R/^S marks - the start of a new search, and moves on to the next match. - Typing ^H or DEL deletes the last character from the search - string, and searches from the starting location of the last search. - Therefore, repeated DEL's appear to unwind to the match nearest - the point at which the last ^R or ^S was typed. If DEL is - repeated until the search string is empty the search location - begins from the start of the history list. Typing ESC or - any other editing character accepts the current match and - loads it into the buffer, terminating the search. -^T : Toggle the characters under and to the left of the cursor. -^U : Kill from beginning to the end of the line. -^Y : Yank previously killed text back at current location. Note that - this will overwrite or insert, depending on the current mode. -M-F/M-B : Move cursor forward/backward one word. -M-D : Delete the word under the cursor. -^SPC : Set mark. -^W : Kill from mark to point. -^X : Exchange mark and point. -TAB : By default adds spaces to buffer to get to next TAB stop - (just after every 8th column), although this may be rebound by the - programmer, as described below. -NL, CR : returns current buffer to the program. - -DOS and ANSI terminal arrow key sequences are recognized, and act like: - - up : same as ^P - down : same as ^N - left : same as ^B - right : same as ^F - -************************** Programmer Interface *************************** - -The programmer accesses input-edit through five functions, and optionally -through three additional function pointer hooks. The five functions are: - -char *Getline(char *prompt) - - Prints the prompt and allows the user to edit the current line. A - pointer to the line is returned when the user finishes by - typing a newline or a return. Unlike GNU readline, the returned - pointer points to a static buffer, so it should not be free'd, and - the buffer contains the newline character. The user enters an - end-of-file by typing ^D on an empty line, in which case the - first character of the returned buffer is '\0'. Getline never - returns a NULL pointer. The getline function sets terminal modes - needed to make it work, and resets them before returning to the - caller. The getline function also looks for characters that would - generate a signal, and resets the terminal modes before raising the - signal condition. If the signal handler returns to getline, - the screen is automatically redrawn and editing can continue. - Getline now requires both the input and output stream be connected - to the terminal (not redirected) so the main program should check - to make sure this is true. If input or output have been redirected - the main program should use buffered IO (stdio) rather than - the slow 1 character read()s that getline uses (note: this limitation - has been removed). - -char *Getlinem(int mode, char *prompt) - - mode: -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup - - More specialized version of the previous function. Depending on - the mode, it behaves differently. Its main use is to allow - character by character input from the input stream (useful when - in an X eventloop). It will return NULL as long as no newline - has been received. Its use is typically as follows: - 1) In the program initialization part one calls: Getlinem(-1,"prompt>") - 2) In the X inputhandler: if ((line = Getlinem(1,NULL))) { - 3) In the termination routine: Getlinem(2,NULL) - With mode=0 the function behaves exactly like the previous function. - -void Gl_config(const char *which, int value) - - Set some config options. Which can be: - "noecho": do not echo characters (used for passwd input) - "erase": do erase line after return (used for text scrollers) - -void Gl_setwidth(int width) - - Set the width of the terminal to the specified width. The default - width is 80 characters, so this function need only be called if the - width of the terminal is not 80. Since horizontal scrolling is - controlled by this parameter it is important to get it right. - -void Gl_histinit(char *file) - - This function reads a history list from file. So lines from a - previous session can be used again. - -void Gl_histadd(char *buf) - - The Gl_histadd function checks to see if the buf is not empty or - whitespace, and also checks to make sure it is different than - the last saved buffer to avoid repeats on the history list. - If the buf is a new non-blank string a copy is made and saved on - the history list, so the caller can re-use the specified buf. - -The main loop in testgl.c, included in this directory, shows how the -input-edit package can be used: - -extern char *Getline(); -extern void Gl_histadd(); -main() -{ - char *p; - Gl_histinit(".hist"); - do { - p = Getline("PROMPT>>>> "); - Gl_histadd(p); - fputs(p, stdout); - } while (*p != 0); -} - -In order to allow the main program to have additional access to the buffer, -to implement things such as completion or auto-indent modes, three -function pointers can be bound to user functions to modify the buffer as -described below. By default gl_in_hook and gl_out_hook are set to NULL, -and gl_tab_hook is bound to a function that inserts spaces until the next -logical tab stop is reached. The user can reassign any of these pointers -to other functions. Each of the functions bound to these hooks receives -the current buffer as the first argument, and must return the location of -the leftmost change made in the buffer. If the buffer isn't modified the -functions should return -1. When the hook function returns the screen is -updated to reflect any changes made by the user function. - -int (*gl_in_hook)(char *buf) - - If gl_in_hook is non-NULL the function is called each time a new - buffer is loaded. It is called when getline is entered, with an - empty buffer, it is called each time a new buffer is loaded from - the history with ^P or ^N, and it is called when an incremental - search string is accepted (when the search is terminated). The - buffer can be modified and will be redrawn upon return to Getline(). - -int (*gl_out_hook)(char *buf) - - If gl_out_hook is non-NULL it is called when a line has been - completed by the user entering a newline or return. The buffer - handed to the hook does not yet have the newline appended. If the - buffer is modified the screen is redrawn before getline returns the - buffer to the caller. - -int (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc) - - If gl_tab_hook is non-NULL, it is called whenever a tab is typed. - In addition to receiving the buffer, the current prompt width is - given (needed to do tabbing right) and a pointer to the cursor - offset is given, where a 0 offset means the first character in the - line. Not only does the cursor_loc tell the programmer where the - TAB was received, but it can be reset so that the cursor will end - up at the specified location after the screen is redrawn. -*/ - -/* forward reference needed for gl_tab_hook */ -static int gl_tab(char *buf, int offset, int *loc); - -/********************* exported interface ********************************/ - -static int (*gl_in_hook)(char *buf) = 0; -static int (*gl_out_hook)(char *buf) = 0; -static int (*gl_tab_hook)(char *buf, int prompt_width, int *loc) = gl_tab; - -/******************** imported interface *********************************/ -#ifdef DMALLOC -/* reports leaks, which is the history buffer. dont care */ -#undef DMALLOC -#endif -#include "sigar_getline.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include -#include -#include -#include -#include -#include - -/******************** internal interface *********************************/ - -static char *sigar_getlinem(int mode, char *prompt); /* allows reading char by char */ - -static void sigar_getline_config(const char *which, int value); /* set some options */ - -static void sigar_getline_clear_screen(void); - -#define BUF_SIZE 8096 - -static int gl_init_done = -1; /* terminal mode flag */ -static int gl_notty = 0; /* 1 when not a tty */ -static int gl_eof = 0; /* 1 when not a tty and read() == -1 */ -static int gl_termw = 80; /* actual terminal width */ -static int gl_scroll = 27; /* width of EOL scrolling region */ -static int gl_width = 0; /* net size available for input */ -static int gl_extent = 0; /* how far to redraw, 0 means all */ -static int gl_overwrite = 0; /* overwrite mode */ -static int gl_no_echo = 0; /* do not echo input characters */ -static int gl_passwd = 0; /* do not echo input characters */ -static int gl_erase_line = 0; /* erase line before returning */ -static int gl_pos, gl_cnt = 0; /* position and size of input */ -static char gl_buf[BUF_SIZE]; /* input buffer */ -static char gl_killbuf[BUF_SIZE]=""; /* killed text */ -static char *gl_prompt; /* to save the prompt string */ -static char gl_intrc = 0; /* keyboard SIGINT char */ -static char gl_quitc = 0; /* keyboard SIGQUIT char */ -static char gl_suspc = 0; /* keyboard SIGTSTP char */ -static char gl_dsuspc = 0; /* delayed SIGTSTP char */ -static int gl_search_mode = 0; /* search mode flag */ -static int gl_bell_enabled = 0; /* bell mode */ -static int gl_savehist = 0; /* # of lines to save in hist file */ -static char gl_histfile[256]; /* name of history file */ - -static void gl_init(); /* prepare to edit a line */ -static void gl_bell(); /* ring bell */ -static void gl_cleanup(); /* to undo gl_init */ -static void gl_char_init(); /* get ready for no echo input */ -static void gl_char_cleanup(); /* undo gl_char_init */ - -static void gl_addchar(int c); /* install specified char */ -static void gl_del(int loc); /* del, either left (-1) or cur (0) */ -static void gl_error(char *buf); /* write error msg and die */ -static void gl_fixup(char *p, int c, int cur); /* fixup state variables and screen */ -static int gl_getc(); /* read one char from terminal */ -static void gl_kill(); /* delete to EOL */ -static void gl_newline(); /* handle \n or \r */ -static void gl_putc(int c); /* write one char to terminal */ -static void gl_puts(char *buf); /* write a line to terminal */ -static void gl_transpose(); /* transpose two chars */ -static void gl_yank(); /* yank killed text */ - -static int is_whitespace(char c); /* "whitespace" very loosely interpreted */ -static void gl_back_1_word(); /* move cursor back one word */ -static void gl_kill_1_word(); /* kill to end of word */ -static void gl_kill_region(int i, int j); /* kills from i to j */ -static void gl_fwd_1_word(); /* move cursor forward one word */ -static void gl_set_mark(); /* sets mark to be at point */ -static void gl_exch(); /* exchanges point and mark */ -static void gl_wipe(); /* kills from mark to point */ -static int gl_mark = -1; /* position of mark. gl_mark<0 if not set */ - -static void hist_init(); /* initializes hist pointers */ -static char *hist_next(); /* return ptr to next item */ -static char *hist_prev(); /* return ptr to prev item */ -static char *hist_save(char *p); /* makes copy of a string, without NL */ - -static void search_addchar(int c); /* increment search string */ -static void search_term(); /* reset with current contents */ -static void search_back(int s); /* look back for current string */ -static void search_forw(int s); /* look forw for current string */ - -/************************ nonportable part *********************************/ - -#ifdef MSDOS -#include -#endif - -#ifdef WIN32 -# define MSDOS -# include -# include -#endif /* WIN32 */ - -#ifdef __MWERKS__ -#define R__MWERKS -#endif - -#ifdef R__MWERKS -# include -#endif - -#if defined(_AIX) || defined(__Lynx__) || defined(__APPLE__) -#define unix -#endif - -#if defined(__hpux) || defined(__osf__) /* W.Karig@gsi.de */ -#ifndef unix -#define unix -#endif -#endif - -#ifdef unix -#include -#if !defined(__osf__) && !defined(_AIX) /* W.Karig@gsi.de */ -#include -#endif - -#if defined(__linux__) && defined(__powerpc__) -# define R__MKLINUX // = linux on PowerMac -#endif -#if defined(__linux__) && defined(__alpha__) -# define R__ALPHALINUX // = linux on Alpha -#endif - -#if defined(TIOCGETP) && !defined(__sgi) && !defined(R__MKLINUX) && \ - !defined(R__ALPHALINUX) /* use BSD interface if possible */ -#include -static struct sgttyb new_tty, old_tty; -static struct tchars tch; -static struct ltchars ltch; -#else -#ifdef SIGTSTP /* need POSIX interface to handle SUSP */ -#include -#if defined(__sun) || defined(__sgi) || defined(R__MKLINUX) || \ - defined(R__ALPHALINUX) -#undef TIOCGETP /* Solaris and SGI define TIOCGETP in */ -#undef TIOCSETP -#endif -static struct termios new_termios, old_termios; -#else /* use SYSV interface */ -#include -static struct termio new_termio, old_termio; -#endif -#endif -#endif /* unix */ - -#ifdef VMS -#include -#include -#include -#include -#include -#include unixio - -static int setbuff[2]; /* buffer to set terminal attributes */ -static short chan = -1; /* channel to terminal */ -struct dsc$descriptor_s descrip; /* VMS descriptor */ -#endif - -static void -sigar_getline_config(const char *which, int value) -{ - if (strcmp(which, "noecho") == 0) - gl_no_echo = value; - else if (strcmp(which, "erase") == 0) - gl_erase_line = value; - else - printf("gl_config: %s ?\n", which); -} - -static void -gl_char_init() /* turn off input echo */ -{ - if (gl_notty) return; -#ifdef unix -#ifdef TIOCGETP /* BSD */ - ioctl(0, TIOCGETC, &tch); - ioctl(0, TIOCGLTC, <ch); - gl_intrc = tch.t_intrc; - gl_quitc = tch.t_quitc; - gl_suspc = ltch.t_suspc; - gl_dsuspc = ltch.t_dsuspc; - ioctl(0, TIOCGETP, &old_tty); - new_tty = old_tty; - new_tty.sg_flags |= RAW; - new_tty.sg_flags &= ~ECHO; - ioctl(0, TIOCSETP, &new_tty); -#else -#ifdef SIGTSTP /* POSIX */ - tcgetattr(0, &old_termios); - gl_intrc = old_termios.c_cc[VINTR]; - gl_quitc = old_termios.c_cc[VQUIT]; -#ifdef VSUSP - gl_suspc = old_termios.c_cc[VSUSP]; -#endif -#ifdef VDSUSP - gl_dsuspc = old_termios.c_cc[VDSUSP]; -#endif - new_termios = old_termios; - new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF); - new_termios.c_iflag |= (IGNBRK|IGNPAR); - new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; - tcsetattr(0, TCSANOW, &new_termios); -#else /* SYSV */ - ioctl(0, TCGETA, &old_termio); - gl_intrc = old_termio.c_cc[VINTR]; - gl_quitc = old_termio.c_cc[VQUIT]; - new_termio = old_termio; - new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF); - new_termio.c_iflag |= (IGNBRK|IGNPAR); - new_termio.c_lflag &= ~(ICANON|ISIG|ECHO); - new_termio.c_cc[VMIN] = 1; - new_termio.c_cc[VTIME] = 0; - ioctl(0, TCSETA, &new_termio); -#endif -#endif -#endif /* unix */ - -#ifdef MSDOS - gl_intrc = 'C' - '@'; - gl_quitc = 'Q' - '@'; -// gl_suspc = ltch.t_suspc; -#endif /* MSDOS */ - -#ifdef R__MWERKS - gl_intrc = 'C' - '@'; - gl_quitc = 'Q' - '@'; -#endif - -#ifdef vms - descrip.dsc$w_length = strlen("tt:"); - descrip.dsc$b_dtype = DSC$K_DTYPE_T; - descrip.dsc$b_class = DSC$K_CLASS_S; - descrip.dsc$a_pointer = "tt:"; - (void)sys$assign(&descrip,&chan,0,0); - (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0); - setbuff[1] |= TT$M_NOECHO; - (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0); -#endif /* vms */ -} - -static void -gl_char_cleanup() /* undo effects of gl_char_init */ -{ - if (gl_notty) return; -#ifdef unix -#ifdef TIOCSETP /* BSD */ - ioctl(0, TIOCSETP, &old_tty); -#else -#ifdef SIGTSTP /* POSIX */ - tcsetattr(0, TCSANOW, &old_termios); -#else /* SYSV */ - ioctl(0, TCSETA, &old_termio); -#endif -#endif -#endif /* unix */ - -#ifdef vms - setbuff[1] &= ~TT$M_NOECHO; - (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0); - sys$dassgn(chan); - chan = -1; -#endif -} - -#if defined(MSDOS) && !defined(WIN32) -// +DECK, PAUSE, T=XCC, IF=WINNT. (from KERNDOS.CAR ) -# include - int pause_() - { - int first_char; - first_char = _getch(); - if (first_char == 0 || first_char == 0xE0) first_char = -_getch(); - return first_char; - } -#endif - -#if defined(MSDOS) && defined(WIN32) -//______________________________________________________________________________ -int pause_() -{ - static HANDLE hConsoleInput = NULL; - static iCharCount = 0; - static int chLastChar = 0; - - DWORD cRead; - - INPUT_RECORD pirBuffer; - KEY_EVENT_RECORD *KeyEvent= (KEY_EVENT_RECORD *)&(pirBuffer.Event); - - if (!hConsoleInput) hConsoleInput = GetStdHandle(STD_INPUT_HANDLE); - - if (iCharCount) iCharCount--; // Whether several symbols had been read - else { - chLastChar = 0; - while (chLastChar == 0) { - if (!ReadConsoleInput(hConsoleInput, // handle of a console input buffer - &pirBuffer, // address of the buffer for read data - 1, // number of records to read - &cRead // address of number of records read - )) return 0; - - if (pirBuffer.EventType == KEY_EVENT && KeyEvent->bKeyDown == TRUE){ - iCharCount = KeyEvent->wRepeatCount - 1; - chLastChar = ((int) (KeyEvent->uChar).AsciiChar & 0xffff); - if (chLastChar) - OemToCharBuff((char const *)&chLastChar,(char *)&chLastChar,1); - else - chLastChar = - (KeyEvent->wVirtualScanCode); -// chLastChar = - (KeyEvent->wVirtualKeyCode); - } - } - } - return chLastChar; - -} -#endif - -static int -gl_getc() -/* get a character without echoing it to screen */ -{ -#ifdef MSDOS -# define k_ctrl_C 3 -# define k_ctrl_Z 26 -# define k_ctrl_Q 17 -# define k_ctrl_K 11 -# define k_rt_arr -77 -# define k_lt_arr -75 -# define k_up_arr -72 -# define k_dn_arr -80 -# define k_PGUP -73 -# define k_PGDW -81 -# define k_HOME -71 -# define k_END -79 -# define k_INS -82 -# define k_DEL -83 -# define k_ENTER 13 -# define k_CR 13 -# define k_BS 8 -# define k_ESC 27 -# define k_alt_H -35 -# define k_beep 7 -# ifndef WIN32 - int get_cursor__(int *,int *); - int display_off__(int *); - int display_on__(); - int locate_(int *,int *); - int ixc, iyc; -# endif - int pause_(); -#endif - - int c; - -#if defined(unix) - unsigned char ch; - while ((c = (read(0, &ch, 1) > 0) ? ch : -1) == -1 && errno == EINTR) - errno = 0; -#endif - -#if defined(R__MWERKS) - c = getchar(); -#endif - -#ifdef MSDOS - c = pause_(); - if (c < 0) { - switch (c) { - case k_up_arr: c = 'P' - '@'; /* up -> ^P = 16 */ - break; - case k_dn_arr: c = 'N' - '@'; /* down -> ^N = 14 */ - break; - case k_lt_arr: c = 'B' - '@'; /* left -> ^B =2 */ - break; - case k_rt_arr: c = 'F' - '@'; /* right -> ^F = 6*/ - break; - case k_INS: c = 'O' - '@'; /* right -> ^O = 15*/ - break; - case k_DEL: c = 'D' - '@'; /* Delete character under cursor = 4*/ - break; - case k_END: c = 'E' - '@'; /* Moves cursor to end of line * = 5 */ - break; - case k_HOME: c = 'A' - '@'; /* Moves cursor to beginning of line = 1*/ - break; - default: c = 0; /* make it garbage */ - } - } - else { - switch(c) { - case k_ESC: c = 'U' - '@'; /* Clear full line -> ^U */ - break; - default: - break; - } - } -#endif - -#ifdef vms - if(chan < 0) { - c='\0'; - } - (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0); - c &= 0177; /* get a char */ -#endif - return c; -} - -static void -gl_putc(int c) -{ - char ch = c; - - if (gl_notty) return; - - if ( !gl_passwd || !isgraph(c)) - { -#ifdef WIN32 - CharToOemBuff((char const *)&c,&ch,1); -#endif - - sigar_write(1, &ch, 1); - } -#if defined(unix) || defined(MSDOS) || defined(WIN32) || defined(R__MWERKS) -#ifdef TIOCSETP /* BSD in RAW mode, map NL to NL,CR */ - if (ch == '\n') { - ch = '\r'; - sigar_write(1, &ch, 1); - } -#endif -#endif -} - -/******************** fairly portable part *********************************/ - -static void -gl_puts(char *buf) -{ - int len = strlen(buf); - - if (gl_notty) return; -#ifdef WIN32 - { - char *OemBuf = (char *)malloc(2*len); - CharToOemBuff(buf,OemBuf,len); - sigar_write(1, OemBuf, len); - free(OemBuf); - } -#else - sigar_write(1, buf, len); -#endif -} - -static void -gl_error(char *buf) -{ - int len = strlen(buf); - - gl_cleanup(); -#ifdef WIN32 - { - char *OemBuf = (char *)malloc(2*len); - CharToOemBuff(buf,OemBuf,len); - sigar_write(2, OemBuf, len); - free(OemBuf); - } -#else - sigar_write(2, buf, len); -#endif - exit(1); -} - -static void -gl_init() -/* set up variables and terminal */ -{ - if (gl_init_done < 0) { /* -1 only on startup */ - hist_init(); - } - if (sigar_isatty(0) == 0 || sigar_isatty(1) == 0) - gl_notty = 1; - gl_char_init(); - gl_init_done = 1; -} - -static void -gl_bell() -{ - if (gl_bell_enabled) { - gl_putc('\007'); - } -} - -static void -gl_cleanup() -/* undo effects of gl_init, as necessary */ -{ - if (gl_init_done > 0) - gl_char_cleanup(); - gl_init_done = 0; -} - -SIGAR_DECLARE(void) -sigar_getline_setwidth(int w) -{ - if (w > 20) { - gl_termw = w; - gl_scroll = w / 3; - } else { - gl_error("\n*** Error: minimum screen width is 21\n"); - } -} - -SIGAR_DECLARE(void) -sigar_getline_windowchanged() -{ -#ifdef TIOCGWINSZ - if (sigar_isatty(0)) { - static char lenv[32], cenv[32]; - struct winsize wins; - ioctl(0, TIOCGWINSZ, &wins); - - if (wins.ws_col == 0) wins.ws_col = 80; - if (wins.ws_row == 0) wins.ws_row = 24; - - sigar_getline_setwidth(wins.ws_col); - - sprintf(lenv, "LINES=%d", wins.ws_row); - putenv(lenv); - sprintf(cenv, "COLUMNS=%d", wins.ws_col); - putenv(cenv); - } -#endif -} - -/* -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup */ - -static char * -sigar_getlinem(int mode, char *prompt) -{ - int c, loc, tmp; - int sig; - - if (mode == 2) { - gl_cleanup(); - return NULL; - } - - if (mode < 1) { - if (mode == -1) { - sigar_getline_config("noecho", 0); - sigar_getline_config("erase", 0); - } - gl_init(); - gl_prompt = (prompt)? prompt : (char*)""; - gl_buf[0] = 0; - if (gl_in_hook) - gl_in_hook(gl_buf); - gl_fixup(gl_prompt, -2, BUF_SIZE); - if (mode == -1) return NULL; - } - while ((c = gl_getc()) >= 0) { - gl_extent = 0; /* reset to full extent */ -#ifndef WIN32 - if (isprint(c)) { -#else - if (c >= ' ') { -#endif - if (gl_search_mode) - search_addchar(c); - else - gl_addchar(c); - } else { - if (gl_search_mode) { - if (c == '\033' || c == '\016' || c == '\020') { - search_term(); - c = 0; /* ignore the character */ - } else if (c == '\010' || c == '\177') { - search_addchar(-1); /* unwind search string */ - c = 0; - } else if (c != '\022' && c != '\023') { - search_term(); /* terminate and handle char */ - } - } - /* NOTE: - * sometimes M-x turns on bit 8 ( M-x --> 'x' + 128 ) - * sometimes M-x prepends an escape character ( M-x --> '\033','x' ) - * both cases are handled ... - */ - switch (c) - { - case 'b'+128: /* M-b */ - case 'B'+128: /* M-B */ - gl_back_1_word(); - break; - case 'd'+128: /* M-d */ - case 'D'+128: /* M-D */ - gl_kill_1_word(); - break; - case 'f'+128: /* M-f */ - case 'F'+128: /* M-F */ - gl_fwd_1_word(); - break; - case '\000': /* ^SPC */ - gl_set_mark(); - break; - case '\027': /* ^W */ - gl_wipe(); - break; - case '\030': /* ^X */ - gl_exch(); - break; - case '\n': /* newline */ - case '\r': - gl_newline(); - gl_cleanup(); - return gl_buf; - /*NOTREACHED*/ - break; - case '\001': gl_fixup(gl_prompt, -1, 0); /* ^A */ - break; - case '\002': gl_fixup(gl_prompt, -1, gl_pos-1); /* ^B */ - break; - case '\004': /* ^D */ - if (gl_cnt == 0) { - gl_buf[0] = 0; - gl_cleanup(); - gl_putc('\n'); - return gl_buf; - } else { - gl_del(0); - } - break; - case '\005': gl_fixup(gl_prompt, -1, gl_cnt); /* ^E */ - break; - case '\006': gl_fixup(gl_prompt, -1, gl_pos+1); /* ^F */ - break; - case '\010': case '\177': gl_del(-1); /* ^H and DEL */ - break; - case '\t': /* TAB */ - if (gl_tab_hook) { - tmp = gl_pos; - loc = gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp); - if (loc >= 0 || tmp != gl_pos || loc == -2) - gl_fixup(gl_prompt, loc, tmp); - } - break; - case '\013': gl_kill(); /* ^K */ - break; - case '\014': sigar_getline_clear_screen(); /* ^L */ - break; - case '\016': /* ^N */ - strcpy(gl_buf, hist_next()); - if (gl_in_hook) - gl_in_hook(gl_buf); - gl_fixup(gl_prompt, 0, BUF_SIZE); - break; - case '\017': gl_overwrite = !gl_overwrite; /* ^O */ - break; - case '\020': /* ^P */ - strcpy(gl_buf, hist_prev()); - if (gl_in_hook) - gl_in_hook(gl_buf); - gl_fixup(gl_prompt, 0, BUF_SIZE); - break; - case '\022': search_back(1); /* ^R */ - break; - case '\023': search_forw(1); /* ^S */ - break; - case '\024': gl_transpose(); /* ^T */ - break; - case '\025': gl_fixup(gl_prompt,-1,0); gl_kill(); /* ^U */ - break; - case '\031': gl_yank(); /* ^Y */ - break; - case '\033': - switch(c = gl_getc()) - { - case 'b': /* M-b */ - case 'B': /* M-B */ - gl_back_1_word(); - break; - case 'd': /* M-d */ - case 'D': /* M-D */ - gl_kill_1_word(); - break; - case 'f': /* M-f */ - case 'F': /* M-F */ - gl_fwd_1_word(); - break; - case '[': /* ansi arrow keys */ - case 'O': /* xterm arrow keys */ - switch(c = gl_getc()) - { - case 'A': /* up */ - strcpy(gl_buf, hist_prev()); - if (gl_in_hook) - gl_in_hook(gl_buf); - gl_fixup(gl_prompt, 0, BUF_SIZE); - break; - case 'B': /* down */ - strcpy(gl_buf, hist_next()); - if (gl_in_hook) - gl_in_hook(gl_buf); - gl_fixup(gl_prompt, 0, BUF_SIZE); - break; - case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */ - break; - case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */ - break; - default: /* who knows */ - gl_bell(); - break; - } - break; - default: - gl_bell(); - } - break; - default: /* check for a terminal signal */ - -#if defined(unix) || defined(WIN32) || defined(R__MWERKS) - if (c > 0) { /* ignore 0 (reset above) */ - sig = 0; -#ifdef SIGINT - if (c == gl_intrc) - sig = SIGINT; -#endif -#ifdef SIGQUIT - if (c == gl_quitc) - sig = SIGQUIT; -#endif -#ifdef SIGTSTP - if (c == gl_suspc || c == gl_dsuspc) - sig = SIGTSTP; -#endif - if (sig != 0) { - gl_cleanup(); -#if !defined(WIN32) - raise(sig); -#endif -#ifdef WIN32 - if (sig == SIGINT) GenerateConsoleCtrlEvent(CTRL_C_EVENT,0); - else raise(sig); -#endif - gl_init(); - sigar_getline_redraw(); - c = 0; - } - } -#endif /* unix */ - if (c > 0) - gl_bell(); - break; - } - } - if (mode == 1) return NULL; - } - if (c == -1 && gl_notty) - gl_eof = 1; - else - gl_eof = 0; - - gl_cleanup(); - gl_buf[0] = 0; - return gl_buf; -} - -SIGAR_DECLARE(int) -sigar_getline_eof() -{ - return gl_eof; -} - -SIGAR_DECLARE(char *) -sigar_getline(char *prompt) -{ - return sigar_getlinem(0, prompt); -} - -static void -gl_addchar(int c) -/* adds the character c to the input buffer at current location */ -{ - int i; - - if (gl_cnt >= BUF_SIZE - 1) - gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); - if (gl_overwrite == 0 || gl_pos == gl_cnt) { - for (i=gl_cnt; i >= gl_pos; i--) - gl_buf[i+1] = gl_buf[i]; - gl_buf[gl_pos] = c; - gl_fixup(gl_prompt, gl_pos, gl_pos+1); - } else { - gl_buf[gl_pos] = c; - gl_extent = 1; - gl_fixup(gl_prompt, gl_pos, gl_pos+1); - } -} - -static void -gl_yank() -/* adds the kill buffer to the input buffer at current location */ -{ - int i, len; - - len = strlen(gl_killbuf); - if (len > 0) { - gl_mark = gl_pos; - if (gl_overwrite == 0) { - if (gl_cnt + len >= BUF_SIZE - 1) - gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); - for (i=gl_cnt; i >= gl_pos; i--) - gl_buf[i+len] = gl_buf[i]; - for (i=0; i < len; i++) - gl_buf[gl_pos+i] = gl_killbuf[i]; - gl_fixup(gl_prompt, gl_pos, gl_pos+len); - } else { - if (gl_pos + len > gl_cnt) { - if (gl_pos + len >= BUF_SIZE - 1) - gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); - gl_buf[gl_pos + len] = 0; - } - for (i=0; i < len; i++) - gl_buf[gl_pos+i] = gl_killbuf[i]; - gl_extent = len; - gl_fixup(gl_prompt, gl_pos, gl_pos+len); - } - } else - gl_bell(); -} - -static void -gl_transpose() -/* switch character under cursor and to left of cursor */ -{ - int c; - - if (gl_pos > 0 && gl_cnt > gl_pos) { - c = gl_buf[gl_pos-1]; - gl_buf[gl_pos-1] = gl_buf[gl_pos]; - gl_buf[gl_pos] = c; - gl_extent = 2; - gl_fixup(gl_prompt, gl_pos-1, gl_pos); - } else - gl_bell(); -} - -static void -gl_newline() -/* - * Cleans up entire line before returning to caller. A \n is appended. - * If line longer than screen, we redraw starting at beginning - */ -{ - int change = gl_cnt; - int len = gl_cnt; - int loc = gl_width - 5; /* shifts line back to start position */ - - if (gl_cnt >= BUF_SIZE - 1) - gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); - if (gl_out_hook) { - change = gl_out_hook(gl_buf); - len = strlen(gl_buf); - } - if (gl_erase_line) { - char gl_buf0 = gl_buf[0]; - gl_buf[0] = '\0'; - gl_fixup("", 0, 0); - gl_buf[0] = gl_buf0; - } - else { - if (loc > len) - loc = len; - gl_fixup(gl_prompt, change, loc); /* must do this before appending \n */ - gl_putc('\n'); - } -#if 0 - gl_buf[len] = '\n'; - gl_buf[len+1] = '\0'; -#endif - gl_mark = -1; -} - -static void -gl_del(int loc) -/* - * Delete a character. The loc variable can be: - * -1 : delete character to left of cursor - * 0 : delete character under cursor - */ -{ - int i; - - if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) { - for (i=gl_pos+loc; i < gl_cnt; i++) - gl_buf[i] = gl_buf[i+1]; - gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc); - } else - gl_bell(); -} - -static void -gl_kill() -/* delete from current position to the end of line */ -{ - if (gl_pos < gl_cnt) { - strcpy(gl_killbuf, gl_buf + gl_pos); - gl_buf[gl_pos] = '\0'; - gl_fixup(gl_prompt, gl_pos, gl_pos); - } else - gl_bell(); -} - -SIGAR_DECLARE(void) sigar_getline_redraw(void) -/* emit a newline, reset and redraw prompt and current input line */ -{ - if (gl_init_done > 0) { - gl_putc('\n'); - gl_fixup(gl_prompt, -2, gl_pos); - } -} - -#define CLEAR_SCREEN "\033[2J" - -static void sigar_getline_clear_screen(void) -{ - if (gl_init_done > 0) { - gl_putc('\n'); - /* XXX what to do for non-ansi term? */ - gl_puts(CLEAR_SCREEN); - gl_fixup(gl_prompt, -2, gl_pos); - } -} - -SIGAR_DECLARE(void) sigar_getline_reset(void) -{ - gl_fixup(gl_prompt,-1,0); - gl_kill(); -} - -static void -gl_fixup(char *prompt, int change, int cursor) -/* - * This function is used both for redrawing when input changes or for - * moving within the input line. The parameters are: - * prompt: compared to last_prompt[] for changes; - * change : the index of the start of changes in the input buffer, - * with -1 indicating no changes, -2 indicating we're on - * a new line, redraw everything. - * cursor : the desired location of the cursor after the call. - * A value of BUF_SIZE can be used to indicate the cursor should - * move just past the end of the input line. - */ -{ - static int gl_shift; /* index of first on screen character */ - static int off_right; /* true if more text right of screen */ - static int off_left; /* true if more text left of screen */ - static char last_prompt[BUF_SIZE] = ""; - int left = 0, right = -1; /* bounds for redraw */ - int padl; /* how much to erase at end of line */ - int backup; /* how far to backup before fixing */ - int new_shift; /* value of shift based on cursor */ - int extra; /* adjusts when shift (scroll) happens */ - int i; - int new_right = -1; /* alternate right bound, using gl_extent */ - int l1, l2; - - if (change == -2) { /* reset */ - gl_pos = gl_cnt = gl_shift = off_right = off_left = 0; - gl_passwd = 0; - gl_puts(prompt); - gl_passwd = gl_no_echo; - strcpy(last_prompt, prompt); - change = 0; - gl_width = gl_termw - strlen(prompt); - } else if (strcmp(prompt, last_prompt) != 0) { - l1 = strlen(last_prompt); - l2 = strlen(prompt); - gl_cnt = gl_cnt + l1 - l2; - strcpy(last_prompt, prompt); - backup = gl_pos - gl_shift + l1; - for (i=0; i < backup; i++) - gl_putc('\b'); - gl_passwd = 0; - gl_puts(prompt); - gl_passwd = gl_no_echo; - gl_pos = gl_shift; - gl_width = gl_termw - l2; - change = 0; - } - padl = (off_right)? gl_width - 1 : gl_cnt - gl_shift; /* old length */ - backup = gl_pos - gl_shift; - if (change >= 0) { - gl_cnt = strlen(gl_buf); - if (change > gl_cnt) - change = gl_cnt; - } - if (cursor > gl_cnt) { - if (cursor != BUF_SIZE) /* BUF_SIZE means end of line */ - gl_bell(); - cursor = gl_cnt; - } - if (cursor < 0) { - gl_bell(); - cursor = 0; - } - if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2)) - extra = 2; /* shift the scrolling boundary */ - else - extra = 0; - new_shift = cursor + extra + gl_scroll - gl_width; - if (new_shift > 0) { - new_shift /= gl_scroll; - new_shift *= gl_scroll; - } else - new_shift = 0; - if (new_shift != gl_shift) { /* scroll occurs */ - gl_shift = new_shift; - off_left = (gl_shift)? 1 : 0; - off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; - left = gl_shift; - new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt; - } else if (change >= 0) { /* no scroll, but text changed */ - if (change < gl_shift + off_left) { - left = gl_shift; - } else { - left = change; - backup = gl_pos - change; - } - off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; - right = (off_right)? gl_shift + gl_width - 2 : gl_cnt; - new_right = (gl_extent && (right > left + gl_extent))? - left + gl_extent : right; - } - padl -= (off_right)? gl_width - 1 : gl_cnt - gl_shift; - padl = (padl < 0)? 0 : padl; - if (left <= right) { /* clean up screen */ - for (i=0; i < backup; i++) - gl_putc('\b'); - if (left == gl_shift && off_left) { - gl_putc('$'); - left++; - } - for (i=left; i < new_right; i++) - gl_putc(gl_buf[i]); - gl_pos = new_right; - if (off_right && new_right == right) { - gl_putc('$'); - gl_pos++; - } else { - for (i=0; i < padl; i++) /* erase remains of prev line */ - gl_putc(' '); - gl_pos += padl; - } - } - i = gl_pos - cursor; /* move to final cursor location */ - if (i > 0) { - while (i--) - gl_putc('\b'); - } else { - for (i=gl_pos; i < cursor; i++) - gl_putc(gl_buf[i]); - } - gl_pos = cursor; -} - -static int -gl_tab(char *buf, int offset, int *loc) -/* default tab handler, acts like tabstops every 8 cols */ -{ - int i, count, len; - - len = strlen(buf); - count = 8 - (offset + *loc) % 8; - for (i=len; i >= *loc; i--) - buf[i+count] = buf[i]; - for (i=0; i < count; i++) - buf[*loc+i] = ' '; - i = *loc; - *loc = i + count; - return i; -} - -/******************* History stuff **************************************/ - -#ifndef HIST_SIZE -#define HIST_SIZE 100 -#endif - -static int hist_pos = 0, hist_last = 0; -static char *hist_buf[HIST_SIZE]; - -static void -hist_init() -{ - int i; - - if (gl_savehist) return; - - hist_buf[0] = ""; - for (i=1; i < HIST_SIZE; i++) - hist_buf[i] = (char *)0; -} - -SIGAR_DECLARE(void) sigar_getline_completer_set(sigar_getline_completer_t func) -{ - if (func) { - gl_tab_hook = func; - } - else { - gl_tab_hook = gl_tab; - } -} - -SIGAR_DECLARE(void) -sigar_getline_histinit(char *file) -{ - char line[256]; - FILE *fp; - int nline = 1; /* prevent from becoming 0 */ - - gl_savehist = 0; - - hist_init(); - - if (!strcmp(file, "-")) return; - - sprintf(gl_histfile, "%s", file); - - fp = fopen(gl_histfile, "r"); - if (fp) - while (fgets(line, 256, fp)) { - nline++; - sigar_getline_histadd(line); - } - else - fp = fopen(gl_histfile, "w"); - - if (fp) fclose(fp); - - gl_savehist = nline; -} - -SIGAR_DECLARE(void) -sigar_getline_histadd(char *buf) -{ - static char *prev = 0; - char *p = buf; - int len; - - while (*p == ' ' || *p == '\t' || *p == '\n') - p++; - if (*p) { - len = strlen(buf); - if (strchr(p, '\n')) /* previously line already has NL stripped */ - len--; - if (prev == 0 || strlen(prev) != len || - strncmp(prev, buf, len) != 0) { - hist_buf[hist_last] = hist_save(buf); - prev = hist_buf[hist_last]; - hist_last = (hist_last + 1) % HIST_SIZE; - if (hist_buf[hist_last] && *hist_buf[hist_last]) { - free(hist_buf[hist_last]); - } - hist_buf[hist_last] = ""; - - /* append command to history file */ - if (gl_savehist) { - FILE *fp; - fp = fopen(gl_histfile, "a+"); - if (fp) { - fprintf(fp, "%s\n", prev); - gl_savehist++; - fclose(fp); - } - - /* if more than HIST_SIZE lines, safe last 60 command and delete rest */ - if (gl_savehist > HIST_SIZE) { - FILE *ftmp; - char tname[L_tmpnam]; - char line[BUFSIZ]; - - fp = fopen(gl_histfile, "r"); - tmpnam(tname); - ftmp = fopen(tname, "w"); - if (fp && ftmp) { - int nline = 0; - while (fgets(line, BUFSIZ, fp)) { - nline++; - gl_savehist = 1; /* prevent from becoming 0 */ - if (nline > HIST_SIZE-60) { - gl_savehist++; - fprintf(ftmp, "%s", line); - } - } - } - if (fp) fclose(fp); - if (ftmp) fclose(ftmp); - - /* copy back to history file */ - fp = fopen(gl_histfile, "w"); - ftmp = fopen(tname, "r"); - if (fp && ftmp) - while (fgets(line, BUFSIZ, ftmp)) - fprintf(fp, "%s", line); - - if (fp) fclose(fp); - if (ftmp) fclose(ftmp); - remove(tname); - } - } - } - } - hist_pos = hist_last; -} - -static char * -hist_prev() -/* loads previous hist entry into input buffer, sticks on first */ -{ - char *p = 0; - int next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE; - - if (hist_buf[hist_pos] != 0 && next != hist_last) { - hist_pos = next; - p = hist_buf[hist_pos]; - } - if (p == 0) { - p = ""; - gl_bell(); - } - return p; -} - -static char * -hist_next() -/* loads next hist entry into input buffer, clears on last */ -{ - char *p = 0; - - if (hist_pos != hist_last) { - hist_pos = (hist_pos+1) % HIST_SIZE; - p = hist_buf[hist_pos]; - } - if (p == 0) { - p = ""; - gl_bell(); - } - return p; -} - -static char * -hist_save(char *p) -/* makes a copy of the string */ -{ - char *s = 0; - int len = strlen(p); - char *nl = strchr(p, '\n'); - - if (nl) { - if ((s = (char *)malloc(len)) != 0) { - strncpy(s, p, len-1); - s[len-1] = 0; - } - } else { - if ((s = (char *)malloc(len+1)) != 0) { - strcpy(s, p); - } - } - if (s == 0) - gl_error("\n*** Error: hist_save() failed on malloc\n"); - return s; -} - -/******************* Search stuff **************************************/ - -static char search_prompt[101]; /* prompt includes search string */ -static char search_string[100]; -static int search_pos = 0; /* current location in search_string */ -static int search_forw_flg = 0; /* search direction flag */ -static int search_last = 0; /* last match found */ - -static void -search_update(int c) -{ - if (c == 0) { - search_pos = 0; - search_string[0] = 0; - search_prompt[0] = '?'; - search_prompt[1] = ' '; - search_prompt[2] = 0; - } else if (c > 0) { - search_string[search_pos] = c; - search_string[search_pos+1] = 0; - search_prompt[search_pos] = c; - search_prompt[search_pos+1] = '?'; - search_prompt[search_pos+2] = ' '; - search_prompt[search_pos+3] = 0; - search_pos++; - } else { - if (search_pos > 0) { - search_pos--; - search_string[search_pos] = 0; - search_prompt[search_pos] = '?'; - search_prompt[search_pos+1] = ' '; - search_prompt[search_pos+2] = 0; - } else { - gl_bell(); - hist_pos = hist_last; - } - } -} - -static void -search_addchar(int c) -{ - char *loc; - - search_update(c); - if (c < 0) { - if (search_pos > 0) { - hist_pos = search_last; - } else { - gl_buf[0] = 0; - hist_pos = hist_last; - } - strcpy(gl_buf, hist_buf[hist_pos]); - } - if ((loc = strstr(gl_buf, search_string)) != 0) { - gl_fixup(search_prompt, 0, loc - gl_buf); - } else if (search_pos > 0) { - if (search_forw_flg) { - search_forw(0); - } else { - search_back(0); - } - } else { - gl_fixup(search_prompt, 0, 0); - } -} - -static void -search_term() -{ - gl_search_mode = 0; - if (gl_buf[0] == 0) /* not found, reset hist list */ - hist_pos = hist_last; - if (gl_in_hook) - gl_in_hook(gl_buf); - gl_fixup(gl_prompt, 0, gl_pos); -} - -static void -search_back(int new_search) -{ - int found = 0; - char *p, *loc; - - search_forw_flg = 0; - if (gl_search_mode == 0) { - search_last = hist_pos = hist_last; - search_update(0); - gl_search_mode = 1; - gl_buf[0] = 0; - gl_fixup(search_prompt, 0, 0); - } else if (search_pos > 0) { - while (!found) { - p = hist_prev(); - if (*p == 0) { /* not found, done looking */ - gl_buf[0] = 0; - gl_fixup(search_prompt, 0, 0); - found = 1; - } else if ((loc = strstr(p, search_string)) != 0) { - strcpy(gl_buf, p); - gl_fixup(search_prompt, 0, loc - p); - if (new_search) - search_last = hist_pos; - found = 1; - } - } - } else { - gl_bell(); - } -} - -static void -search_forw(int new_search) -{ - int found = 0; - char *p, *loc; - - search_forw_flg = 1; - if (gl_search_mode == 0) { - search_last = hist_pos = hist_last; - search_update(0); - gl_search_mode = 1; - gl_buf[0] = 0; - gl_fixup(search_prompt, 0, 0); - } else if (search_pos > 0) { - while (!found) { - p = hist_next(); - if (*p == 0) { /* not found, done looking */ - gl_buf[0] = 0; - gl_fixup(search_prompt, 0, 0); - found = 1; - } else if ((loc = strstr(p, search_string)) != 0) { - strcpy(gl_buf, p); - gl_fixup(search_prompt, 0, loc - p); - if (new_search) - search_last = hist_pos; - found = 1; - } - } - } else { - gl_bell(); - } -} -#if 0 -/*********************************************************************** - * * - * Strip blanks from both sides of a string. Space for the new * - * string is allocated and a pointer to it is returned. * - * * - ***********************************************************************/ -char *strip(char *s) -{ - char *r, *t1, *t2; - int l; - - l = strlen(s); - r = (char *)calloc(l+1, 1); - - if (l == 0) { - *r = '\0'; - return r; - } - - /* get rid of leading blanks */ - t1 = s; - while (*t1 == ' ') - t1++; - - t2 = s + l - 1; - while (*t2 == ' ' && t2 > s) - t2--; - - if (t1 > t2) { - *r = '\0'; - return r; - } - strncpy(r, t1, (size_t) (t2-t1+1)); - - return r; -} -#endif -/*****************************************************************************/ -/* Extra routine provided by Christian Lacunza */ -/*****************************************************************************/ - -/* move cursor back to beginning of _current_ word */ -/* unless it's already at the beginning, */ -/* in which case it moves back to the beginning */ -/* of the _previous_ word. */ -static void gl_back_1_word( void ) -{ - int i = gl_pos; - - /* if we're at the beginning of a word, */ - /* slip back into the preceeding whitespace */ - if( i>0 && is_whitespace(gl_buf[i-1]) ) { - i-=1; - } - - /* now move back over all consecutive whitespace */ - while( i>0 && is_whitespace(gl_buf[i]) ) { - i-=1; - } - - /* now keep moving back over all consecutive non-whitespace */ - /* until we find the beginning of this word. */ - /* ie. stop just before more whitespace shows up. */ - while( i>0 && !is_whitespace(gl_buf[i-1]) ) { - i-=1; - } - - /* move the cursor here */ - gl_fixup(gl_prompt, -1, i); -} - -/* kills from current position to end of word */ -static void gl_kill_1_word( void ) -{ - int i = gl_pos; - int j = gl_pos; - -/* delete this: */ -#if 0 - /* not sure what to do with "punctuation" */ - if( is_whitespace(gl_buf[j]) && gl_buf[j]!=' ' ) { - return; - } - /* first find a word */ - while( j - -#ifdef SIGAR_HAS_PCRE -#include "pcre.h" -#endif - -/* See http://gcc.gnu.org/ml/libstdc++/2002-03/msg00164.html */ -#if defined(WIN32) || (defined(__hpux) && defined(SIGAR_64BIT)) -#define strtoull strtoul -#elif (defined(__hpux) && !defined(SIGAR_64BIT)) -#define strtoull __strtoull -#else -#include -#endif - -#define SIGAR_CLEAR_ERRNO() errno = 0 - -#define strtonum_failed(src, ptr) \ - ((src == ptr) || (errno == ERANGE) || (*ptr != '\0')) - -typedef struct ptql_parse_branch_t ptql_parse_branch_t; -typedef struct ptql_branch_t ptql_branch_t; - -/* adhere to calling convention, else risk stack corruption */ -#ifdef WIN32 -#define SIGAPI WINAPI -#else -#define SIGAPI -#endif - -typedef int (SIGAPI *ptql_get_t)(sigar_t *sigar, sigar_pid_t pid, void *data); -typedef int (*ptql_branch_init_t)(ptql_parse_branch_t *parsed, ptql_branch_t *branch, - sigar_ptql_error_t *error); - -typedef int (*ptql_op_ui64_t)(ptql_branch_t *branch, - sigar_uint64_t haystack, - sigar_uint64_t needle); - -typedef int (*ptql_op_ui32_t)(ptql_branch_t *branch, - sigar_uint32_t haystack, - sigar_uint32_t needle); - -typedef int (*ptql_op_dbl_t)(ptql_branch_t *branch, - double haystack, - double needle); - -typedef int (*ptql_op_str_t)(ptql_branch_t *branch, - char *haystack, - char *needle); - -typedef int (*ptql_op_chr_t)(ptql_branch_t *branch, - char haystack, - char needle); - -typedef enum { - PTQL_VALUE_TYPE_UI64, - PTQL_VALUE_TYPE_UI32, - PTQL_VALUE_TYPE_DBL, - PTQL_VALUE_TYPE_CHR, - PTQL_VALUE_TYPE_STR, - PTQL_VALUE_TYPE_ANY -} ptql_value_type_t; - -typedef enum { - PTQL_OP_EQ, - PTQL_OP_NE, - PTQL_OP_GT, - PTQL_OP_GE, - PTQL_OP_LT, - PTQL_OP_LE, -#define PTQL_OP_MAX_NSTR PTQL_OP_LE - PTQL_OP_EW, /* rest are string only */ - PTQL_OP_SW, - PTQL_OP_RE, - PTQL_OP_CT, - PTQL_OP_MAX -} ptql_op_name_t; - -#define PTQL_OP_FLAG_PARENT 1 -#define PTQL_OP_FLAG_REF 2 -#define PTQL_OP_FLAG_GLOB 4 -#define PTQL_OP_FLAG_PID 8 -#define PTQL_OP_FLAG_ICASE 16 - -struct ptql_parse_branch_t { - char *name; - char *attr; - char *op; - char *value; - unsigned int op_flags; -}; - -typedef struct { - char *name; - ptql_get_t get; - size_t offset; - unsigned int data_size; - ptql_value_type_t type; - ptql_branch_init_t init; -} ptql_lookup_t; - -#define DATA_PTR(branch) \ - ((char *)branch->data.ptr + branch->lookup->offset) - -#define IS_ICASE(branch) \ - (branch->op_flags & PTQL_OP_FLAG_ICASE) - -#define branch_strcmp(branch, s1, s2) \ - (IS_ICASE(branch) ? strcasecmp(s1, s2) : strcmp(s1, s2)) - -#define branch_strncmp(branch, s1, s2, n) \ - (IS_ICASE(branch) ? strncasecmp(s1, s2, n) : strncmp(s1, s2, n)) - -#define branch_strEQ(branch, s1, s2) \ - (branch_strcmp(branch, s1, s2) == 0) - -#define branch_strnEQ(branch, s1, s2, n) \ - (branch_strncmp(branch, s1, s2, n) == 0) - -#define branch_strstr(branch, s1, s2) \ - (IS_ICASE(branch) ? sigar_strcasestr(s1, s2) : strstr(s1, s2)) - -#define IS_PID_SERVICE_QUERY(branch) \ - (branch->flags >= PTQL_PID_SERVICE_NAME) - -static void data_free(void *data) -{ - free(data); -} - -typedef union { - sigar_pid_t pid; - sigar_uint64_t ui64; - sigar_uint32_t ui32; - double dbl; - char chr[4]; - char *str; - void *ptr; -} any_value_t; - -struct ptql_branch_t { - ptql_lookup_t *lookup; - any_value_t data; - unsigned int data_size; - void (*data_free)(void *); - unsigned int flags; - unsigned int op_flags; - ptql_op_name_t op_name; - union { - ptql_op_ui64_t ui64; - ptql_op_ui32_t ui32; - ptql_op_dbl_t dbl; - ptql_op_chr_t chr; - ptql_op_str_t str; - } match; - any_value_t value; - void (*value_free)(void *); -}; - -typedef struct { - char *name; - ptql_lookup_t *members; -} ptql_entry_t; - -typedef struct { - unsigned long number; - unsigned long size; - ptql_branch_t *data; -} ptql_branch_list_t; - -struct sigar_ptql_query_t { - ptql_branch_list_t branches; -#ifdef PTQL_DEBUG - char *ptql; -#endif -}; - -/* XXX optimize */ -static ptql_op_name_t ptql_op_code_get(char *op) -{ - if (strEQ(op, "eq")) { - return PTQL_OP_EQ; - } - else if (strEQ(op, "ne")) { - return PTQL_OP_NE; - } - else if (strEQ(op, "gt")) { - return PTQL_OP_GT; - } - else if (strEQ(op, "ge")) { - return PTQL_OP_GE; - } - else if (strEQ(op, "lt")) { - return PTQL_OP_LT; - } - else if (strEQ(op, "le")) { - return PTQL_OP_LE; - } - else if (strEQ(op, "ew")) { - return PTQL_OP_EW; - } - else if (strEQ(op, "sw")) { - return PTQL_OP_SW; - } - else if (strEQ(op, "re")) { - return PTQL_OP_RE; - } - else if (strEQ(op, "ct")) { - return PTQL_OP_CT; - } - else { - return PTQL_OP_MAX; - } -} - -static int ptql_op_ui64_eq(ptql_branch_t *branch, - sigar_uint64_t haystack, sigar_uint64_t needle) -{ - return haystack == needle; -} - -static int ptql_op_ui64_ne(ptql_branch_t *branch, - sigar_uint64_t haystack, sigar_uint64_t needle) -{ - return haystack != needle; -} - -static int ptql_op_ui64_gt(ptql_branch_t *branch, - sigar_uint64_t haystack, sigar_uint64_t needle) -{ - return haystack > needle; -} - -static int ptql_op_ui64_ge(ptql_branch_t *branch, - sigar_uint64_t haystack, sigar_uint64_t needle) -{ - return haystack >= needle; -} - -static int ptql_op_ui64_lt(ptql_branch_t *branch, - sigar_uint64_t haystack, sigar_uint64_t needle) -{ - return haystack < needle; -} - -static int ptql_op_ui64_le(ptql_branch_t *branch, - sigar_uint64_t haystack, sigar_uint64_t needle) -{ - return haystack <= needle; -} - -static ptql_op_ui64_t ptql_op_ui64[] = { - ptql_op_ui64_eq, - ptql_op_ui64_ne, - ptql_op_ui64_gt, - ptql_op_ui64_ge, - ptql_op_ui64_lt, - ptql_op_ui64_le -}; - -static int ptql_op_ui32_eq(ptql_branch_t *branch, - sigar_uint32_t haystack, sigar_uint32_t needle) -{ - return haystack == needle; -} - -static int ptql_op_ui32_ne(ptql_branch_t *branch, - sigar_uint32_t haystack, sigar_uint32_t needle) -{ - return haystack != needle; -} - -static int ptql_op_ui32_gt(ptql_branch_t *branch, - sigar_uint32_t haystack, sigar_uint32_t needle) -{ - return haystack > needle; -} - -static int ptql_op_ui32_ge(ptql_branch_t *branch, - sigar_uint32_t haystack, sigar_uint32_t needle) -{ - return haystack >= needle; -} - -static int ptql_op_ui32_lt(ptql_branch_t *branch, - sigar_uint32_t haystack, sigar_uint32_t needle) -{ - return haystack < needle; -} - -static int ptql_op_ui32_le(ptql_branch_t *branch, - sigar_uint32_t haystack, sigar_uint32_t needle) -{ - return haystack <= needle; -} - -static ptql_op_ui32_t ptql_op_ui32[] = { - ptql_op_ui32_eq, - ptql_op_ui32_ne, - ptql_op_ui32_gt, - ptql_op_ui32_ge, - ptql_op_ui32_lt, - ptql_op_ui32_le -}; - -static int ptql_op_dbl_eq(ptql_branch_t *branch, - double haystack, double needle) -{ - return haystack == needle; -} - -static int ptql_op_dbl_ne(ptql_branch_t *branch, - double haystack, double needle) -{ - return haystack != needle; -} - -static int ptql_op_dbl_gt(ptql_branch_t *branch, - double haystack, double needle) -{ - return haystack > needle; -} - -static int ptql_op_dbl_ge(ptql_branch_t *branch, - double haystack, double needle) -{ - return haystack >= needle; -} - -static int ptql_op_dbl_lt(ptql_branch_t *branch, - double haystack, double needle) -{ - return haystack < needle; -} - -static int ptql_op_dbl_le(ptql_branch_t *branch, - double haystack, double needle) -{ - return haystack <= needle; -} - -static ptql_op_dbl_t ptql_op_dbl[] = { - ptql_op_dbl_eq, - ptql_op_dbl_ne, - ptql_op_dbl_gt, - ptql_op_dbl_ge, - ptql_op_dbl_lt, - ptql_op_dbl_le -}; - -static int ptql_op_str_eq(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strEQ(branch, haystack, needle); -} - -static int ptql_op_str_ne(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return !branch_strEQ(branch, haystack, needle); -} - -static int ptql_op_str_gt(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strcmp(branch, haystack, needle) > 0; -} - -static int ptql_op_str_ge(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strcmp(branch, haystack, needle) >= 0; -} - -static int ptql_op_str_lt(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strcmp(branch, haystack, needle) < 0; -} - -static int ptql_op_str_le(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strcmp(branch, haystack, needle) <= 0; -} - -static int ptql_op_str_ew(ptql_branch_t *branch, - char *haystack, char *needle) -{ - int nlen = strlen(needle); - int hlen = strlen(haystack); - int diff = hlen - nlen; - if (diff < 0) { - return 0; - } - return branch_strnEQ(branch, haystack + diff, needle, nlen); -} - -static int ptql_op_str_sw(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strnEQ(branch, haystack, needle, strlen(needle)); -} - -static int ptql_op_str_re(ptql_branch_t *branch, - char *haystack, char *needle) -{ -#ifdef SIGAR_HAS_PCRE - pcre *re = (pcre *)branch->value.ptr; - int len = strlen(haystack); - int rc = - pcre_exec(re, NULL, haystack, len, 0, 0, NULL, 0); - return rc >= 0; -#else - return 0; -#endif -} - -static int ptql_op_str_ct(ptql_branch_t *branch, - char *haystack, char *needle) -{ - return branch_strstr(branch, haystack, needle) != NULL; -} - -static ptql_op_str_t ptql_op_str[] = { - ptql_op_str_eq, - ptql_op_str_ne, - ptql_op_str_gt, - ptql_op_str_ge, - ptql_op_str_lt, - ptql_op_str_le, - ptql_op_str_ew, - ptql_op_str_sw, - ptql_op_str_re, - ptql_op_str_ct -}; - -static int ptql_op_chr_eq(ptql_branch_t *branch, - char haystack, char needle) -{ - return haystack == needle; -} - -static int ptql_op_chr_ne(ptql_branch_t *branch, - char haystack, char needle) -{ - return haystack != needle; -} - -static int ptql_op_chr_gt(ptql_branch_t *branch, - char haystack, char needle) -{ - return haystack > needle; -} - -static int ptql_op_chr_ge(ptql_branch_t *branch, - char haystack, char needle) -{ - return haystack >= needle; -} - -static int ptql_op_chr_lt(ptql_branch_t *branch, - char haystack, char needle) -{ - return haystack < needle; -} - -static int ptql_op_chr_le(ptql_branch_t *branch, - char haystack, char needle) -{ - return haystack <= needle; -} - -static ptql_op_chr_t ptql_op_chr[] = { - ptql_op_chr_eq, - ptql_op_chr_ne, - ptql_op_chr_gt, - ptql_op_chr_ge, - ptql_op_chr_lt, - ptql_op_chr_le -}; - -#define PTQL_BRANCH_LIST_MAX 3 - -#define PTQL_BRANCH_LIST_GROW(branches) \ - if ((branches)->number >= (branches)->size) { \ - ptql_branch_list_grow(branches); \ - } - -static int ptql_branch_list_create(ptql_branch_list_t *branches) -{ - branches->number = 0; - branches->size = PTQL_BRANCH_LIST_MAX; - branches->data = malloc(sizeof(*(branches->data)) * - branches->size); - - return SIGAR_OK; -} - -static int ptql_branch_list_grow(ptql_branch_list_t *branches) -{ - branches->data = - realloc(branches->data, - sizeof(*(branches->data)) * - (branches->size + PTQL_BRANCH_LIST_MAX)); - branches->size += PTQL_BRANCH_LIST_MAX; - - return SIGAR_OK; -} - -static int ptql_branch_list_destroy(ptql_branch_list_t *branches) -{ - if (branches->size) { - int i; - - for (i=0; inumber; i++) { - ptql_branch_t *branch = - &branches->data[i]; - - if (branch->data_size && branch->data.ptr) { - branch->data_free(branch->data.ptr); - } - - if (branch->lookup && - ((branch->lookup->type == PTQL_VALUE_TYPE_STR) || - (branch->lookup->type == PTQL_VALUE_TYPE_ANY)) && - !(branch->op_flags & PTQL_OP_FLAG_REF)) - { - if (branch->value.str) { - branch->value_free(branch->value.str); - } - } - } - - free(branches->data); - branches->number = branches->size = 0; - } - - return SIGAR_OK; -} - -#ifdef WIN32 -#define vsnprintf _vsnprintf -#endif - -#define PTQL_ERRNAN \ - ptql_error(error, "Query value '%s' is not a number", parsed->value) - -static int ptql_error(sigar_ptql_error_t *error, const char *format, ...) -{ - va_list args; - - if (error != NULL) { - va_start(args, format); - vsnprintf(error->message, sizeof(error->message), format, args); - va_end(args); - } - - return SIGAR_PTQL_MALFORMED_QUERY; -} - -static int ptql_branch_init_any(ptql_parse_branch_t *parsed, - ptql_branch_t *branch, - sigar_ptql_error_t *error) -{ - branch->data.str = sigar_strdup(parsed->attr); - branch->data_size = strlen(parsed->attr); - return SIGAR_OK; -} - -static int ptql_str_match(sigar_t *sigar, ptql_branch_t *branch, char *value) -{ - if (!branch->value.str) { - return 0; - } -#ifndef SIGAR_HAS_PCRE - if (branch->op_name == PTQL_OP_RE) { - if (sigar->ptql_re_impl) { - return sigar->ptql_re_impl(sigar->ptql_re_data, - value, - branch->value.str); - } - else { - return 0; - } - } -#endif - return branch->match.str(branch, - value, - branch->value.str); -} - -static int ptql_branch_match(ptql_branch_t *branch) -{ - switch (branch->lookup->type) { - case PTQL_VALUE_TYPE_UI64: - return branch->match.ui64(branch, - *(sigar_uint64_t *)DATA_PTR(branch), - branch->value.ui64); - case PTQL_VALUE_TYPE_UI32: - return branch->match.ui32(branch, - *(sigar_uint32_t *)DATA_PTR(branch), - branch->value.ui32); - case PTQL_VALUE_TYPE_DBL: - return branch->match.dbl(branch, - *(double *)DATA_PTR(branch), - branch->value.dbl); - case PTQL_VALUE_TYPE_CHR: - return branch->match.chr(branch, - *(char *)DATA_PTR(branch), - branch->value.chr[0]); - case PTQL_VALUE_TYPE_STR: - case PTQL_VALUE_TYPE_ANY: - if (!branch->value.str) { - return 0; - } - return branch->match.str(branch, - (char *)DATA_PTR(branch), - branch->value.str); - default: - return 0; - } -} - -static int ptql_branch_match_ref(ptql_branch_t *branch, ptql_branch_t *ref) -{ - switch (branch->lookup->type) { - case PTQL_VALUE_TYPE_UI64: - return branch->match.ui64(branch, - *(sigar_uint64_t *)DATA_PTR(branch), - *(sigar_uint64_t *)DATA_PTR(ref)); - case PTQL_VALUE_TYPE_UI32: - return branch->match.ui32(branch, - *(sigar_uint32_t *)DATA_PTR(branch), - *(sigar_uint32_t *)DATA_PTR(ref)); - case PTQL_VALUE_TYPE_DBL: - return branch->match.dbl(branch, - *(double *)DATA_PTR(branch), - *(double *)DATA_PTR(ref)); - case PTQL_VALUE_TYPE_CHR: - return branch->match.chr(branch, - *(char *)DATA_PTR(branch), - *(char *)DATA_PTR(ref)); - case PTQL_VALUE_TYPE_STR: - case PTQL_VALUE_TYPE_ANY: - return branch->match.str(branch, - (char *)DATA_PTR(branch), - (char *)DATA_PTR(ref)); - default: - return 0; - } -} - -enum { - PTQL_PID_PID, - PTQL_PID_FILE, - PTQL_PID_SUDO_FILE, - PTQL_PID_TCP_PORT, - PTQL_PID_UDP_PORT, - PTQL_PID_SERVICE_NAME, - PTQL_PID_SERVICE_DISPLAY, - PTQL_PID_SERVICE_PATH, - PTQL_PID_SERVICE_EXE, - PTQL_PID_SERVICE_PID -}; - -#ifdef SIGAR_64BIT - -#define str2pid(value, ptr) strtoull(value, &ptr, 10) - -#define pid_branch_match(branch, pid, match_pid) \ - ptql_op_ui64[branch->op_name](branch, pid, match_pid) - -#else - -#define str2pid(value, ptr) strtoul(value, &ptr, 10) - -#define pid_branch_match(branch, pid, match_pid) \ - ptql_op_ui32[branch->op_name](branch, pid, match_pid) - -#endif - -#ifndef WIN32 -#include -int sigar_sudo_file2str(const char *fname, char *buffer, int buflen) -{ - FILE *fp; - struct stat sb; - - if (stat(fname, &sb) < 0) { - return errno; - } - if (sb.st_size > buflen) { - return ENOMEM; - } - snprintf(buffer, buflen, "sudo cat %s", fname); - if (!(fp = popen(buffer, "r"))) { - return errno; - } - (void)fgets(buffer, buflen, fp); - pclose(fp); - - return SIGAR_OK; -} -#endif - -static int ptql_branch_init_service(ptql_parse_branch_t *parsed, - ptql_branch_t *branch, - sigar_ptql_error_t *error) -{ - branch->op_flags |= PTQL_OP_FLAG_PID; - - if (strEQ(parsed->attr, "Name")) { - branch->flags = PTQL_PID_SERVICE_NAME; - } - else if (strEQ(parsed->attr, "DisplayName")) { - branch->flags = PTQL_PID_SERVICE_DISPLAY; - } - else if (strEQ(parsed->attr, "Path")) { - branch->flags = PTQL_PID_SERVICE_PATH; - } - else if (strEQ(parsed->attr, "Exe")) { - /* basename of Path */ - branch->flags = PTQL_PID_SERVICE_EXE; - } - else if (strEQ(parsed->attr, "Pid")) { - branch->flags = PTQL_PID_SERVICE_PID; - } - else { - return ptql_error(error, "Unsupported %s attribute: %s", - parsed->name, parsed->attr); - } - -#ifdef WIN32 - branch->data.str = sigar_strdup(parsed->value); - branch->data_size = strlen(parsed->value); -#endif - return SIGAR_OK; -} - -static int ptql_branch_init_pid(ptql_parse_branch_t *parsed, - ptql_branch_t *branch, - sigar_ptql_error_t *error) -{ - int use_sudo = 0; - branch->op_flags |= PTQL_OP_FLAG_PID; - - if (strEQ(parsed->attr, "Pid")) { - branch->flags = PTQL_PID_PID; - if (strEQ(parsed->value, "$$")) { - branch->data.pid = getpid(); - } - else { - char *ptr; - SIGAR_CLEAR_ERRNO(); - branch->data.pid = str2pid(parsed->value, ptr); - if (strtonum_failed(parsed->value, ptr)) { - return PTQL_ERRNAN; - } - } - return SIGAR_OK; - } - else if (strEQ(parsed->attr, "PidFile") || - (use_sudo = strEQ(parsed->attr, "SudoPidFile"))) - { - branch->flags = use_sudo ? PTQL_PID_SUDO_FILE : PTQL_PID_FILE; - branch->data.str = sigar_strdup(parsed->value); - branch->data_size = strlen(parsed->value); - return SIGAR_OK; - } - - return ptql_error(error, "Unsupported %s attribute: %s", - parsed->name, parsed->attr); -} - -#ifdef WIN32 -#define QUERY_SC_SIZE 8192 - -static int ptql_service_query_config(SC_HANDLE scm_handle, - char *name, - LPQUERY_SERVICE_CONFIG config) -{ - int status; - DWORD bytes; - SC_HANDLE handle = - OpenService(scm_handle, name, SERVICE_QUERY_CONFIG); - - if (!handle) { - return GetLastError(); - } - - if (QueryServiceConfig(handle, config, QUERY_SC_SIZE, &bytes)) { - status = SIGAR_OK; - } - else { - status = GetLastError(); - } - - CloseServiceHandle(handle); - return status; -} - -static int sigar_services_walk(sigar_services_walker_t *walker, - ptql_branch_t *branch) -{ - sigar_services_status_t ss; - char buffer[QUERY_SC_SIZE]; - char exe[SIGAR_CMDLINE_MAX]; - LPQUERY_SERVICE_CONFIG config = (LPQUERY_SERVICE_CONFIG)buffer; - DWORD i, status; - - SIGAR_ZERO(&ss); - status = sigar_services_status_get(&ss, walker->flags); - if (status != SIGAR_OK) { - return status; - } - for (i=0; iadd_service(walker, name) != SIGAR_OK) { - break; - } - continue; - } - - switch (branch->flags) { - case PTQL_PID_SERVICE_DISPLAY: - value = ss.services[i].lpDisplayName; - break; - case PTQL_PID_SERVICE_PATH: - case PTQL_PID_SERVICE_EXE: - status = ptql_service_query_config(ss.handle, name, config); - if (status == SIGAR_OK) { - if (branch->flags == PTQL_PID_SERVICE_EXE) { - value = - sigar_service_exe_get(config->lpBinaryPathName, - exe, 1); - } - else { - value = config->lpBinaryPathName; - } - } - else { - continue; - } - break; - case PTQL_PID_SERVICE_PID: - sigar_service_pid_get(walker->sigar, - name, - &service_pid); - break; - case PTQL_PID_SERVICE_NAME: - default: - value = name; - break; - } - - if ((value && ptql_str_match(walker->sigar, branch, value)) || - (service_pid && - pid_branch_match(branch, service_pid, atoi(branch->data.str)))) - { - if (walker->add_service(walker, name) != SIGAR_OK) { - break; - } - } - } - - sigar_services_status_close(&ss); - - return SIGAR_OK; -} - -static int ptql_pid_service_add(sigar_services_walker_t *walker, - char *name) -{ - sigar_pid_t service_pid; - sigar_proc_list_t *proclist = - (sigar_proc_list_t *)walker->data; - int status = - sigar_service_pid_get(walker->sigar, - name, - &service_pid); - - if (status == SIGAR_OK) { - SIGAR_PROC_LIST_GROW(proclist); - proclist->data[proclist->number++] = service_pid; - } - - return SIGAR_OK; -} - -static int ptql_pid_service_list_get(sigar_t *sigar, - ptql_branch_t *branch, - sigar_proc_list_t *proclist) -{ - sigar_services_walker_t walker; - walker.sigar = sigar; - walker.flags = SERVICE_ACTIVE; - walker.data = proclist; - walker.add_service = ptql_pid_service_add; - - return sigar_services_walk(&walker, branch); -} - -int sigar_services_query(char *ptql, - sigar_ptql_error_t *error, - sigar_services_walker_t *walker) -{ - int status; - sigar_ptql_query_t *query; - - if (ptql == NULL) { - return sigar_services_walk(walker, NULL); - } - - status = sigar_ptql_query_create(&query, (char *)ptql, error); - if (status != SIGAR_OK) { - return status; - } - - if (query->branches.number == 1) { - ptql_branch_t *branch = &query->branches.data[0]; - - if (IS_PID_SERVICE_QUERY(branch)) { - status = sigar_services_walk(walker, branch); - } - else { - ptql_error(error, "Invalid Service query: %s", ptql); - status = SIGAR_PTQL_MALFORMED_QUERY; - } - } - else { - ptql_error(error, "Too many queries (%d), must be (1)", - query->branches.number); - status = SIGAR_PTQL_MALFORMED_QUERY; - } - - sigar_ptql_query_destroy(query); - - return status; -} -#endif - -static int ptql_pid_port_get(sigar_t *sigar, - ptql_branch_t *branch, - sigar_pid_t *pid) -{ - unsigned long port = - branch->data.ui32; - int status; - int proto = - branch->flags == PTQL_PID_UDP_PORT ? - SIGAR_NETCONN_UDP : SIGAR_NETCONN_TCP; - - status = - sigar_proc_port_get(sigar, proto, port, pid); - - return status; -} - -static int ptql_pid_get(sigar_t *sigar, - ptql_branch_t *branch, - sigar_pid_t *pid) -{ - if ((branch->flags == PTQL_PID_FILE) || - (branch->flags == PTQL_PID_SUDO_FILE)) - { - char *ptr, buffer[SIGAR_PATH_MAX+1]; - const char *fname = (const char *)branch->data.str; - int status, len = sizeof(buffer)-1; - - if (branch->flags == PTQL_PID_FILE) { - status = sigar_file2str(fname, buffer, len); - } - else { -#ifdef WIN32 - return SIGAR_ENOTIMPL; -#else - status = sigar_sudo_file2str(fname, buffer, len); -#endif - } - if (status != SIGAR_OK) { - return status; - } - SIGAR_CLEAR_ERRNO(); - *pid = str2pid(buffer, ptr); - if ((buffer == ptr) || (errno == ERANGE)) { - return errno; - } - } - else if (branch->flags == PTQL_PID_SERVICE_NAME) { -#ifdef WIN32 - int status = - sigar_service_pid_get(sigar, - branch->data.str, pid); - if (status != SIGAR_OK) { - return status; - } -#else - return SIGAR_ENOTIMPL; -#endif - } - else if ((branch->flags == PTQL_PID_UDP_PORT) || - (branch->flags == PTQL_PID_TCP_PORT)) - { - int status = ptql_pid_port_get(sigar, branch, pid); - if (status != SIGAR_OK) { - return status; - } - } - else { - *pid = branch->data.pid; - } - - return SIGAR_OK; -} - -static int ptql_pid_list_get(sigar_t *sigar, - ptql_branch_t *branch, - sigar_proc_list_t *proclist) -{ - int status, i; - sigar_pid_t match_pid; - - if (IS_PID_SERVICE_QUERY(branch)) { - if ((branch->flags > PTQL_PID_SERVICE_NAME) || - (branch->op_name != PTQL_OP_EQ)) - { -#ifdef WIN32 - return ptql_pid_service_list_get(sigar, branch, proclist); -#else - return SIGAR_OK; /* no matches */ -#endif - } - } - - status = ptql_pid_get(sigar, branch, &match_pid); - - if (status != SIGAR_OK) { - /* XXX treated as non-match but would be nice to propagate */ - return SIGAR_OK; - } - - status = sigar_proc_list_get(sigar, NULL); - if (status != SIGAR_OK) { - return status; - } - for (i=0; ipids->number; i++) { - sigar_pid_t pid = sigar->pids->data[i]; - if (pid_branch_match(branch, pid, match_pid)) { - SIGAR_PROC_LIST_GROW(proclist); - proclist->data[proclist->number++] = pid; - } - } - - return SIGAR_OK; -} - -static int SIGAPI ptql_pid_match(sigar_t *sigar, - sigar_pid_t pid, - void *data) -{ - /* query already used to filter proc_list */ - return SIGAR_OK; -} - -static int ptql_args_branch_init(ptql_parse_branch_t *parsed, - ptql_branch_t *branch, - sigar_ptql_error_t *error) -{ - if (strEQ(parsed->attr, "*")) { - branch->op_flags |= PTQL_OP_FLAG_GLOB; - } - else { - char *end; - - SIGAR_CLEAR_ERRNO(); - branch->data.ui32 = - strtol(parsed->attr, &end, 10); - - if (strtonum_failed(parsed->attr, end)) { - /* conversion failed */ - return ptql_error(error, "%s is not a number", parsed->attr); - } - } - return SIGAR_OK; -} - -static int SIGAPI ptql_args_match(sigar_t *sigar, - sigar_pid_t pid, - void *data) -{ - ptql_branch_t *branch = - (ptql_branch_t *)data; - int status, matched=0; - sigar_proc_args_t args; - - status = sigar_proc_args_get(sigar, pid, &args); - if (status != SIGAR_OK) { - return status; - } - - if (branch->op_flags & PTQL_OP_FLAG_GLOB) { - int i; - for (i=0; idata.ui32; - - /* e.g. find last element of args: Args.-1.eq=weblogic.Server */ - if (num < 0) { - num += args.number; - } - if ((num >= 0) && (num < args.number)) { - matched = - ptql_str_match(sigar, branch, args.data[num]); - } - } - - sigar_proc_args_destroy(sigar, &args); - - return matched ? SIGAR_OK : !SIGAR_OK; -} - -typedef struct { - sigar_t *sigar; - ptql_branch_t *branch; - sigar_uint32_t ix; - int matched; -} proc_modules_match_t; - -static int proc_modules_match(void *data, char *name, int len) -{ - proc_modules_match_t *matcher = - (proc_modules_match_t *)data; - ptql_branch_t *branch = matcher->branch; - - if (branch->op_flags & PTQL_OP_FLAG_GLOB) { /* Modules.*.ct=libc */ - matcher->matched = - ptql_str_match(matcher->sigar, branch, name); - - if (matcher->matched) { - return !SIGAR_OK; /* stop iterating */ - } - } - else { - if (matcher->ix++ == branch->data.ui32) { /* Modules.3.ct=libc */ - matcher->matched = - ptql_str_match(matcher->sigar, branch, name); - return !SIGAR_OK; /* stop iterating */ - } - } - - return SIGAR_OK; -} - -static int SIGAPI ptql_modules_match(sigar_t *sigar, - sigar_pid_t pid, - void *data) -{ - ptql_branch_t *branch = - (ptql_branch_t *)data; - int status; - sigar_proc_modules_t procmods; - proc_modules_match_t matcher; - - matcher.sigar = sigar; - matcher.branch = branch; - matcher.ix = 0; - matcher.matched = 0; - - procmods.module_getter = proc_modules_match; - procmods.data = &matcher; - - status = sigar_proc_modules_get(sigar, pid, &procmods); - - if (status != SIGAR_OK) { - return status; - } - - return matcher.matched ? SIGAR_OK : !SIGAR_OK; -} - -typedef struct { - const char *key; - int klen; - char *val; - int vlen; -} sigar_proc_env_entry_t; - -static int sigar_proc_env_get_key(void *data, - const char *key, int klen, - char *val, int vlen) -{ - sigar_proc_env_entry_t *entry = - (sigar_proc_env_entry_t *)data; - - if ((entry->klen == klen) && - (strcmp(entry->key, key) == 0)) - { - entry->val = val; - entry->vlen = vlen; - return !SIGAR_OK; /* foundit; stop iterating */ - } - - return SIGAR_OK; -} - -static int SIGAPI ptql_env_match(sigar_t *sigar, - sigar_pid_t pid, - void *data) -{ - ptql_branch_t *branch = - (ptql_branch_t *)data; - int status, matched=0; - sigar_proc_env_t procenv; - sigar_proc_env_entry_t entry; - - /* XXX ugh this is klunky */ - entry.key = branch->data.str; - entry.klen = branch->data_size; - entry.val = NULL; - - procenv.type = SIGAR_PROC_ENV_KEY; - procenv.key = branch->data.str; - procenv.klen = branch->data_size; - procenv.env_getter = sigar_proc_env_get_key; - procenv.data = &entry; - - status = sigar_proc_env_get(sigar, pid, &procenv); - if (status != SIGAR_OK) { - return status; - } - else { - if (entry.val) { - matched = - ptql_str_match(sigar, branch, entry.val); - } - } - - return matched ? SIGAR_OK : !SIGAR_OK; -} - -static int ptql_branch_init_port(ptql_parse_branch_t *parsed, - ptql_branch_t *branch, - sigar_ptql_error_t *error) -{ - char *ptr; - - /* only 'eq' is supported here */ - if (branch->op_name != PTQL_OP_EQ) { - return ptql_error(error, "%s requires 'eq' operator", - parsed->name); - } - - if (strEQ(parsed->attr, "tcp")) { - branch->flags = PTQL_PID_TCP_PORT; - } - else if (strEQ(parsed->attr, "udp")) { - branch->flags = PTQL_PID_TCP_PORT; - } - else { - return ptql_error(error, "Unsupported %s protocol: %s", - parsed->name, parsed->attr); - } - - branch->op_flags |= PTQL_OP_FLAG_PID; - SIGAR_CLEAR_ERRNO(); - branch->data.ui32 = strtoul(parsed->value, &ptr, 10); - if (strtonum_failed(parsed->value, ptr)) { - return PTQL_ERRNAN; - } - - return SIGAR_OK; -} - -#define PTQL_LOOKUP_ENTRY(cname, member, type) \ - (ptql_get_t)sigar_##cname##_get, \ - sigar_offsetof(sigar_##cname##_t, member), \ - sizeof(sigar_##cname##_t), \ - PTQL_VALUE_TYPE_##type, \ - NULL - -/* XXX uid/pid can be larger w/ 64bit mode */ -#define PTQL_VALUE_TYPE_PID PTQL_VALUE_TYPE_UI32 -#define PTQL_VALUE_TYPE_UID PTQL_VALUE_TYPE_UI32 - -static ptql_lookup_t PTQL_Time[] = { - { "StartTime", PTQL_LOOKUP_ENTRY(proc_time, start_time, UI64) }, - { "User", PTQL_LOOKUP_ENTRY(proc_time, user, UI64) }, - { "Sys", PTQL_LOOKUP_ENTRY(proc_time, sys, UI64) }, - { "Total", PTQL_LOOKUP_ENTRY(proc_time, total, UI64) }, - { NULL } -}; - -static ptql_lookup_t PTQL_Cpu[] = { - { "StartTime", PTQL_LOOKUP_ENTRY(proc_cpu, start_time, UI64) }, - { "User", PTQL_LOOKUP_ENTRY(proc_cpu, user, UI64) }, - { "Sys", PTQL_LOOKUP_ENTRY(proc_cpu, sys, UI64) }, - { "Total", PTQL_LOOKUP_ENTRY(proc_cpu, total, UI64) }, - { "Percent", PTQL_LOOKUP_ENTRY(proc_cpu, percent, DBL) }, - { NULL } -}; - -static ptql_lookup_t PTQL_CredName[] = { - { "User", PTQL_LOOKUP_ENTRY(proc_cred_name, user, STR) }, - { "Group", PTQL_LOOKUP_ENTRY(proc_cred_name, group, STR) }, - { NULL } -}; - -static ptql_lookup_t PTQL_Mem[] = { - { "Size", PTQL_LOOKUP_ENTRY(proc_mem, size, UI64) }, - { "Resident", PTQL_LOOKUP_ENTRY(proc_mem, resident, UI64) }, - { "Share", PTQL_LOOKUP_ENTRY(proc_mem, share, UI64) }, - { "MinorFaults", PTQL_LOOKUP_ENTRY(proc_mem, minor_faults, UI64) }, - { "MajorFaults", PTQL_LOOKUP_ENTRY(proc_mem, major_faults, UI64) }, - { "PageFaults", PTQL_LOOKUP_ENTRY(proc_mem, page_faults, UI64) }, - { NULL } -}; - -static ptql_lookup_t PTQL_Exe[] = { - { "Name", PTQL_LOOKUP_ENTRY(proc_exe, name, STR) }, - { "Cwd", PTQL_LOOKUP_ENTRY(proc_exe, cwd, STR) }, - { NULL } -}; - -static ptql_lookup_t PTQL_Cred[] = { - { "Uid", PTQL_LOOKUP_ENTRY(proc_cred, uid, UID) }, - { "Gid", PTQL_LOOKUP_ENTRY(proc_cred, gid, UID) }, - { "Euid", PTQL_LOOKUP_ENTRY(proc_cred, euid, UID) }, - { "Egid", PTQL_LOOKUP_ENTRY(proc_cred, egid, UID) }, - { NULL } -}; - -static ptql_lookup_t PTQL_State[] = { - { "State", PTQL_LOOKUP_ENTRY(proc_state, state, CHR) }, - { "Name", PTQL_LOOKUP_ENTRY(proc_state, name, STR) }, - { "Ppid", PTQL_LOOKUP_ENTRY(proc_state, ppid, PID) }, - { "Tty", PTQL_LOOKUP_ENTRY(proc_state, tty, UI32) }, - { "Nice", PTQL_LOOKUP_ENTRY(proc_state, nice, UI32) }, - { "Priority", PTQL_LOOKUP_ENTRY(proc_state, priority, UI32) }, - { "Threads", PTQL_LOOKUP_ENTRY(proc_state, threads, UI64) }, - { "Processor", PTQL_LOOKUP_ENTRY(proc_state, processor, UI32) }, - { NULL } -}; - -static ptql_lookup_t PTQL_Fd[] = { - { "Total", PTQL_LOOKUP_ENTRY(proc_fd, total, UI64) }, - { NULL } -}; - -static ptql_lookup_t PTQL_Args[] = { - { NULL, ptql_args_match, 0, 0, PTQL_VALUE_TYPE_ANY, ptql_args_branch_init } -}; - -static ptql_lookup_t PTQL_Modules[] = { - { NULL, ptql_modules_match, 0, 0, PTQL_VALUE_TYPE_ANY, ptql_args_branch_init } -}; - -static ptql_lookup_t PTQL_Env[] = { - { NULL, ptql_env_match, 0, 0, PTQL_VALUE_TYPE_ANY, ptql_branch_init_any } -}; - -static ptql_lookup_t PTQL_Port[] = { - { NULL, ptql_pid_match, 0, 0, PTQL_VALUE_TYPE_ANY, ptql_branch_init_port } -}; - -static ptql_lookup_t PTQL_Pid[] = { - { NULL, ptql_pid_match, 0, 0, PTQL_VALUE_TYPE_ANY, ptql_branch_init_pid } -}; - -static ptql_lookup_t PTQL_Service[] = { - { NULL, ptql_pid_match, 0, 0, PTQL_VALUE_TYPE_ANY, ptql_branch_init_service } -}; - -static ptql_entry_t ptql_map[] = { - { "Time", PTQL_Time }, - { "Cpu", PTQL_Cpu }, - { "CredName", PTQL_CredName }, - { "Mem", PTQL_Mem }, - { "Exe", PTQL_Exe }, - { "Cred", PTQL_Cred }, - { "State", PTQL_State }, - { "Fd", PTQL_Fd }, - { "Args", PTQL_Args }, - { "Modules", PTQL_Modules }, - { "Env", PTQL_Env }, - { "Port", PTQL_Port }, - { "Pid", PTQL_Pid }, - { "Service", PTQL_Service }, - { NULL } -}; - -static int ptql_branch_parse(char *query, ptql_parse_branch_t *branch, - sigar_ptql_error_t *error) -{ - char *ptr = strchr(query, '='); - if (!ptr) { - return ptql_error(error, "Missing '='"); - } - - branch->op_flags = 0; - - *ptr = '\0'; - branch->value = ++ptr; - - if ((ptr = strchr(query, '.'))) { - *ptr = '\0'; - branch->name = query; - query = ++ptr; - } - else { - return ptql_error(error, "Missing '.'"); - } - - if ((ptr = strchr(query, '.'))) { - *ptr = '\0'; - branch->attr = query; - query = ++ptr; - } - else { - return ptql_error(error, "Missing '.'"); - } - - if (*query) { - char flag; - - while (sigar_isupper((flag = *query))) { - switch (flag) { - case 'P': - branch->op_flags |= PTQL_OP_FLAG_PARENT; - break; - case 'I': - branch->op_flags |= PTQL_OP_FLAG_ICASE; - break; - default: - return ptql_error(error, "Unsupported modifier: %c", flag); - } - - ++query; - } - - branch->op = query; - } - else { - return ptql_error(error, "Missing query"); - } - - /* Pid.Service -> Service.Name */ - if (strEQ(branch->attr, "Service")) { - branch->name = branch->attr; - branch->attr = "Name"; - } - - return SIGAR_OK; -} - -static int ptql_branch_add(ptql_parse_branch_t *parsed, - ptql_branch_list_t *branches, - sigar_ptql_error_t *error) -{ - ptql_branch_t *branch; - ptql_entry_t *entry = NULL; - ptql_lookup_t *lookup = NULL; - int i, is_set=0; - char *ptr; - - PTQL_BRANCH_LIST_GROW(branches); - - branch = &branches->data[branches->number++]; - SIGAR_ZERO(branch); - branch->data_free = data_free; - branch->value_free = data_free; - branch->op_flags = parsed->op_flags; - - branch->op_name = ptql_op_code_get(parsed->op); - if (branch->op_name == PTQL_OP_MAX) { - return ptql_error(error, "Unsupported operator: %s", parsed->op); - } - - for (i=0; ptql_map[i].name; i++) { - if (strEQ(ptql_map[i].name, parsed->name)) { - entry = &ptql_map[i]; - break; - } - } - - if (!entry) { - return ptql_error(error, "Unsupported method: %s", parsed->name); - } - - for (i=0; entry->members[i].name; i++) { - if (strEQ(entry->members[i].name, parsed->attr)) { - lookup = &entry->members[i]; - break; - } - } - - if (!lookup) { - if (entry->members[0].type == PTQL_VALUE_TYPE_ANY) { - /* Args, Env, etc. */ - lookup = &entry->members[0]; - } - else { - return ptql_error(error, "Unsupported %s attribute: %s", - parsed->name, parsed->attr); - } - } - - if (lookup->init) { - int status = lookup->init(parsed, branch, error); - if (status != SIGAR_OK) { - return status; - } - } - - branch->lookup = lookup; - - if ((lookup->type < PTQL_VALUE_TYPE_STR) && - (branch->op_name > PTQL_OP_MAX_NSTR)) - { - return ptql_error(error, "Unsupported operator '%s' for %s.%s", - parsed->op, parsed->name, parsed->attr); - } - - if (*parsed->value == '$') { - is_set = 1; - - if (branch->op_name == PTQL_OP_RE) { - /* not for use with .re */ - return ptql_error(error, "Unsupported operator '%s' with variable %s", - parsed->op, parsed->value); - } - - if (sigar_isdigit(*(parsed->value+1))) { - branch->op_flags |= PTQL_OP_FLAG_REF; - parsed->op_flags = branch->op_flags; /* for use by caller */ - branch->value.ui32 = atoi(parsed->value+1) - 1; - - if (branch->value.ui32 >= branches->number) { - /* out-of-range */ - return ptql_error(error, "Variable %s out of range (%d)", - parsed->value, branches->number); - } - else if (branch->value.ui32 == branches->number-1) { - /* self reference */ - return ptql_error(error, "Variable %s self reference", - parsed->value); - } - } - else { - if ((ptr = getenv(parsed->value+1))) { - branch->value.str = sigar_strdup(ptr); - } - else { - branch->value.str = NULL; - } - } - } - else if (branch->op_name == PTQL_OP_RE) { -#ifdef SIGAR_HAS_PCRE - const char *error; - int offset; - pcre *re = - pcre_compile(parsed->value, 0, - &error, &offset, NULL); - if (!re) { - /* XXX pcre_error ? */ - return ptql_error(error, "Invalid regex"); - } - is_set = 1; - branch->value.ptr = re; - branch->value_free = pcre_free; -#endif - } - - switch (lookup->type) { - case PTQL_VALUE_TYPE_UI64: - branch->match.ui64 = ptql_op_ui64[branch->op_name]; - if (!is_set) { - SIGAR_CLEAR_ERRNO(); - branch->value.ui64 = strtoull(parsed->value, &ptr, 10); - if (strtonum_failed(parsed->value, ptr)) { - return PTQL_ERRNAN; - } - } - break; - case PTQL_VALUE_TYPE_UI32: - branch->match.ui32 = ptql_op_ui32[branch->op_name]; - if (!is_set) { - SIGAR_CLEAR_ERRNO(); - branch->value.ui32 = strtoul(parsed->value, &ptr, 10); - if (strtonum_failed(parsed->value, ptr)) { - return PTQL_ERRNAN; - } - } - break; - case PTQL_VALUE_TYPE_DBL: - branch->match.dbl = ptql_op_dbl[branch->op_name]; - if (!is_set) { - SIGAR_CLEAR_ERRNO(); - branch->value.dbl = strtod(parsed->value, &ptr); - if (strtonum_failed(parsed->value, ptr)) { - return PTQL_ERRNAN; - } - } - break; - case PTQL_VALUE_TYPE_CHR: - branch->match.chr = ptql_op_chr[branch->op_name]; - if (!is_set) { - if (strlen(parsed->value) != 1) { - return ptql_error(error, "%s is not a char", parsed->value); - } - branch->value.chr[0] = parsed->value[0]; - } - break; - case PTQL_VALUE_TYPE_STR: - case PTQL_VALUE_TYPE_ANY: - branch->match.str = ptql_op_str[branch->op_name]; - if (!is_set) { - branch->value.str = sigar_strdup(parsed->value); - } - break; - } - - return SIGAR_OK; -} - -static int ptql_branch_compare(const void *b1, const void *b2) -{ - /* XXX can do better */ - ptql_branch_t *branch1 = (ptql_branch_t *)b1; - ptql_branch_t *branch2 = (ptql_branch_t *)b2; - return - branch1->lookup->type - - branch2->lookup->type; -} - -SIGAR_DECLARE(int) sigar_ptql_query_create(sigar_ptql_query_t **queryp, - char *ptql, - sigar_ptql_error_t *error) -{ - char *ptr, *ptql_copy = sigar_strdup(ptql); - int status = SIGAR_OK; - int has_ref = 0; - sigar_ptql_query_t *query = - *queryp = malloc(sizeof(*query)); - - (void)ptql_error(error, "Malformed query"); - -#ifdef PTQL_DEBUG - query->ptql = sigar_strdup(ptql); -#endif - - ptql = ptql_copy; - - ptql_branch_list_create(&query->branches); - - do { - ptql_parse_branch_t parsed; - - if ((ptr = strchr(ptql, ','))) { - *ptr = '\0'; - } - - status = ptql_branch_parse(ptql, &parsed, error); - if (status == SIGAR_OK) { - status = - ptql_branch_add(&parsed, &query->branches, error); - - if (status != SIGAR_OK) { - break; - } - if (parsed.op_flags & PTQL_OP_FLAG_REF) { - has_ref = 1; - } - } - else { - break; - } - - if (ptr) { - ptql = ++ptr; - } - else { - break; - } - } while (*ptql); - - free(ptql_copy); - - if (status != SIGAR_OK) { - sigar_ptql_query_destroy(query); - *queryp = NULL; - } - else if (!has_ref && (query->branches.number > 1)) { - qsort(query->branches.data, - query->branches.number, - sizeof(query->branches.data[0]), - ptql_branch_compare); - } - - if (status == SIGAR_OK) { - (void)ptql_error(error, "OK"); - } - return status; -} - -SIGAR_DECLARE(int) sigar_ptql_query_destroy(sigar_ptql_query_t *query) -{ -#ifdef PTQL_DEBUG - free(query->ptql); -#endif - ptql_branch_list_destroy(&query->branches); - free(query); - return SIGAR_OK; -} - -SIGAR_DECLARE(void) sigar_ptql_re_impl_set(sigar_t *sigar, void *data, - sigar_ptql_re_impl_t impl) -{ - sigar->ptql_re_data = data; - sigar->ptql_re_impl = impl; -} - -SIGAR_DECLARE(int) sigar_ptql_query_match(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_pid_t query_pid) -{ - int i; - - for (i=0; ibranches.number; i++) { - sigar_pid_t pid = query_pid; - int status, matched=0; - ptql_branch_t *branch = &query->branches.data[i]; - ptql_lookup_t *lookup = branch->lookup; - - if (branch->op_flags & PTQL_OP_FLAG_PARENT) { - sigar_proc_state_t state; - - status = sigar_proc_state_get(sigar, pid, &state); - if (status != SIGAR_OK) { - return status; - } - - pid = state.ppid; - } - - if (lookup->type == PTQL_VALUE_TYPE_ANY) { - /* Args, Env, etc. */ - status = lookup->get(sigar, pid, branch); - if (status == SIGAR_OK) { - matched = 1; - } - } - else { - /* standard sigar_proc_*_get / structptr + offset */ - if (!branch->data.ptr) { - branch->data_size = lookup->data_size; - branch->data.ptr = malloc(branch->data_size); - } - status = lookup->get(sigar, pid, branch->data.ptr); - if (status != SIGAR_OK) { - return status; - } - - if (branch->op_flags & PTQL_OP_FLAG_REF) { - ptql_branch_t *ref = - &query->branches.data[branch->value.ui32]; - - matched = ptql_branch_match_ref(branch, ref); - } -#ifndef SIGAR_HAS_PCRE - else if (branch->lookup->type == PTQL_VALUE_TYPE_STR) { - matched = ptql_str_match(sigar, branch, (char *)DATA_PTR(branch)); - } -#endif - else { - matched = ptql_branch_match(branch); - } - } - - if (!matched) { - return 1; - } - } - - return SIGAR_OK; -} - -static int ptql_proc_list_get(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_proc_list_t **proclist) -{ - int status; - int i; - - *proclist = NULL; - - for (i=0; ibranches.number; i++) { - ptql_branch_t *branch = &query->branches.data[i]; - - if (branch->op_flags & PTQL_OP_FLAG_PID) { - /* pre-filter pid list for Pid.* queries */ - /* XXX multiple Pid.* may result in dups */ - if (*proclist == NULL) { - *proclist = malloc(sizeof(**proclist)); - SIGAR_ZERO(*proclist); - sigar_proc_list_create(*proclist); - } - status = ptql_pid_list_get(sigar, branch, *proclist); - if (status != SIGAR_OK) { - sigar_proc_list_destroy(sigar, *proclist); - free(*proclist); - return status; - } - } - } - - if (*proclist) { - return SIGAR_OK; - } - - status = sigar_proc_list_get(sigar, NULL); - if (status != SIGAR_OK) { - return status; - } - *proclist = sigar->pids; - return SIGAR_OK; -} - -static int ptql_proc_list_destroy(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - if (proclist != sigar->pids) { - sigar_proc_list_destroy(sigar, proclist); - free(proclist); - } - - return SIGAR_OK; -} - -SIGAR_DECLARE(int) sigar_ptql_query_find_process(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_pid_t *pid) -{ - int status; - int i, matches=0; - sigar_proc_list_t *pids; - - status = ptql_proc_list_get(sigar, query, &pids); - if (status != SIGAR_OK) { - return status; - } - - for (i=0; inumber; i++) { - int query_status = - sigar_ptql_query_match(sigar, query, pids->data[i]); - - if (query_status == SIGAR_OK) { - *pid = pids->data[i]; - matches++; - } - else if (query_status == SIGAR_ENOTIMPL) { - /* let caller know query is invalid. */ - status = query_status; - break; - } /* else ok, e.g. permission denied */ - } - - ptql_proc_list_destroy(sigar, pids); - - if (status != SIGAR_OK) { - return status; - } - - if (matches == 1) { - return SIGAR_OK; - } - else if (matches == 0) { - sigar_strerror_set(sigar, - "Query did not match any processes"); - } - else { - sigar_strerror_printf(sigar, - "Query matched multiple processes (%d)", - matches); - } - - return -1; -} - -SIGAR_DECLARE(int) sigar_ptql_query_find(sigar_t *sigar, - sigar_ptql_query_t *query, - sigar_proc_list_t *proclist) -{ - int status; - int i; - sigar_proc_list_t *pids; - - status = ptql_proc_list_get(sigar, query, &pids); - if (status != SIGAR_OK) { - return status; - } - - sigar_proc_list_create(proclist); - - for (i=0; inumber; i++) { - int query_status = - sigar_ptql_query_match(sigar, query, pids->data[i]); - - if (query_status == SIGAR_OK) { - SIGAR_PROC_LIST_GROW(proclist); - proclist->data[proclist->number++] = pids->data[i]; - } - else if (query_status == SIGAR_ENOTIMPL) { - /* let caller know query is invalid. */ - status = query_status; - break; - } - } - - ptql_proc_list_destroy(sigar, pids); - - if (status != SIGAR_OK) { - sigar_proc_list_destroy(sigar, proclist); - return status; - } - - return SIGAR_OK; -} diff --git a/vendor/sigar/src/sigar_signal.c b/vendor/sigar/src/sigar_signal.c deleted file mode 100644 index e32d231f6..000000000 --- a/vendor/sigar/src/sigar_signal.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2007 Hyperic, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" - -#ifdef WIN32 -#include -#endif - -#include -#include - -SIGAR_DECLARE(int) sigar_proc_kill(sigar_pid_t pid, int signum) -{ -#ifdef WIN32 - int status = -1; - HANDLE proc = - OpenProcess(PROCESS_ALL_ACCESS, - TRUE, (DWORD)pid); - - if (proc) { - switch (signum) { - case 0: - status = SIGAR_OK; - break; - default: - if (TerminateProcess(proc, signum)) { - status = SIGAR_OK; - } - break; - } - - CloseHandle(proc); - - if (status == SIGAR_OK) { - return SIGAR_OK; - } - } - return GetLastError(); -#else - if (kill(pid, signum) == -1) { - return errno; - } - return SIGAR_OK; -#endif -} - -SIGAR_DECLARE(int) sigar_signum_get(char *name) -{ - if (strnEQ(name, "SIG", 3)) { - name += 3; - } - - switch (*name) { - case 'A': -#ifdef SIGABRT - if (strEQ(name, "ABRT")) return SIGABRT; -#endif -#ifdef SIGALRM - if (strEQ(name, "ALRM")) return SIGALRM; -#endif - break; - case 'B': -#ifdef SIGBUS - if (strEQ(name, "BUS")) return SIGBUS; -#endif - break; - case 'C': -#ifdef SIGCONT - if (strEQ(name, "CONT")) return SIGCONT; -#endif -#ifdef SIGCHLD - if (strEQ(name, "CHLD")) return SIGCHLD; -#endif -#ifdef SIGCLD - if (strEQ(name, "CLD")) return SIGCLD; -#endif - break; - case 'E': -#ifdef SIGEMT - if (strEQ(name, "EMT")) return SIGEMT; -#endif - break; - case 'F': -#ifdef SIGFPE - if (strEQ(name, "FPE")) return SIGFPE; -#endif - break; - case 'H': -#ifdef SIGHUP - if (strEQ(name, "HUP")) return SIGHUP; -#endif - break; - case 'I': -#ifdef SIGINT - if (strEQ(name, "INT")) return SIGINT; -#endif -#ifdef SIGILL - if (strEQ(name, "ILL")) return SIGILL; -#endif -#ifdef SIGIOT - if (strEQ(name, "IOT")) return SIGIOT; -#endif -#ifdef SIGIO - if (strEQ(name, "IO")) return SIGIO; -#endif -#ifdef SIGINFO - if (strEQ(name, "INFO")) return SIGINFO; -#endif - break; - case 'K': -#ifdef SIGKILL - if (strEQ(name, "KILL")) return SIGKILL; -#endif - break; - case 'P': -#ifdef SIGPOLL - if (strEQ(name, "POLL")) return SIGPOLL; -#endif -#ifdef SIGPIPE - if (strEQ(name, "PIPE")) return SIGPIPE; -#endif -#ifdef SIGPROF - if (strEQ(name, "PROF")) return SIGPROF; -#endif -#ifdef SIGPWR - if (strEQ(name, "PWR")) return SIGPWR; -#endif - break; - case 'Q': -#ifdef SIGQUIT - if (strEQ(name, "QUIT")) return SIGQUIT; -#endif - break; - case 'S': -#ifdef SIGSEGV - if (strEQ(name, "SEGV")) return SIGSEGV; -#endif -#ifdef SIGSYS - if (strEQ(name, "SYS")) return SIGSYS; -#endif -#ifdef SIGSTOP - if (strEQ(name, "STOP")) return SIGSTOP; -#endif -#ifdef SIGSTKFLT - if (strEQ(name, "STKFLT")) return SIGSTKFLT; -#endif - break; - case 'T': -#ifdef SIGTRAP - if (strEQ(name, "TRAP")) return SIGTRAP; -#endif -#ifdef SIGTERM - if (strEQ(name, "TERM")) return SIGTERM; -#endif -#ifdef SIGTSTP - if (strEQ(name, "TSTP")) return SIGTSTP; -#endif -#ifdef SIGTTIN - if (strEQ(name, "TTIN")) return SIGTTIN; -#endif -#ifdef SIGTTOU - if (strEQ(name, "TTOU")) return SIGTTOU; -#endif - break; - case 'U': -#ifdef SIGURG - if (strEQ(name, "URG")) return SIGURG; -#endif -#ifdef SIGUSR1 - if (strEQ(name, "USR1")) return SIGUSR1; -#endif -#ifdef SIGUSR2 - if (strEQ(name, "USR2")) return SIGUSR2; -#endif - break; - case 'V': -#ifdef SIGVTALRM - if (strEQ(name, "VTALRM")) return SIGVTALRM; -#endif - break; - case 'W': -#ifdef SIGWINCH - if (strEQ(name, "WINCH")) return SIGWINCH; -#endif - break; - case 'X': -#ifdef SIGXCPU - if (strEQ(name, "XCPU")) return SIGXCPU; -#endif -#ifdef SIGXFSZ - if (strEQ(name, "XFSZ")) return SIGXFSZ; -#endif - break; - default: - break; - } - - return -1; -} - diff --git a/vendor/sigar/src/sigar_util.c b/vendor/sigar/src/sigar_util.c deleted file mode 100644 index 3c668fc63..000000000 --- a/vendor/sigar/src/sigar_util.c +++ /dev/null @@ -1,1060 +0,0 @@ -/* - * Copyright (c) 2004-2009 Hyperic, Inc. - * Copyright (c) 2009 SpringSource, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -#include "sigar.h" -#include "sigar_private.h" -#include "sigar_util.h" -#include "sigar_os.h" - -#ifndef WIN32 - -#include -#include - -SIGAR_INLINE char *sigar_uitoa(char *buf, unsigned int n, int *len) -{ - char *start = buf + UITOA_BUFFER_SIZE - 1; - - *start = 0; - - do { - *--start = '0' + (n % 10); - ++*len; - n /= 10; - } while (n); - - return start; -} - -SIGAR_INLINE char *sigar_skip_line(char *buffer, int buflen) -{ - char *ptr = buflen ? - (char *)memchr(buffer, '\n', buflen) : /* bleh */ - strchr(buffer, '\n'); - return ++ptr; -} - -SIGAR_INLINE char *sigar_skip_token(char *p) -{ - while (sigar_isspace(*p)) p++; - while (*p && !sigar_isspace(*p)) p++; - return p; -} - -SIGAR_INLINE char *sigar_skip_multiple_token(char *p, int count) -{ - int i; - - for (i = 0; i < count; i++) { - p = sigar_skip_token(p); - } - - return p; -} - -char *sigar_getword(char **line, char stop) -{ - char *pos = *line; - int len; - char *res; - - while ((*pos != stop) && *pos) { - ++pos; - } - - len = pos - *line; - res = malloc(len + 1); - memcpy(res, *line, len); - res[len] = 0; - - if (stop) { - while (*pos == stop) { - ++pos; - } - } - - *line = pos; - - return res; -} - -/* avoiding sprintf */ - -char *sigar_proc_filename(char *buffer, int buflen, - sigar_pid_t bigpid, - const char *fname, int fname_len) -{ - int len = 0; - char *ptr = buffer; - unsigned int pid = (unsigned int)bigpid; /* XXX -- This isn't correct */ - char pid_buf[UITOA_BUFFER_SIZE]; - char *pid_str = sigar_uitoa(pid_buf, pid, &len); - - assert((unsigned int)buflen >= - (SSTRLEN(PROCP_FS_ROOT) + UITOA_BUFFER_SIZE + fname_len + 1)); - - memcpy(ptr, PROCP_FS_ROOT, SSTRLEN(PROCP_FS_ROOT)); - ptr += SSTRLEN(PROCP_FS_ROOT); - - memcpy(ptr, pid_str, len); - ptr += len; - - memcpy(ptr, fname, fname_len); - ptr += fname_len; - *ptr = '\0'; - - return buffer; -} - -int sigar_proc_file2str(char *buffer, int buflen, - sigar_pid_t pid, - const char *fname, - int fname_len) -{ - int retval; - - buffer = sigar_proc_filename(buffer, buflen, pid, - fname, fname_len); - - retval = sigar_file2str(buffer, buffer, buflen); - - if (retval != SIGAR_OK) { - switch (retval) { - case ENOENT: - retval = ESRCH; /* no such process */ - default: - break; - } - } - - return retval; -} - -int sigar_proc_list_procfs_get(sigar_t *sigar, - sigar_proc_list_t *proclist) -{ - DIR *dirp = opendir("/proc"); - struct dirent *ent; -#ifdef HAVE_READDIR_R - struct dirent dbuf; -#endif - - if (!dirp) { - return errno; - } - -#ifdef HAVE_READDIR_R - while (readdir_r(dirp, &dbuf, &ent) == 0) { - if (ent == NULL) { - break; - } -#else - while ((ent = readdir(dirp))) { -#endif - if (!sigar_isdigit(*ent->d_name)) { - continue; - } - - /* XXX: more sanity checking */ - - SIGAR_PROC_LIST_GROW(proclist); - - proclist->data[proclist->number++] = - strtoul(ent->d_name, NULL, 10); - } - - closedir(dirp); - - return SIGAR_OK; -} - -int sigar_proc_fd_count(sigar_t *sigar, sigar_pid_t pid, - sigar_uint64_t *total) -{ - DIR *dirp; - struct dirent *ent; -#ifdef HAVE_READDIR_R - struct dirent dbuf; -#endif - char name[BUFSIZ]; - - (void)SIGAR_PROC_FILENAME(name, pid, "/fd"); - - *total = 0; - - if (!(dirp = opendir(name))) { - return errno; - } - -#ifdef HAVE_READDIR_R - while (readdir_r(dirp, &dbuf, &ent) == 0) { - if (ent == NULL) { - break; - } -#else - while ((ent = readdir(dirp))) { -#endif - if (!sigar_isdigit(*ent->d_name)) { - continue; - } - - (*total)++; - } - - closedir(dirp); - - return SIGAR_OK; -} - -int sigar_procfs_args_get(sigar_t *sigar, sigar_pid_t pid, - sigar_proc_args_t *procargs) -{ - char buffer[9086], *buf=NULL, *ptr; - int fd, len, total=0; - - (void)SIGAR_PROC_FILENAME(buffer, pid, "/cmdline"); - - if ((fd = open(buffer, O_RDONLY)) < 0) { - if (errno == ENOENT) { - return ESRCH; - } - return errno; - } - - buffer[0] = '\0'; - - /* XXX: possible to get rid of some mallocs here. - * but, unlikely this will be called often so it - * might not even matter much. - */ - while ((len = read(fd, buffer, sizeof(buffer)-1)) > 0) { - if (len == 0) { - break; - } - buf = realloc(buf, total+len+1); - memcpy(buf+total, buffer, len); - total += len; - } - - close(fd); - - /* e.g. /proc/2/cmdline */ - if (total == 0) { - procargs->number = 0; - return SIGAR_OK; - } - - buf[total] = '\0'; - ptr = buf; - - while (total > 0) { - int alen = strlen(ptr)+1; - char *arg = malloc(alen); - - SIGAR_PROC_ARGS_GROW(procargs); - memcpy(arg, ptr, alen); - - procargs->data[procargs->number++] = arg; - - total -= alen; - if (total > 0) { - ptr += alen; - } - } - - free(buf); - - return SIGAR_OK; -} - -#endif /* WIN32 */ - -/* from httpd/server/util.c */ -char *sigar_strcasestr(const char *s1, const char *s2) -{ - char *p1, *p2; - if (*s2 == '\0') { - /* an empty s2 */ - return((char *)s1); - } - while(1) { - for ( ; (*s1 != '\0') && (sigar_tolower(*s1) != sigar_tolower(*s2)); s1++); - if (*s1 == '\0') { - return(NULL); - } - /* found first character of s2, see if the rest matches */ - p1 = (char *)s1; - p2 = (char *)s2; - for (++p1, ++p2; sigar_tolower(*p1) == sigar_tolower(*p2); ++p1, ++p2) { - if (*p1 == '\0') { - /* both strings ended together */ - return((char *)s1); - } - } - if (*p2 == '\0') { - /* second string ended, a match */ - break; - } - /* didn't find a match here, try starting at next character in s1 */ - s1++; - } - return((char *)s1); -} - -int sigar_mem_calc_ram(sigar_t *sigar, sigar_mem_t *mem) -{ - sigar_int64_t total = mem->total / 1024, diff; - sigar_uint64_t lram = (mem->total / (1024 * 1024)); - int ram = (int)lram; /* must cast after division */ - int remainder = ram % 8; - - if (remainder > 0) { - ram += (8 - remainder); - } - - mem->ram = ram; - - diff = total - (mem->actual_free / 1024); - mem->used_percent = - (double)(diff * 100) / total; - - diff = total - (mem->actual_used / 1024); - mem->free_percent = - (double)(diff * 100) / total; - - return ram; -} - -#ifndef WIN32 - -sigar_iodev_t *sigar_iodev_get(sigar_t *sigar, - const char *dirname) -{ - sigar_cache_entry_t *entry; - struct stat sb; - sigar_uint64_t id; - sigar_file_system_list_t fslist; - int i, status, is_dev=0; - int debug = SIGAR_LOG_IS_DEBUG(sigar); - char dev_name[SIGAR_FS_NAME_LEN]; - - if (!sigar->fsdev) { - sigar->fsdev = sigar_cache_new(15); - } - - if (*dirname != '/') { - snprintf(dev_name, sizeof(dev_name), - SIGAR_DEV_PREFIX "%s", dirname); - dirname = dev_name; - is_dev = 1; - } - else if (SIGAR_NAME_IS_DEV(dirname)) { - is_dev = 1; - } - - if (stat(dirname, &sb) < 0) { - if (debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[iodev] stat(%s) failed", - dirname); - } - return NULL; - } - - id = SIGAR_FSDEV_ID(sb); - - entry = sigar_cache_get(sigar->fsdev, id); - - if (entry->value != NULL) { - return (sigar_iodev_t *)entry->value; - } - - if (is_dev) { - sigar_iodev_t *iodev; - entry->value = iodev = malloc(sizeof(*iodev)); - SIGAR_ZERO(iodev); - SIGAR_SSTRCPY(iodev->name, dirname); - if (debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[iodev] %s is_dev=true", dirname); - } - return iodev; - } - - status = sigar_file_system_list_get(sigar, &fslist); - - if (status != SIGAR_OK) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[iodev] file_system_list failed: %s", - sigar_strerror(sigar, status)); - return NULL; - } - - for (i=0; itype == SIGAR_FSTYPE_LOCAL_DISK) { - int retval = stat(fsp->dir_name, &sb); - sigar_cache_entry_t *ent; - - if (retval < 0) { - if (debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[iodev] inode stat(%s) failed", - fsp->dir_name); - } - continue; /* cant cache w/o inode */ - } - - ent = sigar_cache_get(sigar->fsdev, SIGAR_FSDEV_ID(sb)); - if (ent->value) { - continue; /* already cached */ - } - - if (SIGAR_NAME_IS_DEV(fsp->dev_name)) { - sigar_iodev_t *iodev; - ent->value = iodev = malloc(sizeof(*iodev)); - SIGAR_ZERO(iodev); - iodev->is_partition = 1; - SIGAR_SSTRCPY(iodev->name, fsp->dev_name); - - if (debug) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[iodev] map %s -> %s", - fsp->dir_name, iodev->name); - } - } - } - } - - sigar_file_system_list_destroy(sigar, &fslist); - - if (entry->value && - (((sigar_iodev_t *)entry->value)->name[0] != '\0')) - { - return (sigar_iodev_t *)entry->value; - } - else { - return NULL; - } -} -#endif - -double sigar_file_system_usage_calc_used(sigar_t *sigar, - sigar_file_system_usage_t *fsusage) -{ - /* - * win32 will not convert __uint64 to double. - * convert to KB then do unsigned long -> double. - */ - sigar_uint64_t b_used = (fsusage->total - fsusage->free) / 1024; - sigar_uint64_t b_avail = fsusage->avail / 1024; - unsigned long utotal = b_used + b_avail; - unsigned long used = b_used; - - if (utotal != 0) { - unsigned long u100 = used * 100; - double pct = u100 / utotal + - ((u100 % utotal != 0) ? 1 : 0); - return pct / 100; - } - - return 0; -} - -typedef struct { - sigar_uint32_t eax; - sigar_uint32_t ebx; - sigar_uint32_t ecx; - sigar_uint32_t edx; -} sigar_cpuid_t; - -#if defined(__GNUC__) && !defined(__sun) - -# if defined(__i386__) -# define SIGAR_HAS_CPUID -static void sigar_cpuid(sigar_uint32_t request, sigar_cpuid_t *id) -{ - /* derived from: */ - /* http://svn.red-bean.com/repos/minor/trunk/gc/barriers-ia-32.c */ - asm volatile ("mov %%ebx, %%esi\n\t" - "cpuid\n\t" - "xchgl %%ebx, %%esi" - : "=a" (id->eax), - "=S" (id->ebx), - "=c" (id->ecx), - "=d" (id->edx) - : "0" (request) - : "memory"); -} -# elif defined(__amd64__) -# define SIGAR_HAS_CPUID -static void sigar_cpuid(sigar_uint32_t request, - sigar_cpuid_t *id) -{ - /* http://svn.red-bean.com/repos/minor/trunk/gc/barriers-amd64.c */ - asm volatile ("cpuid\n\t" - : "=a" (id->eax), - "=b" (id->ebx), - "=c" (id->ecx), - "=d" (id->edx) - : "0" (request) - : "memory"); -} -# endif -#elif defined(WIN32) -# ifdef _M_X64 -# include -# define SIGAR_HAS_CPUID -static void sigar_cpuid(sigar_uint32_t request, - sigar_cpuid_t *id) -{ - sigar_uint32_t info[4]; - __cpuid(info, request); /* as of MSVC 7 */ - memcpy(id, &info[0], sizeof(info)); -} -# else -# define SIGAR_HAS_CPUID -static void sigar_cpuid(sigar_uint32_t request, - sigar_cpuid_t *id) -{ - __asm { - mov edi, id - mov eax, [edi].eax - mov ecx, [edi].ecx - cpuid - mov [edi].eax, eax - mov [edi].ebx, ebx - mov [edi].ecx, ecx - mov [edi].edx, edx - } -} -# endif -#endif - -#define INTEL_ID 0x756e6547 -#define AMD_ID 0x68747541 - -int sigar_cpu_core_count(sigar_t *sigar) -{ -#if defined(SIGAR_HAS_CPUID) - sigar_cpuid_t id; - - if (sigar->lcpu == -1) { - sigar->lcpu = 1; - - sigar_cpuid(0, &id); - - if ((id.ebx == INTEL_ID) || (id.ebx == AMD_ID)) { - sigar_cpuid(1, &id); - - if (id.edx & (1<<28)) { - sigar->lcpu = (id.ebx & 0x00FF0000) >> 16; - } - } - - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[cpu] %d cores per socket", sigar->lcpu); - } - - return sigar->lcpu; -#elif defined(__sun) || defined(__hpux) || defined(_AIX) - return 1; -#else - sigar->lcpu = 1; - return sigar->lcpu; -#endif -} - -int sigar_cpu_core_rollup(sigar_t *sigar) -{ -#ifdef SIGAR_HAS_CPUID - int log_rollup = - SIGAR_LOG_IS_DEBUG(sigar) && - (sigar->lcpu == -1); - - (void)sigar_cpu_core_count(sigar); - - if (sigar->cpu_list_cores) { - if (log_rollup && (sigar->lcpu > 1)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[cpu] treating cores as-is"); - } - } - else { - if (log_rollup && (sigar->lcpu > 1)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "[cpu] rolling up cores to sockets"); - return 1; - } - } -#endif - return 0; -} - -#define IS_CPU_R(p) \ - ((*p == '(') && (*(p+1) == 'R') && (*(p+2) == ')')) - -typedef struct { - char *name; /* search */ - int len; - char *rname; /* replace */ - int rlen; -} cpu_model_str_t; - -/* to later replace 's' with 'r' */ -#define CPU_MODEL_ENT_R(s, r) \ - { s, sizeof(s)-1, r, sizeof(r) } - -#define CPU_MODEL_ENT(s) \ - CPU_MODEL_ENT_R(s, s) - -/* after the vendor part of the string is removed, - * looking for startsWith the entries below - * to remove the crap after the model name, see - * ../exp/intel_amd_cpu_models.txt - */ -static const cpu_model_str_t cpu_models[] = { - /* intel */ - CPU_MODEL_ENT("Xeon"), - CPU_MODEL_ENT_R("XEON", "Xeon"), - CPU_MODEL_ENT("Pentium III"), - CPU_MODEL_ENT("Pentium II"), - CPU_MODEL_ENT_R("Pentium(R) III", "Pentium III"), - CPU_MODEL_ENT_R("Pentium(R) 4", "Pentium 4"), - CPU_MODEL_ENT_R("Pentium(R) M", "Pentium M"), - CPU_MODEL_ENT("Pentium Pro"), - CPU_MODEL_ENT("Celeron"), - - /* amd */ - CPU_MODEL_ENT("Opteron"), - CPU_MODEL_ENT("Athlon"), - CPU_MODEL_ENT("Duron"), - CPU_MODEL_ENT_R("K6(tm)-III", "K6 III"), - CPU_MODEL_ENT_R("K6(tm) 3D+", "K6 3D+"), - { NULL } -}; - -/* common to win32 and linux */ -void sigar_cpu_model_adjust(sigar_t *sigar, sigar_cpu_info_t *info) -{ - int len, i; - char model[128], *ptr=model, *end; - - memcpy(model, info->model, sizeof(model)); - - /* trim leading and trailing spaces */ - len = strlen(model); - end = &model[len-1]; - while (*ptr == ' ') ++ptr; - while (*end == ' ') *end-- = '\0'; - - /* remove vendor from model name */ - len = strlen(info->vendor); - if (strnEQ(ptr, info->vendor, len)) { - ptr += len; - if (IS_CPU_R(ptr)) { - ptr += 3; /* remove (R) */ - } - while (*ptr == ' ') ++ptr; - } - - if (*ptr == '-') { - ++ptr; /* e.g. was AMD-K6... */ - } - - for (i=0; cpu_models[i].name; i++) { - const cpu_model_str_t *cpu_model = &cpu_models[i]; - - if (strnEQ(ptr, cpu_model->name, cpu_model->len)) { - memcpy(info->model, cpu_model->rname, cpu_model->rlen); - return; - } - } - - strcpy(info->model, ptr); -} - -/* attempt to derive MHz from model name - * currently works for certain intel strings - * see exp/intel_amd_cpu_models.txt - */ -int sigar_cpu_mhz_from_model(char *model) -{ - int mhz = SIGAR_FIELD_NOTIMPL; - char *ptr = model; - - while (*ptr && (ptr = strchr(ptr, ' '))) { - while(*ptr && !sigar_isdigit(*ptr)) { - ptr++; - } - mhz = sigar_strtoul(ptr); - - if (*ptr == '.') { - /* e.g. "2.40GHz" */ - ++ptr; - mhz *= 100; - mhz += sigar_strtoul(ptr); - break; - } - else if (strnEQ(ptr, "GHz", 3) || - strnEQ(ptr, "MHz", 3)) - { - /* e.g. "1500MHz" */ - break; - } - else { - mhz = SIGAR_FIELD_NOTIMPL; - } - } - - if (mhz != SIGAR_FIELD_NOTIMPL) { - if (strnEQ(ptr, "GHz", 3)) { - mhz *= 10; - } - } - - return mhz; -} - -#if !defined(WIN32) && !defined(NETWARE) -#include -#include -#include -#include -#ifdef SIGAR_HPUX -#include -#endif -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun) || defined(DARWIN) -#include -#endif -#if defined(__sun) || defined(SIGAR_HPUX) -#include -#endif -#if defined(_AIX) || defined(SIGAR_HPUX) || defined(__OpenBSD__) || defined(__NetBSD__) -#include -#endif - -static enum clnt_stat get_sockaddr(struct sockaddr_in *addr, char *host) -{ - register struct hostent *hp; - sigar_hostent_t data; - - memset(addr, 0, sizeof(struct sockaddr_in)); - addr->sin_family = AF_INET; - - if ((addr->sin_addr.s_addr = inet_addr(host)) == -1) { - if (!(hp = sigar_gethostbyname(host, &data))) { - return RPC_UNKNOWNHOST; - } - memcpy(&addr->sin_addr, hp->h_addr, hp->h_length); - } - - return RPC_SUCCESS; -} - -char *sigar_rpc_strerror(int err) -{ - return (char *)clnt_sperrno(err); -} - -SIGAR_DECLARE(int) sigar_rpc_ping(char *host, - int protocol, - unsigned long program, - unsigned long version) -{ - CLIENT *client; - struct sockaddr_in addr; - int sock; - struct timeval timeout; - unsigned short port = 0; - enum clnt_stat rpc_stat; - - rpc_stat = get_sockaddr(&addr, host); - if (rpc_stat != RPC_SUCCESS) { - return rpc_stat; - } - - timeout.tv_sec = 2; - timeout.tv_usec = 0; - addr.sin_port = htons(port); - sock = RPC_ANYSOCK; - - if (protocol == SIGAR_NETCONN_UDP) { - client = - clntudp_create(&addr, program, version, - timeout, &sock); - } - else if (protocol == SIGAR_NETCONN_TCP) { - client = - clnttcp_create(&addr, program, version, - &sock, 0, 0); - } - else { - return RPC_UNKNOWNPROTO; - } - - if (!client) { - return rpc_createerr.cf_stat; - } - - timeout.tv_sec = 10; - timeout.tv_usec = 0; - rpc_stat = clnt_call(client, NULLPROC, (xdrproc_t)xdr_void, NULL, - (xdrproc_t)xdr_void, NULL, timeout); - - clnt_destroy(client); - - return rpc_stat; -} -#endif - -int sigar_file2str(const char *fname, char *buffer, int buflen) -{ - int len, status; - int fd = open(fname, O_RDONLY); - - if (fd < 0) { - return ENOENT; - } - - if ((len = read(fd, buffer, buflen)) < 0) { - status = errno; - } - else { - status = SIGAR_OK; - buffer[len] = '\0'; - } - close(fd); - - return status; -} - -#ifdef WIN32 -#define vsnprintf _vsnprintf -#endif - -#ifdef WIN32 -# define rindex strrchr -#endif - -static int proc_module_get_self(void *data, char *name, int len) -{ - sigar_t *sigar = (sigar_t *)data; - char *ptr = rindex(name, '/'); - - if (!ptr) { - return SIGAR_OK; - } - - if (strnEQ(ptr+1, "libsigar-", 9)) { - int offset = ptr - name; - - sigar->self_path = sigar_strdup(name); - *(sigar->self_path + offset) = '\0'; /* chop libsigar-*.so */ - - if (SIGAR_LOG_IS_DEBUG(sigar)) { - sigar_log_printf(sigar, SIGAR_LOG_DEBUG, - "detected sigar-lib='%s'", - sigar->self_path); - } - - return !SIGAR_OK; /* break loop */ - } - - return SIGAR_OK; -} - -char *sigar_get_self_path(sigar_t *sigar) -{ - if (!sigar->self_path) { - sigar_proc_modules_t procmods; - char *self_path = getenv("SIGAR_PATH"); - - if (self_path) { - sigar->self_path = sigar_strdup(self_path); - return sigar->self_path; - } - - procmods.module_getter = proc_module_get_self; - procmods.data = sigar; - - sigar_proc_modules_get(sigar, - sigar_pid_get(sigar), - &procmods); - - if (!sigar->self_path) { - /* dont try again */ - sigar->self_path = sigar_strdup("."); - } - } - - return sigar->self_path; -} - -#ifdef SIGAR_HAS_DLINFO_MODULES - -static int sigar_dlinfo_get(sigar_t *sigar, const char *func, - void **handle, Link_map **map) -{ - Dl_info dli; - - if (!dladdr((void *)((uintptr_t)sigar_dlinfo_get), &dli)) { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "[%s] dladdr(%s) = %s", - func, SIGAR_FUNC, dlerror()); - return ESRCH; - } - - if (!(*handle = dlopen(dli.dli_fname, RTLD_LAZY))) { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "[%s] dlopen(%s) = %s", - func, dli.dli_fname, dlerror()); - return ESRCH; - } - - dlinfo(*handle, RTLD_DI_LINKMAP, map); - - if (!map) { - sigar_log_printf(sigar, SIGAR_LOG_ERROR, - "[%s] dlinfo = %s", - func, dlerror()); - return ESRCH; - } - - return SIGAR_OK; -} - -int sigar_dlinfo_modules(sigar_t *sigar, sigar_proc_modules_t *procmods) -{ - int status; - void *handle; - Link_map *map; - - status = sigar_dlinfo_get(sigar, SIGAR_FUNC, &handle, &map); - if (status != SIGAR_OK) { - return status; - } - - while (map->l_prev != NULL) { - map = map->l_prev; - } - - do { - int status = - procmods->module_getter(procmods->data, - (char *)map->l_name, - strlen(map->l_name)); - - if (status != SIGAR_OK) { - /* not an error; just stop iterating */ - break; - } - } while ((map = map->l_next)); - - dlclose(handle); - - return SIGAR_OK; -} -#endif - -SIGAR_DECLARE(void) sigar_log_printf(sigar_t *sigar, int level, - const char *format, ...) -{ - va_list args; - char buffer[8192]; - - if (level > sigar->log_level) { - return; - } - - if (!sigar->log_impl) { - return; - } - - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - - sigar->log_impl(sigar, sigar->log_data, level, buffer); -} - -SIGAR_DECLARE(void) sigar_log(sigar_t *sigar, int level, char *message) -{ - if (level > sigar->log_level) { - return; - } - - if (!sigar->log_impl) { - return; - } - - sigar->log_impl(sigar, sigar->log_data, level, message); -} - -SIGAR_DECLARE(void) sigar_log_impl_set(sigar_t *sigar, void *data, - sigar_log_impl_t impl) -{ - sigar->log_data = data; - sigar->log_impl = impl; -} - -SIGAR_DECLARE(int) sigar_log_level_get(sigar_t *sigar) -{ - return sigar->log_level; -} - -static const char *log_levels[] = { - "FATAL", - "ERROR", - "WARN", - "INFO", - "DEBUG", - "TRACE" -}; - -SIGAR_DECLARE(const char *) sigar_log_level_string_get(sigar_t *sigar) -{ - return log_levels[sigar->log_level]; -} - -SIGAR_DECLARE(void) sigar_log_level_set(sigar_t *sigar, int level) -{ - sigar->log_level = level; -} - -SIGAR_DECLARE(void) sigar_log_impl_file(sigar_t *sigar, void *data, - int level, char *message) -{ - FILE *fp = (FILE*)data; - fprintf(fp, "[%s] %s\n", log_levels[level], message); -} - -#ifndef WIN32 -sigar_int64_t sigar_time_now_millis(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return ((tv.tv_sec * SIGAR_USEC) + tv.tv_usec) / SIGAR_MSEC; -} -#endif diff --git a/vendor/sigar/src/sigar_version_autoconf.c b/vendor/sigar/src/sigar_version_autoconf.c deleted file mode 100644 index 7b1f865bf..000000000 --- a/vendor/sigar/src/sigar_version_autoconf.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "sigar.h" - -static sigar_version_t sigar_version = { - __DATE__, - "@SCM_REVISION@", - "libsigar 1.6.2", - "x86_64-apple-darwin11.4.0", - "darwin11.4.0", - "x86_64", - "SIGAR-1.6.2, " - "SCM revision @SCM_REVISION@, " - "built "__DATE__" as x86_64", - 1, - 6, - 2, - 0 -}; - -SIGAR_DECLARE(sigar_version_t *) sigar_version_get(void) -{ - return &sigar_version; -} diff --git a/vendor/udt4/CMakeLists.txt b/vendor/udt4/CMakeLists.txt new file mode 100644 index 000000000..9d65ee7aa --- /dev/null +++ b/vendor/udt4/CMakeLists.txt @@ -0,0 +1,26 @@ + +IF( APPLE ) + add_definitions( -DOSX ) +ELSEIF( WIN32 ) + +ELSE() + add_definitions( -DLINUX ) +ENDIF() + +set( udt_sources + src/api.cpp + src/buffer.cpp + src/cache.cpp + src/ccc.cpp + src/channel.cpp + src/common.cpp + src/core.cpp + src/epoll.cpp + src/list.cpp + src/md5.cpp + src/packet.cpp + src/queue.cpp + src/window.cpp + ) + +add_library( udt ${udt_sources} ) diff --git a/vendor/udt4/LICENSE.txt b/vendor/udt4/LICENSE.txt new file mode 100644 index 000000000..eec89df38 --- /dev/null +++ b/vendor/udt4/LICENSE.txt @@ -0,0 +1,32 @@ +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. diff --git a/vendor/udt4/README.txt b/vendor/udt4/README.txt new file mode 100644 index 000000000..105be4281 --- /dev/null +++ b/vendor/udt4/README.txt @@ -0,0 +1,43 @@ +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All Rights Reserved. +Copyright (c) 2011 - 2012, Google, Inc. All Rights Reserved. + +UDP-based Data Transfer (UDT) Library - version 4 +Author: Yunhong Gu [yunhong.gu @ gmail.com] + +UDT version 4 is free software under BSD License. See ./LICENSE.txt. + +============================================================================ + +UDT Website: +http://udt.sf.net +http://sf.net/projects/udt/ + + +CONTENT: +./src: UDT source code +./app: Example programs +./doc: UDT documentation (HTML) +./win: Visual C++ project files for the Windows version of UDT + + +To make: + make -e os=XXX arch=YYY + +XXX: [LINUX(default), BSD, OSX] +YYY: [IA32(default), POWERPC, IA64, AMD64] + +For example, on OS X, you may need to do "make -e os=OSX arch=POWERPC"; +on 32-bit i386 Linux system, simply use "make". + +On Windows systems, use the Visual C++ project files in ./win directory. + +Note for BSD users, please use GNU Make. + +To use UDT in your application: +Read index.htm in ./doc. The documentation is in HTML format and requires your +browser to support JavaScript. + + +Questions? please post to the UDT project forum: +https://sourceforge.net/projects/udt/forums diff --git a/vendor/udt4/src/api.cpp b/vendor/udt4/src/api.cpp new file mode 100644 index 000000000..e4442182a --- /dev/null +++ b/vendor/udt4/src/api.cpp @@ -0,0 +1,2392 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 07/09/2011 +*****************************************************************************/ + +#ifdef WIN32 + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#else + #include +#endif +#include +#include "api.h" +#include "core.h" + +using namespace std; + +CUDTSocket::CUDTSocket(): +m_Status(INIT), +m_TimeStamp(0), +m_iIPversion(0), +m_pSelfAddr(NULL), +m_pPeerAddr(NULL), +m_SocketID(0), +m_ListenSocket(0), +m_PeerID(0), +m_iISN(0), +m_pUDT(NULL), +m_pQueuedSockets(NULL), +m_pAcceptSockets(NULL), +m_AcceptCond(), +m_AcceptLock(), +m_uiBackLog(0), +m_iMuxID(-1) +{ + #ifndef WIN32 + pthread_mutex_init(&m_AcceptLock, NULL); + pthread_cond_init(&m_AcceptCond, NULL); + pthread_mutex_init(&m_ControlLock, NULL); + #else + m_AcceptLock = CreateMutex(NULL, false, NULL); + m_AcceptCond = CreateEvent(NULL, false, false, NULL); + m_ControlLock = CreateMutex(NULL, false, NULL); + #endif +} + +CUDTSocket::~CUDTSocket() +{ + if (AF_INET == m_iIPversion) + { + delete (sockaddr_in*)m_pSelfAddr; + delete (sockaddr_in*)m_pPeerAddr; + } + else + { + delete (sockaddr_in6*)m_pSelfAddr; + delete (sockaddr_in6*)m_pPeerAddr; + } + + delete m_pUDT; + m_pUDT = NULL; + + delete m_pQueuedSockets; + delete m_pAcceptSockets; + + #ifndef WIN32 + pthread_mutex_destroy(&m_AcceptLock); + pthread_cond_destroy(&m_AcceptCond); + pthread_mutex_destroy(&m_ControlLock); + #else + CloseHandle(m_AcceptLock); + CloseHandle(m_AcceptCond); + CloseHandle(m_ControlLock); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// + +CUDTUnited::CUDTUnited(): +m_Sockets(), +m_ControlLock(), +m_IDLock(), +m_SocketID(0), +m_TLSError(), +m_mMultiplexer(), +m_MultiplexerLock(), +m_pCache(NULL), +m_bClosing(false), +m_GCStopLock(), +m_GCStopCond(), +m_InitLock(), +m_iInstanceCount(0), +m_bGCStatus(false), +m_GCThread(), +m_ClosedSockets() +{ + // Socket ID MUST start from a random value + srand((unsigned int)CTimer::getTime()); + m_SocketID = 1 + (int)((1 << 30) * (double(rand()) / RAND_MAX)); + + #ifndef WIN32 + pthread_mutex_init(&m_ControlLock, NULL); + pthread_mutex_init(&m_IDLock, NULL); + pthread_mutex_init(&m_InitLock, NULL); + #else + m_ControlLock = CreateMutex(NULL, false, NULL); + m_IDLock = CreateMutex(NULL, false, NULL); + m_InitLock = CreateMutex(NULL, false, NULL); + #endif + + #ifndef WIN32 + pthread_key_create(&m_TLSError, TLSDestroy); + #else + m_TLSError = TlsAlloc(); + m_TLSLock = CreateMutex(NULL, false, NULL); + #endif + + m_pCache = new CCache; +} + +CUDTUnited::~CUDTUnited() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_ControlLock); + pthread_mutex_destroy(&m_IDLock); + pthread_mutex_destroy(&m_InitLock); + #else + CloseHandle(m_ControlLock); + CloseHandle(m_IDLock); + CloseHandle(m_InitLock); + #endif + + #ifndef WIN32 + pthread_key_delete(m_TLSError); + #else + TlsFree(m_TLSError); + CloseHandle(m_TLSLock); + #endif + + delete m_pCache; +} + +int CUDTUnited::startup() +{ + CGuard gcinit(m_InitLock); + + if (m_iInstanceCount++ > 0) + return 0; + + // Global initialization code + #ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + + if (0 != WSAStartup(wVersionRequested, &wsaData)) + throw CUDTException(1, 0, WSAGetLastError()); + #endif + + //init CTimer::EventLock + + if (m_bGCStatus) + return true; + + m_bClosing = false; + #ifndef WIN32 + pthread_mutex_init(&m_GCStopLock, NULL); + pthread_cond_init(&m_GCStopCond, NULL); + pthread_create(&m_GCThread, NULL, garbageCollect, this); + #else + m_GCStopLock = CreateMutex(NULL, false, NULL); + m_GCStopCond = CreateEvent(NULL, false, false, NULL); + DWORD ThreadID; + m_GCThread = CreateThread(NULL, 0, garbageCollect, this, 0, &ThreadID); + #endif + + m_bGCStatus = true; + + return 0; +} + +int CUDTUnited::cleanup() +{ + CGuard gcinit(m_InitLock); + + if (--m_iInstanceCount > 0) + return 0; + + //destroy CTimer::EventLock + + if (!m_bGCStatus) + return 0; + + m_bClosing = true; + #ifndef WIN32 + pthread_cond_signal(&m_GCStopCond); + pthread_join(m_GCThread, NULL); + pthread_mutex_destroy(&m_GCStopLock); + pthread_cond_destroy(&m_GCStopCond); + #else + SetEvent(m_GCStopCond); + WaitForSingleObject(m_GCThread, INFINITE); + CloseHandle(m_GCThread); + CloseHandle(m_GCStopLock); + CloseHandle(m_GCStopCond); + #endif + + m_bGCStatus = false; + + // Global destruction code + #ifdef WIN32 + WSACleanup(); + #endif + + return 0; +} + +UDTSOCKET CUDTUnited::newSocket(int af, int type) +{ + if ((type != SOCK_STREAM) && (type != SOCK_DGRAM)) + throw CUDTException(5, 3, 0); + + CUDTSocket* ns = NULL; + + try + { + ns = new CUDTSocket; + ns->m_pUDT = new CUDT; + if (AF_INET == af) + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); + ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; + } + else + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); + ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; + } + } + catch (...) + { + delete ns; + throw CUDTException(3, 2, 0); + } + + CGuard::enterCS(m_IDLock); + ns->m_SocketID = -- m_SocketID; + CGuard::leaveCS(m_IDLock); + + ns->m_Status = INIT; + ns->m_ListenSocket = 0; + ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->m_pUDT->m_iSockType = (SOCK_STREAM == type) ? UDT_STREAM : UDT_DGRAM; + ns->m_pUDT->m_iIPversion = ns->m_iIPversion = af; + ns->m_pUDT->m_pCache = m_pCache; + + // protect the m_Sockets structure. + CGuard::enterCS(m_ControlLock); + try + { + m_Sockets[ns->m_SocketID] = ns; + } + catch (...) + { + //failure and rollback + CGuard::leaveCS(m_ControlLock); + delete ns; + ns = NULL; + } + CGuard::leaveCS(m_ControlLock); + + if (NULL == ns) + throw CUDTException(3, 2, 0); + + return ns->m_SocketID; +} + +int CUDTUnited::newConnection(const UDTSOCKET listen, const sockaddr* peer, CHandShake* hs) +{ + CUDTSocket* ns = NULL; + CUDTSocket* ls = locate(listen); + + if (NULL == ls) + return -1; + + // if this connection has already been processed + if (NULL != (ns = locate(peer, hs->m_iID, hs->m_iISN))) + { + if (ns->m_pUDT->m_bBroken) + { + // last connection from the "peer" address has been broken + ns->m_Status = CLOSED; + ns->m_TimeStamp = CTimer::getTime(); + + CGuard::enterCS(ls->m_AcceptLock); + ls->m_pQueuedSockets->erase(ns->m_SocketID); + ls->m_pAcceptSockets->erase(ns->m_SocketID); + CGuard::leaveCS(ls->m_AcceptLock); + } + else + { + // connection already exist, this is a repeated connection request + // respond with existing HS information + + hs->m_iISN = ns->m_pUDT->m_iISN; + hs->m_iMSS = ns->m_pUDT->m_iMSS; + hs->m_iFlightFlagSize = ns->m_pUDT->m_iFlightFlagSize; + hs->m_iReqType = -1; + hs->m_iID = ns->m_SocketID; + + return 0; + + //except for this situation a new connection should be started + } + } + + // exceeding backlog, refuse the connection request + if (ls->m_pQueuedSockets->size() >= ls->m_uiBackLog) + return -1; + + try + { + ns = new CUDTSocket; + ns->m_pUDT = new CUDT(*(ls->m_pUDT)); + if (AF_INET == ls->m_iIPversion) + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); + ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; + ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in); + memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in)); + } + else + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); + ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; + ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); + memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in6)); + } + } + catch (...) + { + delete ns; + return -1; + } + + CGuard::enterCS(m_IDLock); + ns->m_SocketID = -- m_SocketID; + CGuard::leaveCS(m_IDLock); + + ns->m_ListenSocket = listen; + ns->m_iIPversion = ls->m_iIPversion; + ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->m_PeerID = hs->m_iID; + ns->m_iISN = hs->m_iISN; + + int error = 0; + + try + { + // bind to the same addr of listening socket + ns->m_pUDT->open(); + updateMux(ns, ls); + ns->m_pUDT->connect(peer, hs); + } + catch (...) + { + error = 1; + goto ERR_ROLLBACK; + } + + ns->m_Status = CONNECTED; + + // copy address information of local node + ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(ns->m_pSelfAddr); + CIPAddress::pton(ns->m_pSelfAddr, ns->m_pUDT->m_piSelfIP, ns->m_iIPversion); + + // protect the m_Sockets structure. + CGuard::enterCS(m_ControlLock); + try + { + m_Sockets[ns->m_SocketID] = ns; + m_PeerRec[(ns->m_PeerID << 30) + ns->m_iISN].insert(ns->m_SocketID); + } + catch (...) + { + error = 2; + } + CGuard::leaveCS(m_ControlLock); + + CGuard::enterCS(ls->m_AcceptLock); + try + { + ls->m_pQueuedSockets->insert(ns->m_SocketID); + } + catch (...) + { + error = 3; + } + CGuard::leaveCS(ls->m_AcceptLock); + + // acknowledge users waiting for new connections on the listening socket + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, true); + + CTimer::triggerEvent(); + + ERR_ROLLBACK: + if (error > 0) + { + ns->m_pUDT->close(); + ns->m_Status = CLOSED; + ns->m_TimeStamp = CTimer::getTime(); + + return -1; + } + + // wake up a waiting accept() call + #ifndef WIN32 + pthread_mutex_lock(&(ls->m_AcceptLock)); + pthread_cond_signal(&(ls->m_AcceptCond)); + pthread_mutex_unlock(&(ls->m_AcceptLock)); + #else + SetEvent(ls->m_AcceptCond); + #endif + + return 1; +} + +CUDT* CUDTUnited::lookup(const UDTSOCKET u) +{ + // protects the m_Sockets structure + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) + throw CUDTException(5, 4, 0); + + return i->second->m_pUDT; +} + +UDTSTATUS CUDTUnited::getStatus(const UDTSOCKET u) +{ + // protects the m_Sockets structure + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if (i == m_Sockets.end()) + { + if (m_ClosedSockets.find(u) != m_ClosedSockets.end()) + return CLOSED; + + return NONEXIST; + } + + if (i->second->m_pUDT->m_bBroken) + return BROKEN; + + return i->second->m_Status; +} + +int CUDTUnited::bind(const UDTSOCKET u, const sockaddr* name, int namelen) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // cannot bind a socket more than once + if (INIT != s->m_Status) + throw CUDTException(5, 0, 0); + + // check the size of SOCKADDR structure + if (AF_INET == s->m_iIPversion) + { + if (namelen != sizeof(sockaddr_in)) + throw CUDTException(5, 3, 0); + } + else + { + if (namelen != sizeof(sockaddr_in6)) + throw CUDTException(5, 3, 0); + } + + s->m_pUDT->open(); + updateMux(s, name); + s->m_Status = OPENED; + + // copy address information of local node + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + + return 0; +} + +int CUDTUnited::bind(UDTSOCKET u, UDPSOCKET udpsock) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // cannot bind a socket more than once + if (INIT != s->m_Status) + throw CUDTException(5, 0, 0); + + sockaddr_in name4; + sockaddr_in6 name6; + sockaddr* name; + socklen_t namelen; + + if (AF_INET == s->m_iIPversion) + { + namelen = sizeof(sockaddr_in); + name = (sockaddr*)&name4; + } + else + { + namelen = sizeof(sockaddr_in6); + name = (sockaddr*)&name6; + } + + if (-1 == ::getsockname(udpsock, name, &namelen)) + throw CUDTException(5, 3); + + s->m_pUDT->open(); + updateMux(s, name, &udpsock); + s->m_Status = OPENED; + + // copy address information of local node + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + + return 0; +} + +int CUDTUnited::listen(const UDTSOCKET u, int backlog) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // do nothing if the socket is already listening + if (LISTENING == s->m_Status) + return 0; + + // a socket can listen only if is in OPENED status + if (OPENED != s->m_Status) + throw CUDTException(5, 5, 0); + + // listen is not supported in rendezvous connection setup + if (s->m_pUDT->m_bRendezvous) + throw CUDTException(5, 7, 0); + + if (backlog <= 0) + throw CUDTException(5, 3, 0); + + s->m_uiBackLog = backlog; + + try + { + s->m_pQueuedSockets = new set; + s->m_pAcceptSockets = new set; + } + catch (...) + { + delete s->m_pQueuedSockets; + delete s->m_pAcceptSockets; + throw CUDTException(3, 2, 0); + } + + s->m_pUDT->listen(); + + s->m_Status = LISTENING; + + return 0; +} + +UDTSOCKET CUDTUnited::accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen) +{ + if ((NULL != addr) && (NULL == addrlen)) + throw CUDTException(5, 3, 0); + + CUDTSocket* ls = locate(listen); + + if (ls == NULL) + throw CUDTException(5, 4, 0); + + // the "listen" socket must be in LISTENING status + if (LISTENING != ls->m_Status) + throw CUDTException(5, 6, 0); + + // no "accept" in rendezvous connection setup + if (ls->m_pUDT->m_bRendezvous) + throw CUDTException(5, 7, 0); + + UDTSOCKET u = CUDT::INVALID_SOCK; + bool accepted = false; + + // !!only one conection can be set up each time!! + #ifndef WIN32 + while (!accepted) + { + pthread_mutex_lock(&(ls->m_AcceptLock)); + + if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) + { + // This socket has been closed. + accepted = true; + } + else if (ls->m_pQueuedSockets->size() > 0) + { + u = *(ls->m_pQueuedSockets->begin()); + ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); + ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); + accepted = true; + } + else if (!ls->m_pUDT->m_bSynRecving) + { + accepted = true; + } + + if (!accepted && (LISTENING == ls->m_Status)) + pthread_cond_wait(&(ls->m_AcceptCond), &(ls->m_AcceptLock)); + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); + + pthread_mutex_unlock(&(ls->m_AcceptLock)); + } + #else + while (!accepted) + { + WaitForSingleObject(ls->m_AcceptLock, INFINITE); + + if (ls->m_pQueuedSockets->size() > 0) + { + u = *(ls->m_pQueuedSockets->begin()); + ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); + ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); + + accepted = true; + } + else if (!ls->m_pUDT->m_bSynRecving) + accepted = true; + + ReleaseMutex(ls->m_AcceptLock); + + if (!accepted & (LISTENING == ls->m_Status)) + WaitForSingleObject(ls->m_AcceptCond, INFINITE); + + if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) + { + // Send signal to other threads that are waiting to accept. + SetEvent(ls->m_AcceptCond); + accepted = true; + } + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); + } + #endif + + if (u == CUDT::INVALID_SOCK) + { + // non-blocking receiving, no connection available + if (!ls->m_pUDT->m_bSynRecving) + throw CUDTException(6, 2, 0); + + // listening socket is closed + throw CUDTException(5, 6, 0); + } + + if ((addr != NULL) && (addrlen != NULL)) + { + if (AF_INET == locate(u)->m_iIPversion) + *addrlen = sizeof(sockaddr_in); + else + *addrlen = sizeof(sockaddr_in6); + + // copy address information of peer node + memcpy(addr, locate(u)->m_pPeerAddr, *addrlen); + } + + return u; +} + +int CUDTUnited::connect(const UDTSOCKET u, const sockaddr* name, int namelen) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // check the size of SOCKADDR structure + if (AF_INET == s->m_iIPversion) + { + if (namelen != sizeof(sockaddr_in)) + throw CUDTException(5, 3, 0); + } + else + { + if (namelen != sizeof(sockaddr_in6)) + throw CUDTException(5, 3, 0); + } + + // a socket can "connect" only if it is in INIT or OPENED status + if (INIT == s->m_Status) + { + if (!s->m_pUDT->m_bRendezvous) + { + s->m_pUDT->open(); + updateMux(s); + s->m_Status = OPENED; + } + else + throw CUDTException(5, 8, 0); + } + else if (OPENED != s->m_Status) + throw CUDTException(5, 2, 0); + + // connect_complete() may be called before connect() returns. + // So we need to update the status before connect() is called, + // otherwise the status may be overwritten with wrong value (CONNECTED vs. CONNECTING). + s->m_Status = CONNECTING; + try + { + s->m_pUDT->connect(name); + } + catch (CUDTException e) + { + s->m_Status = OPENED; + throw e; + } + + // record peer address + delete s->m_pPeerAddr; + if (AF_INET == s->m_iIPversion) + { + s->m_pPeerAddr = (sockaddr*)(new sockaddr_in); + memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in)); + } + else + { + s->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); + memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in6)); + } + + return 0; +} + +void CUDTUnited::connect_complete(const UDTSOCKET u) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + // copy address information of local node + // the local port must be correctly assigned BEFORE CUDT::connect(), + // otherwise if connect() fails, the multiplexer cannot be located by garbage collection and will cause leak + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + CIPAddress::pton(s->m_pSelfAddr, s->m_pUDT->m_piSelfIP, s->m_iIPversion); + + s->m_Status = CONNECTED; +} + +int CUDTUnited::close(const UDTSOCKET u) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard socket_cg(s->m_ControlLock); + + if (s->m_Status == LISTENING) + { + if (s->m_pUDT->m_bBroken) + return 0; + + s->m_TimeStamp = CTimer::getTime(); + s->m_pUDT->m_bBroken = true; + + // broadcast all "accept" waiting + #ifndef WIN32 + pthread_mutex_lock(&(s->m_AcceptLock)); + pthread_cond_broadcast(&(s->m_AcceptCond)); + pthread_mutex_unlock(&(s->m_AcceptLock)); + #else + SetEvent(s->m_AcceptCond); + #endif + + return 0; + } + + s->m_pUDT->close(); + + // synchronize with garbage collection. + CGuard manager_cg(m_ControlLock); + + // since "s" is located before m_ControlLock, locate it again in case it became invalid + map::iterator i = m_Sockets.find(u); + if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) + return 0; + s = i->second; + + s->m_Status = CLOSED; + + // a socket will not be immediated removed when it is closed + // in order to prevent other methods from accessing invalid address + // a timer is started and the socket will be removed after approximately 1 second + s->m_TimeStamp = CTimer::getTime(); + + m_Sockets.erase(s->m_SocketID); + m_ClosedSockets.insert(pair(s->m_SocketID, s)); + + CTimer::triggerEvent(); + + return 0; +} + +int CUDTUnited::getpeername(const UDTSOCKET u, sockaddr* name, int* namelen) +{ + if (CONNECTED != getStatus(u)) + throw CUDTException(2, 2, 0); + + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken) + throw CUDTException(2, 2, 0); + + if (AF_INET == s->m_iIPversion) + *namelen = sizeof(sockaddr_in); + else + *namelen = sizeof(sockaddr_in6); + + // copy address information of peer node + memcpy(name, s->m_pPeerAddr, *namelen); + + return 0; +} + +int CUDTUnited::getsockname(const UDTSOCKET u, sockaddr* name, int* namelen) +{ + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + if (s->m_pUDT->m_bBroken) + throw CUDTException(5, 4, 0); + + if (INIT == s->m_Status) + throw CUDTException(2, 2, 0); + + if (AF_INET == s->m_iIPversion) + *namelen = sizeof(sockaddr_in); + else + *namelen = sizeof(sockaddr_in6); + + // copy address information of local node + memcpy(name, s->m_pSelfAddr, *namelen); + + return 0; +} + +int CUDTUnited::select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) +{ + uint64_t entertime = CTimer::getTime(); + + uint64_t to; + if (NULL == timeout) + to = 0xFFFFFFFFFFFFFFFFULL; + else + to = timeout->tv_sec * 1000000 + timeout->tv_usec; + + // initialize results + int count = 0; + set rs, ws, es; + + // retrieve related UDT sockets + vector ru, wu, eu; + CUDTSocket* s; + if (NULL != readfds) + for (set::iterator i1 = readfds->begin(); i1 != readfds->end(); ++ i1) + { + if (BROKEN == getStatus(*i1)) + { + rs.insert(*i1); + ++ count; + } + else if (NULL == (s = locate(*i1))) + throw CUDTException(5, 4, 0); + else + ru.push_back(s); + } + if (NULL != writefds) + for (set::iterator i2 = writefds->begin(); i2 != writefds->end(); ++ i2) + { + if (BROKEN == getStatus(*i2)) + { + ws.insert(*i2); + ++ count; + } + else if (NULL == (s = locate(*i2))) + throw CUDTException(5, 4, 0); + else + wu.push_back(s); + } + if (NULL != exceptfds) + for (set::iterator i3 = exceptfds->begin(); i3 != exceptfds->end(); ++ i3) + { + if (BROKEN == getStatus(*i3)) + { + es.insert(*i3); + ++ count; + } + else if (NULL == (s = locate(*i3))) + throw CUDTException(5, 4, 0); + else + eu.push_back(s); + } + + do + { + // query read sockets + for (vector::iterator j1 = ru.begin(); j1 != ru.end(); ++ j1) + { + s = *j1; + + if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && ((s->m_pUDT->m_iSockType == UDT_STREAM) || (s->m_pUDT->m_pRcvBuffer->getRcvMsgNum() > 0))) + || (!s->m_pUDT->m_bListening && (s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected)) + || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0)) + || (s->m_Status == CLOSED)) + { + rs.insert(s->m_SocketID); + ++ count; + } + } + + // query write sockets + for (vector::iterator j2 = wu.begin(); j2 != wu.end(); ++ j2) + { + s = *j2; + + if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() < s->m_pUDT->m_iSndBufSize)) + || s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected || (s->m_Status == CLOSED)) + { + ws.insert(s->m_SocketID); + ++ count; + } + } + + // query exceptions on sockets + for (vector::iterator j3 = eu.begin(); j3 != eu.end(); ++ j3) + { + // check connection request status, not supported now + } + + if (0 < count) + break; + + CTimer::waitForEvent(); + } while (to > CTimer::getTime() - entertime); + + if (NULL != readfds) + *readfds = rs; + + if (NULL != writefds) + *writefds = ws; + + if (NULL != exceptfds) + *exceptfds = es; + + return count; +} + +int CUDTUnited::selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) +{ + uint64_t entertime = CTimer::getTime(); + + uint64_t to; + if (msTimeOut >= 0) + to = msTimeOut * 1000; + else + to = 0xFFFFFFFFFFFFFFFFULL; + + // initialize results + int count = 0; + if (NULL != readfds) + readfds->clear(); + if (NULL != writefds) + writefds->clear(); + if (NULL != exceptfds) + exceptfds->clear(); + + do + { + for (vector::const_iterator i = fds.begin(); i != fds.end(); ++ i) + { + CUDTSocket* s = locate(*i); + + if ((NULL == s) || s->m_pUDT->m_bBroken || (s->m_Status == CLOSED)) + { + if (NULL != exceptfds) + { + exceptfds->push_back(*i); + ++ count; + } + continue; + } + + if (NULL != readfds) + { + if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && ((s->m_pUDT->m_iSockType == UDT_STREAM) || (s->m_pUDT->m_pRcvBuffer->getRcvMsgNum() > 0))) + || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0))) + { + readfds->push_back(s->m_SocketID); + ++ count; + } + } + + if (NULL != writefds) + { + if (s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() < s->m_pUDT->m_iSndBufSize)) + { + writefds->push_back(s->m_SocketID); + ++ count; + } + } + } + + if (count > 0) + break; + + CTimer::waitForEvent(); + } while (to > CTimer::getTime() - entertime); + + return count; +} + +int CUDTUnited::epoll_create() +{ + return m_EPoll.create(); +} + +int CUDTUnited::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) +{ + CUDTSocket* s = locate(u); + int ret = -1; + if (NULL != s) + { + ret = m_EPoll.add_usock(eid, u, events); + s->m_pUDT->addEPoll(eid); + } + else + { + throw CUDTException(5, 4); + } + + return ret; +} + +int CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + return m_EPoll.add_ssock(eid, s, events); +} + +int CUDTUnited::epoll_remove_usock(const int eid, const UDTSOCKET u) +{ + int ret = m_EPoll.remove_usock(eid, u); + + CUDTSocket* s = locate(u); + if (NULL != s) + { + s->m_pUDT->removeEPoll(eid); + } + //else + //{ + // throw CUDTException(5, 4); + //} + + return ret; +} + +int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +{ + return m_EPoll.remove_ssock(eid, s); +} + +int CUDTUnited::epoll_wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + return m_EPoll.wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +int CUDTUnited::epoll_release(const int eid) +{ + return m_EPoll.release(eid); +} + +CUDTSocket* CUDTUnited::locate(const UDTSOCKET u) +{ + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) + return NULL; + + return i->second; +} + +CUDTSocket* CUDTUnited::locate(const sockaddr* peer, const UDTSOCKET id, int32_t isn) +{ + CGuard cg(m_ControlLock); + + map >::iterator i = m_PeerRec.find((id << 30) + isn); + if (i == m_PeerRec.end()) + return NULL; + + for (set::iterator j = i->second.begin(); j != i->second.end(); ++ j) + { + map::iterator k = m_Sockets.find(*j); + // this socket might have been closed and moved m_ClosedSockets + if (k == m_Sockets.end()) + continue; + + if (CIPAddress::ipcmp(peer, k->second->m_pPeerAddr, k->second->m_iIPversion)) + return k->second; + } + + return NULL; +} + +void CUDTUnited::checkBrokenSockets() +{ + CGuard cg(m_ControlLock); + + // set of sockets To Be Closed and To Be Removed + vector tbc; + vector tbr; + + for (map::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++ i) + { + // check broken connection + if (i->second->m_pUDT->m_bBroken) + { + if (i->second->m_Status == LISTENING) + { + // for a listening socket, it should wait an extra 3 seconds in case a client is connecting + if (CTimer::getTime() - i->second->m_TimeStamp < 3000000) + continue; + } + else if ((i->second->m_pUDT->m_pRcvBuffer != NULL) && (i->second->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && (i->second->m_pUDT->m_iBrokenCounter -- > 0)) + { + // if there is still data in the receiver buffer, wait longer + continue; + } + + //close broken connections and start removal timer + i->second->m_Status = CLOSED; + i->second->m_TimeStamp = CTimer::getTime(); + tbc.push_back(i->first); + m_ClosedSockets[i->first] = i->second; + + // remove from listener's queue + map::iterator ls = m_Sockets.find(i->second->m_ListenSocket); + if (ls == m_Sockets.end()) + { + ls = m_ClosedSockets.find(i->second->m_ListenSocket); + if (ls == m_ClosedSockets.end()) + continue; + } + + CGuard::enterCS(ls->second->m_AcceptLock); + ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); + ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); + CGuard::leaveCS(ls->second->m_AcceptLock); + } + } + + for (map::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++ j) + { + if (j->second->m_pUDT->m_ullLingerExpiration > 0) + { + // asynchronous close: + if ((NULL == j->second->m_pUDT->m_pSndBuffer) || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) || (j->second->m_pUDT->m_ullLingerExpiration <= CTimer::getTime())) + { + j->second->m_pUDT->m_ullLingerExpiration = 0; + j->second->m_pUDT->m_bClosing = true; + j->second->m_TimeStamp = CTimer::getTime(); + } + } + + // timeout 1 second to destroy a socket AND it has been removed from RcvUList + if ((CTimer::getTime() - j->second->m_TimeStamp > 1000000) && ((NULL == j->second->m_pUDT->m_pRNode) || !j->second->m_pUDT->m_pRNode->m_bOnList)) + { + tbr.push_back(j->first); + } + } + + // move closed sockets to the ClosedSockets structure + for (vector::iterator k = tbc.begin(); k != tbc.end(); ++ k) + m_Sockets.erase(*k); + + // remove those timeout sockets + for (vector::iterator l = tbr.begin(); l != tbr.end(); ++ l) + removeSocket(*l); +} + +void CUDTUnited::removeSocket(const UDTSOCKET u) +{ + map::iterator i = m_ClosedSockets.find(u); + + // invalid socket ID + if (i == m_ClosedSockets.end()) + return; + + // decrease multiplexer reference count, and remove it if necessary + const int mid = i->second->m_iMuxID; + + if (NULL != i->second->m_pQueuedSockets) + { + CGuard::enterCS(i->second->m_AcceptLock); + + // if it is a listener, close all un-accepted sockets in its queue and remove them later + for (set::iterator q = i->second->m_pQueuedSockets->begin(); q != i->second->m_pQueuedSockets->end(); ++ q) + { + m_Sockets[*q]->m_pUDT->m_bBroken = true; + m_Sockets[*q]->m_pUDT->close(); + m_Sockets[*q]->m_TimeStamp = CTimer::getTime(); + m_Sockets[*q]->m_Status = CLOSED; + m_ClosedSockets[*q] = m_Sockets[*q]; + m_Sockets.erase(*q); + } + + CGuard::leaveCS(i->second->m_AcceptLock); + } + + // remove from peer rec + map >::iterator j = m_PeerRec.find((i->second->m_PeerID << 30) + i->second->m_iISN); + if (j != m_PeerRec.end()) + { + j->second.erase(u); + if (j->second.empty()) + m_PeerRec.erase(j); + } + + // delete this one + i->second->m_pUDT->close(); + delete i->second; + m_ClosedSockets.erase(i); + + map::iterator m; + m = m_mMultiplexer.find(mid); + if (m == m_mMultiplexer.end()) + { + //something is wrong!!! + return; + } + + m->second.m_iRefCount --; + if (0 == m->second.m_iRefCount) + { + m->second.m_pChannel->close(); + delete m->second.m_pSndQueue; + delete m->second.m_pRcvQueue; + delete m->second.m_pTimer; + delete m->second.m_pChannel; + m_mMultiplexer.erase(m); + } +} + +void CUDTUnited::setError(CUDTException* e) +{ + #ifndef WIN32 + delete (CUDTException*)pthread_getspecific(m_TLSError); + pthread_setspecific(m_TLSError, e); + #else + CGuard tg(m_TLSLock); + delete (CUDTException*)TlsGetValue(m_TLSError); + TlsSetValue(m_TLSError, e); + m_mTLSRecord[GetCurrentThreadId()] = e; + #endif +} + +CUDTException* CUDTUnited::getError() +{ + #ifndef WIN32 + if(NULL == pthread_getspecific(m_TLSError)) + pthread_setspecific(m_TLSError, new CUDTException); + return (CUDTException*)pthread_getspecific(m_TLSError); + #else + CGuard tg(m_TLSLock); + if(NULL == TlsGetValue(m_TLSError)) + { + CUDTException* e = new CUDTException; + TlsSetValue(m_TLSError, e); + m_mTLSRecord[GetCurrentThreadId()] = e; + } + return (CUDTException*)TlsGetValue(m_TLSError); + #endif +} + +#ifdef WIN32 +void CUDTUnited::checkTLSValue() +{ + CGuard tg(m_TLSLock); + + vector tbr; + for (map::iterator i = m_mTLSRecord.begin(); i != m_mTLSRecord.end(); ++ i) + { + HANDLE h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, i->first); + if (NULL == h) + { + tbr.push_back(i->first); + break; + } + if (WAIT_OBJECT_0 == WaitForSingleObject(h, 0)) + { + delete i->second; + tbr.push_back(i->first); + } + CloseHandle(h); + } + for (vector::iterator j = tbr.begin(); j != tbr.end(); ++ j) + m_mTLSRecord.erase(*j); +} +#endif + +void CUDTUnited::updateMux(CUDTSocket* s, const sockaddr* addr, const UDPSOCKET* udpsock) +{ + CGuard cg(m_ControlLock); + + if ((s->m_pUDT->m_bReuseAddr) && (NULL != addr)) + { + int port = (AF_INET == s->m_pUDT->m_iIPversion) ? ntohs(((sockaddr_in*)addr)->sin_port) : ntohs(((sockaddr_in6*)addr)->sin6_port); + + // find a reusable address + for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++ i) + { + if ((i->second.m_iIPversion == s->m_pUDT->m_iIPversion) && (i->second.m_iMSS == s->m_pUDT->m_iMSS) && i->second.m_bReusable) + { + if (i->second.m_iPort == port) + { + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; + s->m_iMuxID = i->second.m_iID; + return; + } + } + } + } + + // a new multiplexer is needed + CMultiplexer m; + m.m_iMSS = s->m_pUDT->m_iMSS; + m.m_iIPversion = s->m_pUDT->m_iIPversion; + m.m_iRefCount = 1; + m.m_bReusable = s->m_pUDT->m_bReuseAddr; + m.m_iID = s->m_SocketID; + + m.m_pChannel = new CChannel(s->m_pUDT->m_iIPversion); + m.m_pChannel->setSndBufSize(s->m_pUDT->m_iUDPSndBufSize); + m.m_pChannel->setRcvBufSize(s->m_pUDT->m_iUDPRcvBufSize); + + try + { + if (NULL != udpsock) + m.m_pChannel->open(*udpsock); + else + m.m_pChannel->open(addr); + } + catch (CUDTException& e) + { + m.m_pChannel->close(); + delete m.m_pChannel; + throw e; + } + + sockaddr* sa = (AF_INET == s->m_pUDT->m_iIPversion) ? (sockaddr*) new sockaddr_in : (sockaddr*) new sockaddr_in6; + m.m_pChannel->getSockAddr(sa); + m.m_iPort = (AF_INET == s->m_pUDT->m_iIPversion) ? ntohs(((sockaddr_in*)sa)->sin_port) : ntohs(((sockaddr_in6*)sa)->sin6_port); + if (AF_INET == s->m_pUDT->m_iIPversion) delete (sockaddr_in*)sa; else delete (sockaddr_in6*)sa; + + m.m_pTimer = new CTimer; + + m.m_pSndQueue = new CSndQueue; + m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); + m.m_pRcvQueue = new CRcvQueue; + m.m_pRcvQueue->init(32, s->m_pUDT->m_iPayloadSize, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + + m_mMultiplexer[m.m_iID] = m; + + s->m_pUDT->m_pSndQueue = m.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = m.m_pRcvQueue; + s->m_iMuxID = m.m_iID; +} + +void CUDTUnited::updateMux(CUDTSocket* s, const CUDTSocket* ls) +{ + CGuard cg(m_ControlLock); + + int port = (AF_INET == ls->m_iIPversion) ? ntohs(((sockaddr_in*)ls->m_pSelfAddr)->sin_port) : ntohs(((sockaddr_in6*)ls->m_pSelfAddr)->sin6_port); + + // find the listener's address + for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++ i) + { + if (i->second.m_iPort == port) + { + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; + s->m_iMuxID = i->second.m_iID; + return; + } + } +} + +#ifndef WIN32 + void* CUDTUnited::garbageCollect(void* p) +#else + DWORD WINAPI CUDTUnited::garbageCollect(LPVOID p) +#endif +{ + CUDTUnited* self = (CUDTUnited*)p; + + CGuard gcguard(self->m_GCStopLock); + + while (!self->m_bClosing) + { + self->checkBrokenSockets(); + + #ifdef WIN32 + self->checkTLSValue(); + #endif + + #ifndef WIN32 + timeval now; + timespec timeout; + gettimeofday(&now, 0); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + + pthread_cond_timedwait(&self->m_GCStopCond, &self->m_GCStopLock, &timeout); + #else + WaitForSingleObject(self->m_GCStopCond, 1000); + #endif + } + + // remove all sockets and multiplexers + CGuard::enterCS(self->m_ControlLock); + for (map::iterator i = self->m_Sockets.begin(); i != self->m_Sockets.end(); ++ i) + { + i->second->m_pUDT->m_bBroken = true; + i->second->m_pUDT->close(); + i->second->m_Status = CLOSED; + i->second->m_TimeStamp = CTimer::getTime(); + self->m_ClosedSockets[i->first] = i->second; + + // remove from listener's queue + map::iterator ls = self->m_Sockets.find(i->second->m_ListenSocket); + if (ls == self->m_Sockets.end()) + { + ls = self->m_ClosedSockets.find(i->second->m_ListenSocket); + if (ls == self->m_ClosedSockets.end()) + continue; + } + + CGuard::enterCS(ls->second->m_AcceptLock); + ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); + ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); + CGuard::leaveCS(ls->second->m_AcceptLock); + } + self->m_Sockets.clear(); + + for (map::iterator j = self->m_ClosedSockets.begin(); j != self->m_ClosedSockets.end(); ++ j) + { + j->second->m_TimeStamp = 0; + } + CGuard::leaveCS(self->m_ControlLock); + + while (true) + { + self->checkBrokenSockets(); + + CGuard::enterCS(self->m_ControlLock); + bool empty = self->m_ClosedSockets.empty(); + CGuard::leaveCS(self->m_ControlLock); + + if (empty) + break; + + CTimer::sleep(); + } + + #ifndef WIN32 + return NULL; + #else + return 0; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// + +int CUDT::startup() +{ + return s_UDTUnited.startup(); +} + +int CUDT::cleanup() +{ + return s_UDTUnited.cleanup(); +} + +UDTSOCKET CUDT::socket(int af, int type, int) +{ + if (!s_UDTUnited.m_bGCStatus) + s_UDTUnited.startup(); + + try + { + return s_UDTUnited.newSocket(af, type); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return INVALID_SOCK; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return INVALID_SOCK; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return INVALID_SOCK; + } +} + +int CUDT::bind(UDTSOCKET u, const sockaddr* name, int namelen) +{ + try + { + return s_UDTUnited.bind(u, name, namelen); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::bind(UDTSOCKET u, UDPSOCKET udpsock) +{ + try + { + return s_UDTUnited.bind(u, udpsock); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::listen(UDTSOCKET u, int backlog) +{ + try + { + return s_UDTUnited.listen(u, backlog); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +UDTSOCKET CUDT::accept(UDTSOCKET u, sockaddr* addr, int* addrlen) +{ + try + { + return s_UDTUnited.accept(u, addr, addrlen); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return INVALID_SOCK; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return INVALID_SOCK; + } +} + +int CUDT::connect(UDTSOCKET u, const sockaddr* name, int namelen) +{ + try + { + return s_UDTUnited.connect(u, name, namelen); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::close(UDTSOCKET u) +{ + try + { + return s_UDTUnited.close(u); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::getpeername(UDTSOCKET u, sockaddr* name, int* namelen) +{ + try + { + return s_UDTUnited.getpeername(u, name, namelen); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::getsockname(UDTSOCKET u, sockaddr* name, int* namelen) +{ + try + { + return s_UDTUnited.getsockname(u, name, namelen);; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::getsockopt(UDTSOCKET u, int, UDTOpt optname, void* optval, int* optlen) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->getOpt(optname, optval, *optlen); + return 0; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::setsockopt(UDTSOCKET u, int, UDTOpt optname, const void* optval, int optlen) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->setOpt(optname, optval, optlen); + return 0; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::send(UDTSOCKET u, const char* buf, int len, int) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->send(buf, len); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::recv(UDTSOCKET u, char* buf, int len, int) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recv(buf, len); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::sendmsg(UDTSOCKET u, const char* buf, int len, int ttl, bool inorder) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendmsg(buf, len, ttl, inorder); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::recvmsg(UDTSOCKET u, char* buf, int len) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvmsg(buf, len); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int64_t CUDT::sendfile(UDTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendfile(ifs, offset, size, block); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int64_t CUDT::recvfile(UDTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvfile(ofs, offset, size, block); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::select(int, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) +{ + if ((NULL == readfds) && (NULL == writefds) && (NULL == exceptfds)) + { + s_UDTUnited.setError(new CUDTException(5, 3, 0)); + return ERROR; + } + + try + { + return s_UDTUnited.select(readfds, writefds, exceptfds, timeout); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) +{ + if ((NULL == readfds) && (NULL == writefds) && (NULL == exceptfds)) + { + s_UDTUnited.setError(new CUDTException(5, 3, 0)); + return ERROR; + } + + try + { + return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_create() +{ + try + { + return s_UDTUnited.epoll_create(); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) +{ + try + { + return s_UDTUnited.epoll_add_usock(eid, u, events); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + try + { + return s_UDTUnited.epoll_add_ssock(eid, s, events); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_remove_usock(const int eid, const UDTSOCKET u) +{ + try + { + return s_UDTUnited.epoll_remove_usock(eid, u); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +{ + try + { + return s_UDTUnited.epoll_remove_ssock(eid, s); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + try + { + return s_UDTUnited.epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_release(const int eid) +{ + try + { + return s_UDTUnited.epoll_release(eid); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +CUDTException& CUDT::getlasterror() +{ + return *s_UDTUnited.getError(); +} + +int CUDT::perfmon(UDTSOCKET u, CPerfMon* perf, bool clear) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->sample(perf, clear); + return 0; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +CUDT* CUDT::getUDTHandle(UDTSOCKET u) +{ + try + { + return s_UDTUnited.lookup(u); + } + catch (...) + { + return NULL; + } +} + +UDTSTATUS CUDT::getsockstate(UDTSOCKET u) +{ + try + { + return s_UDTUnited.getStatus(u); + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return NONEXIST; + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +namespace UDT +{ + +int startup() +{ + return CUDT::startup(); +} + +int cleanup() +{ + return CUDT::cleanup(); +} + +UDTSOCKET socket(int af, int type, int protocol) +{ + return CUDT::socket(af, type, protocol); +} + +int bind(UDTSOCKET u, const struct sockaddr* name, int namelen) +{ + return CUDT::bind(u, name, namelen); +} + +int bind2(UDTSOCKET u, UDPSOCKET udpsock) +{ + return CUDT::bind(u, udpsock); +} + +int listen(UDTSOCKET u, int backlog) +{ + return CUDT::listen(u, backlog); +} + +UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen) +{ + return CUDT::accept(u, addr, addrlen); +} + +int connect(UDTSOCKET u, const struct sockaddr* name, int namelen) +{ + return CUDT::connect(u, name, namelen); +} + +int close(UDTSOCKET u) +{ + return CUDT::close(u); +} + +int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen) +{ + return CUDT::getpeername(u, name, namelen); +} + +int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen) +{ + return CUDT::getsockname(u, name, namelen); +} + +int getsockopt(UDTSOCKET u, int level, SOCKOPT optname, void* optval, int* optlen) +{ + return CUDT::getsockopt(u, level, optname, optval, optlen); +} + +int setsockopt(UDTSOCKET u, int level, SOCKOPT optname, const void* optval, int optlen) +{ + return CUDT::setsockopt(u, level, optname, optval, optlen); +} + +int send(UDTSOCKET u, const char* buf, int len, int flags) +{ + return CUDT::send(u, buf, len, flags); +} + +int recv(UDTSOCKET u, char* buf, int len, int flags) +{ + return CUDT::recv(u, buf, len, flags); +} + +int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl, bool inorder) +{ + return CUDT::sendmsg(u, buf, len, ttl, inorder); +} + +int recvmsg(UDTSOCKET u, char* buf, int len) +{ + return CUDT::recvmsg(u, buf, len); +} + +int64_t sendfile(UDTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) +{ + return CUDT::sendfile(u, ifs, offset, size, block); +} + +int64_t recvfile(UDTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) +{ + return CUDT::recvfile(u, ofs, offset, size, block); +} + +int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) +{ + fstream ifs(path, ios::binary | ios::in); + int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); + ifs.close(); + return ret; +} + +int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) +{ + fstream ofs(path, ios::binary | ios::out); + int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); + ofs.close(); + return ret; +} + +int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout) +{ + return CUDT::select(nfds, readfds, writefds, exceptfds, timeout); +} + +int selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) +{ + return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); +} + +int epoll_create() +{ + return CUDT::epoll_create(); +} + +int epoll_add_usock(int eid, UDTSOCKET u, const int* events) +{ + return CUDT::epoll_add_usock(eid, u, events); +} + +int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) +{ + return CUDT::epoll_add_ssock(eid, s, events); +} + +int epoll_remove_usock(int eid, UDTSOCKET u) +{ + return CUDT::epoll_remove_usock(eid, u); +} + +int epoll_remove_ssock(int eid, SYSSOCKET s) +{ + return CUDT::epoll_remove_ssock(eid, s); +} + +int epoll_wait(int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +#define SET_RESULT(val, num, fds, it) \ + if ((val != NULL) && !val->empty()) \ + { \ + if (*num > static_cast(val->size())) \ + *num = val->size(); \ + int count = 0; \ + for (it = val->begin(); it != val->end(); ++ it) \ + { \ + if (count >= *num) \ + break; \ + fds[count ++] = *it; \ + } \ + } +int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, + SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum) +{ + // This API is an alternative format for epoll_wait, created for compatability with other languages. + // Users need to pass in an array for holding the returned sockets, with the maximum array length + // stored in *rnum, etc., which will be updated with returned number of sockets. + + set readset; + set writeset; + set lrset; + set lwset; + set* rval = NULL; + set* wval = NULL; + set* lrval = NULL; + set* lwval = NULL; + if ((readfds != NULL) && (rnum != NULL)) + rval = &readset; + if ((writefds != NULL) && (wnum != NULL)) + wval = &writeset; + if ((lrfds != NULL) && (lrnum != NULL)) + lrval = &lrset; + if ((lwfds != NULL) && (lwnum != NULL)) + lwval = &lwset; + + int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); + if (ret > 0) + { + set::const_iterator i; + SET_RESULT(rval, rnum, readfds, i); + SET_RESULT(wval, wnum, writefds, i); + set::const_iterator j; + SET_RESULT(lrval, lrnum, lrfds, j); + SET_RESULT(lwval, lwnum, lwfds, j); + } + return ret; +} + +int epoll_release(int eid) +{ + return CUDT::epoll_release(eid); +} + +ERRORINFO& getlasterror() +{ + return CUDT::getlasterror(); +} + +int getlasterror_code() +{ + return CUDT::getlasterror().getErrorCode(); +} + +const char* getlasterror_desc() +{ + return CUDT::getlasterror().getErrorMessage(); +} + +int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear) +{ + return CUDT::perfmon(u, perf, clear); +} + +UDTSTATUS getsockstate(UDTSOCKET u) +{ + return CUDT::getsockstate(u); +} + +} // namespace UDT diff --git a/vendor/udt4/src/api.h b/vendor/udt4/src/api.h new file mode 100644 index 000000000..24f1a028e --- /dev/null +++ b/vendor/udt4/src/api.h @@ -0,0 +1,268 @@ +/***************************************************************************** +Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 09/28/2010 +*****************************************************************************/ + +#ifndef __UDT_API_H__ +#define __UDT_API_H__ + + +#include +#include +#include "udt.h" +#include "packet.h" +#include "queue.h" +#include "cache.h" +#include "epoll.h" + +class CUDT; + +class CUDTSocket +{ +public: + CUDTSocket(); + ~CUDTSocket(); + + UDTSTATUS m_Status; // current socket state + + uint64_t m_TimeStamp; // time when the socket is closed + + int m_iIPversion; // IP version + sockaddr* m_pSelfAddr; // pointer to the local address of the socket + sockaddr* m_pPeerAddr; // pointer to the peer address of the socket + + UDTSOCKET m_SocketID; // socket ID + UDTSOCKET m_ListenSocket; // ID of the listener socket; 0 means this is an independent socket + + UDTSOCKET m_PeerID; // peer socket ID + int32_t m_iISN; // initial sequence number, used to tell different connection from same IP:port + + CUDT* m_pUDT; // pointer to the UDT entity + + std::set* m_pQueuedSockets; // set of connections waiting for accept() + std::set* m_pAcceptSockets; // set of accept()ed connections + + pthread_cond_t m_AcceptCond; // used to block "accept" call + pthread_mutex_t m_AcceptLock; // mutex associated to m_AcceptCond + + unsigned int m_uiBackLog; // maximum number of connections in queue + + int m_iMuxID; // multiplexer ID + + pthread_mutex_t m_ControlLock; // lock this socket exclusively for control APIs: bind/listen/connect + +private: + CUDTSocket(const CUDTSocket&); + CUDTSocket& operator=(const CUDTSocket&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CUDTUnited +{ +friend class CUDT; +friend class CRendezvousQueue; + +public: + CUDTUnited(); + ~CUDTUnited(); + +public: + + // Functionality: + // initialize the UDT library. + // Parameters: + // None. + // Returned value: + // 0 if success, otherwise -1 is returned. + + int startup(); + + // Functionality: + // release the UDT library. + // Parameters: + // None. + // Returned value: + // 0 if success, otherwise -1 is returned. + + int cleanup(); + + // Functionality: + // Create a new UDT socket. + // Parameters: + // 0) [in] af: IP version, IPv4 (AF_INET) or IPv6 (AF_INET6). + // 1) [in] type: socket type, SOCK_STREAM or SOCK_DGRAM + // Returned value: + // The new UDT socket ID, or INVALID_SOCK. + + UDTSOCKET newSocket(int af, int type); + + // Functionality: + // Create a new UDT connection. + // Parameters: + // 0) [in] listen: the listening UDT socket; + // 1) [in] peer: peer address. + // 2) [in/out] hs: handshake information from peer side (in), negotiated value (out); + // Returned value: + // If the new connection is successfully created: 1 success, 0 already exist, -1 error. + + int newConnection(const UDTSOCKET listen, const sockaddr* peer, CHandShake* hs); + + // Functionality: + // look up the UDT entity according to its ID. + // Parameters: + // 0) [in] u: the UDT socket ID. + // Returned value: + // Pointer to the UDT entity. + + CUDT* lookup(const UDTSOCKET u); + + // Functionality: + // Check the status of the UDT socket. + // Parameters: + // 0) [in] u: the UDT socket ID. + // Returned value: + // UDT socket status, or NONEXIST if not found. + + UDTSTATUS getStatus(const UDTSOCKET u); + + // socket APIs + + int bind(const UDTSOCKET u, const sockaddr* name, int namelen); + int bind(const UDTSOCKET u, UDPSOCKET udpsock); + int listen(const UDTSOCKET u, int backlog); + UDTSOCKET accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen); + int connect(const UDTSOCKET u, const sockaddr* name, int namelen); + int close(const UDTSOCKET u); + int getpeername(const UDTSOCKET u, sockaddr* name, int* namelen); + int getsockname(const UDTSOCKET u, sockaddr* name, int* namelen); + int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); + int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + int epoll_create(); + int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); + int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + int epoll_remove_usock(const int eid, const UDTSOCKET u); + int epoll_remove_ssock(const int eid, const SYSSOCKET s); + int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* lwfds = NULL); + int epoll_release(const int eid); + + // Functionality: + // record the UDT exception. + // Parameters: + // 0) [in] e: pointer to a UDT exception instance. + // Returned value: + // None. + + void setError(CUDTException* e); + + // Functionality: + // look up the most recent UDT exception. + // Parameters: + // None. + // Returned value: + // pointer to a UDT exception instance. + + CUDTException* getError(); + +private: +// void init(); + +private: + std::map m_Sockets; // stores all the socket structures + + pthread_mutex_t m_ControlLock; // used to synchronize UDT API + + pthread_mutex_t m_IDLock; // used to synchronize ID generation + UDTSOCKET m_SocketID; // seed to generate a new unique socket ID + + std::map > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn + +private: + pthread_key_t m_TLSError; // thread local error record (last error) + #ifndef WIN32 + static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;} + #else + std::map m_mTLSRecord; + void checkTLSValue(); + pthread_mutex_t m_TLSLock; + #endif + +private: + void connect_complete(const UDTSOCKET u); + CUDTSocket* locate(const UDTSOCKET u); + CUDTSocket* locate(const sockaddr* peer, const UDTSOCKET id, int32_t isn); + void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL); + void updateMux(CUDTSocket* s, const CUDTSocket* ls); + +private: + std::map m_mMultiplexer; // UDP multiplexer + pthread_mutex_t m_MultiplexerLock; + +private: + CCache* m_pCache; // UDT network information cache + +private: + volatile bool m_bClosing; + pthread_mutex_t m_GCStopLock; + pthread_cond_t m_GCStopCond; + + pthread_mutex_t m_InitLock; + int m_iInstanceCount; // number of startup() called by application + bool m_bGCStatus; // if the GC thread is working (true) + + pthread_t m_GCThread; + #ifndef WIN32 + static void* garbageCollect(void*); + #else + static DWORD WINAPI garbageCollect(LPVOID); + #endif + + std::map m_ClosedSockets; // temporarily store closed sockets + + void checkBrokenSockets(); + void removeSocket(const UDTSOCKET u); + +private: + CEPoll m_EPoll; // handling epoll data structures and events + +private: + CUDTUnited(const CUDTUnited&); + CUDTUnited& operator=(const CUDTUnited&); +}; + +#endif diff --git a/vendor/udt4/src/buffer.cpp b/vendor/udt4/src/buffer.cpp new file mode 100644 index 000000000..327ab764c --- /dev/null +++ b/vendor/udt4/src/buffer.cpp @@ -0,0 +1,652 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 03/12/2011 +*****************************************************************************/ + +#include +#include +#include "buffer.h" + +using namespace std; + +CSndBuffer::CSndBuffer(int size, int mss): +m_BufLock(), +m_pBlock(NULL), +m_pFirstBlock(NULL), +m_pCurrBlock(NULL), +m_pLastBlock(NULL), +m_pBuffer(NULL), +m_iNextMsgNo(1), +m_iSize(size), +m_iMSS(mss), +m_iCount(0) +{ + // initial physical buffer of "size" + m_pBuffer = new Buffer; + m_pBuffer->m_pcData = new char [m_iSize * m_iMSS]; + m_pBuffer->m_iSize = m_iSize; + m_pBuffer->m_pNext = NULL; + + // circular linked list for out bound packets + m_pBlock = new Block; + Block* pb = m_pBlock; + for (int i = 1; i < m_iSize; ++ i) + { + pb->m_pNext = new Block; + pb->m_iMsgNo = 0; + pb = pb->m_pNext; + } + pb->m_pNext = m_pBlock; + + pb = m_pBlock; + char* pc = m_pBuffer->m_pcData; + for (int i = 0; i < m_iSize; ++ i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iMSS; + } + + m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock; + + #ifndef WIN32 + pthread_mutex_init(&m_BufLock, NULL); + #else + m_BufLock = CreateMutex(NULL, false, NULL); + #endif +} + +CSndBuffer::~CSndBuffer() +{ + Block* pb = m_pBlock->m_pNext; + while (pb != m_pBlock) + { + Block* temp = pb; + pb = pb->m_pNext; + delete temp; + } + delete m_pBlock; + + while (m_pBuffer != NULL) + { + Buffer* temp = m_pBuffer; + m_pBuffer = m_pBuffer->m_pNext; + delete [] temp->m_pcData; + delete temp; + } + + #ifndef WIN32 + pthread_mutex_destroy(&m_BufLock); + #else + CloseHandle(m_BufLock); + #endif +} + +void CSndBuffer::addBuffer(const char* data, int len, int ttl, bool order) +{ + int size = len / m_iMSS; + if ((len % m_iMSS) != 0) + size ++; + + // dynamically increase sender buffer + while (size + m_iCount >= m_iSize) + increase(); + + uint64_t time = CTimer::getTime(); + int32_t inorder = order; + inorder <<= 29; + + Block* s = m_pLastBlock; + for (int i = 0; i < size; ++ i) + { + int pktlen = len - i * m_iMSS; + if (pktlen > m_iMSS) + pktlen = m_iMSS; + + memcpy(s->m_pcData, data + i * m_iMSS, pktlen); + s->m_iLength = pktlen; + + s->m_iMsgNo = m_iNextMsgNo | inorder; + if (i == 0) + s->m_iMsgNo |= 0x80000000; + if (i == size - 1) + s->m_iMsgNo |= 0x40000000; + + s->m_OriginTime = time; + s->m_iTTL = ttl; + + s = s->m_pNext; + } + m_pLastBlock = s; + + CGuard::enterCS(m_BufLock); + m_iCount += size; + CGuard::leaveCS(m_BufLock); + + m_iNextMsgNo ++; + if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo) + m_iNextMsgNo = 1; +} + +int CSndBuffer::addBufferFromFile(fstream& ifs, int len) +{ + int size = len / m_iMSS; + if ((len % m_iMSS) != 0) + size ++; + + // dynamically increase sender buffer + while (size + m_iCount >= m_iSize) + increase(); + + Block* s = m_pLastBlock; + int total = 0; + for (int i = 0; i < size; ++ i) + { + if (ifs.bad() || ifs.fail() || ifs.eof()) + break; + + int pktlen = len - i * m_iMSS; + if (pktlen > m_iMSS) + pktlen = m_iMSS; + + ifs.read(s->m_pcData, pktlen); + if ((pktlen = ifs.gcount()) <= 0) + break; + + // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite + s->m_iMsgNo = m_iNextMsgNo | 0x20000000; + if (i == 0) + s->m_iMsgNo |= 0x80000000; + if (i == size - 1) + s->m_iMsgNo |= 0x40000000; + + s->m_iLength = pktlen; + s->m_iTTL = -1; + s = s->m_pNext; + + total += pktlen; + } + m_pLastBlock = s; + + CGuard::enterCS(m_BufLock); + m_iCount += size; + CGuard::leaveCS(m_BufLock); + + m_iNextMsgNo ++; + if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo) + m_iNextMsgNo = 1; + + return total; +} + +int CSndBuffer::readData(char** data, int32_t& msgno) +{ + // No data to read + if (m_pCurrBlock == m_pLastBlock) + return 0; + + *data = m_pCurrBlock->m_pcData; + int readlen = m_pCurrBlock->m_iLength; + msgno = m_pCurrBlock->m_iMsgNo; + + m_pCurrBlock = m_pCurrBlock->m_pNext; + + return readlen; +} + +int CSndBuffer::readData(char** data, const int offset, int32_t& msgno, int& msglen) +{ + CGuard bufferguard(m_BufLock); + + Block* p = m_pFirstBlock; + + for (int i = 0; i < offset; ++ i) + p = p->m_pNext; + + if ((p->m_iTTL >= 0) && ((CTimer::getTime() - p->m_OriginTime) / 1000 > (uint64_t)p->m_iTTL)) + { + msgno = p->m_iMsgNo & 0x1FFFFFFF; + + msglen = 1; + p = p->m_pNext; + bool move = false; + while (msgno == (p->m_iMsgNo & 0x1FFFFFFF)) + { + if (p == m_pCurrBlock) + move = true; + p = p->m_pNext; + if (move) + m_pCurrBlock = p; + msglen ++; + } + + return -1; + } + + *data = p->m_pcData; + int readlen = p->m_iLength; + msgno = p->m_iMsgNo; + + return readlen; +} + +void CSndBuffer::ackData(int offset) +{ + CGuard bufferguard(m_BufLock); + + for (int i = 0; i < offset; ++ i) + m_pFirstBlock = m_pFirstBlock->m_pNext; + + m_iCount -= offset; + + CTimer::triggerEvent(); +} + +int CSndBuffer::getCurrBufSize() const +{ + return m_iCount; +} + +void CSndBuffer::increase() +{ + int unitsize = m_pBuffer->m_iSize; + + // new physical buffer + Buffer* nbuf = NULL; + try + { + nbuf = new Buffer; + nbuf->m_pcData = new char [unitsize * m_iMSS]; + } + catch (...) + { + delete nbuf; + throw CUDTException(3, 2, 0); + } + nbuf->m_iSize = unitsize; + nbuf->m_pNext = NULL; + + // insert the buffer at the end of the buffer list + Buffer* p = m_pBuffer; + while (NULL != p->m_pNext) + p = p->m_pNext; + p->m_pNext = nbuf; + + // new packet blocks + Block* nblk = NULL; + try + { + nblk = new Block; + } + catch (...) + { + delete nblk; + throw CUDTException(3, 2, 0); + } + Block* pb = nblk; + for (int i = 1; i < unitsize; ++ i) + { + pb->m_pNext = new Block; + pb = pb->m_pNext; + } + + // insert the new blocks onto the existing one + pb->m_pNext = m_pLastBlock->m_pNext; + m_pLastBlock->m_pNext = nblk; + + pb = nblk; + char* pc = nbuf->m_pcData; + for (int i = 0; i < unitsize; ++ i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iMSS; + } + + m_iSize += unitsize; +} + +//////////////////////////////////////////////////////////////////////////////// + +CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize): +m_pUnit(NULL), +m_iSize(bufsize), +m_pUnitQueue(queue), +m_iStartPos(0), +m_iLastAckPos(0), +m_iMaxPos(0), +m_iNotch(0) +{ + m_pUnit = new CUnit* [m_iSize]; + for (int i = 0; i < m_iSize; ++ i) + m_pUnit[i] = NULL; +} + +CRcvBuffer::~CRcvBuffer() +{ + for (int i = 0; i < m_iSize; ++ i) + { + if (NULL != m_pUnit[i]) + { + m_pUnit[i]->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + } + } + + delete [] m_pUnit; +} + +int CRcvBuffer::addData(CUnit* unit, int offset) +{ + int pos = (m_iLastAckPos + offset) % m_iSize; + if (offset > m_iMaxPos) + m_iMaxPos = offset; + + if (NULL != m_pUnit[pos]) + return -1; + + m_pUnit[pos] = unit; + + unit->m_iFlag = 1; + ++ m_pUnitQueue->m_iCount; + + return 0; +} + +int CRcvBuffer::readBuffer(char* data, int len) +{ + int p = m_iStartPos; + int lastack = m_iLastAckPos; + int rs = len; + + while ((p != lastack) && (rs > 0)) + { + int unitsize = m_pUnit[p]->m_Packet.getLength() - m_iNotch; + if (unitsize > rs) + unitsize = rs; + + memcpy(data, m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); + data += unitsize; + + if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch)) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + + if (++ p == m_iSize) + p = 0; + + m_iNotch = 0; + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + m_iStartPos = p; + return len - rs; +} + +int CRcvBuffer::readBufferToFile(fstream& ofs, int len) +{ + int p = m_iStartPos; + int lastack = m_iLastAckPos; + int rs = len; + + while ((p != lastack) && (rs > 0)) + { + int unitsize = m_pUnit[p]->m_Packet.getLength() - m_iNotch; + if (unitsize > rs) + unitsize = rs; + + ofs.write(m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); + if (ofs.fail()) + break; + + if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch)) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + + if (++ p == m_iSize) + p = 0; + + m_iNotch = 0; + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + m_iStartPos = p; + + return len - rs; +} + +void CRcvBuffer::ackData(int len) +{ + m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; + m_iMaxPos -= len; + if (m_iMaxPos < 0) + m_iMaxPos = 0; + + CTimer::triggerEvent(); +} + +int CRcvBuffer::getAvailBufSize() const +{ + // One slot must be empty in order to tell the difference between "empty buffer" and "full buffer" + return m_iSize - getRcvDataSize() - 1; +} + +int CRcvBuffer::getRcvDataSize() const +{ + if (m_iLastAckPos >= m_iStartPos) + return m_iLastAckPos - m_iStartPos; + + return m_iSize + m_iLastAckPos - m_iStartPos; +} + +void CRcvBuffer::dropMsg(int32_t msgno) +{ + for (int i = m_iStartPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize) + if ((NULL != m_pUnit[i]) && (msgno == m_pUnit[i]->m_Packet.m_iMsgNo)) + m_pUnit[i]->m_iFlag = 3; +} + +int CRcvBuffer::readMsg(char* data, int len) +{ + int p, q; + bool passack; + if (!scanMsg(p, q, passack)) + return 0; + + int rs = len; + while (p != (q + 1) % m_iSize) + { + int unitsize = m_pUnit[p]->m_Packet.getLength(); + if ((rs >= 0) && (unitsize > rs)) + unitsize = rs; + + if (unitsize > 0) + { + memcpy(data, m_pUnit[p]->m_Packet.m_pcData, unitsize); + data += unitsize; + rs -= unitsize; + } + + if (!passack) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + } + else + m_pUnit[p]->m_iFlag = 2; + + if (++ p == m_iSize) + p = 0; + } + + if (!passack) + m_iStartPos = (q + 1) % m_iSize; + + return len - rs; +} + +int CRcvBuffer::getRcvMsgNum() +{ + int p, q; + bool passack; + return scanMsg(p, q, passack) ? 1 : 0; +} + +bool CRcvBuffer::scanMsg(int& p, int& q, bool& passack) +{ + // empty buffer + if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0)) + return false; + + //skip all bad msgs at the beginning + while (m_iStartPos != m_iLastAckPos) + { + if (NULL == m_pUnit[m_iStartPos]) + { + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + continue; + } + + if ((1 == m_pUnit[m_iStartPos]->m_iFlag) && (m_pUnit[m_iStartPos]->m_Packet.getMsgBoundary() > 1)) + { + bool good = true; + + // look ahead for the whole message + for (int i = m_iStartPos; i != m_iLastAckPos;) + { + if ((NULL == m_pUnit[i]) || (1 != m_pUnit[i]->m_iFlag)) + { + good = false; + break; + } + + if ((m_pUnit[i]->m_Packet.getMsgBoundary() == 1) || (m_pUnit[i]->m_Packet.getMsgBoundary() == 3)) + break; + + if (++ i == m_iSize) + i = 0; + } + + if (good) + break; + } + + CUnit* tmp = m_pUnit[m_iStartPos]; + m_pUnit[m_iStartPos] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + } + + p = -1; // message head + q = m_iStartPos; // message tail + passack = m_iStartPos == m_iLastAckPos; + bool found = false; + + // looking for the first message + for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i <= n; ++ i) + { + if ((NULL != m_pUnit[q]) && (1 == m_pUnit[q]->m_iFlag)) + { + switch (m_pUnit[q]->m_Packet.getMsgBoundary()) + { + case 3: // 11 + p = q; + found = true; + break; + + case 2: // 10 + p = q; + break; + + case 1: // 01 + if (p != -1) + found = true; + } + } + else + { + // a hole in this message, not valid, restart search + p = -1; + } + + if (found) + { + // the msg has to be ack'ed or it is allowed to read out of order, and was not read before + if (!passack || !m_pUnit[q]->m_Packet.getMsgOrderFlag()) + break; + + found = false; + } + + if (++ q == m_iSize) + q = 0; + + if (q == m_iLastAckPos) + passack = true; + } + + // no msg found + if (!found) + { + // if the message is larger than the receiver buffer, return part of the message + if ((p != -1) && ((q + 1) % m_iSize == p)) + found = true; + } + + return found; +} diff --git a/vendor/udt4/src/buffer.h b/vendor/udt4/src/buffer.h new file mode 100644 index 000000000..4377e790b --- /dev/null +++ b/vendor/udt4/src/buffer.h @@ -0,0 +1,275 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +*****************************************************************************/ + +#ifndef __UDT_BUFFER_H__ +#define __UDT_BUFFER_H__ + + +#include "udt.h" +#include "list.h" +#include "queue.h" +#include + +class CSndBuffer +{ +public: + CSndBuffer(int size = 32, int mss = 1500); + ~CSndBuffer(); + + // Functionality: + // Insert a user buffer into the sending list. + // Parameters: + // 0) [in] data: pointer to the user data block. + // 1) [in] len: size of the block. + // 2) [in] ttl: time to live in milliseconds + // 3) [in] order: if the block should be delivered in order, for DGRAM only + // Returned value: + // None. + + void addBuffer(const char* data, int len, int ttl = -1, bool order = false); + + // Functionality: + // Read a block of data from file and insert it into the sending list. + // Parameters: + // 0) [in] ifs: input file stream. + // 1) [in] len: size of the block. + // Returned value: + // actual size of data added from the file. + + int addBufferFromFile(std::fstream& ifs, int len); + + // Functionality: + // Find data position to pack a DATA packet from the furthest reading point. + // Parameters: + // 0) [out] data: the pointer to the data position. + // 1) [out] msgno: message number of the packet. + // Returned value: + // Actual length of data read. + + int readData(char** data, int32_t& msgno); + + // Functionality: + // Find data position to pack a DATA packet for a retransmission. + // Parameters: + // 0) [out] data: the pointer to the data position. + // 1) [in] offset: offset from the last ACK point. + // 2) [out] msgno: message number of the packet. + // 3) [out] msglen: length of the message + // Returned value: + // Actual length of data read. + + int readData(char** data, const int offset, int32_t& msgno, int& msglen); + + // Functionality: + // Update the ACK point and may release/unmap/return the user data according to the flag. + // Parameters: + // 0) [in] offset: number of packets acknowledged. + // Returned value: + // None. + + void ackData(int offset); + + // Functionality: + // Read size of data still in the sending list. + // Parameters: + // None. + // Returned value: + // Current size of the data in the sending list. + + int getCurrBufSize() const; + +private: + void increase(); + +private: + pthread_mutex_t m_BufLock; // used to synchronize buffer operation + + struct Block + { + char* m_pcData; // pointer to the data block + int m_iLength; // length of the block + + int32_t m_iMsgNo; // message number + uint64_t m_OriginTime; // original request time + int m_iTTL; // time to live (milliseconds) + + Block* m_pNext; // next block + } *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; + + // m_pBlock: The head pointer + // m_pFirstBlock: The first block + // m_pCurrBlock: The current block + // m_pLastBlock: The last block (if first == last, buffer is empty) + + struct Buffer + { + char* m_pcData; // buffer + int m_iSize; // size + Buffer* m_pNext; // next buffer + } *m_pBuffer; // physical buffer + + int32_t m_iNextMsgNo; // next message number + + int m_iSize; // buffer size (number of packets) + int m_iMSS; // maximum seqment/packet size + + int m_iCount; // number of used blocks + +private: + CSndBuffer(const CSndBuffer&); + CSndBuffer& operator=(const CSndBuffer&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CRcvBuffer +{ +public: + CRcvBuffer(CUnitQueue* queue, int bufsize = 65536); + ~CRcvBuffer(); + + // Functionality: + // Write data into the buffer. + // Parameters: + // 0) [in] unit: pointer to a data unit containing new packet + // 1) [in] offset: offset from last ACK point. + // Returned value: + // 0 is success, -1 if data is repeated. + + int addData(CUnit* unit, int offset); + + // Functionality: + // Read data into a user buffer. + // Parameters: + // 0) [in] data: pointer to user buffer. + // 1) [in] len: length of user buffer. + // Returned value: + // size of data read. + + int readBuffer(char* data, int len); + + // Functionality: + // Read data directly into file. + // Parameters: + // 0) [in] file: C++ file stream. + // 1) [in] len: expected length of data to write into the file. + // Returned value: + // size of data read. + + int readBufferToFile(std::fstream& ofs, int len); + + // Functionality: + // Update the ACK point of the buffer. + // Parameters: + // 0) [in] len: size of data to be acknowledged. + // Returned value: + // 1 if a user buffer is fulfilled, otherwise 0. + + void ackData(int len); + + // Functionality: + // Query how many buffer space left for data receiving. + // Parameters: + // None. + // Returned value: + // size of available buffer space (including user buffer) for data receiving. + + int getAvailBufSize() const; + + // Functionality: + // Query how many data has been continuously received (for reading). + // Parameters: + // None. + // Returned value: + // size of valid (continous) data for reading. + + int getRcvDataSize() const; + + // Functionality: + // mark the message to be dropped from the message list. + // Parameters: + // 0) [in] msgno: message nuumer. + // Returned value: + // None. + + void dropMsg(int32_t msgno); + + // Functionality: + // read a message. + // Parameters: + // 0) [out] data: buffer to write the message into. + // 1) [in] len: size of the buffer. + // Returned value: + // actuall size of data read. + + int readMsg(char* data, int len); + + // Functionality: + // Query how many messages are available now. + // Parameters: + // None. + // Returned value: + // number of messages available for recvmsg. + + int getRcvMsgNum(); + +private: + bool scanMsg(int& start, int& end, bool& passack); + +private: + CUnit** m_pUnit; // pointer to the protocol buffer + int m_iSize; // size of the protocol buffer + CUnitQueue* m_pUnitQueue; // the shared unit queue + + int m_iStartPos; // the head position for I/O (inclusive) + int m_iLastAckPos; // the last ACKed position (exclusive) + // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 + int m_iMaxPos; // the furthest data position + + int m_iNotch; // the starting read point of the first unit + +private: + CRcvBuffer(); + CRcvBuffer(const CRcvBuffer&); + CRcvBuffer& operator=(const CRcvBuffer&); +}; + + +#endif diff --git a/vendor/udt4/src/cache.cpp b/vendor/udt4/src/cache.cpp new file mode 100644 index 000000000..ea0aad190 --- /dev/null +++ b/vendor/udt4/src/cache.cpp @@ -0,0 +1,123 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +*****************************************************************************/ + +#ifdef WIN32 + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif + +#include +#include "cache.h" +#include "core.h" + +using namespace std; + +CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj) +{ + std::copy(obj.m_piIP, obj.m_piIP + 3, m_piIP); + m_iIPversion = obj.m_iIPversion; + m_ullTimeStamp = obj.m_ullTimeStamp; + m_iRTT = obj.m_iRTT; + m_iBandwidth = obj.m_iBandwidth; + m_iLossRate = obj.m_iLossRate; + m_iReorderDistance = obj.m_iReorderDistance; + m_dInterval = obj.m_dInterval; + m_dCWnd = obj.m_dCWnd; + + return *this; +} + +bool CInfoBlock::operator==(const CInfoBlock& obj) +{ + if (m_iIPversion != obj.m_iIPversion) + return false; + + else if (m_iIPversion == AF_INET) + return (m_piIP[0] == obj.m_piIP[0]); + + for (int i = 0; i < 4; ++ i) + { + if (m_piIP[i] != obj.m_piIP[i]) + return false; + } + + return true; +} + +CInfoBlock* CInfoBlock::clone() +{ + CInfoBlock* obj = new CInfoBlock; + + std::copy(m_piIP, m_piIP + 3, obj->m_piIP); + obj->m_iIPversion = m_iIPversion; + obj->m_ullTimeStamp = m_ullTimeStamp; + obj->m_iRTT = m_iRTT; + obj->m_iBandwidth = m_iBandwidth; + obj->m_iLossRate = m_iLossRate; + obj->m_iReorderDistance = m_iReorderDistance; + obj->m_dInterval = m_dInterval; + obj->m_dCWnd = m_dCWnd; + + return obj; +} + +int CInfoBlock::getKey() +{ + if (m_iIPversion == AF_INET) + return m_piIP[0]; + + return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; +} + +void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[]) +{ + if (ver == AF_INET) + { + ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr; + ip[1] = ip[2] = ip[3] = 0; + } + else + { + memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16); + } +} diff --git a/vendor/udt4/src/cache.h b/vendor/udt4/src/cache.h new file mode 100644 index 000000000..22d96246a --- /dev/null +++ b/vendor/udt4/src/cache.h @@ -0,0 +1,293 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/27/2011 +*****************************************************************************/ + +#ifndef __UDT_CACHE_H__ +#define __UDT_CACHE_H__ + +#include +#include + +#include "common.h" +#include "udt.h" + +class CCacheItem +{ +public: + virtual ~CCacheItem() {} + +public: + virtual CCacheItem& operator=(const CCacheItem&) = 0; + + // The "==" operator SHOULD only compare key values. + virtual bool operator==(const CCacheItem&) = 0; + + // Functionality: + // get a deep copy clone of the current item + // Parameters: + // None. + // Returned value: + // Pointer to the new item, or NULL if failed. + + virtual CCacheItem* clone() = 0; + + // Functionality: + // get a random key value between 0 and MAX_INT to be used for the hash in cache + // Parameters: + // None. + // Returned value: + // A random hash key. + + virtual int getKey() = 0; + + // If there is any shared resources between the cache item and its clone, + // the shared resource should be released by this function. + virtual void release() {} +}; + +template class CCache +{ +public: + CCache(int size = 1024): + m_iMaxSize(size), + m_iHashSize(size * 3), + m_iCurrSize(0) + { + m_vHashPtr.resize(m_iHashSize); + CGuard::createMutex(m_Lock); + } + + ~CCache() + { + clear(); + CGuard::releaseMutex(m_Lock); + } + +public: + // Functionality: + // find the matching item in the cache. + // Parameters: + // 0) [in/out] data: storage for the retrieved item; initially it must carry the key information + // Returned value: + // 0 if found a match, otherwise -1. + + int lookup(T* data) + { + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + const ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*data == ***i) + { + // copy the cached info + *data = ***i; + return 0; + } + } + + return -1; + } + + // Functionality: + // update an item in the cache, or insert one if it doesn't exist; oldest item may be removed + // Parameters: + // 0) [in] data: the new item to updated/inserted to the cache + // Returned value: + // 0 if success, otherwise -1. + + int update(T* data) + { + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + T* curr = NULL; + + ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*data == ***i) + { + // update the existing entry with the new value + ***i = *data; + curr = **i; + + // remove the current entry + m_StorageList.erase(*i); + item_list.erase(i); + + // re-insert to the front + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + return 0; + } + } + + // create new entry and insert to front + curr = data->clone(); + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + ++ m_iCurrSize; + if (m_iCurrSize >= m_iMaxSize) + { + // Cache overflow, remove oldest entry. + T* last_data = m_StorageList.back(); + int last_key = last_data->getKey() % m_iHashSize; + + item_list = m_vHashPtr[last_key]; + for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*last_data == ***i) + { + item_list.erase(i); + break; + } + } + + last_data->release(); + delete last_data; + m_StorageList.pop_back(); + -- m_iCurrSize; + } + + return 0; + } + + // Functionality: + // Specify the cache size (i.e., max number of items). + // Parameters: + // 0) [in] size: max cache size. + // Returned value: + // None. + + void setSizeLimit(int size) + { + m_iMaxSize = size; + m_iHashSize = size * 3; + m_vHashPtr.resize(m_iHashSize); + } + + // Functionality: + // Clear all entries in the cache, restore to initialization state. + // Parameters: + // None. + // Returned value: + // None. + + void clear() + { + for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) + { + (*i)->release(); + delete *i; + } + m_StorageList.clear(); + for (typename std::vector::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i) + i->clear(); + m_iCurrSize = 0; + } + +private: + std::list m_StorageList; + typedef typename std::list::iterator ItemPtr; + typedef std::list ItemPtrList; + std::vector m_vHashPtr; + + int m_iMaxSize; + int m_iHashSize; + int m_iCurrSize; + + pthread_mutex_t m_Lock; + +private: + CCache(const CCache&); + CCache& operator=(const CCache&); +}; + + +class CInfoBlock +{ +public: + uint32_t m_piIP[4]; // IP address, machine read only, not human readable format + int m_iIPversion; // IP version + uint64_t m_ullTimeStamp; // last update time + int m_iRTT; // RTT + int m_iBandwidth; // estimated bandwidth + int m_iLossRate; // average loss rate + int m_iReorderDistance; // packet reordering distance + double m_dInterval; // inter-packet time, congestion control + double m_dCWnd; // congestion window size, congestion control + +public: + virtual ~CInfoBlock() {} + virtual CInfoBlock& operator=(const CInfoBlock& obj); + virtual bool operator==(const CInfoBlock& obj); + virtual CInfoBlock* clone(); + virtual int getKey(); + virtual void release() {} + +public: + + // Functionality: + // convert sockaddr structure to an integer array + // Parameters: + // 0) [in] addr: network address + // 1) [in] ver: IP version + // 2) [out] ip: the result machine readable IP address in integer array + // Returned value: + // None. + + static void convert(const sockaddr* addr, int ver, uint32_t ip[]); +}; + + +#endif diff --git a/vendor/udt4/src/ccc.cpp b/vendor/udt4/src/ccc.cpp new file mode 100644 index 000000000..048b7eca2 --- /dev/null +++ b/vendor/udt4/src/ccc.cpp @@ -0,0 +1,314 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/21/2013 +*****************************************************************************/ + + +#include "core.h" +#include "ccc.h" +#include +#include + +CCC::CCC(): +m_iSYNInterval(CUDT::m_iSYNInterval), +m_dPktSndPeriod(1.0), +m_dCWndSize(16.0), +m_iBandwidth(), +m_dMaxCWndSize(), +m_iMSS(), +m_iSndCurrSeqNo(), +m_iRcvRate(), +m_iRTT(), +m_pcParam(NULL), +m_iPSize(0), +m_UDT(), +m_iACKPeriod(0), +m_iACKInterval(0), +m_bUserDefinedRTO(false), +m_iRTO(-1), +m_PerfInfo() +{ +} + +CCC::~CCC() +{ + delete [] m_pcParam; +} + +void CCC::setACKTimer(int msINT) +{ + m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT; +} + +void CCC::setACKInterval(int pktINT) +{ + m_iACKInterval = pktINT; +} + +void CCC::setRTO(int usRTO) +{ + m_bUserDefinedRTO = true; + m_iRTO = usRTO; +} + +void CCC::sendCustomMsg(CPacket& pkt) const +{ + CUDT* u = CUDT::getUDTHandle(m_UDT); + + if (NULL != u) + { + pkt.m_iID = u->m_PeerID; + u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt); + } +} + +const CPerfMon* CCC::getPerfInfo() +{ + try + { + CUDT* u = CUDT::getUDTHandle(m_UDT); + if (NULL != u) + u->sample(&m_PerfInfo, false); + } + catch (...) + { + return NULL; + } + + return &m_PerfInfo; +} + +void CCC::setMSS(int mss) +{ + m_iMSS = mss; +} + +void CCC::setBandwidth(int bw) +{ + m_iBandwidth = bw; +} + +void CCC::setSndCurrSeqNo(int32_t seqno) +{ + m_iSndCurrSeqNo = seqno; +} + +void CCC::setRcvRate(int rcvrate) +{ + m_iRcvRate = rcvrate; +} + +void CCC::setMaxCWndSize(int cwnd) +{ + m_dMaxCWndSize = cwnd; +} + +void CCC::setRTT(int rtt) +{ + m_iRTT = rtt; +} + +void CCC::setUserParam(const char* param, int size) +{ + delete [] m_pcParam; + m_pcParam = new char[size]; + memcpy(m_pcParam, param, size); + m_iPSize = size; +} + +// +CUDTCC::CUDTCC(): +m_iRCInterval(), +m_LastRCTime(), +m_bSlowStart(), +m_iLastAck(), +m_bLoss(), +m_iLastDecSeq(), +m_dLastDecPeriod(), +m_iNAKCount(), +m_iDecRandom(), +m_iAvgNAKNum(), +m_iDecCount() +{ +} + +void CUDTCC::init() +{ + m_iRCInterval = m_iSYNInterval; + m_LastRCTime = CTimer::getTime(); + setACKTimer(m_iRCInterval); + + m_bSlowStart = true; + m_iLastAck = m_iSndCurrSeqNo; + m_bLoss = false; + m_iLastDecSeq = CSeqNo::decseq(m_iLastAck); + m_dLastDecPeriod = 1; + m_iAvgNAKNum = 0; + m_iNAKCount = 0; + m_iDecRandom = 1; + + m_dCWndSize = 16; + m_dPktSndPeriod = 1; +} + +void CUDTCC::onACK(int32_t ack) +{ + int64_t B = 0; + double inc = 0; + // Note: 1/24/2012 + // The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01 + // because the original was too small and caused sending rate to stay at low level + // for long time. + const double min_inc = 0.01; + + uint64_t currtime = CTimer::getTime(); + if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval) + return; + + m_LastRCTime = currtime; + + if (m_bSlowStart) + { + m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack); + m_iLastAck = ack; + + if (m_dCWndSize > m_dMaxCWndSize) + { + m_bSlowStart = false; + if (m_iRcvRate > 0) + m_dPktSndPeriod = 1000000.0 / m_iRcvRate; + else + m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize; + } + } + else + m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16; + + // During Slow Start, no rate increase + if (m_bSlowStart) + return; + + if (m_bLoss) + { + m_bLoss = false; + return; + } + + B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod); + if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B)) + B = m_iBandwidth / 9; + if (B <= 0) + inc = min_inc; + else + { + // inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS) + // Beta = 1.5 * 10^(-6) + + inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS; + + if (inc < min_inc) + inc = min_inc; + } + + m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval); +} + +void CUDTCC::onLoss(const int32_t* losslist, int) +{ + //Slow Start stopped, if it hasn't yet + if (m_bSlowStart) + { + m_bSlowStart = false; + if (m_iRcvRate > 0) + { + // Set the sending rate to the receiving rate. + m_dPktSndPeriod = 1000000.0 / m_iRcvRate; + return; + } + // If no receiving rate is observed, we have to compute the sending + // rate according to the current window size, and decrease it + // using the method below. + m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); + } + + m_bLoss = true; + + if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0) + { + m_dLastDecPeriod = m_dPktSndPeriod; + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); + + m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125); + m_iNAKCount = 1; + m_iDecCount = 1; + + m_iLastDecSeq = m_iSndCurrSeqNo; + + // remove global synchronization using randomization + srand(m_iLastDecSeq); + m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX)); + if (m_iDecRandom < 1) + m_iDecRandom = 1; + } + else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom))) + { + // 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); + m_iLastDecSeq = m_iSndCurrSeqNo; + } +} + +void CUDTCC::onTimeout() +{ + if (m_bSlowStart) + { + m_bSlowStart = false; + if (m_iRcvRate > 0) + m_dPktSndPeriod = 1000000.0 / m_iRcvRate; + else + m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); + } + else + { + /* + m_dLastDecPeriod = m_dPktSndPeriod; + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2); + m_iLastDecSeq = m_iLastAck; + */ + } +} diff --git a/vendor/udt4/src/ccc.h b/vendor/udt4/src/ccc.h new file mode 100644 index 000000000..558597533 --- /dev/null +++ b/vendor/udt4/src/ccc.h @@ -0,0 +1,278 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/28/2012 +*****************************************************************************/ + + +#ifndef __UDT_CCC_H__ +#define __UDT_CCC_H__ + + +#include "udt.h" +#include "packet.h" + + +class UDT_API CCC +{ +friend class CUDT; + +public: + CCC(); + virtual ~CCC(); + +private: + CCC(const CCC&); + CCC& operator=(const CCC&) {return *this;} + +public: + + // Functionality: + // Callback function to be called (only) at the start of a UDT connection. + // note that this is different from CCC(), which is always called. + // Parameters: + // None. + // Returned value: + // None. + + virtual void init() {} + + // Functionality: + // Callback function to be called when a UDT connection is closed. + // Parameters: + // None. + // Returned value: + // None. + + virtual void close() {} + + // Functionality: + // Callback function to be called when an ACK packet is received. + // Parameters: + // 0) [in] ackno: the data sequence number acknowledged by this ACK. + // Returned value: + // None. + + virtual void onACK(int32_t) {} + + // Functionality: + // Callback function to be called when a loss report is received. + // Parameters: + // 0) [in] losslist: list of sequence number of packets, in the format describled in packet.cpp. + // 1) [in] size: length of the loss list. + // Returned value: + // None. + + virtual void onLoss(const int32_t*, int) {} + + // Functionality: + // Callback function to be called when a timeout event occurs. + // Parameters: + // None. + // Returned value: + // None. + + virtual void onTimeout() {} + + // Functionality: + // Callback function to be called when a data is sent. + // Parameters: + // 0) [in] seqno: the data sequence number. + // 1) [in] size: the payload size. + // Returned value: + // None. + + virtual void onPktSent(const CPacket*) {} + + // Functionality: + // Callback function to be called when a data is received. + // Parameters: + // 0) [in] seqno: the data sequence number. + // 1) [in] size: the payload size. + // Returned value: + // None. + + virtual void onPktReceived(const CPacket*) {} + + // Functionality: + // Callback function to Process a user defined packet. + // Parameters: + // 0) [in] pkt: the user defined packet. + // Returned value: + // None. + + virtual void processCustomMsg(const CPacket*) {} + +protected: + + // Functionality: + // Set periodical acknowldging and the ACK period. + // Parameters: + // 0) [in] msINT: the period to send an ACK. + // Returned value: + // None. + + void setACKTimer(int msINT); + + // Functionality: + // Set packet-based acknowldging and the number of packets to send an ACK. + // Parameters: + // 0) [in] pktINT: the number of packets to send an ACK. + // Returned value: + // None. + + void setACKInterval(int pktINT); + + // Functionality: + // Set RTO value. + // Parameters: + // 0) [in] msRTO: RTO in macroseconds. + // Returned value: + // None. + + void setRTO(int usRTO); + + // Functionality: + // Send a user defined control packet. + // Parameters: + // 0) [in] pkt: user defined packet. + // Returned value: + // None. + + void sendCustomMsg(CPacket& pkt) const; + + // Functionality: + // retrieve performance information. + // Parameters: + // None. + // Returned value: + // Pointer to a performance info structure. + + const CPerfMon* getPerfInfo(); + + // Functionality: + // Set user defined parameters. + // Parameters: + // 0) [in] param: the paramters in one buffer. + // 1) [in] size: the size of the buffer. + // Returned value: + // None. + + void setUserParam(const char* param, int size); + +private: + void setMSS(int mss); + void setMaxCWndSize(int cwnd); + void setBandwidth(int bw); + void setSndCurrSeqNo(int32_t seqno); + void setRcvRate(int rcvrate); + void setRTT(int rtt); + +protected: + const int32_t& m_iSYNInterval; // UDT constant parameter, SYN + + double m_dPktSndPeriod; // Packet sending period, in microseconds + double m_dCWndSize; // Congestion window size, in packets + + int m_iBandwidth; // estimated bandwidth, packets per second + double m_dMaxCWndSize; // maximum cwnd size, in packets + + int m_iMSS; // Maximum Packet Size, including all packet headers + int32_t m_iSndCurrSeqNo; // current maximum seq no sent out + int m_iRcvRate; // packet arrive rate at receiver side, packets per second + int m_iRTT; // current estimated RTT, microsecond + + char* m_pcParam; // user defined parameter + int m_iPSize; // size of m_pcParam + +private: + UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to + + int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds + int m_iACKInterval; // How many packets to send one ACK, in packets + + bool m_bUserDefinedRTO; // if the RTO value is defined by users + int m_iRTO; // RTO value, microseconds + + CPerfMon m_PerfInfo; // protocol statistics information +}; + +class CCCVirtualFactory +{ +public: + virtual ~CCCVirtualFactory() {} + + virtual CCC* create() = 0; + virtual CCCVirtualFactory* clone() = 0; +}; + +template +class CCCFactory: public CCCVirtualFactory +{ +public: + virtual ~CCCFactory() {} + + virtual CCC* create() {return new T;} + virtual CCCVirtualFactory* clone() {return new CCCFactory;} +}; + +class CUDTCC: public CCC +{ +public: + CUDTCC(); + +public: + virtual void init(); + virtual void onACK(int32_t); + virtual void onLoss(const int32_t*, int); + virtual void onTimeout(); + +private: + int m_iRCInterval; // UDT Rate control interval + uint64_t m_LastRCTime; // last rate increase time + bool m_bSlowStart; // if in slow start phase + int32_t m_iLastAck; // last ACKed seq no + bool m_bLoss; // if loss happened since last rate increase + int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened + double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened + int m_iNAKCount; // NAK counter + int m_iDecRandom; // random threshold on decrease by number of loss events + int m_iAvgNAKNum; // average number of NAKs per congestion + int m_iDecCount; // number of decreases in a congestion epoch +}; + +#endif diff --git a/vendor/udt4/src/channel.cpp b/vendor/udt4/src/channel.cpp new file mode 100644 index 000000000..7b010f0fe --- /dev/null +++ b/vendor/udt4/src/channel.cpp @@ -0,0 +1,340 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +****************************************************************************/ + +/**************************************************************************** +written by + Yunhong Gu, last updated 01/27/2011 +*****************************************************************************/ + +#ifndef WIN32 + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif +#include "channel.h" +#include "packet.h" + +#ifdef WIN32 + #define socklen_t int +#endif + +#ifndef WIN32 + #define NET_ERROR errno +#else + #define NET_ERROR WSAGetLastError() +#endif + + +CChannel::CChannel(): +m_iIPversion(AF_INET), +m_iSockAddrSize(sizeof(sockaddr_in)), +m_iSocket(), +m_iSndBufSize(65536), +m_iRcvBufSize(65536) +{ +} + +CChannel::CChannel(int version): +m_iIPversion(version), +m_iSocket(), +m_iSndBufSize(65536), +m_iRcvBufSize(65536) +{ + m_iSockAddrSize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); +} + +CChannel::~CChannel() +{ +} + +void CChannel::open(const sockaddr* addr) +{ + // construct an socket + m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0); + + #ifdef WIN32 + if (INVALID_SOCKET == m_iSocket) + #else + if (m_iSocket < 0) + #endif + throw CUDTException(1, 0, NET_ERROR); + + if (NULL != addr) + { + socklen_t namelen = m_iSockAddrSize; + + if (0 != ::bind(m_iSocket, addr, namelen)) + throw CUDTException(1, 3, NET_ERROR); + } + else + { + //sendto or WSASendTo will also automatically bind the socket + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = m_iIPversion; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != ::getaddrinfo(NULL, "0", &hints, &res)) + throw CUDTException(1, 3, NET_ERROR); + + if (0 != ::bind(m_iSocket, res->ai_addr, res->ai_addrlen)) + throw CUDTException(1, 3, NET_ERROR); + + ::freeaddrinfo(res); + } + + setUDPSockOpt(); +} + +void CChannel::open(UDPSOCKET udpsock) +{ + m_iSocket = udpsock; + setUDPSockOpt(); +} + +void CChannel::setUDPSockOpt() +{ + #if defined(BSD) || defined(OSX) + // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value + int maxsize = 64000; + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int)); + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int)); + #else + // for other systems, if requested is greated than maximum, the maximum value will be automactally used + if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) || + (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))) + throw CUDTException(1, 3, NET_ERROR); + #endif + + timeval tv; + tv.tv_sec = 0; + #if defined (BSD) || defined (OSX) + // Known BSD bug as the day I wrote this code. + // A small time out value will cause the socket to block forever. + tv.tv_usec = 10000; + #else + tv.tv_usec = 100; + #endif + + #ifdef UNIX + // Set non-blocking I/O + // UNIX does not support SO_RCVTIMEO + int opts = ::fcntl(m_iSocket, F_GETFL); + if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) + throw CUDTException(1, 3, NET_ERROR); + #elif WIN32 + DWORD ot = 1; //milliseconds + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&ot, sizeof(DWORD))) + throw CUDTException(1, 3, NET_ERROR); + #else + // Set receiving time-out value + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval))) + throw CUDTException(1, 3, NET_ERROR); + #endif +} + +void CChannel::close() const +{ + #ifndef WIN32 + ::close(m_iSocket); + #else + ::closesocket(m_iSocket); + #endif +} + +int CChannel::getSndBufSize() +{ + socklen_t size = sizeof(socklen_t); + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size); + return m_iSndBufSize; +} + +int CChannel::getRcvBufSize() +{ + socklen_t size = sizeof(socklen_t); + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size); + return m_iRcvBufSize; +} + +void CChannel::setSndBufSize(int size) +{ + m_iSndBufSize = size; +} + +void CChannel::setRcvBufSize(int size) +{ + m_iRcvBufSize = size; +} + +void CChannel::getSockAddr(sockaddr* addr) const +{ + socklen_t namelen = m_iSockAddrSize; + ::getsockname(m_iSocket, addr, &namelen); +} + +void CChannel::getPeerAddr(sockaddr* addr) const +{ + socklen_t namelen = m_iSockAddrSize; + ::getpeername(m_iSocket, addr, &namelen); +} + +int CChannel::sendto(const sockaddr* addr, CPacket& packet) const +{ + // convert control information into network order + if (packet.getFlag()) + for (int i = 0, n = packet.getLength() / 4; i < n; ++ i) + *((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i)); + + // convert packet header into network order + //for (int j = 0; j < 4; ++ j) + // packet.m_nHeader[j] = htonl(packet.m_nHeader[j]); + uint32_t* p = packet.m_nHeader; + for (int j = 0; j < 4; ++ j) + { + *p = htonl(*p); + ++ p; + } + + #ifndef WIN32 + msghdr mh; + mh.msg_name = (sockaddr*)addr; + mh.msg_namelen = m_iSockAddrSize; + mh.msg_iov = (iovec*)packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + int res = ::sendmsg(m_iSocket, &mh, 0); + #else + DWORD size = CPacket::m_iPktHdrSize + packet.getLength(); + int addrsize = m_iSockAddrSize; + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL); + res = (0 == res) ? size : -1; + #endif + + // convert back into local host order + //for (int k = 0; k < 4; ++ k) + // packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]); + p = packet.m_nHeader; + for (int k = 0; k < 4; ++ k) + { + *p = ntohl(*p); + ++ p; + } + + if (packet.getFlag()) + { + for (int l = 0, n = packet.getLength() / 4; l < n; ++ l) + *((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l)); + } + + return res; +} + +int CChannel::recvfrom(sockaddr* addr, CPacket& packet) const +{ + #ifndef WIN32 + msghdr mh; + mh.msg_name = addr; + mh.msg_namelen = m_iSockAddrSize; + mh.msg_iov = packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + #ifdef UNIX + fd_set set; + timeval tv; + FD_ZERO(&set); + FD_SET(m_iSocket, &set); + tv.tv_sec = 0; + tv.tv_usec = 10000; + ::select(m_iSocket+1, &set, NULL, &set, &tv); + #endif + + int res = ::recvmsg(m_iSocket, &mh, 0); + #else + DWORD size = CPacket::m_iPktHdrSize + packet.getLength(); + DWORD flag = 0; + int addrsize = m_iSockAddrSize; + + int res = ::WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL); + res = (0 == res) ? size : -1; + #endif + + if (res <= 0) + { + packet.setLength(-1); + return -1; + } + + packet.setLength(res - CPacket::m_iPktHdrSize); + + // convert back into local host order + //for (int i = 0; i < 4; ++ i) + // packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]); + uint32_t* p = packet.m_nHeader; + for (int i = 0; i < 4; ++ i) + { + *p = ntohl(*p); + ++ p; + } + + if (packet.getFlag()) + { + for (int j = 0, n = packet.getLength() / 4; j < n; ++ j) + *((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j)); + } + + return packet.getLength(); +} diff --git a/vendor/udt4/src/channel.h b/vendor/udt4/src/channel.h new file mode 100644 index 000000000..0e47acc16 --- /dev/null +++ b/vendor/udt4/src/channel.h @@ -0,0 +1,171 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/27/2011 +*****************************************************************************/ + +#ifndef __UDT_CHANNEL_H__ +#define __UDT_CHANNEL_H__ + + +#include "udt.h" +#include "packet.h" + + +class CChannel +{ +public: + CChannel(); + CChannel(int version); + ~CChannel(); + + // Functionality: + // Open a UDP channel. + // Parameters: + // 0) [in] addr: The local address that UDP will use. + // Returned value: + // None. + + void open(const sockaddr* addr = NULL); + + // Functionality: + // Open a UDP channel based on an existing UDP socket. + // Parameters: + // 0) [in] udpsock: UDP socket descriptor. + // Returned value: + // None. + + void open(UDPSOCKET udpsock); + + // Functionality: + // Disconnect and close the UDP entity. + // Parameters: + // None. + // Returned value: + // None. + + void close() const; + + // Functionality: + // Get the UDP sending buffer size. + // Parameters: + // None. + // Returned value: + // Current UDP sending buffer size. + + int getSndBufSize(); + + // Functionality: + // Get the UDP receiving buffer size. + // Parameters: + // None. + // Returned value: + // Current UDP receiving buffer size. + + int getRcvBufSize(); + + // Functionality: + // Set the UDP sending buffer size. + // Parameters: + // 0) [in] size: expected UDP sending buffer size. + // Returned value: + // None. + + void setSndBufSize(int size); + + // Functionality: + // Set the UDP receiving buffer size. + // Parameters: + // 0) [in] size: expected UDP receiving buffer size. + // Returned value: + // None. + + void setRcvBufSize(int size); + + // Functionality: + // Query the socket address that the channel is using. + // Parameters: + // 0) [out] addr: pointer to store the returned socket address. + // Returned value: + // None. + + void getSockAddr(sockaddr* addr) const; + + // Functionality: + // Query the peer side socket address that the channel is connect to. + // Parameters: + // 0) [out] addr: pointer to store the returned socket address. + // Returned value: + // None. + + void getPeerAddr(sockaddr* addr) const; + + // Functionality: + // Send a packet to the given address. + // Parameters: + // 0) [in] addr: pointer to the destination address. + // 1) [in] packet: reference to a CPacket entity. + // Returned value: + // Actual size of data sent. + + int sendto(const sockaddr* addr, CPacket& packet) const; + + // Functionality: + // Receive a packet from the channel and record the source address. + // Parameters: + // 0) [in] addr: pointer to the source address. + // 1) [in] packet: reference to a CPacket entity. + // Returned value: + // Actual size of data received. + + int recvfrom(sockaddr* addr, CPacket& packet) const; + +private: + void setUDPSockOpt(); + +private: + int m_iIPversion; // IP version + int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test) + + UDPSOCKET m_iSocket; // socket descriptor + + int m_iSndBufSize; // UDP sending buffer size + int m_iRcvBufSize; // UDP receiving buffer size +}; + + +#endif diff --git a/vendor/udt4/src/common.cpp b/vendor/udt4/src/common.cpp new file mode 100644 index 000000000..3b6ffda4b --- /dev/null +++ b/vendor/udt4/src/common.cpp @@ -0,0 +1,765 @@ +/***************************************************************************** +Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 07/25/2010 +*****************************************************************************/ + + +#ifndef WIN32 + #include + #include + #include + #ifdef OSX + #include + #endif +#else + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif + +#include +#include "md5.h" +#include "common.h" + +bool CTimer::m_bUseMicroSecond = false; +uint64_t CTimer::s_ullCPUFrequency = CTimer::readCPUFrequency(); +#ifndef WIN32 + pthread_mutex_t CTimer::m_EventLock = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t CTimer::m_EventCond = PTHREAD_COND_INITIALIZER; +#else + pthread_mutex_t CTimer::m_EventLock = CreateMutex(NULL, false, NULL); + pthread_cond_t CTimer::m_EventCond = CreateEvent(NULL, false, false, NULL); +#endif + +CTimer::CTimer(): +m_ullSchedTime(), +m_TickCond(), +m_TickLock() +{ + #ifndef WIN32 + pthread_mutex_init(&m_TickLock, NULL); + pthread_cond_init(&m_TickCond, NULL); + #else + m_TickLock = CreateMutex(NULL, false, NULL); + m_TickCond = CreateEvent(NULL, false, false, NULL); + #endif +} + +CTimer::~CTimer() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_TickLock); + pthread_cond_destroy(&m_TickCond); + #else + CloseHandle(m_TickLock); + CloseHandle(m_TickCond); + #endif +} + +void CTimer::rdtsc(uint64_t &x) +{ + if (m_bUseMicroSecond) + { + x = getTime(); + return; + } + + #ifdef IA32 + uint32_t lval, hval; + //asm volatile ("push %eax; push %ebx; push %ecx; push %edx"); + //asm volatile ("xor %eax, %eax; cpuid"); + asm volatile ("rdtsc" : "=a" (lval), "=d" (hval)); + //asm volatile ("pop %edx; pop %ecx; pop %ebx; pop %eax"); + x = hval; + x = (x << 32) | lval; + #elif defined(IA64) + asm ("mov %0=ar.itc" : "=r"(x) :: "memory"); + #elif defined(AMD64) + uint32_t lval, hval; + asm ("rdtsc" : "=a" (lval), "=d" (hval)); + x = hval; + x = (x << 32) | lval; + #elif defined(WIN32) + //HANDLE hCurThread = ::GetCurrentThread(); + //DWORD_PTR dwOldMask = ::SetThreadAffinityMask(hCurThread, 1); + BOOL ret = QueryPerformanceCounter((LARGE_INTEGER *)&x); + //SetThreadAffinityMask(hCurThread, dwOldMask); + if (!ret) + x = getTime() * s_ullCPUFrequency; + #elif defined(OSX) + x = mach_absolute_time(); + #else + // use system call to read time clock for other archs + x = getTime(); + #endif +} + +uint64_t CTimer::readCPUFrequency() +{ + uint64_t frequency = 1; // 1 tick per microsecond. + + #if defined(IA32) || defined(IA64) || defined(AMD64) + uint64_t t1, t2; + + rdtsc(t1); + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep(&ts, NULL); + rdtsc(t2); + + // CPU clocks per microsecond + frequency = (t2 - t1) / 100000; + #elif defined(WIN32) + int64_t ccf; + if (QueryPerformanceFrequency((LARGE_INTEGER *)&ccf)) + frequency = ccf / 1000000; + #elif defined(OSX) + mach_timebase_info_data_t info; + mach_timebase_info(&info); + frequency = info.denom * 1000ULL / info.numer; + #endif + + // Fall back to microsecond if the resolution is not high enough. + if (frequency < 10) + { + frequency = 1; + m_bUseMicroSecond = true; + } + return frequency; +} + +uint64_t CTimer::getCPUFrequency() +{ + return s_ullCPUFrequency; +} + +void CTimer::sleep(uint64_t interval) +{ + uint64_t t; + rdtsc(t); + + // sleep next "interval" time + sleepto(t + interval); +} + +void CTimer::sleepto(uint64_t nexttime) +{ + // Use class member such that the method can be interrupted by others + m_ullSchedTime = nexttime; + + uint64_t t; + rdtsc(t); + + while (t < m_ullSchedTime) + { + #ifndef NO_BUSY_WAITING + #ifdef IA32 + __asm__ volatile ("pause; rep; nop; nop; nop; nop; nop;"); + #elif IA64 + __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;"); + #elif AMD64 + __asm__ volatile ("nop; nop; nop; nop; nop;"); + #endif + #else + #ifndef WIN32 + timeval now; + timespec timeout; + gettimeofday(&now, 0); + if (now.tv_usec < 990000) + { + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + 10000) * 1000; + } + else + { + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; + } + pthread_mutex_lock(&m_TickLock); + pthread_cond_timedwait(&m_TickCond, &m_TickLock, &timeout); + pthread_mutex_unlock(&m_TickLock); + #else + WaitForSingleObject(m_TickCond, 1); + #endif + #endif + + rdtsc(t); + } +} + +void CTimer::interrupt() +{ + // schedule the sleepto time to the current CCs, so that it will stop + rdtsc(m_ullSchedTime); + tick(); +} + +void CTimer::tick() +{ + #ifndef WIN32 + pthread_cond_signal(&m_TickCond); + #else + SetEvent(m_TickCond); + #endif +} + +uint64_t CTimer::getTime() +{ + //For Cygwin and other systems without microsecond level resolution, uncomment the following three lines + //uint64_t x; + //rdtsc(x); + //return x / s_ullCPUFrequency; + //Specific fix may be necessary if rdtsc is not available either. + + #ifndef WIN32 + timeval t; + gettimeofday(&t, 0); + return t.tv_sec * 1000000ULL + t.tv_usec; + #else + LARGE_INTEGER ccf; + HANDLE hCurThread = ::GetCurrentThread(); + DWORD_PTR dwOldMask = ::SetThreadAffinityMask(hCurThread, 1); + if (QueryPerformanceFrequency(&ccf)) + { + LARGE_INTEGER cc; + if (QueryPerformanceCounter(&cc)) + { + SetThreadAffinityMask(hCurThread, dwOldMask); + return (cc.QuadPart * 1000000ULL / ccf.QuadPart); + } + } + + SetThreadAffinityMask(hCurThread, dwOldMask); + return GetTickCount() * 1000ULL; + #endif +} + +void CTimer::triggerEvent() +{ + #ifndef WIN32 + pthread_cond_signal(&m_EventCond); + #else + SetEvent(m_EventCond); + #endif +} + +void CTimer::waitForEvent() +{ + #ifndef WIN32 + timeval now; + timespec timeout; + gettimeofday(&now, 0); + if (now.tv_usec < 990000) + { + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + 10000) * 1000; + } + else + { + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; + } + pthread_mutex_lock(&m_EventLock); + pthread_cond_timedwait(&m_EventCond, &m_EventLock, &timeout); + pthread_mutex_unlock(&m_EventLock); + #else + WaitForSingleObject(m_EventCond, 1); + #endif +} + +void CTimer::sleep() +{ + #ifndef WIN32 + usleep(10); + #else + Sleep(1); + #endif +} + + +// +// Automatically lock in constructor +CGuard::CGuard(pthread_mutex_t& lock): +m_Mutex(lock), +m_iLocked() +{ + #ifndef WIN32 + m_iLocked = pthread_mutex_lock(&m_Mutex); + #else + m_iLocked = WaitForSingleObject(m_Mutex, INFINITE); + #endif +} + +// Automatically unlock in destructor +CGuard::~CGuard() +{ + #ifndef WIN32 + if (0 == m_iLocked) + pthread_mutex_unlock(&m_Mutex); + #else + if (WAIT_FAILED != m_iLocked) + ReleaseMutex(m_Mutex); + #endif +} + +void CGuard::enterCS(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_lock(&lock); + #else + WaitForSingleObject(lock, INFINITE); + #endif +} + +void CGuard::leaveCS(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_unlock(&lock); + #else + ReleaseMutex(lock); + #endif +} + +void CGuard::createMutex(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_init(&lock, NULL); + #else + lock = CreateMutex(NULL, false, NULL); + #endif +} + +void CGuard::releaseMutex(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_destroy(&lock); + #else + CloseHandle(lock); + #endif +} + +void CGuard::createCond(pthread_cond_t& cond) +{ + #ifndef WIN32 + pthread_cond_init(&cond, NULL); + #else + cond = CreateEvent(NULL, false, false, NULL); + #endif +} + +void CGuard::releaseCond(pthread_cond_t& cond) +{ + #ifndef WIN32 + pthread_cond_destroy(&cond); + #else + CloseHandle(cond); + #endif + +} + +// +CUDTException::CUDTException(int major, int minor, int err): +m_iMajor(major), +m_iMinor(minor) +{ + if (-1 == err) + #ifndef WIN32 + m_iErrno = errno; + #else + m_iErrno = GetLastError(); + #endif + else + m_iErrno = err; +} + +CUDTException::CUDTException(const CUDTException& e): +m_iMajor(e.m_iMajor), +m_iMinor(e.m_iMinor), +m_iErrno(e.m_iErrno), +m_strMsg() +{ +} + +CUDTException::~CUDTException() +{ +} + +const char* CUDTException::getErrorMessage() +{ + // translate "Major:Minor" code into text message. + + switch (m_iMajor) + { + case 0: + m_strMsg = "Success"; + break; + + case 1: + m_strMsg = "Connection setup failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": connection time out"; + break; + + case 2: + m_strMsg += ": connection rejected"; + break; + + case 3: + m_strMsg += ": unable to create/configure UDP socket"; + break; + + case 4: + m_strMsg += ": abort for security reasons"; + break; + + default: + break; + } + + break; + + case 2: + switch (m_iMinor) + { + case 1: + m_strMsg = "Connection was broken"; + break; + + case 2: + m_strMsg = "Connection does not exist"; + break; + + default: + break; + } + + break; + + case 3: + m_strMsg = "System resource failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": unable to create new threads"; + break; + + case 2: + m_strMsg += ": unable to allocate buffers"; + break; + + default: + break; + } + + break; + + case 4: + m_strMsg = "File system failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": cannot seek read position"; + break; + + case 2: + m_strMsg += ": failure in read"; + break; + + case 3: + m_strMsg += ": cannot seek write position"; + break; + + case 4: + m_strMsg += ": failure in write"; + break; + + default: + break; + } + + break; + + case 5: + m_strMsg = "Operation not supported"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": Cannot do this operation on a BOUND socket"; + break; + + case 2: + m_strMsg += ": Cannot do this operation on a CONNECTED socket"; + break; + + case 3: + m_strMsg += ": Bad parameters"; + break; + + case 4: + m_strMsg += ": Invalid socket ID"; + break; + + case 5: + m_strMsg += ": Cannot do this operation on an UNBOUND socket"; + break; + + case 6: + m_strMsg += ": Socket is not in listening state"; + break; + + case 7: + m_strMsg += ": Listen/accept is not supported in rendezous connection setup"; + break; + + case 8: + m_strMsg += ": Cannot call connect on UNBOUND socket in rendezvous connection setup"; + break; + + case 9: + m_strMsg += ": This operation is not supported in SOCK_STREAM mode"; + break; + + case 10: + m_strMsg += ": This operation is not supported in SOCK_DGRAM mode"; + break; + + case 11: + m_strMsg += ": Another socket is already listening on the same port"; + break; + + case 12: + m_strMsg += ": Message is too large to send (it must be less than the UDT send buffer size)"; + break; + + case 13: + m_strMsg += ": Invalid epoll ID"; + break; + + default: + break; + } + + break; + + case 6: + m_strMsg = "Non-blocking call failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": no buffer available for sending"; + break; + + case 2: + m_strMsg += ": no data available for reading"; + break; + + default: + break; + } + + break; + + case 7: + m_strMsg = "The peer side has signalled an error"; + + break; + + default: + m_strMsg = "Unknown error"; + } + + // Adding "errno" information + if ((0 != m_iMajor) && (0 < m_iErrno)) + { + m_strMsg += ": "; + #ifndef WIN32 + char errmsg[1024]; + if (strerror_r(m_iErrno, errmsg, 1024) == 0) + m_strMsg += errmsg; + #else + LPVOID lpMsgBuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, m_iErrno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); + m_strMsg += (char*)lpMsgBuf; + LocalFree(lpMsgBuf); + #endif + } + + // period + #ifndef WIN32 + m_strMsg += "."; + #endif + + return m_strMsg.c_str(); +} + +int CUDTException::getErrorCode() const +{ + return m_iMajor * 1000 + m_iMinor; +} + +void CUDTException::clear() +{ + m_iMajor = 0; + m_iMinor = 0; + m_iErrno = 0; +} + +const int CUDTException::SUCCESS = 0; +const int CUDTException::ECONNSETUP = 1000; +const int CUDTException::ENOSERVER = 1001; +const int CUDTException::ECONNREJ = 1002; +const int CUDTException::ESOCKFAIL = 1003; +const int CUDTException::ESECFAIL = 1004; +const int CUDTException::ECONNFAIL = 2000; +const int CUDTException::ECONNLOST = 2001; +const int CUDTException::ENOCONN = 2002; +const int CUDTException::ERESOURCE = 3000; +const int CUDTException::ETHREAD = 3001; +const int CUDTException::ENOBUF = 3002; +const int CUDTException::EFILE = 4000; +const int CUDTException::EINVRDOFF = 4001; +const int CUDTException::ERDPERM = 4002; +const int CUDTException::EINVWROFF = 4003; +const int CUDTException::EWRPERM = 4004; +const int CUDTException::EINVOP = 5000; +const int CUDTException::EBOUNDSOCK = 5001; +const int CUDTException::ECONNSOCK = 5002; +const int CUDTException::EINVPARAM = 5003; +const int CUDTException::EINVSOCK = 5004; +const int CUDTException::EUNBOUNDSOCK = 5005; +const int CUDTException::ENOLISTEN = 5006; +const int CUDTException::ERDVNOSERV = 5007; +const int CUDTException::ERDVUNBOUND = 5008; +const int CUDTException::ESTREAMILL = 5009; +const int CUDTException::EDGRAMILL = 5010; +const int CUDTException::EDUPLISTEN = 5011; +const int CUDTException::ELARGEMSG = 5012; +const int CUDTException::EINVPOLLID = 5013; +const int CUDTException::EASYNCFAIL = 6000; +const int CUDTException::EASYNCSND = 6001; +const int CUDTException::EASYNCRCV = 6002; +const int CUDTException::ETIMEOUT = 6003; +const int CUDTException::EPEERERR = 7000; +const int CUDTException::EUNKNOWN = -1; + + +// +bool CIPAddress::ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a1 = (sockaddr_in*)addr1; + sockaddr_in* a2 = (sockaddr_in*)addr2; + + if ((a1->sin_port == a2->sin_port) && (a1->sin_addr.s_addr == a2->sin_addr.s_addr)) + return true; + } + else + { + sockaddr_in6* a1 = (sockaddr_in6*)addr1; + sockaddr_in6* a2 = (sockaddr_in6*)addr2; + + if (a1->sin6_port == a2->sin6_port) + { + for (int i = 0; i < 16; ++ i) + if (*((char*)&(a1->sin6_addr) + i) != *((char*)&(a2->sin6_addr) + i)) + return false; + + return true; + } + } + + return false; +} + +void CIPAddress::ntop(const sockaddr* addr, uint32_t ip[4], int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a = (sockaddr_in*)addr; + ip[0] = a->sin_addr.s_addr; + } + else + { + sockaddr_in6* a = (sockaddr_in6*)addr; + ip[3] = (a->sin6_addr.s6_addr[15] << 24) + (a->sin6_addr.s6_addr[14] << 16) + (a->sin6_addr.s6_addr[13] << 8) + a->sin6_addr.s6_addr[12]; + ip[2] = (a->sin6_addr.s6_addr[11] << 24) + (a->sin6_addr.s6_addr[10] << 16) + (a->sin6_addr.s6_addr[9] << 8) + a->sin6_addr.s6_addr[8]; + ip[1] = (a->sin6_addr.s6_addr[7] << 24) + (a->sin6_addr.s6_addr[6] << 16) + (a->sin6_addr.s6_addr[5] << 8) + a->sin6_addr.s6_addr[4]; + ip[0] = (a->sin6_addr.s6_addr[3] << 24) + (a->sin6_addr.s6_addr[2] << 16) + (a->sin6_addr.s6_addr[1] << 8) + a->sin6_addr.s6_addr[0]; + } +} + +void CIPAddress::pton(sockaddr* addr, const uint32_t ip[4], int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a = (sockaddr_in*)addr; + a->sin_addr.s_addr = ip[0]; + } + else + { + sockaddr_in6* a = (sockaddr_in6*)addr; + for (int i = 0; i < 4; ++ i) + { + a->sin6_addr.s6_addr[i * 4] = ip[i] & 0xFF; + a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8); + a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16); + a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24); + } + } +} + +// +void CMD5::compute(const char* input, unsigned char result[16]) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)input, strlen(input)); + md5_finish(&state, result); +} diff --git a/vendor/udt4/src/common.h b/vendor/udt4/src/common.h new file mode 100644 index 000000000..20c0bb4aa --- /dev/null +++ b/vendor/udt4/src/common.h @@ -0,0 +1,321 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 08/01/2009 +*****************************************************************************/ + +#ifndef __UDT_COMMON_H__ +#define __UDT_COMMON_H__ + + +#ifndef WIN32 + #include + #include + #include +#else + #include +#endif +#include +#include +#include "udt.h" + + +#ifdef WIN32 + // Windows compability + typedef HANDLE pthread_t; + typedef HANDLE pthread_mutex_t; + typedef HANDLE pthread_cond_t; + typedef DWORD pthread_key_t; +#endif + + +//////////////////////////////////////////////////////////////////////////////// + +class CTimer +{ +public: + CTimer(); + ~CTimer(); + +public: + + // Functionality: + // Sleep for "interval" CCs. + // Parameters: + // 0) [in] interval: CCs to sleep. + // Returned value: + // None. + + void sleep(uint64_t interval); + + // Functionality: + // Seelp until CC "nexttime". + // Parameters: + // 0) [in] nexttime: next time the caller is waken up. + // Returned value: + // None. + + void sleepto(uint64_t nexttime); + + // Functionality: + // Stop the sleep() or sleepto() methods. + // Parameters: + // None. + // Returned value: + // None. + + void interrupt(); + + // Functionality: + // trigger the clock for a tick, for better granuality in no_busy_waiting timer. + // Parameters: + // None. + // Returned value: + // None. + + void tick(); + +public: + + // Functionality: + // Read the CPU clock cycle into x. + // Parameters: + // 0) [out] x: to record cpu clock cycles. + // Returned value: + // None. + + static void rdtsc(uint64_t &x); + + // Functionality: + // return the CPU frequency. + // Parameters: + // None. + // Returned value: + // CPU frequency. + + static uint64_t getCPUFrequency(); + + // Functionality: + // check the current time, 64bit, in microseconds. + // Parameters: + // None. + // Returned value: + // current time in microseconds. + + static uint64_t getTime(); + + // Functionality: + // trigger an event such as new connection, close, new data, etc. for "select" call. + // Parameters: + // None. + // Returned value: + // None. + + static void triggerEvent(); + + // Functionality: + // wait for an event to br triggered by "triggerEvent". + // Parameters: + // None. + // Returned value: + // None. + + static void waitForEvent(); + + // Functionality: + // sleep for a short interval. exact sleep time does not matter + // Parameters: + // None. + // Returned value: + // None. + + static void sleep(); + +private: + uint64_t getTimeInMicroSec(); + +private: + uint64_t m_ullSchedTime; // next schedulled time + + pthread_cond_t m_TickCond; + pthread_mutex_t m_TickLock; + + static pthread_cond_t m_EventCond; + static pthread_mutex_t m_EventLock; + +private: + static uint64_t s_ullCPUFrequency; // CPU frequency : clock cycles per microsecond + static uint64_t readCPUFrequency(); + static bool m_bUseMicroSecond; // No higher resolution timer available, use gettimeofday(). +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CGuard +{ +public: + CGuard(pthread_mutex_t& lock); + ~CGuard(); + +public: + static void enterCS(pthread_mutex_t& lock); + static void leaveCS(pthread_mutex_t& lock); + + static void createMutex(pthread_mutex_t& lock); + static void releaseMutex(pthread_mutex_t& lock); + + static void createCond(pthread_cond_t& cond); + static void releaseCond(pthread_cond_t& cond); + +private: + pthread_mutex_t& m_Mutex; // Alias name of the mutex to be protected + int m_iLocked; // Locking status + + CGuard& operator=(const CGuard&); +}; + + + +//////////////////////////////////////////////////////////////////////////////// + +// UDT Sequence Number 0 - (2^31 - 1) + +// seqcmp: compare two seq#, considering the wraping +// seqlen: length from the 1st to the 2nd seq#, including both +// seqoff: offset from the 2nd to the 1st seq# +// incseq: increase the seq# by 1 +// decseq: decrease the seq# by 1 +// incseq: increase the seq# by a given offset + +class CSeqNo +{ +public: + inline static int seqcmp(int32_t seq1, int32_t seq2) + {return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);} + + inline static int seqlen(int32_t seq1, int32_t seq2) + {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} + + inline static int seqoff(int32_t seq1, int32_t seq2) + { + if (abs(seq1 - seq2) < m_iSeqNoTH) + return seq2 - seq1; + + if (seq1 < seq2) + return seq2 - seq1 - m_iMaxSeqNo - 1; + + return seq2 - seq1 + m_iMaxSeqNo + 1; + } + + inline static int32_t incseq(int32_t seq) + {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + + inline static int32_t decseq(int32_t seq) + {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + + inline static int32_t incseq(int32_t seq, int32_t inc) + {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} + +public: + static const int32_t m_iSeqNoTH; // threshold for comparing seq. no. + static const int32_t m_iMaxSeqNo; // maximum sequence number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +// UDT ACK Sub-sequence Number: 0 - (2^31 - 1) + +class CAckNo +{ +public: + inline static int32_t incack(int32_t ackno) + {return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;} + +public: + static const int32_t m_iMaxAckSeqNo; // maximum ACK sub-sequence number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +// UDT Message Number: 0 - (2^29 - 1) + +class CMsgNo +{ +public: + inline static int msgcmp(int32_t msgno1, int32_t msgno2) + {return (abs(msgno1 - msgno2) < m_iMsgNoTH) ? (msgno1 - msgno2) : (msgno2 - msgno1);} + + inline static int msglen(int32_t msgno1, int32_t msgno2) + {return (msgno1 <= msgno2) ? (msgno2 - msgno1 + 1) : (msgno2 - msgno1 + m_iMaxMsgNo + 2);} + + inline static int msgoff(int32_t msgno1, int32_t msgno2) + { + if (abs(msgno1 - msgno2) < m_iMsgNoTH) + return msgno2 - msgno1; + + if (msgno1 < msgno2) + return msgno2 - msgno1 - m_iMaxMsgNo - 1; + + return msgno2 - msgno1 + m_iMaxMsgNo + 1; + } + + inline static int32_t incmsg(int32_t msgno) + {return (msgno == m_iMaxMsgNo) ? 0 : msgno + 1;} + +public: + static const int32_t m_iMsgNoTH; // threshold for comparing msg. no. + static const int32_t m_iMaxMsgNo; // maximum message number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CIPAddress +{ + static bool ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver = AF_INET); + static void ntop(const sockaddr* addr, uint32_t ip[4], int ver = AF_INET); + static void pton(sockaddr* addr, const uint32_t ip[4], int ver = AF_INET); +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CMD5 +{ + static void compute(const char* input, unsigned char result[16]); +}; + + +#endif diff --git a/vendor/udt4/src/core.cpp b/vendor/udt4/src/core.cpp new file mode 100644 index 000000000..ba989aa51 --- /dev/null +++ b/vendor/udt4/src/core.cpp @@ -0,0 +1,2675 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/28/2012 +*****************************************************************************/ + +#ifndef WIN32 + #include + #include + #include + #include + #include + #include +#else + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif +#include +#include +#include "queue.h" +#include "core.h" + +using namespace std; + + +CUDTUnited CUDT::s_UDTUnited; + +const UDTSOCKET CUDT::INVALID_SOCK = -1; +const int CUDT::ERROR = -1; + +const UDTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; +const int UDT::ERROR = CUDT::ERROR; + +const int32_t CSeqNo::m_iSeqNoTH = 0x3FFFFFFF; +const int32_t CSeqNo::m_iMaxSeqNo = 0x7FFFFFFF; +const int32_t CAckNo::m_iMaxAckSeqNo = 0x7FFFFFFF; +const int32_t CMsgNo::m_iMsgNoTH = 0xFFFFFFF; +const int32_t CMsgNo::m_iMaxMsgNo = 0x1FFFFFFF; + +const int CUDT::m_iVersion = 4; +const int CUDT::m_iSYNInterval = 10000; +const int CUDT::m_iSelfClockInterval = 64; + + +CUDT::CUDT() +{ + m_pSndBuffer = NULL; + m_pRcvBuffer = NULL; + m_pSndLossList = NULL; + m_pRcvLossList = NULL; + m_pACKWindow = NULL; + m_pSndTimeWindow = NULL; + m_pRcvTimeWindow = NULL; + + m_pSndQueue = NULL; + m_pRcvQueue = NULL; + m_pPeerAddr = NULL; + m_pSNode = NULL; + m_pRNode = NULL; + + // Initilize mutex and condition variables + initSynch(); + + // Default UDT configurations + m_iMSS = 1500; + m_bSynSending = true; + m_bSynRecving = true; + m_iFlightFlagSize = 25600; + m_iSndBufSize = 8192; + m_iRcvBufSize = 8192; //Rcv buffer MUST NOT be bigger than Flight Flag size + m_Linger.l_onoff = 1; + m_Linger.l_linger = 180; + m_iUDPSndBufSize = 65536; + m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; + m_iSockType = UDT_STREAM; + m_iIPversion = AF_INET; + m_bRendezvous = false; + m_iSndTimeOut = -1; + m_iRcvTimeOut = -1; + m_bReuseAddr = true; + m_llMaxBW = -1; + + m_pCCFactory = new CCCFactory; + m_pCC = NULL; + m_pCache = NULL; + + // Initial status + m_bOpened = false; + m_bListening = false; + m_bConnecting = false; + m_bConnected = false; + m_bClosing = false; + m_bShutdown = false; + m_bBroken = false; + m_bPeerHealth = true; + m_ullLingerExpiration = 0; +} + +CUDT::CUDT(const CUDT& ancestor) +{ + m_pSndBuffer = NULL; + m_pRcvBuffer = NULL; + m_pSndLossList = NULL; + m_pRcvLossList = NULL; + m_pACKWindow = NULL; + m_pSndTimeWindow = NULL; + m_pRcvTimeWindow = NULL; + + m_pSndQueue = NULL; + m_pRcvQueue = NULL; + m_pPeerAddr = NULL; + m_pSNode = NULL; + m_pRNode = NULL; + + // Initilize mutex and condition variables + initSynch(); + + // Default UDT configurations + m_iMSS = ancestor.m_iMSS; + m_bSynSending = ancestor.m_bSynSending; + m_bSynRecving = ancestor.m_bSynRecving; + m_iFlightFlagSize = ancestor.m_iFlightFlagSize; + m_iSndBufSize = ancestor.m_iSndBufSize; + m_iRcvBufSize = ancestor.m_iRcvBufSize; + m_Linger = ancestor.m_Linger; + m_iUDPSndBufSize = ancestor.m_iUDPSndBufSize; + m_iUDPRcvBufSize = ancestor.m_iUDPRcvBufSize; + m_iSockType = ancestor.m_iSockType; + m_iIPversion = ancestor.m_iIPversion; + m_bRendezvous = ancestor.m_bRendezvous; + m_iSndTimeOut = ancestor.m_iSndTimeOut; + m_iRcvTimeOut = ancestor.m_iRcvTimeOut; + m_bReuseAddr = true; // this must be true, because all accepted sockets shared the same port with the listener + m_llMaxBW = ancestor.m_llMaxBW; + + m_pCCFactory = ancestor.m_pCCFactory->clone(); + m_pCC = NULL; + m_pCache = ancestor.m_pCache; + + // Initial status + m_bOpened = false; + m_bListening = false; + m_bConnecting = false; + m_bConnected = false; + m_bClosing = false; + m_bShutdown = false; + m_bBroken = false; + m_bPeerHealth = true; + m_ullLingerExpiration = 0; +} + +CUDT::~CUDT() +{ + // release mutex/condtion variables + destroySynch(); + + // destroy the data structures + delete m_pSndBuffer; + delete m_pRcvBuffer; + delete m_pSndLossList; + delete m_pRcvLossList; + delete m_pACKWindow; + delete m_pSndTimeWindow; + delete m_pRcvTimeWindow; + delete m_pCCFactory; + delete m_pCC; + delete m_pPeerAddr; + delete m_pSNode; + delete m_pRNode; +} + +void CUDT::setOpt(UDTOpt optName, const void* optval, int) +{ + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + + CGuard cg(m_ConnectionLock); + CGuard sendguard(m_SendLock); + CGuard recvguard(m_RecvLock); + + switch (optName) + { + case UDT_MSS: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + if (*(int*)optval < int(28 + CHandShake::m_iContentSize)) + throw CUDTException(5, 3, 0); + + m_iMSS = *(int*)optval; + + // Packet size cannot be greater than UDP buffer size + if (m_iMSS > m_iUDPSndBufSize) + m_iMSS = m_iUDPSndBufSize; + if (m_iMSS > m_iUDPRcvBufSize) + m_iMSS = m_iUDPRcvBufSize; + + break; + + case UDT_SNDSYN: + m_bSynSending = *(bool *)optval; + break; + + case UDT_RCVSYN: + m_bSynRecving = *(bool *)optval; + break; + + case UDT_CC: + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 1, 0); + if (NULL != m_pCCFactory) + delete m_pCCFactory; + m_pCCFactory = ((CCCVirtualFactory *)optval)->clone(); + + break; + + case UDT_FC: + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 2, 0); + + if (*(int*)optval < 1) + throw CUDTException(5, 3); + + // Mimimum recv flight flag size is 32 packets + if (*(int*)optval > 32) + m_iFlightFlagSize = *(int*)optval; + else + m_iFlightFlagSize = 32; + + break; + + case UDT_SNDBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + if (*(int*)optval <= 0) + throw CUDTException(5, 3, 0); + + m_iSndBufSize = *(int*)optval / (m_iMSS - 28); + + break; + + case UDT_RCVBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + if (*(int*)optval <= 0) + throw CUDTException(5, 3, 0); + + // Mimimum recv buffer size is 32 packets + if (*(int*)optval > (m_iMSS - 28) * 32) + m_iRcvBufSize = *(int*)optval / (m_iMSS - 28); + else + m_iRcvBufSize = 32; + + // recv buffer MUST not be greater than FC size + if (m_iRcvBufSize > m_iFlightFlagSize) + m_iRcvBufSize = m_iFlightFlagSize; + + break; + + case UDT_LINGER: + m_Linger = *(linger*)optval; + break; + + case UDP_SNDBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + m_iUDPSndBufSize = *(int*)optval; + + if (m_iUDPSndBufSize < m_iMSS) + m_iUDPSndBufSize = m_iMSS; + + break; + + case UDP_RCVBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + m_iUDPRcvBufSize = *(int*)optval; + + if (m_iUDPRcvBufSize < m_iMSS) + m_iUDPRcvBufSize = m_iMSS; + + break; + + case UDT_RENDEZVOUS: + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 1, 0); + m_bRendezvous = *(bool *)optval; + break; + + case UDT_SNDTIMEO: + m_iSndTimeOut = *(int*)optval; + break; + + case UDT_RCVTIMEO: + m_iRcvTimeOut = *(int*)optval; + break; + + case UDT_REUSEADDR: + if (m_bOpened) + throw CUDTException(5, 1, 0); + m_bReuseAddr = *(bool*)optval; + break; + + case UDT_MAXBW: + m_llMaxBW = *(int64_t*)optval; + break; + + default: + throw CUDTException(5, 0, 0); + } +} + +void CUDT::getOpt(UDTOpt optName, void* optval, int& optlen) +{ + CGuard cg(m_ConnectionLock); + + switch (optName) + { + case UDT_MSS: + *(int*)optval = m_iMSS; + optlen = sizeof(int); + break; + + case UDT_SNDSYN: + *(bool*)optval = m_bSynSending; + optlen = sizeof(bool); + break; + + case UDT_RCVSYN: + *(bool*)optval = m_bSynRecving; + optlen = sizeof(bool); + break; + + case UDT_CC: + if (!m_bOpened) + throw CUDTException(5, 5, 0); + *(CCC**)optval = m_pCC; + optlen = sizeof(CCC*); + + break; + + case UDT_FC: + *(int*)optval = m_iFlightFlagSize; + optlen = sizeof(int); + break; + + case UDT_SNDBUF: + *(int*)optval = m_iSndBufSize * (m_iMSS - 28); + optlen = sizeof(int); + break; + + case UDT_RCVBUF: + *(int*)optval = m_iRcvBufSize * (m_iMSS - 28); + optlen = sizeof(int); + break; + + case UDT_LINGER: + if (optlen < (int)(sizeof(linger))) + throw CUDTException(5, 3, 0); + + *(linger*)optval = m_Linger; + optlen = sizeof(linger); + break; + + case UDP_SNDBUF: + *(int*)optval = m_iUDPSndBufSize; + optlen = sizeof(int); + break; + + case UDP_RCVBUF: + *(int*)optval = m_iUDPRcvBufSize; + optlen = sizeof(int); + break; + + case UDT_RENDEZVOUS: + *(bool *)optval = m_bRendezvous; + optlen = sizeof(bool); + break; + + case UDT_SNDTIMEO: + *(int*)optval = m_iSndTimeOut; + optlen = sizeof(int); + break; + + case UDT_RCVTIMEO: + *(int*)optval = m_iRcvTimeOut; + optlen = sizeof(int); + break; + + case UDT_REUSEADDR: + *(bool *)optval = m_bReuseAddr; + optlen = sizeof(bool); + break; + + case UDT_MAXBW: + *(int64_t*)optval = m_llMaxBW; + optlen = sizeof(int64_t); + break; + + case UDT_STATE: + *(int32_t*)optval = s_UDTUnited.getStatus(m_SocketID); + optlen = sizeof(int32_t); + break; + + case UDT_EVENT: + { + int32_t event = 0; + if (m_bBroken) + event |= UDT_EPOLL_ERR; + else + { + if (m_pRcvBuffer && (m_pRcvBuffer->getRcvDataSize() > 0)) + event |= UDT_EPOLL_IN; + if (m_pSndBuffer && (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) + event |= UDT_EPOLL_OUT; + } + *(int32_t*)optval = event; + optlen = sizeof(int32_t); + break; + } + + case UDT_SNDDATA: + if (m_pSndBuffer) + *(int32_t*)optval = m_pSndBuffer->getCurrBufSize(); + else + *(int32_t*)optval = 0; + optlen = sizeof(int32_t); + break; + + case UDT_RCVDATA: + if (m_pRcvBuffer) + *(int32_t*)optval = m_pRcvBuffer->getRcvDataSize(); + else + *(int32_t*)optval = 0; + optlen = sizeof(int32_t); + break; + + default: + throw CUDTException(5, 0, 0); + } +} + +void CUDT::open() +{ + CGuard cg(m_ConnectionLock); + + // Initial sequence number, loss, acknowledgement, etc. + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + + m_iEXPCount = 1; + m_iBandwidth = 1; + m_iDeliveryRate = 16; + m_iAckSeqNo = 0; + m_ullLastAckTime = 0; + + // trace information + m_StartTime = CTimer::getTime(); + m_llSentTotal = m_llRecvTotal = m_iSndLossTotal = m_iRcvLossTotal = m_iRetransTotal = m_iSentACKTotal = m_iRecvACKTotal = m_iSentNAKTotal = m_iRecvNAKTotal = 0; + m_LastSampleTime = CTimer::getTime(); + m_llTraceSent = m_llTraceRecv = m_iTraceSndLoss = m_iTraceRcvLoss = m_iTraceRetrans = m_iSentACK = m_iRecvACK = m_iSentNAK = m_iRecvNAK = 0; + m_llSndDuration = m_llSndDurationTotal = 0; + + // structures for queue + if (NULL == m_pSNode) + m_pSNode = new CSNode; + m_pSNode->m_pUDT = this; + m_pSNode->m_llTimeStamp = 1; + m_pSNode->m_iHeapLoc = -1; + + if (NULL == m_pRNode) + m_pRNode = new CRNode; + m_pRNode->m_pUDT = this; + m_pRNode->m_llTimeStamp = 1; + m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL; + m_pRNode->m_bOnList = false; + + m_iRTT = 10 * m_iSYNInterval; + m_iRTTVar = m_iRTT >> 1; + m_ullCPUFrequency = CTimer::getCPUFrequency(); + + // set up the timers + m_ullSYNInt = m_iSYNInterval * m_ullCPUFrequency; + + // set minimum NAK and EXP timeout to 100ms + m_ullMinNakInt = 300000 * m_ullCPUFrequency; + m_ullMinExpInt = 300000 * m_ullCPUFrequency; + + m_ullACKInt = m_ullSYNInt; + m_ullNAKInt = m_ullMinNakInt; + + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + m_ullNextACKTime = currtime + m_ullSYNInt; + m_ullNextNAKTime = currtime + m_ullNAKInt; + + m_iPktCount = 0; + m_iLightACKCount = 1; + + m_ullTargetTime = 0; + m_ullTimeDiff = 0; + + // Now UDT is opened. + m_bOpened = true; +} + +void CUDT::listen() +{ + CGuard cg(m_ConnectionLock); + + if (!m_bOpened) + throw CUDTException(5, 0, 0); + + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 2, 0); + + // listen can be called more than once + if (m_bListening) + return; + + // if there is already another socket listening on the same port + if (m_pRcvQueue->setListener(this) < 0) + throw CUDTException(5, 11, 0); + + m_bListening = true; +} + +void CUDT::connect(const sockaddr* serv_addr) +{ + CGuard cg(m_ConnectionLock); + + if (!m_bOpened) + throw CUDTException(5, 0, 0); + + if (m_bListening) + throw CUDTException(5, 2, 0); + + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 2, 0); + + // record peer/server address + delete m_pPeerAddr; + m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; + memcpy(m_pPeerAddr, serv_addr, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + + // register this socket in the rendezvous queue + // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this function + uint64_t ttl = 3000000; + if (m_bRendezvous) + ttl *= 10; + ttl += CTimer::getTime(); + m_pRcvQueue->registerConnector(m_SocketID, this, m_iIPversion, serv_addr, ttl); + + // This is my current configurations + m_ConnReq.m_iVersion = m_iVersion; + m_ConnReq.m_iType = m_iSockType; + m_ConnReq.m_iMSS = m_iMSS; + m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; + m_ConnReq.m_iReqType = (!m_bRendezvous) ? 1 : 0; + m_ConnReq.m_iID = m_SocketID; + CIPAddress::ntop(serv_addr, m_ConnReq.m_piPeerIP, m_iIPversion); + + // Random Initial Sequence Number + srand((unsigned int)CTimer::getTime()); + m_iISN = m_ConnReq.m_iISN = (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); + + m_iLastDecSeq = m_iISN - 1; + m_iSndLastAck = m_iISN; + m_iSndLastDataAck = m_iISN; + m_iSndCurrSeqNo = m_iISN - 1; + m_iSndLastAck2 = m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // Inform the server my configurations. + CPacket request; + char* reqdata = new char [m_iPayloadSize]; + request.pack(0, NULL, reqdata, m_iPayloadSize); + // ID = 0, connection request + request.m_iID = 0; + + int hs_size = m_iPayloadSize; + m_ConnReq.serialize(reqdata, hs_size); + request.setLength(hs_size); + m_pSndQueue->sendto(serv_addr, request); + m_llLastReqTime = CTimer::getTime(); + + m_bConnecting = true; + + // asynchronous connect, return immediately + if (!m_bSynRecving) + { + delete [] reqdata; + return; + } + + // Wait for the negotiated configurations from the peer side. + CPacket response; + char* resdata = new char [m_iPayloadSize]; + response.pack(0, NULL, resdata, m_iPayloadSize); + + CUDTException e(0, 0); + + while (!m_bClosing) + { + // avoid sending too many requests, at most 1 request per 250ms + if (CTimer::getTime() - m_llLastReqTime > 250000) + { + m_ConnReq.serialize(reqdata, hs_size); + request.setLength(hs_size); + if (m_bRendezvous) + request.m_iID = m_ConnRes.m_iID; + m_pSndQueue->sendto(serv_addr, request); + m_llLastReqTime = CTimer::getTime(); + } + + response.setLength(m_iPayloadSize); + if (m_pRcvQueue->recvfrom(m_SocketID, response) > 0) + { + if (connect(response) <= 0) + break; + + // new request/response should be sent out immediately on receving a response + m_llLastReqTime = 0; + } + + if (CTimer::getTime() > ttl) + { + // timeout + e = CUDTException(1, 1, 0); + break; + } + } + + delete [] reqdata; + delete [] resdata; + + if (e.getErrorCode() == 0) + { + if (m_bClosing) // if the socket is closed before connection... + e = CUDTException(1); + else if (1002 == m_ConnRes.m_iReqType) // connection request rejected + e = CUDTException(1, 2, 0); + else if ((!m_bRendezvous) && (m_iISN != m_ConnRes.m_iISN)) // secuity check + e = CUDTException(1, 4, 0); + } + + if (e.getErrorCode() != 0) + throw e; +} + +int CUDT::connect(const CPacket& response) throw () +{ + // this is the 2nd half of a connection request. If the connection is setup successfully this returns 0. + // returning -1 means there is an error. + // returning 1 or 2 means the connection is in process and needs more handshake + + if (!m_bConnecting) + return -1; + + if (m_bRendezvous && ((0 == response.getFlag()) || (1 == response.getType())) && (0 != m_ConnRes.m_iType)) + { + //a data packet or a keep-alive packet comes, which means the peer side is already connected + // in this situation, the previously recorded response will be used + goto POST_CONNECT; + } + + if ((1 != response.getFlag()) || (0 != response.getType())) + return -1; + + m_ConnRes.deserialize(response.m_pcData, response.getLength()); + + if (m_bRendezvous) + { + // regular connect should NOT communicate with rendezvous connect + // rendezvous connect require 3-way handshake + if (1 == m_ConnRes.m_iReqType) + return -1; + + if ((0 == m_ConnReq.m_iReqType) || (0 == m_ConnRes.m_iReqType)) + { + m_ConnReq.m_iReqType = -1; + // the request time must be updated so that the next handshake can be sent out immediately. + m_llLastReqTime = 0; + return 1; + } + } + else + { + // set cookie + if (1 == m_ConnRes.m_iReqType) + { + m_ConnReq.m_iReqType = -1; + m_ConnReq.m_iCookie = m_ConnRes.m_iCookie; + m_llLastReqTime = 0; + return 1; + } + } + +POST_CONNECT: + // Remove from rendezvous queue + m_pRcvQueue->removeConnector(m_SocketID); + + // Re-configure according to the negotiated values. + m_iMSS = m_ConnRes.m_iMSS; + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + m_iPeerISN = m_ConnRes.m_iISN; + m_iRcvLastAck = m_ConnRes.m_iISN; + m_iRcvLastAckAck = m_ConnRes.m_iISN; + m_iRcvCurrSeqNo = m_ConnRes.m_iISN - 1; + m_PeerID = m_ConnRes.m_iID; + memcpy(m_piSelfIP, m_ConnRes.m_piPeerIP, 16); + + // Prepare all data structures + try + { + m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); + // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. + m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); + m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); + m_pACKWindow = new CACKWindow(1024); + m_pRcvTimeWindow = new CPktTimeWindow(16, 64); + m_pSndTimeWindow = new CPktTimeWindow(); + } + catch (...) + { + throw CUDTException(3, 2, 0); + } + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + m_pCC = m_pCCFactory->create(); + m_pCC->m_UDT = m_SocketID; + m_pCC->setMSS(m_iMSS); + m_pCC->setMaxCWndSize(m_iFlowWindowSize); + m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setRTT(m_iRTT); + m_pCC->setBandwidth(m_iBandwidth); + m_pCC->init(); + + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + // And, I am connected too. + m_bConnecting = false; + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + // acknowledge the management module. + s_UDTUnited.connect_complete(m_SocketID); + + // acknowledde any waiting epolls to write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + + return 0; +} + +void CUDT::connect(const sockaddr* peer, CHandShake* hs) +{ + CGuard cg(m_ConnectionLock); + + // Uses the smaller MSS between the peers + if (hs->m_iMSS > m_iMSS) + hs->m_iMSS = m_iMSS; + else + m_iMSS = hs->m_iMSS; + + // exchange info for maximum flow window size + m_iFlowWindowSize = hs->m_iFlightFlagSize; + hs->m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; + + m_iPeerISN = hs->m_iISN; + + m_iRcvLastAck = hs->m_iISN; + m_iRcvLastAckAck = hs->m_iISN; + m_iRcvCurrSeqNo = hs->m_iISN - 1; + + m_PeerID = hs->m_iID; + hs->m_iID = m_SocketID; + + // use peer's ISN and send it back for security check + m_iISN = hs->m_iISN; + + m_iLastDecSeq = m_iISN - 1; + m_iSndLastAck = m_iISN; + m_iSndLastDataAck = m_iISN; + m_iSndCurrSeqNo = m_iISN - 1; + m_iSndLastAck2 = m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // this is a reponse handshake + hs->m_iReqType = -1; + + // get local IP address and send the peer its IP address (because UDP cannot get local IP address) + memcpy(m_piSelfIP, hs->m_piPeerIP, 16); + CIPAddress::ntop(peer, hs->m_piPeerIP, m_iIPversion); + + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + + // Prepare all structures + try + { + m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); + m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); + m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); + m_pACKWindow = new CACKWindow(1024); + m_pRcvTimeWindow = new CPktTimeWindow(16, 64); + m_pSndTimeWindow = new CPktTimeWindow(); + } + catch (...) + { + throw CUDTException(3, 2, 0); + } + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(peer, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + m_pCC = m_pCCFactory->create(); + m_pCC->m_UDT = m_SocketID; + m_pCC->setMSS(m_iMSS); + m_pCC->setMaxCWndSize(m_iFlowWindowSize); + m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setRTT(m_iRTT); + m_pCC->setBandwidth(m_iBandwidth); + m_pCC->init(); + + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; + memcpy(m_pPeerAddr, peer, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + + // And of course, it is connected. + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + //send the response to the peer, see listen() for more discussions about this + CPacket response; + int size = CHandShake::m_iContentSize; + char* buffer = new char[size]; + hs->serialize(buffer, size); + response.pack(0, NULL, buffer, size); + response.m_iID = m_PeerID; + m_pSndQueue->sendto(peer, response); + delete [] buffer; +} + +void CUDT::close() +{ + if (!m_bOpened) + return; + + if (0 != m_Linger.l_onoff) + { + uint64_t entertime = CTimer::getTime(); + + while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && (CTimer::getTime() - entertime < m_Linger.l_linger * 1000000ULL)) + { + // linger has been checked by previous close() call and has expired + if (m_ullLingerExpiration >= entertime) + break; + + if (!m_bSynSending) + { + // if this socket enables asynchronous sending, return immediately and let GC to close it later + if (0 == m_ullLingerExpiration) + m_ullLingerExpiration = entertime + m_Linger.l_linger * 1000000ULL; + + return; + } + + #ifndef WIN32 + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + #else + Sleep(1); + #endif + } + } + + // remove this socket from the snd queue + if (m_bConnected) + m_pSndQueue->m_pSndUList->remove(this); + + // trigger any pending IO events. + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true); + // then remove itself from all epoll monitoring + try + { + for (set::iterator i = m_sPollID.begin(); i != m_sPollID.end(); ++ i) + s_UDTUnited.m_EPoll.remove_usock(*i, m_SocketID); + } + catch (...) + { + } + + if (!m_bOpened) + return; + + // Inform the threads handler to stop. + m_bClosing = true; + + CGuard cg(m_ConnectionLock); + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + + if (m_bListening) + { + m_bListening = false; + m_pRcvQueue->removeListener(this); + } + else if (m_bConnecting) + { + m_pRcvQueue->removeConnector(m_SocketID); + } + + if (m_bConnected) + { + if (!m_bShutdown) + sendCtrl(5); + + m_pCC->close(); + + // Store current connection information. + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); + ib.m_iRTT = m_iRTT; + ib.m_iBandwidth = m_iBandwidth; + m_pCache->update(&ib); + + m_bConnected = false; + } + + // waiting all send and recv calls to stop + CGuard sendguard(m_SendLock); + CGuard recvguard(m_RecvLock); + + // CLOSED. + m_bOpened = false; +} + +int CUDT::send(const char* data, int len) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + // throw an exception if not connected + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (len <= 0) + return 0; + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + } + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + if (!m_bSynSending) + throw CUDTException(6, 1, 0); + else + { + // wait here during a blocking sending + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth && (CTimer::getTime() < exptime)) + pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); + } + pthread_mutex_unlock(&m_SendBlockLock); + #else + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + WaitForSingleObject(m_SendBlockCond, INFINITE); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth && (CTimer::getTime() < exptime)) + WaitForSingleObject(m_SendBlockCond, DWORD((exptime - CTimer::getTime()) / 1000)); + } + #endif + + // check the connection status + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if (!m_bPeerHealth) + { + m_bPeerHealth = true; + throw CUDTException(7); + } + } + } + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + if (m_iSndTimeOut >= 0) + throw CUDTException(6, 3, 0); + + return 0; + } + + int size = (m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize; + if (size > len) + size = len; + + // record total time used for sending + if (0 == m_pSndBuffer->getCurrBufSize()) + m_llSndDurationCounter = CTimer::getTime(); + + // insert the user buffer into the sening list + m_pSndBuffer->addBuffer(data, size); + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return size; +} + +int CUDT::recv(char* data, int len) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + if (len <= 0) + return 0; + + CGuard recvguard(m_RecvLock); + + if (0 == m_pRcvBuffer->getRcvDataSize()) + { + if (!m_bSynRecving) + throw CUDTException(6, 2, 0); + else + { + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + { + pthread_cond_timedwait(&m_RecvDataCond, &m_RecvDataLock, &locktime); + if (CTimer::getTime() >= exptime) + break; + } + } + pthread_mutex_unlock(&m_RecvDataLock); + #else + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + WaitForSingleObject(m_RecvDataCond, INFINITE); + } + else + { + uint64_t enter_time = CTimer::getTime(); + + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + { + int diff = int(CTimer::getTime() - enter_time) / 1000; + if (diff >= m_iRcvTimeOut) + break; + WaitForSingleObject(m_RecvDataCond, DWORD(m_iRcvTimeOut - diff )); + } + } + #endif + } + } + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + int res = m_pRcvBuffer->readBuffer(data, len); + + if (m_pRcvBuffer->getRcvDataSize() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(6, 3, 0); + + return res; +} + +int CUDT::sendmsg(const char* data, int len, int msttl, bool inorder) +{ + if (UDT_STREAM == m_iSockType) + throw CUDTException(5, 9, 0); + + // throw an exception if not connected + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (len <= 0) + return 0; + + if (len > m_iSndBufSize * m_iPayloadSize) + throw CUDTException(5, 12, 0); + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + } + + if ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) + { + if (!m_bSynSending) + throw CUDTException(6, 1, 0); + else + { + // wait here during a blocking sending + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len)) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) && (CTimer::getTime() < exptime)) + pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); + } + pthread_mutex_unlock(&m_SendBlockLock); + #else + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len)) + WaitForSingleObject(m_SendBlockCond, INFINITE); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) && (CTimer::getTime() < exptime)) + WaitForSingleObject(m_SendBlockCond, DWORD((exptime - CTimer::getTime()) / 1000)); + } + #endif + + // check the connection status + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + } + } + + if ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) + { + if (m_iSndTimeOut >= 0) + throw CUDTException(6, 3, 0); + + return 0; + } + + // record total time used for sending + if (0 == m_pSndBuffer->getCurrBufSize()) + m_llSndDurationCounter = CTimer::getTime(); + + // insert the user buffer into the sening list + m_pSndBuffer->addBuffer(data, len, msttl, inorder); + + // insert this socket to the snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return len; +} + +int CUDT::recvmsg(char* data, int len) +{ + if (UDT_STREAM == m_iSockType) + throw CUDTException(5, 9, 0); + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (len <= 0) + return 0; + + CGuard recvguard(m_RecvLock); + + if (m_bBroken || m_bClosing) + { + int res = m_pRcvBuffer->readMsg(data, len); + + if (m_pRcvBuffer->getRcvMsgNum() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if (0 == res) + throw CUDTException(2, 1, 0); + else + return res; + } + + if (!m_bSynRecving) + { + int res = m_pRcvBuffer->readMsg(data, len); + if (0 == res) + throw CUDTException(6, 2, 0); + else + return res; + } + + int res = 0; + bool timeout = false; + + do + { + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == (res = m_pRcvBuffer->readMsg(data, len)))) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + if (pthread_cond_timedwait(&m_RecvDataCond, &m_RecvDataLock, &locktime) == ETIMEDOUT) + timeout = true; + + res = m_pRcvBuffer->readMsg(data, len); + } + pthread_mutex_unlock(&m_RecvDataLock); + #else + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == (res = m_pRcvBuffer->readMsg(data, len)))) + WaitForSingleObject(m_RecvDataCond, INFINITE); + } + else + { + if (WaitForSingleObject(m_RecvDataCond, DWORD(m_iRcvTimeOut)) == WAIT_TIMEOUT) + timeout = true; + + res = m_pRcvBuffer->readMsg(data, len); + } + #endif + + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + } while ((0 == res) && !timeout); + + if (m_pRcvBuffer->getRcvMsgNum() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(6, 3, 0); + + return res; +} + +int64_t CUDT::sendfile(fstream& ifs, int64_t& offset, int64_t size, int block) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (size <= 0) + return 0; + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + } + + int64_t tosend = size; + int unitsize; + + // positioning... + try + { + ifs.seekg((streamoff)offset); + } + catch (...) + { + throw CUDTException(4, 1); + } + + // sending block by block + while (tosend > 0) + { + if (ifs.fail()) + throw CUDTException(4, 4); + + if (ifs.eof()) + break; + + unitsize = int((tosend >= block) ? block : tosend); + + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + pthread_mutex_unlock(&m_SendBlockLock); + #else + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + WaitForSingleObject(m_SendBlockCond, INFINITE); + #endif + + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if (!m_bPeerHealth) + { + // reset peer health status, once this error returns, the app should handle the situation at the peer side + m_bPeerHealth = true; + throw CUDTException(7); + } + + // record total time used for sending + if (0 == m_pSndBuffer->getCurrBufSize()) + m_llSndDurationCounter = CTimer::getTime(); + + int64_t sentsize = m_pSndBuffer->addBufferFromFile(ifs, unitsize); + + if (sentsize > 0) + { + tosend -= sentsize; + offset += sentsize; + } + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + } + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return size - tosend; +} + +int64_t CUDT::recvfile(fstream& ofs, int64_t& offset, int64_t size, int block) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + if (size <= 0) + return 0; + + CGuard recvguard(m_RecvLock); + + int64_t torecv = size; + int unitsize = block; + int recvsize; + + // positioning... + try + { + ofs.seekp((streamoff)offset); + } + catch (...) + { + throw CUDTException(4, 3); + } + + // receiving... "recvfile" is always blocking + while (torecv > 0) + { + if (ofs.fail()) + { + // send the sender a signal so it will not be blocked forever + int32_t err_code = CUDTException::EFILE; + sendCtrl(8, &err_code); + + throw CUDTException(4, 4); + } + + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + pthread_mutex_unlock(&m_RecvDataLock); + #else + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + WaitForSingleObject(m_RecvDataCond, INFINITE); + #endif + + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + unitsize = int((torecv >= block) ? block : torecv); + recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize); + + if (recvsize > 0) + { + torecv -= recvsize; + offset += recvsize; + } + } + + if (m_pRcvBuffer->getRcvDataSize() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + return size - torecv; +} + +void CUDT::sample(CPerfMon* perf, bool clear) +{ + if (!m_bConnected) + throw CUDTException(2, 2, 0); + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + + uint64_t currtime = CTimer::getTime(); + perf->msTimeStamp = (currtime - m_StartTime) / 1000; + + perf->pktSent = m_llTraceSent; + perf->pktRecv = m_llTraceRecv; + perf->pktSndLoss = m_iTraceSndLoss; + perf->pktRcvLoss = m_iTraceRcvLoss; + perf->pktRetrans = m_iTraceRetrans; + perf->pktSentACK = m_iSentACK; + perf->pktRecvACK = m_iRecvACK; + perf->pktSentNAK = m_iSentNAK; + perf->pktRecvNAK = m_iRecvNAK; + perf->usSndDuration = m_llSndDuration; + + perf->pktSentTotal = m_llSentTotal; + perf->pktRecvTotal = m_llRecvTotal; + perf->pktSndLossTotal = m_iSndLossTotal; + perf->pktRcvLossTotal = m_iRcvLossTotal; + perf->pktRetransTotal = m_iRetransTotal; + perf->pktSentACKTotal = m_iSentACKTotal; + perf->pktRecvACKTotal = m_iRecvACKTotal; + perf->pktSentNAKTotal = m_iSentNAKTotal; + perf->pktRecvNAKTotal = m_iRecvNAKTotal; + perf->usSndDurationTotal = m_llSndDurationTotal; + + double interval = double(currtime - m_LastSampleTime); + + perf->mbpsSendRate = double(m_llTraceSent) * m_iPayloadSize * 8.0 / interval; + perf->mbpsRecvRate = double(m_llTraceRecv) * m_iPayloadSize * 8.0 / interval; + + perf->usPktSndPeriod = m_ullInterval / double(m_ullCPUFrequency); + perf->pktFlowWindow = m_iFlowWindowSize; + perf->pktCongestionWindow = (int)m_dCongestionWindow; + perf->pktFlightSize = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) - 1; + perf->msRTT = m_iRTT/1000.0; + perf->mbpsBandwidth = m_iBandwidth * m_iPayloadSize * 8.0 / 1000000.0; + + #ifndef WIN32 + if (0 == pthread_mutex_trylock(&m_ConnectionLock)) + #else + if (WAIT_OBJECT_0 == WaitForSingleObject(m_ConnectionLock, 0)) + #endif + { + perf->byteAvailSndBuf = (NULL == m_pSndBuffer) ? 0 : (m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iMSS; + perf->byteAvailRcvBuf = (NULL == m_pRcvBuffer) ? 0 : m_pRcvBuffer->getAvailBufSize() * m_iMSS; + + #ifndef WIN32 + pthread_mutex_unlock(&m_ConnectionLock); + #else + ReleaseMutex(m_ConnectionLock); + #endif + } + else + { + perf->byteAvailSndBuf = 0; + perf->byteAvailRcvBuf = 0; + } + + if (clear) + { + m_llTraceSent = m_llTraceRecv = m_iTraceSndLoss = m_iTraceRcvLoss = m_iTraceRetrans = m_iSentACK = m_iRecvACK = m_iSentNAK = m_iRecvNAK = 0; + m_llSndDuration = 0; + m_LastSampleTime = currtime; + } +} + +void CUDT::CCUpdate() +{ + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + if (m_llMaxBW <= 0) + return; + const double minSP = 1000000.0 / (double(m_llMaxBW) / m_iMSS) * m_ullCPUFrequency; + if (m_ullInterval < minSP) + m_ullInterval = minSP; +} + +void CUDT::initSynch() +{ + #ifndef WIN32 + pthread_mutex_init(&m_SendBlockLock, NULL); + pthread_cond_init(&m_SendBlockCond, NULL); + pthread_mutex_init(&m_RecvDataLock, NULL); + pthread_cond_init(&m_RecvDataCond, NULL); + pthread_mutex_init(&m_SendLock, NULL); + pthread_mutex_init(&m_RecvLock, NULL); + pthread_mutex_init(&m_AckLock, NULL); + pthread_mutex_init(&m_ConnectionLock, NULL); + #else + m_SendBlockLock = CreateMutex(NULL, false, NULL); + m_SendBlockCond = CreateEvent(NULL, false, false, NULL); + m_RecvDataLock = CreateMutex(NULL, false, NULL); + m_RecvDataCond = CreateEvent(NULL, false, false, NULL); + m_SendLock = CreateMutex(NULL, false, NULL); + m_RecvLock = CreateMutex(NULL, false, NULL); + m_AckLock = CreateMutex(NULL, false, NULL); + m_ConnectionLock = CreateMutex(NULL, false, NULL); + #endif +} + +void CUDT::destroySynch() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_SendBlockLock); + pthread_cond_destroy(&m_SendBlockCond); + pthread_mutex_destroy(&m_RecvDataLock); + pthread_cond_destroy(&m_RecvDataCond); + pthread_mutex_destroy(&m_SendLock); + pthread_mutex_destroy(&m_RecvLock); + pthread_mutex_destroy(&m_AckLock); + pthread_mutex_destroy(&m_ConnectionLock); + #else + CloseHandle(m_SendBlockLock); + CloseHandle(m_SendBlockCond); + CloseHandle(m_RecvDataLock); + CloseHandle(m_RecvDataCond); + CloseHandle(m_SendLock); + CloseHandle(m_RecvLock); + CloseHandle(m_AckLock); + CloseHandle(m_ConnectionLock); + #endif +} + +void CUDT::releaseSynch() +{ + #ifndef WIN32 + // wake up user calls + pthread_mutex_lock(&m_SendBlockLock); + pthread_cond_signal(&m_SendBlockCond); + pthread_mutex_unlock(&m_SendBlockLock); + + pthread_mutex_lock(&m_SendLock); + pthread_mutex_unlock(&m_SendLock); + + pthread_mutex_lock(&m_RecvDataLock); + pthread_cond_signal(&m_RecvDataCond); + pthread_mutex_unlock(&m_RecvDataLock); + + pthread_mutex_lock(&m_RecvLock); + pthread_mutex_unlock(&m_RecvLock); + #else + SetEvent(m_SendBlockCond); + WaitForSingleObject(m_SendLock, INFINITE); + ReleaseMutex(m_SendLock); + SetEvent(m_RecvDataCond); + WaitForSingleObject(m_RecvLock, INFINITE); + ReleaseMutex(m_RecvLock); + #endif +} + +void CUDT::sendCtrl(int pkttype, void* lparam, void* rparam, int size) +{ + CPacket ctrlpkt; + + switch (pkttype) + { + case 2: //010 - Acknowledgement + { + int32_t ack; + + // If there is no loss, the ACK is the current largest sequence number plus 1; + // Otherwise it is the smallest sequence number in the receiver loss list. + if (0 == m_pRcvLossList->getLossLength()) + ack = CSeqNo::incseq(m_iRcvCurrSeqNo); + else + ack = m_pRcvLossList->getFirstLostSeq(); + + if (ack == m_iRcvLastAckAck) + break; + + // send out a lite ACK + // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number + if (4 == size) + { + ctrlpkt.pack(pkttype, NULL, &ack, size); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + } + + uint64_t currtime; + CTimer::rdtsc(currtime); + + // There are new received packets to acknowledge, update related information. + if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) + { + int acksize = CSeqNo::seqoff(m_iRcvLastAck, ack); + + m_iRcvLastAck = ack; + + m_pRcvBuffer->ackData(acksize); + + // signal a waiting "recv" call if there is any data available + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + if (m_bSynRecving) + pthread_cond_signal(&m_RecvDataCond); + pthread_mutex_unlock(&m_RecvDataLock); + #else + if (m_bSynRecving) + SetEvent(m_RecvDataCond); + #endif + + // acknowledge any waiting epolls to read + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); + } + else if (ack == m_iRcvLastAck) + { + if ((currtime - m_ullLastAckTime) < ((m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency)) + break; + } + else + break; + + // Send out the ACK only if has not been received by the sender before + if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0) + { + int32_t data[6]; + + m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo); + data[0] = m_iRcvLastAck; + data[1] = m_iRTT; + data[2] = m_iRTTVar; + data[3] = m_pRcvBuffer->getAvailBufSize(); + // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock + if (data[3] < 2) + data[3] = 2; + + if (currtime - m_ullLastAckTime > m_ullSYNInt) + { + data[4] = m_pRcvTimeWindow->getPktRcvSpeed(); + data[5] = m_pRcvTimeWindow->getBandwidth(); + ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, 24); + + CTimer::rdtsc(m_ullLastAckTime); + } + else + { + ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, 16); + } + + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + m_pACKWindow->store(m_iAckSeqNo, m_iRcvLastAck); + + ++ m_iSentACK; + ++ m_iSentACKTotal; + } + + break; + } + + case 6: //110 - Acknowledgement of Acknowledgement + ctrlpkt.pack(pkttype, lparam); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 3: //011 - Loss Report + { + if (NULL != rparam) + { + if (1 == size) + { + // only 1 loss packet + ctrlpkt.pack(pkttype, NULL, (int32_t *)rparam + 1, 4); + } + else + { + // more than 1 loss packets + ctrlpkt.pack(pkttype, NULL, rparam, 8); + } + + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + ++ m_iSentNAK; + ++ m_iSentNAKTotal; + } + else if (m_pRcvLossList->getLossLength() > 0) + { + // this is periodically NAK report; make sure NAK cannot be sent back too often + + // read loss list from the local receiver loss list + int32_t* data = new int32_t[m_iPayloadSize / 4]; + int losslen; + m_pRcvLossList->getLossArray(data, losslen, m_iPayloadSize / 4); + + if (0 < losslen) + { + ctrlpkt.pack(pkttype, NULL, data, losslen * 4); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + ++ m_iSentNAK; + ++ m_iSentNAKTotal; + } + + delete [] data; + } + + // update next NAK time, which should wait enough time for the retansmission, but not too long + m_ullNAKInt = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency; + int rcv_speed = m_pRcvTimeWindow->getPktRcvSpeed(); + if (rcv_speed > 0) + m_ullNAKInt += (m_pRcvLossList->getLossLength() * 1000000ULL / rcv_speed) * m_ullCPUFrequency; + if (m_ullNAKInt < m_ullMinNakInt) + m_ullNAKInt = m_ullMinNakInt; + + break; + } + + case 4: //100 - Congestion Warning + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + CTimer::rdtsc(m_ullLastWarningTime); + + break; + + case 1: //001 - Keep-alive + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 0: //000 - Handshake + ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 5: //101 - Shutdown + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 7: //111 - Msg drop request + ctrlpkt.pack(pkttype, lparam, rparam, 8); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 8: //1000 - acknowledge the peer side a special error + ctrlpkt.pack(pkttype, lparam); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 32767: //0x7FFF - Resevered for future use + break; + + default: + break; + } +} + +void CUDT::processCtrl(CPacket& ctrlpkt) +{ + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + + switch (ctrlpkt.getType()) + { + case 2: //010 - Acknowledgement + { + int32_t ack; + + // process a lite ACK + if (4 == ctrlpkt.getLength()) + { + ack = *(int32_t *)ctrlpkt.m_pcData; + if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) + { + m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ack); + m_iSndLastAck = ack; + } + + break; + } + + // read ACK seq. no. + ack = ctrlpkt.getAckSeqNo(); + + // send ACK acknowledgement + // number of ACK2 can be much less than number of ACK + uint64_t now = CTimer::getTime(); + if ((currtime - m_ullSndLastAck2Time > (uint64_t)m_iSYNInterval) || (ack == m_iSndLastAck2)) + { + sendCtrl(6, &ack); + m_iSndLastAck2 = ack; + m_ullSndLastAck2Time = now; + } + + // Got data ACK + ack = *(int32_t *)ctrlpkt.m_pcData; + + // check the validation of the ack + if (CSeqNo::seqcmp(ack, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0) + { + //this should not happen: attack or bug + m_bBroken = true; + m_iBrokenCounter = 0; + break; + } + + if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) + { + // Update Flow Window Size, must update before and together with m_iSndLastAck + m_iFlowWindowSize = *((int32_t *)ctrlpkt.m_pcData + 3); + m_iSndLastAck = ack; + } + + // protect packet retransmission + CGuard::enterCS(m_AckLock); + + int offset = CSeqNo::seqoff(m_iSndLastDataAck, ack); + if (offset <= 0) + { + // discard it if it is a repeated ACK + CGuard::leaveCS(m_AckLock); + break; + } + + // acknowledge the sending buffer + m_pSndBuffer->ackData(offset); + + // record total time used for sending + m_llSndDuration += currtime - m_llSndDurationCounter; + m_llSndDurationTotal += currtime - m_llSndDurationCounter; + m_llSndDurationCounter = currtime; + + // update sending variables + m_iSndLastDataAck = ack; + m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck)); + + CGuard::leaveCS(m_AckLock); + + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + if (m_bSynSending) + pthread_cond_signal(&m_SendBlockCond); + pthread_mutex_unlock(&m_SendBlockLock); + #else + if (m_bSynSending) + SetEvent(m_SendBlockCond); + #endif + + // acknowledde any waiting epolls to write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + + // Update RTT + //m_iRTT = *((int32_t *)ctrlpkt.m_pcData + 1); + //m_iRTTVar = *((int32_t *)ctrlpkt.m_pcData + 2); + int rtt = *((int32_t *)ctrlpkt.m_pcData + 1); + m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; + m_iRTT = (m_iRTT * 7 + rtt) >> 3; + + m_pCC->setRTT(m_iRTT); + + if (ctrlpkt.getLength() > 16) + { + // Update Estimated Bandwidth and packet delivery rate + if (*((int32_t *)ctrlpkt.m_pcData + 4) > 0) + m_iDeliveryRate = (m_iDeliveryRate * 7 + *((int32_t *)ctrlpkt.m_pcData + 4)) >> 3; + + if (*((int32_t *)ctrlpkt.m_pcData + 5) > 0) + m_iBandwidth = (m_iBandwidth * 7 + *((int32_t *)ctrlpkt.m_pcData + 5)) >> 3; + + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setBandwidth(m_iBandwidth); + } + + m_pCC->onACK(ack); + CCUpdate(); + + ++ m_iRecvACK; + ++ m_iRecvACKTotal; + + break; + } + + case 6: //110 - Acknowledgement of Acknowledgement + { + int32_t ack; + int rtt = -1; + + // update RTT + rtt = m_pACKWindow->acknowledge(ctrlpkt.getAckSeqNo(), ack); + if (rtt <= 0) + break; + + //if increasing delay detected... + // sendCtrl(4); + + // RTT EWMA + m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; + m_iRTT = (m_iRTT * 7 + rtt) >> 3; + + m_pCC->setRTT(m_iRTT); + + // update last ACK that has been received by the sender + if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) + m_iRcvLastAckAck = ack; + + break; + } + + case 3: //011 - Loss Report + { + int32_t* losslist = (int32_t *)(ctrlpkt.m_pcData); + + m_pCC->onLoss(losslist, ctrlpkt.getLength() / 4); + CCUpdate(); + + bool secure = true; + + // decode loss list message and insert loss into the sender loss list + for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++ i) + { + if (0 != (losslist[i] & 0x80000000)) + { + if ((CSeqNo::seqcmp(losslist[i] & 0x7FFFFFFF, losslist[i + 1]) > 0) || (CSeqNo::seqcmp(losslist[i + 1], m_iSndCurrSeqNo) > 0)) + { + // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq + secure = false; + break; + } + + int num = 0; + if (CSeqNo::seqcmp(losslist[i] & 0x7FFFFFFF, m_iSndLastAck) >= 0) + num = m_pSndLossList->insert(losslist[i] & 0x7FFFFFFF, losslist[i + 1]); + else if (CSeqNo::seqcmp(losslist[i + 1], m_iSndLastAck) >= 0) + num = m_pSndLossList->insert(m_iSndLastAck, losslist[i + 1]); + + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + + ++ i; + } + else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) + { + if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) + { + //seq_a must not be greater than the most recent sent seq + secure = false; + break; + } + + int num = m_pSndLossList->insert(losslist[i], losslist[i]); + + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + } + } + + if (!secure) + { + //this should not happen: attack or bug + m_bBroken = true; + m_iBrokenCounter = 0; + break; + } + + // the lost packet (retransmission) should be sent out immediately + m_pSndQueue->m_pSndUList->update(this); + + ++ m_iRecvNAK; + ++ m_iRecvNAKTotal; + + break; + } + + case 4: //100 - Delay Warning + // One way packet delay is increasing, so decrease the sending rate + m_ullInterval = (uint64_t)ceil(m_ullInterval * 1.125); + m_iLastDecSeq = m_iSndCurrSeqNo; + + break; + + case 1: //001 - Keep-alive + // The only purpose of keep-alive packet is to tell that the peer is still alive + // nothing needs to be done. + + break; + + case 0: //000 - Handshake + { + CHandShake req; + req.deserialize(ctrlpkt.m_pcData, ctrlpkt.getLength()); + if ((req.m_iReqType > 0) || (m_bRendezvous && (req.m_iReqType != -2))) + { + // The peer side has not received the handshake message, so it keeps querying + // resend the handshake packet + + CHandShake initdata; + initdata.m_iISN = m_iISN; + initdata.m_iMSS = m_iMSS; + initdata.m_iFlightFlagSize = m_iFlightFlagSize; + initdata.m_iReqType = (!m_bRendezvous) ? -1 : -2; + initdata.m_iID = m_SocketID; + + char* hs = new char [m_iPayloadSize]; + int hs_size = m_iPayloadSize; + initdata.serialize(hs, hs_size); + sendCtrl(0, NULL, hs, hs_size); + delete [] hs; + } + + break; + } + + case 5: //101 - Shutdown + m_bShutdown = true; + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 60; + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + + CTimer::triggerEvent(); + + break; + + case 7: //111 - Msg drop request + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq()); + m_pRcvLossList->remove(*(int32_t*)ctrlpkt.m_pcData, *(int32_t*)(ctrlpkt.m_pcData + 4)); + + // move forward with current recv seq no. + if ((CSeqNo::seqcmp(*(int32_t*)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) + && (CSeqNo::seqcmp(*(int32_t*)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0)) + { + m_iRcvCurrSeqNo = *(int32_t*)(ctrlpkt.m_pcData + 4); + } + + break; + + case 8: // 1000 - An error has happened to the peer side + //int err_type = packet.getAddInfo(); + + // currently only this error is signalled from the peer side + // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately + // giving the app a chance to fix the issue + + m_bPeerHealth = false; + + break; + + case 32767: //0x7FFF - reserved and user defined messages + m_pCC->processCustomMsg(&ctrlpkt); + CCUpdate(); + + break; + + default: + break; + } +} + +int CUDT::packData(CPacket& packet, uint64_t& ts) +{ + int payload = 0; + bool probe = false; + + uint64_t entertime; + CTimer::rdtsc(entertime); + + if ((0 != m_ullTargetTime) && (entertime > m_ullTargetTime)) + m_ullTimeDiff += entertime - m_ullTargetTime; + + // Loss retransmission always has higher priority. + if ((packet.m_iSeqNo = m_pSndLossList->getLostSeq()) >= 0) + { + // protect m_iSndLastDataAck from updating by ACK processing + CGuard ackguard(m_AckLock); + + int offset = CSeqNo::seqoff(m_iSndLastDataAck, packet.m_iSeqNo); + if (offset < 0) + return 0; + + int msglen; + + payload = m_pSndBuffer->readData(&(packet.m_pcData), offset, packet.m_iMsgNo, msglen); + + if (-1 == payload) + { + int32_t seqpair[2]; + seqpair[0] = packet.m_iSeqNo; + seqpair[1] = CSeqNo::incseq(seqpair[0], msglen); + sendCtrl(7, &packet.m_iMsgNo, seqpair, 8); + + // only one msg drop request is necessary + m_pSndLossList->remove(seqpair[1]); + + // skip all dropped packets + if (CSeqNo::seqcmp(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])) < 0) + m_iSndCurrSeqNo = CSeqNo::incseq(seqpair[1]); + + return 0; + } + else if (0 == payload) + return 0; + + ++ m_iTraceRetrans; + ++ m_iRetransTotal; + } + else + { + // If no loss, pack a new packet. + + // check congestion/flow window limit + int cwnd = (m_iFlowWindowSize < (int)m_dCongestionWindow) ? m_iFlowWindowSize : (int)m_dCongestionWindow; + if (cwnd >= CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo))) + { + if (0 != (payload = m_pSndBuffer->readData(&(packet.m_pcData), packet.m_iMsgNo))) + { + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); + m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); + + packet.m_iSeqNo = m_iSndCurrSeqNo; + + // every 16 (0xF) packets, a packet pair is sent + if (0 == (packet.m_iSeqNo & 0xF)) + probe = true; + } + else + { + m_ullTargetTime = 0; + m_ullTimeDiff = 0; + ts = 0; + return 0; + } + } + else + { + m_ullTargetTime = 0; + m_ullTimeDiff = 0; + ts = 0; + return 0; + } + } + + packet.m_iTimeStamp = int(CTimer::getTime() - m_StartTime); + packet.m_iID = m_PeerID; + packet.setLength(payload); + + m_pCC->onPktSent(&packet); + //m_pSndTimeWindow->onPktSent(packet.m_iTimeStamp); + + ++ m_llTraceSent; + ++ m_llSentTotal; + + if (probe) + { + // sends out probing packet pair + ts = entertime; + probe = false; + } + else + { + #ifndef NO_BUSY_WAITING + ts = entertime + m_ullInterval; + #else + if (m_ullTimeDiff >= m_ullInterval) + { + ts = entertime; + m_ullTimeDiff -= m_ullInterval; + } + else + { + ts = entertime + m_ullInterval - m_ullTimeDiff; + m_ullTimeDiff = 0; + } + #endif + } + + m_ullTargetTime = ts; + + return payload; +} + +int CUDT::processData(CUnit* unit) +{ + CPacket& packet = unit->m_Packet; + + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + + m_pCC->onPktReceived(&packet); + ++ m_iPktCount; + // update time information + m_pRcvTimeWindow->onPktArrival(); + + // check if it is probing packet pair + if (0 == (packet.m_iSeqNo & 0xF)) + m_pRcvTimeWindow->probe1Arrival(); + else if (1 == (packet.m_iSeqNo & 0xF)) + m_pRcvTimeWindow->probe2Arrival(); + + ++ m_llTraceRecv; + ++ m_llRecvTotal; + + int32_t offset = CSeqNo::seqoff(m_iRcvLastAck, packet.m_iSeqNo); + if ((offset < 0) || (offset >= m_pRcvBuffer->getAvailBufSize())) + return -1; + + if (m_pRcvBuffer->addData(unit, offset) < 0) + return -1; + + // Loss detection. + if (CSeqNo::seqcmp(packet.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) + { + // If loss found, insert them to the receiver loss list + m_pRcvLossList->insert(CSeqNo::incseq(m_iRcvCurrSeqNo), CSeqNo::decseq(packet.m_iSeqNo)); + + // pack loss list for NAK + int32_t lossdata[2]; + lossdata[0] = CSeqNo::incseq(m_iRcvCurrSeqNo) | 0x80000000; + lossdata[1] = CSeqNo::decseq(packet.m_iSeqNo); + + // Generate loss report immediately. + sendCtrl(3, NULL, lossdata, (CSeqNo::incseq(m_iRcvCurrSeqNo) == CSeqNo::decseq(packet.m_iSeqNo)) ? 1 : 2); + + int loss = CSeqNo::seqlen(m_iRcvCurrSeqNo, packet.m_iSeqNo) - 2; + m_iTraceRcvLoss += loss; + m_iRcvLossTotal += loss; + } + + // This is not a regular fixed size packet... + //an irregular sized packet usually indicates the end of a message, so send an ACK immediately + if (packet.getLength() != m_iPayloadSize) + CTimer::rdtsc(m_ullNextACKTime); + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + if (CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + m_iRcvCurrSeqNo = packet.m_iSeqNo; + else + m_pRcvLossList->remove(packet.m_iSeqNo); + + return 0; +} + +int CUDT::listen(sockaddr* addr, CPacket& packet) +{ + if (m_bClosing) + return 1002; + + if (packet.getLength() != CHandShake::m_iContentSize) + return 1004; + + CHandShake hs; + hs.deserialize(packet.m_pcData, packet.getLength()); + + // SYN cookie + char clienthost[NI_MAXHOST]; + char clientport[NI_MAXSERV]; + getnameinfo(addr, (AF_INET == m_iVersion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), clienthost, sizeof(clienthost), clientport, sizeof(clientport), NI_NUMERICHOST|NI_NUMERICSERV); + int64_t timestamp = (CTimer::getTime() - m_StartTime) / 60000000; // secret changes every one minute + stringstream cookiestr; + cookiestr << clienthost << ":" << clientport << ":" << timestamp; + unsigned char cookie[16]; + CMD5::compute(cookiestr.str().c_str(), cookie); + + if (1 == hs.m_iReqType) + { + hs.m_iCookie = *(int*)cookie; + packet.m_iID = hs.m_iID; + int size = packet.getLength(); + hs.serialize(packet.m_pcData, size); + m_pSndQueue->sendto(addr, packet); + return 0; + } + else + { + if (hs.m_iCookie != *(int*)cookie) + { + timestamp --; + cookiestr << clienthost << ":" << clientport << ":" << timestamp; + CMD5::compute(cookiestr.str().c_str(), cookie); + + if (hs.m_iCookie != *(int*)cookie) + return -1; + } + } + + int32_t id = hs.m_iID; + + // When a peer side connects in... + if ((1 == packet.getFlag()) && (0 == packet.getType())) + { + if ((hs.m_iVersion != m_iVersion) || (hs.m_iType != m_iSockType)) + { + // mismatch, reject the request + hs.m_iReqType = 1002; + int size = CHandShake::m_iContentSize; + hs.serialize(packet.m_pcData, size); + packet.m_iID = id; + m_pSndQueue->sendto(addr, packet); + } + else + { + int result = s_UDTUnited.newConnection(m_SocketID, addr, &hs); + if (result == -1) + hs.m_iReqType = 1002; + + // send back a response if connection failed or connection already existed + // new connection response should be sent in connect() + if (result != 1) + { + int size = CHandShake::m_iContentSize; + hs.serialize(packet.m_pcData, size); + packet.m_iID = id; + m_pSndQueue->sendto(addr, packet); + } + else + { + // a new connection has been created, enable epoll for write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } + } + } + + return hs.m_iReqType; +} + +void CUDT::checkTimers() +{ + // update CC parameters + CCUpdate(); + //uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9); + //if (m_ullInterval < minint) + // m_ullInterval = minint; + + uint64_t currtime; + CTimer::rdtsc(currtime); + + if ((currtime > m_ullNextACKTime) || ((m_pCC->m_iACKInterval > 0) && (m_pCC->m_iACKInterval <= m_iPktCount))) + { + // ACK timer expired or ACK interval is reached + + sendCtrl(2); + CTimer::rdtsc(currtime); + if (m_pCC->m_iACKPeriod > 0) + m_ullNextACKTime = currtime + m_pCC->m_iACKPeriod * m_ullCPUFrequency; + else + m_ullNextACKTime = currtime + m_ullACKInt; + + m_iPktCount = 0; + m_iLightACKCount = 1; + } + else if (m_iSelfClockInterval * m_iLightACKCount <= m_iPktCount) + { + //send a "light" ACK + sendCtrl(2, NULL, NULL, 4); + ++ m_iLightACKCount; + } + + // we are not sending back repeated NAK anymore and rely on the sender's EXP for retransmission + //if ((m_pRcvLossList->getLossLength() > 0) && (currtime > m_ullNextNAKTime)) + //{ + // // NAK timer expired, and there is loss to be reported. + // sendCtrl(3); + // + // CTimer::rdtsc(currtime); + // m_ullNextNAKTime = currtime + m_ullNAKInt; + //} + + uint64_t next_exp_time; + if (m_pCC->m_bUserDefinedRTO) + next_exp_time = m_ullLastRspTime + m_pCC->m_iRTO * m_ullCPUFrequency; + else + { + uint64_t exp_int = (m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + m_iSYNInterval) * m_ullCPUFrequency; + if (exp_int < m_iEXPCount * m_ullMinExpInt) + exp_int = m_iEXPCount * m_ullMinExpInt; + next_exp_time = m_ullLastRspTime + exp_int; + } + + if (currtime > next_exp_time) + { + // Haven't receive any information from the peer, is it dead?! + // timeout: at least 16 expirations and must be greater than 10 seconds + if ((m_iEXPCount > 16) && (currtime - m_ullLastRspTime > 5000000 * m_ullCPUFrequency)) + { + // + // Connection is broken. + // UDT does not signal any information about this instead of to stop quietly. + // Application will detect this when it calls any UDT methods next time. + // + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 30; + + // update snd U list to remove this socket + m_pSndQueue->m_pSndUList->update(this); + + releaseSynch(); + + // app can call any UDT API to learn the connection_broken error + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true); + + CTimer::triggerEvent(); + + return; + } + + // sender: Insert all the packets sent after last received acknowledgement into the sender loss list. + // recver: Send out a keep-alive packet + if (m_pSndBuffer->getCurrBufSize() > 0) + { + if ((CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && (m_pSndLossList->getLossLength() == 0)) + { + // resend all unacknowledged packets on timeout, but only if there is no packet in the loss list + int32_t csn = m_iSndCurrSeqNo; + int num = m_pSndLossList->insert(m_iSndLastAck, csn); + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + } + + m_pCC->onTimeout(); + CCUpdate(); + + // immediately restart transmission + m_pSndQueue->m_pSndUList->update(this); + } + else + { + sendCtrl(1); + } + + ++ m_iEXPCount; + // Reset last response time since we just sent a heart-beat. + m_ullLastRspTime = currtime; + } +} + +void CUDT::addEPoll(const int eid) +{ + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.insert(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + + if (!m_bConnected || m_bBroken || m_bClosing) + return; + + if (((UDT_STREAM == m_iSockType) && (m_pRcvBuffer->getRcvDataSize() > 0)) || + ((UDT_DGRAM == m_iSockType) && (m_pRcvBuffer->getRcvMsgNum() > 0))) + { + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); + } + if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) + { + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } +} + +void CUDT::removeEPoll(const int eid) +{ + // clear IO events notifications; + // since this happens after the epoll ID has been removed, they cannot be set again + set remove; + remove.insert(eid); + s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, UDT_EPOLL_IN | UDT_EPOLL_OUT, false); + + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.erase(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); +} diff --git a/vendor/udt4/src/core.h b/vendor/udt4/src/core.h new file mode 100644 index 000000000..47caa7900 --- /dev/null +++ b/vendor/udt4/src/core.h @@ -0,0 +1,458 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/28/2012 +*****************************************************************************/ + +#ifndef __UDT_CORE_H__ +#define __UDT_CORE_H__ + + +#include "udt.h" +#include "common.h" +#include "list.h" +#include "buffer.h" +#include "window.h" +#include "packet.h" +#include "channel.h" +#include "api.h" +#include "ccc.h" +#include "cache.h" +#include "queue.h" + +enum UDTSockType {UDT_STREAM = 1, UDT_DGRAM}; + +class CUDT +{ +friend class CUDTSocket; +friend class CUDTUnited; +friend class CCC; +friend struct CUDTComp; +friend class CCache; +friend class CRendezvousQueue; +friend class CSndQueue; +friend class CRcvQueue; +friend class CSndUList; +friend class CRcvUList; + +private: // constructor and desctructor + CUDT(); + CUDT(const CUDT& ancestor); + const CUDT& operator=(const CUDT&) {return *this;} + ~CUDT(); + +public: //API + static int startup(); + static int cleanup(); + static UDTSOCKET socket(int af, int type = SOCK_STREAM, int protocol = 0); + static int bind(UDTSOCKET u, const sockaddr* name, int namelen); + static int bind(UDTSOCKET u, UDPSOCKET udpsock); + static int listen(UDTSOCKET u, int backlog); + static UDTSOCKET accept(UDTSOCKET u, sockaddr* addr, int* addrlen); + static int connect(UDTSOCKET u, const sockaddr* name, int namelen); + static int close(UDTSOCKET u); + static int getpeername(UDTSOCKET u, sockaddr* name, int* namelen); + static int getsockname(UDTSOCKET u, sockaddr* name, int* namelen); + static int getsockopt(UDTSOCKET u, int level, UDTOpt optname, void* optval, int* optlen); + static int setsockopt(UDTSOCKET u, int level, UDTOpt optname, const void* optval, int optlen); + static int send(UDTSOCKET u, const char* buf, int len, int flags); + static int recv(UDTSOCKET u, char* buf, int len, int flags); + static int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false); + static int recvmsg(UDTSOCKET u, char* buf, int len); + static int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); + static int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); + static int select(int nfds, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); + static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + static int epoll_create(); + static int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); + static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static int epoll_remove_usock(const int eid, const UDTSOCKET u); + static int epoll_remove_ssock(const int eid, const SYSSOCKET s); + static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); + static int epoll_release(const int eid); + static CUDTException& getlasterror(); + static int perfmon(UDTSOCKET u, CPerfMon* perf, bool clear = true); + static UDTSTATUS getsockstate(UDTSOCKET u); + +public: // internal API + static CUDT* getUDTHandle(UDTSOCKET u); + +private: + // Functionality: + // initialize a UDT entity and bind to a local address. + // Parameters: + // None. + // Returned value: + // None. + + void open(); + + // Functionality: + // Start listening to any connection request. + // Parameters: + // None. + // Returned value: + // None. + + void listen(); + + // Functionality: + // Connect to a UDT entity listening at address "peer". + // Parameters: + // 0) [in] peer: The address of the listening UDT entity. + // Returned value: + // None. + + void connect(const sockaddr* peer); + + // Functionality: + // Process the response handshake packet. + // Parameters: + // 0) [in] pkt: handshake packet. + // Returned value: + // Return 0 if connected, positive value if connection is in progress, otherwise error code. + + int connect(const CPacket& pkt) throw (); + + // Functionality: + // Connect to a UDT entity listening at address "peer", which has sent "hs" request. + // Parameters: + // 0) [in] peer: The address of the listening UDT entity. + // 1) [in/out] hs: The handshake information sent by the peer side (in), negotiated value (out). + // Returned value: + // None. + + void connect(const sockaddr* peer, CHandShake* hs); + + // Functionality: + // Close the opened UDT entity. + // Parameters: + // None. + // Returned value: + // None. + + void close(); + + // Functionality: + // Request UDT to send out a data block "data" with size of "len". + // Parameters: + // 0) [in] data: The address of the application data to be sent. + // 1) [in] len: The size of the data block. + // Returned value: + // Actual size of data sent. + + int send(const char* data, int len); + + // Functionality: + // Request UDT to receive data to a memory block "data" with size of "len". + // Parameters: + // 0) [out] data: data received. + // 1) [in] len: The desired size of data to be received. + // Returned value: + // Actual size of data received. + + int recv(char* data, int len); + + // Functionality: + // send a message of a memory block "data" with size of "len". + // Parameters: + // 0) [out] data: data received. + // 1) [in] len: The desired size of data to be received. + // 2) [in] ttl: the time-to-live of the message. + // 3) [in] inorder: if the message should be delivered in order. + // Returned value: + // Actual size of data sent. + + int sendmsg(const char* data, int len, int ttl, bool inorder); + + // Functionality: + // Receive a message to buffer "data". + // Parameters: + // 0) [out] data: data received. + // 1) [in] len: size of the buffer. + // Returned value: + // Actual size of data received. + + int recvmsg(char* data, int len); + + // Functionality: + // Request UDT to send out a file described as "fd", starting from "offset", with size of "size". + // Parameters: + // 0) [in] ifs: The input file stream. + // 1) [in, out] offset: From where to read and send data; output is the new offset when the call returns. + // 2) [in] size: How many data to be sent. + // 3) [in] block: size of block per read from disk + // Returned value: + // Actual size of data sent. + + int64_t sendfile(std::fstream& ifs, int64_t& offset, int64_t size, int block = 366000); + + // Functionality: + // Request UDT to receive data into a file described as "fd", starting from "offset", with expected size of "size". + // Parameters: + // 0) [out] ofs: The output file stream. + // 1) [in, out] offset: From where to write data; output is the new offset when the call returns. + // 2) [in] size: How many data to be received. + // 3) [in] block: size of block per write to disk + // Returned value: + // Actual size of data received. + + int64_t recvfile(std::fstream& ofs, int64_t& offset, int64_t size, int block = 7320000); + + // Functionality: + // Configure UDT options. + // Parameters: + // 0) [in] optName: The enum name of a UDT option. + // 1) [in] optval: The value to be set. + // 2) [in] optlen: size of "optval". + // Returned value: + // None. + + void setOpt(UDTOpt optName, const void* optval, int optlen); + + // Functionality: + // Read UDT options. + // Parameters: + // 0) [in] optName: The enum name of a UDT option. + // 1) [in] optval: The value to be returned. + // 2) [out] optlen: size of "optval". + // Returned value: + // None. + + void getOpt(UDTOpt optName, void* optval, int& optlen); + + // Functionality: + // read the performance data since last sample() call. + // Parameters: + // 0) [in, out] perf: pointer to a CPerfMon structure to record the performance data. + // 1) [in] clear: flag to decide if the local performance trace should be cleared. + // Returned value: + // None. + + void sample(CPerfMon* perf, bool clear = true); + +private: + static CUDTUnited s_UDTUnited; // UDT global management base + +public: + static const UDTSOCKET INVALID_SOCK; // invalid socket descriptor + static const int ERROR; // socket api error returned value + +private: // Identification + UDTSOCKET m_SocketID; // UDT socket number + UDTSockType m_iSockType; // Type of the UDT connection (SOCK_STREAM or SOCK_DGRAM) + UDTSOCKET m_PeerID; // peer id, for multiplexer + static const int m_iVersion; // UDT version, for compatibility use + +private: // Packet sizes + int m_iPktSize; // Maximum/regular packet size, in bytes + int m_iPayloadSize; // Maximum/regular payload size, in bytes + +private: // Options + int m_iMSS; // Maximum Segment Size, in bytes + bool m_bSynSending; // Sending syncronization mode + bool m_bSynRecving; // Receiving syncronization mode + int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side + int m_iSndBufSize; // Maximum UDT sender buffer size + int m_iRcvBufSize; // Maximum UDT receiver buffer size + linger m_Linger; // Linger information on close + int m_iUDPSndBufSize; // UDP sending buffer size + int m_iUDPRcvBufSize; // UDP receiving buffer size + int m_iIPversion; // IP version + bool m_bRendezvous; // Rendezvous connection mode + int m_iSndTimeOut; // sending timeout in milliseconds + int m_iRcvTimeOut; // receiving timeout in milliseconds + bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer + int64_t m_llMaxBW; // maximum data transfer rate (threshold) + +private: // congestion control + CCCVirtualFactory* m_pCCFactory; // Factory class to create a specific CC instance + CCC* m_pCC; // congestion control class + CCache* m_pCache; // network information cache + +private: // Status + volatile bool m_bListening; // If the UDT entit is listening to connection + volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed + volatile bool m_bConnected; // Whether the connection is on or off + volatile bool m_bClosing; // If the UDT entity is closing + volatile bool m_bShutdown; // If the peer side has shutdown the connection + volatile bool m_bBroken; // If the connection has been broken + volatile bool m_bPeerHealth; // If the peer status is normal + bool m_bOpened; // If the UDT entity has been opened + int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected + + int m_iEXPCount; // Expiration counter + int m_iBandwidth; // Estimated bandwidth, number of packets per second + int m_iRTT; // RTT, in microseconds + int m_iRTTVar; // RTT variance + int m_iDeliveryRate; // Packet arrival rate at the receiver side + + uint64_t m_ullLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) + + CHandShake m_ConnReq; // connection request + CHandShake m_ConnRes; // connection response + int64_t m_llLastReqTime; // last time when a connection request is sent + +private: // Sending related data + CSndBuffer* m_pSndBuffer; // Sender buffer + CSndLossList* m_pSndLossList; // Sender loss list + CPktTimeWindow* m_pSndTimeWindow; // Packet sending time window + + volatile uint64_t m_ullInterval; // Inter-packet time, in CPU clock cycles + uint64_t m_ullTimeDiff; // aggregate difference in inter-packet time + + volatile int m_iFlowWindowSize; // Flow control window size + volatile double m_dCongestionWindow; // congestion window size + + volatile int32_t m_iSndLastAck; // Last ACK received + volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list + volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that has been sent + int32_t m_iLastDecSeq; // Sequence number sent last decrease occurs + int32_t m_iSndLastAck2; // Last ACK2 sent back + uint64_t m_ullSndLastAck2Time; // The time when last ACK2 was sent back + + int32_t m_iISN; // Initial Sequence Number + + void CCUpdate(); + +private: // Receiving related data + CRcvBuffer* m_pRcvBuffer; // Receiver buffer + CRcvLossList* m_pRcvLossList; // Receiver loss list + CACKWindow* m_pACKWindow; // ACK history window + CPktTimeWindow* m_pRcvTimeWindow; // Packet arrival time window + + int32_t m_iRcvLastAck; // Last sent ACK + uint64_t m_ullLastAckTime; // Timestamp of last ACK + int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged + int32_t m_iAckSeqNo; // Last ACK sequence number + int32_t m_iRcvCurrSeqNo; // Largest received sequence number + + uint64_t m_ullLastWarningTime; // Last time that a warning message is sent + + int32_t m_iPeerISN; // Initial Sequence Number of the peer side + +private: // synchronization: mutexes and conditions + pthread_mutex_t m_ConnectionLock; // used to synchronize connection operation + + pthread_cond_t m_SendBlockCond; // used to block "send" call + pthread_mutex_t m_SendBlockLock; // lock associated to m_SendBlockCond + + pthread_mutex_t m_AckLock; // used to protected sender's loss list when processing ACK + + pthread_cond_t m_RecvDataCond; // used to block "recv" when there is no data + pthread_mutex_t m_RecvDataLock; // lock associated to m_RecvDataCond + + pthread_mutex_t m_SendLock; // used to synchronize "send" call + pthread_mutex_t m_RecvLock; // used to synchronize "recv" call + + void initSynch(); + void destroySynch(); + void releaseSynch(); + +private: // Generation and processing of packets + void sendCtrl(int pkttype, void* lparam = NULL, void* rparam = NULL, int size = 0); + void processCtrl(CPacket& ctrlpkt); + int packData(CPacket& packet, uint64_t& ts); + int processData(CUnit* unit); + int listen(sockaddr* addr, CPacket& packet); + +private: // Trace + uint64_t m_StartTime; // timestamp when the UDT entity is started + int64_t m_llSentTotal; // total number of sent data packets, including retransmissions + int64_t m_llRecvTotal; // total number of received packets + int m_iSndLossTotal; // total number of lost packets (sender side) + int m_iRcvLossTotal; // total number of lost packets (receiver side) + int m_iRetransTotal; // total number of retransmitted packets + int m_iSentACKTotal; // total number of sent ACK packets + int m_iRecvACKTotal; // total number of received ACK packets + int m_iSentNAKTotal; // total number of sent NAK packets + int m_iRecvNAKTotal; // total number of received NAK packets + int64_t m_llSndDurationTotal; // total real time for sending + + uint64_t m_LastSampleTime; // last performance sample time + int64_t m_llTraceSent; // number of pakctes sent in the last trace interval + int64_t m_llTraceRecv; // number of pakctes received in the last trace interval + int m_iTraceSndLoss; // number of lost packets in the last trace interval (sender side) + int m_iTraceRcvLoss; // number of lost packets in the last trace interval (receiver side) + int m_iTraceRetrans; // number of retransmitted packets in the last trace interval + int m_iSentACK; // number of ACKs sent in the last trace interval + int m_iRecvACK; // number of ACKs received in the last trace interval + int m_iSentNAK; // number of NAKs sent in the last trace interval + int m_iRecvNAK; // number of NAKs received in the last trace interval + int64_t m_llSndDuration; // real time for sending + int64_t m_llSndDurationCounter; // timers to record the sending duration + +private: // Timers + uint64_t m_ullCPUFrequency; // CPU clock frequency, used for Timer, ticks per microsecond + + static const int m_iSYNInterval; // Periodical Rate Control Interval, 10000 microsecond + static const int m_iSelfClockInterval; // ACK interval for self-clocking + + uint64_t m_ullNextACKTime; // Next ACK time, in CPU clock cycles, same below + uint64_t m_ullNextNAKTime; // Next NAK time + + volatile uint64_t m_ullSYNInt; // SYN interval + volatile uint64_t m_ullACKInt; // ACK interval + volatile uint64_t m_ullNAKInt; // NAK interval + volatile uint64_t m_ullLastRspTime; // time stamp of last response from the peer + + uint64_t m_ullMinNakInt; // NAK timeout lower bound; too small value can cause unnecessary retransmission + uint64_t m_ullMinExpInt; // timeout lower bound threshold: too small timeout can cause problem + + int m_iPktCount; // packet counter for ACK + int m_iLightACKCount; // light ACK counter + + uint64_t m_ullTargetTime; // scheduled time of next packet sending + + void checkTimers(); + +private: // for UDP multiplexer + CSndQueue* m_pSndQueue; // packet sending queue + CRcvQueue* m_pRcvQueue; // packet receiving queue + sockaddr* m_pPeerAddr; // peer address + uint32_t m_piSelfIP[4]; // local UDP IP address + CSNode* m_pSNode; // node information for UDT list used in snd queue + CRNode* m_pRNode; // node information for UDT list used in rcv queue + +private: // for epoll + std::set m_sPollID; // set of epoll ID to trigger + void addEPoll(const int eid); + void removeEPoll(const int eid); +}; + + +#endif diff --git a/vendor/udt4/src/epoll.cpp b/vendor/udt4/src/epoll.cpp new file mode 100644 index 000000000..0e7ddb11b --- /dev/null +++ b/vendor/udt4/src/epoll.cpp @@ -0,0 +1,367 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/01/2011 +*****************************************************************************/ + +#ifdef LINUX + #include + #include +#endif +#include +#include +#include +#include + +#include "common.h" +#include "epoll.h" +#include "udt.h" + +using namespace std; + +CEPoll::CEPoll(): +m_iIDSeed(0) +{ + CGuard::createMutex(m_EPollLock); +} + +CEPoll::~CEPoll() +{ + CGuard::releaseMutex(m_EPollLock); +} + +int CEPoll::create() +{ + CGuard pg(m_EPollLock); + + int localid = 0; + + #ifdef LINUX + localid = epoll_create(1024); + if (localid < 0) + throw CUDTException(-1, 0, errno); + #else + // on BSD, use kqueue + // on Solaris, use /dev/poll + // on Windows, select + #endif + + if (++ m_iIDSeed >= 0x7FFFFFFF) + m_iIDSeed = 0; + + CEPollDesc desc; + desc.m_iID = m_iIDSeed; + desc.m_iLocalID = localid; + m_mPolls[desc.m_iID] = desc; + + return desc.m_iID; +} + +int CEPoll::add_usock(const int eid, const UDTSOCKET& u, const int* events) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + + if (!events || (*events & UDT_EPOLL_IN)) + p->second.m_sUDTSocksIn.insert(u); + if (!events || (*events & UDT_EPOLL_OUT)) + p->second.m_sUDTSocksOut.insert(u); + + return 0; +} + +int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + +#ifdef LINUX + epoll_event ev; + memset(&ev, 0, sizeof(epoll_event)); + + if (NULL == events) + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + else + { + ev.events = 0; + if (*events & UDT_EPOLL_IN) + ev.events |= EPOLLIN; + if (*events & UDT_EPOLL_OUT) + ev.events |= EPOLLOUT; + if (*events & UDT_EPOLL_ERR) + ev.events |= EPOLLERR; + } + + ev.data.fd = s; + if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0) + throw CUDTException(); +#endif + + p->second.m_sLocals.insert(s); + + return 0; +} + +int CEPoll::remove_usock(const int eid, const UDTSOCKET& u) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + + p->second.m_sUDTSocksIn.erase(u); + p->second.m_sUDTSocksOut.erase(u); + p->second.m_sUDTSocksEx.erase(u); + + return 0; +} + +int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + +#ifdef LINUX + epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only. + if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0) + throw CUDTException(); +#endif + + p->second.m_sLocals.erase(s); + + return 0; +} + +int CEPoll::wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + // if all fields is NULL and waiting time is infinite, then this would be a deadlock + if (!readfds && !writefds && !lrfds && lwfds && (msTimeOut < 0)) + throw CUDTException(5, 3, 0); + + // Clear these sets in case the app forget to do it. + if (readfds) readfds->clear(); + if (writefds) writefds->clear(); + if (lrfds) lrfds->clear(); + if (lwfds) lwfds->clear(); + + int total = 0; + + int64_t entertime = CTimer::getTime(); + while (true) + { + CGuard::enterCS(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + { + CGuard::leaveCS(m_EPollLock); + throw CUDTException(5, 13); + } + + if (p->second.m_sUDTSocksIn.empty() && p->second.m_sUDTSocksOut.empty() && p->second.m_sLocals.empty() && (msTimeOut < 0)) + { + // no socket is being monitored, this may be a deadlock + CGuard::leaveCS(m_EPollLock); + throw CUDTException(5, 3); + } + + // Sockets with exceptions are returned to both read and write sets. + if ((NULL != readfds) && (!p->second.m_sUDTReads.empty() || !p->second.m_sUDTExcepts.empty())) + { + *readfds = p->second.m_sUDTReads; + for (set::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i) + readfds->insert(*i); + total += p->second.m_sUDTReads.size() + p->second.m_sUDTExcepts.size(); + } + if ((NULL != writefds) && (!p->second.m_sUDTWrites.empty() || !p->second.m_sUDTExcepts.empty())) + { + *writefds = p->second.m_sUDTWrites; + for (set::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i) + writefds->insert(*i); + total += p->second.m_sUDTWrites.size() + p->second.m_sUDTExcepts.size(); + } + + if (lrfds || lwfds) + { + #ifdef LINUX + const int max_events = p->second.m_sLocals.size(); + epoll_event ev[max_events]; + int nfds = ::epoll_wait(p->second.m_iLocalID, ev, max_events, 0); + + for (int i = 0; i < nfds; ++ i) + { + if ((NULL != lrfds) && (ev[i].events & EPOLLIN)) + { + lrfds->insert(ev[i].data.fd); + ++ total; + } + if ((NULL != lwfds) && (ev[i].events & EPOLLOUT)) + { + lwfds->insert(ev[i].data.fd); + ++ total; + } + } + #else + //currently "select" is used for all non-Linux platforms. + //faster approaches can be applied for specific systems in the future. + + //"select" has a limitation on the number of sockets + + fd_set readfds; + fd_set writefds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + for (set::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) + { + if (lrfds) + FD_SET(*i, &readfds); + if (lwfds) + FD_SET(*i, &writefds); + } + + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + if (::select(0, &readfds, &writefds, NULL, &tv) > 0) + { + for (set::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) + { + if (lrfds && FD_ISSET(*i, &readfds)) + { + lrfds->insert(*i); + ++ total; + } + if (lwfds && FD_ISSET(*i, &writefds)) + { + lwfds->insert(*i); + ++ total; + } + } + } + #endif + } + + CGuard::leaveCS(m_EPollLock); + + if (total > 0) + return total; + + if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * 1000LL)) + throw CUDTException(6, 3, 0); + + CTimer::waitForEvent(); + } + + return 0; +} + +int CEPoll::release(const int eid) +{ + CGuard pg(m_EPollLock); + + map::iterator i = m_mPolls.find(eid); + if (i == m_mPolls.end()) + throw CUDTException(5, 13); + + #ifdef LINUX + // release local/system epoll descriptor + ::close(i->second.m_iLocalID); + #endif + + m_mPolls.erase(i); + + return 0; +} + +namespace +{ + +void update_epoll_sets(const UDTSOCKET& uid, const set& watch, set& result, bool enable) +{ + if (enable && (watch.find(uid) != watch.end())) + { + result.insert(uid); + } + else if (!enable) + { + result.erase(uid); + } +} + +} // namespace + +int CEPoll::update_events(const UDTSOCKET& uid, std::set& eids, int events, bool enable) +{ + CGuard pg(m_EPollLock); + + map::iterator p; + + vector lost; + for (set::iterator i = eids.begin(); i != eids.end(); ++ i) + { + p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + lost.push_back(*i); + } + else + { + if ((events & UDT_EPOLL_IN) != 0) + update_epoll_sets(uid, p->second.m_sUDTSocksIn, p->second.m_sUDTReads, enable); + if ((events & UDT_EPOLL_OUT) != 0) + update_epoll_sets(uid, p->second.m_sUDTSocksOut, p->second.m_sUDTWrites, enable); + if ((events & UDT_EPOLL_ERR) != 0) + update_epoll_sets(uid, p->second.m_sUDTSocksEx, p->second.m_sUDTExcepts, enable); + } + } + + for (vector::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} diff --git a/vendor/udt4/src/epoll.h b/vendor/udt4/src/epoll.h new file mode 100644 index 000000000..a19f8abbb --- /dev/null +++ b/vendor/udt4/src/epoll.h @@ -0,0 +1,173 @@ +/***************************************************************************** +Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 08/20/2010 +*****************************************************************************/ + +#ifndef __UDT_EPOLL_H__ +#define __UDT_EPOLL_H__ + + +#include +#include +#include "udt.h" + + +struct CEPollDesc +{ + int m_iID; // epoll ID + std::set m_sUDTSocksOut; // set of UDT sockets waiting for write events + std::set m_sUDTSocksIn; // set of UDT sockets waiting for read events + std::set m_sUDTSocksEx; // set of UDT sockets waiting for exceptions + + int m_iLocalID; // local system epoll ID + std::set m_sLocals; // set of local (non-UDT) descriptors + + std::set m_sUDTWrites; // UDT sockets ready for write + std::set m_sUDTReads; // UDT sockets ready for read + std::set m_sUDTExcepts; // UDT sockets with exceptions (connection broken, etc.) +}; + +class CEPoll +{ +friend class CUDT; +friend class CRendezvousQueue; + +public: + CEPoll(); + ~CEPoll(); + +public: // for CUDTUnited API + + // Functionality: + // create a new EPoll. + // Parameters: + // None. + // Returned value: + // new EPoll ID if success, otherwise an error number. + + int create(); + + // Functionality: + // add a UDT socket to an EPoll. + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] u: UDT Socket ID. + // 2) [in] events: events to watch. + // Returned value: + // 0 if success, otherwise an error number. + + int add_usock(const int eid, const UDTSOCKET& u, const int* events = NULL); + + // Functionality: + // add a system socket to an EPoll. + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] s: system Socket ID. + // 2) [in] events: events to watch. + // Returned value: + // 0 if success, otherwise an error number. + + int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + + // Functionality: + // remove a UDT socket event from an EPoll; socket will be removed if no events to watch + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] u: UDT socket ID. + // Returned value: + // 0 if success, otherwise an error number. + + int remove_usock(const int eid, const UDTSOCKET& u); + + // Functionality: + // remove a system socket event from an EPoll; socket will be removed if no events to watch + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] s: system socket ID. + // Returned value: + // 0 if success, otherwise an error number. + + int remove_ssock(const int eid, const SYSSOCKET& s); + + // Functionality: + // wait for EPoll events or timeout. + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [out] readfds: UDT sockets available for reading. + // 2) [out] writefds: UDT sockets available for writing. + // 3) [in] msTimeOut: timeout threshold, in milliseconds. + // 4) [out] lrfds: system file descriptors for reading. + // 5) [out] lwfds: system file descriptors for writing. + // Returned value: + // number of sockets available for IO. + + int wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds, std::set* lwfds); + + // Functionality: + // close and release an EPoll. + // Parameters: + // 0) [in] eid: EPoll ID. + // Returned value: + // 0 if success, otherwise an error number. + + int release(const int eid); + +public: // for CUDT to acknowledge IO status + + // Functionality: + // Update events available for a UDT socket. + // Parameters: + // 0) [in] uid: UDT socket ID. + // 1) [in] eids: EPoll IDs to be set + // 1) [in] events: Combination of events to update + // 1) [in] enable: true -> enable, otherwise disable + // Returned value: + // 0 if success, otherwise an error number + + int update_events(const UDTSOCKET& uid, std::set& eids, int events, bool enable); + +private: + int m_iIDSeed; // seed to generate a new ID + pthread_mutex_t m_SeedLock; + + std::map m_mPolls; // all epolls + pthread_mutex_t m_EPollLock; +}; + + +#endif diff --git a/vendor/udt4/src/list.cpp b/vendor/udt4/src/list.cpp new file mode 100644 index 000000000..00b7e5706 --- /dev/null +++ b/vendor/udt4/src/list.cpp @@ -0,0 +1,703 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/22/2011 +*****************************************************************************/ + +#include "list.h" + +CSndLossList::CSndLossList(int size): +m_piData1(NULL), +m_piData2(NULL), +m_piNext(NULL), +m_iHead(-1), +m_iLength(0), +m_iSize(size), +m_iLastInsertPos(-1), +m_ListLock() +{ + m_piData1 = new int32_t [m_iSize]; + m_piData2 = new int32_t [m_iSize]; + m_piNext = new int [m_iSize]; + + // -1 means there is no data in the node + for (int i = 0; i < size; ++ i) + { + m_piData1[i] = -1; + m_piData2[i] = -1; + } + + // sender list needs mutex protection + #ifndef WIN32 + pthread_mutex_init(&m_ListLock, 0); + #else + m_ListLock = CreateMutex(NULL, false, NULL); + #endif +} + +CSndLossList::~CSndLossList() +{ + delete [] m_piData1; + delete [] m_piData2; + delete [] m_piNext; + + #ifndef WIN32 + pthread_mutex_destroy(&m_ListLock); + #else + CloseHandle(m_ListLock); + #endif +} + +int CSndLossList::insert(int32_t seqno1, int32_t seqno2) +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + { + // insert data into an empty list + + m_iHead = 0; + m_piData1[m_iHead] = seqno1; + if (seqno2 != seqno1) + m_piData2[m_iHead] = seqno2; + + m_piNext[m_iHead] = -1; + m_iLastInsertPos = m_iHead; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + + return m_iLength; + } + + // otherwise find the position where the data can be inserted + int origlen = m_iLength; + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1); + int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (offset < 0) + { + // Insert data prior to the head pointer + + m_piData1[loc] = seqno1; + if (seqno2 != seqno1) + m_piData2[loc] = seqno2; + + // new node becomes head + m_piNext[loc] = m_iHead; + m_iHead = loc; + m_iLastInsertPos = loc; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + } + else if (offset > 0) + { + if (seqno1 == m_piData1[loc]) + { + m_iLastInsertPos = loc; + + // first seqno is equivlent, compare the second + if (-1 == m_piData2[loc]) + { + if (seqno2 != seqno1) + { + m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1; + m_piData2[loc] = seqno2; + } + } + else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0) + { + // new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7] + m_iLength += CSeqNo::seqlen(m_piData2[loc], seqno2) - 1; + m_piData2[loc] = seqno2; + } + else + // Do nothing if it is already there + return 0; + } + else + { + // searching the prior node + int i; + if ((-1 != m_iLastInsertPos) && (CSeqNo::seqcmp(m_piData1[m_iLastInsertPos], seqno1) < 0)) + i = m_iLastInsertPos; + else + i = m_iHead; + + while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno1) < 0)) + i = m_piNext[i]; + + if ((-1 == m_piData2[i]) || (CSeqNo::seqcmp(m_piData2[i], seqno1) < 0)) + { + m_iLastInsertPos = loc; + + // no overlap, create new node + m_piData1[loc] = seqno1; + if (seqno2 != seqno1) + m_piData2[loc] = seqno2; + + m_piNext[loc] = m_piNext[i]; + m_piNext[i] = loc; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + } + else + { + m_iLastInsertPos = i; + + // overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7] + if (CSeqNo::seqcmp(m_piData2[i], seqno2) < 0) + { + m_iLength += CSeqNo::seqlen(m_piData2[i], seqno2) - 1; + m_piData2[i] = seqno2; + + loc = i; + } + else + return 0; + } + } + } + else + { + m_iLastInsertPos = m_iHead; + + // insert to head node + if (seqno2 != seqno1) + { + if (-1 == m_piData2[loc]) + { + m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1; + m_piData2[loc] = seqno2; + } + else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0) + { + m_iLength += CSeqNo::seqlen(m_piData2[loc], seqno2) - 1; + m_piData2[loc] = seqno2; + } + else + return 0; + } + else + return 0; + } + + // coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9] + while ((-1 != m_piNext[loc]) && (-1 != m_piData2[loc])) + { + int i = m_piNext[loc]; + + if (CSeqNo::seqcmp(m_piData1[i], CSeqNo::incseq(m_piData2[loc])) <= 0) + { + // coalesce if there is overlap + if (-1 != m_piData2[i]) + { + if (CSeqNo::seqcmp(m_piData2[i], m_piData2[loc]) > 0) + { + if (CSeqNo::seqcmp(m_piData2[loc], m_piData1[i]) >= 0) + m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[loc]); + + m_piData2[loc] = m_piData2[i]; + } + else + m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[i]); + } + else + { + if (m_piData1[i] == CSeqNo::incseq(m_piData2[loc])) + m_piData2[loc] = m_piData1[i]; + else + m_iLength --; + } + + m_piData1[i] = -1; + m_piData2[i] = -1; + m_piNext[loc] = m_piNext[i]; + } + else + break; + } + + return m_iLength - origlen; +} + +void CSndLossList::remove(int32_t seqno) +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + return; + + // Remove all from the head pointer to a node with a larger seq. no. or the list is empty + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno); + int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (0 == offset) + { + // It is the head. Remove the head and point to the next node + loc = (loc + 1) % m_iSize; + + if (-1 == m_piData2[m_iHead]) + loc = m_piNext[m_iHead]; + else + { + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[m_iHead], CSeqNo::incseq(seqno)) > 0) + m_piData2[loc] = m_piData2[m_iHead]; + + m_piData2[m_iHead] = -1; + + m_piNext[loc] = m_piNext[m_iHead]; + } + + m_piData1[m_iHead] = -1; + + if (m_iLastInsertPos == m_iHead) + m_iLastInsertPos = -1; + + m_iHead = loc; + + m_iLength --; + } + else if (offset > 0) + { + int h = m_iHead; + + if (seqno == m_piData1[loc]) + { + // target node is not empty, remove part/all of the seqno in the node. + int temp = loc; + loc = (loc + 1) % m_iSize; + + if (-1 == m_piData2[temp]) + m_iHead = m_piNext[temp]; + else + { + // remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3) + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[temp], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[temp]; + m_iHead = loc; + m_piNext[loc] = m_piNext[temp]; + m_piNext[temp] = loc; + m_piData2[temp] = -1; + } + } + else + { + // target node is empty, check prior node + int i = m_iHead; + while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno) < 0)) + i = m_piNext[i]; + + loc = (loc + 1) % m_iSize; + + if (-1 == m_piData2[i]) + m_iHead = m_piNext[i]; + else if (CSeqNo::seqcmp(m_piData2[i], seqno) > 0) + { + // remove part/all seqno in the prior node + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[i], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[i]; + + m_piData2[i] = seqno; + + m_piNext[loc] = m_piNext[i]; + m_piNext[i] = loc; + + m_iHead = loc; + } + else + m_iHead = m_piNext[i]; + } + + // Remove all nodes prior to the new head + while (h != m_iHead) + { + if (m_piData2[h] != -1) + { + m_iLength -= CSeqNo::seqlen(m_piData1[h], m_piData2[h]); + m_piData2[h] = -1; + } + else + m_iLength --; + + m_piData1[h] = -1; + + if (m_iLastInsertPos == h) + m_iLastInsertPos = -1; + + h = m_piNext[h]; + } + } +} + +int CSndLossList::getLossLength() +{ + CGuard listguard(m_ListLock); + + return m_iLength; +} + +int32_t CSndLossList::getLostSeq() +{ + if (0 == m_iLength) + return -1; + + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + return -1; + + if (m_iLastInsertPos == m_iHead) + m_iLastInsertPos = -1; + + // return the first loss seq. no. + int32_t seqno = m_piData1[m_iHead]; + + // head moves to the next node + if (-1 == m_piData2[m_iHead]) + { + //[3, -1] becomes [], and head moves to next node in the list + m_piData1[m_iHead] = -1; + m_iHead = m_piNext[m_iHead]; + } + else + { + // shift to next node, e.g., [3, 7] becomes [], [4, 7] + int loc = (m_iHead + 1) % m_iSize; + + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[m_iHead], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[m_iHead]; + + m_piData1[m_iHead] = -1; + m_piData2[m_iHead] = -1; + + m_piNext[loc] = m_piNext[m_iHead]; + m_iHead = loc; + } + + m_iLength --; + + return seqno; +} + +//////////////////////////////////////////////////////////////////////////////// + +CRcvLossList::CRcvLossList(int size): +m_piData1(NULL), +m_piData2(NULL), +m_piNext(NULL), +m_piPrior(NULL), +m_iHead(-1), +m_iTail(-1), +m_iLength(0), +m_iSize(size) +{ + m_piData1 = new int32_t [m_iSize]; + m_piData2 = new int32_t [m_iSize]; + m_piNext = new int [m_iSize]; + m_piPrior = new int [m_iSize]; + + // -1 means there is no data in the node + for (int i = 0; i < size; ++ i) + { + m_piData1[i] = -1; + m_piData2[i] = -1; + } +} + +CRcvLossList::~CRcvLossList() +{ + delete [] m_piData1; + delete [] m_piData2; + delete [] m_piNext; + delete [] m_piPrior; +} + +void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) +{ + // Data to be inserted must be larger than all those in the list + // guaranteed by the UDT receiver + + if (0 == m_iLength) + { + // insert data into an empty list + m_iHead = 0; + m_iTail = 0; + m_piData1[m_iHead] = seqno1; + if (seqno2 != seqno1) + m_piData2[m_iHead] = seqno2; + + m_piNext[m_iHead] = -1; + m_piPrior[m_iHead] = -1; + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + + return; + } + + // otherwise searching for the position where the node should be + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1); + int loc = (m_iHead + offset) % m_iSize; + + if ((-1 != m_piData2[m_iTail]) && (CSeqNo::incseq(m_piData2[m_iTail]) == seqno1)) + { + // coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7] + loc = m_iTail; + m_piData2[loc] = seqno2; + } + else + { + // create new node + m_piData1[loc] = seqno1; + + if (seqno2 != seqno1) + m_piData2[loc] = seqno2; + + m_piNext[m_iTail] = loc; + m_piPrior[loc] = m_iTail; + m_piNext[loc] = -1; + m_iTail = loc; + } + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); +} + +bool CRcvLossList::remove(int32_t seqno) +{ + if (0 == m_iLength) + return false; + + // locate the position of "seqno" in the list + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno); + if (offset < 0) + return false; + + int loc = (m_iHead + offset) % m_iSize; + + if (seqno == m_piData1[loc]) + { + // This is a seq. no. that starts the loss sequence + + if (-1 == m_piData2[loc]) + { + // there is only 1 loss in the sequence, delete it from the node + if (m_iHead == loc) + { + m_iHead = m_piNext[m_iHead]; + if (-1 != m_iHead) + m_piPrior[m_iHead] = -1; + } + else + { + m_piNext[m_piPrior[loc]] = m_piNext[loc]; + if (-1 != m_piNext[loc]) + m_piPrior[m_piNext[loc]] = m_piPrior[loc]; + else + m_iTail = m_piPrior[loc]; + } + + m_piData1[loc] = -1; + } + else + { + // there are more than 1 loss in the sequence + // move the node to the next and update the starter as the next loss inSeqNo(seqno) + + // find next node + int i = (loc + 1) % m_iSize; + + // remove the "seqno" and change the starter as next seq. no. + m_piData1[i] = CSeqNo::incseq(m_piData1[loc]); + + // process the sequence end + if (CSeqNo::seqcmp(m_piData2[loc], CSeqNo::incseq(m_piData1[loc])) > 0) + m_piData2[i] = m_piData2[loc]; + + // remove the current node + m_piData1[loc] = -1; + m_piData2[loc] = -1; + + // update list pointer + m_piNext[i] = m_piNext[loc]; + m_piPrior[i] = m_piPrior[loc]; + + if (m_iHead == loc) + m_iHead = i; + else + m_piNext[m_piPrior[i]] = i; + + if (m_iTail == loc) + m_iTail = i; + else + m_piPrior[m_piNext[i]] = i; + } + + m_iLength --; + + return true; + } + + // There is no loss sequence in the current position + // the "seqno" may be contained in a previous node + + // searching previous node + int i = (loc - 1 + m_iSize) % m_iSize; + while (-1 == m_piData1[i]) + i = (i - 1 + m_iSize) % m_iSize; + + // not contained in this node, return + if ((-1 == m_piData2[i]) || (CSeqNo::seqcmp(seqno, m_piData2[i]) > 0)) + return false; + + if (seqno == m_piData2[i]) + { + // it is the sequence end + + if (seqno == CSeqNo::incseq(m_piData1[i])) + m_piData2[i] = -1; + else + m_piData2[i] = CSeqNo::decseq(seqno); + } + else + { + // split the sequence + + // construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end + // located at "loc + 1" + loc = (loc + 1) % m_iSize; + + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[i], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[i]; + + // the first (original) sequence is between the original sequence start to CSeqNo::decseq(seqno) + if (seqno == CSeqNo::incseq(m_piData1[i])) + m_piData2[i] = -1; + else + m_piData2[i] = CSeqNo::decseq(seqno); + + // update the list pointer + m_piNext[loc] = m_piNext[i]; + m_piNext[i] = loc; + m_piPrior[loc] = i; + + if (m_iTail == i) + m_iTail = loc; + else + m_piPrior[m_piNext[loc]] = loc; + } + + m_iLength --; + + return true; +} + +bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2) +{ + if (seqno1 <= seqno2) + { + for (int32_t i = seqno1; i <= seqno2; ++ i) + remove(i); + } + else + { + for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j) + remove(j); + for (int32_t k = 0; k <= seqno2; ++ k) + remove(k); + } + + return true; +} + +bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const +{ + if (0 == m_iLength) + return false; + + int p = m_iHead; + + while (-1 != p) + { + if ((CSeqNo::seqcmp(m_piData1[p], seqno1) == 0) || + ((CSeqNo::seqcmp(m_piData1[p], seqno1) > 0) && (CSeqNo::seqcmp(m_piData1[p], seqno2) <= 0)) || + ((CSeqNo::seqcmp(m_piData1[p], seqno1) < 0) && (m_piData2[p] != -1) && CSeqNo::seqcmp(m_piData2[p], seqno1) >= 0)) + return true; + + p = m_piNext[p]; + } + + return false; +} + +int CRcvLossList::getLossLength() const +{ + return m_iLength; +} + +int CRcvLossList::getFirstLostSeq() const +{ + if (0 == m_iLength) + return -1; + + return m_piData1[m_iHead]; +} + +void CRcvLossList::getLossArray(int32_t* array, int& len, int limit) +{ + len = 0; + + int i = m_iHead; + + while ((len < limit - 1) && (-1 != i)) + { + array[len] = m_piData1[i]; + if (-1 != m_piData2[i]) + { + // there are more than 1 loss in the sequence + array[len] |= 0x80000000; + ++ len; + array[len] = m_piData2[i]; + } + + ++ len; + + i = m_piNext[i]; + } +} diff --git a/vendor/udt4/src/list.h b/vendor/udt4/src/list.h new file mode 100644 index 000000000..6be9694f4 --- /dev/null +++ b/vendor/udt4/src/list.h @@ -0,0 +1,202 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/22/2011 +*****************************************************************************/ + +#ifndef __UDT_LIST_H__ +#define __UDT_LIST_H__ + + +#include "udt.h" +#include "common.h" + + +class CSndLossList +{ +public: + CSndLossList(int size = 1024); + ~CSndLossList(); + + // Functionality: + // Insert a seq. no. into the sender loss list. + // Parameters: + // 0) [in] seqno1: sequence number starts. + // 1) [in] seqno2: sequence number ends. + // Returned value: + // number of packets that are not in the list previously. + + int insert(int32_t seqno1, int32_t seqno2); + + // Functionality: + // Remove ALL the seq. no. that are not greater than the parameter. + // Parameters: + // 0) [in] seqno: sequence number. + // Returned value: + // None. + + void remove(int32_t seqno); + + // Functionality: + // Read the loss length. + // Parameters: + // None. + // Returned value: + // The length of the list. + + int getLossLength(); + + // Functionality: + // Read the first (smallest) loss seq. no. in the list and remove it. + // Parameters: + // None. + // Returned value: + // The seq. no. or -1 if the list is empty. + + int32_t getLostSeq(); + +private: + int32_t* m_piData1; // sequence number starts + int32_t* m_piData2; // seqnence number ends + int* m_piNext; // next node in the list + + int m_iHead; // first node + int m_iLength; // loss length + int m_iSize; // size of the static array + int m_iLastInsertPos; // position of last insert node + + pthread_mutex_t m_ListLock; // used to synchronize list operation + +private: + CSndLossList(const CSndLossList&); + CSndLossList& operator=(const CSndLossList&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CRcvLossList +{ +public: + CRcvLossList(int size = 1024); + ~CRcvLossList(); + + // Functionality: + // Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list. + // Parameters: + // 0) [in] seqno1: sequence number starts. + // 1) [in] seqno2: seqeunce number ends. + // Returned value: + // None. + + void insert(int32_t seqno1, int32_t seqno2); + + // Functionality: + // Remove a loss seq. no. from the receiver's loss list. + // Parameters: + // 0) [in] seqno: sequence number. + // Returned value: + // if the packet is removed (true) or no such lost packet is found (false). + + bool remove(int32_t seqno); + + // Functionality: + // Remove all packets between seqno1 and seqno2. + // Parameters: + // 0) [in] seqno1: start sequence number. + // 1) [in] seqno2: end sequence number. + // Returned value: + // if the packet is removed (true) or no such lost packet is found (false). + + bool remove(int32_t seqno1, int32_t seqno2); + + // Functionality: + // Find if there is any lost packets whose sequence number falling seqno1 and seqno2. + // Parameters: + // 0) [in] seqno1: start sequence number. + // 1) [in] seqno2: end sequence number. + // Returned value: + // True if found; otherwise false. + + bool find(int32_t seqno1, int32_t seqno2) const; + + // Functionality: + // Read the loss length. + // Parameters: + // None. + // Returned value: + // the length of the list. + + int getLossLength() const; + + // Functionality: + // Read the first (smallest) seq. no. in the list. + // Parameters: + // None. + // Returned value: + // the sequence number or -1 if the list is empty. + + int getFirstLostSeq() const; + + // Functionality: + // Get a encoded loss array for NAK report. + // Parameters: + // 0) [out] array: the result list of seq. no. to be included in NAK. + // 1) [out] physical length of the result array. + // 2) [in] limit: maximum length of the array. + // Returned value: + // None. + + void getLossArray(int32_t* array, int& len, int limit); + +private: + int32_t* m_piData1; // sequence number starts + int32_t* m_piData2; // sequence number ends + int* m_piNext; // next node in the list + int* m_piPrior; // prior node in the list; + + int m_iHead; // first node in the list + int m_iTail; // last node in the list; + int m_iLength; // loss length + int m_iSize; // size of the static array + +private: + CRcvLossList(const CRcvLossList&); + CRcvLossList& operator=(const CRcvLossList&); +}; + + +#endif diff --git a/vendor/udt4/src/md5.cpp b/vendor/udt4/src/md5.cpp new file mode 100644 index 000000000..d6fd5d370 --- /dev/null +++ b/vendor/udt4/src/md5.cpp @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.cpp,v 1.3 2008/01/20 22:52:04 lilyco Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/vendor/udt4/src/md5.h b/vendor/udt4/src/md5.h new file mode 100644 index 000000000..f7402e7e2 --- /dev/null +++ b/vendor/udt4/src/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.2 2007/12/24 05:58:37 lilyco Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/vendor/udt4/src/packet.cpp b/vendor/udt4/src/packet.cpp new file mode 100644 index 000000000..e238a04a7 --- /dev/null +++ b/vendor/udt4/src/packet.cpp @@ -0,0 +1,411 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/12/2011 +*****************************************************************************/ + + +////////////////////////////////////////////////////////////////////////////// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Packet Header | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// ~ Data / Control Information Field ~ +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |ff |o| Message Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time Stamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Socket ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// bit 0: +// 0: Data Packet +// 1: Control Packet +// bit ff: +// 11: solo message packet +// 10: first packet of a message +// 01: last packet of a message +// bit o: +// 0: in order delivery not required +// 1: in order delivery required +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| Type | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Additional Info | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time Stamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Socket ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// bit 1-15: +// 0: Protocol Connection Handshake +// Add. Info: Undefined +// Control Info: Handshake information (see CHandShake) +// 1: Keep-alive +// Add. Info: Undefined +// Control Info: None +// 2: Acknowledgement (ACK) +// Add. Info: The ACK sequence number +// Control Info: The sequence number to which (but not include) all the previous packets have beed received +// Optional: RTT +// RTT Variance +// available receiver buffer size (in bytes) +// advertised flow window size (number of packets) +// estimated bandwidth (number of packets per second) +// 3: Negative Acknowledgement (NAK) +// Add. Info: Undefined +// Control Info: Loss list (see loss list coding below) +// 4: Congestion/Delay Warning +// Add. Info: Undefined +// Control Info: None +// 5: Shutdown +// Add. Info: Undefined +// Control Info: None +// 6: Acknowledgement of Acknowledement (ACK-square) +// Add. Info: The ACK sequence number +// Control Info: None +// 7: Message Drop Request +// Add. Info: Message ID +// Control Info: first sequence number of the message +// last seqeunce number of the message +// 8: Error Signal from the Peer Side +// Add. Info: Error code +// Control Info: None +// 0x7FFF: Explained by bits 16 - 31 +// +// bit 16 - 31: +// This space is used for future expansion or user defined control packets. +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| Sequence Number a (first) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number b (last) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number (single) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Loss List Field Coding: +// For any consectutive lost seqeunce numbers that the differnece between +// the last and first is more than 1, only record the first (a) and the +// the last (b) sequence numbers in the loss list field, and modify the +// the first bit of a to 1. +// For any single loss or consectutive loss less than 2 packets, use +// the original sequence numbers in the field. + + +#include +#include "packet.h" + + +const int CPacket::m_iPktHdrSize = 16; +const int CHandShake::m_iContentSize = 48; + + +// Set up the aliases in the constructure +CPacket::CPacket(): +m_iSeqNo((int32_t&)(m_nHeader[0])), +m_iMsgNo((int32_t&)(m_nHeader[1])), +m_iTimeStamp((int32_t&)(m_nHeader[2])), +m_iID((int32_t&)(m_nHeader[3])), +m_pcData((char*&)(m_PacketVector[1].iov_base)), +__pad() +{ + for (int i = 0; i < 4; ++ i) + m_nHeader[i] = 0; + m_PacketVector[0].iov_base = (char *)m_nHeader; + m_PacketVector[0].iov_len = CPacket::m_iPktHdrSize; + m_PacketVector[1].iov_base = NULL; + m_PacketVector[1].iov_len = 0; +} + +CPacket::~CPacket() +{ +} + +int CPacket::getLength() const +{ + return m_PacketVector[1].iov_len; +} + +void CPacket::setLength(int len) +{ + m_PacketVector[1].iov_len = len; +} + +void CPacket::pack(int pkttype, void* lparam, void* rparam, int size) +{ + // Set (bit-0 = 1) and (bit-1~15 = type) + m_nHeader[0] = 0x80000000 | (pkttype << 16); + + // Set additional information and control information field + switch (pkttype) + { + case 2: //0010 - Acknowledgement (ACK) + // ACK packet seq. no. + if (NULL != lparam) + m_nHeader[1] = *(int32_t *)lparam; + + // data ACK seq. no. + // optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second) + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + + break; + + case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2) + // ACK packet seq. no. + m_nHeader[1] = *(int32_t *)lparam; + + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 3: //0011 - Loss Report (NAK) + // loss list + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + + break; + + case 4: //0100 - Congestion Warning + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 1: //0001 - Keep-alive + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 0: //0000 - Handshake + // control info filed is handshake info + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; //sizeof(CHandShake); + + break; + + case 5: //0101 - Shutdown + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 7: //0111 - Message Drop Request + // msg id + m_nHeader[1] = *(int32_t *)lparam; + + //first seq no, last seq no + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + + break; + + case 8: //1000 - Error Signal from the Peer Side + // Error type + m_nHeader[1] = *(int32_t *)lparam; + + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 32767: //0x7FFF - Reserved for user defined control packets + // for extended control packet + // "lparam" contains the extended type information for bit 16 - 31 + // "rparam" is the control information + m_nHeader[0] |= *(int32_t *)lparam; + + if (NULL != rparam) + { + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + } + else + { + m_PacketVector[1].iov_base = (char *)&__pad; + m_PacketVector[1].iov_len = 4; + } + + break; + + default: + break; + } +} + +iovec* CPacket::getPacketVector() +{ + return m_PacketVector; +} + +int CPacket::getFlag() const +{ + // read bit 0 + return m_nHeader[0] >> 31; +} + +int CPacket::getType() const +{ + // read bit 1~15 + return (m_nHeader[0] >> 16) & 0x00007FFF; +} + +int CPacket::getExtendedType() const +{ + // read bit 16~31 + return m_nHeader[0] & 0x0000FFFF; +} + +int32_t CPacket::getAckSeqNo() const +{ + // read additional information field + return m_nHeader[1]; +} + +int CPacket::getMsgBoundary() const +{ + // read [1] bit 0~1 + return m_nHeader[1] >> 30; +} + +bool CPacket::getMsgOrderFlag() const +{ + // read [1] bit 2 + return (1 == ((m_nHeader[1] >> 29) & 1)); +} + +int32_t CPacket::getMsgSeq() const +{ + // read [1] bit 3~31 + return m_nHeader[1] & 0x1FFFFFFF; +} + +CPacket* CPacket::clone() const +{ + CPacket* pkt = new CPacket; + memcpy(pkt->m_nHeader, m_nHeader, m_iPktHdrSize); + pkt->m_pcData = new char[m_PacketVector[1].iov_len]; + memcpy(pkt->m_pcData, m_pcData, m_PacketVector[1].iov_len); + pkt->m_PacketVector[1].iov_len = m_PacketVector[1].iov_len; + + return pkt; +} + +CHandShake::CHandShake(): +m_iVersion(0), +m_iType(0), +m_iISN(0), +m_iMSS(0), +m_iFlightFlagSize(0), +m_iReqType(0), +m_iID(0), +m_iCookie(0) +{ + for (int i = 0; i < 4; ++ i) + m_piPeerIP[i] = 0; +} + +int CHandShake::serialize(char* buf, int& size) +{ + if (size < m_iContentSize) + return -1; + + int32_t* p = (int32_t*)buf; + *p++ = m_iVersion; + *p++ = m_iType; + *p++ = m_iISN; + *p++ = m_iMSS; + *p++ = m_iFlightFlagSize; + *p++ = m_iReqType; + *p++ = m_iID; + *p++ = m_iCookie; + for (int i = 0; i < 4; ++ i) + *p++ = m_piPeerIP[i]; + + size = m_iContentSize; + + return 0; +} + +int CHandShake::deserialize(const char* buf, int size) +{ + if (size < m_iContentSize) + return -1; + + int32_t* p = (int32_t*)buf; + m_iVersion = *p++; + m_iType = *p++; + m_iISN = *p++; + m_iMSS = *p++; + m_iFlightFlagSize = *p++; + m_iReqType = *p++; + m_iID = *p++; + m_iCookie = *p++; + for (int i = 0; i < 4; ++ i) + m_piPeerIP[i] = *p++; + + return 0; +} diff --git a/vendor/udt4/src/packet.h b/vendor/udt4/src/packet.h new file mode 100644 index 000000000..76cc95183 --- /dev/null +++ b/vendor/udt4/src/packet.h @@ -0,0 +1,223 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/02/2011 +*****************************************************************************/ + +#ifndef __UDT_PACKET_H__ +#define __UDT_PACKET_H__ + + +#include "udt.h" + +#ifdef WIN32 + struct iovec + { + int iov_len; + char* iov_base; + }; +#endif + +class CChannel; + +class CPacket +{ +friend class CChannel; +friend class CSndQueue; +friend class CRcvQueue; + +public: + int32_t& m_iSeqNo; // alias: sequence number + int32_t& m_iMsgNo; // alias: message number + int32_t& m_iTimeStamp; // alias: timestamp + int32_t& m_iID; // alias: socket ID + char*& m_pcData; // alias: data/control information + + static const int m_iPktHdrSize; // packet header size + +public: + CPacket(); + ~CPacket(); + + // Functionality: + // Get the payload or the control information field length. + // Parameters: + // None. + // Returned value: + // the payload or the control information field length. + + int getLength() const; + + // Functionality: + // Set the payload or the control information field length. + // Parameters: + // 0) [in] len: the payload or the control information field length. + // Returned value: + // None. + + void setLength(int len); + + // Functionality: + // Pack a Control packet. + // Parameters: + // 0) [in] pkttype: packet type filed. + // 1) [in] lparam: pointer to the first data structure, explained by the packet type. + // 2) [in] rparam: pointer to the second data structure, explained by the packet type. + // 3) [in] size: size of rparam, in number of bytes; + // Returned value: + // None. + + void pack(int pkttype, void* lparam = NULL, void* rparam = NULL, int size = 0); + + // Functionality: + // Read the packet vector. + // Parameters: + // None. + // Returned value: + // Pointer to the packet vector. + + iovec* getPacketVector(); + + // Functionality: + // Read the packet flag. + // Parameters: + // None. + // Returned value: + // packet flag (0 or 1). + + int getFlag() const; + + // Functionality: + // Read the packet type. + // Parameters: + // None. + // Returned value: + // packet type filed (000 ~ 111). + + int getType() const; + + // Functionality: + // Read the extended packet type. + // Parameters: + // None. + // Returned value: + // extended packet type filed (0x000 ~ 0xFFF). + + int getExtendedType() const; + + // Functionality: + // Read the ACK-2 seq. no. + // Parameters: + // None. + // Returned value: + // packet header field (bit 16~31). + + int32_t getAckSeqNo() const; + + // Functionality: + // Read the message boundary flag bit. + // Parameters: + // None. + // Returned value: + // packet header field [1] (bit 0~1). + + int getMsgBoundary() const; + + // Functionality: + // Read the message inorder delivery flag bit. + // Parameters: + // None. + // Returned value: + // packet header field [1] (bit 2). + + bool getMsgOrderFlag() const; + + // Functionality: + // Read the message sequence number. + // Parameters: + // None. + // Returned value: + // packet header field [1] (bit 3~31). + + int32_t getMsgSeq() const; + + // Functionality: + // Clone this packet. + // Parameters: + // None. + // Returned value: + // Pointer to the new packet. + + CPacket* clone() const; + +protected: + uint32_t m_nHeader[4]; // The 128-bit header field + iovec m_PacketVector[2]; // The 2-demension vector of UDT packet [header, data] + + int32_t __pad; + +protected: + CPacket& operator=(const CPacket&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CHandShake +{ +public: + CHandShake(); + + int serialize(char* buf, int& size); + int deserialize(const char* buf, int size); + +public: + static const int m_iContentSize; // Size of hand shake data + +public: + int32_t m_iVersion; // UDT version + int32_t m_iType; // UDT socket type + int32_t m_iISN; // random initial sequence number + int32_t m_iMSS; // maximum segment size + int32_t m_iFlightFlagSize; // flow control window size + int32_t m_iReqType; // connection request type: 1: regular connection request, 0: rendezvous connection request, -1/-2: response + int32_t m_iID; // socket ID + int32_t m_iCookie; // cookie + uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to +}; + + +#endif diff --git a/vendor/udt4/src/queue.cpp b/vendor/udt4/src/queue.cpp new file mode 100644 index 000000000..2caea2ae9 --- /dev/null +++ b/vendor/udt4/src/queue.cpp @@ -0,0 +1,1253 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2011 +*****************************************************************************/ + +#ifdef WIN32 + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif +#include + +#include "common.h" +#include "core.h" +#include "queue.h" + +using namespace std; + +CUnitQueue::CUnitQueue(): +m_pQEntry(NULL), +m_pCurrQueue(NULL), +m_pLastQueue(NULL), +m_iSize(0), +m_iCount(0), +m_iMSS(), +m_iIPversion() +{ +} + +CUnitQueue::~CUnitQueue() +{ + CQEntry* p = m_pQEntry; + + while (p != NULL) + { + delete [] p->m_pUnit; + delete [] p->m_pBuffer; + + CQEntry* q = p; + if (p == m_pLastQueue) + p = NULL; + else + p = p->m_pNext; + delete q; + } +} + +int CUnitQueue::init(int size, int mss, int version) +{ + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; + + try + { + tempq = new CQEntry; + tempu = new CUnit [size]; + tempb = new char [size * mss]; + } + catch (...) + { + delete tempq; + delete [] tempu; + delete [] tempb; + + return -1; + } + + for (int i = 0; i < size; ++ i) + { + tempu[i].m_iFlag = 0; + tempu[i].m_Packet.m_pcData = tempb + i * mss; + } + tempq->m_pUnit = tempu; + tempq->m_pBuffer = tempb; + tempq->m_iSize = size; + + m_pQEntry = m_pCurrQueue = m_pLastQueue = tempq; + m_pQEntry->m_pNext = m_pQEntry; + + m_pAvailUnit = m_pCurrQueue->m_pUnit; + + m_iSize = size; + m_iMSS = mss; + m_iIPversion = version; + + return 0; +} + +int CUnitQueue::increase() +{ + // adjust/correct m_iCount + int real_count = 0; + CQEntry* p = m_pQEntry; + while (p != NULL) + { + CUnit* u = p->m_pUnit; + for (CUnit* end = u + p->m_iSize; u != end; ++ u) + if (u->m_iFlag != 0) + ++ real_count; + + if (p == m_pLastQueue) + p = NULL; + else + p = p->m_pNext; + } + m_iCount = real_count; + if (double(m_iCount) / m_iSize < 0.9) + return -1; + + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; + + // all queues have the same size + int size = m_pQEntry->m_iSize; + + try + { + tempq = new CQEntry; + tempu = new CUnit [size]; + tempb = new char [size * m_iMSS]; + } + catch (...) + { + delete tempq; + delete [] tempu; + delete [] tempb; + + return -1; + } + + for (int i = 0; i < size; ++ i) + { + tempu[i].m_iFlag = 0; + tempu[i].m_Packet.m_pcData = tempb + i * m_iMSS; + } + tempq->m_pUnit = tempu; + tempq->m_pBuffer = tempb; + tempq->m_iSize = size; + + m_pLastQueue->m_pNext = tempq; + m_pLastQueue = tempq; + m_pLastQueue->m_pNext = m_pQEntry; + + m_iSize += size; + + return 0; +} + +int CUnitQueue::shrink() +{ + // currently queue cannot be shrunk. + return -1; +} + +CUnit* CUnitQueue::getNextAvailUnit() +{ + if (m_iCount * 10 > m_iSize * 9) + increase(); + + if (m_iCount >= m_iSize) + return NULL; + + CQEntry* entrance = m_pCurrQueue; + + do + { + for (CUnit* sentinel = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize - 1; m_pAvailUnit != sentinel; ++ m_pAvailUnit) + if (m_pAvailUnit->m_iFlag == 0) + return m_pAvailUnit; + + if (m_pCurrQueue->m_pUnit->m_iFlag == 0) + { + m_pAvailUnit = m_pCurrQueue->m_pUnit; + return m_pAvailUnit; + } + + m_pCurrQueue = m_pCurrQueue->m_pNext; + m_pAvailUnit = m_pCurrQueue->m_pUnit; + } while (m_pCurrQueue != entrance); + + increase(); + + return NULL; +} + + +CSndUList::CSndUList(): +m_pHeap(NULL), +m_iArrayLength(4096), +m_iLastEntry(-1), +m_ListLock(), +m_pWindowLock(NULL), +m_pWindowCond(NULL), +m_pTimer(NULL) +{ + m_pHeap = new CSNode*[m_iArrayLength]; + + #ifndef WIN32 + pthread_mutex_init(&m_ListLock, NULL); + #else + m_ListLock = CreateMutex(NULL, false, NULL); + #endif +} + +CSndUList::~CSndUList() +{ + delete [] m_pHeap; + + #ifndef WIN32 + pthread_mutex_destroy(&m_ListLock); + #else + CloseHandle(m_ListLock); + #endif +} + +void CSndUList::insert(int64_t ts, const CUDT* u) +{ + CGuard listguard(m_ListLock); + + // increase the heap array size if necessary + if (m_iLastEntry == m_iArrayLength - 1) + { + CSNode** temp = NULL; + + try + { + temp = new CSNode*[m_iArrayLength * 2]; + } + catch(...) + { + return; + } + + memcpy(temp, m_pHeap, sizeof(CSNode*) * m_iArrayLength); + m_iArrayLength *= 2; + delete [] m_pHeap; + m_pHeap = temp; + } + + insert_(ts, u); +} + +void CSndUList::update(const CUDT* u, bool reschedule) +{ + CGuard listguard(m_ListLock); + + CSNode* n = u->m_pSNode; + + if (n->m_iHeapLoc >= 0) + { + if (!reschedule) + return; + + if (n->m_iHeapLoc == 0) + { + n->m_llTimeStamp = 1; + m_pTimer->interrupt(); + return; + } + + remove_(u); + } + + insert_(1, u); +} + +int CSndUList::pop(sockaddr*& addr, CPacket& pkt) +{ + CGuard listguard(m_ListLock); + + if (-1 == m_iLastEntry) + return -1; + + // no pop until the next schedulled time + uint64_t ts; + CTimer::rdtsc(ts); + if (ts < m_pHeap[0]->m_llTimeStamp) + return -1; + + CUDT* u = m_pHeap[0]->m_pUDT; + remove_(u); + + if (!u->m_bConnected || u->m_bBroken) + return -1; + + // pack a packet from the socket + if (u->packData(pkt, ts) <= 0) + return -1; + + addr = u->m_pPeerAddr; + + // insert a new entry, ts is the next processing time + if (ts > 0) + insert_(ts, u); + + return 1; +} + +void CSndUList::remove(const CUDT* u) +{ + CGuard listguard(m_ListLock); + + remove_(u); +} + +uint64_t CSndUList::getNextProcTime() +{ + CGuard listguard(m_ListLock); + + if (-1 == m_iLastEntry) + return 0; + + return m_pHeap[0]->m_llTimeStamp; +} + +void CSndUList::insert_(int64_t ts, const CUDT* u) +{ + CSNode* n = u->m_pSNode; + + // do not insert repeated node + if (n->m_iHeapLoc >= 0) + return; + + m_iLastEntry ++; + m_pHeap[m_iLastEntry] = n; + n->m_llTimeStamp = ts; + + int q = m_iLastEntry; + int p = q; + while (p != 0) + { + p = (q - 1) >> 1; + if (m_pHeap[p]->m_llTimeStamp > m_pHeap[q]->m_llTimeStamp) + { + CSNode* t = m_pHeap[p]; + m_pHeap[p] = m_pHeap[q]; + m_pHeap[q] = t; + t->m_iHeapLoc = q; + q = p; + } + else + break; + } + + n->m_iHeapLoc = q; + + // an earlier event has been inserted, wake up sending worker + if (n->m_iHeapLoc == 0) + m_pTimer->interrupt(); + + // first entry, activate the sending queue + if (0 == m_iLastEntry) + { + #ifndef WIN32 + pthread_mutex_lock(m_pWindowLock); + pthread_cond_signal(m_pWindowCond); + pthread_mutex_unlock(m_pWindowLock); + #else + SetEvent(*m_pWindowCond); + #endif + } +} + +void CSndUList::remove_(const CUDT* u) +{ + CSNode* n = u->m_pSNode; + + if (n->m_iHeapLoc >= 0) + { + // remove the node from heap + m_pHeap[n->m_iHeapLoc] = m_pHeap[m_iLastEntry]; + m_iLastEntry --; + m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc; + + int q = n->m_iHeapLoc; + int p = q * 2 + 1; + while (p <= m_iLastEntry) + { + if ((p + 1 <= m_iLastEntry) && (m_pHeap[p]->m_llTimeStamp > m_pHeap[p + 1]->m_llTimeStamp)) + p ++; + + if (m_pHeap[q]->m_llTimeStamp > m_pHeap[p]->m_llTimeStamp) + { + CSNode* t = m_pHeap[p]; + m_pHeap[p] = m_pHeap[q]; + m_pHeap[p]->m_iHeapLoc = p; + m_pHeap[q] = t; + m_pHeap[q]->m_iHeapLoc = q; + + q = p; + p = q * 2 + 1; + } + else + break; + } + + n->m_iHeapLoc = -1; + } + + // the only event has been deleted, wake up immediately + if (0 == m_iLastEntry) + m_pTimer->interrupt(); +} + +// +CSndQueue::CSndQueue(): +m_WorkerThread(), +m_pSndUList(NULL), +m_pChannel(NULL), +m_pTimer(NULL), +m_WindowLock(), +m_WindowCond(), +m_bClosing(false), +m_ExitCond() +{ + #ifndef WIN32 + pthread_cond_init(&m_WindowCond, NULL); + pthread_mutex_init(&m_WindowLock, NULL); + #else + m_WindowLock = CreateMutex(NULL, false, NULL); + m_WindowCond = CreateEvent(NULL, false, false, NULL); + m_ExitCond = CreateEvent(NULL, false, false, NULL); + #endif +} + +CSndQueue::~CSndQueue() +{ + m_bClosing = true; + + #ifndef WIN32 + pthread_mutex_lock(&m_WindowLock); + pthread_cond_signal(&m_WindowCond); + pthread_mutex_unlock(&m_WindowLock); + if (0 != m_WorkerThread) + pthread_join(m_WorkerThread, NULL); + pthread_cond_destroy(&m_WindowCond); + pthread_mutex_destroy(&m_WindowLock); + #else + SetEvent(m_WindowCond); + if (NULL != m_WorkerThread) + WaitForSingleObject(m_ExitCond, INFINITE); + CloseHandle(m_WorkerThread); + CloseHandle(m_WindowLock); + CloseHandle(m_WindowCond); + CloseHandle(m_ExitCond); + #endif + + delete m_pSndUList; +} + +void CSndQueue::init(CChannel* c, CTimer* t) +{ + m_pChannel = c; + m_pTimer = t; + m_pSndUList = new CSndUList; + m_pSndUList->m_pWindowLock = &m_WindowLock; + m_pSndUList->m_pWindowCond = &m_WindowCond; + m_pSndUList->m_pTimer = m_pTimer; + + #ifndef WIN32 + if (0 != pthread_create(&m_WorkerThread, NULL, CSndQueue::worker, this)) + { + m_WorkerThread = 0; + throw CUDTException(3, 1); + } + #else + DWORD threadID; + m_WorkerThread = CreateThread(NULL, 0, CSndQueue::worker, this, 0, &threadID); + if (NULL == m_WorkerThread) + throw CUDTException(3, 1); + #endif +} + +#ifndef WIN32 + void* CSndQueue::worker(void* param) +#else + DWORD WINAPI CSndQueue::worker(LPVOID param) +#endif +{ + CSndQueue* self = (CSndQueue*)param; + + while (!self->m_bClosing) + { + uint64_t ts = self->m_pSndUList->getNextProcTime(); + + if (ts > 0) + { + // wait until next processing time of the first socket on the list + uint64_t currtime; + CTimer::rdtsc(currtime); + if (currtime < ts) + self->m_pTimer->sleepto(ts); + + // it is time to send the next pkt + sockaddr* addr; + CPacket pkt; + if (self->m_pSndUList->pop(addr, pkt) < 0) + continue; + + self->m_pChannel->sendto(addr, pkt); + } + else + { + // wait here if there is no sockets with data to be sent + #ifndef WIN32 + pthread_mutex_lock(&self->m_WindowLock); + if (!self->m_bClosing && (self->m_pSndUList->m_iLastEntry < 0)) + pthread_cond_wait(&self->m_WindowCond, &self->m_WindowLock); + pthread_mutex_unlock(&self->m_WindowLock); + #else + WaitForSingleObject(self->m_WindowCond, INFINITE); + #endif + } + } + + #ifndef WIN32 + return NULL; + #else + SetEvent(self->m_ExitCond); + return 0; + #endif +} + +int CSndQueue::sendto(const sockaddr* addr, CPacket& packet) +{ + // send out the packet immediately (high priority), this is a control packet + m_pChannel->sendto(addr, packet); + return packet.getLength(); +} + + +// +CRcvUList::CRcvUList(): +m_pUList(NULL), +m_pLast(NULL) +{ +} + +CRcvUList::~CRcvUList() +{ +} + +void CRcvUList::insert(const CUDT* u) +{ + CRNode* n = u->m_pRNode; + CTimer::rdtsc(n->m_llTimeStamp); + + if (NULL == m_pUList) + { + // empty list, insert as the single node + n->m_pPrev = n->m_pNext = NULL; + m_pLast = m_pUList = n; + + return; + } + + // always insert at the end for RcvUList + n->m_pPrev = m_pLast; + n->m_pNext = NULL; + m_pLast->m_pNext = n; + m_pLast = n; +} + +void CRcvUList::remove(const CUDT* u) +{ + CRNode* n = u->m_pRNode; + + if (!n->m_bOnList) + return; + + if (NULL == n->m_pPrev) + { + // n is the first node + m_pUList = n->m_pNext; + if (NULL == m_pUList) + m_pLast = NULL; + else + m_pUList->m_pPrev = NULL; + } + else + { + n->m_pPrev->m_pNext = n->m_pNext; + if (NULL == n->m_pNext) + { + // n is the last node + m_pLast = n->m_pPrev; + } + else + n->m_pNext->m_pPrev = n->m_pPrev; + } + + n->m_pNext = n->m_pPrev = NULL; +} + +void CRcvUList::update(const CUDT* u) +{ + CRNode* n = u->m_pRNode; + + if (!n->m_bOnList) + return; + + CTimer::rdtsc(n->m_llTimeStamp); + + // if n is the last node, do not need to change + if (NULL == n->m_pNext) + return; + + if (NULL == n->m_pPrev) + { + m_pUList = n->m_pNext; + m_pUList->m_pPrev = NULL; + } + else + { + n->m_pPrev->m_pNext = n->m_pNext; + n->m_pNext->m_pPrev = n->m_pPrev; + } + + n->m_pPrev = m_pLast; + n->m_pNext = NULL; + m_pLast->m_pNext = n; + m_pLast = n; +} + +// +CHash::CHash(): +m_pBucket(NULL), +m_iHashSize(0) +{ +} + +CHash::~CHash() +{ + for (int i = 0; i < m_iHashSize; ++ i) + { + CBucket* b = m_pBucket[i]; + while (NULL != b) + { + CBucket* n = b->m_pNext; + delete b; + b = n; + } + } + + delete [] m_pBucket; +} + +void CHash::init(int size) +{ + m_pBucket = new CBucket* [size]; + + for (int i = 0; i < size; ++ i) + m_pBucket[i] = NULL; + + m_iHashSize = size; +} + +CUDT* CHash::lookup(int32_t id) +{ + // simple hash function (% hash table size); suitable for socket descriptors + CBucket* b = m_pBucket[id % m_iHashSize]; + + while (NULL != b) + { + if (id == b->m_iID) + return b->m_pUDT; + b = b->m_pNext; + } + + return NULL; +} + +void CHash::insert(int32_t id, CUDT* u) +{ + CBucket* b = m_pBucket[id % m_iHashSize]; + + CBucket* n = new CBucket; + n->m_iID = id; + n->m_pUDT = u; + n->m_pNext = b; + + m_pBucket[id % m_iHashSize] = n; +} + +void CHash::remove(int32_t id) +{ + CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* p = NULL; + + while (NULL != b) + { + if (id == b->m_iID) + { + if (NULL == p) + m_pBucket[id % m_iHashSize] = b->m_pNext; + else + p->m_pNext = b->m_pNext; + + delete b; + + return; + } + + p = b; + b = b->m_pNext; + } +} + + +// +CRendezvousQueue::CRendezvousQueue(): +m_lRendezvousID(), +m_RIDVectorLock() +{ + #ifndef WIN32 + pthread_mutex_init(&m_RIDVectorLock, NULL); + #else + m_RIDVectorLock = CreateMutex(NULL, false, NULL); + #endif +} + +CRendezvousQueue::~CRendezvousQueue() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_RIDVectorLock); + #else + CloseHandle(m_RIDVectorLock); + #endif + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in*)i->m_pPeerAddr; + else + delete (sockaddr_in6*)i->m_pPeerAddr; + } + + m_lRendezvousID.clear(); +} + +void CRendezvousQueue::insert(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl) +{ + CGuard vg(m_RIDVectorLock); + + CRL r; + r.m_iID = id; + r.m_pUDT = u; + r.m_iIPversion = ipv; + r.m_pPeerAddr = (AF_INET == ipv) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; + memcpy(r.m_pPeerAddr, addr, (AF_INET == ipv) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + r.m_ullTTL = ttl; + + m_lRendezvousID.push_back(r); +} + +void CRendezvousQueue::remove(const UDTSOCKET& id) +{ + CGuard vg(m_RIDVectorLock); + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + if (i->m_iID == id) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in*)i->m_pPeerAddr; + else + delete (sockaddr_in6*)i->m_pPeerAddr; + + m_lRendezvousID.erase(i); + + return; + } + } +} + +CUDT* CRendezvousQueue::retrieve(const sockaddr* addr, UDTSOCKET& id) +{ + CGuard vg(m_RIDVectorLock); + + // TODO: optimize search + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + if (CIPAddress::ipcmp(addr, i->m_pPeerAddr, i->m_iIPversion) && ((0 == id) || (id == i->m_iID))) + { + id = i->m_iID; + return i->m_pUDT; + } + } + + return NULL; +} + +void CRendezvousQueue::updateConnStatus() +{ + if (m_lRendezvousID.empty()) + return; + + CGuard vg(m_RIDVectorLock); + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + // avoid sending too many requests, at most 1 request per 250ms + if (CTimer::getTime() - i->m_pUDT->m_llLastReqTime > 250000) + { + if (CTimer::getTime() >= i->m_ullTTL) + { + // connection timer expired, acknowledge app via epoll + i->m_pUDT->m_bConnecting = false; + CUDT::s_UDTUnited.m_EPoll.update_events(i->m_iID, i->m_pUDT->m_sPollID, UDT_EPOLL_ERR, true); + continue; + } + + CPacket request; + char* reqdata = new char [i->m_pUDT->m_iPayloadSize]; + request.pack(0, NULL, reqdata, i->m_pUDT->m_iPayloadSize); + // ID = 0, connection request + request.m_iID = !i->m_pUDT->m_bRendezvous ? 0 : i->m_pUDT->m_ConnRes.m_iID; + int hs_size = i->m_pUDT->m_iPayloadSize; + i->m_pUDT->m_ConnReq.serialize(reqdata, hs_size); + request.setLength(hs_size); + i->m_pUDT->m_pSndQueue->sendto(i->m_pPeerAddr, request); + i->m_pUDT->m_llLastReqTime = CTimer::getTime(); + delete [] reqdata; + } + } +} + +// +CRcvQueue::CRcvQueue(): +m_WorkerThread(), +m_UnitQueue(), +m_pRcvUList(NULL), +m_pHash(NULL), +m_pChannel(NULL), +m_pTimer(NULL), +m_iPayloadSize(), +m_bClosing(false), +m_ExitCond(), +m_LSLock(), +m_pListener(NULL), +m_pRendezvousQueue(NULL), +m_vNewEntry(), +m_IDLock(), +m_mBuffer(), +m_PassLock(), +m_PassCond() +{ + #ifndef WIN32 + pthread_mutex_init(&m_PassLock, NULL); + pthread_cond_init(&m_PassCond, NULL); + pthread_mutex_init(&m_LSLock, NULL); + pthread_mutex_init(&m_IDLock, NULL); + #else + m_PassLock = CreateMutex(NULL, false, NULL); + m_PassCond = CreateEvent(NULL, false, false, NULL); + m_LSLock = CreateMutex(NULL, false, NULL); + m_IDLock = CreateMutex(NULL, false, NULL); + m_ExitCond = CreateEvent(NULL, false, false, NULL); + #endif +} + +CRcvQueue::~CRcvQueue() +{ + m_bClosing = true; + + #ifndef WIN32 + if (0 != m_WorkerThread) + pthread_join(m_WorkerThread, NULL); + pthread_mutex_destroy(&m_PassLock); + pthread_cond_destroy(&m_PassCond); + pthread_mutex_destroy(&m_LSLock); + pthread_mutex_destroy(&m_IDLock); + #else + if (NULL != m_WorkerThread) + WaitForSingleObject(m_ExitCond, INFINITE); + CloseHandle(m_WorkerThread); + CloseHandle(m_PassLock); + CloseHandle(m_PassCond); + CloseHandle(m_LSLock); + CloseHandle(m_IDLock); + CloseHandle(m_ExitCond); + #endif + + delete m_pRcvUList; + delete m_pHash; + delete m_pRendezvousQueue; + + // remove all queued messages + for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++ i) + { + while (!i->second.empty()) + { + CPacket* pkt = i->second.front(); + delete [] pkt->m_pcData; + delete pkt; + i->second.pop(); + } + } +} + +void CRcvQueue::init(int qsize, int payload, int version, int hsize, CChannel* cc, CTimer* t) +{ + m_iPayloadSize = payload; + + m_UnitQueue.init(qsize, payload, version); + + m_pHash = new CHash; + m_pHash->init(hsize); + + m_pChannel = cc; + m_pTimer = t; + + m_pRcvUList = new CRcvUList; + m_pRendezvousQueue = new CRendezvousQueue; + + #ifndef WIN32 + if (0 != pthread_create(&m_WorkerThread, NULL, CRcvQueue::worker, this)) + { + m_WorkerThread = 0; + throw CUDTException(3, 1); + } + #else + DWORD threadID; + m_WorkerThread = CreateThread(NULL, 0, CRcvQueue::worker, this, 0, &threadID); + if (NULL == m_WorkerThread) + throw CUDTException(3, 1); + #endif +} + +#ifndef WIN32 + void* CRcvQueue::worker(void* param) +#else + DWORD WINAPI CRcvQueue::worker(LPVOID param) +#endif +{ + CRcvQueue* self = (CRcvQueue*)param; + + sockaddr* addr = (AF_INET == self->m_UnitQueue.m_iIPversion) ? (sockaddr*) new sockaddr_in : (sockaddr*) new sockaddr_in6; + CUDT* u = NULL; + int32_t id; + + while (!self->m_bClosing) + { + #ifdef NO_BUSY_WAITING + self->m_pTimer->tick(); + #endif + + // check waiting list, if new socket, insert it to the list + while (self->ifNewEntry()) + { + CUDT* ne = self->getNewEntry(); + if (NULL != ne) + { + self->m_pRcvUList->insert(ne); + self->m_pHash->insert(ne->m_SocketID, ne); + } + } + + // find next available slot for incoming packet + CUnit* unit = self->m_UnitQueue.getNextAvailUnit(); + if (NULL == unit) + { + // no space, skip this packet + CPacket temp; + temp.m_pcData = new char[self->m_iPayloadSize]; + temp.setLength(self->m_iPayloadSize); + self->m_pChannel->recvfrom(addr, temp); + delete [] temp.m_pcData; + goto TIMER_CHECK; + } + + unit->m_Packet.setLength(self->m_iPayloadSize); + + // reading next incoming packet, recvfrom returns -1 is nothing has been received + if (self->m_pChannel->recvfrom(addr, unit->m_Packet) < 0) + goto TIMER_CHECK; + + id = unit->m_Packet.m_iID; + + // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets + if (0 == id) + { + if (NULL != self->m_pListener) + self->m_pListener->listen(addr, unit->m_Packet); + else if (NULL != (u = self->m_pRendezvousQueue->retrieve(addr, id))) + { + // asynchronous connect: call connect here + // otherwise wait for the UDT socket to retrieve this packet + if (!u->m_bSynRecving) + u->connect(unit->m_Packet); + else + self->storePkt(id, unit->m_Packet.clone()); + } + } + else if (id > 0) + { + if (NULL != (u = self->m_pHash->lookup(id))) + { + if (CIPAddress::ipcmp(addr, u->m_pPeerAddr, u->m_iIPversion)) + { + if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) + { + if (0 == unit->m_Packet.getFlag()) + u->processData(unit); + else + u->processCtrl(unit->m_Packet); + + u->checkTimers(); + self->m_pRcvUList->update(u); + } + } + } + else if (NULL != (u = self->m_pRendezvousQueue->retrieve(addr, id))) + { + if (!u->m_bSynRecving) + u->connect(unit->m_Packet); + else + self->storePkt(id, unit->m_Packet.clone()); + } + } + +TIMER_CHECK: + // take care of the timing event for all UDT sockets + + uint64_t currtime; + CTimer::rdtsc(currtime); + + CRNode* ul = self->m_pRcvUList->m_pUList; + uint64_t ctime = currtime - 100000 * CTimer::getCPUFrequency(); + while ((NULL != ul) && (ul->m_llTimeStamp < ctime)) + { + CUDT* u = ul->m_pUDT; + + if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) + { + u->checkTimers(); + self->m_pRcvUList->update(u); + } + else + { + // the socket must be removed from Hash table first, then RcvUList + self->m_pHash->remove(u->m_SocketID); + self->m_pRcvUList->remove(u); + u->m_pRNode->m_bOnList = false; + } + + ul = self->m_pRcvUList->m_pUList; + } + + // Check connection requests status for all sockets in the RendezvousQueue. + self->m_pRendezvousQueue->updateConnStatus(); + } + + if (AF_INET == self->m_UnitQueue.m_iIPversion) + delete (sockaddr_in*)addr; + else + delete (sockaddr_in6*)addr; + + #ifndef WIN32 + return NULL; + #else + SetEvent(self->m_ExitCond); + return 0; + #endif +} + +int CRcvQueue::recvfrom(int32_t id, CPacket& packet) +{ + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + + if (i == m_mBuffer.end()) + { + #ifndef WIN32 + uint64_t now = CTimer::getTime(); + timespec timeout; + + timeout.tv_sec = now / 1000000 + 1; + timeout.tv_nsec = (now % 1000000) * 1000; + + pthread_cond_timedwait(&m_PassCond, &m_PassLock, &timeout); + #else + ReleaseMutex(m_PassLock); + WaitForSingleObject(m_PassCond, 1000); + WaitForSingleObject(m_PassLock, INFINITE); + #endif + + i = m_mBuffer.find(id); + if (i == m_mBuffer.end()) + { + packet.setLength(-1); + return -1; + } + } + + // retrieve the earliest packet + CPacket* newpkt = i->second.front(); + + if (packet.getLength() < newpkt->getLength()) + { + packet.setLength(-1); + return -1; + } + + // copy packet content + memcpy(packet.m_nHeader, newpkt->m_nHeader, CPacket::m_iPktHdrSize); + memcpy(packet.m_pcData, newpkt->m_pcData, newpkt->getLength()); + packet.setLength(newpkt->getLength()); + + delete [] newpkt->m_pcData; + delete newpkt; + + // remove this message from queue, + // if no more messages left for this socket, release its data structure + i->second.pop(); + if (i->second.empty()) + m_mBuffer.erase(i); + + return packet.getLength(); +} + +int CRcvQueue::setListener(CUDT* u) +{ + CGuard lslock(m_LSLock); + + if (NULL != m_pListener) + return -1; + + m_pListener = u; + return 0; +} + +void CRcvQueue::removeListener(const CUDT* u) +{ + CGuard lslock(m_LSLock); + + if (u == m_pListener) + m_pListener = NULL; +} + +void CRcvQueue::registerConnector(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl) +{ + m_pRendezvousQueue->insert(id, u, ipv, addr, ttl); +} + +void CRcvQueue::removeConnector(const UDTSOCKET& id) +{ + m_pRendezvousQueue->remove(id); + + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + if (i != m_mBuffer.end()) + { + while (!i->second.empty()) + { + delete [] i->second.front()->m_pcData; + delete i->second.front(); + i->second.pop(); + } + m_mBuffer.erase(i); + } +} + +void CRcvQueue::setNewEntry(CUDT* u) +{ + CGuard listguard(m_IDLock); + m_vNewEntry.push_back(u); +} + +bool CRcvQueue::ifNewEntry() +{ + return !(m_vNewEntry.empty()); +} + +CUDT* CRcvQueue::getNewEntry() +{ + CGuard listguard(m_IDLock); + + if (m_vNewEntry.empty()) + return NULL; + + CUDT* u = (CUDT*)*(m_vNewEntry.begin()); + m_vNewEntry.erase(m_vNewEntry.begin()); + + return u; +} + +void CRcvQueue::storePkt(int32_t id, CPacket* pkt) +{ + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + + if (i == m_mBuffer.end()) + { + m_mBuffer[id].push(pkt); + + #ifndef WIN32 + pthread_cond_signal(&m_PassCond); + #else + SetEvent(m_PassCond); + #endif + } + else + { + //avoid storing too many packets, in case of malfunction or attack + if (i->second.size() > 16) + return; + + i->second.push(pkt); + } +} diff --git a/vendor/udt4/src/queue.h b/vendor/udt4/src/queue.h new file mode 100644 index 000000000..9feff18d1 --- /dev/null +++ b/vendor/udt4/src/queue.h @@ -0,0 +1,527 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/12/2011 +*****************************************************************************/ + + +#ifndef __UDT_QUEUE_H__ +#define __UDT_QUEUE_H__ + +#include "channel.h" +#include "common.h" +#include "packet.h" +#include +#include +#include +#include + +class CUDT; + +struct CUnit +{ + CPacket m_Packet; // packet + int m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped +}; + +class CUnitQueue +{ +friend class CRcvQueue; +friend class CRcvBuffer; + +public: + CUnitQueue(); + ~CUnitQueue(); + +public: + + // Functionality: + // Initialize the unit queue. + // Parameters: + // 1) [in] size: queue size + // 2) [in] mss: maximum segament size + // 3) [in] version: IP version + // Returned value: + // 0: success, -1: failure. + + int init(int size, int mss, int version); + + // Functionality: + // Increase (double) the unit queue size. + // Parameters: + // None. + // Returned value: + // 0: success, -1: failure. + + int increase(); + + // Functionality: + // Decrease (halve) the unit queue size. + // Parameters: + // None. + // Returned value: + // 0: success, -1: failure. + + int shrink(); + + // Functionality: + // find an available unit for incoming packet. + // Parameters: + // None. + // Returned value: + // Pointer to the available unit, NULL if not found. + + CUnit* getNextAvailUnit(); + +private: + struct CQEntry + { + CUnit* m_pUnit; // unit queue + char* m_pBuffer; // data buffer + int m_iSize; // size of each queue + + CQEntry* m_pNext; + } + *m_pQEntry, // pointer to the first unit queue + *m_pCurrQueue, // pointer to the current available queue + *m_pLastQueue; // pointer to the last unit queue + + CUnit* m_pAvailUnit; // recent available unit + + int m_iSize; // total size of the unit queue, in number of packets + int m_iCount; // total number of valid packets in the queue + + int m_iMSS; // unit buffer size + int m_iIPversion; // IP version + +private: + CUnitQueue(const CUnitQueue&); + CUnitQueue& operator=(const CUnitQueue&); +}; + +struct CSNode +{ + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + uint64_t m_llTimeStamp; // Time Stamp + + int m_iHeapLoc; // location on the heap, -1 means not on the heap +}; + +class CSndUList +{ +friend class CSndQueue; + +public: + CSndUList(); + ~CSndUList(); + +public: + + // Functionality: + // Insert a new UDT instance into the list. + // Parameters: + // 1) [in] ts: time stamp: next processing time + // 2) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void insert(int64_t ts, const CUDT* u); + + // Functionality: + // Update the timestamp of the UDT instance on the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // 2) [in] resechedule: if the timestampe shoudl be rescheduled + // Returned value: + // None. + + void update(const CUDT* u, bool reschedule = true); + + // Functionality: + // Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. + // Parameters: + // 0) [out] addr: destination address of the next packet + // 1) [out] pkt: the next packet to be sent + // Returned value: + // 1 if successfully retrieved, -1 if no packet found. + + int pop(sockaddr*& addr, CPacket& pkt); + + // Functionality: + // Remove UDT instance from the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void remove(const CUDT* u); + + // Functionality: + // Retrieve the next scheduled processing time. + // Parameters: + // None. + // Returned value: + // Scheduled processing time of the first UDT socket in the list. + + uint64_t getNextProcTime(); + +private: + void insert_(int64_t ts, const CUDT* u); + void remove_(const CUDT* u); + +private: + CSNode** m_pHeap; // The heap array + int m_iArrayLength; // physical length of the array + int m_iLastEntry; // position of last entry on the heap array + + pthread_mutex_t m_ListLock; + + pthread_mutex_t* m_pWindowLock; + pthread_cond_t* m_pWindowCond; + + CTimer* m_pTimer; + +private: + CSndUList(const CSndUList&); + CSndUList& operator=(const CSndUList&); +}; + +struct CRNode +{ + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + uint64_t m_llTimeStamp; // Time Stamp + + CRNode* m_pPrev; // previous link + CRNode* m_pNext; // next link + + bool m_bOnList; // if the node is already on the list +}; + +class CRcvUList +{ +public: + CRcvUList(); + ~CRcvUList(); + +public: + + // Functionality: + // Insert a new UDT instance to the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void insert(const CUDT* u); + + // Functionality: + // Remove the UDT instance from the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void remove(const CUDT* u); + + // Functionality: + // Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void update(const CUDT* u); + +public: + CRNode* m_pUList; // the head node + +private: + CRNode* m_pLast; // the last node + +private: + CRcvUList(const CRcvUList&); + CRcvUList& operator=(const CRcvUList&); +}; + +class CHash +{ +public: + CHash(); + ~CHash(); + +public: + + // Functionality: + // Initialize the hash table. + // Parameters: + // 1) [in] size: hash table size + // Returned value: + // None. + + void init(int size); + + // Functionality: + // Look for a UDT instance from the hash table. + // Parameters: + // 1) [in] id: socket ID + // Returned value: + // Pointer to a UDT instance, or NULL if not found. + + CUDT* lookup(int32_t id); + + // Functionality: + // Insert an entry to the hash table. + // Parameters: + // 1) [in] id: socket ID + // 2) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void insert(int32_t id, CUDT* u); + + // Functionality: + // Remove an entry from the hash table. + // Parameters: + // 1) [in] id: socket ID + // Returned value: + // None. + + void remove(int32_t id); + +private: + struct CBucket + { + int32_t m_iID; // Socket ID + CUDT* m_pUDT; // Socket instance + + CBucket* m_pNext; // next bucket + } **m_pBucket; // list of buckets (the hash table) + + int m_iHashSize; // size of hash table + +private: + CHash(const CHash&); + CHash& operator=(const CHash&); +}; + +class CRendezvousQueue +{ +public: + CRendezvousQueue(); + ~CRendezvousQueue(); + +public: + void insert(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); + void remove(const UDTSOCKET& id); + CUDT* retrieve(const sockaddr* addr, UDTSOCKET& id); + + void updateConnStatus(); + +private: + struct CRL + { + UDTSOCKET m_iID; // UDT socket ID (self) + CUDT* m_pUDT; // UDT instance + int m_iIPversion; // IP version + sockaddr* m_pPeerAddr; // UDT sonnection peer address + uint64_t m_ullTTL; // the time that this request expires + }; + std::list m_lRendezvousID; // The sockets currently in rendezvous mode + + pthread_mutex_t m_RIDVectorLock; +}; + +class CSndQueue +{ +friend class CUDT; +friend class CUDTUnited; + +public: + CSndQueue(); + ~CSndQueue(); + +public: + + // Functionality: + // Initialize the sending queue. + // Parameters: + // 1) [in] c: UDP channel to be associated to the queue + // 2) [in] t: Timer + // Returned value: + // None. + + void init(CChannel* c, CTimer* t); + + // Functionality: + // Send out a packet to a given address. + // Parameters: + // 1) [in] addr: destination address + // 2) [in] packet: packet to be sent out + // Returned value: + // Size of data sent out. + + int sendto(const sockaddr* addr, CPacket& packet); + +private: +#ifndef WIN32 + static void* worker(void* param); +#else + static DWORD WINAPI worker(LPVOID param); +#endif + + pthread_t m_WorkerThread; + +private: + CSndUList* m_pSndUList; // List of UDT instances for data sending + CChannel* m_pChannel; // The UDP channel for data sending + CTimer* m_pTimer; // Timing facility + + pthread_mutex_t m_WindowLock; + pthread_cond_t m_WindowCond; + + volatile bool m_bClosing; // closing the worker + pthread_cond_t m_ExitCond; + +private: + CSndQueue(const CSndQueue&); + CSndQueue& operator=(const CSndQueue&); +}; + +class CRcvQueue +{ +friend class CUDT; +friend class CUDTUnited; + +public: + CRcvQueue(); + ~CRcvQueue(); + +public: + + // Functionality: + // Initialize the receiving queue. + // Parameters: + // 1) [in] size: queue size + // 2) [in] mss: maximum packet size + // 3) [in] version: IP version + // 4) [in] hsize: hash table size + // 5) [in] c: UDP channel to be associated to the queue + // 6) [in] t: timer + // Returned value: + // None. + + void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t); + + // Functionality: + // Read a packet for a specific UDT socket id. + // Parameters: + // 1) [in] id: Socket ID + // 2) [out] packet: received packet + // Returned value: + // Data size of the packet + + int recvfrom(int32_t id, CPacket& packet); + +private: +#ifndef WIN32 + static void* worker(void* param); +#else + static DWORD WINAPI worker(LPVOID param); +#endif + + pthread_t m_WorkerThread; + +private: + CUnitQueue m_UnitQueue; // The received packet queue + + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + CTimer* m_pTimer; // shared timer with the snd queue + + int m_iPayloadSize; // packet payload size + + volatile bool m_bClosing; // closing the workder + pthread_cond_t m_ExitCond; + +private: + int setListener(CUDT* u); + void removeListener(const CUDT* u); + + void registerConnector(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); + void removeConnector(const UDTSOCKET& id); + + void setNewEntry(CUDT* u); + bool ifNewEntry(); + CUDT* getNewEntry(); + + void storePkt(int32_t id, CPacket* pkt); + +private: + pthread_mutex_t m_LSLock; + CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity + CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + + std::vector m_vNewEntry; // newly added entries, to be inserted + pthread_mutex_t m_IDLock; + + std::map > m_mBuffer; // temporary buffer for rendezvous connection request + pthread_mutex_t m_PassLock; + pthread_cond_t m_PassCond; + +private: + CRcvQueue(const CRcvQueue&); + CRcvQueue& operator=(const CRcvQueue&); +}; + +struct CMultiplexer +{ + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + CTimer* m_pTimer; // The timer + + int m_iPort; // The UDP port number of this multiplexer + int m_iIPversion; // IP version + int m_iMSS; // Maximum Segment Size + int m_iRefCount; // number of UDT instances that are associated with this multiplexer + bool m_bReusable; // if this one can be shared with others + + int m_iID; // multiplexer ID +}; + +#endif diff --git a/vendor/udt4/src/udt.h b/vendor/udt4/src/udt.h new file mode 100644 index 000000000..7dc75a3d8 --- /dev/null +++ b/vendor/udt4/src/udt.h @@ -0,0 +1,359 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/18/2011 +*****************************************************************************/ + +#ifndef __UDT_H__ +#define __UDT_H__ + + +#ifndef WIN32 + #include + #include + #include +#else + #ifdef __MINGW__ + #include + #include + #endif + #include +#endif +#include +#include +#include +#include + +#include + + +//////////////////////////////////////////////////////////////////////////////// + +//if compiling on VC6.0 or pre-WindowsXP systems +//use -DLEGACY_WIN32 + +//if compiling with MinGW, it only works on XP or above +//use -D_WIN32_WINNT=0x0501 + + +#ifdef WIN32 + #if !defined(__MINGW__) && defined(UDT_IS_DLL) + // Explicitly define 32-bit and 64-bit numbers + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + #ifndef LEGACY_WIN32 + typedef unsigned __int64 uint64_t; + #else + // VC 6.0 does not support unsigned __int64: may cause potential problems. + typedef __int64 uint64_t; + #endif + + #ifdef UDT_EXPORTS + #define UDT_API __declspec(dllexport) + #else + #define UDT_API __declspec(dllimport) + #endif + #else + #define UDT_API + #endif +#else + #define UDT_API __attribute__ ((visibility("default"))) +#endif + +#define NO_BUSY_WAITING + +#ifdef WIN32 + #ifndef __MINGW__ + typedef SOCKET SYSSOCKET; + #else + typedef int SYSSOCKET; + #endif +#else + typedef int SYSSOCKET; +#endif + +typedef SYSSOCKET UDPSOCKET; +typedef int UDTSOCKET; + +//////////////////////////////////////////////////////////////////////////////// + +typedef std::set ud_set; +#define UD_CLR(u, uset) ((uset)->erase(u)) +#define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end()) +#define UD_SET(u, uset) ((uset)->insert(u)) +#define UD_ZERO(uset) ((uset)->clear()) + +enum EPOLLOpt +{ + // this values are defined same as linux epoll.h + // so that if system values are used by mistake, they should have the same effect + UDT_EPOLL_IN = 0x1, + UDT_EPOLL_OUT = 0x4, + UDT_EPOLL_ERR = 0x8 +}; + +enum UDTSTATUS {INIT = 1, OPENED, LISTENING, CONNECTING, CONNECTED, BROKEN, CLOSING, CLOSED, NONEXIST}; + +//////////////////////////////////////////////////////////////////////////////// + +enum UDTOpt +{ + UDT_MSS, // the Maximum Transfer Unit + UDT_SNDSYN, // if sending is blocking + UDT_RCVSYN, // if receiving is blocking + UDT_CC, // custom congestion control algorithm + UDT_FC, // Flight flag size (window size) + UDT_SNDBUF, // maximum buffer in sending queue + UDT_RCVBUF, // UDT receiving buffer size + UDT_LINGER, // waiting for unsent data when closing + UDP_SNDBUF, // UDP sending buffer size + UDP_RCVBUF, // UDP receiving buffer size + UDT_MAXMSG, // maximum datagram message size + UDT_MSGTTL, // time-to-live of a datagram message + UDT_RENDEZVOUS, // rendezvous connection mode + UDT_SNDTIMEO, // send() timeout + UDT_RCVTIMEO, // recv() timeout + UDT_REUSEADDR, // reuse an existing port or create a new one + UDT_MAXBW, // maximum bandwidth (bytes per second) that the connection can use + UDT_STATE, // current socket state, see UDTSTATUS, read only + UDT_EVENT, // current avalable events associated with the socket + UDT_SNDDATA, // size of data in the sending buffer + UDT_RCVDATA // size of data available for recv +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CPerfMon +{ + // global measurements + int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds + int64_t pktSentTotal; // total number of sent data packets, including retransmissions + int64_t pktRecvTotal; // total number of received packets + int pktSndLossTotal; // total number of lost packets (sender side) + int pktRcvLossTotal; // total number of lost packets (receiver side) + int pktRetransTotal; // total number of retransmitted packets + int pktSentACKTotal; // total number of sent ACK packets + int pktRecvACKTotal; // total number of received ACK packets + int pktSentNAKTotal; // total number of sent NAK packets + int pktRecvNAKTotal; // total number of received NAK packets + int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive) + + // local measurements + int64_t pktSent; // number of sent data packets, including retransmissions + int64_t pktRecv; // number of received packets + int pktSndLoss; // number of lost packets (sender side) + int pktRcvLoss; // number of lost packets (receiver side) + int pktRetrans; // number of retransmitted packets + int pktSentACK; // number of sent ACK packets + int pktRecvACK; // number of received ACK packets + int pktSentNAK; // number of sent NAK packets + int pktRecvNAK; // number of received NAK packets + double mbpsSendRate; // sending rate in Mb/s + double mbpsRecvRate; // receiving rate in Mb/s + int64_t usSndDuration; // busy sending time (i.e., idle time exclusive) + + // instant measurements + double usPktSndPeriod; // packet sending period, in microseconds + int pktFlowWindow; // flow window size, in number of packets + int pktCongestionWindow; // congestion window size, in number of packets + int pktFlightSize; // number of packets on flight + double msRTT; // RTT, in milliseconds + double mbpsBandwidth; // estimated bandwidth, in Mb/s + int byteAvailSndBuf; // available UDT sender buffer size + int byteAvailRcvBuf; // available UDT receiver buffer size +}; + +//////////////////////////////////////////////////////////////////////////////// + +class UDT_API CUDTException +{ +public: + CUDTException(int major = 0, int minor = 0, int err = -1); + CUDTException(const CUDTException& e); + virtual ~CUDTException(); + + // Functionality: + // Get the description of the exception. + // Parameters: + // None. + // Returned value: + // Text message for the exception description. + + virtual const char* getErrorMessage(); + + // Functionality: + // Get the system errno for the exception. + // Parameters: + // None. + // Returned value: + // errno. + + virtual int getErrorCode() const; + + // Functionality: + // Clear the error code. + // Parameters: + // None. + // Returned value: + // None. + + virtual void clear(); + +private: + int m_iMajor; // major exception categories + +// 0: correct condition +// 1: network setup exception +// 2: network connection broken +// 3: memory exception +// 4: file exception +// 5: method not supported +// 6+: undefined error + + int m_iMinor; // for specific error reasons + int m_iErrno; // errno returned by the system if there is any + std::string m_strMsg; // text error message + + std::string m_strAPI; // the name of UDT function that returns the error + std::string m_strDebug; // debug information, set to the original place that causes the error + +public: // Error Code + static const int SUCCESS; + static const int ECONNSETUP; + static const int ENOSERVER; + static const int ECONNREJ; + static const int ESOCKFAIL; + static const int ESECFAIL; + static const int ECONNFAIL; + static const int ECONNLOST; + static const int ENOCONN; + static const int ERESOURCE; + static const int ETHREAD; + static const int ENOBUF; + static const int EFILE; + static const int EINVRDOFF; + static const int ERDPERM; + static const int EINVWROFF; + static const int EWRPERM; + static const int EINVOP; + static const int EBOUNDSOCK; + static const int ECONNSOCK; + static const int EINVPARAM; + static const int EINVSOCK; + static const int EUNBOUNDSOCK; + static const int ENOLISTEN; + static const int ERDVNOSERV; + static const int ERDVUNBOUND; + static const int ESTREAMILL; + static const int EDGRAMILL; + static const int EDUPLISTEN; + static const int ELARGEMSG; + static const int EINVPOLLID; + static const int EASYNCFAIL; + static const int EASYNCSND; + static const int EASYNCRCV; + static const int ETIMEOUT; + static const int EPEERERR; + static const int EUNKNOWN; +}; + +//////////////////////////////////////////////////////////////////////////////// + +// If you need to export these APIs to be used by a different language, +// declare extern "C" for them, and add a "udt_" prefix to each API. +// The following APIs: sendfile(), recvfile(), epoll_wait(), geterrormsg(), +// include C++ specific feature, please use the corresponding sendfile2(), etc. + +namespace UDT +{ + +typedef CUDTException ERRORINFO; +typedef UDTOpt SOCKOPT; +typedef CPerfMon TRACEINFO; +typedef ud_set UDSET; + +UDT_API extern const UDTSOCKET INVALID_SOCK; +#undef ERROR +UDT_API extern const int ERROR; + +UDT_API int startup(); +UDT_API int cleanup(); +UDT_API UDTSOCKET socket(int af, int type, int protocol); +UDT_API int bind(UDTSOCKET u, const struct sockaddr* name, int namelen); +UDT_API int bind2(UDTSOCKET u, UDPSOCKET udpsock); +UDT_API int listen(UDTSOCKET u, int backlog); +UDT_API UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen); +UDT_API int connect(UDTSOCKET u, const struct sockaddr* name, int namelen); +UDT_API int close(UDTSOCKET u); +UDT_API int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen); +UDT_API int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen); +UDT_API int getsockopt(UDTSOCKET u, int level, SOCKOPT optname, void* optval, int* optlen); +UDT_API int setsockopt(UDTSOCKET u, int level, SOCKOPT optname, const void* optval, int optlen); +UDT_API int send(UDTSOCKET u, const char* buf, int len, int flags); +UDT_API int recv(UDTSOCKET u, char* buf, int len, int flags); +UDT_API int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false); +UDT_API int recvmsg(UDTSOCKET u, char* buf, int len); +UDT_API int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); +UDT_API int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); +UDT_API int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000); +UDT_API int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000); + +// select and selectEX are DEPRECATED; please use epoll. +UDT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout); +UDT_API int selectEx(const std::vector& fds, std::vector* readfds, + std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + +UDT_API int epoll_create(); +UDT_API int epoll_add_usock(int eid, UDTSOCKET u, const int* events = NULL); +UDT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL); +UDT_API int epoll_remove_usock(int eid, UDTSOCKET u); +UDT_API int epoll_remove_ssock(int eid, SYSSOCKET s); +UDT_API int epoll_wait(int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, + std::set* lrfds = NULL, std::set* wrfds = NULL); +UDT_API int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, + SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL); +UDT_API int epoll_release(int eid); +UDT_API ERRORINFO& getlasterror(); +UDT_API int getlasterror_code(); +UDT_API const char* getlasterror_desc(); +UDT_API int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear = true); +UDT_API UDTSTATUS getsockstate(UDTSOCKET u); + +} // namespace UDT + +#endif diff --git a/vendor/udt4/src/window.cpp b/vendor/udt4/src/window.cpp new file mode 100644 index 000000000..bca37e908 --- /dev/null +++ b/vendor/udt4/src/window.cpp @@ -0,0 +1,286 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/22/2011 +*****************************************************************************/ + +#include +#include "common.h" +#include "window.h" +#include + +using namespace std; + +CACKWindow::CACKWindow(int size): +m_piACKSeqNo(NULL), +m_piACK(NULL), +m_pTimeStamp(NULL), +m_iSize(size), +m_iHead(0), +m_iTail(0) +{ + m_piACKSeqNo = new int32_t[m_iSize]; + m_piACK = new int32_t[m_iSize]; + m_pTimeStamp = new uint64_t[m_iSize]; + + m_piACKSeqNo[0] = -1; +} + +CACKWindow::~CACKWindow() +{ + delete [] m_piACKSeqNo; + delete [] m_piACK; + delete [] m_pTimeStamp; +} + +void CACKWindow::store(int32_t seq, int32_t ack) +{ + m_piACKSeqNo[m_iHead] = seq; + m_piACK[m_iHead] = ack; + m_pTimeStamp[m_iHead] = CTimer::getTime(); + + m_iHead = (m_iHead + 1) % m_iSize; + + // overwrite the oldest ACK since it is not likely to be acknowledged + if (m_iHead == m_iTail) + m_iTail = (m_iTail + 1) % m_iSize; +} + +int CACKWindow::acknowledge(int32_t seq, int32_t& ack) +{ + if (m_iHead >= m_iTail) + { + // Head has not exceeded the physical boundary of the window + + for (int i = m_iTail, n = m_iHead; i < n; ++ i) + { + // looking for indentical ACK Seq. No. + if (seq == m_piACKSeqNo[i]) + { + // return the Data ACK it carried + ack = m_piACK[i]; + + // calculate RTT + int rtt = int(CTimer::getTime() - m_pTimeStamp[i]); + + if (i + 1 == m_iHead) + { + m_iTail = m_iHead = 0; + m_piACKSeqNo[0] = -1; + } + else + m_iTail = (i + 1) % m_iSize; + + return rtt; + } + } + + // Bad input, the ACK node has been overwritten + return -1; + } + + // Head has exceeded the physical window boundary, so it is behind tail + for (int j = m_iTail, n = m_iHead + m_iSize; j < n; ++ j) + { + // looking for indentical ACK seq. no. + if (seq == m_piACKSeqNo[j % m_iSize]) + { + // return Data ACK + j %= m_iSize; + ack = m_piACK[j]; + + // calculate RTT + int rtt = int(CTimer::getTime() - m_pTimeStamp[j]); + + if (j == m_iHead) + { + m_iTail = m_iHead = 0; + m_piACKSeqNo[0] = -1; + } + else + m_iTail = (j + 1) % m_iSize; + + return rtt; + } + } + + // bad input, the ACK node has been overwritten + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// + +CPktTimeWindow::CPktTimeWindow(int asize, int psize): +m_iAWSize(asize), +m_piPktWindow(NULL), +m_iPktWindowPtr(0), +m_iPWSize(psize), +m_piProbeWindow(NULL), +m_iProbeWindowPtr(0), +m_iLastSentTime(0), +m_iMinPktSndInt(1000000), +m_LastArrTime(), +m_CurrArrTime(), +m_ProbeTime() +{ + m_piPktWindow = new int[m_iAWSize]; + m_piPktReplica = new int[m_iAWSize]; + m_piProbeWindow = new int[m_iPWSize]; + m_piProbeReplica = new int[m_iPWSize]; + + m_LastArrTime = CTimer::getTime(); + + for (int i = 0; i < m_iAWSize; ++ i) + m_piPktWindow[i] = 1000000; + + for (int k = 0; k < m_iPWSize; ++ k) + m_piProbeWindow[k] = 1000; +} + +CPktTimeWindow::~CPktTimeWindow() +{ + delete [] m_piPktWindow; + delete [] m_piPktReplica; + delete [] m_piProbeWindow; + delete [] m_piProbeReplica; +} + +int CPktTimeWindow::getMinPktSndInt() const +{ + return m_iMinPktSndInt; +} + +int CPktTimeWindow::getPktRcvSpeed() const +{ + // get median value, but cannot change the original value order in the window + std::copy(m_piPktWindow, m_piPktWindow + m_iAWSize - 1, m_piPktReplica); + std::nth_element(m_piPktReplica, m_piPktReplica + (m_iAWSize / 2), m_piPktReplica + m_iAWSize - 1); + int median = m_piPktReplica[m_iAWSize / 2]; + + int count = 0; + int sum = 0; + int upper = median << 3; + int lower = median >> 3; + + // median filtering + int* p = m_piPktWindow; + for (int i = 0, n = m_iAWSize; i < n; ++ i) + { + if ((*p < upper) && (*p > lower)) + { + ++ count; + sum += *p; + } + ++ p; + } + + // claculate speed, or return 0 if not enough valid value + if (count > (m_iAWSize >> 1)) + return (int)ceil(1000000.0 / (sum / count)); + else + return 0; +} + +int CPktTimeWindow::getBandwidth() const +{ + // get median value, but cannot change the original value order in the window + std::copy(m_piProbeWindow, m_piProbeWindow + m_iPWSize - 1, m_piProbeReplica); + std::nth_element(m_piProbeReplica, m_piProbeReplica + (m_iPWSize / 2), m_piProbeReplica + m_iPWSize - 1); + int median = m_piProbeReplica[m_iPWSize / 2]; + + int count = 1; + int sum = median; + int upper = median << 3; + int lower = median >> 3; + + // median filtering + int* p = m_piProbeWindow; + for (int i = 0, n = m_iPWSize; i < n; ++ i) + { + if ((*p < upper) && (*p > lower)) + { + ++ count; + sum += *p; + } + ++ p; + } + + return (int)ceil(1000000.0 / (double(sum) / double(count))); +} + +void CPktTimeWindow::onPktSent(int currtime) +{ + int interval = currtime - m_iLastSentTime; + + if ((interval < m_iMinPktSndInt) && (interval > 0)) + m_iMinPktSndInt = interval; + + m_iLastSentTime = currtime; +} + +void CPktTimeWindow::onPktArrival() +{ + m_CurrArrTime = CTimer::getTime(); + + // record the packet interval between the current and the last one + *(m_piPktWindow + m_iPktWindowPtr) = int(m_CurrArrTime - m_LastArrTime); + + // the window is logically circular + ++ m_iPktWindowPtr; + if (m_iPktWindowPtr == m_iAWSize) + m_iPktWindowPtr = 0; + + // remember last packet arrival time + m_LastArrTime = m_CurrArrTime; +} + +void CPktTimeWindow::probe1Arrival() +{ + m_ProbeTime = CTimer::getTime(); +} + +void CPktTimeWindow::probe2Arrival() +{ + m_CurrArrTime = CTimer::getTime(); + + // record the probing packets interval + *(m_piProbeWindow + m_iProbeWindowPtr) = int(m_CurrArrTime - m_ProbeTime); + // the window is logically circular + ++ m_iProbeWindowPtr; + if (m_iProbeWindowPtr == m_iPWSize) + m_iProbeWindowPtr = 0; +} diff --git a/vendor/udt4/src/window.h b/vendor/udt4/src/window.h new file mode 100644 index 000000000..f118a26c7 --- /dev/null +++ b/vendor/udt4/src/window.h @@ -0,0 +1,187 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +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. + +* 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. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +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. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/22/2011 +*****************************************************************************/ + +#ifndef __UDT_WINDOW_H__ +#define __UDT_WINDOW_H__ + + +#ifndef WIN32 + #include + #include +#endif +#include "udt.h" + + +class CACKWindow +{ +public: + CACKWindow(int size = 1024); + ~CACKWindow(); + + // Functionality: + // Write an ACK record into the window. + // Parameters: + // 0) [in] seq: ACK seq. no. + // 1) [in] ack: DATA ACK no. + // Returned value: + // None. + + void store(int32_t seq, int32_t ack); + + // Functionality: + // Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT . + // Parameters: + // 0) [in] seq: ACK-2 seq. no. + // 1) [out] ack: the DATA ACK no. that matches the ACK-2 no. + // Returned value: + // RTT. + + int acknowledge(int32_t seq, int32_t& ack); + +private: + int32_t* m_piACKSeqNo; // Seq. No. for the ACK packet + int32_t* m_piACK; // Data Seq. No. carried by the ACK packet + uint64_t* m_pTimeStamp; // The timestamp when the ACK was sent + + int m_iSize; // Size of the ACK history window + int m_iHead; // Pointer to the lastest ACK record + int m_iTail; // Pointer to the oldest ACK record + +private: + CACKWindow(const CACKWindow&); + CACKWindow& operator=(const CACKWindow&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CPktTimeWindow +{ +public: + CPktTimeWindow(int asize = 16, int psize = 16); + ~CPktTimeWindow(); + + // Functionality: + // read the minimum packet sending interval. + // Parameters: + // None. + // Returned value: + // minimum packet sending interval (microseconds). + + int getMinPktSndInt() const; + + // Functionality: + // Calculate the packes arrival speed. + // Parameters: + // None. + // Returned value: + // Packet arrival speed (packets per second). + + int getPktRcvSpeed() const; + + // Functionality: + // Estimate the bandwidth. + // Parameters: + // None. + // Returned value: + // Estimated bandwidth (packets per second). + + int getBandwidth() const; + + // Functionality: + // Record time information of a packet sending. + // Parameters: + // 0) currtime: timestamp of the packet sending. + // Returned value: + // None. + + void onPktSent(int currtime); + + // Functionality: + // Record time information of an arrived packet. + // Parameters: + // None. + // Returned value: + // None. + + void onPktArrival(); + + // Functionality: + // Record the arrival time of the first probing packet. + // Parameters: + // None. + // Returned value: + // None. + + void probe1Arrival(); + + // Functionality: + // Record the arrival time of the second probing packet and the interval between packet pairs. + // Parameters: + // None. + // Returned value: + // None. + + void probe2Arrival(); + +private: + int m_iAWSize; // size of the packet arrival history window + int* m_piPktWindow; // packet information window + int* m_piPktReplica; + int m_iPktWindowPtr; // position pointer of the packet info. window. + + int m_iPWSize; // size of probe history window size + int* m_piProbeWindow; // record inter-packet time for probing packet pairs + int* m_piProbeReplica; + int m_iProbeWindowPtr; // position pointer to the probing window + + int m_iLastSentTime; // last packet sending time + int m_iMinPktSndInt; // Minimum packet sending interval + + uint64_t m_LastArrTime; // last packet arrival time + uint64_t m_CurrArrTime; // current packet arrival time + uint64_t m_ProbeTime; // arrival time of the first probing packet + +private: + CPktTimeWindow(const CPktTimeWindow&); + CPktTimeWindow &operator=(const CPktTimeWindow&); +}; + + +#endif diff --git a/vendor/websocketpp b/vendor/websocketpp new file mode 160000 index 000000000..378437aec --- /dev/null +++ b/vendor/websocketpp @@ -0,0 +1 @@ +Subproject commit 378437aecdcb1dfe62096ffd5d944bf1f640ccc3