diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml new file mode 100644 index 0000000000..31f53ae62c --- /dev/null +++ b/.github/workflows/abi.yml @@ -0,0 +1,42 @@ +--- +name: abi + +on: + push: + branches: + - patches-2.1 + - release-*-pull + tags: + - release-* + +jobs: + abi: + runs-on: ubuntu-18.04 + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Install Dependencies + run: + sudo apt install + abi-tracker + abi-monitor + abi-dumper + abi-compliance-checker + pkgdiff + vtable-dumper + + - name: Generate + shell: bash + run: | + ./extra/abi-check/abi_check.sh + env: + ABI_CHECK_ROOT: /tmp/le-abi-root + + - uses: actions/upload-artifact@v1 + with: + name: build + path: /tmp/le-abi-root/work/abi-check diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000000..703fb96203 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,210 @@ +--- +name: linux + +on: + pull_request: + types: [opened, synchronize] + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + push: + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + +jobs: + cmake: + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + EVENT_MATRIX: + - DIST + - NONE + - DISABLE_OPENSSL + - DISABLE_THREAD_SUPPORT + - DISABLE_DEBUG_MODE + - DISABLE_MM_REPLACEMENT + - COMPILER_CLANG + - TEST_EXPORT_STATIC + - TEST_EXPORT_SHARED + - ASAN + - TSAN + - UBSAN + + steps: + - uses: actions/checkout@v2.0.0 + - name: Cache Build + uses: actions/cache@v1.1.0 + with: + path: build + key: ${{ matrix.os }}-cmake-${{ matrix.EVENT_MATRIX }}-v2 + - name: Cache Dist Build + uses: actions/cache@v1.1.0 + with: + path: dist + key: ${{ matrix.os }}-cmake-dist-${{ matrix.EVENT_MATRIX }}-v2 + + - name: Build And Test + shell: bash + run: | + if [ "${{ matrix.EVENT_MATRIX }}" == "DIST" ]; then + ./autogen.sh + mkdir -p dist + cd dist + ../configure + rm -fr *.tar.gz + make dist + archive=$(echo *.tar.gz) + tar -vxf $archive + cd $(basename $archive .tar.gz) + fi + + export TSAN_OPTIONS=suppressions=$PWD/extra/tsan.supp + export LSAN_OPTIONS=suppressions=$PWD/extra/lsan.supp + + if [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_OPENSSL" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_THREAD_SUPPORT" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_THREAD_SUPPORT=ON" + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_DEBUG_MODE" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_DEBUG_MODE=ON" + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_MM_REPLACEMENT" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_MM_REPLACEMENT=ON" + elif [ "${{ matrix.EVENT_MATRIX }}" == "COMPILER_CLANG" ]; then + EVENT_CMAKE_OPTIONS="" + export CC=clang + elif [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_STATIC" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + elif [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_SHARED" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=SHARED -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + elif [ "${{ matrix.EVENT_MATRIX }}" == "ASAN" ]; then + EVENT_CMAKE_OPTIONS="-DCMAKE_C_FLAGS=-fsanitize=address -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=debug" + elif [ "${{ matrix.EVENT_MATRIX }}" == "TSAN" ]; then + EVENT_CMAKE_OPTIONS="-DCMAKE_C_FLAGS=-fsanitize=thread -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=debug" + elif [ "${{ matrix.EVENT_MATRIX }}" == "UBSAN" ]; then + EVENT_CMAKE_OPTIONS="-DCMAKE_C_FLAGS=-fsanitize=undefined -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=debug" + else + EVENT_CMAKE_OPTIONS="" + fi + + #run build and test + JOBS=20 + export CTEST_PARALLEL_LEVEL=$JOBS + export CTEST_OUTPUT_ON_FAILURE=1 + mkdir -p build + cd build + echo [cmake]: cmake .. $EVENT_CMAKE_OPTIONS + cmake .. $EVENT_CMAKE_OPTIONS || (rm -rf * && cmake .. $EVENT_CMAKE_OPTIONS) + cmake --build . + if [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_STATIC" ]; then + sudo python3 ../test-export/test-export.py static + elif [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_SHARED" ]; then + sudo python3 ../test-export/test-export.py shared + else + cmake --build . --target verify + fi + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ${{ matrix.os }}-cmake-${{ matrix.EVENT_MATRIX }}-build + path: build + - uses: actions/upload-artifact@v1 + if: failure() && matrix.EVENT_MATRIX == 'DIST' + with: + name: ${{ matrix.os }}-cmake-${{ matrix.EVENT_MATRIX }}-dist + path: dist + + autotools: + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + EVENT_MATRIX: + - DIST + - NONE + - DISABLE_OPENSSL + - DISABLE_THREAD_SUPPORT + - DISABLE_DEBUG_MODE + - DISABLE_MM_REPLACEMENT + - COMPILER_CLANG + + steps: + - uses: actions/checkout@v2.0.0 + - name: Cache Build + uses: actions/cache@v1.1.0 + with: + path: build + key: ${{ matrix.os }}-autotools-${{ matrix.EVENT_MATRIX }}-v2 + - name: Cache Dist Build + uses: actions/cache@v1.1.0 + with: + path: dist + key: ${{ matrix.os }}-autotools-dist-${{ matrix.EVENT_MATRIX }}-v2 + + - name: Build And Test + shell: bash + run: | + if [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_OPENSSL" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-openssl" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_THREAD_SUPPORT" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-thread-support" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_DEBUG_MODE" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-debug-mode" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_MM_REPLACEMENT" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-malloc-replacement" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "COMPILER_CLANG" ]; then + EVENT_CONFIGURE_OPTIONS="" + export CC=clang + + else + EVENT_CONFIGURE_OPTIONS="" + fi + + #run build and test + JOBS=20 + ./autogen.sh + + if [ "${{ matrix.EVENT_MATRIX }}" == "DIST" ]; then + mkdir -p dist + cd dist + rm -fr *.tar.gz + ../configure $EVENT_CONFIGURE_OPTIONS + make dist + archive=$(echo *.tar.gz) + tar -vxf $archive + cd $(basename $archive .tar.gz) + fi + + mkdir -p build + cd build + echo [configure]: ../configure $EVENT_CONFIGURE_OPTIONS + ../configure $EVENT_CONFIGURE_OPTIONS + make + make -j $JOBS verify + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ${{ matrix.os }}-autotools-${{ matrix.EVENT_MATRIX }}-build + path: build + - uses: actions/upload-artifact@v1 + if: failure() && matrix.EVENT_MATRIX == 'DIST' + with: + name: ${{ matrix.os }}-autotools-${{ matrix.EVENT_MATRIX }}-dist + path: dist diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000000..910a627a58 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,168 @@ +--- +name: macos + +on: + pull_request: + types: [opened, synchronize] + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + push: + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + +jobs: + cmake: + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + os: [macos-latest] + EVENT_MATRIX: + - NONE + - DISABLE_OPENSSL + - DISABLE_THREAD_SUPPORT + - DISABLE_DEBUG_MODE + - DISABLE_MM_REPLACEMENT + - TEST_EXPORT_STATIC + - TEST_EXPORT_SHARED + - OPENSSL_1_1 + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Cache Build + uses: actions/cache@v1.1.0 + with: + path: build + key: macos-10.15-cmake-${{ matrix.EVENT_MATRIX }}-v2 + + - name: Build And Test + shell: bash + run: | + if [ "${{ matrix.EVENT_MATRIX }}" == "OPENSSL_1_1" ]; then + export OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 + else + export OPENSSL_ROOT_DIR=/usr/local/opt/openssl + fi + + if [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_OPENSSL" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_THREAD_SUPPORT" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_THREAD_SUPPORT=ON" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_DEBUG_MODE" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_DEBUG_MODE=ON" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_MM_REPLACEMENT" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_MM_REPLACEMENT=ON" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_STATIC" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_SHARED" ]; then + EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=SHARED -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + + else + EVENT_CMAKE_OPTIONS="" + fi + + #run build and test + JOBS=1 + export CTEST_PARALLEL_LEVEL=$JOBS + export CTEST_OUTPUT_ON_FAILURE=1 + mkdir -p build + cd build + echo [cmake]: cmake .. $EVENT_CMAKE_OPTIONS + cmake .. $EVENT_CMAKE_OPTIONS || (rm -rf * && cmake .. $EVENT_CMAKE_OPTIONS) + cmake --build . + if [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_STATIC" ]; then + sudo python3 ../test-export/test-export.py static + elif [ "${{ matrix.EVENT_MATRIX }}" == "TEST_EXPORT_SHARED" ]; then + sudo python3 ../test-export/test-export.py shared + else + cmake --build . --target verify + fi + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ${{ matrix.os }}-cmake-${{ matrix.EVENT_MATRIX }}-build + path: build + + autotools: + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + os: [macos-latest] + EVENT_MATRIX: + - NONE + - DISABLE_OPENSSL + - DISABLE_THREAD_SUPPORT + - DISABLE_DEBUG_MODE + - DISABLE_MM_REPLACEMENT + - OPENSSL_1_1 + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Cache Build + uses: actions/cache@v1.1.0 + with: + path: build + key: ${{ matrix.os }}-autotools-${{ matrix.EVENT_MATRIX }}-v2 + + - name: Install Depends + run: brew install autoconf automake libtool pkg-config + + - name: Build And Test + shell: bash + run: | + if [ "${{ matrix.EVENT_MATRIX }}" == "OPENSSL_1_1" ]; then + export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig:$PKG_CONFIG_PATH" + else + export PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" + fi + + if [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_OPENSSL" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-openssl" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_THREAD_SUPPORT" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-thread-support" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_DEBUG_MODE" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-debug-mode" + + elif [ "${{ matrix.EVENT_MATRIX }}" == "DISABLE_MM_REPLACEMENT" ]; then + EVENT_CONFIGURE_OPTIONS="--disable-malloc-replacement" + + else + EVENT_CONFIGURE_OPTIONS="" + fi + + #run build and test + JOBS=1 + ./autogen.sh + mkdir -p build + cd build + echo [configure]: ../configure $EVENT_CONFIGURE_OPTIONS + ../configure $EVENT_CONFIGURE_OPTIONS + make + make -j $JOBS verify + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ${{ matrix.os }}-autotools-${{ matrix.EVENT_MATRIX }}-build + path: build diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml new file mode 100644 index 0000000000..a297e8caa0 --- /dev/null +++ b/.github/workflows/mingw.yml @@ -0,0 +1,170 @@ +--- +name: mingw + +on: + pull_request: + types: [opened, synchronize] + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + push: + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + +jobs: + autotools: + runs-on: windows-2019 + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + EVENT_MATRIX: + - none + - disable-openssl + - disable-thread-support + - disable-debug-mode + - disable-malloc-replacement + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Cache MinGW + id: cache-mingw + uses: actions/cache@v1.1.2 + with: + path: D:\a\_temp\msys + key: windows-mingw + + - name: Cache Build + uses: actions/cache@v1.1.2 + with: + path: build + key: mingw-autotools-${{ matrix.EVENT_MATRIX }}-v2 + + - uses: numworks/setup-msys2@v1 + if: steps.cache-mingw.outputs.cache-hit != 'true' + with: + msystem: MINGW64 + + - name: Install Dependes + if: steps.cache-mingw.outputs.cache-hit != 'true' + run: | + msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc autoconf automake libtool mingw-w64-x86_64-openssl + + - name: Build And Test + shell: powershell + run: | + $env:EVENT_CONFIGURE_OPTIONS="" + if ( "${{ matrix.EVENT_MATRIX }}" -ne "none" ) { + $env:EVENT_CONFIGURE_OPTIONS="--${{ matrix.EVENT_MATRIX }}" + } + $env:EVENT_TESTS_PARALLEL=1 + $env:EVENT_BUILD_PARALLEL=10 + + $script=' + export PATH="/mingw64/bin:/usr/bin:/bin:/usr/local/bin:/opt/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:$PATH" + ./autogen.sh 2>&1 3>&1 + [[ $? -ne 0 ]] && exit 1 + mkdir -p build + cd build + [[ $? -ne 0 ]] && exit 1 + LDFLAGS="-L/mingw64/lib" CFLAGS="-I/mingw64/include" ../configure $EVENT_CONFIGURE_OPTIONS 2>&1 + [[ $? -ne 0 ]] && exit 1 + make -j $EVENT_BUILD_PARALLEL 2>&1 + [[ $? -ne 0 ]] && exit 1 + make verify -j $EVENT_TESTS_PARALLEL 2>&1 ' + D:\a\_temp\msys\msys64\usr\bin\bash.exe -c $script + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: mingw-${{ matrix.EVENT_MATRIX }}-build + path: build + + cmake: + runs-on: windows-2019 + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + EVENT_MATRIX: + - NONE + - DISABLE_OPENSSL + - DISABLE_THREAD_SUPPORT + - DISABLE_DEBUG_MODE + - DISABLE_MM_REPLACEMENT + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Cache MinGW + id: cache-mingw-cmake + uses: actions/cache@v1.1.2 + with: + path: D:\a\_temp\msys + key: windows-mingw-cmake + + - name: Cache Build + uses: actions/cache@v1.1.2 + with: + path: build + key: mingw-cmake-${{ matrix.EVENT_MATRIX }}-v2 + + - uses: numworks/setup-msys2@v1 + if: steps.cache-mingw-cmake.outputs.cache-hit != 'true' + with: + msystem: MINGW64 + + - name: Install Dependes + if: steps.cache-mingw-cmake.outputs.cache-hit != 'true' + run: | + msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-openssl + + - name: Build And Test + shell: powershell + run: | + $EVENT_CONFIGURE_OPTIONS="" + if ( "${{ matrix.EVENT_MATRIX }}" -ne "NONE" ) { + $EVENT_CONFIGURE_OPTIONS="-DEVENT__${{ matrix.EVENT_MATRIX }}=ON" + } + $env:PATH="D:\a\_temp\msys\msys64\mingw64\bin;D:\a\_temp\msys\msys64\usr\bin;$env:PATH" + mkdir build -ea 0 + cd build + function cmake_configure($retry) + { + $errcode=0 + try { + cmake .. -G "MSYS Makefiles" $EVENT_CONFIGURE_OPTIONS -DCMAKE_C_FLAGS=-w + $errcode=$LastExitCode + } + catch { + $errcode=1 + } + finally { + if ($errcode -ne 0) { + if ($retry -eq 0) { + $host.SetShouldExit($LastExitCode) + } else { + echo "Remove all entries in build directory" + rm -r -fo * + cmake_configure 0 + } + } + } + } + cmake_configure 1 + cmake --build . + ctest -V + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: mingw-${{ matrix.EVENT_MATRIX }}-build + path: build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000000..b30038a09d --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,256 @@ +--- +name: windows + +on: + pull_request: + types: [opened, synchronize] + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + push: + paths-ignore: + - '**.md' + - '.mailmap' + - 'ChangeLog*' + - 'whatsnew*' + - 'LICENSE' + +jobs: + vs2017: + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + os: [windows-2016] + EVENT_MATRIX: [NONE] + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Cache Depends + id: cache-depends + uses: actions/cache@v1.0.3 + with: + path: C:\vcpkg\installed + key: ${{ matrix.os }}-vcpkg + + - name: Cache Build + uses: actions/cache@v1.0.3 + with: + path: build + key: ${{ matrix.os }}-${{ matrix.EVENT_MATRIX }}-v3 + + - name: Install Depends + if: steps.cache-depends.outputs.cache-hit != 'true' + shell: powershell + run: | + vcpkg install openssl:x64-windows + vcpkg install zlib:x64-windows + + - name: Build And Test + shell: powershell + run: | + $OPENSSL_ROOT_DIR="C:\vcpkg\installed\x64-windows" + $EVENT_BUILD_PARALLEL=10 + $EVENT_TESTS_PARALLEL=1 + $env:PATH="$OPENSSL_ROOT_DIR/bin;$env:PATH" + + mkdir build -ea 0 + cd build + + $CMAKE_CMD="cmake -G 'Visual Studio 15 2017 Win64' .." + function cmake_configure($retry) + { + $errcode=0 + try { + if ($retry -eq 0) { + echo "[cmake configure retry] $CMAKE_CMD" + } else { + echo "[cmake configure] $CMAKE_CMD" + } + Invoke-Expression $CMAKE_CMD + $errcode=$LastExitCode + } + catch { + $errcode=1 + } + finally { + if ($errcode -ne 0) { + if ($retry -eq 0) { + $host.SetShouldExit($LastExitCode) + } else { + echo "Remove all entries in build directory" + rm -r -fo * + cmake_configure 0 + } + } + } + } + cmake_configure 1 + + try { + cmake --build . -j $EVENT_BUILD_PARALLEL -- /nologo /verbosity:minimal + if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } + + if ("${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_STATIC") { + python ../test-export/test-export.py static + } + elseif ("${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_SHARED") { + python ../test-export/test-export.py shared + } + else { + ctest --output-on-failure -j $EVENT_TESTS_PARALLEL + if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } + } + } catch { + $host.SetShouldExit($LastExitCode) + } + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ${{ matrix.os }}-${{ matrix.EVENT_MATRIX }}-build + path: build + + vs2019: + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + os: [windows-2019] + EVENT_MATRIX: + - NONE + - LIBRARY_TYPE_STATIC + - DISABLE_OPENSSL + - DISABLE_THREAD_SUPPORT + - DISABLE_DEBUG_MODE + - DISABLE_MM_REPLACEMENT + - DUNICODE + - TEST_EXPORT_SHARED + - TEST_EXPORT_STATIC + + steps: + - uses: actions/checkout@v2.0.0 + + - name: Cache Depends + id: cache-depends + uses: actions/cache@v1.1.0 + with: + path: C:\vcpkg\installed + key: ${{ matrix.os }}-vcpkg + + - name: Cache Build + uses: actions/cache@v1.1.0 + with: + path: build + key: ${{ matrix.os }}-${{ matrix.EVENT_MATRIX }}-v3 + + - name: Install Depends + if: steps.cache-depends.outputs.cache-hit != 'true' + shell: powershell + run: | + vcpkg install openssl:x64-windows + vcpkg install zlib:x64-windows + + - name: Build And Test + shell: powershell + run: | + $OPENSSL_ROOT_DIR="C:\vcpkg\installed\x64-windows" + $EVENT_BUILD_PARALLEL=10 + $EVENT_TESTS_PARALLEL=1 + $env:PATH="$OPENSSL_ROOT_DIR/bin;$env:PATH" + + if ( "${{ matrix.EVENT_MATRIX }}" -eq "LIBRARY_TYPE_STATIC" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "DISABLE_OPENSSL" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "DISABLE_THREAD_SUPPORT" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_THREAD_SUPPORT=ON" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "DISABLE_DEBUG_MODE" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_DEBUG_MODE=ON" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "DISABLE_MM_REPLACEMENT" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_MM_REPLACEMENT=ON" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "UNICODE" ) { + $EVENT_CMAKE_OPTIONS="-DCMAKE_C_FLAGS='-DUNICODE -D_UNICODE'" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_SHARED" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + } + elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_STATIC" ) { + $EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + } + else { + $EVENT_CMAKE_OPTIONS="" + } + + mkdir build -ea 0 + cd build + + if ("${{ matrix.os }}" -eq "windows-2016") { + $CMAKE_CMD="cmake -G 'Visual Studio 15 2017 Win64' .." + } + else { # windows-2019 + $CMAKE_CMD="cmake -G 'Visual Studio 16 2019' -A x64 .. $EVENT_CMAKE_OPTIONS" + } + function cmake_configure($retry) + { + $errcode=0 + try { + if ($retry -eq 0) { + echo "[cmake configure retry] $CMAKE_CMD" + } else { + echo "[cmake configure] $CMAKE_CMD" + } + Invoke-Expression $CMAKE_CMD + $errcode=$LastExitCode + } + catch { + $errcode=1 + } + finally { + if ($errcode -ne 0) { + if ($retry -eq 0) { + $host.SetShouldExit($LastExitCode) + } else { + echo "Remove all entries in build directory" + rm -r -fo * + cmake_configure 0 + } + } + } + } + cmake_configure 1 + + try { + cmake --build . -j $EVENT_BUILD_PARALLEL -- /nologo /verbosity:minimal + if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } + + if ("${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_STATIC") { + python ../test-export/test-export.py static + } + elseif ("${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_SHARED") { + python ../test-export/test-export.py shared + } + else { + ctest --output-on-failure -j $EVENT_TESTS_PARALLEL + if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } + } + } catch { + $host.SetShouldExit($LastExitCode) + } + + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ${{ matrix.os }}-${{ matrix.EVENT_MATRIX }}-build + path: build diff --git a/.gitignore b/.gitignore index 955846cdff..096e687b2b 100644 --- a/.gitignore +++ b/.gitignore @@ -66,7 +66,6 @@ cscope* /aclocal.m4 /compile -/doxygen /config.cache /config.guess /config.log @@ -84,6 +83,12 @@ cscope* /stamp-h1 /stamp-h2 +# files generated by doxygen +doxygen +CMakeDoxyfile.in +CMakeDoxygenDefaults.cmake +Doxyfile.doxygen + /sample/dns-example /sample/event-read-fifo /sample/hello-world @@ -126,7 +131,7 @@ CTestTestfile.cmake DartConfiguration.tcl LibeventConfig.cmake LibeventConfigVersion.cmake -LibeventTargets.cmake +LibeventTargets*.cmake bin/ cmake_install.cmake Uninstall.cmake @@ -141,6 +146,7 @@ event_extra.dir *.sln *.filters install_manifest.txt +test-export/build # ninja build.ninja diff --git a/.mailmap b/.mailmap index 0aec464600..b9805d018f 100644 --- a/.mailmap +++ b/.mailmap @@ -1,5 +1,7 @@ # name -> email Azat Khuzhin +yuangongji -# email -> email - +# primary email -> alias + +yuangongji <82787816@qq.com> diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 830c956835..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,123 +0,0 @@ -os: - - linux - - osx -sudo: false -dist: trusty -osx_image: xcode10.1 - -branches: - except: - - /.*appveyor.*/ - - /.*win.*/ - - /.*mingw.*/ - - /.*freebsd.*/ - - /.*bitrise.*/ - -git: - quiet: true - -env: - matrix: - - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__COVERAGE=ON -DCMAKE_BUILD_TYPE=debug" COVERALLS=yes - - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="" - - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" - - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_THREAD_SUPPORT=ON" - - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_DEBUG_MODE=ON" - - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_MM_REPLACEMENT=ON" - - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="" - - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="--disable-openssl" - - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="--disable-thread-support" - - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="--disable-debug-mode" - - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="--disable-malloc-replacement" - -matrix: - exclude: - - os: osx - env: EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__COVERAGE=ON -DCMAKE_BUILD_TYPE=debug" COVERALLS=yes - allow_failures: - - os: osx - fast_finish: true - -language: c -compiler: - - gcc - - clang - -before_install: - # do not run with clang, since it fails (SIGSEGV) - - if [ "$CC" = "clang" ]; then - unset COVERALLS; - fi - - if [ -n "$COVERALLS" ]; then - pip install --user cpp-coveralls; - fi - - export JOBS=20 - - export TIMEOUT=50 - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ "$CC" == "gcc" ]; then - export CC=$(ls /usr/local/Cellar/gcc/*/bin/gcc-?); - fi - - export OPENSSL_ROOT=$(echo /usr/local/Cellar/openssl/*); - export - CMAKE_INCLUDE_PATH=$OPENSSL_ROOT/include - CMAKE_LIBRARY_PATH=$OPENSSL_ROOT/lib; - export - CFLAGS=-I$CMAKE_INCLUDE_PATH - LDFLAGS=-L$CMAKE_LIBRARY_PATH; - - export JOBS=1; - fi - -addons: - apt: - sources: - - xenial - - sourceline: 'deb http://archive.ubuntu.com/ubuntu xenial main' - packages: - - zlib1g-dev - - libssl-dev - - build-essential - - automake - - autoconf - - cmake - - lcov - homebrew: - packages: - - openssl - - lcov - - libtool - - gcc - - -script: - - if [ "$EVENT_BUILD_METHOD" = "autotools" ]; then - ./autogen.sh && - ./configure $EVENT_CONFIGURE_OPTIONS && - make && - travis_wait $TIMEOUT make -j $JOBS verify; - fi - - if [ "$EVENT_BUILD_METHOD" = "cmake" ]; then - export - CTEST_PARALLEL_LEVEL=$JOBS - CTEST_OUTPUT_ON_FAILURE=1; - - mkdir build && - cd build && - cmake .. $EVENT_CMAKE_OPTIONS && - travis_wait $TIMEOUT - cmake --build . --target verify; - fi - -after_script: - - if [ -n "$COVERALLS" ]; then - coveralls - --build-root . - --root .. - --exclude test - --exclude sample - --exclude cmake - --exclude build/CMakeFiles/CheckTypeSize - --exclude build/CMakeFiles/CompilerIdC - --gcov-options '\-lp'; - fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 70acb696ca..676727f165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,16 +46,13 @@ set(EVENT__LIBRARY_TYPE DEFAULT CACHE STRING project(libevent C) -set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") string(REGEX MATCH "SunOS" SOLARIS "${CMAKE_SYSTEM_NAME}") include(CheckTypeSize) -include(CheckFunctionExistsEx) include(CheckFileOffsetBits) -include(CheckFunctionExists) -include(CheckIncludeFile) -include(CheckIncludeFiles) +include(Macros) include(CheckVariableExists) include(CheckSymbolExists) include(CheckStructHasMember) @@ -84,10 +81,10 @@ set(EVENT_ABI_LIBVERSION set(EVENT_PACKAGE_VERSION "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}") -set(EVENT_NUMERIC_VERSION 0x02010b00) +set(EVENT_NUMERIC_VERSION 0x02010c00) # equals to VERSION_INFO in Makefile.am set(EVENT_ABI_LIBVERSION_CURRENT 7) -set(EVENT_ABI_LIBVERSION_REVISION 0) +set(EVENT_ABI_LIBVERSION_REVISION 1) set(EVENT_ABI_LIBVERSION_AGE 0) # equals to RELEASE in Makefile.am @@ -158,9 +155,18 @@ option(EVENT__COVERAGE # Put the libaries and binaries that get built into directories at the # top of the build tree rather than in hard-to-find leaf directories. -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +# +# But only if this variables are not defined yet +# (i.e. libevent is used via add_subdirectory()) +if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +endif() +if (NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +endif() +if (NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +endif() if (EVENT__ENABLE_VERBOSE_DEBUG) add_definitions(-DUSE_DEBUG=1) @@ -243,7 +249,6 @@ if (${MSVC}) if (EVENT__MSVC_STATIC_RUNTIME) foreach (flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL @@ -326,11 +331,14 @@ if (APPLE) ) endif() +if (MINGW OR CYGWIN) + set(WIN32 TRUE) +endif() + # Winsock. if(WIN32) - set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h ws2tcpip.h) - set(CMAKE_REQUIRED_LIBRARIES ws2_32.lib shell32.lib advapi32.lib) - set(CMAKE_REQUIRED_DEFINITIONS -FIwinsock2.h -FIws2tcpip.h) + set(CMAKE_REQUIRED_LIBRARIES ws2_32 shell32 advapi32) + set(CMAKE_REQUIRED_DEFINITIONS -FIwinsock2.h -FIws2tcpip.h -D_WIN32_WINNT=0x0600) endif() if (SOLARIS) set(CMAKE_REQUIRED_LIBRARIES socket nsl) @@ -352,145 +360,161 @@ endif() if (_GNU_SOURCE) add_definitions(-D_GNU_SOURCE=1) + set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) +endif() + +# Check if header files exist +list(APPEND FILES_TO_CHECK + fcntl.h + inttypes.h + memory.h + signal.h + stdarg.h + stddef.h + stdint.h + stdlib.h + string.h + errno.h + unistd.h + time.h + sys/types.h + sys/stat.h + sys/time.h + sys/param.h +) +if (WIN32) + list(APPEND FILES_TO_CHECK + io.h + winsock2.h + ws2tcpip.h + afunix.h + ) +else() + list(APPEND FILES_TO_CHECK + netdb.h + dlfcn.h + arpa/inet.h + poll.h + port.h + sys/socket.h + sys/random.h + sys/un.h + sys/devpoll.h + sys/epoll.h + sys/eventfd.h + sys/event.h + sys/ioctl.h + sys/mman.h + sys/queue.h + sys/select.h + sys/sendfile.h + sys/uio.h + sys/wait.h + sys/resource.h + sys/timerfd.h + netinet/in.h + netinet/in6.h + netinet/tcp.h + ifaddrs.h + ) endif() -CHECK_INCLUDE_FILE(sys/types.h EVENT__HAVE_SYS_TYPES_H) -if(EVENT__HAVE_SYS_TYPES_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/types.h) -endif() - -CHECK_INCLUDE_FILE(sys/socket.h EVENT__HAVE_SYS_SOCKET_H) -if(EVENT__HAVE_SYS_SOCKET_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) +if (NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") + list(APPEND FILES_TO_CHECK sys/sysctl.h) endif() -CHECK_INCLUDE_FILE(netinet/in.h EVENT__HAVE_NETINET_IN_H) -if(EVENT__HAVE_NETINET_IN_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES netinet/in.h) +if (APPLE) + list(APPEND FILES_TO_CHECK + mach/mach_time.h + mach/mach.h + ) endif() -CHECK_INCLUDE_FILE(sys/un.h EVENT__HAVE_SYS_UN_H) -if(EVENT__HAVE_SYS_UN_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/un.h) +foreach(FILE ${FILES_TO_CHECK}) + CHECK_INCLUDE_FILE_CONCAT(${FILE} "EVENT") +endforeach() +unset(FILES_TO_CHECK) + +# Check if functions exist +list(APPEND SYMBOLS_TO_CHECK + getaddrinfo + getnameinfo + getprotobynumber + getservbyname + gethostbyname + inet_ntop + inet_pton + gettimeofday + signal + strtoll + splice + strlcpy + strsep + strtok_r + vasprintf + timerclear + timercmp + timerisset + timeradd + nanosleep + putenv + umask +) +if (NOT EVENT__DISABLE_CLOCK_GETTIME) + list(APPEND SYMBOLS_TO_CHECK clock_gettime) endif() -if(WIN32) - CHECK_INCLUDE_FILE(afunix.h EVENT__HAVE_AFUNIX_H) - if(EVENT__HAVE_AFUNIX_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES afunix.h) +if (WIN32) + list(APPEND SYMBOLS_TO_CHECK + _gmtime64_s + _gmtime64 + ) +else() + list(APPEND SYMBOLS_TO_CHECK + getifaddrs + select + epoll_create + epoll_create1 + epoll_ctl + eventfd + poll + port_create + kqueue + fcntl + mmap + pipe + pipe2 + sendfile + sigaction + strsignal + sysctl + accept4 + arc4random + arc4random_buf + arc4random_addrandom + getrandom + getegid + geteuid + issetugid + usleep + timerfd_create + setenv + unsetenv + setrlimit + gethostbyname_r + ) + if (APPLE) + list(APPEND SYMBOLS_TO_CHECK mach_absolute_time) endif() endif() -CHECK_TYPE_SIZE("struct sockaddr_un" EVENT__HAVE_STRUCT_SOCKADDR_UN) -CHECK_INCLUDE_FILE(netinet/in6.h EVENT__HAVE_NETINET_IN6_H) -if(EVENT__HAVE_NETINET_IN6_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES netinet/in6.h) -endif() - -CHECK_INCLUDE_FILE(unistd.h EVENT__HAVE_UNISTD_H) -CHECK_INCLUDE_FILE(netdb.h EVENT__HAVE_NETDB_H) -CHECK_INCLUDE_FILE(dlfcn.h EVENT__HAVE_DLFCN_H) -CHECK_INCLUDE_FILE(arpa/inet.h EVENT__HAVE_ARPA_INET_H) -CHECK_INCLUDE_FILE(fcntl.h EVENT__HAVE_FCNTL_H) -if(EVENT__HAVE_FCNTL_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES fcntl.h) -endif() -CHECK_INCLUDE_FILE(inttypes.h EVENT__HAVE_INTTYPES_H) -CHECK_INCLUDE_FILE(memory.h EVENT__HAVE_MEMORY_H) -CHECK_INCLUDE_FILE(poll.h EVENT__HAVE_POLL_H) -CHECK_INCLUDE_FILE(port.h EVENT__HAVE_PORT_H) -if(EVENT__HAVE_PORT_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES port.h) -endif() -CHECK_INCLUDE_FILE(signal.h EVENT__HAVE_SIGNAL_H) -CHECK_INCLUDE_FILE(stdarg.h EVENT__HAVE_STDARG_H) -CHECK_INCLUDE_FILE(stddef.h EVENT__HAVE_STDDEF_H) -CHECK_INCLUDE_FILE(stdint.h EVENT__HAVE_STDINT_H) -CHECK_INCLUDE_FILE(stdlib.h EVENT__HAVE_STDLIB_H) -CHECK_INCLUDE_FILE(strings.h EVENT__HAVE_STRINGS_H) -CHECK_INCLUDE_FILE(string.h EVENT__HAVE_STRING_H) -CHECK_INCLUDE_FILE(sys/devpoll.h EVENT__HAVE_DEVPOLL) -CHECK_INCLUDE_FILE(sys/epoll.h EVENT__HAVE_SYS_EPOLL_H) -CHECK_INCLUDE_FILE(sys/eventfd.h EVENT__HAVE_SYS_EVENTFD_H) -CHECK_INCLUDE_FILE(sys/event.h EVENT__HAVE_SYS_EVENT_H) -CHECK_INCLUDE_FILE(sys/ioctl.h EVENT__HAVE_SYS_IOCTL_H) -CHECK_INCLUDE_FILE(sys/mman.h EVENT__HAVE_SYS_MMAN_H) -CHECK_INCLUDE_FILE(sys/param.h EVENT__HAVE_SYS_PARAM_H) -CHECK_INCLUDE_FILE(sys/queue.h EVENT__HAVE_SYS_QUEUE_H) -CHECK_INCLUDE_FILE(sys/select.h EVENT__HAVE_SYS_SELECT_H) -CHECK_INCLUDE_FILE(sys/sendfile.h EVENT__HAVE_SYS_SENDFILE_H) -CHECK_INCLUDE_FILE(sys/stat.h EVENT__HAVE_SYS_STAT_H) -CHECK_INCLUDE_FILE(sys/time.h EVENT__HAVE_SYS_TIME_H) -if(EVENT__HAVE_SYS_TIME_H) - list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/time.h) -endif() -CHECK_INCLUDE_FILE(sys/uio.h EVENT__HAVE_SYS_UIO_H) -CHECK_INCLUDE_FILES("sys/types.h;ifaddrs.h" EVENT__HAVE_IFADDRS_H) -CHECK_INCLUDE_FILE(mach/mach_time.h EVENT__HAVE_MACH_MACH_TIME_H) -CHECK_INCLUDE_FILE(netinet/tcp.h EVENT__HAVE_NETINET_TCP_H) -CHECK_INCLUDE_FILE(sys/wait.h EVENT__HAVE_SYS_WAIT_H) -CHECK_INCLUDE_FILE(sys/resource.h EVENT__HAVE_SYS_RESOURCE_H) -CHECK_INCLUDE_FILE(sys/sysctl.h EVENT__HAVE_SYS_SYSCTL_H) -CHECK_INCLUDE_FILE(sys/timerfd.h EVENT__HAVE_SYS_TIMERFD_H) -CHECK_INCLUDE_FILE(errno.h EVENT__HAVE_ERRNO_H) - - -CHECK_FUNCTION_EXISTS_EX(epoll_create EVENT__HAVE_EPOLL) -CHECK_FUNCTION_EXISTS_EX(epoll_ctl EVENT__HAVE_EPOLL_CTL) -CHECK_FUNCTION_EXISTS_EX(eventfd EVENT__HAVE_EVENTFD) -if(NOT EVENT__DISABLE_CLOCK_GETTIME) - CHECK_FUNCTION_EXISTS_EX(clock_gettime EVENT__HAVE_CLOCK_GETTIME) -endif() -CHECK_FUNCTION_EXISTS_EX(fcntl EVENT__HAVE_FCNTL) -CHECK_FUNCTION_EXISTS_EX(getaddrinfo EVENT__HAVE_GETADDRINFO) -CHECK_FUNCTION_EXISTS_EX(getnameinfo EVENT__HAVE_GETNAMEINFO) -CHECK_FUNCTION_EXISTS_EX(gettimeofday EVENT__HAVE_GETTIMEOFDAY) -CHECK_FUNCTION_EXISTS_EX(getprotobynumber EVENT__HAVE_GETPROTOBYNUMBER) -CHECK_FUNCTION_EXISTS_EX(getservbyname EVENT__HAVE_GETSERVBYNAME) -CHECK_FUNCTION_EXISTS_EX(inet_ntop EVENT__HAVE_INET_NTOP) -CHECK_FUNCTION_EXISTS_EX(inet_pton EVENT__HAVE_INET_PTON) -CHECK_FUNCTION_EXISTS_EX(kqueue EVENT__HAVE_KQUEUE) -CHECK_FUNCTION_EXISTS_EX(mmap EVENT__HAVE_MMAP) -CHECK_FUNCTION_EXISTS_EX(pipe EVENT__HAVE_PIPE) -CHECK_FUNCTION_EXISTS_EX(pipe2 EVENT__HAVE_PIPE2) -CHECK_FUNCTION_EXISTS_EX(poll EVENT__HAVE_POLL) -CHECK_FUNCTION_EXISTS_EX(port_create EVENT__HAVE_PORT_CREATE) -CHECK_FUNCTION_EXISTS_EX(sendfile EVENT__HAVE_SENDFILE) -CHECK_FUNCTION_EXISTS_EX(sigaction EVENT__HAVE_SIGACTION) -CHECK_FUNCTION_EXISTS_EX(signal EVENT__HAVE_SIGNAL) -CHECK_FUNCTION_EXISTS_EX(splice EVENT__HAVE_SPLICE) -CHECK_FUNCTION_EXISTS_EX(strlcpy EVENT__HAVE_STRLCPY) -CHECK_FUNCTION_EXISTS_EX(strsep EVENT__HAVE_STRSEP) -CHECK_FUNCTION_EXISTS_EX(strtok_r EVENT__HAVE_STRTOK_R) -CHECK_FUNCTION_EXISTS_EX(strtoll EVENT__HAVE_STRTOLL) -CHECK_FUNCTION_EXISTS_EX(vasprintf EVENT__HAVE_VASPRINTF) -CHECK_FUNCTION_EXISTS_EX(sysctl EVENT__HAVE_SYSCTL) -CHECK_FUNCTION_EXISTS_EX(accept4 EVENT__HAVE_ACCEPT4) -CHECK_FUNCTION_EXISTS_EX(arc4random EVENT__HAVE_ARC4RANDOM) -CHECK_FUNCTION_EXISTS_EX(arc4random_buf EVENT__HAVE_ARC4RANDOM_BUF) -CHECK_FUNCTION_EXISTS_EX(arc4random_addrandom EVENT__HAVE_ARC4RANDOM_ADDRANDOM) -CHECK_FUNCTION_EXISTS_EX(epoll_create1 EVENT__HAVE_EPOLL_CREATE1) -CHECK_FUNCTION_EXISTS_EX(getegid EVENT__HAVE_GETEGID) -CHECK_FUNCTION_EXISTS_EX(geteuid EVENT__HAVE_GETEUID) -CHECK_FUNCTION_EXISTS_EX(getifaddrs EVENT__HAVE_GETIFADDRS) -CHECK_FUNCTION_EXISTS_EX(issetugid EVENT__HAVE_ISSETUGID) -CHECK_FUNCTION_EXISTS_EX(mach_absolute_time EVENT__HAVE_MACH_ABSOLUTE_TIME) -CHECK_FUNCTION_EXISTS_EX(nanosleep EVENT__HAVE_NANOSLEEP) -CHECK_FUNCTION_EXISTS_EX(usleep EVENT__HAVE_USLEEP) -CHECK_FUNCTION_EXISTS_EX(timeradd EVENT__HAVE_TIMERADD) -CHECK_FUNCTION_EXISTS_EX(timerclear EVENT__HAVE_TIMERCLEAR) -CHECK_FUNCTION_EXISTS_EX(timercmp EVENT__HAVE_TIMERCMP) -CHECK_FUNCTION_EXISTS_EX(timerfd_create EVENT__HAVE_TIMERFD_CREATE) -CHECK_FUNCTION_EXISTS_EX(timerisset EVENT__HAVE_TIMERISSET) -CHECK_FUNCTION_EXISTS_EX(putenv EVENT__HAVE_PUTENV) -CHECK_FUNCTION_EXISTS_EX(setenv EVENT__HAVE_SETENV) -CHECK_FUNCTION_EXISTS_EX(setrlimit EVENT__HAVE_SETRLIMIT) -CHECK_FUNCTION_EXISTS_EX(umask EVENT__HAVE_UMASK) -CHECK_FUNCTION_EXISTS_EX(unsetenv EVENT__HAVE_UNSETENV) +# Add stdio.h for vasprintf +set(EVENT_INCLUDES ${EVENT_INCLUDES} stdio.h) +CHECK_SYMBOLS_EXIST("${SYMBOLS_TO_CHECK}" "${EVENT_INCLUDES}" "EVENT") +unset(SYMBOLS_TO_CHECK) +set(EVENT__HAVE_EPOLL ${EVENT__HAVE_EPOLL_CREATE}) # Get the gethostbyname_r prototype. -CHECK_FUNCTION_EXISTS_EX(gethostbyname_r EVENT__HAVE_GETHOSTBYNAME_R) - if(EVENT__HAVE_GETHOSTBYNAME_R) CHECK_PROTOTYPE_DEFINITION(gethostbyname_r "int gethostbyname_r(const char *name, struct hostent *hp, struct hostent_data *hdata)" @@ -515,10 +539,10 @@ if(HAVE_PORT_H AND HAVE_PORT_CREATE) set(EVENT__HAVE_EVENT_PORTS 1) endif() -if(NOT WIN32) - CHECK_FUNCTION_EXISTS_EX(select EVENT__HAVE_SELECT) -endif() +# Only `CHECK_TYPE_SIZE()' will use `CMAKE_EXTRA_INCLUDE_FILES' +set(CMAKE_EXTRA_INCLUDE_FILES ${EVENT_INCLUDES}) +CHECK_TYPE_SIZE("struct sockaddr_un" EVENT__HAVE_STRUCT_SOCKADDR_UN) CHECK_TYPE_SIZE("uint8_t" EVENT__HAVE_UINT8_T) CHECK_TYPE_SIZE("uint16_t" EVENT__HAVE_UINT16_T) CHECK_TYPE_SIZE("uint32_t" EVENT__HAVE_UINT32_T) @@ -557,8 +581,6 @@ CHECK_SYMBOL_EXISTS("__FUNCTION__" "" EVENT__HAVE___FUNCTION__) CHECK_SYMBOL_EXISTS(TAILQ_FOREACH sys/queue.h EVENT__HAVE_TAILQFOREACH) CHECK_CONST_EXISTS(CTL_KERN sys/sysctl.h EVENT__HAVE_DECL_CTL_KERN) CHECK_CONST_EXISTS(KERN_ARND sys/sysctl.h EVENT__HAVE_DECL_KERN_ARND) -CHECK_CONST_EXISTS(KERN_RANDOM sys/sysctl.h EVENT__HAVE_DECL_KERN_RANDOM) -CHECK_CONST_EXISTS(RANDOM_UUID sys/sysctl.h EVENT__HAVE_DECL_RANDOM_UUID) CHECK_SYMBOL_EXISTS(F_SETFD fcntl.h EVENT__HAVE_SETFD) CHECK_TYPE_SIZE(fd_mask EVENT__HAVE_FD_MASK) @@ -927,19 +949,21 @@ configure_file( include(AddEventLibrary) add_event_library(event_core SOURCES ${SRC_CORE}) add_event_library(event_extra - LIBRARIES event_core_shared + INNER_LIBRARIES event_core SOURCES ${SRC_EXTRA}) if (NOT EVENT__DISABLE_OPENSSL) add_event_library(event_openssl - LIBRARIES event_core_shared ${OPENSSL_LIBRARIES} + INNER_LIBRARIES event_core + OUTER_INCLUDES ${OPENSSL_INCLUDE_DIR} + LIBRARIES ${OPENSSL_LIBRARIES} SOURCES ${SRC_OPENSSL}) endif() -if (CMAKE_USE_PTHREADS_INIT) +if (EVENT__HAVE_PTHREADS) set(SRC_PTHREADS evthread_pthread.c) add_event_library(event_pthreads - LIBRARIES event_core_shared + INNER_LIBRARIES event_core SOURCES ${SRC_PTHREADS}) endif() @@ -950,6 +974,18 @@ add_event_library(event SOURCES ${SRC_CORE} ${SRC_EXTRA}) set(WIN32_GETOPT) if (WIN32) + set(_TMPLIBS) + if (${EVENT_LIBRARY_STATIC}) + list(APPEND _TMPLIBS event_core_static event_static) + endif() + if (${EVENT_LIBRARY_SHARED}) + list(APPEND _TMPLIBS event_core_shared event_shared) + endif() + foreach(lib ${_TMPLIBS}) + target_link_libraries(${lib} iphlpapi) + endforeach() + unset(_TMPLIBS) + list(APPEND WIN32_GETOPT WIN32-Code/getopt.c WIN32-Code/getopt_long.c) @@ -969,6 +1005,9 @@ macro(add_sample_prog ssl name) if (${ssl}) target_link_libraries(${name} event_openssl) + if(WIN32) + target_link_libraries(${name} crypt32) + endif() endif() endmacro() if (NOT EVENT__DISABLE_SAMPLES) @@ -1064,7 +1103,7 @@ if (NOT EVENT__DISABLE_TESTS) DEPENDS event_rpcgen.py test/regress.rpc - COMMAND ${PYTHON_EXECUTABLE} ../event_rpcgen.py regress.rpc + COMMAND ${PYTHON_EXECUTABLE} ../event_rpcgen.py --quiet regress.rpc WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test) list(APPEND SRC_REGRESS @@ -1091,7 +1130,7 @@ if (NOT EVENT__DISABLE_TESTS) if (NOT EVENT__DISABLE_THREAD_SUPPORT) list(APPEND SRC_REGRESS test/regress_thread.c) endif() - elseif (CMAKE_USE_PTHREADS_INIT) + elseif (EVENT__HAVE_PTHREADS) list(APPEND SRC_REGRESS test/regress_thread.c) endif() @@ -1140,6 +1179,7 @@ if (NOT EVENT__DISABLE_TESTS) # - ellzey set(TESTPROGS test-changelist test-eof + test-closed test-fdleak test-init test-time @@ -1198,7 +1238,7 @@ if (NOT EVENT__DISABLE_TESTS) # Default environment variables turns off all event systems, # then we enable each one, one at a time when creating the tests. - set(DEFAULT_TEST_ENV_VARS "EVENT_SHOW_METHOD=1;") + set(DEFAULT_TEST_ENV_VARS) foreach(BACKEND ${BACKENDS}) set(BACKEND_ENV_VAR "EVENT_NO${BACKEND}=1") list(APPEND DEFAULT_TEST_ENV_VARS "${BACKEND_ENV_VAR}") @@ -1247,13 +1287,13 @@ if (NOT EVENT__DISABLE_TESTS) set(TEST_NAME regress__${BACKEND_TEST_NAME}) add_test(${TEST_NAME} - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress --quiet) set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ENV_VARS}") add_test(${TEST_NAME}_debug - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress --quiet) set_tests_properties(${TEST_NAME}_debug PROPERTIES ENVIRONMENT "${ENV_VARS};EVENT_DEBUG_MODE=1") @@ -1412,18 +1452,24 @@ endif() # Installation preparation. # -if(WIN32 AND NOT CYGWIN) - set(DEF_INSTALL_CMAKE_DIR cmake) -else() - set(DEF_INSTALL_CMAKE_DIR lib/cmake/libevent) -endif() - set(EVENT_INSTALL_CMAKE_DIR - "${CMAKE_INSTALL_PREFIX}/${DEF_INSTALL_CMAKE_DIR}" - CACHE PATH "Installation directory for CMake files") + "${CMAKE_INSTALL_PREFIX}/lib/cmake/libevent") export(PACKAGE libevent) +function(gen_package_config forinstall) + if(${forinstall}) + set(CONFIG_FOR_INSTALL_TREE 1) + set(dir "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}") + else() + set(CONFIG_FOR_INSTALL_TREE 0) + set(dir "${PROJECT_BINARY_DIR}") + endif() + configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfig.cmake.in + "${dir}/LibeventConfig.cmake" + @ONLY) +endfunction() + # Generate the config file for the build-tree. set(EVENT__INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/include" @@ -1433,26 +1479,10 @@ set(LIBEVENT_INCLUDE_DIRS ${EVENT__INCLUDE_DIRS} CACHE PATH "Libevent include directories") -configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfigBuildTree.cmake.in - ${PROJECT_BINARY_DIR}/LibeventConfig.cmake - @ONLY) +gen_package_config(0) # Generate the config file for the installation tree. -# Calculate the relative directory from the Cmake dir. -file(RELATIVE_PATH - REL_INCLUDE_DIR - "${EVENT_INSTALL_CMAKE_DIR}" - "${CMAKE_INSTALL_PREFIX}/include") - -# Note the LIBEVENT_CMAKE_DIR is defined in LibeventConfig.cmake.in, -# we escape it here so it's evaluated when it is included instead -# so that the include dirs are given relative to where the -# config file is located. -set(EVENT_INSTALL_INCLUDE_DIR "\${LIBEVENT_CMAKE_DIR}/${REL_INCLUDE_DIR}") - -configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfig.cmake.in - ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake - @ONLY) +gen_package_config(1) # Generate version info for both build-tree and install-tree. configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfigVersion.cmake.in @@ -1464,6 +1494,11 @@ install(FILES ${HDR_COMPAT} DESTINATION "include" COMPONENT dev) +# Install public headers +install(FILES ${HDR_PUBLIC} + DESTINATION "include/event2" + COMPONENT dev) + # Install the configs. install(FILES ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake @@ -1472,9 +1507,19 @@ install(FILES COMPONENT dev) # Install exports for the install-tree. -install(EXPORT LibeventTargets - DESTINATION "${DEF_INSTALL_CMAKE_DIR}" +macro(install_export type) + install(EXPORT LibeventTargets-${type} + NAMESPACE ${PROJECT_NAME}:: + DESTINATION "${EVENT_INSTALL_CMAKE_DIR}" COMPONENT dev) +endmacro() + +if (${EVENT_LIBRARY_STATIC}) + install_export(static) +endif() +if (${EVENT_LIBRARY_SHARED}) + install_export(shared) +endif() # Install the scripts. install(PROGRAMS @@ -1483,25 +1528,24 @@ install(PROGRAMS COMPONENT runtime) # Create documents with doxygen. -find_program(DOXYGEN doxygen) -if (DOXYGEN) - add_custom_target(doxygen - COMMAND ${DOXYGEN} Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -else() - message(WARNING "The doxygen target will not support since doxygen command was not found!") +option(EVENT__DOXYGEN + "Enables doxygen documentation" OFF) +if (EVENT__DOXYGEN) + include(UseDoxygen) + UseDoxygen() endif() -# Create the uninstall target. -# https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake -configure_file(${PROJECT_SOURCE_DIR}/cmake/Uninstall.cmake.in - ${PROJECT_BINARY_DIR}/Uninstall.cmake - @ONLY) - -add_custom_target(uninstall - COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/Uninstall.cmake) +if (NOT TARGET uninstall) + # Create the uninstall target. + # https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake + configure_file(${PROJECT_SOURCE_DIR}/cmake/Uninstall.cmake.in + ${PROJECT_BINARY_DIR}/Uninstall.cmake + @ONLY) + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${PROJECT_BINARY_DIR}/Uninstall.cmake) +endif() message(STATUS "") message(STATUS " ---( Libevent " ${EVENT_VERSION} " )---") diff --git a/ChangeLog b/ChangeLog index e89d5a8108..17874b14b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,84 @@ +Changes in version 2.1.12-stable (05 Jul 2020) + + This release contains mostly bug fixes (I decided not to port some features + that can be ported even without ABI breakage, if you cannot find feature that + you are interested in, please give us a note!) + + Since 2.1.12 libevent will use github actions as main CI, since + it recommends itself better then travis/appveyor (and had been removed from + upstream). + + Look carefully at "slightly touches the behaviour" section. + + Below you will find some of changes (this list has been cleaned up from the + patches that touches only tests and similar): + + CI: + o Backport github actions to 2.1 (be3acd7c Azat Khuzhin) + o Merge branch 'event_rpcgen.py-cleanup' (f0ded5f3, 48e04887 Enji Cooper) + o Add API/ABI checker (using LVC) (709210d4, 2af1f6cc yuangongji) + + test: + o tinytest: support timeout on Windows (794e8f75 yuangongji) + o Merge branch 'osx-clock' (e85afbe3 Azat Khuzhin) + o test-ratelim: calculate timers bias (for slow CPUs) to avoid false-positive (8ad26d0b Azat Khuzhin) + + fixes: + o buffer: do not pass NULL to memcpy() from evbuffer_pullup() (5b063049 Azat Khuzhin) + o http: fix undefined-shift in EVUTIL_IS*_ helpers (6b8d02a7 Azat Khuzhin) + o Check error code of evhttp_add_header_internal() in evhttp_parse_query_impl() (97e28f09 Azat Khuzhin) + o http: fix EVHTTP_CON_AUTOFREE in case of timeout (and some else) (1be25938 Azat Khuzhin) + o evdns: Add additional validation for values of dns options (c2972453 ayuseleznev) + o There is typo in GetAdaptersAddresses windows library. It should be iphlpapi.dll (891adda9 Aleksandr-Melnikov) + o Merge branch 'EV_CLOSED-and-EV_ET-fixes' (db2efdf5 Azat Khuzhin) + o Fix memory corruption in EV_CLOSURE_EVENT_FINALIZE with debug enabled (8ccd8f56 Jan Kasiak) + o increase segment refcnt only if evbuffer_add_file_segment() succeeds (30662a3c yuangongji) + o evdns: fix a crash when evdns_base with waiting requests is freed (6f8e0e97 ayuseleznev) + o event_base_once: fix potential null pointer threat (2e9ceb16 chenguolong) + o http: do not assume body for CONNECT (1b42270b Azat Khuzhin) + o evbuffer_add_file: fix freeing of segment in the error path (5f017bde Azat Khuzhin) + o Fix checking return value of the evdns_base_resolv_conf_parse() (fc51bf2c Azat Khuzhin) + o Merge branch 'fix-signal-leak' (poll/select now needs reinit) (1c9cc07b Azat Khuzhin) + + improvements: + o evutil_time: improve evutil_gettimeofday on Windows (a8219143 Nick Grifka) + o Support EV_CLOSED on linux for poll(2) (2530e7c6 Azat Khuzhin) + o Parse IPv6 scope IDs. (f602211f Philip Homburg) + o evutil_time: Implements usleep() using wait funtion on Windows (d42240d1 yuangongji) + o evutil_time: detect and use _gmtime64_s()/_gmtime64() (f4a6152c yuangongji) + + slightly touches the behaviour: + o bufferevent: allow setting priority on socket and openssl type (4dd3acdd Nicolas J. Bouliane) + o Fix EV_CLOSED detection/reporting (epoll only) (1df324d4 Azat Khuzhin) (XXX) + o Revert "Warn if forked from the event loop during event_reinit()" (71f5c0d3 Azat Khuzhin) + + samples: + o https-client: load certificates from the system cert store on Windows (e9478640 yuangongji) + + build fixes: + o Do not use sysctl.h on linux (it had been deprecated) (d2871a37 Azat Khuzhin) + o cmake: avoid problems from use of CMAKE_USE_PTHREADS_INIT (a62ec765 Paul Osborne) + o Update list of cmake files for autotools dist archive (2016f017 Azat Khuzhin) + o LibeventConfig.cmake: restore CMAKE_FIND_LIBRARY_SUFFIXES and LIBEVENT_STATIC_LINK default (640f9cf6 Mario Emmenlauer) + o cmake: fix getaddrinfo checking error (dea51c2e yuangongji) + o autoconf: fix getaddrinfo checking errors on mingw (b9bf7fa7 yuangongji) + o Do not use shared global structures on CYGWIN (8a9b5655 Azat Khuzhin) + o Added uninstall target check to cmakelists (3f1fb1f9 Dimo Markov) + o Fix compilation without OPENSSL_API_COMPAT (921bdcdd Azat Khuzhin) + o cmake: improve package config file (1c047618, baec84f2 yuangongji) + o Link with iphlpapi only on windows (976f7d34 Azat Khuzhin) + o autotools: fails build when need but can not find openssl (93174bb5 yuangongji) + o Merge branch 'http-connect' (e2424229 Azat Khuzhin) + o Fix compat with NetBSD >= 10 (5febb4e1 Kamil Rytarowski) + o cmake: fix getrandom() detection (e0e5f3bd Azat Khuzhin) + o arc4random: replace sysctl() with getrandom (on linux) (66ec78fd Azat Khuzhin) + o Upgrade autoconf (after upgrading minimum required to 2.67) (45da7d9d yuangongji) + o eliminate some C4267 warnings in Windows (9e468c77 yuangongji) + o autotools: attach doxygen target into all target (5d1e8570 yuangongji) + o cmake: attach doxygen target into all target (7a85300a yuangongji) + o Change the minimum version of automake to 1.13 and autoconf to 2.67 (fdb8fb66 ygj6) + o Add Uninstall.cmake.in into dist archive (877f2355 Azat Khuzhin) + Changes in version 2.1.11-stable (01 Aug 2019) This release contains one ABI breakage fix (that had been introduced in diff --git a/Doxyfile b/Doxyfile index d9d6603459..3f094f7213 100644 --- a/Doxyfile +++ b/Doxyfile @@ -17,11 +17,11 @@ # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = libevent +PROJECT_NAME = $(PROJECT)-$(VERSION) # Place all output under 'doxygen/' -OUTPUT_DIRECTORY = doxygen/ +OUTPUT_DIRECTORY = $(DOCDIR) # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style @@ -64,24 +64,24 @@ STRIP_FROM_PATH = include/ # with spaces. INPUT = \ - include/event2/buffer.h \ - include/event2/buffer_compat.h \ - include/event2/bufferevent.h \ - include/event2/bufferevent_compat.h \ - include/event2/bufferevent_ssl.h \ - include/event2/dns.h \ - include/event2/dns_compat.h \ - include/event2/event.h \ - include/event2/event_compat.h \ - include/event2/http.h \ - include/event2/http_compat.h \ - include/event2/listener.h \ - include/event2/rpc.h \ - include/event2/rpc_compat.h \ - include/event2/tag.h \ - include/event2/tag_compat.h \ - include/event2/thread.h \ - include/event2/util.h + $(SRCDIR)/include/event2/buffer.h \ + $(SRCDIR)/include/event2/buffer_compat.h \ + $(SRCDIR)/include/event2/bufferevent.h \ + $(SRCDIR)/include/event2/bufferevent_compat.h \ + $(SRCDIR)/include/event2/bufferevent_ssl.h \ + $(SRCDIR)/include/event2/dns.h \ + $(SRCDIR)/include/event2/dns_compat.h \ + $(SRCDIR)/include/event2/event.h \ + $(SRCDIR)/include/event2/event_compat.h \ + $(SRCDIR)/include/event2/http.h \ + $(SRCDIR)/include/event2/http_compat.h \ + $(SRCDIR)/include/event2/listener.h \ + $(SRCDIR)/include/event2/rpc.h \ + $(SRCDIR)/include/event2/rpc_compat.h \ + $(SRCDIR)/include/event2/tag.h \ + $(SRCDIR)/include/event2/tag_compat.h \ + $(SRCDIR)/include/event2/thread.h \ + $(SRCDIR)/include/event2/util.h #--------------------------------------------------------------------------- # configuration options related to the HTML output @@ -90,7 +90,7 @@ INPUT = \ # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. -GENERATE_HTML = YES +GENERATE_HTML = $(GENERATE_HTML) #--------------------------------------------------------------------------- # configuration options related to the LaTeX output @@ -99,7 +99,7 @@ GENERATE_HTML = YES # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. -GENERATE_LATEX = YES +GENERATE_LATEX = $(GENERATE_LATEX) # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be @@ -175,7 +175,7 @@ LATEX_HIDE_INDICES = NO # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages -GENERATE_MAN = NO +GENERATE_MAN = $(GENERATE_MAN) # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) diff --git a/Makefile.am b/Makefile.am index dd905026fd..21815008a4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,8 +5,8 @@ # See LICENSE for copying information. # 'foreign' means that we're not enforcing GNU package rules strictly. -# '1.11.2' means that we need automake 1.11.2 or later (and we do). -AUTOMAKE_OPTIONS = foreign 1.11.2 subdir-objects +# '1.13' means that we need automake 1.13 or later (and we do). +AUTOMAKE_OPTIONS = foreign 1.13 subdir-objects ACLOCAL_AMFLAGS = -I m4 @@ -38,7 +38,7 @@ RELEASE = -release 2.1 # # Once an RC is out, DO NOT MAKE ANY ABI-BREAKING CHANGES IN THAT SERIES # UNLESS YOU REALLY REALLY HAVE TO. -VERSION_INFO = 7:0:0 +VERSION_INFO = 7:1:0 # History: RELEASE VERSION_INFO # 2.0.1-alpha -- 2.0 1:0:0 @@ -75,6 +75,7 @@ VERSION_INFO = 7:0:0 # 2.1.9-beta-- 2.1 6:3:0 (No ABI change) # 2.1.10-stable-- 2.1 6:4:0 (No ABI change, WRONG) # 2.1.11-stable-- 2.1 7:0:0 (ABI changed) +# 2.1.12-stable-- 2.1 7:1:0 (No ABI change) # ABI version history for this package effectively restarts every time # we change RELEASE. Version 1.4.x had RELEASE of 1.4. @@ -113,8 +114,6 @@ CMAKE_FILES = \ cmake/CheckConstExists.cmake \ cmake/CheckFileOffsetBits.c \ cmake/CheckFileOffsetBits.cmake \ - cmake/CheckFunctionExistsEx.c \ - cmake/CheckFunctionExistsEx.cmake \ cmake/CheckFunctionKeywords.cmake \ cmake/CheckPrototypeDefinition.c.in \ cmake/CheckPrototypeDefinition.cmake \ @@ -122,10 +121,11 @@ CMAKE_FILES = \ cmake/CodeCoverage.cmake \ cmake/COPYING-CMAKE-SCRIPTS \ cmake/Copyright.txt \ - cmake/FindGit.cmake \ - cmake/LibeventConfigBuildTree.cmake.in \ cmake/LibeventConfig.cmake.in \ cmake/LibeventConfigVersion.cmake.in \ + cmake/Macros.cmake \ + cmake/Uninstall.cmake.in \ + cmake/UseDoxygen.cmake \ cmake/VersionViaGit.cmake \ event-config.h.cmake \ evconfig-private.h.cmake \ @@ -177,6 +177,7 @@ include test/include.am if BUILD_WIN32 +SYS_CORE_LIBS = -liphlpapi SYS_LIBS = -lws2_32 -lshell32 -ladvapi32 SYS_SRC = win32select.c buffer_iocp.c event_iocp.c \ bufferevent_async.c @@ -188,6 +189,7 @@ endif else +SYS_CORE_LIBS = SYS_LIBS = SYS_SRC = SYS_INCLUDES = @@ -264,11 +266,11 @@ AM_LDFLAGS = $(LIBEVENT_LDFLAGS) GENERIC_LDFLAGS = -version-info $(VERSION_INFO) $(RELEASE) $(NO_UNDEFINED) $(AM_LDFLAGS) libevent_la_SOURCES = $(CORE_SRC) $(EXTRAS_SRC) -libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) $(SYS_CORE_LIBS) libevent_la_LDFLAGS = $(GENERIC_LDFLAGS) libevent_core_la_SOURCES = $(CORE_SRC) -libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) $(SYS_CORE_LIBS) libevent_core_la_LDFLAGS = $(GENERIC_LDFLAGS) if PTHREADS @@ -336,9 +338,7 @@ endif verify: check -doxygen: FORCE - doxygen $(srcdir)/Doxyfile -FORCE: +include doxygen.am DISTCLEANFILES += *~ libevent.pc libevent_core.pc libevent_extra.pc ./include/event2/event-config.h diff --git a/README.md b/README.md index 8be37f4950..1247e220f5 100644 --- a/README.md +++ b/README.md @@ -464,6 +464,24 @@ fixing bugs: * jeremyerb * Fabrice Fontaine * wenyg + * Aleksandr-Melnikov + * ayuseleznev + * chenguolong + * Dimo Markov + * dota17 + * fanquake + * Jan Kasiak + * Kamil Rytarowski + * Mario Emmenlauer + * Michael Davidsaver + * mohuang + * Nick Grifka + * Nicolas J. Bouliane + * Paul Osborne + * Philip Homburg + * Wataru Ashihara + * William A Rowe Jr + * yangyongsheng If we have forgotten your name, please contact us. diff --git a/WIN32-Code/nmake/event2/event-config.h b/WIN32-Code/nmake/event2/event-config.h index 35ec16fd9f..3f3a0d2968 100644 --- a/WIN32-Code/nmake/event2/event-config.h +++ b/WIN32-Code/nmake/event2/event-config.h @@ -271,7 +271,7 @@ /* #undef EVENT__HAVE_WORKING_KQUEUE */ /* Numeric representation of the version */ -#define EVENT__NUMERIC_VERSION 0x02010b00 +#define EVENT__NUMERIC_VERSION 0x02010c00 /* Name of package */ #define EVENT__PACKAGE "libevent" @@ -332,7 +332,7 @@ #define EVENT__TIME_WITH_SYS_TIME 1 /* Version number of package */ -#define EVENT__VERSION "2.1.11-stable" +#define EVENT__VERSION "2.1.12-stable" /* 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. */ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index edfe337287..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,112 +0,0 @@ -version: 2.1.11.{build} - -os: Visual Studio 2017 -platform: - - x64 - -branches: - except: - - /.*travis.*/ - - /.*linux.*/ - - /.*freebsd.*/ - - /.*osx.*/ - - /.*bitrise.*/ -skip_commits: - message: /travis/ - files: - - .travis.yml - -environment: - global: - APPVEYOR_SAVE_CACHE_ON_ERROR: true - MINGW_ROOT: C:/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1 - OPENSSL_ROOT: C:/OpenSSL-Win64 - MPATH: C:/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/bin;C:/msys64/bin;C:/cygwin64/bin - EVENT_TESTS_PARALLEL: 20 - EVENT_BUILD_PARALLEL: 10 - matrix: - # !EVENT_ALLOW_FAILURE - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "" - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "-DEVENT__LIBRARY_TYPE=STATIC" - # EVENT_ALLOW_FAILURE - - EVENT_BUILD_METHOD: "autotools" - EVENT_CONFIGURE_OPTIONS: "" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "autotools" - EVENT_CONFIGURE_OPTIONS: "--disable-openssl" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "autotools" - EVENT_CONFIGURE_OPTIONS: "--disable-thread-support" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "autotools" - EVENT_CONFIGURE_OPTIONS: "--disable-debug-mode" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "autotools" - EVENT_CONFIGURE_OPTIONS: "--disable-malloc-replacement" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "-DEVENT__DISABLE_OPENSSL=ON" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "-DEVENT__DISABLE_THREAD_SUPPORT=ON" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "-DEVENT__DISABLE_DEBUG_MODE=ON" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "-DEVENT__DISABLE_MM_REPLACEMENT=ON" - EVENT_ALLOW_FAILURE: 1 - - EVENT_BUILD_METHOD: "cmake" - EVENT_CMAKE_OPTIONS: "-DCMAKE_C_FLAGS='-DUNICODE -D_UNICODE'" - EVENT_ALLOW_FAILURE: 1 - -matrix: - allow_failures: - - EVENT_ALLOW_FAILURE: 1 - fast_finish: true - -init: - - 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' - - 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' - - 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' - - 'echo PATH is: %PATH%' - -build_script: - - ps: | - if ($env:EVENT_BUILD_METHOD -eq 'autotools') { - $env:PATH="$env:MPATH;$env:OPENSSL_ROOT/bin;$env:PATH" - $env:LDFLAGS="-L$($env:OPENSSL_ROOT)/lib -L$($env:OPENSSL_ROOT)" - $env:CFLAGS="-I$($env:OPENSSL_ROOT)/include" - - bash ./autogen.sh 2>&1 3>&1 - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - - md build-autotools 2> $null - cd build-autotools - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - - bash ../configure $env:EVENT_CONFIGURE_OPTIONS 2>&1 - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - make -j $env:EVENT_BUILD_PARALLEL 2>&1 - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - make verify -j $env:EVENT_TESTS_PARALLEL 2>&1 - } else { - md build-cmake 2> $null - cd build-cmake - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - cmake -G "Visual Studio 15 2017 Win64" .. $env:EVENT_CMAKE_OPTIONS - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - cmake --build . -j $env:EVENT_BUILD_PARALLEL -- /nologo /verbosity:minimal - if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - ctest --output-on-failure -j $env:EVENT_TESTS_PARALLEL - } - -cache: - - build-cmake - - build-autotools - -on_failure: - - 7z a libevent.zip . - - appveyor PushArtifact libevent.zip diff --git a/arc4random.c b/arc4random.c index be64452ba3..8729f6b92f 100644 --- a/arc4random.c +++ b/arc4random.c @@ -63,6 +63,9 @@ #ifdef EVENT__HAVE_SYS_SYSCTL_H #include #endif +#ifdef EVENT__HAVE_SYS_RANDOM_H +#include +#endif #endif #include #include @@ -167,17 +170,11 @@ arc4_seed_win32(void) } #endif -#if defined(EVENT__HAVE_SYS_SYSCTL_H) && defined(EVENT__HAVE_SYSCTL) -#if EVENT__HAVE_DECL_CTL_KERN && EVENT__HAVE_DECL_KERN_RANDOM && EVENT__HAVE_DECL_RANDOM_UUID -#define TRY_SEED_SYSCTL_LINUX +#if defined(EVENT__HAVE_GETRANDOM) +#define TRY_SEED_GETRANDOM static int -arc4_seed_sysctl_linux(void) +arc4_seed_getrandom(void) { - /* Based on code by William Ahern, this function tries to use the - * RANDOM_UUID sysctl to get entropy from the kernel. This can work - * even if /dev/urandom is inaccessible for some reason (e.g., we're - * running in a chroot). */ - int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID }; unsigned char buf[ADD_ENTROPY]; size_t len, n; unsigned i; @@ -188,7 +185,7 @@ arc4_seed_sysctl_linux(void) for (len = 0; len < sizeof(buf); len += n) { n = sizeof(buf) - len; - if (0 != sysctl(mib, 3, &buf[len], &n, NULL, 0)) + if (0 == getrandom(&buf[len], n, 0)) return -1; } /* make sure that the buffer actually got set. */ @@ -202,8 +199,9 @@ arc4_seed_sysctl_linux(void) evutil_memclear_(buf, sizeof(buf)); return 0; } -#endif +#endif /* EVENT__HAVE_GETRANDOM */ +#if defined(EVENT__HAVE_SYS_SYSCTL_H) && defined(EVENT__HAVE_SYSCTL) #if EVENT__HAVE_DECL_CTL_KERN && EVENT__HAVE_DECL_KERN_ARND #define TRY_SEED_SYSCTL_BSD static int @@ -342,6 +340,10 @@ arc4_seed(void) if (0 == arc4_seed_win32()) ok = 1; #endif +#ifdef TRY_SEED_GETRANDOM + if (0 == arc4_seed_getrandom()) + ok = 1; +#endif #ifdef TRY_SEED_URANDOM if (0 == arc4_seed_urandom()) ok = 1; @@ -351,12 +353,6 @@ arc4_seed(void) 0 == arc4_seed_proc_sys_kernel_random_uuid()) ok = 1; #endif -#ifdef TRY_SEED_SYSCTL_LINUX - /* Apparently Linux is deprecating sysctl, and spewing warning - * messages when you try to use it. */ - if (!ok && 0 == arc4_seed_sysctl_linux()) - ok = 1; -#endif #ifdef TRY_SEED_SYSCTL_BSD if (0 == arc4_seed_sysctl_bsd()) ok = 1; diff --git a/buffer.c b/buffer.c index a51b6c5f66..3524b3504d 100644 --- a/buffer.c +++ b/buffer.c @@ -1421,9 +1421,11 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) for (; chain != NULL && (size_t)size >= chain->off; chain = next) { next = chain->next; - memcpy(buffer, chain->buffer + chain->misalign, chain->off); - size -= chain->off; - buffer += chain->off; + if (chain->buffer) { + memcpy(buffer, chain->buffer + chain->misalign, chain->off); + size -= chain->off; + buffer += chain->off; + } if (chain == last_with_data) removed_last_with_data = 1; if (&chain->next == buf->last_with_datap) @@ -3209,7 +3211,6 @@ evbuffer_add_file_segment(struct evbuffer *buf, } } } - ++seg->refcnt; EVLOCK_UNLOCK(seg->lock, 0); if (buf->freeze_end) @@ -3273,6 +3274,9 @@ evbuffer_add_file_segment(struct evbuffer *buf, chain->off = length; } + EVLOCK_LOCK(seg->lock, 0); + ++seg->refcnt; + EVLOCK_UNLOCK(seg->lock, 0); extra->segment = seg; buf->n_add_for_cb += length; evbuffer_chain_insert(buf, chain); diff --git a/bufferevent_sock.c b/bufferevent_sock.c index f275b02380..f40a8d9c57 100644 --- a/bufferevent_sock.c +++ b/bufferevent_sock.c @@ -650,7 +650,7 @@ bufferevent_priority_set(struct bufferevent *bufev, int priority) struct bufferevent_private *bufev_p = BEV_UPCAST(bufev); BEV_LOCK(bufev); - if (!BEV_IS_SOCKET(bufev)) + if (BEV_IS_ASYNC(bufev) || BEV_IS_FILTER(bufev) || BEV_IS_PAIR(bufev)) goto done; if (event_priority_set(&bufev->ev_read, priority) == -1) diff --git a/checkpatch.sh b/checkpatch.sh index 6eaa19c46e..5393799dcd 100755 --- a/checkpatch.sh +++ b/checkpatch.sh @@ -35,7 +35,7 @@ Example: OPTS: -p - treat as patch -f - treat as regular file - -f - treat as regular file and print diff + -d - treat as regular file and print diff -r - treat as git revision (default) -C - check using clang-format (default) -U - check with uncrustify @@ -164,8 +164,8 @@ function clang_style() local c="${options[cfg]}" echo "{ $(sed -e 's/#.*//' -e '/---/d' -e '/\.\.\./d' "$c" | tr $'\n' ,) }" } -function clang_format() { clang-format --style="$(clang_style)" "$@"; } -function clang_format_diff() { clang-format-diff --style="$(clang_style)" "$@"; } +function clang_format() { clang-format -style="$(clang_style)" "$@"; } +function clang_format_diff() { cat "$@" | clang-format-diff -p1 -style="$(clang_style)"; } # for non-bare repo will work function clang_format_git() { git format-patch --stdout "$@" -1 | clang_format_diff; } diff --git a/cmake/AddEventLibrary.cmake b/cmake/AddEventLibrary.cmake index 352c86ba58..04f5837e98 100644 --- a/cmake/AddEventLibrary.cmake +++ b/cmake/AddEventLibrary.cmake @@ -35,6 +35,45 @@ macro(generate_pkgconfig LIB_NAME) ) endmacro() +# LIB_NAME maybe event_core, event_extra, event_openssl, event_pthreads or event. +# Targets whose LIB_NAME is not 'event' should be exported and installed. +macro(export_install_target TYPE LIB_NAME OUTER_INCLUDES) + if("${LIB_NAME}" STREQUAL "event") + install(TARGETS "${LIB_NAME}_${TYPE}" + LIBRARY DESTINATION "lib" COMPONENT lib + ARCHIVE DESTINATION "lib" COMPONENT lib + RUNTIME DESTINATION "lib" COMPONENT lib + COMPONENT dev + ) + else() + string(REPLACE "event_" "" PURE_NAME ${LIB_NAME}) + string(TOUPPER ${TYPE} UPPER_TYPE) + list(APPEND LIBEVENT_${UPPER_TYPE}_LIBRARIES "${PURE_NAME}") + set(OUTER_INCS) + if (NOT "${OUTER_INCLUDES}" STREQUAL "NONE") + set(OUTER_INCS ${OUTER_INCLUDES}) + endif() + target_include_directories("${LIB_NAME}_${TYPE}" + PUBLIC "$" + "$" + "$" + ${OUTER_INCS} + ) + set_target_properties("${LIB_NAME}_${TYPE}" PROPERTIES EXPORT_NAME ${PURE_NAME}) + export(TARGETS "${LIB_NAME}_${TYPE}" + NAMESPACE ${PROJECT_NAME}:: + FILE "${PROJECT_BINARY_DIR}/LibeventTargets-${TYPE}.cmake" + APPEND + ) + install(TARGETS "${LIB_NAME}_${TYPE}" + EXPORT LibeventTargets-${TYPE} + LIBRARY DESTINATION "lib" COMPONENT lib + ARCHIVE DESTINATION "lib" COMPONENT lib + RUNTIME DESTINATION "lib" COMPONENT lib + COMPONENT dev + ) + endif() +endmacro() # Global variables that it uses: # - EVENT_ABI_LIBVERSION @@ -44,8 +83,6 @@ endmacro() # - EVENT_PACKAGE_RELEASE # - CMAKE_THREAD_LIBS_INIT LIB_PLATFORM # - OPENSSL_LIBRARIES -# - HDR_PUBLIC -# - EVENT_INSTALL_INCLUDE_DIR # - EVENT_SHARED_FLAGS # - EVENT_LIBRARY_STATIC # - EVENT_LIBRARY_SHARED @@ -57,11 +94,13 @@ macro(add_event_library LIB_NAME) cmake_parse_arguments(LIB "" # Options "VERSION" # One val - "SOURCES;LIBRARIES" # Multi val + "SOURCES;LIBRARIES;INNER_LIBRARIES;OUTER_INCLUDES" # Multi val ${ARGN} ) - set(ADD_EVENT_LIBRARY_TARGETS) + if ("${LIB_OUTER_INCLUDES}" STREQUAL "") + set(LIB_OUTER_INCLUDES NONE) + endif() set(ADD_EVENT_LIBRARY_INTERFACE) if (${EVENT_LIBRARY_STATIC}) @@ -69,12 +108,17 @@ macro(add_event_library LIB_NAME) set_target_properties("${LIB_NAME}_static" PROPERTIES OUTPUT_NAME "${LIB_NAME}" CLEAN_DIRECT_OUTPUT 1) - set_target_properties( - "${LIB_NAME}_static" PROPERTIES - PUBLIC_HEADER "${HDR_PUBLIC}") - list(APPEND LIBEVENT_STATIC_LIBRARIES "${LIB_NAME}_static") - list(APPEND ADD_EVENT_LIBRARY_TARGETS "${LIB_NAME}_static") + if(LIB_INNER_LIBRARIES) + set(INNER_LIBRARIES "${LIB_INNER_LIBRARIES}_static") + endif() + target_link_libraries("${LIB_NAME}_static" + ${CMAKE_THREAD_LIBS_INIT} + ${LIB_PLATFORM} + ${INNER_LIBRARIES} + ${LIB_LIBRARIES}) + + export_install_target(static "${LIB_NAME}" "${LIB_OUTER_INCLUDES}") set(ADD_EVENT_LIBRARY_INTERFACE "${LIB_NAME}_static") endif() @@ -82,9 +126,13 @@ macro(add_event_library LIB_NAME) if (${EVENT_LIBRARY_SHARED}) add_library("${LIB_NAME}_shared" SHARED ${LIB_SOURCES}) + if(LIB_INNER_LIBRARIES) + set(INNER_LIBRARIES "${LIB_INNER_LIBRARIES}_shared") + endif() target_link_libraries("${LIB_NAME}_shared" ${CMAKE_THREAD_LIBS_INIT} ${LIB_PLATFORM} + ${INNER_LIBRARIES} ${LIB_LIBRARIES}) if (EVENT_SHARED_FLAGS) @@ -110,14 +158,10 @@ macro(add_event_library LIB_NAME) "${LIB_NAME}_shared" PROPERTIES OUTPUT_NAME "${LIB_NAME}-${EVENT_PACKAGE_RELEASE}" VERSION "${CURRENT_MINUS_AGE}.${EVENT_ABI_LIBVERSION_AGE}.${EVENT_ABI_LIBVERSION_REVISION}" - SOVERSION "${CURRENT_MINUS_AGE}") + SOVERSION "${CURRENT_MINUS_AGE}" + INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") endif() - set_target_properties( - "${LIB_NAME}_shared" PROPERTIES - PUBLIC_HEADER "${HDR_PUBLIC}" - CLEAN_DIRECT_OUTPUT 1) - if (NOT WIN32) set(LIB_LINK_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}") @@ -127,33 +171,19 @@ macro(add_event_library LIB_NAME) COMMAND ${CMAKE_COMMAND} -E create_symlink "$" "${LIB_LINK_NAME}" - WORKING_DIRECTORY "lib") + WORKING_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") endif() - list(APPEND LIBEVENT_SHARED_LIBRARIES "${LIB_NAME}_shared") - list(APPEND ADD_EVENT_LIBRARY_TARGETS "${LIB_NAME}_shared") + export_install_target(shared "${LIB_NAME}" "${LIB_OUTER_INCLUDES}") set(ADD_EVENT_LIBRARY_INTERFACE "${LIB_NAME}_shared") - endif() - export(TARGETS ${ADD_EVENT_LIBRARY_TARGETS} - FILE "${PROJECT_BINARY_DIR}/LibeventTargets.cmake" - APPEND - ) - - install(TARGETS ${ADD_EVENT_LIBRARY_TARGETS} - EXPORT LibeventTargets - LIBRARY DESTINATION "lib" COMPONENT lib - ARCHIVE DESTINATION "lib" COMPONENT lib - RUNTIME DESTINATION "lib" COMPONENT lib - PUBLIC_HEADER DESTINATION "include/event2" - COMPONENT dev - ) - if (NOT WIN32 AND ${EVENT_LIBRARY_SHARED}) - install(FILES - "$/${LIB_LINK_NAME}" - DESTINATION "lib" - COMPONENT lib) + if (NOT WIN32) + install(FILES + "$/${LIB_LINK_NAME}" + DESTINATION "lib" + COMPONENT lib) + endif() endif() add_library(${LIB_NAME} INTERFACE) diff --git a/cmake/CheckFunctionExistsEx.c b/cmake/CheckFunctionExistsEx.c deleted file mode 100644 index 224e340412..0000000000 --- a/cmake/CheckFunctionExistsEx.c +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef CHECK_FUNCTION_EXISTS - -#ifdef __cplusplus -extern "C" -#endif - char - CHECK_FUNCTION_EXISTS(void); -#ifdef __CLASSIC_C__ -int main() -{ - int ac; - char* av[]; -#else -int main(int ac, char* av[]) -{ -#endif - CHECK_FUNCTION_EXISTS(); - if (ac > 1000) { - return *av[0]; - } - return 0; -} - -#else /* CHECK_FUNCTION_EXISTS */ - -#error "CHECK_FUNCTION_EXISTS has to specify the function" - -#endif /* CHECK_FUNCTION_EXISTS */ diff --git a/cmake/CheckFunctionExistsEx.cmake b/cmake/CheckFunctionExistsEx.cmake deleted file mode 100644 index 78bc2ecc1b..0000000000 --- a/cmake/CheckFunctionExistsEx.cmake +++ /dev/null @@ -1,69 +0,0 @@ -# - Check if a C function can be linked -# CHECK_FUNCTION_EXISTS( ) -# -# Check that the is provided by libraries on the system and -# store the result in a . This does not verify that any -# system header file declares the function, only that it can be found -# at link time (considure using CheckSymbolExists). -# -# The following variables may be set before calling this macro to -# modify the way the check is run: -# -# CMAKE_REQUIRED_FLAGS = string of compile command line flags -# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) -# CMAKE_REQUIRED_INCLUDES = list of include directories -# CMAKE_REQUIRED_LIBRARIES = list of libraries to link - -#============================================================================= -# Copyright 2002-2011 Kitware, Inc. -# -# 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.) - -MACRO(CHECK_FUNCTION_EXISTS_EX FUNCTION VARIABLE) - IF(${VARIABLE} MATCHES "^${VARIABLE}$") - SET(MACRO_CHECK_FUNCTION_DEFINITIONS - "-DCHECK_FUNCTION_EXISTS=${FUNCTION} ${CMAKE_REQUIRED_FLAGS}") - MESSAGE(STATUS "Looking for ${FUNCTION}") - IF(CMAKE_REQUIRED_LIBRARIES) - SET(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - ELSE(CMAKE_REQUIRED_LIBRARIES) - SET(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES) - ENDIF(CMAKE_REQUIRED_LIBRARIES) - IF(CMAKE_REQUIRED_INCLUDES) - SET(CHECK_FUNCTION_EXISTS_ADD_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - ELSE(CMAKE_REQUIRED_INCLUDES) - SET(CHECK_FUNCTION_EXISTS_ADD_INCLUDES) - ENDIF(CMAKE_REQUIRED_INCLUDES) - TRY_COMPILE(${VARIABLE} - ${CMAKE_BINARY_DIR} - ${PROJECT_SOURCE_DIR}/cmake/CheckFunctionExistsEx.c - COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} - "${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES}" - "${CHECK_FUNCTION_EXISTS_ADD_INCLUDES}" - OUTPUT_VARIABLE OUTPUT) - IF(${VARIABLE}) - SET(${VARIABLE} 1 CACHE INTERNAL "Have function ${FUNCTION}") - MESSAGE(STATUS "Looking for ${FUNCTION} - found") - FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Determining if the function ${FUNCTION} exists passed with the following output:\n" - "${OUTPUT}\n\n") - ELSE(${VARIABLE}) - MESSAGE(STATUS "Looking for ${FUNCTION} - not found") - SET(${VARIABLE} "" CACHE INTERNAL "Have function ${FUNCTION}") - FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Determining if the function ${FUNCTION} exists failed with the following output:\n" - "${OUTPUT}\n\n") - ENDIF(${VARIABLE}) - ENDIF() -ENDMACRO(CHECK_FUNCTION_EXISTS_EX) diff --git a/cmake/FindGit.cmake b/cmake/FindGit.cmake deleted file mode 100644 index 2abbfe4e9d..0000000000 --- a/cmake/FindGit.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# The module defines the following variables: -# GIT_EXECUTABLE - path to git command line client -# GIT_FOUND - true if the command line client was found -# Example usage: -# find_package(Git) -# if(GIT_FOUND) -# message("git found: ${GIT_EXECUTABLE}") -# endif() - -#============================================================================= -# Copyright 2010 Kitware, Inc. -# -# 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 distributed this file outside of CMake, substitute the full -# License text for the above reference.) - -# Look for 'git' or 'eg' (easy git) -set(git_names git eg) - -# Prefer .cmd variants on Windows unless running in a Makefile -# in the MSYS shell. -if(WIN32) - if(NOT CMAKE_GENERATOR MATCHES "MSYS") - set(git_names git.cmd git eg.cmd eg) - endif() -endif() - -find_program(GIT_EXECUTABLE - NAMES ${git_names} - DOC "git command line client") - -mark_as_advanced(GIT_EXECUTABLE) - -# Handle the QUIETLY and REQUIRED arguments and set GIT_FOUND to TRUE if -# all listed variables are TRUE - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Git DEFAULT_MSG GIT_EXECUTABLE) - diff --git a/cmake/LibeventConfig.cmake.in b/cmake/LibeventConfig.cmake.in index 54223360a4..7b808c3245 100644 --- a/cmake/LibeventConfig.cmake.in +++ b/cmake/LibeventConfig.cmake.in @@ -1,18 +1,183 @@ # - Config file for the Libevent package # It defines the following variables -# LIBEVENT_INCLUDE_DIRS - include directories +# LIBEVENT_FOUND - true if libevent and all required components found on the system +# LIBEVENT_xxx_FOUND - true if component xxx(see available components) found on the system +# LIBEVENT_VERSION - libevent version in format Major.Minor.Patch +# LIBEVENT_INCLUDE_DIRS - directories where libevent header is located. +# LIBEVENT_INCLUDE_DIR - same as DIRS +# LIBEVENT_LIBRARIES - libevent library to link against. +# LIBEVENT_LIBRARY - same as LIBRARIES +# +# These variables are deprecated, don't use them. # LIBEVENT_STATIC_LIBRARIES - libraries to link against (archive/static) # LIBEVENT_SHARED_LIBRARIES - libraries to link against (shared) +# +# When you try to locate the libevent libraries, you should specify which components you want to use. +# The following table lists all available components. If none is given, all imported targets will used. +# core - the core functons of libevent +# extra - extra functions, contains http, dns and rpc +# pthreads - multiple threads for libevent, not exists on Windows +# openssl - openssl support for libevent +# +# By default, the shared libraries of libevent will be found. To find the static ones instead, +# you must set the LIBEVENT_STATIC_LINK variable to TRUE before calling find_package(Libevent ...). +# If no component provided, all components will be used. +# example: +# set(LIBEVENT_STATIC_LINK TRUE) +# find_package(Libevent 2.2 REQUIRED COMPONENTS core) +# include_directories(${LIBEVENT_INCLUDE_DIRS}) # Can be omitted +# target_link_libraries(myapp ${LIBEVENT_LIBRARIES}) +# or target_link_libraries(myapp libevent::core) +# +# find_package() can handle dependencies automatically. For example, given the 'openssl' component, +# all dependencies (libevent_core, libssl, libcrypto and openssl include directories) will be found. -# Get the path of the current file. -get_filename_component(LIBEVENT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -# Set the include directories. -set(LIBEVENT_INCLUDE_DIRS "@EVENT_INSTALL_INCLUDE_DIR@") +set(CONFIG_FOR_INSTALL_TREE @CONFIG_FOR_INSTALL_TREE@) -# Include the project Targets file, this contains definitions for IMPORTED targets. -include(${LIBEVENT_CMAKE_DIR}/LibeventTargets.cmake) +set(LIBEVENT_VERSION @EVENT_PACKAGE_VERSION@) # IMPORTED targets from LibeventTargets.cmake set(LIBEVENT_STATIC_LIBRARIES "@LIBEVENT_STATIC_LIBRARIES@") set(LIBEVENT_SHARED_LIBRARIES "@LIBEVENT_SHARED_LIBRARIES@") + +# Default to the same type as libevent was built: +if(NOT DEFINED LIBEVENT_STATIC_LINK) + set(LIBEVENT_STATIC_LINK NOT @EVENT_LIBRARY_SHARED@) +endif() + +set(CMAKE_FIND_LIBRARY_SUFFIXES_SAVE "${CMAKE_FIND_LIBRARY_SUFFIXES}") +if(${LIBEVENT_STATIC_LINK}) + set(_LIB_TYPE static) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(_AVAILABLE_LIBS "${LIBEVENT_STATIC_LIBRARIES}") +else() + set(_LIB_TYPE shared) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(_AVAILABLE_LIBS "${LIBEVENT_SHARED_LIBRARIES}") +endif() + +# Get the path of the current file. +get_filename_component(LIBEVENT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_INSTALL_PREFIX "${LIBEVENT_CMAKE_DIR}/../../.." ABSOLUTE) + +macro(message_if_needed _flag _msg) + if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + message(${_flag} "${_msg}") + endif() +endmacro() + +macro(no_component_msg _comp) + if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED_${_comp}) + set(pthreadlib) + if(NOT WIN32) + set(pthreadlib ", pthreads") + endif() + message(FATAL_ERROR "Your libevent library does not contain a ${_comp} component!\n" + "The valid components are core, extra${pthreadlib} and openssl.") + else() + message_if_needed(WARNING "Your libevent library does not contain a ${_comp} component!") + endif() +endmacro() + +set(_EVENT_COMPONENTS) +if(${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS) + list(REMOVE_DUPLICATES ${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS) + foreach(_comp ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS}) + list(FIND _AVAILABLE_LIBS ${_comp} _INDEX) + if(_INDEX GREATER -1) + list(APPEND _EVENT_COMPONENTS ${_comp}) + else() + no_component_msg(${_comp}) + endif() + endforeach() +else() + set(_EVENT_COMPONENTS ${_AVAILABLE_LIBS}) +endif() + +set(_POSSIBLE_PKG_NAMES) +list(APPEND _POSSIBLE_PKG_NAMES ${CMAKE_FIND_PACKAGE_NAME} LIBEVENT Libevent libevent) +list(REMOVE_DUPLICATES _POSSIBLE_PKG_NAMES) + +macro(set_case_insensitive_found _comp) + foreach(name ${_POSSIBLE_PKG_NAMES}) + if("${_comp}" STREQUAL "") + set(${name}_FOUND TRUE) + set(${name}_NOTFOUND FALSE) + else() + set(${name}_${_comp}_FOUND TRUE) + set(${name}_${_comp}_NOTFOUND FALSE) + endif() + endforeach() +endmacro() + +if(CONFIG_FOR_INSTALL_TREE) + ## Config for install tree ---------------------------------------- + # Find includes + unset(_event_h CACHE) + find_path(_event_h + NAMES event2/event.h + PATHS "${_INSTALL_PREFIX}/include" + NO_DEFAULT_PATH) + if(_event_h) + set(LIBEVENT_INCLUDE_DIRS "${_event_h}") + message_if_needed(STATUS "Found libevent include directory: ${_event_h}") + else() + message_if_needed(WARNING "Your libevent library does not contain header files!") + endif() + + # Find libraries + macro(find_event_lib _comp) + unset(_event_lib CACHE) + find_library(_event_lib + NAMES "event_${_comp}" + PATHS "${_INSTALL_PREFIX}/lib" + NO_DEFAULT_PATH) + if(_event_lib) + list(APPEND LIBEVENT_LIBRARIES "libevent::${_comp}") + set_case_insensitive_found(${_comp}) + message_if_needed(STATUS "Found libevent component: ${_event_lib}") + else() + no_component_msg(${_comp}) + endif() + endmacro() + + foreach(comp ${_EVENT_COMPONENTS}) + find_event_lib(${comp}) + endforeach() +else() + ## Config for build tree ---------------------------------------- + set(LIBEVENT_INCLUDE_DIRS "@EVENT__INCLUDE_DIRS@") + foreach(_comp ${_EVENT_COMPONENTS}) + list(APPEND LIBEVENT_LIBRARIES "libevent::${_comp}") + set_case_insensitive_found(${_comp}) + endforeach() +endif() + +set(LIBEVENT_INCLUDE_DIR ${LIBEVENT_INCLUDE_DIRS}) +if(LIBEVENT_LIBRARIES) + set(LIBEVENT_LIBRARY ${LIBEVENT_LIBRARIES}) + if(CONFIG_FOR_INSTALL_TREE) + message_if_needed(STATUS "Found libevent ${LIBEVENT_VERSION} in ${_INSTALL_PREFIX}") + else() + message_if_needed(STATUS "Found libevent ${LIBEVENT_VERSION} in ${LIBEVENT_CMAKE_DIR}") + endif() + + # Avoid including targets more than one times + if(NOT TARGET event_core_${_LIB_TYPE}) + # Include the project Targets file, this contains definitions for IMPORTED targets. + include(${LIBEVENT_CMAKE_DIR}/LibeventTargets-${_LIB_TYPE}.cmake) + endif() +else() + if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "Can not find any libraries for libevent.") + else() + message_if_needed(WARNING "Can not find any libraries for libevent.") + endif() +endif() + +set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES_SAVE}") +unset(_LIB_TYPE) +unset(_AVAILABLE_LIBS) +unset(_EVENT_COMPONENTS) +unset(_POSSIBLE_PKG_NAMES) +unset(_INSTALL_PREFIX) diff --git a/cmake/LibeventConfigBuildTree.cmake.in b/cmake/LibeventConfigBuildTree.cmake.in deleted file mode 100644 index 02edef32fc..0000000000 --- a/cmake/LibeventConfigBuildTree.cmake.in +++ /dev/null @@ -1,17 +0,0 @@ -# - Config file for the Libevent package -# It defines the following variables -# LIBEVENT_INCLUDE_DIRS - include directories for FooBar -# LIBEVENT_LIBRARIES - libraries to link against - -# Get the path of the current file. -get_filename_component(LIBEVENT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -# Set the include directories. -set(LIBEVENT_INCLUDE_DIRS "@EVENT__INCLUDE_DIRS@") - -# Include the project Targets file, this contains definitions for IMPORTED targets. -include(${LIBEVENT_CMAKE_DIR}/LibeventTargets.cmake) - -# IMPORTED targets from LibeventTargets.cmake -set(LIBEVENT_LIBRARIES event event_core event_extra) - diff --git a/cmake/Macros.cmake b/cmake/Macros.cmake new file mode 100644 index 0000000000..e480bbfdca --- /dev/null +++ b/cmake/Macros.cmake @@ -0,0 +1,36 @@ +include(CheckSymbolExists) +include(CheckIncludeFiles) + +# Check if each symbol in the symbol list exists, +# and define PREFIX__HAVE_SYMNAME to 1 if yes. +# +# SYMLIST: list of symbols to check +# HEADERS: header files to be included in check code +# PREFIX: the prefix of definition +macro(CHECK_SYMBOLS_EXIST SYMLIST HEADERS PREFIX) + foreach(SYMNAME ${SYMLIST}) + string(TOUPPER "${SYMNAME}" SYMNAME_UPPER) + if ("${PREFIX}" STREQUAL "") + set(HAVE_SYM_DEF "HAVE_${SYMNAME_UPPER}") + else() + set(HAVE_SYM_DEF "${PREFIX}__HAVE_${SYMNAME_UPPER}") + endif() + CHECK_SYMBOL_EXISTS(${SYMNAME} "${HEADERS}" ${HAVE_SYM_DEF}) + endforeach() +endmacro() + +# Check if file exists, define PREFIX__HAVE_FILE to 1 if yes, +# and collect file to EVENT_INCLUDES +macro(CHECK_INCLUDE_FILE_CONCAT FILE PREFIX) + string(REGEX REPLACE "[./]" "_" FILE_UL ${FILE}) + string(TOUPPER "${FILE_UL}" FILE_UL_UPPER) + if ("${PREFIX}" STREQUAL "") + set(HAVE_FILE_DEF "HAVE_${FILE_UL_UPPER}") + else() + set(HAVE_FILE_DEF "${PREFIX}__HAVE_${FILE_UL_UPPER}") + endif() + CHECK_INCLUDE_FILES("${EVENT_INCLUDES};${FILE}" ${HAVE_FILE_DEF}) + if(${HAVE_FILE_DEF}) + set(EVENT_INCLUDES ${EVENT_INCLUDES} ${FILE}) + endif() +endmacro() diff --git a/cmake/UseDoxygen.cmake b/cmake/UseDoxygen.cmake new file mode 100644 index 0000000000..3b60d5a0f5 --- /dev/null +++ b/cmake/UseDoxygen.cmake @@ -0,0 +1,111 @@ +# Use FindDoxygen.cmake to generate documentation. + +option(DOXYGEN_GENERATE_HTML "Generate HTML" ON) +option(DOXYGEN_GENERATE_MAN "Generate man pages" OFF) +option(DOXYGEN_MAN_LINKS "Generate man links" ON) +option(DOXYGEN_GENERATE_LATEX "Generate LaTeX" OFF) + +# If the case-insensitive value of the cmake option is one of +# "off, no, false" or 0, it is equal to false, otherwise true. +# And the values of the doxygen config does not exactly match it. +# So we need to convert the cmake option to a doxygen config. +macro(_convert_to_dx_cfg CMK_OPTION) + if (${CMK_OPTION}) + set(${CMK_OPTION} YES) + else() + set(${CMK_OPTION} NO) + endif() +endmacro() + +macro(UseDoxygen) + if (${CMAKE_VERSION} VERSION_LESS "3.9") + # Old versions of cmake have poor support for Doxygen generation. + message(FATAL_ERROR "Doxygen generation only enabled for cmake 3.9 and higher") + else() + find_package(Doxygen) + if (DOXYGEN_FOUND) + set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME}) + set(DOXYGEN_PROJECT_NUMBER ${EVENT_PACKAGE_VERSION}) + set(DOXYGEN_PROJECT_BRIEF "Event notification library") + set(DOXYGEN_OUTPUT_DIRECTORY doxygen) + set(DOXYGEN_STRIP_FROM_PATH include) + set(DOXYGEN_JAVADOC_AUTOBRIEF YES) + set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) + set(DOXYGEN_SORT_BRIEF_DOCS YES) + set(DOXYGEN_RECURSIVE NO) + + _convert_to_dx_cfg(DOXYGEN_GENERATE_HTML) + _convert_to_dx_cfg(DOXYGEN_GENERATE_MAN) + _convert_to_dx_cfg(DOXYGEN_MAN_LINKS) + _convert_to_dx_cfg(DOXYGEN_GENERATE_LATEX) + + set(DOXYGEN_LATEX_CMD_NAME latex) + set(DOXYGEN_PAPER_TYPE a4wide) + set(DOXYGEN_PDF_HYPERLINKS NO) + + set(DOXYGEN_GENERATE_RTF NO) + set(DOXYGEN_GENERATE_XML NO) + set(DOXYGEN_GENERATE_CHI NO) + + set(DOXYGEN_PREDEFINED TAILQ_ENTRY + RB_ENTRY + EVENT_DEFINED_TQENTRY_ + EVENT_IN_DOXYGEN_ + ) + + set(DOX_INPUT include/event2/buffer.h + include/event2/buffer_compat.h + include/event2/bufferevent.h + include/event2/bufferevent_compat.h + include/event2/bufferevent_ssl.h + include/event2/dns.h + include/event2/dns_compat.h + include/event2/event.h + include/event2/event_compat.h + include/event2/http.h + include/event2/http_compat.h + include/event2/listener.h + include/event2/rpc.h + include/event2/rpc_compat.h + include/event2/tag.h + include/event2/tag_compat.h + include/event2/thread.h + include/event2/util.h + ) + # Add 'doxygen' target + doxygen_add_docs(doxygen + ${DOX_INPUT} + ALL + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Generating doxygen documentation for ${PROJECT_NAME}..." + ) + + # Use 'make clean' to remove the generated directory + set_property(DIRECTORY + PROPERTY ADDITIONAL_MAKE_CLEAN_FILES + "${PROJECT_BINARY_DIR}/${DOXYGEN_OUTPUT_DIRECTORY}" + ) + + # Install html into /share/doc/ + if ("${DOXYGEN_GENERATE_HTML}" STREQUAL "YES") + install(DIRECTORY + ${PROJECT_BINARY_DIR}/${DOXYGEN_OUTPUT_DIRECTORY}/html + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME} + COMPONENT doc + ) + endif() + + # Install manual into /share/man/man3 + if ("${DOXYGEN_GENERATE_MAN}" STREQUAL "YES") + install(DIRECTORY + ${PROJECT_BINARY_DIR}/${DOXYGEN_OUTPUT_DIRECTORY}/man/man3 + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man + COMPONENT doc + ) + endif() + + else(DOXYGEN_FOUND) + message(FATAL_ERROR "Doxygen command not found, set EVENT__DOXYGEN to disable") + endif (DOXYGEN_FOUND) + endif() +endmacro() diff --git a/cmake/VersionViaGit.cmake b/cmake/VersionViaGit.cmake index 504980ad1f..24eb6af9b7 100644 --- a/cmake/VersionViaGit.cmake +++ b/cmake/VersionViaGit.cmake @@ -23,7 +23,7 @@ macro(event_fuzzy_version_from_git) # set our defaults. set(EVENT_GIT___VERSION_MAJOR 2) set(EVENT_GIT___VERSION_MINOR 1) - set(EVENT_GIT___VERSION_PATCH 11) + set(EVENT_GIT___VERSION_PATCH 12) set(EVENT_GIT___VERSION_STAGE "stable") find_package(Git) @@ -31,7 +31,7 @@ macro(event_fuzzy_version_from_git) if (GIT_FOUND) execute_process( COMMAND - ${GIT_EXECUTABLE} describe --abbrev=0 + ${GIT_EXECUTABLE} describe --abbrev=0 --always WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE @@ -42,7 +42,9 @@ macro(event_fuzzy_version_from_git) ) string(REGEX REPLACE "[\\._-]" ";" VERSION_LIST "${GITVERSION}") - list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) + if(VERSION_LIST) + list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) + endif() if ((GITRET EQUAL 0) AND (VERSION_LIST_LENGTH EQUAL 5)) list(GET VERSION_LIST 1 _MAJOR) diff --git a/configure.ac b/configure.ac index 298d3ab9da..d00e063a14 100644 --- a/configure.ac +++ b/configure.ac @@ -5,30 +5,23 @@ dnl See LICENSE for copying information. dnl dnl Original version Dug Song -AC_INIT(libevent,2.1.11-stable) -AC_PREREQ(2.62) +AC_INIT(libevent,2.1.12-stable) +AC_PREREQ(2.67) AC_CONFIG_SRCDIR(event.c) AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE dnl AM_SILENT_RULES req. automake 1.11. [no] defaults V=1 -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS(config.h evconfig-private.h:evconfig-private.h.in) -AC_DEFINE(NUMERIC_VERSION, 0x02010b00, [Numeric representation of the version]) +AC_DEFINE(NUMERIC_VERSION, 0x02010c00, [Numeric representation of the version]) dnl Initialize prefix. -if test "$prefix" = "NONE"; then - prefix="/usr/local" -fi +AC_PREFIX_DEFAULT([/usr/local]) dnl Try and get a full POSIX environment on obscure systems -ifdef([AC_USE_SYSTEM_EXTENSIONS], [ AC_USE_SYSTEM_EXTENSIONS -], [ -AC_AIX -AC_GNU_SOURCE -AC_MINIX -]) AC_CANONICAL_BUILD AC_CANONICAL_HOST @@ -48,11 +41,7 @@ AC_PROG_INSTALL AC_PROG_LN_S # AC_PROG_MKDIR_P - $(MKDIR_P) should be defined by AM_INIT_AUTOMAKE -# AC_PROG_SED is only available in Autoconf >= 2.59b; workaround for older -# versions -ifdef([AC_PROG_SED], [AC_PROG_SED], [ -AC_CHECK_PROGS(SED, [gsed sed]) -]) +AC_PROG_SED AC_PROG_GCC_TRADITIONAL @@ -126,7 +115,8 @@ AC_ARG_ENABLE([clock-gettime], [], [enable_clock_gettime=yes]) -AC_PROG_LIBTOOL +LT_PREREQ([2.4.2]) +LT_INIT dnl Uncomment "AC_DISABLE_SHARED" to make shared libraries not get dnl built by default. You can also turn shared libs on and off from @@ -150,40 +140,46 @@ AC_SEARCH_LIBS([sendfile], [sendfile]) dnl - check if the macro _WIN32 is defined on this compiler. dnl - (this is how we check for a windows compiler) AC_MSG_CHECKING(for WIN32) -AC_TRY_COMPILE(, - [ +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([], + [ #ifndef _WIN32 die horribly #endif - ], - bwin32=true; AC_MSG_RESULT(yes), - bwin32=false; AC_MSG_RESULT(no), + ] + )], + [bwin32=true; AC_MSG_RESULT(yes)], + [bwin32=false; AC_MSG_RESULT(no)] ) dnl - check if the macro __midipix__ is defined on this compiler. dnl - (this is how we check for a midipix version of GCC) AC_MSG_CHECKING(for MIDIPIX) -AC_TRY_COMPILE(, - [ +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([], + [ #ifndef __midipix__ die horribly #endif - ], - midipix=true; AC_MSG_RESULT(yes), - midipix=false; AC_MSG_RESULT(no), + ] + )], + [midipix=true; AC_MSG_RESULT(yes)], + [midipix=false; AC_MSG_RESULT(no)] ) dnl - check if the macro __CYGWIN__ is defined on this compiler. dnl - (this is how we check for a cygwin version of GCC) AC_MSG_CHECKING(for CYGWIN) -AC_TRY_COMPILE(, - [ +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([], + [ #ifndef __CYGWIN__ die horribly #endif - ], - cygwin=true; AC_MSG_RESULT(yes), - cygwin=false; AC_MSG_RESULT(no), + ] + )], + [cygwin=true; AC_MSG_RESULT(yes)], + [cygwin=false; AC_MSG_RESULT(no)] ) AC_CHECK_HEADERS([zlib.h]) @@ -226,6 +222,7 @@ AC_CHECK_HEADERS([ \ fcntl.h \ ifaddrs.h \ mach/mach_time.h \ + mach/mach.h \ netdb.h \ netinet/in.h \ netinet/in6.h \ @@ -252,14 +249,21 @@ AC_CHECK_HEADERS([ \ sys/timerfd.h \ sys/uio.h \ sys/wait.h \ + sys/random.h \ errno.h \ + afunix.h \ ]) -AC_CHECK_HEADERS(sys/sysctl.h, [], [], [ -#ifdef HAVE_SYS_PARAM_H -#include -#endif -]) +case "${host_os}" in + linux*) ;; + *) + AC_CHECK_HEADERS(sys/sysctl.h, [], [], [ + #ifdef HAVE_SYS_PARAM_H + #include + #endif + ]) +esac + if test "x$ac_cv_header_sys_queue_h" = "xyes"; then AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h) AC_EGREP_CPP(yes, @@ -332,7 +336,7 @@ if test "x$ac_cv_header_sys_time_h" = "xyes"; then fi if test "x$ac_cv_header_sys_sysctl_h" = "xyes"; then - AC_CHECK_DECLS([CTL_KERN, KERN_RANDOM, RANDOM_UUID, KERN_ARND], [], [], + AC_CHECK_DECLS([CTL_KERN, KERN_ARND], [], [], [[#include #include ]] ) @@ -344,7 +348,7 @@ AM_CONDITIONAL(BUILD_MIDIPIX, test x$midipix = xtrue) AM_CONDITIONAL(BUILD_WITH_NO_UNDEFINED, test x$bwin32 = xtrue || test x$cygwin = xtrue || test x$midipix = xtrue) if test x$bwin32 = xtrue; then - AC_SEARCH_LIBS([getservbyname],[ws2_32]) + AC_HAVE_LIBRARY([ws2_32]) fi dnl Checks for typedefs, structures, and compiler characteristics. @@ -364,11 +368,7 @@ AC_CHECK_FUNCS([ \ getegid \ geteuid \ getifaddrs \ - getnameinfo \ - getprotobynumber \ gettimeofday \ - inet_ntop \ - inet_pton \ issetugid \ mach_absolute_time \ mmap \ @@ -392,10 +392,36 @@ AC_CHECK_FUNCS([ \ unsetenv \ usleep \ vasprintf \ - getservbyname \ + getrandom \ ]) + +AS_IF([test x$bwin32 = xtrue], + AC_CHECK_FUNCS(_gmtime64_s, , [AC_CHECK_FUNCS(_gmtime64)]) +) + AM_CONDITIONAL(STRLCPY_IMPL, [test x"$ac_cv_func_strlcpy" = xno]) +m4_define([funcstochk], + [getnameinfo + getprotobynumber + getservbyname + inet_ntop + inet_pton] +) + +AS_IF([test x$bwin32 = xtrue], + [AX_CHECK_DECLS_EX([funcstochk getaddrinfo], + [#ifdef _WIN32 + #include + #include + #endif])], + [AC_CHECK_FUNCS(m4_normalize(funcstochk))] +) + +m4_undefine([funcstochk]) + +dnl check getaddrinfo and gethostbyname_r for non-windows +AS_IF([test x$bwin32 = xfalse], [ AC_CACHE_CHECK( [for getaddrinfo], [libevent_cv_getaddrinfo], @@ -441,27 +467,27 @@ AC_CHECK_FUNC(gethostbyname_r, [ [Define this if gethostbyname_r takes 6 arguments]) AC_MSG_RESULT(6) ], [ - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include ], [ char *cp1, *cp2; struct hostent *h1; int i1, i2; (void)gethostbyname_r(cp1,h1,cp2,i1,&i2); - ], [ + ])], [ AC_DEFINE(HAVE_GETHOSTBYNAME_R) AC_DEFINE(HAVE_GETHOSTBYNAME_R_5_ARG, 1, [Define this if gethostbyname_r takes 5 arguments]) AC_MSG_RESULT(5) - ], [ - AC_TRY_COMPILE([ + ], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include ], [ char *cp1; struct hostent *h1; struct hostent_data hd; (void) gethostbyname_r(cp1,h1,&hd); - ], [ + ])], [ AC_DEFINE(HAVE_GETHOSTBYNAME_R) AC_DEFINE(HAVE_GETHOSTBYNAME_R_3_ARG, 1, [Define this if gethostbyname_r takes 3 arguments]) @@ -475,6 +501,7 @@ AC_CHECK_FUNC(gethostbyname_r, [ ]) fi +]) dnl end of checking getaddrinfo and gethostbyname_r AC_MSG_CHECKING(for F_SETFD in fcntl.h) AC_EGREP_CPP(yes, @@ -517,7 +544,8 @@ if test "x$ac_cv_header_sys_event_h" = "xyes"; then AC_CHECK_FUNCS(kqueue, [havekqueue=yes], ) if test "x$havekqueue" = "xyes" ; then AC_MSG_CHECKING(for working kqueue) - AC_TRY_RUN( + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([ #ifdef HAVE_STDLIB_H #include #endif @@ -530,50 +558,50 @@ if test "x$ac_cv_header_sys_event_h" = "xyes"; then #include #include #include - -int -main(int argc, char **argv) -{ + ], [[ int kq; int n; - int fd[[2]]; + int fd[2]; struct kevent ev; struct timespec ts; - char buf[[80000]]; + char buf[80000]; if (pipe(fd) == -1) - exit(1); - if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1) - exit(1); + return 1; + if (fcntl(fd[1], F_SETFL, O_NONBLOCK) == -1) + return 1; - while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf)) + while ((n = write(fd[1], buf, sizeof(buf))) == sizeof(buf)) ; - if ((kq = kqueue()) == -1) - exit(1); + if ((kq = kqueue()) == -1) + return 1; memset(&ev, 0, sizeof(ev)); - ev.ident = fd[[1]]; + ev.ident = fd[1]; ev.filter = EVFILT_WRITE; ev.flags = EV_ADD | EV_ENABLE; n = kevent(kq, &ev, 1, NULL, 0, NULL); if (n == -1) - exit(1); + return 1; - read(fd[[0]], buf, sizeof(buf)); + read(fd[0], buf, sizeof(buf)); ts.tv_sec = 0; ts.tv_nsec = 0; n = kevent(kq, NULL, 0, &ev, 1, &ts); if (n == -1 || n == 0) - exit(1); - - exit(0); -}, [AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_WORKING_KQUEUE, 1, - [Define if kqueue works correctly with pipes]) - havekqueue=yes - ], AC_MSG_RESULT(no), AC_MSG_RESULT(no)) + return 1; + + return 0; + ]] + )], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_WORKING_KQUEUE, 1, + [Define if kqueue works correctly with pipes]) + havekqueue=yes + ], [AC_MSG_RESULT(no)], [AC_MSG_RESULT(no)] + ) fi fi AM_CONDITIONAL(KQUEUE_BACKEND, [test "x$havekqueue" = "xyes"]) @@ -589,7 +617,8 @@ fi if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then if test "x$haveepoll" = "xno" ; then AC_MSG_CHECKING(for epoll system call) - AC_TRY_RUN( + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ #include #include #include @@ -602,21 +631,21 @@ epoll_create(int size) { return (syscall(__NR_epoll_create, size)); } - -int -main(int argc, char **argv) -{ + ]],[[ int epfd; epfd = epoll_create(256); - exit (epfd == -1 ? 1 : 0); -}, [AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_EPOLL, 1, - [Define if your system supports the epoll system calls]) - needsignal=yes - have_epoll=yes - AC_LIBOBJ(epoll_sub) - ], AC_MSG_RESULT(no), AC_MSG_RESULT(no)) + return (epfd == -1 ? 1 : 0); + ]] + )], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EPOLL, 1, + [Define if your system supports the epoll system calls]) + needsignal=yes + have_epoll=yes + AC_LIBOBJ(epoll_sub) + ], [AC_MSG_RESULT(no)], [AC_MSG_RESULT(no)] + ) fi fi AM_CONDITIONAL(EPOLL_BACKEND, [test "x$haveepoll" = "xyes"]) @@ -726,39 +755,48 @@ AC_CHECK_TYPES([struct linger],,, #ifdef HAVE_SYS_SOCKET_H #include #endif +#ifdef _WIN32 +#include +#endif ]) AC_MSG_CHECKING([for socklen_t]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ #include #ifdef _WIN32 #include #else #include - #endif], - [socklen_t x;], - AC_MSG_RESULT([yes]), + #endif + ],[socklen_t x;] + )], + [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE(socklen_t, unsigned int, - [Define to unsigned int if you dont have it])] + [Define to unsigned int if you dont have it])] ) # __func__/__FUNCTION__ is not a macros in general AC_MSG_CHECKING([whether our compiler supports __func__]) -AC_TRY_COMPILE([], - [ const char *cp = __func__; ], - [ AC_DEFINE(HAVE___func__, 1, [Define to 1 if compiler have __func__]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([], + [ const char *cp = __func__; ] + )], + [ AC_DEFINE(HAVE___func__, 1, [Define to 1 if compiler have __func__]) AC_MSG_RESULT([yes]) ], - AC_MSG_RESULT([no]) + [AC_MSG_RESULT([no])] ) AC_MSG_CHECKING([whether our compiler supports __FUNCTION__]) -AC_TRY_COMPILE([], - [ const char *cp = __FUNCTION__; ], - [ AC_DEFINE(HAVE___FUNCTION__, 1, [Define to 1 if compiler have __FUNCTION__]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([], + [ const char *cp = __FUNCTION__; ] + )], + [ AC_DEFINE(HAVE___FUNCTION__, 1, [Define to 1 if compiler have __FUNCTION__]) AC_MSG_RESULT([yes]) ], - AC_MSG_RESULT([no]) + [AC_MSG_RESULT([no])] ) # check if we can compile with pthreads @@ -828,7 +866,7 @@ if test x$enable_gcc_warnings != xno && test "$GCC" = "yes"; then #endif])], have_clang=yes, have_clang=no) # -W is the same as -Wextra - CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wbad-function-cast" + CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wmissing-declarations -Wnested-externs -Wbad-function-cast" if test x$enable_gcc_warnings = xyes; then CFLAGS="$CFLAGS -Werror" fi @@ -951,5 +989,19 @@ AC_SUBST([LIBEVENT_GC_SECTIONS]) AM_CONDITIONAL([INSTALL_LIBEVENT], [test "$enable_libevent_install" = "yes"]) +# Doxygen support +DX_HTML_FEATURE(ON) +DX_MAN_FEATURE(OFF) +DX_RTF_FEATURE(OFF) +DX_XML_FEATURE(OFF) +DX_PDF_FEATURE(OFF) +DX_PS_FEATURE(OFF) +DX_CHM_FEATURE(OFF) +DX_CHI_FEATURE(OFF) +DX_INIT_DOXYGEN([libevent], [${top_srcdir}/Doxyfile], [doxygen]) + +AM_CONDITIONAL([ENABLE_DOXYGEN], [test "$DX_FLAG_doc" = "1"]) +AM_CONDITIONAL([ENABLE_DOXYGEN_MAN], [test "$DX_FLAG_man" = "1"]) + AC_CONFIG_FILES( [libevent.pc libevent_openssl.pc libevent_pthreads.pc libevent_core.pc libevent_extra.pc] ) AC_OUTPUT(Makefile) diff --git a/doxygen.am b/doxygen.am new file mode 100644 index 0000000000..916d7c4079 --- /dev/null +++ b/doxygen.am @@ -0,0 +1,55 @@ +# Doxygen documentation will not be generated with default configuration, +# unless '--enable-doxygen-doc' is configured. +# The following targets are all about doxygen: +# make # 'make doxygen' would be auto executed +# make doxygen # generating doxygen documentation +# make doxygen-doc # same as 'make doxygen' +# make clean # clean docs generated by doxygen +# make install # install doxygen documentation +# make uninstall # uninstall doxygen documentation + +if ENABLE_DOXYGEN + +# Add all needed rules defined in ax_prog_doxygen.m4 +@DX_RULES@ + +# Use 'make clean' to clean docs generated by doxygen. +clean-local: + -rm -rf $(DX_CLEANFILES) + +# integrate doxygen with automake targets +man3_MANS = @DX_DOCDIR@/man/man3/* +$(man3_MANS): doxygen-doc + +# Docs will be installed. It may be one or more docs supported +# by doxygen, but does not include 'man'. +docdirs = $(DX_INSTALL_DOCS) + +# Rules for installing docs generated by doxygen into $(htmldir), +# The typical value of $(htmldir) is '/usr/local/share/doc/$(PACKAGE)' +install-data-local: + @if ! test -d "$(DESTDIR)$(htmldir)"; then \ + echo "$(mkinstalldirs) '$(DESTDIR)$(htmldir)'"; \ + $(mkinstalldirs) '$(DESTDIR)$(htmldir)'; \ + fi + @for d in $(docdirs); do \ + echo "cp -pR $$d '$(DESTDIR)$(htmldir)/'"; \ + cp -pR $$d '$(DESTDIR)$(htmldir)/'; \ + done + +# Rules for uninstalling docs generated by doxygen from $(htmldir) +uninstall-local: + @for d in $(docdirs); do \ + d=`basename $$d`; \ + echo "test ! -d '$(DESTDIR)$(htmldir)/'$$d || \ + { find '$(DESTDIR)$(htmldir)/'$$d -type d ! -perm -200 -exec chmod u+w '{}' ';' && \ + rm -rf '$(DESTDIR)$(htmldir)/'$$d; }"; \ + test ! -d '$(DESTDIR)$(htmldir)/'$$d || \ + { find '$(DESTDIR)$(htmldir)/'$$d -type d ! -perm -200 -exec chmod u+w '{}' ';' && \ + rm -rf '$(DESTDIR)$(htmldir)/'$$d; }; \ + done + rmdir "$(DESTDIR)$(htmldir)/" || true + +doxygen: doxygen-doc + +endif ENABLE_DOXYGEN diff --git a/epoll.c b/epoll.c index a0df0d21bf..bdec2e4569 100644 --- a/epoll.c +++ b/epoll.c @@ -281,7 +281,7 @@ epoll_apply_one_change(struct event_base *base, return 0; } - if ((ch->read_change|ch->write_change) & EV_CHANGE_ET) + if ((ch->read_change|ch->write_change|ch->close_change) & EV_CHANGE_ET) events |= EPOLLET; memset(&epev, 0, sizeof(epev)); @@ -486,7 +486,9 @@ epoll_dispatch(struct event_base *base, struct timeval *tv) continue; #endif - if (what & (EPOLLHUP|EPOLLERR)) { + if (what & EPOLLERR) { + ev = EV_READ | EV_WRITE; + } else if ((what & EPOLLHUP) && !(what & EPOLLRDHUP)) { ev = EV_READ | EV_WRITE; } else { if (what & EPOLLIN) diff --git a/evdns.c b/evdns.c index de3848ad18..a5b31a3c00 100644 --- a/evdns.c +++ b/evdns.c @@ -3531,6 +3531,7 @@ evdns_base_set_option_impl(struct evdns_base *base, base->global_max_retransmits = retries; } else if (str_matches_option(option, "randomize-case:")) { int randcase = strtoint(val); + if (randcase == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; base->global_randomize_case = randcase; } else if (str_matches_option(option, "bind-to:")) { @@ -3554,11 +3555,13 @@ evdns_base_set_option_impl(struct evdns_base *base, sizeof(tv)); } else if (str_matches_option(option, "so-rcvbuf:")) { int buf = strtoint(val); + if (buf == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting SO_RCVBUF to %s", val); base->so_rcvbuf = buf; } else if (str_matches_option(option, "so-sndbuf:")) { int buf = strtoint(val); + if (buf == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting SO_SNDBUF to %s", val); base->so_sndbuf = buf; @@ -4032,7 +4035,7 @@ evdns_base_new(struct event_base *event_base, int flags) #else r = evdns_base_resolv_conf_parse(base, opts, "/etc/resolv.conf"); #endif - if (r == -1) { + if (r) { evdns_base_free_and_unlock(base, 0); return NULL; } @@ -4106,6 +4109,11 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests) /* TODO(nickm) we might need to refcount here. */ + while (base->req_waiting_head) { + if (fail_requests) + reply_schedule_callback(base->req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL); + request_finished(base->req_waiting_head, &base->req_waiting_head, 1); + } for (i = 0; i < base->n_req_heads; ++i) { while (base->req_heads[i]) { if (fail_requests) @@ -4113,11 +4121,6 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests) request_finished(base->req_heads[i], &REQ_HEAD(base, base->req_heads[i]->trans_id), 1); } } - while (base->req_waiting_head) { - if (fail_requests) - reply_schedule_callback(base->req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(base->req_waiting_head, &base->req_waiting_head, 1); - } base->global_requests_inflight = base->global_requests_waiting = 0; for (server = base->server_head; server; server = server_next) { diff --git a/event-config.h.cmake b/event-config.h.cmake index 498ab1eac9..fccf0cf059 100644 --- a/event-config.h.cmake +++ b/event-config.h.cmake @@ -75,11 +75,8 @@ /* Define to 1 if you have the declaration of `KERN_ARND'. */ #define EVENT__HAVE_DECL_KERN_ARND @EVENT__HAVE_DECL_KERN_ARND@ -/* Define to 1 if you have the declaration of `KERN_RANDOM'. */ -#define EVENT__HAVE_DECL_KERN_RANDOM @EVENT__HAVE_DECL_KERN_RANDOM@ - -/* Define to 1 if you have the declaration of `RANDOM_UUID'. */ -#define EVENT__HAVE_DECL_RANDOM_UUID @EVENT__HAVE_DECL_RANDOM_UUID@ +/* Define to 1 if you have `getrandom' function. */ +#cmakedefine EVENT__HAVE_GETRANDOM 1 /* Define if /dev/poll is available */ #cmakedefine EVENT__HAVE_DEVPOLL 1 @@ -181,6 +178,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_MACH_MACH_TIME_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine EVENT__HAVE_MACH_MACH_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_MEMORY_H 1 @@ -274,9 +274,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_STDLIB_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine EVENT__HAVE_STRINGS_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_STRING_H 1 @@ -292,6 +289,12 @@ /* Define to 1 if you have the `strtoll' function. */ #cmakedefine EVENT__HAVE_STRTOLL 1 +/* Define to 1 if you have the `_gmtime64_s' function. */ +#cmakedefine EVENT__HAVE__GMTIME64_S 1 + +/* Define to 1 if you have the `_gmtime64' function. */ +#cmakedefine EVENT__HAVE__GMTIME64 1 + /* Define to 1 if the system has the type `struct addrinfo'. */ #cmakedefine EVENT__HAVE_STRUCT_ADDRINFO 1 @@ -367,6 +370,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_SYS_STAT_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine EVENT__HAVE_SYS_RANDOM_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine EVENT__HAVE_SYS_SYSCTL_H 1 diff --git a/event-internal.h b/event-internal.h index 92941b7136..9e5ff42447 100644 --- a/event-internal.h +++ b/event-internal.h @@ -418,7 +418,7 @@ int event_add_nolock_(struct event *ev, * if it is running in another thread and it doesn't have EV_FINALIZE set. */ #define EVENT_DEL_AUTOBLOCK 2 -/** Argument for event_del_nolock_. Tells event_del to procede even if the +/** Argument for event_del_nolock_. Tells event_del to proceed even if the * event is set up for finalization rather for regular use.*/ #define EVENT_DEL_EVEN_IF_FINALIZING 3 int event_del_nolock_(struct event *ev, int blocking); diff --git a/event.c b/event.c index b2ad341026..7a42b73191 100644 --- a/event.c +++ b/event.c @@ -987,12 +987,6 @@ event_reinit(struct event_base *base) EVBASE_ACQUIRE_LOCK(base, th_base_lock); - if (base->running_loop) { - event_warnx("%s: forked from the event_loop.", __func__); - res = -1; - goto done; - } - evsel = base->evsel; /* check if this event mechanism requires reinit on the backend */ @@ -1712,8 +1706,8 @@ event_process_active_single_queue(struct event_base *base, evcb_evfinalize = ev->ev_evcallback.evcb_cb_union.evcb_evfinalize; EVUTIL_ASSERT((evcb->evcb_flags & EVLIST_FINALIZING)); EVBASE_RELEASE_LOCK(base, th_base_lock); - evcb_evfinalize(ev, ev->ev_arg); event_debug_note_teardown_(ev); + evcb_evfinalize(ev, ev->ev_arg); if (evcb_closure == EV_CLOSURE_EVENT_FINALIZE_FREE) mm_free(ev); } @@ -2062,6 +2056,9 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events, int res = 0; int activate = 0; + if (!base) + return (-1); + /* We cannot support signals that just fire once, or persistent * events. */ if (events & (EV_SIGNAL|EV_PERSIST)) diff --git a/event_rpcgen.py b/event_rpcgen.py index 0911ca253c..0bae3b0fe6 100755 --- a/event_rpcgen.py +++ b/event_rpcgen.py @@ -6,65 +6,79 @@ # # Generates marshaling code based on libevent. +# pylint: disable=too-many-lines +# pylint: disable=too-many-branches +# pylint: disable=too-many-public-methods +# pylint: disable=too-many-statements +# pylint: disable=global-statement + # TODO: -# 1) use optparse to allow the strategy shell to parse options, and -# to allow the instantiated factory (for the specific output language) -# to parse remaining options -# 2) move the globals into a class that manages execution (including the -# progress outputs that space stderr at the moment) -# 3) emit other languages +# 1) propagate the arguments/options parsed by argparse down to the +# instantiated factory objects. +# 2) move the globals into a class that manages execution, including the +# progress outputs that go to stderr at the moment. +# 3) emit other languages. -import sys +import argparse import re +import sys _NAME = "event_rpcgen.py" _VERSION = "0.1" # Globals -line_count = 0 +LINE_COUNT = 0 -white = re.compile(r'\s+') -cppcomment = re.compile(r'\/\/.*$') -nonident = re.compile(r'[^a-zA-Z0-9_]') -structref = re.compile(r'^struct\[([a-zA-Z_][a-zA-Z0-9_]*)\]$') -structdef = re.compile(r'^struct +[a-zA-Z_][a-zA-Z0-9_]* *{$') +CPPCOMMENT_RE = re.compile(r"\/\/.*$") +NONIDENT_RE = re.compile(r"\W") +PREPROCESSOR_DEF_RE = re.compile(r"^#define") +STRUCT_REF_RE = re.compile(r"^struct\[(?P[a-zA-Z_][a-zA-Z0-9_]*)\]$") +STRUCT_DEF_RE = re.compile(r"^struct +[a-zA-Z_][a-zA-Z0-9_]* *{$") +WHITESPACE_RE = re.compile(r"\s+") -headerdirect = [] -cppdirect = [] +HEADER_DIRECT = [] +CPP_DIRECT = [] + +QUIETLY = False -QUIETLY = 0 def declare(s): if not QUIETLY: print(s) + def TranslateList(mylist, mydict): return [x % mydict for x in mylist] -# Exception class for parse errors + class RpcGenError(Exception): - def __init__(self, why): - self.why = why - def __str__(self): - return str(self.why) + """An Exception class for parse errors.""" + + def __init__(self, why): # pylint: disable=super-init-not-called + self.why = why + + def __str__(self): + return str(self.why) + # Holds everything that makes a struct -class Struct: +class Struct(object): def __init__(self, name): self._name = name self._entries = [] self._tags = {} - declare(' Created struct: %s' % name) + declare(" Created struct: %s" % name) def AddEntry(self, entry): if entry.Tag() in self._tags: raise RpcGenError( 'Entry "%s" duplicates tag number %d from "%s" ' - 'around line %d' % (entry.Name(), entry.Tag(), - self._tags[entry.Tag()], line_count)) + "around line %d" + % (entry.Name(), entry.Tag(), self._tags[entry.Tag()], LINE_COUNT) + ) self._entries.append(entry) self._tags[entry.Tag()] = entry.Name() - declare(' Added entry: %s' % entry.Name()) + declare(" Added entry: %s" % entry.Name()) def Name(self): return self._name @@ -75,10 +89,12 @@ def EntryTagName(self, entry): name = "%s_%s" % (self._name, entry.Name()) return name.upper() - def PrintIndented(self, file, ident, code): + @staticmethod + def PrintIndented(filep, ident, code): """Takes an array, add indentation to each entry and prints it.""" for entry in code: - file.write('%s%s\n' % (ident, entry)) + filep.write("%s%s\n" % (ident, entry)) + class StructCCode(Struct): """ Knows how to generate C code for a struct """ @@ -86,42 +102,41 @@ class StructCCode(Struct): def __init__(self, name): Struct.__init__(self, name) - def PrintTags(self, file): + def PrintTags(self, filep): """Prints the tag definitions for a structure.""" - file.write('/* Tag definition for %s */\n' % self._name) - file.write('enum %s_ {\n' % self._name.lower()) + filep.write("/* Tag definition for %s */\n" % self._name) + filep.write("enum %s_ {\n" % self._name.lower()) for entry in self._entries: - file.write(' %s=%d,\n' % (self.EntryTagName(entry), entry.Tag())) - file.write(' %s_MAX_TAGS\n' % (self._name.upper())) - file.write('};\n\n') + filep.write(" %s=%d,\n" % (self.EntryTagName(entry), entry.Tag())) + filep.write(" %s_MAX_TAGS\n" % (self._name.upper())) + filep.write("};\n\n") - def PrintForwardDeclaration(self, file): - file.write('struct %s;\n' % self._name) + def PrintForwardDeclaration(self, filep): + filep.write("struct %s;\n" % self._name) - def PrintDeclaration(self, file): - file.write('/* Structure declaration for %s */\n' % self._name) - file.write('struct %s_access_ {\n' % self._name) + def PrintDeclaration(self, filep): + filep.write("/* Structure declaration for %s */\n" % self._name) + filep.write("struct %s_access_ {\n" % self._name) for entry in self._entries: - dcl = entry.AssignDeclaration('(*%s_assign)' % entry.Name()) - dcl.extend( - entry.GetDeclaration('(*%s_get)' % entry.Name())) + dcl = entry.AssignDeclaration("(*%s_assign)" % entry.Name()) + dcl.extend(entry.GetDeclaration("(*%s_get)" % entry.Name())) if entry.Array(): - dcl.extend( - entry.AddDeclaration('(*%s_add)' % entry.Name())) - self.PrintIndented(file, ' ', dcl) - file.write('};\n\n') + dcl.extend(entry.AddDeclaration("(*%s_add)" % entry.Name())) + self.PrintIndented(filep, " ", dcl) + filep.write("};\n\n") - file.write('struct %s {\n' % self._name) - file.write(' struct %s_access_ *base;\n\n' % self._name) + filep.write("struct %s {\n" % self._name) + filep.write(" struct %s_access_ *base;\n\n" % self._name) for entry in self._entries: dcl = entry.Declaration() - self.PrintIndented(file, ' ', dcl) - file.write('\n') + self.PrintIndented(filep, " ", dcl) + filep.write("\n") for entry in self._entries: - file.write(' ev_uint8_t %s_set;\n' % entry.Name()) - file.write('};\n\n') + filep.write(" ev_uint8_t %s_set;\n" % entry.Name()) + filep.write("};\n\n") - file.write("""struct %(name)s *%(name)s_new(void); + filep.write( + """struct %(name)s *%(name)s_new(void); struct %(name)s *%(name)s_new_with_arg(void *); void %(name)s_free(struct %(name)s *); void %(name)s_clear(struct %(name)s *); @@ -131,226 +146,291 @@ def PrintDeclaration(self, file): void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t, const struct %(name)s *); int evtag_unmarshal_%(name)s(struct evbuffer *, ev_uint32_t, - struct %(name)s *);\n""" % { 'name' : self._name }) - + struct %(name)s *);\n""" + % {"name": self._name} + ) # Write a setting function of every variable for entry in self._entries: - self.PrintIndented(file, '', entry.AssignDeclaration( - entry.AssignFuncName())) - self.PrintIndented(file, '', entry.GetDeclaration( - entry.GetFuncName())) + self.PrintIndented( + filep, "", entry.AssignDeclaration(entry.AssignFuncName()) + ) + self.PrintIndented(filep, "", entry.GetDeclaration(entry.GetFuncName())) if entry.Array(): - self.PrintIndented(file, '', entry.AddDeclaration( - entry.AddFuncName())) + self.PrintIndented(filep, "", entry.AddDeclaration(entry.AddFuncName())) - file.write('/* --- %s done --- */\n\n' % self._name) + filep.write("/* --- %s done --- */\n\n" % self._name) - def PrintCode(self, file): - file.write(('/*\n' - ' * Implementation of %s\n' - ' */\n\n') % self._name) + def PrintCode(self, filep): + filep.write( + """/* + * Implementation of %s + */ +""" + % (self._name) + ) - file.write('static struct %(name)s_access_ %(name)s_base__ = {\n' % \ - { 'name' : self._name }) + filep.write( + """ +static struct %(name)s_access_ %(name)s_base__ = { +""" + % {"name": self._name} + ) for entry in self._entries: - self.PrintIndented(file, ' ', entry.CodeBase()) - file.write('};\n\n') + self.PrintIndented(filep, " ", entry.CodeBase()) + filep.write("};\n\n") # Creation - file.write(( - 'struct %(name)s *\n' - '%(name)s_new(void)\n' - '{\n' - ' return %(name)s_new_with_arg(NULL);\n' - '}\n' - '\n' - 'struct %(name)s *\n' - '%(name)s_new_with_arg(void *unused)\n' - '{\n' - ' struct %(name)s *tmp;\n' - ' if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) {\n' - ' event_warn("%%s: malloc", __func__);\n' - ' return (NULL);\n' - ' }\n' - ' tmp->base = &%(name)s_base__;\n\n') % { 'name' : self._name }) + filep.write( + """struct %(name)s * +%(name)s_new(void) +{ + return %(name)s_new_with_arg(NULL); +} + +struct %(name)s * +%(name)s_new_with_arg(void *unused) +{ + struct %(name)s *tmp; + if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) { + event_warn("%%s: malloc", __func__); + return (NULL); + } + tmp->base = &%(name)s_base__; + +""" + % {"name": self._name} + ) for entry in self._entries: - self.PrintIndented(file, ' ', entry.CodeInitialize('tmp')) - file.write(' tmp->%s_set = 0;\n\n' % entry.Name()) + self.PrintIndented(filep, " ", entry.CodeInitialize("tmp")) + filep.write(" tmp->%s_set = 0;\n\n" % entry.Name()) + + filep.write( + """ return (tmp); +} - file.write(( - ' return (tmp);\n' - '}\n\n')) +""" + ) # Adding for entry in self._entries: if entry.Array(): - self.PrintIndented(file, '', entry.CodeAdd()) - file.write('\n') + self.PrintIndented(filep, "", entry.CodeAdd()) + filep.write("\n") # Assigning for entry in self._entries: - self.PrintIndented(file, '', entry.CodeAssign()) - file.write('\n') + self.PrintIndented(filep, "", entry.CodeAssign()) + filep.write("\n") # Getting for entry in self._entries: - self.PrintIndented(file, '', entry.CodeGet()) - file.write('\n') + self.PrintIndented(filep, "", entry.CodeGet()) + filep.write("\n") # Clearing - file.write(( 'void\n' - '%(name)s_clear(struct %(name)s *tmp)\n' - '{' - '\n') % { 'name' : self._name }) + filep.write( + """void +%(name)s_clear(struct %(name)s *tmp) +{ +""" + % {"name": self._name} + ) for entry in self._entries: - self.PrintIndented(file, ' ', entry.CodeClear('tmp')) + self.PrintIndented(filep, " ", entry.CodeClear("tmp")) - file.write('}\n\n') + filep.write("}\n\n") # Freeing - file.write(( 'void\n' - '%(name)s_free(struct %(name)s *tmp)\n' - '{' - '\n') % { 'name' : self._name }) + filep.write( + """void +%(name)s_free(struct %(name)s *tmp) +{ +""" + % {"name": self._name} + ) for entry in self._entries: - self.PrintIndented(file, ' ', entry.CodeFree('tmp')) + self.PrintIndented(filep, " ", entry.CodeFree("tmp")) + + filep.write( + """ free(tmp); +} - file.write((' free(tmp);\n' - '}\n\n')) +""" + ) # Marshaling - file.write(('void\n' - '%(name)s_marshal(struct evbuffer *evbuf, ' - 'const struct %(name)s *tmp)' - '{\n') % { 'name' : self._name }) + filep.write( + """void +%(name)s_marshal(struct evbuffer *evbuf, const struct %(name)s *tmp) { +""" + % {"name": self._name} + ) for entry in self._entries: - indent = ' ' + indent = " " # Optional entries do not have to be set if entry.Optional(): - indent += ' ' - file.write(' if (tmp->%s_set) {\n' % entry.Name()) + indent += " " + filep.write(" if (tmp->%s_set) {\n" % entry.Name()) self.PrintIndented( - file, indent, - entry.CodeMarshal('evbuf', self.EntryTagName(entry), - entry.GetVarName('tmp'), - entry.GetVarLen('tmp'))) + filep, + indent, + entry.CodeMarshal( + "evbuf", + self.EntryTagName(entry), + entry.GetVarName("tmp"), + entry.GetVarLen("tmp"), + ), + ) if entry.Optional(): - file.write(' }\n') + filep.write(" }\n") - file.write('}\n\n') + filep.write("}\n\n") # Unmarshaling - file.write(('int\n' - '%(name)s_unmarshal(struct %(name)s *tmp, ' - ' struct evbuffer *evbuf)\n' - '{\n' - ' ev_uint32_t tag;\n' - ' while (evbuffer_get_length(evbuf) > 0) {\n' - ' if (evtag_peek(evbuf, &tag) == -1)\n' - ' return (-1);\n' - ' switch (tag) {\n' - '\n') % { 'name' : self._name }) + filep.write( + """int +%(name)s_unmarshal(struct %(name)s *tmp, struct evbuffer *evbuf) +{ + ev_uint32_t tag; + while (evbuffer_get_length(evbuf) > 0) { + if (evtag_peek(evbuf, &tag) == -1) + return (-1); + switch (tag) { + +""" + % {"name": self._name} + ) for entry in self._entries: - file.write(' case %s:\n' % self.EntryTagName(entry)) + filep.write(" case %s:\n" % (self.EntryTagName(entry))) if not entry.Array(): - file.write(( - ' if (tmp->%s_set)\n' - ' return (-1);' - '\n') % (entry.Name())) + filep.write( + """ if (tmp->%s_set) + return (-1); +""" + % (entry.Name()) + ) self.PrintIndented( - file, ' ', - entry.CodeUnmarshal('evbuf', - self.EntryTagName(entry), - entry.GetVarName('tmp'), - entry.GetVarLen('tmp'))) - - file.write(( ' tmp->%s_set = 1;\n' % entry.Name() + - ' break;\n' )) - file.write(( ' default:\n' - ' return -1;\n' - ' }\n' - ' }\n\n' )) + filep, + " ", + entry.CodeUnmarshal( + "evbuf", + self.EntryTagName(entry), + entry.GetVarName("tmp"), + entry.GetVarLen("tmp"), + ), + ) + + filep.write( + """ tmp->%s_set = 1; + break; +""" + % (entry.Name()) + ) + filep.write( + """ default: + return -1; + } + } + +""" + ) # Check if it was decoded completely - file.write(( ' if (%(name)s_complete(tmp) == -1)\n' - ' return (-1);' - '\n') % { 'name' : self._name }) - - # Successfully decoded - file.write(( ' return (0);\n' - '}\n\n')) + filep.write( + """ if (%(name)s_complete(tmp) == -1) + return (-1); + return (0); +} +""" + % {"name": self._name} + ) # Checking if a structure has all the required data - file.write(( - 'int\n' - '%(name)s_complete(struct %(name)s *msg)\n' - '{\n' ) % { 'name' : self._name }) + filep.write( + """ +int +%(name)s_complete(struct %(name)s *msg) +{ +""" + % {"name": self._name} + ) for entry in self._entries: if not entry.Optional(): code = [ - 'if (!msg->%(name)s_set)', - ' return (-1);' ] + """if (!msg->%(name)s_set) + return (-1);""" + ] code = TranslateList(code, entry.GetTranslation()) - self.PrintIndented( - file, ' ', code) + self.PrintIndented(filep, " ", code) self.PrintIndented( - file, ' ', - entry.CodeComplete('msg', entry.GetVarName('msg'))) - file.write(( - ' return (0);\n' - '}\n\n' )) + filep, " ", entry.CodeComplete("msg", entry.GetVarName("msg")) + ) + filep.write( + """ return (0); +} +""" + ) # Complete message unmarshaling - file.write(( - 'int\n' - 'evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ' - 'ev_uint32_t need_tag, struct %(name)s *msg)\n' - '{\n' - ' ev_uint32_t tag;\n' - ' int res = -1;\n' - '\n' - ' struct evbuffer *tmp = evbuffer_new();\n' - '\n' - ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1' - ' || tag != need_tag)\n' - ' goto error;\n' - '\n' - ' if (%(name)s_unmarshal(msg, tmp) == -1)\n' - ' goto error;\n' - '\n' - ' res = 0;\n' - '\n' - ' error:\n' - ' evbuffer_free(tmp);\n' - ' return (res);\n' - '}\n\n' ) % { 'name' : self._name }) + filep.write( + """ +int +evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct %(name)s *msg) +{ + ev_uint32_t tag; + int res = -1; + + struct evbuffer *tmp = evbuffer_new(); + + if (evtag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag) + goto error; + + if (%(name)s_unmarshal(msg, tmp) == -1) + goto error; + + res = 0; + + error: + evbuffer_free(tmp); + return (res); +} +""" + % {"name": self._name} + ) # Complete message marshaling - file.write(( - 'void\n' - 'evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag, ' - 'const struct %(name)s *msg)\n' - '{\n' - ' struct evbuffer *buf_ = evbuffer_new();\n' - ' assert(buf_ != NULL);\n' - ' %(name)s_marshal(buf_, msg);\n' - ' evtag_marshal_buffer(evbuf, tag, buf_);\n ' - ' evbuffer_free(buf_);\n' - '}\n\n' ) % { 'name' : self._name }) - -class Entry: - def __init__(self, type, name, tag): - self._type = type + filep.write( + """ +void +evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag, + const struct %(name)s *msg) +{ + struct evbuffer *buf_ = evbuffer_new(); + assert(buf_ != NULL); + %(name)s_marshal(buf_, msg); + evtag_marshal_buffer(evbuf, tag, buf_); + evbuffer_free(buf_); +} + +""" + % {"name": self._name} + ) + + +class Entry(object): + def __init__(self, ent_type, name, tag): + self._type = ent_type self._name = name self._tag = int(tag) - self._ctype = type - self._optional = 0 - self._can_be_array = 0 - self._array = 0 + self._ctype = ent_type + self._optional = False + self._can_be_array = False + self._array = False self._line_count = -1 self._struct = None self._refname = None @@ -358,8 +438,9 @@ def __init__(self, type, name, tag): self._optpointer = True self._optaddarg = True - def GetInitializer(self): - assert 0, "Entry does not provide initializer" + @staticmethod + def GetInitializer(): + raise NotImplementedError("Entry does not provide an initializer") def SetStruct(self, struct): self._struct = struct @@ -386,326 +467,351 @@ def Name(self): def Type(self): return self._type - def MakeArray(self, yes=1): - self._array = yes + def MakeArray(self): + self._array = True def MakeOptional(self): - self._optional = 1 + self._optional = True def Verify(self): if self.Array() and not self._can_be_array: raise RpcGenError( 'Entry "%s" cannot be created as an array ' - 'around line %d' % (self._name, self.LineCount())) + "around line %d" % (self._name, self.LineCount()) + ) if not self._struct: raise RpcGenError( 'Entry "%s" does not know which struct it belongs to ' - 'around line %d' % (self._name, self.LineCount())) + "around line %d" % (self._name, self.LineCount()) + ) if self._optional and self._array: raise RpcGenError( 'Entry "%s" has illegal combination of optional and array ' - 'around line %d' % (self._name, self.LineCount())) + "around line %d" % (self._name, self.LineCount()) + ) - def GetTranslation(self, extradict = {}): + def GetTranslation(self, extradict=None): + if extradict is None: + extradict = {} mapping = { - "parent_name" : self._struct.Name(), - "name" : self._name, - "ctype" : self._ctype, - "refname" : self._refname, - "optpointer" : self._optpointer and "*" or "", - "optreference" : self._optpointer and "&" or "", - "optaddarg" : - self._optaddarg and ", const %s value" % self._ctype or "" - } + "parent_name": self._struct.Name(), + "name": self._name, + "ctype": self._ctype, + "refname": self._refname, + "optpointer": self._optpointer and "*" or "", + "optreference": self._optpointer and "&" or "", + "optaddarg": self._optaddarg and ", const %s value" % self._ctype or "", + } for (k, v) in list(extradict.items()): mapping[k] = v return mapping def GetVarName(self, var): - return '%(var)s->%(name)s_data' % self.GetTranslation({ 'var' : var }) + return "%(var)s->%(name)s_data" % self.GetTranslation({"var": var}) - def GetVarLen(self, var): - return 'sizeof(%s)' % self._ctype + def GetVarLen(self, _var): + return "sizeof(%s)" % self._ctype def GetFuncName(self): - return '%s_%s_get' % (self._struct.Name(), self._name) + return "%s_%s_get" % (self._struct.Name(), self._name) def GetDeclaration(self, funcname): - code = [ 'int %s(struct %s *, %s *);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, %s *);" % (funcname, self._struct.Name(), self._ctype) + ] return code def CodeGet(self): - code = ( - 'int', - '%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, ' - '%(ctype)s *value)', - '{', - ' if (msg->%(name)s_set != 1)', - ' return (-1);', - ' *value = msg->%(name)s_data;', - ' return (0);', - '}' ) - code = '\n'.join(code) + code = """int +%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, %(ctype)s *value) +{ + if (msg->%(name)s_set != 1) + return (-1); + *value = msg->%(name)s_data; + return (0); +}""" code = code % self.GetTranslation() - return code.split('\n') + return code.split("\n") def AssignFuncName(self): - return '%s_%s_assign' % (self._struct.Name(), self._name) + return "%s_%s_assign" % (self._struct.Name(), self._name) def AddFuncName(self): - return '%s_%s_add' % (self._struct.Name(), self._name) + return "%s_%s_add" % (self._struct.Name(), self._name) def AssignDeclaration(self, funcname): - code = [ 'int %s(struct %s *, const %s);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, const %s);" + % (funcname, self._struct.Name(), self._ctype) + ] return code def CodeAssign(self): - code = [ 'int', - '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,' - ' const %(ctype)s value)', - '{', - ' msg->%(name)s_set = 1;', - ' msg->%(name)s_data = value;', - ' return (0);', - '}' ] - code = '\n'.join(code) + code = [ + "int", + "%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg," + " const %(ctype)s value)", + "{", + " msg->%(name)s_set = 1;", + " msg->%(name)s_data = value;", + " return (0);", + "}", + ] + code = "\n".join(code) code = code % self.GetTranslation() - return code.split('\n') + return code.split("\n") def CodeClear(self, structname): - code = [ '%s->%s_set = 0;' % (structname, self.Name()) ] + code = ["%s->%s_set = 0;" % (structname, self.Name())] return code - def CodeComplete(self, structname, var_name): + @staticmethod + def CodeComplete(_structname, _var_name): return [] - def CodeFree(self, name): + @staticmethod + def CodeFree(_name): return [] def CodeBase(self): - code = [ - '%(parent_name)s_%(name)s_assign,', - '%(parent_name)s_%(name)s_get,' - ] + code = ["%(parent_name)s_%(name)s_assign,", "%(parent_name)s_%(name)s_get,"] if self.Array(): - code.append('%(parent_name)s_%(name)s_add,') + code.append("%(parent_name)s_%(name)s_add,") - code = '\n'.join(code) + code = "\n".join(code) code = code % self.GetTranslation() - return code.split('\n') + return code.split("\n") + class EntryBytes(Entry): - def __init__(self, type, name, tag, length): + def __init__(self, ent_type, name, tag, length): # Init base class - Entry.__init__(self, type, name, tag) + super(EntryBytes, self).__init__(ent_type, name, tag) self._length = length - self._ctype = 'ev_uint8_t' + self._ctype = "ev_uint8_t" - def GetInitializer(self): + @staticmethod + def GetInitializer(): return "NULL" - def GetVarLen(self, var): - return '(%s)' % self._length + def GetVarLen(self, _var): + return "(%s)" % self._length - def CodeArrayAdd(self, varname, value): + @staticmethod + def CodeArrayAdd(varname, _value): # XXX: copy here - return [ '%(varname)s = NULL;' % { 'varname' : varname } ] + return ["%(varname)s = NULL;" % {"varname": varname}] def GetDeclaration(self, funcname): - code = [ 'int %s(struct %s *, %s **);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, %s **);" % (funcname, self._struct.Name(), self._ctype) + ] return code def AssignDeclaration(self, funcname): - code = [ 'int %s(struct %s *, const %s *);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, const %s *);" + % (funcname, self._struct.Name(), self._ctype) + ] return code def Declaration(self): - dcl = ['ev_uint8_t %s_data[%s];' % (self._name, self._length)] + dcl = ["ev_uint8_t %s_data[%s];" % (self._name, self._length)] return dcl def CodeGet(self): name = self._name - code = [ 'int', - '%s_%s_get(struct %s *msg, %s **value)' % ( - self._struct.Name(), name, - self._struct.Name(), self._ctype), - '{', - ' if (msg->%s_set != 1)' % name, - ' return (-1);', - ' *value = msg->%s_data;' % name, - ' return (0);', - '}' ] + code = [ + "int", + "%s_%s_get(struct %s *msg, %s **value)" + % (self._struct.Name(), name, self._struct.Name(), self._ctype), + "{", + " if (msg->%s_set != 1)" % name, + " return (-1);", + " *value = msg->%s_data;" % name, + " return (0);", + "}", + ] return code def CodeAssign(self): name = self._name - code = [ 'int', - '%s_%s_assign(struct %s *msg, const %s *value)' % ( - self._struct.Name(), name, - self._struct.Name(), self._ctype), - '{', - ' msg->%s_set = 1;' % name, - ' memcpy(msg->%s_data, value, %s);' % ( - name, self._length), - ' return (0);', - '}' ] + code = [ + "int", + "%s_%s_assign(struct %s *msg, const %s *value)" + % (self._struct.Name(), name, self._struct.Name(), self._ctype), + "{", + " msg->%s_set = 1;" % name, + " memcpy(msg->%s_data, value, %s);" % (name, self._length), + " return (0);", + "}", + ] return code def CodeUnmarshal(self, buf, tag_name, var_name, var_len): - code = [ 'if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, ' - '%(var)s, %(varlen)s) == -1) {', - ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', - ' return (-1);', - '}' - ] - return TranslateList(code, - self.GetTranslation({ - 'var' : var_name, - 'varlen' : var_len, - 'buf' : buf, - 'tag' : tag_name })) - - def CodeMarshal(self, buf, tag_name, var_name, var_len): - code = ['evtag_marshal(%s, %s, %s, %s);' % ( - buf, tag_name, var_name, var_len)] + code = [ + "if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, " + "%(var)s, %(varlen)s) == -1) {", + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + " return (-1);", + "}", + ] + return TranslateList( + code, + self.GetTranslation( + {"var": var_name, "varlen": var_len, "buf": buf, "tag": tag_name} + ), + ) + + @staticmethod + def CodeMarshal(buf, tag_name, var_name, var_len): + code = ["evtag_marshal(%s, %s, %s, %s);" % (buf, tag_name, var_name, var_len)] return code def CodeClear(self, structname): - code = [ '%s->%s_set = 0;' % (structname, self.Name()), - 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( - structname, self._name, structname, self._name)] + code = [ + "%s->%s_set = 0;" % (structname, self.Name()), + "memset(%s->%s_data, 0, sizeof(%s->%s_data));" + % (structname, self._name, structname, self._name), + ] return code def CodeInitialize(self, name): - code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( - name, self._name, name, self._name)] + code = [ + "memset(%s->%s_data, 0, sizeof(%s->%s_data));" + % (name, self._name, name, self._name) + ] return code def Verify(self): if not self._length: raise RpcGenError( 'Entry "%s" needs a length ' - 'around line %d' % (self._name, self.LineCount())) + "around line %d" % (self._name, self.LineCount()) + ) + + super(EntryBytes, self).Verify() - Entry.Verify(self) class EntryInt(Entry): - def __init__(self, type, name, tag, bits=32): + def __init__(self, ent_type, name, tag, bits=32): # Init base class - Entry.__init__(self, type, name, tag) + super(EntryInt, self).__init__(ent_type, name, tag) - self._can_be_array = 1 + self._can_be_array = True if bits == 32: - self._ctype = 'ev_uint32_t' - self._marshal_type = 'int' + self._ctype = "ev_uint32_t" + self._marshal_type = "int" if bits == 64: - self._ctype = 'ev_uint64_t' - self._marshal_type = 'int64' + self._ctype = "ev_uint64_t" + self._marshal_type = "int64" - def GetInitializer(self): + @staticmethod + def GetInitializer(): return "0" - def CodeArrayFree(self, var): + @staticmethod + def CodeArrayFree(_var): return [] - def CodeArrayAssign(self, varname, srcvar): - return [ '%(varname)s = %(srcvar)s;' % { 'varname' : varname, - 'srcvar' : srcvar } ] + @staticmethod + def CodeArrayAssign(varname, srcvar): + return ["%(varname)s = %(srcvar)s;" % {"varname": varname, "srcvar": srcvar}] - def CodeArrayAdd(self, varname, value): + @staticmethod + def CodeArrayAdd(varname, value): """Returns a new entry of this type.""" - return [ '%(varname)s = %(value)s;' % { 'varname' : varname, - 'value' : value } ] + return ["%(varname)s = %(value)s;" % {"varname": varname, "value": value}] - def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): code = [ - 'if (evtag_unmarshal_%(ma)s(%(buf)s, %(tag)s, &%(var)s) == -1) {', + "if (evtag_unmarshal_%(ma)s(%(buf)s, %(tag)s, &%(var)s) == -1) {", ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', - ' return (-1);', - '}' ] - code = '\n'.join(code) % self.GetTranslation({ - 'ma' : self._marshal_type, - 'buf' : buf, - 'tag' : tag_name, - 'var' : var_name }) - return code.split('\n') - - def CodeMarshal(self, buf, tag_name, var_name, var_len): + " return (-1);", + "}", + ] + code = "\n".join(code) % self.GetTranslation( + {"ma": self._marshal_type, "buf": buf, "tag": tag_name, "var": var_name} + ) + return code.split("\n") + + def CodeMarshal(self, buf, tag_name, var_name, _var_len): code = [ - 'evtag_marshal_%s(%s, %s, %s);' % ( - self._marshal_type, buf, tag_name, var_name)] + "evtag_marshal_%s(%s, %s, %s);" + % (self._marshal_type, buf, tag_name, var_name) + ] return code def Declaration(self): - dcl = ['%s %s_data;' % (self._ctype, self._name)] + dcl = ["%s %s_data;" % (self._ctype, self._name)] return dcl def CodeInitialize(self, name): - code = ['%s->%s_data = 0;' % (name, self._name)] + code = ["%s->%s_data = 0;" % (name, self._name)] return code + class EntryString(Entry): - def __init__(self, type, name, tag): + def __init__(self, ent_type, name, tag): # Init base class - Entry.__init__(self, type, name, tag) + super(EntryString, self).__init__(ent_type, name, tag) - self._can_be_array = 1 - self._ctype = 'char *' + self._can_be_array = True + self._ctype = "char *" - def GetInitializer(self): + @staticmethod + def GetInitializer(): return "NULL" - def CodeArrayFree(self, varname): - code = [ - 'if (%(var)s != NULL) free(%(var)s);' ] + @staticmethod + def CodeArrayFree(varname): + code = ["if (%(var)s != NULL) free(%(var)s);"] - return TranslateList(code, { 'var' : varname }) + return TranslateList(code, {"var": varname}) - def CodeArrayAssign(self, varname, srcvar): + @staticmethod + def CodeArrayAssign(varname, srcvar): code = [ - 'if (%(var)s != NULL)', - ' free(%(var)s);', - '%(var)s = strdup(%(srcvar)s);', - 'if (%(var)s == NULL) {', + "if (%(var)s != NULL)", + " free(%(var)s);", + "%(var)s = strdup(%(srcvar)s);", + "if (%(var)s == NULL) {", ' event_warnx("%%s: strdup", __func__);', - ' return (-1);', - '}' ] + " return (-1);", + "}", + ] - return TranslateList(code, { 'var' : varname, - 'srcvar' : srcvar }) + return TranslateList(code, {"var": varname, "srcvar": srcvar}) - def CodeArrayAdd(self, varname, value): + @staticmethod + def CodeArrayAdd(varname, value): code = [ - 'if (%(value)s != NULL) {', - ' %(var)s = strdup(%(value)s);', - ' if (%(var)s == NULL) {', - ' goto error;', - ' }', - '} else {', - ' %(var)s = NULL;', - '}' ] - - return TranslateList(code, { 'var' : varname, - 'value' : value }) + "if (%(value)s != NULL) {", + " %(var)s = strdup(%(value)s);", + " if (%(var)s == NULL) {", + " goto error;", + " }", + "} else {", + " %(var)s = NULL;", + "}", + ] + + return TranslateList(code, {"var": varname, "value": value}) def GetVarLen(self, var): - return 'strlen(%s)' % self.GetVarName(var) + return "strlen(%s)" % self.GetVarName(var) - def CodeMakeInitalize(self, varname): - return '%(varname)s = NULL;' % { 'varname' : varname } + @staticmethod + def CodeMakeInitalize(varname): + return "%(varname)s = NULL;" % {"varname": varname} def CodeAssign(self): - name = self._name code = """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, const %(ctype)s value) @@ -716,131 +822,137 @@ def CodeAssign(self): return (-1); msg->%(name)s_set = 1; return (0); -}""" % self.GetTranslation() +}""" % ( + self.GetTranslation() + ) - return code.split('\n') + return code.split("\n") - def CodeUnmarshal(self, buf, tag_name, var_name, var_len): - code = ['if (evtag_unmarshal_string(%(buf)s, %(tag)s, &%(var)s) == -1) {', - ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', - ' return (-1);', - '}' - ] - code = '\n'.join(code) % self.GetTranslation({ - 'buf' : buf, - 'tag' : tag_name, - 'var' : var_name }) - return code.split('\n') - - def CodeMarshal(self, buf, tag_name, var_name, var_len): - code = ['evtag_marshal_string(%s, %s, %s);' % ( - buf, tag_name, var_name)] + def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): + code = [ + "if (evtag_unmarshal_string(%(buf)s, %(tag)s, &%(var)s) == -1) {", + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + " return (-1);", + "}", + ] + code = "\n".join(code) % self.GetTranslation( + {"buf": buf, "tag": tag_name, "var": var_name} + ) + return code.split("\n") + + @staticmethod + def CodeMarshal(buf, tag_name, var_name, _var_len): + code = ["evtag_marshal_string(%s, %s, %s);" % (buf, tag_name, var_name)] return code def CodeClear(self, structname): - code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), - ' free(%s->%s_data);' % (structname, self.Name()), - ' %s->%s_data = NULL;' % (structname, self.Name()), - ' %s->%s_set = 0;' % (structname, self.Name()), - '}' - ] + code = [ + "if (%s->%s_set == 1) {" % (structname, self.Name()), + " free(%s->%s_data);" % (structname, self.Name()), + " %s->%s_data = NULL;" % (structname, self.Name()), + " %s->%s_set = 0;" % (structname, self.Name()), + "}", + ] return code def CodeInitialize(self, name): - code = ['%s->%s_data = NULL;' % (name, self._name)] + code = ["%s->%s_data = NULL;" % (name, self._name)] return code def CodeFree(self, name): - code = ['if (%s->%s_data != NULL)' % (name, self._name), - ' free (%s->%s_data);' % (name, self._name)] + code = [ + "if (%s->%s_data != NULL)" % (name, self._name), + " free (%s->%s_data);" % (name, self._name), + ] return code def Declaration(self): - dcl = ['char *%s_data;' % self._name] + dcl = ["char *%s_data;" % self._name] return dcl + class EntryStruct(Entry): - def __init__(self, type, name, tag, refname): + def __init__(self, ent_type, name, tag, refname): # Init base class - Entry.__init__(self, type, name, tag) + super(EntryStruct, self).__init__(ent_type, name, tag) self._optpointer = False - self._can_be_array = 1 + self._can_be_array = True self._refname = refname - self._ctype = 'struct %s*' % refname + self._ctype = "struct %s*" % refname self._optaddarg = False def GetInitializer(self): return "NULL" - def GetVarLen(self, var): - return '-1' + def GetVarLen(self, _var): + return "-1" - def CodeArrayAdd(self, varname, value): + def CodeArrayAdd(self, varname, _value): code = [ - '%(varname)s = %(refname)s_new();', - 'if (%(varname)s == NULL)', - ' goto error;' ] + "%(varname)s = %(refname)s_new();", + "if (%(varname)s == NULL)", + " goto error;", + ] - return TranslateList(code, self.GetTranslation({ 'varname' : varname })) + return TranslateList(code, self.GetTranslation({"varname": varname})) def CodeArrayFree(self, var): - code = [ '%(refname)s_free(%(var)s);' % self.GetTranslation( - { 'var' : var }) ] + code = ["%(refname)s_free(%(var)s);" % self.GetTranslation({"var": var})] return code def CodeArrayAssign(self, var, srcvar): code = [ - 'int had_error = 0;', - 'struct evbuffer *tmp = NULL;', - '%(refname)s_clear(%(var)s);', - 'if ((tmp = evbuffer_new()) == NULL) {', + "int had_error = 0;", + "struct evbuffer *tmp = NULL;", + "%(refname)s_clear(%(var)s);", + "if ((tmp = evbuffer_new()) == NULL) {", ' event_warn("%%s: evbuffer_new()", __func__);', - ' had_error = 1;', - ' goto done;', - '}', - '%(refname)s_marshal(tmp, %(srcvar)s);', - 'if (%(refname)s_unmarshal(%(var)s, tmp) == -1) {', + " had_error = 1;", + " goto done;", + "}", + "%(refname)s_marshal(tmp, %(srcvar)s);", + "if (%(refname)s_unmarshal(%(var)s, tmp) == -1) {", ' event_warnx("%%s: %(refname)s_unmarshal", __func__);', - ' had_error = 1;', - ' goto done;', - '}', - 'done:' - 'if (tmp != NULL)', - ' evbuffer_free(tmp);', - 'if (had_error) {', - ' %(refname)s_clear(%(var)s);', - ' return (-1);', - '}' ] - - return TranslateList(code, self.GetTranslation({ - 'var' : var, - 'srcvar' : srcvar})) + " had_error = 1;", + " goto done;", + "}", + "done:", + "if (tmp != NULL)", + " evbuffer_free(tmp);", + "if (had_error) {", + " %(refname)s_clear(%(var)s);", + " return (-1);", + "}", + ] + + return TranslateList(code, self.GetTranslation({"var": var, "srcvar": srcvar})) def CodeGet(self): name = self._name - code = [ 'int', - '%s_%s_get(struct %s *msg, %s *value)' % ( - self._struct.Name(), name, - self._struct.Name(), self._ctype), - '{', - ' if (msg->%s_set != 1) {' % name, - ' msg->%s_data = %s_new();' % (name, self._refname), - ' if (msg->%s_data == NULL)' % name, - ' return (-1);', - ' msg->%s_set = 1;' % name, - ' }', - ' *value = msg->%s_data;' % name, - ' return (0);', - '}' ] + code = [ + "int", + "%s_%s_get(struct %s *msg, %s *value)" + % (self._struct.Name(), name, self._struct.Name(), self._ctype), + "{", + " if (msg->%s_set != 1) {" % name, + " msg->%s_data = %s_new();" % (name, self._refname), + " if (msg->%s_data == NULL)" % name, + " return (-1);", + " msg->%s_set = 1;" % name, + " }", + " *value = msg->%s_data;" % name, + " return (0);", + "}", + ] return code def CodeAssign(self): - name = self._name - code = """int + code = ( + """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, const %(ctype)s value) { @@ -875,186 +987,210 @@ def CodeAssign(self): msg->%(name)s_data = NULL; } return (-1); -}""" % self.GetTranslation() - return code.split('\n') +}""" + % self.GetTranslation() + ) + return code.split("\n") def CodeComplete(self, structname, var_name): - code = [ 'if (%(structname)s->%(name)s_set && ' - '%(refname)s_complete(%(var)s) == -1)', - ' return (-1);' ] + code = [ + "if (%(structname)s->%(name)s_set && " + "%(refname)s_complete(%(var)s) == -1)", + " return (-1);", + ] - return TranslateList(code, self.GetTranslation({ - 'structname' : structname, - 'var' : var_name })) + return TranslateList( + code, self.GetTranslation({"structname": structname, "var": var_name}) + ) - def CodeUnmarshal(self, buf, tag_name, var_name, var_len): - code = ['%(var)s = %(refname)s_new();', - 'if (%(var)s == NULL)', - ' return (-1);', - 'if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag)s, ' - '%(var)s) == -1) {', - ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', - ' return (-1);', - '}' - ] - code = '\n'.join(code) % self.GetTranslation({ - 'buf' : buf, - 'tag' : tag_name, - 'var' : var_name }) - return code.split('\n') - - def CodeMarshal(self, buf, tag_name, var_name, var_len): - code = ['evtag_marshal_%s(%s, %s, %s);' % ( - self._refname, buf, tag_name, var_name)] + def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): + code = [ + "%(var)s = %(refname)s_new();", + "if (%(var)s == NULL)", + " return (-1);", + "if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag)s, ", + " %(var)s) == -1) {", + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + " return (-1);", + "}", + ] + code = "\n".join(code) % self.GetTranslation( + {"buf": buf, "tag": tag_name, "var": var_name} + ) + return code.split("\n") + + def CodeMarshal(self, buf, tag_name, var_name, _var_len): + code = [ + "evtag_marshal_%s(%s, %s, %s);" % (self._refname, buf, tag_name, var_name) + ] return code def CodeClear(self, structname): - code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), - ' %s_free(%s->%s_data);' % ( - self._refname, structname, self.Name()), - ' %s->%s_data = NULL;' % (structname, self.Name()), - ' %s->%s_set = 0;' % (structname, self.Name()), - '}' - ] + code = [ + "if (%s->%s_set == 1) {" % (structname, self.Name()), + " %s_free(%s->%s_data);" % (self._refname, structname, self.Name()), + " %s->%s_data = NULL;" % (structname, self.Name()), + " %s->%s_set = 0;" % (structname, self.Name()), + "}", + ] return code def CodeInitialize(self, name): - code = ['%s->%s_data = NULL;' % (name, self._name)] + code = ["%s->%s_data = NULL;" % (name, self._name)] return code def CodeFree(self, name): - code = ['if (%s->%s_data != NULL)' % (name, self._name), - ' %s_free(%s->%s_data);' % ( - self._refname, name, self._name)] + code = [ + "if (%s->%s_data != NULL)" % (name, self._name), + " %s_free(%s->%s_data);" % (self._refname, name, self._name), + ] return code def Declaration(self): - dcl = ['%s %s_data;' % (self._ctype, self._name)] + dcl = ["%s %s_data;" % (self._ctype, self._name)] return dcl + class EntryVarBytes(Entry): - def __init__(self, type, name, tag): + def __init__(self, ent_type, name, tag): # Init base class - Entry.__init__(self, type, name, tag) + super(EntryVarBytes, self).__init__(ent_type, name, tag) - self._ctype = 'ev_uint8_t *' + self._ctype = "ev_uint8_t *" - def GetInitializer(self): + @staticmethod + def GetInitializer(): return "NULL" def GetVarLen(self, var): - return '%(var)s->%(name)s_length' % self.GetTranslation({ 'var' : var }) + return "%(var)s->%(name)s_length" % self.GetTranslation({"var": var}) - def CodeArrayAdd(self, varname, value): + @staticmethod + def CodeArrayAdd(varname, _value): # xxx: copy - return [ '%(varname)s = NULL;' % { 'varname' : varname } ] + return ["%(varname)s = NULL;" % {"varname": varname}] def GetDeclaration(self, funcname): - code = [ 'int %s(struct %s *, %s *, ev_uint32_t *);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, %s *, ev_uint32_t *);" + % (funcname, self._struct.Name(), self._ctype) + ] return code def AssignDeclaration(self, funcname): - code = [ 'int %s(struct %s *, const %s, ev_uint32_t);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, const %s, ev_uint32_t);" + % (funcname, self._struct.Name(), self._ctype) + ] return code def CodeAssign(self): name = self._name - code = [ 'int', - '%s_%s_assign(struct %s *msg, ' - 'const %s value, ev_uint32_t len)' % ( - self._struct.Name(), name, - self._struct.Name(), self._ctype), - '{', - ' if (msg->%s_data != NULL)' % name, - ' free (msg->%s_data);' % name, - ' msg->%s_data = malloc(len);' % name, - ' if (msg->%s_data == NULL)' % name, - ' return (-1);', - ' msg->%s_set = 1;' % name, - ' msg->%s_length = len;' % name, - ' memcpy(msg->%s_data, value, len);' % name, - ' return (0);', - '}' ] + code = [ + "int", + "%s_%s_assign(struct %s *msg, " + "const %s value, ev_uint32_t len)" + % (self._struct.Name(), name, self._struct.Name(), self._ctype), + "{", + " if (msg->%s_data != NULL)" % name, + " free (msg->%s_data);" % name, + " msg->%s_data = malloc(len);" % name, + " if (msg->%s_data == NULL)" % name, + " return (-1);", + " msg->%s_set = 1;" % name, + " msg->%s_length = len;" % name, + " memcpy(msg->%s_data, value, len);" % name, + " return (0);", + "}", + ] return code def CodeGet(self): name = self._name - code = [ 'int', - '%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)' % ( - self._struct.Name(), name, - self._struct.Name(), self._ctype), - '{', - ' if (msg->%s_set != 1)' % name, - ' return (-1);', - ' *value = msg->%s_data;' % name, - ' *plen = msg->%s_length;' % name, - ' return (0);', - '}' ] + code = [ + "int", + "%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)" + % (self._struct.Name(), name, self._struct.Name(), self._ctype), + "{", + " if (msg->%s_set != 1)" % name, + " return (-1);", + " *value = msg->%s_data;" % name, + " *plen = msg->%s_length;" % name, + " return (0);", + "}", + ] return code def CodeUnmarshal(self, buf, tag_name, var_name, var_len): - code = ['if (evtag_payload_length(%(buf)s, &%(varlen)s) == -1)', - ' return (-1);', - # We do not want DoS opportunities - 'if (%(varlen)s > evbuffer_get_length(%(buf)s))', - ' return (-1);', - 'if ((%(var)s = malloc(%(varlen)s)) == NULL)', - ' return (-1);', - 'if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, %(var)s, ' - '%(varlen)s) == -1) {', - ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', - ' return (-1);', - '}' - ] - code = '\n'.join(code) % self.GetTranslation({ - 'buf' : buf, - 'tag' : tag_name, - 'var' : var_name, - 'varlen' : var_len }) - return code.split('\n') - - def CodeMarshal(self, buf, tag_name, var_name, var_len): - code = ['evtag_marshal(%s, %s, %s, %s);' % ( - buf, tag_name, var_name, var_len)] + code = [ + "if (evtag_payload_length(%(buf)s, &%(varlen)s) == -1)", + " return (-1);", + # We do not want DoS opportunities + "if (%(varlen)s > evbuffer_get_length(%(buf)s))", + " return (-1);", + "if ((%(var)s = malloc(%(varlen)s)) == NULL)", + " return (-1);", + "if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, %(var)s, " + "%(varlen)s) == -1) {", + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + " return (-1);", + "}", + ] + code = "\n".join(code) % self.GetTranslation( + {"buf": buf, "tag": tag_name, "var": var_name, "varlen": var_len} + ) + return code.split("\n") + + @staticmethod + def CodeMarshal(buf, tag_name, var_name, var_len): + code = ["evtag_marshal(%s, %s, %s, %s);" % (buf, tag_name, var_name, var_len)] return code def CodeClear(self, structname): - code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), - ' free (%s->%s_data);' % (structname, self.Name()), - ' %s->%s_data = NULL;' % (structname, self.Name()), - ' %s->%s_length = 0;' % (structname, self.Name()), - ' %s->%s_set = 0;' % (structname, self.Name()), - '}' - ] + code = [ + "if (%s->%s_set == 1) {" % (structname, self.Name()), + " free (%s->%s_data);" % (structname, self.Name()), + " %s->%s_data = NULL;" % (structname, self.Name()), + " %s->%s_length = 0;" % (structname, self.Name()), + " %s->%s_set = 0;" % (structname, self.Name()), + "}", + ] return code def CodeInitialize(self, name): - code = ['%s->%s_data = NULL;' % (name, self._name), - '%s->%s_length = 0;' % (name, self._name) ] + code = [ + "%s->%s_data = NULL;" % (name, self._name), + "%s->%s_length = 0;" % (name, self._name), + ] return code def CodeFree(self, name): - code = ['if (%s->%s_data != NULL)' % (name, self._name), - ' free(%s->%s_data);' % (name, self._name)] + code = [ + "if (%s->%s_data != NULL)" % (name, self._name), + " free(%s->%s_data);" % (name, self._name), + ] return code def Declaration(self): - dcl = ['ev_uint8_t *%s_data;' % self._name, - 'ev_uint32_t %s_length;' % self._name] + dcl = [ + "ev_uint8_t *%s_data;" % self._name, + "ev_uint32_t %s_length;" % self._name, + ] return dcl + class EntryArray(Entry): + _index = None + def __init__(self, entry): # Init base class - Entry.__init__(self, entry._type, entry._name, entry._tag) + super(EntryArray, self).__init__(entry._type, entry._name, entry._tag) self._entry = entry self._refname = entry._refname @@ -1065,37 +1201,42 @@ def __init__(self, entry): # provide a new function for accessing the variable name def GetVarName(var_name): - return '%(var)s->%(name)s_data[%(index)s]' % \ - self._entry.GetTranslation({'var' : var_name, - 'index' : self._index}) + return "%(var)s->%(name)s_data[%(index)s]" % self._entry.GetTranslation( + {"var": var_name, "index": self._index} + ) + self._entry.GetVarName = GetVarName def GetInitializer(self): return "NULL" - def GetVarName(self, var_name): - return var_name + def GetVarName(self, var): + return var - def GetVarLen(self, var_name): - return '-1' + def GetVarLen(self, _var_name): + return "-1" def GetDeclaration(self, funcname): """Allows direct access to elements of the array.""" code = [ - 'int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);' % - self.GetTranslation({ 'funcname' : funcname }) ] + "int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);" + % self.GetTranslation({"funcname": funcname}) + ] return code def AssignDeclaration(self, funcname): - code = [ 'int %s(struct %s *, int, const %s);' % ( - funcname, self._struct.Name(), self._ctype ) ] + code = [ + "int %s(struct %s *, int, const %s);" + % (funcname, self._struct.Name(), self._ctype) + ] return code def AddDeclaration(self, funcname): code = [ - '%(ctype)s %(optpointer)s ' - '%(funcname)s(struct %(parent_name)s *msg%(optaddarg)s);' % \ - self.GetTranslation({ 'funcname' : funcname }) ] + "%(ctype)s %(optpointer)s " + "%(funcname)s(struct %(parent_name)s *msg%(optaddarg)s);" + % self.GetTranslation({"funcname": funcname}) + ] return code def CodeGet(self): @@ -1107,226 +1248,249 @@ def CodeGet(self): return (-1); *value = msg->%(name)s_data[offset]; return (0); -}""" % self.GetTranslation() +} +""" % ( + self.GetTranslation() + ) - return code.split('\n') + return code.splitlines() def CodeAssign(self): code = [ - 'int', - '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off,', - ' const %(ctype)s value)', - '{', - ' if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length)', - ' return (-1);\n', - ' {' ] + "int", + "%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off,", + " const %(ctype)s value)", + "{", + " if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length)", + " return (-1);", + "", + " {", + ] code = TranslateList(code, self.GetTranslation()) codearrayassign = self._entry.CodeArrayAssign( - 'msg->%(name)s_data[off]' % self.GetTranslation(), 'value') - code += [' ' + x for x in codearrayassign] + "msg->%(name)s_data[off]" % self.GetTranslation(), "value" + ) + code += [" " + x for x in codearrayassign] - code += TranslateList([ - ' }', - ' return (0);', - '}' ], self.GetTranslation()) + code += TranslateList([" }", " return (0);", "}"], self.GetTranslation()) return code def CodeAdd(self): codearrayadd = self._entry.CodeArrayAdd( - 'msg->%(name)s_data[msg->%(name)s_length - 1]' % self.GetTranslation(), - 'value') + "msg->%(name)s_data[msg->%(name)s_length - 1]" % self.GetTranslation(), + "value", + ) code = [ - 'static int', - '%(parent_name)s_%(name)s_expand_to_hold_more(' - 'struct %(parent_name)s *msg)', - '{', - ' int tobe_allocated = msg->%(name)s_num_allocated;', - ' %(ctype)s* new_data = NULL;', - ' tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1;', - ' new_data = (%(ctype)s*) realloc(msg->%(name)s_data,', - ' tobe_allocated * sizeof(%(ctype)s));', - ' if (new_data == NULL)', - ' return -1;', - ' msg->%(name)s_data = new_data;', - ' msg->%(name)s_num_allocated = tobe_allocated;', - ' return 0;' - '}', - '', - '%(ctype)s %(optpointer)s', - '%(parent_name)s_%(name)s_add(' - 'struct %(parent_name)s *msg%(optaddarg)s)', - '{', - ' if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) {', - ' if (%(parent_name)s_%(name)s_expand_to_hold_more(msg)<0)', - ' goto error;', - ' }' ] + "static int", + "%(parent_name)s_%(name)s_expand_to_hold_more(" + "struct %(parent_name)s *msg)", + "{", + " int tobe_allocated = msg->%(name)s_num_allocated;", + " %(ctype)s* new_data = NULL;", + " tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1;", + " new_data = (%(ctype)s*) realloc(msg->%(name)s_data,", + " tobe_allocated * sizeof(%(ctype)s));", + " if (new_data == NULL)", + " return -1;", + " msg->%(name)s_data = new_data;", + " msg->%(name)s_num_allocated = tobe_allocated;", + " return 0;", + "}", + "", + "%(ctype)s %(optpointer)s", + "%(parent_name)s_%(name)s_add(struct %(parent_name)s *msg%(optaddarg)s)", + "{", + " if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) {", + " if (%(parent_name)s_%(name)s_expand_to_hold_more(msg)<0)", + " goto error;", + " }", + ] code = TranslateList(code, self.GetTranslation()) - code += [' ' + x for x in codearrayadd] - - code += TranslateList([ - ' msg->%(name)s_set = 1;', - ' return %(optreference)s(msg->%(name)s_data[' - 'msg->%(name)s_length - 1]);', - 'error:', - ' --msg->%(name)s_length;', - ' return (NULL);', - '}' ], self.GetTranslation()) + code += [" " + x for x in codearrayadd] + + code += TranslateList( + [ + " msg->%(name)s_set = 1;", + " return %(optreference)s(msg->%(name)s_data[" + "msg->%(name)s_length - 1]);", + "error:", + " --msg->%(name)s_length;", + " return (NULL);", + "}", + ], + self.GetTranslation(), + ) return code def CodeComplete(self, structname, var_name): - self._index = 'i' + self._index = "i" tmp = self._entry.CodeComplete(structname, self._entry.GetVarName(var_name)) # skip the whole loop if there is nothing to check if not tmp: return [] - translate = self.GetTranslation({ 'structname' : structname }) + translate = self.GetTranslation({"structname": structname}) code = [ - '{', - ' int i;', - ' for (i = 0; i < %(structname)s->%(name)s_length; ++i) {' ] + "{", + " int i;", + " for (i = 0; i < %(structname)s->%(name)s_length; ++i) {", + ] code = TranslateList(code, translate) - code += [' ' + x for x in tmp] + code += [" " + x for x in tmp] - code += [ - ' }', - '}' ] + code += [" }", "}"] return code - def CodeUnmarshal(self, buf, tag_name, var_name, var_len): - translate = self.GetTranslation({ 'var' : var_name, - 'buf' : buf, - 'tag' : tag_name, - 'init' : self._entry.GetInitializer()}) + def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): + translate = self.GetTranslation( + { + "var": var_name, + "buf": buf, + "tag": tag_name, + "init": self._entry.GetInitializer(), + } + ) code = [ - 'if (%(var)s->%(name)s_length >= %(var)s->%(name)s_num_allocated &&', - ' %(parent_name)s_%(name)s_expand_to_hold_more(%(var)s) < 0) {', + "if (%(var)s->%(name)s_length >= %(var)s->%(name)s_num_allocated &&", + " %(parent_name)s_%(name)s_expand_to_hold_more(%(var)s) < 0) {", ' puts("HEY NOW");', - ' return (-1);', - '}'] + " return (-1);", + "}", + ] # the unmarshal code directly returns code = TranslateList(code, translate) - self._index = '%(var)s->%(name)s_length' % translate - code += self._entry.CodeUnmarshal(buf, tag_name, - self._entry.GetVarName(var_name), - self._entry.GetVarLen(var_name)) + self._index = "%(var)s->%(name)s_length" % translate + code += self._entry.CodeUnmarshal( + buf, + tag_name, + self._entry.GetVarName(var_name), + self._entry.GetVarLen(var_name), + ) - code += [ '++%(var)s->%(name)s_length;' % translate ] + code += ["++%(var)s->%(name)s_length;" % translate] return code - def CodeMarshal(self, buf, tag_name, var_name, var_len): - code = ['{', - ' int i;', - ' for (i = 0; i < %(var)s->%(name)s_length; ++i) {' ] + def CodeMarshal(self, buf, tag_name, var_name, _var_len): + code = ["{", " int i;", " for (i = 0; i < %(var)s->%(name)s_length; ++i) {"] - self._index = 'i' - code += self._entry.CodeMarshal(buf, tag_name, - self._entry.GetVarName(var_name), - self._entry.GetVarLen(var_name)) - code += [' }', - '}' - ] + self._index = "i" + code += self._entry.CodeMarshal( + buf, + tag_name, + self._entry.GetVarName(var_name), + self._entry.GetVarLen(var_name), + ) + code += [" }", "}"] - code = "\n".join(code) % self.GetTranslation({ 'var' : var_name }) + code = "\n".join(code) % self.GetTranslation({"var": var_name}) - return code.split('\n') + return code.split("\n") def CodeClear(self, structname): - translate = self.GetTranslation({ 'structname' : structname }) + translate = self.GetTranslation({"structname": structname}) codearrayfree = self._entry.CodeArrayFree( - '%(structname)s->%(name)s_data[i]' % self.GetTranslation( - { 'structname' : structname } )) + "%(structname)s->%(name)s_data[i]" + % self.GetTranslation({"structname": structname}) + ) - code = [ 'if (%(structname)s->%(name)s_set == 1) {' ] + code = ["if (%(structname)s->%(name)s_set == 1) {"] if codearrayfree: code += [ - ' int i;', - ' for (i = 0; i < %(structname)s->%(name)s_length; ++i) {' ] + " int i;", + " for (i = 0; i < %(structname)s->%(name)s_length; ++i) {", + ] code = TranslateList(code, translate) if codearrayfree: - code += [' ' + x for x in codearrayfree] - code += [ - ' }' ] - - code += TranslateList([ - ' free(%(structname)s->%(name)s_data);', - ' %(structname)s->%(name)s_data = NULL;', - ' %(structname)s->%(name)s_set = 0;', - ' %(structname)s->%(name)s_length = 0;', - ' %(structname)s->%(name)s_num_allocated = 0;', - '}' - ], translate) + code += [" " + x for x in codearrayfree] + code += [" }"] + + code += TranslateList( + [ + " free(%(structname)s->%(name)s_data);", + " %(structname)s->%(name)s_data = NULL;", + " %(structname)s->%(name)s_set = 0;", + " %(structname)s->%(name)s_length = 0;", + " %(structname)s->%(name)s_num_allocated = 0;", + "}", + ], + translate, + ) return code def CodeInitialize(self, name): - code = ['%s->%s_data = NULL;' % (name, self._name), - '%s->%s_length = 0;' % (name, self._name), - '%s->%s_num_allocated = 0;' % (name, self._name)] + code = [ + "%s->%s_data = NULL;" % (name, self._name), + "%s->%s_length = 0;" % (name, self._name), + "%s->%s_num_allocated = 0;" % (name, self._name), + ] return code def CodeFree(self, structname): - code = self.CodeClear(structname); + code = self.CodeClear(structname) - code += TranslateList([ - 'free(%(structname)s->%(name)s_data);' ], - self.GetTranslation({'structname' : structname })) + code += TranslateList( + ["free(%(structname)s->%(name)s_data);"], + self.GetTranslation({"structname": structname}), + ) return code def Declaration(self): - dcl = ['%s *%s_data;' % (self._ctype, self._name), - 'int %s_length;' % self._name, - 'int %s_num_allocated;' % self._name ] + dcl = [ + "%s *%s_data;" % (self._ctype, self._name), + "int %s_length;" % self._name, + "int %s_num_allocated;" % self._name, + ] return dcl + def NormalizeLine(line): - global white - global cppcomment - line = cppcomment.sub('', line) + line = CPPCOMMENT_RE.sub("", line) line = line.strip() - line = white.sub(' ', line) + line = WHITESPACE_RE.sub(" ", line) return line + +ENTRY_NAME_RE = re.compile(r"(?P[^\[\]]+)(\[(?P.*)\])?") +ENTRY_TAG_NUMBER_RE = re.compile(r"(0x)?\d+", re.I) + + def ProcessOneEntry(factory, newstruct, entry): - optional = 0 - array = 0 - entry_type = '' - name = '' - tag = '' + optional = False + array = False + entry_type = "" + name = "" + tag = "" tag_set = None - separator = '' - fixed_length = '' - - tokens = entry.split(' ') - while tokens: - token = tokens[0] - tokens = tokens[1:] + separator = "" + fixed_length = "" + for token in entry.split(" "): if not entry_type: - if not optional and token == 'optional': - optional = 1 + if not optional and token == "optional": + optional = True continue - if not array and token == 'array': - array = 1 + if not array and token == "array": + array = True continue if not entry_type: @@ -1334,53 +1498,52 @@ def ProcessOneEntry(factory, newstruct, entry): continue if not name: - res = re.match(r'^([^\[\]]+)(\[.*\])?$', token) + res = ENTRY_NAME_RE.match(token) if not res: - raise RpcGenError( - 'Cannot parse name: \"%s\" ' - 'around line %d' % (entry, line_count)) - name = res.group(1) - fixed_length = res.group(2) - if fixed_length: - fixed_length = fixed_length[1:-1] + raise RpcGenError( + r"""Cannot parse name: "%s" around line %d""" % (entry, LINE_COUNT) + ) + name = res.group("name") + fixed_length = res.group("fixed_length") continue if not separator: separator = token - if separator != '=': - raise RpcGenError('Expected "=" after name \"%s\" got %s' - % (name, token)) + if separator != "=": + raise RpcGenError( + r'''Expected "=" after name "%s" got "%s"''' % (name, token) + ) continue if not tag_set: tag_set = 1 - if not re.match(r'^(0x)?[0-9]+$', token): - raise RpcGenError('Expected tag number: \"%s\"' % entry) + if not ENTRY_TAG_NUMBER_RE.match(token): + raise RpcGenError(r'''Expected tag number: "%s"''' % (entry)) tag = int(token, 0) continue - raise RpcGenError('Cannot parse \"%s\"' % entry) + raise RpcGenError(r'''Cannot parse "%s"''' % (entry)) if not tag_set: - raise RpcGenError('Need tag number: \"%s\"' % entry) + raise RpcGenError(r'''Need tag number: "%s"''' % (entry)) # Create the right entry - if entry_type == 'bytes': + if entry_type == "bytes": if fixed_length: newentry = factory.EntryBytes(entry_type, name, tag, fixed_length) else: newentry = factory.EntryVarBytes(entry_type, name, tag) - elif entry_type == 'int' and not fixed_length: + elif entry_type == "int" and not fixed_length: newentry = factory.EntryInt(entry_type, name, tag) - elif entry_type == 'int64' and not fixed_length: + elif entry_type == "int64" and not fixed_length: newentry = factory.EntryInt(entry_type, name, tag, bits=64) - elif entry_type == 'string' and not fixed_length: + elif entry_type == "string" and not fixed_length: newentry = factory.EntryString(entry_type, name, tag) else: - res = structref.match(entry_type) + res = STRUCT_REF_RE.match(entry_type) if res: # References another struct defined in our file - newentry = factory.EntryStruct(entry_type, name, tag, res.group(1)) + newentry = factory.EntryStruct(entry_type, name, tag, res.group("name")) else: raise RpcGenError('Bad type: "%s" in "%s"' % (entry_type, entry)) @@ -1392,32 +1555,30 @@ def ProcessOneEntry(factory, newstruct, entry): newentry.MakeArray() newentry.SetStruct(newstruct) - newentry.SetLineCount(line_count) + newentry.SetLineCount(LINE_COUNT) newentry.Verify() if array: # We need to encapsulate this entry into a struct - newname = newentry.Name()+ '_array' - - # Now borgify the new entry. newentry = factory.EntryArray(newentry) newentry.SetStruct(newstruct) - newentry.SetLineCount(line_count) + newentry.SetLineCount(LINE_COUNT) newentry.MakeArray() newstruct.AddEntry(newentry) return structs + def ProcessStruct(factory, data): - tokens = data.split(' ') + tokens = data.split(" ") # First three tokens are: 'struct' 'name' '{' newstruct = factory.Struct(tokens[1]) - inside = ' '.join(tokens[3:-1]) + inside = " ".join(tokens[3:-1]) - tokens = inside.split(';') + tokens = inside.split(";") structs = [] @@ -1432,36 +1593,52 @@ def ProcessStruct(factory, data): structs.append(newstruct) return structs -def GetNextStruct(file): - global line_count - global cppdirect - got_struct = 0 +C_COMMENT_START = "/*" +C_COMMENT_END = "*/" - processed_lines = [] +C_COMMENT_START_RE = re.compile(re.escape(C_COMMENT_START)) +C_COMMENT_END_RE = re.compile(re.escape(C_COMMENT_END)) - have_c_comment = 0 - data = '' - while 1: - line = file.readline() +C_COMMENT_START_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_START))) +C_COMMENT_END_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_END))) + +C_MULTILINE_COMMENT_SUB_RE = re.compile( + r"%s.*?%s" % (re.escape(C_COMMENT_START), re.escape(C_COMMENT_END)) +) +CPP_CONDITIONAL_BLOCK_RE = re.compile(r"#(if( |def)|endif)") +INCLUDE_RE = re.compile(r'#include (".+"|<.+>)') + + +def GetNextStruct(filep): + global CPP_DIRECT + global LINE_COUNT + + got_struct = False + have_c_comment = False + + data = "" + + while True: + line = filep.readline() if not line: break - line_count += 1 + LINE_COUNT += 1 line = line[:-1] - if not have_c_comment and re.search(r'/\*', line): - if re.search(r'/\*.*?\*/', line): - line = re.sub(r'/\*.*?\*/', '', line) + if not have_c_comment and C_COMMENT_START_RE.search(line): + if C_MULTILINE_COMMENT_SUB_RE.search(line): + line = C_MULTILINE_COMMENT_SUB_RE.sub("", line) else: - line = re.sub(r'/\*.*$', '', line) - have_c_comment = 1 + line = C_COMMENT_START_SUB_RE.sub("", line) + have_c_comment = True if have_c_comment: - if not re.search(r'\*/', line): + if not C_COMMENT_END_RE.search(line): continue - have_c_comment = 0 - line = re.sub(r'^.*\*/', '', line) + have_c_comment = False + line = C_COMMENT_END_SUB_RE.sub("", line) line = NormalizeLine(line) @@ -1469,47 +1646,39 @@ def GetNextStruct(file): continue if not got_struct: - if re.match(r'#include ["<].*[>"]', line): - cppdirect.append(line) - continue - - if re.match(r'^#(if( |def)|endif)', line): - cppdirect.append(line) - continue - - if re.match(r'^#define', line): - headerdirect.append(line) - continue - - if not structdef.match(line): - raise RpcGenError('Missing struct on line %d: %s' - % (line_count, line)) + if INCLUDE_RE.match(line): + CPP_DIRECT.append(line) + elif CPP_CONDITIONAL_BLOCK_RE.match(line): + CPP_DIRECT.append(line) + elif PREPROCESSOR_DEF_RE.match(line): + HEADER_DIRECT.append(line) + elif not STRUCT_DEF_RE.match(line): + raise RpcGenError("Missing struct on line %d: %s" % (LINE_COUNT, line)) else: - got_struct = 1 + got_struct = True data += line continue # We are inside the struct - tokens = line.split('}') + tokens = line.split("}") if len(tokens) == 1: - data += ' ' + line + data += " " + line continue - if len(tokens[1]): - raise RpcGenError('Trailing garbage after struct on line %d' - % line_count) + if tokens[1]: + raise RpcGenError("Trailing garbage after struct on line %d" % LINE_COUNT) # We found the end of the struct - data += ' %s}' % tokens[0] + data += " %s}" % tokens[0] break # Remove any comments, that might be in there - data = re.sub(r'/\*.*\*/', '', data) + data = re.sub(r"/\*.*\*/", "", data) return data -def Parse(factory, file): +def Parse(factory, filep): """ Parses the input file and returns C code and corresponding header file. """ @@ -1518,7 +1687,7 @@ def Parse(factory, file): while 1: # Just gets the whole struct nicely formatted - data = GetNextStruct(file) + data = GetNextStruct(filep) if not data: break @@ -1527,205 +1696,230 @@ def Parse(factory, file): return entities -class CCodeGenerator: + +class CCodeGenerator(object): def __init__(self): pass - def GuardName(self, name): + @staticmethod + def GuardName(name): # Use the complete provided path to the input file, with all # non-identifier characters replaced with underscores, to # reduce the chance of a collision between guard macros. - return 'EVENT_RPCOUT_' + nonident.sub('_', name).upper() + '_' + return "EVENT_RPCOUT_%s_" % (NONIDENT_RE.sub("_", name).upper()) def HeaderPreamble(self, name): guard = self.GuardName(name) - pre = ( - '/*\n' - ' * Automatically generated from %s\n' - ' */\n\n' - '#ifndef %s\n' - '#define %s\n\n' ) % ( - name, guard, guard) - - for statement in headerdirect: - pre += '%s\n' % statement - if headerdirect: - pre += '\n' - - pre += ( - '#include /* for ev_uint*_t */\n' - '#include \n' + pre = """ +/* + * Automatically generated from %s + */ + +#ifndef %s +#define %s + +""" % ( + name, + guard, + guard, ) + if HEADER_DIRECT: + for statement in HEADER_DIRECT: + pre += "%s\n" % statement + pre += "\n" + + pre += """ +#include /* for ev_uint*_t */ +#include +""" + return pre def HeaderPostamble(self, name): guard = self.GuardName(name) - return '#endif /* %s */' % guard + return "#endif /* %s */" % (guard) - def BodyPreamble(self, name, header_file): + @staticmethod + def BodyPreamble(name, header_file): global _NAME global _VERSION - slash = header_file.rfind('/') + slash = header_file.rfind("/") if slash != -1: - header_file = header_file[slash+1:] - - pre = ( '/*\n' - ' * Automatically generated from %s\n' - ' * by %s/%s. DO NOT EDIT THIS FILE.\n' - ' */\n\n' ) % (name, _NAME, _VERSION) - pre += ( '#include \n' - '#include \n' - '#include \n' - '#include \n' - '#include \n' - '#include \n' - '#include \n\n' - '#if defined(EVENT__HAVE___func__)\n' - '# ifndef __func__\n' - '# define __func__ __func__\n' - '# endif\n' - '#elif defined(EVENT__HAVE___FUNCTION__)\n' - '# define __func__ __FUNCTION__\n' - '#else\n' - '# define __func__ __FILE__\n' - '#endif\n\n' - ) - - for statement in cppdirect: - pre += '%s\n' % statement + header_file = header_file[slash + 1 :] + + pre = """ +/* + * Automatically generated from %(name)s + * by %(script_name)s/%(script_version)s. DO NOT EDIT THIS FILE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(EVENT__HAVE___func__) +# ifndef __func__ +# define __func__ __func__ +# endif +#elif defined(EVENT__HAVE___FUNCTION__) +# define __func__ __FUNCTION__ +#else +# define __func__ __FILE__ +#endif + +""" % { + "name": name, + "script_name": _NAME, + "script_version": _VERSION, + } + + for statement in CPP_DIRECT: + pre += "%s\n" % statement pre += '\n#include "%s"\n\n' % header_file - pre += 'void event_warn(const char *fmt, ...);\n' - pre += 'void event_warnx(const char *fmt, ...);\n\n' + pre += "void event_warn(const char *fmt, ...);\n" + pre += "void event_warnx(const char *fmt, ...);\n\n" return pre - def HeaderFilename(self, filename): - return '.'.join(filename.split('.')[:-1]) + '.h' + @staticmethod + def HeaderFilename(filename): + return ".".join(filename.split(".")[:-1]) + ".h" - def CodeFilename(self, filename): - return '.'.join(filename.split('.')[:-1]) + '.gen.c' + @staticmethod + def CodeFilename(filename): + return ".".join(filename.split(".")[:-1]) + ".gen.c" - def Struct(self, name): + @staticmethod + def Struct(name): return StructCCode(name) - def EntryBytes(self, entry_type, name, tag, fixed_length): + @staticmethod + def EntryBytes(entry_type, name, tag, fixed_length): return EntryBytes(entry_type, name, tag, fixed_length) - def EntryVarBytes(self, entry_type, name, tag): + @staticmethod + def EntryVarBytes(entry_type, name, tag): return EntryVarBytes(entry_type, name, tag) - def EntryInt(self, entry_type, name, tag, bits=32): + @staticmethod + def EntryInt(entry_type, name, tag, bits=32): return EntryInt(entry_type, name, tag, bits) - def EntryString(self, entry_type, name, tag): + @staticmethod + def EntryString(entry_type, name, tag): return EntryString(entry_type, name, tag) - def EntryStruct(self, entry_type, name, tag, struct_name): + @staticmethod + def EntryStruct(entry_type, name, tag, struct_name): return EntryStruct(entry_type, name, tag, struct_name) - def EntryArray(self, entry): + @staticmethod + def EntryArray(entry): return EntryArray(entry) -class Usage(RpcGenError): - def __init__(self, argv0): - RpcGenError.__init__("usage: %s input.rpc [[output.h] output.c]" - % argv0) -class CommandLine: - def __init__(self, argv): +class CommandLine(object): + def __init__(self, argv=None): """Initialize a command-line to launch event_rpcgen, as if from a command-line with CommandLine(sys.argv). If you're calling this directly, remember to provide a dummy value for sys.argv[0] """ + global QUIETLY + self.filename = None self.header_file = None self.impl_file = None self.factory = CCodeGenerator() - if len(argv) >= 2 and argv[1] == '--quiet': - global QUIETLY - QUIETLY = 1 - del argv[1] + parser = argparse.ArgumentParser( + usage="%(prog)s [options] rpc-file [[h-file] c-file]" + ) + parser.add_argument("--quiet", action="store_true", default=False) + parser.add_argument("rpc_file", type=argparse.FileType("r")) + + args, extra_args = parser.parse_known_args(args=argv) - if len(argv) < 2 or len(argv) > 4: - raise Usage(argv[0]) + QUIETLY = args.quiet - self.filename = argv[1].replace('\\', '/') - if len(argv) == 3: - self.impl_file = argv[2].replace('\\', '/') - if len(argv) == 4: - self.header_file = argv[2].replace('\\', '/') - self.impl_file = argv[3].replace('\\', '/') + if extra_args: + if len(extra_args) == 1: + self.impl_file = extra_args[0].replace("\\", "/") + elif len(extra_args) == 2: + self.header_file = extra_args[0].replace("\\", "/") + self.impl_file = extra_args[1].replace("\\", "/") + else: + parser.error("Spurious arguments provided") - if not self.filename: - raise Usage(argv[0]) + self.rpc_file = args.rpc_file if not self.impl_file: - self.impl_file = self.factory.CodeFilename(self.filename) + self.impl_file = self.factory.CodeFilename(self.rpc_file.name) if not self.header_file: self.header_file = self.factory.HeaderFilename(self.impl_file) - if not self.impl_file.endswith('.c'): - raise RpcGenError("can only generate C implementation files") - if not self.header_file.endswith('.h'): - raise RpcGenError("can only generate C header files") + if not self.impl_file.endswith(".c"): + parser.error("can only generate C implementation files") + if not self.header_file.endswith(".h"): + parser.error("can only generate C header files") def run(self): - filename = self.filename + filename = self.rpc_file.name header_file = self.header_file impl_file = self.impl_file factory = self.factory - declare('Reading \"%s\"' % filename) + declare('Reading "%s"' % filename) - fp = open(filename, 'r') - entities = Parse(factory, fp) - fp.close() + with self.rpc_file: + entities = Parse(factory, self.rpc_file) declare('... creating "%s"' % header_file) - header_fp = open(header_file, 'w') - header_fp.write(factory.HeaderPreamble(filename)) + with open(header_file, "w") as header_fp: + header_fp.write(factory.HeaderPreamble(filename)) - # Create forward declarations: allows other structs to reference - # each other - for entry in entities: - entry.PrintForwardDeclaration(header_fp) - header_fp.write('\n') + # Create forward declarations: allows other structs to reference + # each other + for entry in entities: + entry.PrintForwardDeclaration(header_fp) + header_fp.write("\n") - for entry in entities: - entry.PrintTags(header_fp) - entry.PrintDeclaration(header_fp) - header_fp.write(factory.HeaderPostamble(filename)) - header_fp.close() + for entry in entities: + entry.PrintTags(header_fp) + entry.PrintDeclaration(header_fp) + header_fp.write(factory.HeaderPostamble(filename)) declare('... creating "%s"' % impl_file) - impl_fp = open(impl_file, 'w') - impl_fp.write(factory.BodyPreamble(filename, header_file)) - for entry in entities: - entry.PrintCode(impl_fp) - impl_fp.close() + with open(impl_file, "w") as impl_fp: + impl_fp.write(factory.BodyPreamble(filename, header_file)) + for entry in entities: + entry.PrintCode(impl_fp) -if __name__ == '__main__': - try: - CommandLine(sys.argv).run() - sys.exit(0) +def main(argv=None): + try: + CommandLine(argv=argv).run() + return 0 except RpcGenError as e: sys.stderr.write(e) - sys.exit(1) - except EnvironmentError as e: if e.filename and e.strerror: sys.stderr.write("%s: %s" % (e.filename, e.strerror)) - sys.exit(1) elif e.strerror: sys.stderr.write(e.strerror) - sys.exit(1) else: raise + return 1 + + +if __name__ == "__main__": + sys.exit(main(argv=sys.argv[1:])) diff --git a/evmap.c b/evmap.c index ffc991f5cc..e4e35c6877 100644 --- a/evmap.c +++ b/evmap.c @@ -432,7 +432,7 @@ evmap_io_active_(struct event_base *base, evutil_socket_t fd, short events) if (NULL == ctx) return; LIST_FOREACH(ev, &ctx->events, ev_io_next) { - if (ev->ev_events & events) + if (ev->ev_events & (events & ~EV_ET)) event_active_nolock_(ev, ev->ev_events & events, 1); } } diff --git a/evthread-internal.h b/evthread-internal.h index 5fdf31619c..83e409f09b 100644 --- a/evthread-internal.h +++ b/evthread-internal.h @@ -38,7 +38,7 @@ extern "C" { struct event_base; -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__CYGWIN__) /* On Windows, the way we currently make DLLs, it's not allowed for us to * have shared global structures. Thus, we only do the direct-call-to-function * code path if we know that the local shared library system supports it. diff --git a/evutil.c b/evutil.c index 3412c2aeda..9817f08643 100644 --- a/evutil.c +++ b/evutil.c @@ -41,6 +41,7 @@ /* For structs needed by GetAdaptersAddresses */ #define _WIN32_WINNT 0x0501 #include +#include #endif #include @@ -74,6 +75,9 @@ #endif #include #include +#ifndef _WIN32 +#include +#endif #ifdef EVENT__HAVE_IFADDRS_H #include #endif @@ -694,7 +698,7 @@ evutil_check_ifaddrs(void) "GetAdaptersInfo", but that's deprecated; let's just try GetAdaptersAddresses and fall back to connect+getsockname. */ - HMODULE lib = evutil_load_windows_system_library_(TEXT("ihplapi.dll")); + HMODULE lib = evutil_load_windows_system_library_(TEXT("iphlpapi.dll")); GetAdaptersAddresses_fn_t fn; ULONG size, res; IP_ADAPTER_ADDRESSES *addresses = NULL, *address; @@ -990,6 +994,7 @@ evutil_getaddrinfo_common_(const char *nodename, const char *servname, struct evutil_addrinfo *hints, struct evutil_addrinfo **res, int *portnum) { int port = 0; + unsigned int if_index; const char *pname; if (nodename == NULL && servname == NULL) @@ -1063,10 +1068,12 @@ evutil_getaddrinfo_common_(const char *nodename, const char *servname, if (hints->ai_family == PF_INET6 || hints->ai_family == PF_UNSPEC) { struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); - if (1==evutil_inet_pton(AF_INET6, nodename, &sin6.sin6_addr)) { + if (1 == evutil_inet_pton_scope( + AF_INET6, nodename, &sin6.sin6_addr, &if_index)) { /* Got an ipv6 address. */ sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(port); + sin6.sin6_scope_id = if_index; *res = evutil_new_addrinfo_((struct sockaddr*)&sin6, sizeof(sin6), hints); if (!*res) @@ -1981,6 +1988,41 @@ evutil_inet_ntop(int af, const void *src, char *dst, size_t len) #endif } +int +evutil_inet_pton_scope(int af, const char *src, void *dst, unsigned *indexp) +{ + int r; + unsigned if_index; + char *check, *cp, *tmp_src; + + *indexp = 0; /* Reasonable default */ + + /* Bail out if not IPv6 */ + if (af != AF_INET6) + return evutil_inet_pton(af, src, dst); + + cp = strchr(src, '%'); + + /* Bail out if no zone ID */ + if (cp == NULL) + return evutil_inet_pton(af, src, dst); + + if_index = if_nametoindex(cp + 1); + if (if_index == 0) { + /* Could be numeric */ + if_index = strtoul(cp + 1, &check, 10); + if (check[0] != '\0') + return 0; + } + *indexp = if_index; + tmp_src = mm_strdup(src); + cp = strchr(tmp_src, '%'); + *cp = '\0'; + r = evutil_inet_pton(af, tmp_src, dst); + free(tmp_src); + return r; +} + int evutil_inet_pton(int af, const char *src, void *dst) { @@ -2097,6 +2139,7 @@ int evutil_parse_sockaddr_port(const char *ip_as_string, struct sockaddr *out, int *outlen) { int port; + unsigned int if_index; char buf[128]; const char *cp, *addr_part, *port_part; int is_ipv6; @@ -2166,10 +2209,13 @@ evutil_parse_sockaddr_port(const char *ip_as_string, struct sockaddr *out, int * #endif sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(port); - if (1 != evutil_inet_pton(AF_INET6, addr_part, &sin6.sin6_addr)) + if (1 != evutil_inet_pton_scope( + AF_INET6, addr_part, &sin6.sin6_addr, &if_index)) { return -1; + } if ((int)sizeof(sin6) > *outlen) return -1; + sin6.sin6_scope_id = if_index; memset(out, 0, *outlen); memcpy(out, &sin6, sizeof(sin6)); *outlen = sizeof(sin6); @@ -2323,7 +2369,7 @@ static const unsigned char EVUTIL_TOLOWER_TABLE[256] = { #define IMPL_CTYPE_FN(name) \ int EVUTIL_##name##_(char c) { \ ev_uint8_t u = c; \ - return !!(EVUTIL_##name##_TABLE[(u >> 5) & 7] & (1 << (u & 31))); \ + return !!(EVUTIL_##name##_TABLE[(u >> 5) & 7] & (1U << (u & 31))); \ } IMPL_CTYPE_FN(ISALPHA) IMPL_CTYPE_FN(ISALNUM) diff --git a/evutil_time.c b/evutil_time.c index c3a2358960..c327218227 100644 --- a/evutil_time.c +++ b/evutil_time.c @@ -65,6 +65,9 @@ #ifndef EVENT__HAVE_GETTIMEOFDAY /* No gettimeofday; this must be windows. */ + +typedef void (WINAPI *GetSystemTimePreciseAsFileTime_fn_t) (LPFILETIME); + int evutil_gettimeofday(struct timeval *tv, struct timezone *tz) { @@ -90,7 +93,22 @@ evutil_gettimeofday(struct timeval *tv, struct timezone *tz) if (tv == NULL) return -1; - GetSystemTimeAsFileTime(&ft.ft_ft); + static GetSystemTimePreciseAsFileTime_fn_t GetSystemTimePreciseAsFileTime_fn = NULL; + static int check_precise = 1; + + if (EVUTIL_UNLIKELY(check_precise)) { + HMODULE h = evutil_load_windows_system_library_(TEXT("kernel32.dll")); + if (h != NULL) + GetSystemTimePreciseAsFileTime_fn = + (GetSystemTimePreciseAsFileTime_fn_t) + GetProcAddress(h, "GetSystemTimePreciseAsFileTime"); + check_precise = 0; + } + + if (GetSystemTimePreciseAsFileTime_fn != NULL) + GetSystemTimePreciseAsFileTime_fn(&ft.ft_ft); + else + GetSystemTimeAsFileTime(&ft.ft_ft); if (EVUTIL_UNLIKELY(ft.ft_64 < EPOCH_BIAS)) { /* Time before the unix epoch. */ @@ -126,8 +144,22 @@ evutil_usleep_(const struct timeval *tv) return; #if defined(_WIN32) { - long msec = evutil_tv_to_msec_(tv); - Sleep((DWORD)msec); + __int64 usec; + LARGE_INTEGER li; + HANDLE timer; + + usec = tv->tv_sec * 1000000LL + tv->tv_usec; + if (!usec) + return; + + li.QuadPart = -10LL * usec; + timer = CreateWaitableTimer(NULL, TRUE, NULL); + if (!timer) + return; + + SetWaitableTimer(timer, &li, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); } #elif defined(EVENT__HAVE_NANOSLEEP) { @@ -158,18 +190,28 @@ evutil_date_rfc1123(char *date, const size_t datelen, const struct tm *tm) time_t t = time(NULL); -#ifndef _WIN32 +#if defined(EVENT__HAVE__GMTIME64_S) || !defined(_WIN32) struct tm sys; #endif /* If `tm` is null, set system's current time. */ if (tm == NULL) { -#ifdef _WIN32 - /** TODO: detect _gmtime64()/_gmtime64_s() */ - tm = gmtime(&t); -#else +#if !defined(_WIN32) gmtime_r(&t, &sys); tm = &sys; + /** detect _gmtime64()/_gmtime64_s() */ +#elif defined(EVENT__HAVE__GMTIME64_S) + errno_t err; + err = _gmtime64_s(&sys, &t); + if (err) { + event_errx(1, "Invalid argument to _gmtime64_s"); + } else { + tm = &sys; + } +#elif defined(EVENT__HAVE__GMTIME64) + tm = _gmtime64(&t); +#else + tm = gmtime(&t); #endif } diff --git a/extra/abi-check/README.md b/extra/abi-check/README.md new file mode 100644 index 0000000000..77fdd94128 --- /dev/null +++ b/extra/abi-check/README.md @@ -0,0 +1,35 @@ +## libevent ABI/API changes + + +This script is used to generate information about changes in libevent ABI/API +between various versions using [LVC tools](https://github.com/lvc). Such an +overview can help developers migrate from one version to another. + +Here is the `abi_check.sh`, which is used to generate ABI/API timeline for +libevent. + +You can limit the number of included libevent versions via a number given +as a parameter to the script. For example + +```shell +$ ./abi_check.sh 3 +``` + +generates overview for the last 3 versions and the current version. +If no parameter given, it will generate overview for the last 2 versions and +the current version by default. + +But this script requires some tools that are available in the following docker image: + +``` +docker.pkg.github.com/azat/docker-images/lvc-debian +``` + +And the full command looks like: + +```shell + docker run --rm -it -v $PWD:/src:ro -w /src -v tmp/le-abi-check-root:/abi-root -e ABI_CHECK_ROOT=/abi-root docker.pkg.github.com/azat/docker-images/lvc-debian /src/extra/abi-check/abi_check.sh +``` + +'timeline/libevent/index.html' is the final result and can be viewed +[here](https://libevent.org/abi) diff --git a/extra/abi-check/abi_check.sh b/extra/abi-check/abi_check.sh new file mode 100755 index 0000000000..e860657e9b --- /dev/null +++ b/extra/abi-check/abi_check.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Requirements: +# - wdiff +# - rfcdiff +# - universal-ctags +# - abi-tracker +# - abi-monitor +# - git +# +# All of this are included in: +# docker.pkg.github.com/azat/docker-images/lvc-debian:latest +# +# TODO: +# - move image into libevent namespace + +# verify backward compatibility of API/ABI changes + +set -e + +LIMIT=${1:-2} +EVENT_SOURCE_DIR=${EVENT_SOURCE_DIR:-"$(cd "$(dirname "$0")"/../.. && pwd)"} +ABI_CHECK_ROOT=${ABI_CHECK_ROOT:-$EVENT_SOURCE_DIR/.abi-check} +ABI_CHECK_WORKSPACE=${ABI_CHECK_WORKSPACE:-"work/abi-check"} + +mkdir -p "$ABI_CHECK_ROOT/$ABI_CHECK_WORKSPACE" +cd "$ABI_CHECK_ROOT/$ABI_CHECK_WORKSPACE" + +# copy current source code and profile into workspace +mkdir -p src/libevent/current +mkdir -p installed/libevent/current +( # to avoid cd back + cd "$EVENT_SOURCE_DIR" + # XXX: not `git archive` since it will not copy changes that are not in index, + # and maybe some issues on CI (since it does not contain full clone) + find . -maxdepth 1 -mindepth 1 | { + git check-ignore --no-index --verbose --non-matching --stdin + } | fgrep :: | cut -f2 | grep -v /.git/ | tee /dev/stderr | { + xargs cp -r -t "$ABI_CHECK_ROOT/$ABI_CHECK_WORKSPACE/src/libevent/current/" + } + cp extra/abi-check/libevent.json "$ABI_CHECK_ROOT/$ABI_CHECK_WORKSPACE/" +) + +# run LVC tools +abi-monitor -get -limit "$LIMIT" libevent.json +# XXX: abi-monitor 1.12 supports "-make -j8", but 1.10 does not +# (we can detect which version we have, and add this options) +abi-monitor -v current -build libevent.json +abi-monitor -build libevent.json +abi-tracker -build libevent.json + +# remove useless files +rm -rf src installed build_logs libevent.json diff --git a/extra/abi-check/libevent.json b/extra/abi-check/libevent.json new file mode 100644 index 0000000000..42930e933c --- /dev/null +++ b/extra/abi-check/libevent.json @@ -0,0 +1,12 @@ +{ + "Name": "libevent", + "Title": "Libevent", + "SourceUrl": "https://github.com/libevent/libevent/tags", + "Git": "https://github.com/libevent/libevent.git", + "Maintainer": "Nick Mathewson, Azat Khuzhin and Niels Provos", + "MaintainerUrl": "https://libevent.org", + "BuildSystem": "Autotools", + "HeadersDiff": "On", + "Package": "release-", + "ReleasePattern": "\A([\d\-\.\_]+)(|\-beta|\-rc|\-stable)\Z", +} diff --git a/extra/lsan.supp b/extra/lsan.supp new file mode 100644 index 0000000000..c1b130bf7e --- /dev/null +++ b/extra/lsan.supp @@ -0,0 +1,3 @@ +# TODO: temporary, until tests itself will be fixed +leak:libcrypto.so +leak:libssl.so diff --git a/extra/tsan.supp b/extra/tsan.supp new file mode 100644 index 0000000000..414a12c16f --- /dev/null +++ b/extra/tsan.supp @@ -0,0 +1,2 @@ +# https://github.com/libevent/libevent/issues/777 +race:event_debug_mode_too_late diff --git a/http.c b/http.c index 5331602a7a..04f089bc01 100644 --- a/http.c +++ b/http.c @@ -177,7 +177,7 @@ fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host, extern int debug; -static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse); +static evutil_socket_t create_bind_socket_nonblock(struct evutil_addrinfo *, int reuse); static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse); static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **); static struct evhttp_uri *evhttp_uri_parse_authority(char *source_uri); @@ -358,6 +358,7 @@ evhttp_response_needs_body(struct evhttp_request *req) return (req->response_code != HTTP_NOCONTENT && req->response_code != HTTP_NOTMODIFIED && (req->response_code < 100 || req->response_code >= 200) && + req->type != EVHTTP_REQ_CONNECT && req->type != EVHTTP_REQ_HEAD); } @@ -478,6 +479,9 @@ evhttp_is_connection_close(int flags, struct evkeyvalq* headers) static int evhttp_is_request_connection_close(struct evhttp_request *req) { + if (req->type == EVHTTP_REQ_CONNECT) + return 0; + return evhttp_is_connection_close(req->flags, req->input_headers) || evhttp_is_connection_close(req->flags, req->output_headers); @@ -778,6 +782,11 @@ evhttp_connection_fail_(struct evhttp_connection *evcon, /* We are trying the next request that was queued on us */ if (TAILQ_FIRST(&evcon->requests) != NULL) evhttp_connection_connect_(evcon); + else + if ((evcon->flags & EVHTTP_CON_OUTGOING) && + (evcon->flags & EVHTTP_CON_AUTOFREE)) { + evhttp_connection_free(evcon); + } /* The call to evhttp_connection_reset_ overwrote errno. * Let's restore the original errno, so that the user's @@ -3277,6 +3286,7 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers, p = argument = line; while (p != NULL && *p != '\0') { char *key, *value, *decoded_value; + int err; argument = strsep(&p, "&"); value = argument; @@ -3292,8 +3302,10 @@ evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers, evhttp_decode_uri_internal(value, strlen(value), decoded_value, 1 /*always_decode_plus*/); event_debug(("Query Param: %s -> %s\n", key, decoded_value)); - evhttp_add_header_internal(headers, key, decoded_value); + err = evhttp_add_header_internal(headers, key, decoded_value); mm_free(decoded_value); + if (err) + goto error; } result = 0; @@ -4376,9 +4388,8 @@ name_from_addr(struct sockaddr *sa, ev_socklen_t salen, } /* Create a non-blocking socket and bind it */ -/* todo: rename this function */ static evutil_socket_t -bind_socket_ai(struct evutil_addrinfo *ai, int reuse) +create_bind_socket_nonblock(struct evutil_addrinfo *ai, int reuse) { evutil_socket_t fd; @@ -4452,14 +4463,14 @@ bind_socket(const char *address, ev_uint16_t port, int reuse) /* just create an unbound socket */ if (address == NULL && port == 0) - return bind_socket_ai(NULL, 0); + return create_bind_socket_nonblock(NULL, 0); aitop = make_addrinfo(address, port); if (aitop == NULL) return (-1); - fd = bind_socket_ai(aitop, reuse); + fd = create_bind_socket_nonblock(aitop, reuse); evutil_freeaddrinfo(aitop); diff --git a/include/event2/buffer.h b/include/event2/buffer.h index 468588b9f1..88af3ae141 100644 --- a/include/event2/buffer.h +++ b/include/event2/buffer.h @@ -726,7 +726,8 @@ int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, @param buffer the evbuffer to store the result @param fd the file descriptor to read from - @param howmuch the number of bytes to be read + @param howmuch the number of bytes to be read. If the given number is negative + or out of maximum bytes per one read, as many bytes as we can will be read. @return the number of bytes read, or -1 if an error occurred @see evbuffer_write() */ diff --git a/include/event2/util.h b/include/event2/util.h index e6df62899f..02aa7ba9da 100644 --- a/include/event2/util.h +++ b/include/event2/util.h @@ -612,6 +612,12 @@ int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) /** Replacement for inet_ntop for platforms which lack it. */ EVENT2_EXPORT_SYMBOL const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); +/** Variation of inet_pton that also parses IPv6 scopes. Public for + unit tests. No reason to call this directly. + */ +EVENT2_EXPORT_SYMBOL +int evutil_inet_pton_scope(int af, const char *src, void *dst, + unsigned *indexp); /** Replacement for inet_pton for platforms which lack it. */ EVENT2_EXPORT_SYMBOL int evutil_inet_pton(int af, const char *src, void *dst); diff --git a/kqueue.c b/kqueue.c index d08f512ce4..dfd7751d64 100644 --- a/kqueue.c +++ b/kqueue.c @@ -51,7 +51,10 @@ /* Some platforms apparently define the udata field of struct kevent as * intptr_t, whereas others define it as void*. There doesn't seem to be an * easy way to tell them apart via autoconf, so we need to use OS macros. */ -#if defined(EVENT__HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) && !defined(__CloudABI__) +#if defined(__NetBSD__) +#define PTR_TO_UDATA(x) ((typeof(((struct kevent *)0)->udata))(x)) +#define INT_TO_UDATA(x) ((typeof(((struct kevent *)0)->udata))(intptr_t)(x)) +#elif defined(EVENT__HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) && !defined(__CloudABI__) #define PTR_TO_UDATA(x) ((intptr_t)(x)) #define INT_TO_UDATA(x) ((intptr_t)(x)) #else diff --git a/m4/ax_check_funcs_ex.m4 b/m4/ax_check_funcs_ex.m4 new file mode 100644 index 0000000000..7aaa58b054 --- /dev/null +++ b/m4/ax_check_funcs_ex.m4 @@ -0,0 +1,22 @@ +# Check if the function is available. +# HAVE_XXX will be defined if yes. + +# $1: the name of function +# $2: the headers in where the function declared +AC_DEFUN([AX_CHECK_DECL_EX], [dnl + AS_IF([test "x$2" = "x"], [AC_MSG_ERROR([header not privided])]) + AS_VAR_PUSHDEF([have_func_var], [HAVE_[]m4_toupper($1)]) + AC_CHECK_DECL([$1],dnl + [AC_DEFINE([have_func_var], [1], [Define to 1 if you have the `$1' function.])],,dnl + [$2]dnl + ) + AS_VAR_POPDEF([have_func_var])dnl +]) + +AC_DEFUN([AX_CHECK_DECLS_EX], [dnl + AS_IF([test "x$2" = "x"], [AC_MSG_ERROR([header not privided])]) + m4_foreach([decl],dnl + m4_split(m4_normalize($1)),dnl + [AX_CHECK_DECL_EX([decl], [$2])]dnl + ) +]) diff --git a/m4/ax_prog_doxygen.m4 b/m4/ax_prog_doxygen.m4 new file mode 100644 index 0000000000..e5bdeb5992 --- /dev/null +++ b/m4/ax_prog_doxygen.m4 @@ -0,0 +1,600 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html +# =========================================================================== +# +# SYNOPSIS +# +# DX_INIT_DOXYGEN(PROJECT-NAME, [DOXYFILE-PATH], [OUTPUT-DIR], ...) +# DX_DOXYGEN_FEATURE(ON|OFF) +# DX_DOT_FEATURE(ON|OFF) +# DX_HTML_FEATURE(ON|OFF) +# DX_CHM_FEATURE(ON|OFF) +# DX_CHI_FEATURE(ON|OFF) +# DX_MAN_FEATURE(ON|OFF) +# DX_RTF_FEATURE(ON|OFF) +# DX_XML_FEATURE(ON|OFF) +# DX_PDF_FEATURE(ON|OFF) +# DX_PS_FEATURE(ON|OFF) +# +# DESCRIPTION +# +# The DX_*_FEATURE macros control the default setting for the given +# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for +# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML +# help (for MS users), 'CHI' for generating a separate .chi file by the +# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate +# output formats. The environment variable DOXYGEN_PAPER_SIZE may be +# specified to override the default 'a4wide' paper size. +# +# By default, HTML, PDF and PS documentation is generated as this seems to +# be the most popular and portable combination. MAN pages created by +# Doxygen are usually problematic, though by picking an appropriate subset +# and doing some massaging they might be better than nothing. CHM and RTF +# are specific for MS (note that you can't generate both HTML and CHM at +# the same time). The XML is rather useless unless you apply specialized +# post-processing to it. +# +# The macros mainly control the default state of the feature. The use can +# override the default by specifying --enable or --disable. The macros +# ensure that contradictory flags are not given (e.g., +# --enable-doxygen-html and --enable-doxygen-chm, +# --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each +# feature will be automatically disabled (with a warning) if the required +# programs are missing. +# +# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN +# with the following parameters: a one-word name for the project for use +# as a filename base etc., an optional configuration file name (the +# default is '$(srcdir)/Doxyfile', the same as Doxygen's default), and an +# optional output directory name (the default is 'doxygen-doc'). To run +# doxygen multiple times for different configuration files and output +# directories provide more parameters: the second, forth, sixth, etc +# parameter are configuration file names and the third, fifth, seventh, +# etc parameter are output directories. No checking is done to catch +# duplicates. +# +# Automake Support +# +# The DX_RULES substitution can be used to add all needed rules to the +# Makefile. Note that this is a substitution without being a variable: +# only the @DX_RULES@ syntax will work. +# +# The provided targets are: +# +# doxygen-doc: Generate all doxygen documentation. +# +# doxygen-run: Run doxygen, which will generate some of the +# documentation (HTML, CHM, CHI, MAN, RTF, XML) +# but will not do the post processing required +# for the rest of it (PS, PDF). +# +# doxygen-ps: Generate doxygen PostScript documentation. +# +# doxygen-pdf: Generate doxygen PDF documentation. +# +# Note that by default these are not integrated into the automake targets. +# If doxygen is used to generate man pages, you can achieve this +# integration by setting man3_MANS to the list of man pages generated and +# then adding the dependency: +# +# $(man3_MANS): doxygen-doc +# +# This will cause make to run doxygen and generate all the documentation. +# +# The following variable is intended for use in Makefile.am: +# +# DX_CLEANFILES = everything to clean. +# +# Then add this variable to MOSTLYCLEANFILES. +# +# LICENSE +# +# Copyright (c) 2009 Oren Ben-Kiki +# Copyright (c) 2015 Olaf Mandel +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 24 + +## ----------## +## Defaults. ## +## ----------## + +DX_ENV="" +AC_DEFUN([DX_FEATURE_doc], OFF) +AC_DEFUN([DX_FEATURE_dot], OFF) +AC_DEFUN([DX_FEATURE_man], OFF) +AC_DEFUN([DX_FEATURE_html], ON) +AC_DEFUN([DX_FEATURE_chm], OFF) +AC_DEFUN([DX_FEATURE_chi], OFF) +AC_DEFUN([DX_FEATURE_rtf], OFF) +AC_DEFUN([DX_FEATURE_xml], OFF) +AC_DEFUN([DX_FEATURE_pdf], ON) +AC_DEFUN([DX_FEATURE_ps], ON) + +## --------------- ## +## Private macros. ## +## --------------- ## + +# DX_ENV_APPEND(VARIABLE, VALUE) +# ------------------------------ +# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen and add it +# as a substitution (but not a Makefile variable). The substitution +# is skipped if the variable name is VERSION. +AC_DEFUN([DX_ENV_APPEND], +[AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])dnl +m4_if([$1], [VERSION], [], [AC_SUBST([$1], [$2])dnl +AM_SUBST_NOTMAKE([$1])])dnl +]) + +# DX_DIRNAME_EXPR +# --------------- +# Expand into a shell expression prints the directory part of a path. +AC_DEFUN([DX_DIRNAME_EXPR], + [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) + +# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) +# ------------------------------------- +# Expands according to the M4 (static) status of the feature. +AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) + +# DX_REQUIRE_PROG(VARIABLE, PROGRAM) +# ---------------------------------- +# Require the specified program to be found for the DX_CURRENT_FEATURE to work. +AC_DEFUN([DX_REQUIRE_PROG], [ +AC_PATH_TOOL([$1], [$2]) +if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then + if test "x$2" = "xdoxygen"; then + AC_MSG_ERROR([$2 not found - will not DX_CURRENT_DESCRIPTION]) + else + AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) + fi + AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) +fi +]) + +# DX_TEST_FEATURE(FEATURE) +# ------------------------ +# Expand to a shell expression testing whether the feature is active. +AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) + +# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) +# ------------------------------------------------- +# Verify that a required features has the right state before trying to turn on +# the DX_CURRENT_FEATURE. +AC_DEFUN([DX_CHECK_DEPEND], [ +test "$DX_FLAG_$1" = "$2" \ +|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, + requires, contradicts) doxygen-$1]) +]) + +# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) +# ---------------------------------------------------------- +# Turn off the DX_CURRENT_FEATURE if the required feature is off. +AC_DEFUN([DX_CLEAR_DEPEND], [ +test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) +]) + +# DX_FEATURE_ARG(FEATURE, DESCRIPTION, +# CHECK_DEPEND, CLEAR_DEPEND, +# REQUIRE, DO-IF-ON, DO-IF-OFF) +# -------------------------------------------- +# Parse the command-line option controlling a feature. CHECK_DEPEND is called +# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), +# otherwise CLEAR_DEPEND is called to turn off the default state if a required +# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional +# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and +# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. +AC_DEFUN([DX_ARG_ABLE], [ + AC_DEFUN([DX_CURRENT_FEATURE], [$1]) + AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) + AC_ARG_ENABLE(doxygen-$1, + [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], + [--enable-doxygen-$1]), + DX_IF_FEATURE([$1], [don't $2], [$2]))], + [ +case "$enableval" in +#( +y|Y|yes|Yes|YES) + AC_SUBST([DX_FLAG_$1], 1) + $3 +;; #( +n|N|no|No|NO) + AC_SUBST([DX_FLAG_$1], 0) +;; #( +*) + AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) +;; +esac +], [ +AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) +$4 +]) +if DX_TEST_FEATURE([$1]); then + $5 + : +fi +if DX_TEST_FEATURE([$1]); then + $6 + : +else + $7 + : +fi +]) + +## -------------- ## +## Public macros. ## +## -------------- ## + +# DX_XXX_FEATURE(DEFAULT_STATE) +# ----------------------------- +AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) +AC_DEFUN([DX_DOT_FEATURE], [AC_DEFUN([DX_FEATURE_dot], [$1])]) +AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) +AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) +AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) +AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) +AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) +AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) + +# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR], ...) +# -------------------------------------------------------------- +# PROJECT also serves as the base name for the documentation files. +# The default CONFIG-FILE is "$(srcdir)/Doxyfile" and OUTPUT-DOC-DIR is +# "doxygen-doc". +# More arguments are interpreted as interleaved CONFIG-FILE and +# OUTPUT-DOC-DIR values. +AC_DEFUN([DX_INIT_DOXYGEN], [ + +# Files: +AC_SUBST([DX_PROJECT], [$1]) +AC_SUBST([DX_CONFIG], ['ifelse([$2], [], [$(srcdir)/Doxyfile], [$2])']) +AC_SUBST([DX_DOCDIR], ['ifelse([$3], [], [doxygen-doc], [$3])']) +m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2, + [AC_SUBST([DX_CONFIG]m4_eval(DX_i[/2]), + 'm4_default_nblank_quoted(m4_argn(DX_i, $@), + [$(srcdir)/Doxyfile])')])])dnl +m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 5, m4_count($@,), 2, + [AC_SUBST([DX_DOCDIR]m4_eval([(]DX_i[-1)/2]), + 'm4_default_nblank_quoted(m4_argn(DX_i, $@), + [doxygen-doc])')])])dnl +m4_define([DX_loop], m4_dquote(m4_if(m4_eval(3 < m4_count($@)), 1, + [m4_for([DX_i], 4, m4_count($@), 2, [, m4_eval(DX_i[/2])])], + [])))dnl + +# Environment variables used inside doxygen.cfg: +DX_ENV_APPEND(SRCDIR, $srcdir) +DX_ENV_APPEND(PROJECT, $DX_PROJECT) +DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) + +# Doxygen itself: +DX_ARG_ABLE(doc, [generate any doxygen documentation], + [], + [], + [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) + DX_REQUIRE_PROG([DX_PERL], perl)], + [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) + +# Dot for graphics: +DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_DOT], dot)], + [DX_ENV_APPEND(HAVE_DOT, YES) + DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], + [DX_ENV_APPEND(HAVE_DOT, NO)]) + +# Man pages generation: +DX_ARG_ABLE(man, [generate doxygen manual pages], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_MAN, YES)], + [DX_ENV_APPEND(GENERATE_MAN, NO)]) + +# RTF file generation: +DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_RTF, YES)], + [DX_ENV_APPEND(GENERATE_RTF, NO)]) + +# XML file generation: +DX_ARG_ABLE(xml, [generate doxygen XML documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_XML, YES)], + [DX_ENV_APPEND(GENERATE_XML, NO)]) + +# (Compressed) HTML help generation: +DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_HHC], hhc)], + [DX_ENV_APPEND(HHC_PATH, $DX_HHC) + DX_ENV_APPEND(GENERATE_HTML, YES) + DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], + [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) + +# Separate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file], + [DX_CHECK_DEPEND(chm, 1)], + [DX_CLEAR_DEPEND(chm, 1)], + [], + [DX_ENV_APPEND(GENERATE_CHI, YES)], + [DX_ENV_APPEND(GENERATE_CHI, NO)]) + +# Plain HTML pages generation: +DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], + [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], + [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], + [], + [DX_ENV_APPEND(GENERATE_HTML, YES)], + [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) + +# PostScript file generation: +DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_LATEX], latex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_DVIPS], dvips) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# PDF file generation: +DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# LaTeX generation for PS and/or PDF: +if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then + DX_ENV_APPEND(GENERATE_LATEX, YES) +else + DX_ENV_APPEND(GENERATE_LATEX, NO) +fi + +# Paper size for PS and/or PDF: +AC_ARG_VAR(DOXYGEN_PAPER_SIZE, + [a4wide (default), a4, letter, legal or executive]) +case "$DOXYGEN_PAPER_SIZE" in +#( +"") + AC_SUBST(DOXYGEN_PAPER_SIZE, "") +;; #( +a4wide|a4|letter|legal|executive) + DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) +;; #( +*) + AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) +;; +esac + +# Rules: +AS_IF([[test $DX_FLAG_html -eq 1]], +[[DX_SNIPPET_html="## ------------------------------- ## +## Rules specific for HTML output. ## +## ------------------------------- ## + +DX_CLEAN_HTML = \$(DX_DOCDIR)/html]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/html]])[ + +"]], +[[DX_SNIPPET_html=""]]) +AS_IF([[test $DX_FLAG_chi -eq 1]], +[[DX_SNIPPET_chi=" +DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])["]], +[[DX_SNIPPET_chi=""]]) +AS_IF([[test $DX_FLAG_chm -eq 1]], +[[DX_SNIPPET_chm="## ------------------------------ ## +## Rules specific for CHM output. ## +## ------------------------------ ## + +DX_CLEAN_CHM = \$(DX_DOCDIR)/chm]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/chm]])[\ +${DX_SNIPPET_chi} + +"]], +[[DX_SNIPPET_chm=""]]) +AS_IF([[test $DX_FLAG_man -eq 1]], +[[DX_SNIPPET_man="## ------------------------------ ## +## Rules specific for MAN output. ## +## ------------------------------ ## + +DX_CLEAN_MAN = \$(DX_DOCDIR)/man]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/man]])[ + +"]], +[[DX_SNIPPET_man=""]]) +AS_IF([[test $DX_FLAG_rtf -eq 1]], +[[DX_SNIPPET_rtf="## ------------------------------ ## +## Rules specific for RTF output. ## +## ------------------------------ ## + +DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/rtf]])[ + +"]], +[[DX_SNIPPET_rtf=""]]) +AS_IF([[test $DX_FLAG_xml -eq 1]], +[[DX_SNIPPET_xml="## ------------------------------ ## +## Rules specific for XML output. ## +## ------------------------------ ## + +DX_CLEAN_XML = \$(DX_DOCDIR)/xml]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/xml]])[ + +"]], +[[DX_SNIPPET_xml=""]]) +AS_IF([[test $DX_FLAG_ps -eq 1]], +[[DX_SNIPPET_ps="## ----------------------------- ## +## Rules specific for PS output. ## +## ----------------------------- ## + +DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])[ + +DX_PS_GOAL = doxygen-ps + +doxygen-ps: \$(DX_CLEAN_PS) + +]m4_foreach([DX_i], [DX_loop], +[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag + \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ + \$(DX_LATEX) refman.tex; \\ + \$(DX_MAKEINDEX) refman.idx; \\ + \$(DX_LATEX) refman.tex; \\ + countdown=5; \\ + while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ + refman.log > /dev/null 2>&1 \\ + && test \$\$countdown -gt 0; do \\ + \$(DX_LATEX) refman.tex; \\ + countdown=\`expr \$\$countdown - 1\`; \\ + done; \\ + \$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi + +]])["]], +[[DX_SNIPPET_ps=""]]) +AS_IF([[test $DX_FLAG_pdf -eq 1]], +[[DX_SNIPPET_pdf="## ------------------------------ ## +## Rules specific for PDF output. ## +## ------------------------------ ## + +DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])[ + +DX_PDF_GOAL = doxygen-pdf + +doxygen-pdf: \$(DX_CLEAN_PDF) + +]m4_foreach([DX_i], [DX_loop], +[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag + \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ + \$(DX_PDFLATEX) refman.tex; \\ + \$(DX_MAKEINDEX) refman.idx; \\ + \$(DX_PDFLATEX) refman.tex; \\ + countdown=5; \\ + while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ + refman.log > /dev/null 2>&1 \\ + && test \$\$countdown -gt 0; do \\ + \$(DX_PDFLATEX) refman.tex; \\ + countdown=\`expr \$\$countdown - 1\`; \\ + done; \\ + mv refman.pdf ../\$(PACKAGE).pdf + +]])["]], +[[DX_SNIPPET_pdf=""]]) +AS_IF([[test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1]], +[[DX_SNIPPET_latex="## ------------------------------------------------- ## +## Rules specific for LaTeX (shared for PS and PDF). ## +## ------------------------------------------------- ## + +DX_V_LATEX = \$(_DX_v_LATEX_\$(V)) +_DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY)) +_DX_v_LATEX_0 = @echo \" LATEX \" \$][@; + +DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/latex]])[ + +"]], +[[DX_SNIPPET_latex=""]]) + +AS_IF([[test $DX_FLAG_doc -eq 1]], +[[DX_SNIPPET_doc="## --------------------------------- ## +## Format-independent Doxygen rules. ## +## --------------------------------- ## + +${DX_SNIPPET_html}\ +${DX_SNIPPET_chm}\ +${DX_SNIPPET_man}\ +${DX_SNIPPET_rtf}\ +${DX_SNIPPET_xml}\ +${DX_SNIPPET_ps}\ +${DX_SNIPPET_pdf}\ +${DX_SNIPPET_latex}\ +DX_V_DXGEN = \$(_DX_v_DXGEN_\$(V)) +_DX_v_DXGEN_ = \$(_DX_v_DXGEN_\$(AM_DEFAULT_VERBOSITY)) +_DX_v_DXGEN_0 = @echo \" DXGEN \" \$<; + +.PHONY: doxygen-run doxygen-doc \$(DX_PS_GOAL) \$(DX_PDF_GOAL) + +.INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) + +doxygen-run:]m4_foreach([DX_i], [DX_loop], + [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])[ + +doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) + +]m4_foreach([DX_i], [DX_loop], +[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag: \$(DX_CONFIG]DX_i[) \$(pkginclude_HEADERS) + \$(A""M_V_at)rm -rf \$(DX_DOCDIR]DX_i[) + \$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR]DX_i[) \$(DX_DOXYGEN) \$(DX_CONFIG]DX_i[) + \$(A""M_V_at)echo Timestamp >\$][@ + +]])dnl +[DX_CLEANFILES = \\] +m4_foreach([DX_i], [DX_loop], +[[ \$(DX_DOCDIR]DX_i[)/doxygen_sqlite3.db \\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\ +]])dnl +[ -r \\ + \$(DX_CLEAN_HTML) \\ + \$(DX_CLEAN_CHM) \\ + \$(DX_CLEAN_CHI) \\ + \$(DX_CLEAN_MAN) \\ + \$(DX_CLEAN_RTF) \\ + \$(DX_CLEAN_XML) \\ + \$(DX_CLEAN_PS) \\ + \$(DX_CLEAN_PDF) \\ + \$(DX_CLEAN_LATEX) +DX_INSTALL_DOCS = \\ + \$(DX_CLEAN_HTML) \\ + \$(DX_CLEAN_CHM) \\ + \$(DX_CLEAN_CHI) \\ + \$(DX_CLEAN_RTF) \\ + \$(DX_CLEAN_XML) \\ + \$(DX_CLEAN_PS) \\ + \$(DX_CLEAN_PDF) \\ + \$(DX_CLEAN_LATEX) + "]], +[[DX_SNIPPET_doc=""]]) +AC_SUBST([DX_RULES], +["${DX_SNIPPET_doc}"])dnl +AM_SUBST_NOTMAKE([DX_RULES]) + +#For debugging: +#echo DX_FLAG_doc=$DX_FLAG_doc +#echo DX_FLAG_dot=$DX_FLAG_dot +#echo DX_FLAG_man=$DX_FLAG_man +#echo DX_FLAG_html=$DX_FLAG_html +#echo DX_FLAG_chm=$DX_FLAG_chm +#echo DX_FLAG_chi=$DX_FLAG_chi +#echo DX_FLAG_rtf=$DX_FLAG_rtf +#echo DX_FLAG_xml=$DX_FLAG_xml +#echo DX_FLAG_pdf=$DX_FLAG_pdf +#echo DX_FLAG_ps=$DX_FLAG_ps +#echo DX_ENV=$DX_ENV +]) diff --git a/m4/libevent_openssl.m4 b/m4/libevent_openssl.m4 index 19811981e9..a5ea676200 100644 --- a/m4/libevent_openssl.m4 +++ b/m4/libevent_openssl.m4 @@ -47,6 +47,11 @@ case "$enable_openssl" in AC_SUBST(OPENSSL_LIBS) case "$have_openssl" in yes) AC_DEFINE(HAVE_OPENSSL, 1, [Define if the system has openssl]) ;; + *) AC_MSG_ERROR([openssl is a must but can not be found. You should add the \ +directory containing `openssl.pc' to the `PKG_CONFIG_PATH' environment variable, \ +or set `CFLAGS' and `LDFLAGS' directly for openssl, or use `--disable-openssl' \ +to disable support for openssl encryption]) + ;; esac ;; esac diff --git a/openssl-compat.h b/openssl-compat.h index 5d91ac6402..a23e34251b 100644 --- a/openssl-compat.h +++ b/openssl-compat.h @@ -34,6 +34,9 @@ static inline BIO_METHOD *BIO_meth_new(int type, const char *name) #define TLS_method SSLv23_method +#define X509_getm_notBefore X509_get_notBefore +#define X509_getm_notAfter X509_get_notAfter + #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */ diff --git a/poll.c b/poll.c index fe44071177..c3c9aac52a 100644 --- a/poll.c +++ b/poll.c @@ -53,6 +53,17 @@ #include "evthread-internal.h" #include "time-internal.h" +/* Since Linux 2.6.17, poll is able to report about peer half-closed connection + using special POLLRDHUP flag on a read event. +*/ +#if !defined(POLLRDHUP) +#define POLLRDHUP 0 +#define EARLY_CLOSE_IF_HAVE_RDHUP 0 +#else +#define EARLY_CLOSE_IF_HAVE_RDHUP EV_FEATURE_EARLY_CLOSE +#endif + + struct pollidx { int idxplus1; }; @@ -79,8 +90,8 @@ const struct eventop pollops = { poll_del, poll_dispatch, poll_dealloc, - 0, /* doesn't need_reinit */ - EV_FEATURE_FDS, + 1, /* need_reinit */ + EV_FEATURE_FDS|EARLY_CLOSE_IF_HAVE_RDHUP, sizeof(struct pollidx), }; @@ -204,6 +215,8 @@ poll_dispatch(struct event_base *base, struct timeval *tv) res |= EV_READ; if (what & POLLOUT) res |= EV_WRITE; + if (what & POLLRDHUP) + res |= EV_CLOSED; if (res == 0) continue; @@ -222,7 +235,7 @@ poll_add(struct event_base *base, int fd, short old, short events, void *idx_) int i; EVUTIL_ASSERT((events & EV_SIGNAL) == 0); - if (!(events & (EV_READ|EV_WRITE))) + if (!(events & (EV_READ|EV_WRITE|EV_CLOSED))) return (0); poll_check_ok(pop); @@ -265,6 +278,8 @@ poll_add(struct event_base *base, int fd, short old, short events, void *idx_) pfd->events |= POLLOUT; if (events & EV_READ) pfd->events |= POLLIN; + if (events & EV_CLOSED) + pfd->events |= POLLRDHUP; poll_check_ok(pop); return (0); @@ -283,7 +298,7 @@ poll_del(struct event_base *base, int fd, short old, short events, void *idx_) int i; EVUTIL_ASSERT((events & EV_SIGNAL) == 0); - if (!(events & (EV_READ|EV_WRITE))) + if (!(events & (EV_READ|EV_WRITE|EV_CLOSED))) return (0); poll_check_ok(pop); @@ -297,6 +312,8 @@ poll_del(struct event_base *base, int fd, short old, short events, void *idx_) pfd->events &= ~POLLIN; if (events & EV_WRITE) pfd->events &= ~POLLOUT; + if (events & EV_CLOSED) + pfd->events &= ~POLLRDHUP; poll_check_ok(pop); if (pfd->events) /* Another event cares about that fd. */ diff --git a/sample/dns-example.c b/sample/dns-example.c index 21a75de863..2d07c3874b 100644 --- a/sample/dns-example.c +++ b/sample/dns-example.c @@ -225,8 +225,8 @@ main(int c, char **v) { res = evdns_base_resolv_conf_parse(evdns_base, DNS_OPTION_NAMESERVERS, o.resolv_conf); - if (res < 0) { - fprintf(stderr, "Couldn't configure nameservers"); + if (res) { + fprintf(stderr, "Couldn't configure nameservers\n"); return 1; } } diff --git a/sample/event-read-fifo.c b/sample/event-read-fifo.c index 27b0b530d5..a17b9bd9ae 100644 --- a/sample/event-read-fifo.c +++ b/sample/event-read-fifo.c @@ -129,10 +129,10 @@ main(int argc, char **argv) fprintf(stderr, "Write data to %s\n", fifo); #endif - /* Initalize the event library */ + /* Initialize the event library */ base = event_base_new(); - /* Initalize one event */ + /* Initialize one event */ #ifdef _WIN32 evfifo = event_new(base, (evutil_socket_t)socket, EV_READ|EV_PERSIST, fifo_read, event_self_cbarg()); diff --git a/sample/hello-world.c b/sample/hello-world.c index 2023cd6c6a..a13e06af61 100644 --- a/sample/hello-world.c +++ b/sample/hello-world.c @@ -42,7 +42,7 @@ main(int argc, char **argv) struct evconnlistener *listener; struct event *signal_event; - struct sockaddr_in sin; + struct sockaddr_in sin = {0}; #ifdef _WIN32 WSADATA wsa_data; WSAStartup(0x0201, &wsa_data); @@ -54,7 +54,6 @@ main(int argc, char **argv) return 1; } - memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(PORT); diff --git a/sample/http-connect.c b/sample/http-connect.c index af2c86a8db..53f816d3ae 100644 --- a/sample/http-connect.c +++ b/sample/http-connect.c @@ -23,15 +23,44 @@ struct connect_base struct evhttp_uri *location; }; +static struct evhttp_uri* uri_parse(const char *str) +{ + struct evhttp_uri *uri; + VERIFY(uri = evhttp_uri_parse(str)); + VERIFY(evhttp_uri_get_host(uri)); + VERIFY(evhttp_uri_get_port(uri) > 0); + return uri; +} +static char* uri_path(struct evhttp_uri *uri, char buffer[URL_MAX]) +{ + struct evhttp_uri *path; + + VERIFY(evhttp_uri_join(uri, buffer, URL_MAX)); + + path = evhttp_uri_parse(buffer); + evhttp_uri_set_scheme(path, NULL); + evhttp_uri_set_userinfo(path, 0); + evhttp_uri_set_host(path, NULL); + evhttp_uri_set_port(path, -1); + VERIFY(evhttp_uri_join(path, buffer, URL_MAX)); + return buffer; +} +static char* uri_hostport(struct evhttp_uri *uri, char buffer[URL_MAX]) +{ + VERIFY(evhttp_uri_join(uri, buffer, URL_MAX)); + VERIFY(evhttp_uri_get_host(uri)); + VERIFY(evhttp_uri_get_port(uri) > 0); + evutil_snprintf(buffer, URL_MAX, "%s:%d", + evhttp_uri_get_host(uri), evhttp_uri_get_port(uri)); + return buffer; +} + static void get_cb(struct evhttp_request *req, void *arg) { ev_ssize_t len; struct evbuffer *evbuf; - struct evhttp_connection *evcon; VERIFY(req); - evcon = evhttp_request_get_connection(req); - VERIFY(evcon); evbuf = evhttp_request_get_input_buffer(req); len = evbuffer_get_length(evbuf); @@ -41,26 +70,26 @@ static void get_cb(struct evhttp_request *req, void *arg) static void connect_cb(struct evhttp_request *proxy_req, void *arg) { - char buffer[URL_MAX]; - struct connect_base *base = arg; struct evhttp_connection *evcon = base->evcon; struct evhttp_uri *location = base->location; + struct evhttp_request *req; + char buffer[URL_MAX]; VERIFY(proxy_req); - if (evcon) { - struct evhttp_request *req = evhttp_request_new(get_cb, NULL); - evhttp_add_header(req->output_headers, "Connection", "close"); - VERIFY(!evhttp_make_request(evcon, req, EVHTTP_REQ_GET, - evhttp_uri_join(location, buffer, URL_MAX))); - } + VERIFY(evcon); + + req = evhttp_request_new(get_cb, NULL); + evhttp_add_header(req->output_headers, "Connection", "close"); + evhttp_add_header(req->output_headers, "Host", evhttp_uri_get_host(location)); + VERIFY(!evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + uri_path(location, buffer))); } int main(int argc, const char **argv) { - char buffer[URL_MAX]; + char hostport[URL_MAX]; - struct evhttp_uri *host_port; struct evhttp_uri *location; struct evhttp_uri *proxy; @@ -75,28 +104,8 @@ int main(int argc, const char **argv) return 1; } - { - VERIFY(proxy = evhttp_uri_parse(argv[1])); - VERIFY(evhttp_uri_get_host(proxy)); - VERIFY(evhttp_uri_get_port(proxy) > 0); - } - { - host_port = evhttp_uri_parse(argv[2]); - evhttp_uri_set_scheme(host_port, NULL); - evhttp_uri_set_userinfo(host_port, NULL); - evhttp_uri_set_path(host_port, NULL); - evhttp_uri_set_query(host_port, NULL); - evhttp_uri_set_fragment(host_port, NULL); - VERIFY(evhttp_uri_get_host(host_port)); - VERIFY(evhttp_uri_get_port(host_port) > 0); - } - { - location = evhttp_uri_parse(argv[2]); - evhttp_uri_set_scheme(location, NULL); - evhttp_uri_set_userinfo(location, 0); - evhttp_uri_set_host(location, NULL); - evhttp_uri_set_port(location, -1); - } + proxy = uri_parse(argv[1]); + location = uri_parse(argv[2]); VERIFY(base = event_base_new()); VERIFY(evcon = evhttp_connection_base_new(base, NULL, @@ -105,17 +114,18 @@ int main(int argc, const char **argv) connect_base.location = location; VERIFY(req = evhttp_request_new(connect_cb, &connect_base)); + uri_hostport(location, hostport); evhttp_add_header(req->output_headers, "Connection", "keep-alive"); evhttp_add_header(req->output_headers, "Proxy-Connection", "keep-alive"); - evutil_snprintf(buffer, URL_MAX, "%s:%d", - evhttp_uri_get_host(host_port), evhttp_uri_get_port(host_port)); - evhttp_make_request(evcon, req, EVHTTP_REQ_CONNECT, buffer); + evhttp_add_header(req->output_headers, "Host", hostport); + evhttp_make_request(evcon, req, EVHTTP_REQ_CONNECT, hostport); event_base_dispatch(base); + evhttp_connection_free(evcon); event_base_free(base); evhttp_uri_free(proxy); - evhttp_uri_free(host_port); evhttp_uri_free(location); + return 0; } diff --git a/sample/http-server.c b/sample/http-server.c index cedb2af8ef..049aabc441 100644 --- a/sample/http-server.c +++ b/sample/http-server.c @@ -99,14 +99,14 @@ static const struct table_entry { { NULL, NULL }, }; -struct options -{ +struct options { int port; int iocp; int verbose; int unlink; const char *unixsock; + const char *docroot; }; /* Try to guess a good content-type for 'path' */ @@ -182,7 +182,7 @@ static void send_document_cb(struct evhttp_request *req, void *arg) { struct evbuffer *evb = NULL; - const char *docroot = arg; + struct options *o = arg; const char *uri = evhttp_request_get_uri(req); struct evhttp_uri *decoded = NULL; const char *path; @@ -222,12 +222,12 @@ send_document_cb(struct evhttp_request *req, void *arg) if (strstr(decoded_path, "..")) goto err; - len = strlen(decoded_path)+strlen(docroot)+2; + len = strlen(decoded_path)+strlen(o->docroot)+2; if (!(whole_path = malloc(len))) { perror("malloc"); goto err; } - evutil_snprintf(whole_path, len, "%s/%s", docroot, decoded_path); + evutil_snprintf(whole_path, len, "%s/%s", o->docroot, decoded_path); if (stat(whole_path, &st)<0) { goto err; @@ -346,12 +346,13 @@ send_document_cb(struct evhttp_request *req, void *arg) static void print_usage(FILE *out, const char *prog, int exit_code) { - fprintf(out, "Syntax: [ OPTS ] %s \n", prog); - fprintf(out, " -p - port\n"); - fprintf(out, " -U - bind to unix socket\n"); - fprintf(out, " -u - unlink unix socket before bind\n"); - fprintf(out, " -I - IOCP\n"); - fprintf(out, " -v - verbosity, enables libevent debug logging too\n"); + fprintf(out, + "Syntax: %s [ OPTS ] \n" + " -p - port\n" + " -U - bind to unix socket\n" + " -u - unlink unix socket before bind\n" + " -I - IOCP\n" + " -v - verbosity, enables libevent debug logging too\n", prog); exit(exit_code); } static struct options @@ -374,9 +375,10 @@ parse_opts(int argc, char **argv) } } - if (optind >= argc || (argc-optind) > 1) { + if (optind >= argc || (argc - optind) > 1) { print_usage(stdout, argv[0], 1); } + o.docroot = argv[optind]; return o; } @@ -504,7 +506,7 @@ main(int argc, char **argv) /* We want to accept arbitrary requests, so we need to set a "generic" * cb. We can also add callbacks for specific paths. */ - evhttp_set_gencb(http, send_document_cb, argv[1]); + evhttp_set_gencb(http, send_document_cb, &o); if (o.unixsock) { #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN diff --git a/sample/https-client.c b/sample/https-client.c index 58e449b1b1..5136acebd7 100644 --- a/sample/https-client.c +++ b/sample/https-client.c @@ -118,7 +118,6 @@ err_openssl(const char *func) exit(1); } -#ifndef _WIN32 /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */ static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) { @@ -181,6 +180,35 @@ static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) return 0; } } + +#ifdef _WIN32 +static int +add_cert_for_store(X509_STORE *store, const char *name) +{ + HCERTSTORE sys_store = NULL; + PCCERT_CONTEXT ctx = NULL; + int r = 0; + + sys_store = CertOpenSystemStore(0, name); + if (!sys_store) { + err("failed to open system certificate store"); + return -1; + } + while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) { + X509 *x509 = d2i_X509(NULL, (unsigned char const **)&ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + } else { + r = -1; + err_openssl("d2i_X509"); + break; + } + } + CertCloseStore(sys_store, 0); + return r; +} #endif int @@ -335,17 +363,22 @@ main(int argc, char **argv) goto error; } -#ifndef _WIN32 - /* TODO: Add certificate loading on Windows as well */ - if (crt == NULL) { X509_STORE *store; /* Attempt to use the system's trusted root certificates. */ store = SSL_CTX_get_cert_store(ssl_ctx); +#ifdef _WIN32 + if (add_cert_for_store(store, "CA") < 0 || + add_cert_for_store(store, "AuthRoot") < 0 || + add_cert_for_store(store, "ROOT") < 0) { + goto error; + } +#else // _WIN32 if (X509_STORE_set_default_paths(store) != 1) { err_openssl("X509_STORE_set_default_paths"); goto error; } +#endif // _WIN32 } else { if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) { err_openssl("SSL_CTX_load_verify_locations"); @@ -376,9 +409,6 @@ main(int argc, char **argv) * "wrapping" OpenSSL's routine, not replacing it. */ SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback, (void *) host); -#else // _WIN32 - (void)crt; -#endif // _WIN32 // Create event base base = event_base_new(); diff --git a/sample/include.am b/sample/include.am index cc003b78a6..b6894d46e5 100644 --- a/sample/include.am +++ b/sample/include.am @@ -25,6 +25,9 @@ sample_https_client_SOURCES = \ sample/hostcheck.c \ sample/openssl_hostname_validation.c sample_https_client_LDADD = libevent.la libevent_openssl.la $(OPENSSL_LIBS) $(OPENSSL_LIBADD) +if BUILD_WIN32 +sample_https_client_LDADD += -lcrypt32 +endif sample_https_client_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS) noinst_HEADERS += \ sample/hostcheck.h \ diff --git a/sample/signal-test.c b/sample/signal-test.c index 18668350b8..4aef420515 100644 --- a/sample/signal-test.c +++ b/sample/signal-test.c @@ -44,8 +44,9 @@ signal_cb(evutil_socket_t fd, short event, void *arg) int main(int argc, char **argv) { - struct event *signal_int; + struct event *signal_int = NULL; struct event_base* base; + int ret = 0; #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; @@ -55,18 +56,28 @@ main(int argc, char **argv) (void) WSAStartup(wVersionRequested, &wsaData); #endif - /* Initalize the event library */ + /* Initialize the event library */ base = event_base_new(); + if (!base) { + ret = 1; + goto out; + } - /* Initalize one event */ + /* Initialize one event */ signal_int = evsignal_new(base, SIGINT, signal_cb, event_self_cbarg()); - + if (!signal_int) { + ret = 2; + goto out; + } event_add(signal_int, NULL); event_base_dispatch(base); - event_free(signal_int); - event_base_free(base); - return (0); +out: + if (signal_int) + event_free(signal_int); + if (base) + event_base_free(base); + return ret; } diff --git a/sample/time-test.c b/sample/time-test.c index 8d0fd91b9e..671a1d2108 100644 --- a/sample/time-test.c +++ b/sample/time-test.c @@ -88,10 +88,10 @@ main(int argc, char **argv) flags = 0; } - /* Initalize the event library */ + /* Initialize the event library */ base = event_base_new(); - /* Initalize one event */ + /* Initialize one event */ event_assign(&timeout, base, -1, flags, timeout_cb, (void*) &timeout); evutil_timerclear(&tv); diff --git a/select.c b/select.c index 8ae53cc11e..b1db0e44b6 100644 --- a/select.c +++ b/select.c @@ -98,7 +98,7 @@ const struct eventop selectops = { select_del, select_dispatch, select_dealloc, - 0, /* doesn't need reinit. */ + 1, /* need_reinit. */ EV_FEATURE_FDS, 0, }; diff --git a/test-export/CMakeLists.txt b/test-export/CMakeLists.txt new file mode 100644 index 0000000000..53a1cf527b --- /dev/null +++ b/test-export/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.1.2) +if (POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() +project(verify) +# set(CMAKE_VERBOSE_MAKEFILE 1) +if(NOT ${EVENT__CODE_COMPONENT} STREQUAL "") + string(TOUPPER ${EVENT__CODE_COMPONENT} _UPPER_COMPONENT) +endif() +find_package(Libevent 2.1.0 REQUIRED COMPONENTS ${EVENT__LINK_COMPONENT}) +add_definitions(-DEVENT_EXPORT_TEST_COMPONENT_${_UPPER_COMPONENT}) +add_executable(test-export test-export.c) +target_link_libraries(test-export ${LIBEVENT_LIBRARIES}) +enable_testing() +add_test(test-export test-export) diff --git a/test-export/test-export.c b/test-export/test-export.c new file mode 100644 index 0000000000..9091777554 --- /dev/null +++ b/test-export/test-export.c @@ -0,0 +1,122 @@ +#include +#if defined(EVENT_EXPORT_TEST_COMPONENT_EXTRA) +#include "event2/http.h" +#include "event2/rpc.h" +#include +#elif defined(EVENT_EXPORT_TEST_COMPONENT_PTHREADS) +#include +#elif defined(EVENT_EXPORT_TEST_COMPONENT_OPENSSL) +#include +#include +#include +#endif + +#if defined(EVENT_EXPORT_TEST_COMPONENT_EXTRA) +static int +test() +{ + struct event_base *base = NULL; + struct evhttp *http = NULL; + struct evdns_base *dns_base = NULL; + struct evrpc_base *rpc_base = NULL; + + base = event_base_new(); + if (base) { + http = evhttp_new(base); + dns_base = evdns_base_new(base, + EVDNS_BASE_DISABLE_WHEN_INACTIVE); + } + if (http) + rpc_base = evrpc_init(http); + + if (base) + event_base_free(base); + if (http) + evhttp_free(http); + if (rpc_base) + evrpc_free(rpc_base); + if (dns_base) + evdns_base_free(dns_base, 0); + + return 0; +} +#elif defined(EVENT_EXPORT_TEST_COMPONENT_PTHREADS) +static int +test() +{ + return evthread_use_pthreads(); +} +#elif defined(EVENT_EXPORT_TEST_COMPONENT_OPENSSL) +static int +test() +{ + struct event_base *base = NULL; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + struct bufferevent *bev; + int r = 1; + + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + base = event_base_new(); + if (!base) { + goto error; + } + + ssl_ctx = SSL_CTX_new(SSLv23_method()); + if (!ssl_ctx) { + goto error; + } + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + goto error; + } + bev = bufferevent_openssl_socket_new(base, -1, ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); + if (bev == NULL) { + goto error; + } + r = 0; +error: + if (base) + event_base_free(base); + if (ssl_ctx) + SSL_CTX_free(ssl_ctx); + if (ssl) + SSL_free(ssl); + return r; +} +#else +static int +test() +{ + struct event_base *base = NULL; + + base = event_base_new(); + if (base) + event_base_free(base); + + return 0; +} +#endif + +int +main(int argc, char const *argv[]) +{ + int r = 0; +#ifdef _WIN32 + { + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + } +#endif + r = test(); +#ifdef _WIN32 + WSACleanup(); +#endif + return r; +} diff --git a/test-export/test-export.py b/test-export/test-export.py new file mode 100644 index 0000000000..d71e5dbaa1 --- /dev/null +++ b/test-export/test-export.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +# +# Check if find_package(Libevent COMPONENTS xxx) can get the correct library. +# Note: this script has only been tested on python3. +# Usage: +# cd cmake-build-dir +# cmake .. && cmake --build . +# python /path/to/test-export.py [static|shared] + +import sys +import os +import shutil +import platform +import subprocess +import tempfile + +results = ("success", "failure") +FNULL = open(os.devnull, 'wb') +script_dir = os.path.split(os.path.realpath(sys.argv[0]))[0] +# working_dir is cmake build dir +working_dir = os.getcwd() +if len(sys.argv) > 1 and sys.argv[1] == "static": + link_type = sys.argv[1] +else: + link_type = "shared" + + +def exec_cmd(cmd, silent): + if silent: + p = subprocess.Popen(cmd, stdout=FNULL, stderr=FNULL, shell=True) + else: + p = subprocess.Popen(cmd, shell=True) + p.communicate() + return p.poll() + + +def link_and_run(link, code): + """Check if the source code matches the library component. + + Compile source code relative to one component and link to another component. + Then run the generated executor. + + Args: + link: The name of component that the source code will link with. + code: The source code related component name. + + Returns: + Returns 0 if links and runs successfully, otherwise 1. + """ + exec_cmd("cmake --build . --target clean", True) + arch = '' + if platform.system() == "Windows": + arch = '-A x64' + cmd = 'cmake .. %s -DEVENT__LINK_COMPONENT=%s -DEVENT__CODE_COMPONENT=%s' % ( + arch, link, code) + if link_type == "static": + cmd = "".join([cmd, " -DLIBEVENT_STATIC_LINK=1"]) + r = exec_cmd(cmd, True) + if r == 0: + r = exec_cmd('cmake --build .', True) + if r == 0: + r = exec_cmd('ctest', True) + if r != 0: + r = 1 + return r + +# expect 0:success 1:failure +def testcase(link, code, expect): + r = link_and_run(link, code) + if link == "": + link = "all" + if code == "": + code = "all" + if r != expect: + print('[test-export] fail: link %s and run %s expects %s but gets %s.' % + (link, code, results[expect], results[r])) + sys.exit(1) + else: + print('[test-export] success: link %s and run %s expects and gets %s.' % + (link, code, results[r])) + +# Dependency relationships between libevent libraries: +# core: none +# extra: core +# pthreads: core,pthread +# openssl: core,openssl +def test_group(): + testcase("core", "core", 0) + testcase("extra", "extra", 0) + testcase("openssl", "openssl", 0) + testcase("", "", 0) + testcase("extra", "core", 0) + testcase("openssl", "core", 0) + testcase("core", "extra", 1) + testcase("core", "openssl", 1) + testcase("extra", "openssl", 1) + testcase("openssl", "extra", 1) + if platform.system() != "Windows": + testcase("pthreads", "pthreads", 0) + testcase("pthreads", "core", 0) + testcase("core", "pthreads", 1) + testcase("extra", "pthreads", 1) + testcase("pthreads", "extra", 1) + testcase("pthreads", "openssl", 1) + testcase("openssl", "pthreads", 1) + + +def config_restore(): + if os.path.isfile("tempconfig") and not os.path.isfile("LibeventConfig.cmake"): + os.rename("tempconfig", "LibeventConfig.cmake") + + +def config_backup(): + if os.path.isfile("tempconfig"): + os.remove("tempconfig") + if os.path.isfile("LibeventConfig.cmake"): + os.rename("LibeventConfig.cmake", "tempconfig") + + +shutil.rmtree(os.path.join(script_dir, "build"), ignore_errors=True) + + +def run_test_group(): + os.chdir(script_dir) + if not os.path.isdir("build"): + os.mkdir("build") + os.chdir("build") + test_group() + os.chdir(working_dir) + + +need_exportdll = False +if link_type == "shared" and platform.system() == "Windows": + need_exportdll = True + +# On Windows, we need to add the directory containing the dll to the +# 'PATH' environment variable so that the program can call it. +def export_dll(dir): + if need_exportdll: + os.environ["PATH"] += os.pathsep + dir + + +def unexport_dll(dir): + if need_exportdll: + paths = os.environ["PATH"].split(os.pathsep) + paths = list(set(paths)) + if dir in paths: + paths.remove(dir) + os.environ["PATH"] = os.pathsep.join(paths) + + +print("[test-export] use %s library" % link_type) + +# Test for build tree. +print("[test-export] test for build tree") +dllpath = os.path.join(working_dir, "bin", "Debug") +config_restore() +os.environ["CMAKE_PREFIX_PATH"] = working_dir +export_dll(dllpath) +run_test_group() +del os.environ["CMAKE_PREFIX_PATH"] +unexport_dll(dllpath) + +# Install libevent libraries to system path. Remove LibeventConfig.cmake +# from build directory to avoid confusion when using find_package(). +print("[test-export] test for install tree(in system-wide path)") +if platform.system() == "Windows": + prefix = "C:\\Program Files\\libevent" + dllpath = os.path.join(prefix, "lib") +else: + prefix = "/usr/local" +exec_cmd('cmake -DCMAKE_INSTALL_PREFIX="%s" ..' % prefix, True) +exec_cmd('cmake --build . --target install', True) +config_backup() +os.environ["CMAKE_PREFIX_PATH"] = os.path.join(prefix, "lib/cmake/libevent") +export_dll(dllpath) +run_test_group() +unexport_dll(dllpath) +del os.environ["CMAKE_PREFIX_PATH"] + +# Uninstall the libraries installed in the above steps. Install the libraries +# into a temporary directory. Same as above, remove LibeventConfig.cmake from +# build directory to avoid confusion when using find_package(). +print("[test-export] test for install tree(in non-system-wide path)") +exec_cmd("cmake --build . --target uninstall", True) +tempdir = tempfile.TemporaryDirectory() +cmd = 'cmake -DCMAKE_INSTALL_PREFIX="%s" ..' % tempdir.name +exec_cmd(cmd, True) +exec_cmd("cmake --build . --target install", True) +config_backup() +os.environ["CMAKE_PREFIX_PATH"] = os.path.join(tempdir.name, "lib/cmake/libevent") +dllpath = os.path.join(tempdir.name, "lib") +export_dll(dllpath) +run_test_group() +unexport_dll(dllpath) +del os.environ["CMAKE_PREFIX_PATH"] +config_restore() + +print("[test-export] all testcases have run successfully") diff --git a/test/bench.c b/test/bench.c index 3a6886dba6..f2af4d3f30 100644 --- a/test/bench.c +++ b/test/bench.c @@ -70,6 +70,7 @@ static int writes, failures; static evutil_socket_t *pipes; static int num_pipes, num_active, num_writes; static struct event *events; +static struct event_base *base; static void @@ -105,11 +106,11 @@ run_once(void) for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { if (event_initialized(&events[i])) event_del(&events[i]); - event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *)(ev_intptr_t) i); + event_assign(&events[i], base, cp[0], EV_READ | EV_PERSIST, read_cb, (void *)(ev_intptr_t) i); event_add(&events[i], NULL); } - event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + event_base_loop(base, EVLOOP_ONCE | EVLOOP_NONBLOCK); fired = 0; space = num_pipes / num_active; @@ -123,7 +124,7 @@ run_once(void) int xcount = 0; evutil_gettimeofday(&ts, NULL); do { - event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + event_base_loop(base, EVLOOP_ONCE | EVLOOP_NONBLOCK); xcount++; } while (count != fired); evutil_gettimeofday(&te, NULL); @@ -147,6 +148,9 @@ main(int argc, char **argv) int i, c; struct timeval *tv; evutil_socket_t *cp; + const char **methods; + const char *method = NULL; + struct event_config *cfg = NULL; #ifdef _WIN32 WSADATA WSAData; @@ -155,7 +159,7 @@ main(int argc, char **argv) num_pipes = 100; num_active = 1; num_writes = num_pipes; - while ((c = getopt(argc, argv, "n:a:w:")) != -1) { + while ((c = getopt(argc, argv, "n:a:w:m:l")) != -1) { switch (c) { case 'n': num_pipes = atoi(optarg); @@ -166,6 +170,16 @@ main(int argc, char **argv) case 'w': num_writes = atoi(optarg); break; + case 'm': + method = optarg; + break; + case 'l': + methods = event_get_supported_methods(); + fprintf(stdout, "Using Libevent %s. Available methods are:\n", + event_get_version()); + for (i = 0; methods[i] != NULL; ++i) + printf(" %s\n", methods[i]); + exit(0); default: fprintf(stderr, "Illegal argument \"%c\"\n", c); exit(1); @@ -187,7 +201,16 @@ main(int argc, char **argv) exit(1); } - event_init(); + if (method != NULL) { + cfg = event_config_new(); + methods = event_get_supported_methods(); + for (i = 0; methods[i] != NULL; ++i) + if (strcmp(methods[i], method)) + event_config_avoid_method(cfg, methods[i]); + base = event_base_new_with_config(cfg); + event_config_free(cfg); + } else + base = event_base_new(); for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { #ifdef USE_PIPES diff --git a/test/regress.c b/test/regress.c index 0ebadcb9be..08c30fab9b 100644 --- a/test/regress.c +++ b/test/regress.c @@ -31,10 +31,6 @@ #include #endif -#ifdef EVENT__HAVE_PTHREADS -#include -#endif - #include "event2/event-config.h" #include @@ -73,6 +69,7 @@ #include "time-internal.h" #include "regress.h" +#include "regress_thread.h" #ifndef _WIN32 #include "regress.gen.h" @@ -390,7 +387,7 @@ record_event_cb(evutil_socket_t s, short what, void *ptr) } static void -test_simpleclose(void *ptr) +test_simpleclose_rw(void *ptr) { /* Test that a close of FD is detected as a read and as a write. */ struct event_base *base = event_base_new(); @@ -472,6 +469,56 @@ test_simpleclose(void *ptr) event_base_free(base); } +static void +test_simpleclose(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + evutil_socket_t *pair = data->pair; + const char *flags = (const char *)data->setup_data; + int et = !!strstr(flags, "ET"); + int persist = !!strstr(flags, "persist"); + short events = EV_CLOSED | (et ? EV_ET : 0) | (persist ? EV_PERSIST : 0); + struct event *ev = NULL; + short got_event; + + if (!(event_base_get_features(data->base) & EV_FEATURE_EARLY_CLOSE)) + tt_skip(); + + /* XXX: should this code moved to regress_et.c ? */ + if (et && !(event_base_get_features(data->base) & EV_FEATURE_ET)) + tt_skip(); + + ev = event_new(base, pair[0], events, record_event_cb, &got_event); + tt_assert(ev); + tt_assert(!event_add(ev, NULL)); + + got_event = 0; + if (strstr(flags, "close")) { + tt_assert(!evutil_closesocket(pair[1])); + /* avoid closing in setup routines */ + pair[1] = -1; + } else if (strstr(flags, "shutdown")) { + tt_assert(!shutdown(pair[1], EVUTIL_SHUT_WR)); + } else { + tt_abort_msg("unknown flags"); + } + + /* w/o edge-triggerd but w/ persist it will not stop */ + if (!et && persist) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10000; + tt_assert(!event_base_loopexit(base, &tv)); + } + + tt_int_op(event_base_loop(base, EVLOOP_NONBLOCK), ==, !persist); + tt_int_op(got_event, ==, (events & ~EV_PERSIST)); + +end: + if (ev) + event_free(ev); +} static void test_multiple(void) @@ -979,7 +1026,7 @@ test_fork(void) evutil_closesocket(child_pair[1]); } -#ifdef EVENT__HAVE_PTHREADS +#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED static void* del_wait_thread(void *arg) { struct timeval tv_start, tv_end; @@ -1007,14 +1054,14 @@ static void test_del_wait(void) { struct event ev; - pthread_t thread; + THREAD_T thread; setup_test("event_del will wait: "); event_set(&ev, pair[1], EV_READ|EV_PERSIST, del_wait_cb, &ev); event_add(&ev, NULL); - pthread_create(&thread, NULL, del_wait_thread, NULL); + THREAD_START(thread, del_wait_thread, NULL); if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { tt_fail_perror("write"); @@ -1033,7 +1080,7 @@ test_del_wait(void) test_timeval_diff_eq(&tv_start, &tv_end, 270); } - pthread_join(thread, NULL); + THREAD_JOIN(thread); tt_int_op(test_ok, ==, 1); @@ -1051,14 +1098,14 @@ static void test_del_notify(void) { struct event ev; - pthread_t thread; + THREAD_T thread; test_ok = 1; event_set(&ev, -1, EV_READ, null_cb, &ev); event_add(&ev, NULL); - pthread_create(&thread, NULL, test_del_notify_thread, NULL); + THREAD_START(thread, test_del_notify_thread, NULL); { struct timeval delay = { 0, 1000 }; @@ -1066,7 +1113,7 @@ test_del_notify(void) } event_del(&ev); - pthread_join(thread, NULL); + THREAD_JOIN(thread); } #endif @@ -3464,8 +3511,35 @@ struct testcase_t main_testcases[] = { LEGACY(simpleread, TT_ISOLATED), LEGACY(simpleread_multiple, TT_ISOLATED), LEGACY(simplewrite, TT_ISOLATED), - { "simpleclose", test_simpleclose, TT_FORK, &basic_setup, - NULL }, + { "simpleclose_rw", test_simpleclose_rw, TT_FORK, &basic_setup, NULL }, + /* simpleclose */ + { "simpleclose_close", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close" }, + { "simpleclose_shutdown", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown" }, + /* simpleclose_*_persist */ + { "simpleclose_close_persist", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close_persist" }, + { "simpleclose_shutdown_persist", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown_persist" }, + /* simpleclose_*_et */ + { "simpleclose_close_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close_ET" }, + { "simpleclose_shutdown_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown_ET" }, + /* simpleclose_*_persist_et */ + { "simpleclose_close_persist_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"close_persist_ET" }, + { "simpleclose_shutdown_persist_et", test_simpleclose, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE, + &basic_setup, (void *)"shutdown_persist_ET" }, LEGACY(multiple, TT_ISOLATED), LEGACY(persistent, TT_ISOLATED), LEGACY(combined, TT_ISOLATED), @@ -3505,8 +3579,8 @@ struct testcase_t main_testcases[] = { #ifndef _WIN32 LEGACY(fork, TT_ISOLATED), #endif -#ifdef EVENT__HAVE_PTHREADS - /** TODO: support win32 */ + +#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED LEGACY(del_wait, TT_ISOLATED|TT_NEED_THREADS|TT_RETRIABLE), LEGACY(del_notify, TT_ISOLATED|TT_NEED_THREADS), #endif diff --git a/test/regress.h b/test/regress.h index 643b82ba8b..43cb4eaf1e 100644 --- a/test/regress.h +++ b/test/regress.h @@ -95,6 +95,7 @@ extern int libevent_tests_running_in_debug_mode; #define TT_NO_LOGS (TT_FIRST_USER_FLAG<<5) #define TT_ENABLE_IOCP_FLAG (TT_FIRST_USER_FLAG<<6) #define TT_ENABLE_IOCP (TT_ENABLE_IOCP_FLAG|TT_NEED_THREADS) +#define TT_ENABLE_DEBUG_MODE (TT_ENABLE_IOCP_FLAG<<7) /* All the flags that a legacy test needs. */ #define TT_ISOLATED TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE diff --git a/test/regress_buffer.c b/test/regress_buffer.c index 8ac4b6e038..f259b924bf 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -425,6 +425,36 @@ test_evbuffer_remove_buffer_with_empty3(void *ptr) evbuffer_free(buf); } +static void +test_evbuffer_pullup_with_empty(void *ptr) +{ + struct evbuffer *buf = NULL; + + buf = evbuffer_new(); + evbuffer_add(buf, "foo", 3); + evbuffer_add_reference(buf, NULL, 0, NULL, NULL); + evbuffer_validate(buf); + tt_int_op(evbuffer_get_length(buf), ==, 3); + tt_mem_op(evbuffer_pullup(buf, -1), ==, "foo", 3); + + evbuffer_free(buf); + buf = evbuffer_new(); + evbuffer_validate(buf); + tt_int_op(evbuffer_get_length(buf), ==, 0); + tt_int_op(evbuffer_pullup(buf, -1), ==, NULL); + + evbuffer_free(buf); + buf = evbuffer_new(); + evbuffer_add(buf, "foo", 3); + evbuffer_add_reference(buf, NULL, 0, NULL, NULL); + evbuffer_validate(buf); + tt_mem_op(evbuffer_pullup(buf, 3), ==, "foo", 3); + + end: + if (buf) + evbuffer_free(buf); +} + static void test_evbuffer_remove_buffer_with_empty_front(void *ptr) { @@ -2327,7 +2357,7 @@ test_evbuffer_empty_reference_prepend_buffer(void *ptr) tt_assert(!strncmp((char *)evbuffer_pullup(buf2, -1), "foo", 3)); evbuffer_validate(buf2); - tt_assert(!strncmp((char *)evbuffer_pullup(buf1, -1), "", 0)); + tt_assert(evbuffer_pullup(buf1, -1) == NULL); evbuffer_validate(buf2); end: @@ -2494,28 +2524,37 @@ test_evbuffer_peek(void *info) static void test_evbuffer_freeze(void *ptr) { - struct evbuffer *buf = NULL, *tmp_buf=NULL; + struct basic_test_data *testdata = ptr; + evutil_socket_t *pair = testdata->pair; + struct evbuffer *buf = NULL, *buf_two = NULL, *tmp_buf = NULL; const char string[] = /* Year's End, Richard Wilbur */ "I've known the wind by water banks to shake\n" "The late leaves down, which frozen where they fell\n" "And held in ice as dancers in a spell\n" "Fluttered all winter long into a lake..."; - const int start = !strcmp(ptr, "start"); + const int start = !strcmp(testdata->setup_data, "start"); + const char tmpfilecontent[] = "file_freeze_test_file"; char *cp; char charbuf[128]; + char *tmpfilename = NULL; + int fd = -1; int r; - size_t orig_length; + size_t orig_length, len; struct evbuffer_iovec v[1]; if (!start) - tt_str_op(ptr, ==, "end"); + tt_str_op(testdata->setup_data, ==, "end"); buf = evbuffer_new(); + buf_two = evbuffer_new(); tmp_buf = evbuffer_new(); tt_assert(tmp_buf); evbuffer_add(buf, string, strlen(string)); + evbuffer_add(buf_two, "abc", 3); + evbuffer_add(tmp_buf, "xyz", 3); evbuffer_freeze(buf, start); /* Freeze the start or the end.*/ + evbuffer_freeze(buf_two, start); #define FREEZE_EQ(a, startcase, endcase) \ do { \ @@ -2544,7 +2583,22 @@ test_evbuffer_freeze(void *ptr) FREEZE_EQ(r, 0, -1); r = evbuffer_add_printf(buf, "Hello %s", "world"); FREEZE_EQ(r, 11, -1); - /* TODO: test add_buffer, add_file, read */ + + r = evbuffer_add_buffer(buf, tmp_buf); + FREEZE_EQ(r, 0, -1); + len = strlen(tmpfilecontent); + fd = regress_make_tmpfile(tmpfilecontent, len, &tmpfilename); + r = evbuffer_add_file(buf, fd, 0, len); + FREEZE_EQ(r, 0, -1); + + if (start) + evbuffer_add(tmp_buf, "xyz", 3); + + tt_assert(evbuffer_get_length(tmp_buf)); + len = evbuffer_get_length(tmp_buf); + evbuffer_write(tmp_buf, pair[0]); + r = evbuffer_read(buf, pair[1], -1); + FREEZE_EQ(r, len, -1); if (!start) tt_int_op(orig_length, ==, evbuffer_get_length(buf)); @@ -2562,7 +2616,24 @@ test_evbuffer_freeze(void *ptr) FREEZE_EQ(cp==NULL, 1, 0); if (cp) free(cp); - /* TODO: Test remove_buffer, add_buffer, write, prepend_buffer */ + + evbuffer_add(tmp_buf, "xyz", 3); + tt_assert(evbuffer_get_length(tmp_buf)); + r = evbuffer_remove_buffer(buf, tmp_buf, 3); + FREEZE_EQ(r, -1, 3); + r = evbuffer_drain(buf, 3); + FREEZE_EQ(r, -1, 0); + r = evbuffer_prepend_buffer(buf, tmp_buf); + FREEZE_EQ(r, -1, 0); + + len = evbuffer_get_length(buf); + r = evbuffer_write(buf, pair[0]); + evbuffer_read(tmp_buf, pair[1], -1); + FREEZE_EQ(r, -1, len); + len = evbuffer_get_length(buf_two); + r = evbuffer_write_atmost(buf_two, pair[0], -1); + evbuffer_read(tmp_buf, pair[1], -1); + FREEZE_EQ(r, -1, len); if (start) tt_int_op(orig_length, ==, evbuffer_get_length(buf)); @@ -2571,8 +2642,16 @@ test_evbuffer_freeze(void *ptr) if (buf) evbuffer_free(buf); + if (buf_two) + evbuffer_free(buf_two); + if (tmp_buf) evbuffer_free(tmp_buf); + + if (tmpfilename) { + unlink(tmpfilename); + free(tmpfilename); + } } static void @@ -2756,11 +2835,12 @@ struct testcase_t evbuffer_testcases[] = { { "empty_reference_prepend_buffer", test_evbuffer_empty_reference_prepend_buffer, TT_FORK, NULL, NULL }, { "peek", test_evbuffer_peek, 0, NULL, NULL }, { "peek_first_gt", test_evbuffer_peek_first_gt, 0, NULL, NULL }, - { "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" }, - { "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" }, + { "freeze_start", test_evbuffer_freeze, TT_NEED_SOCKETPAIR, &basic_setup, (void*)"start" }, + { "freeze_end", test_evbuffer_freeze, TT_NEED_SOCKETPAIR, &basic_setup, (void*)"end" }, { "add_iovec", test_evbuffer_add_iovec, 0, NULL, NULL}, { "copyout", test_evbuffer_copyout, 0, NULL, NULL}, { "file_segment_add_cleanup_cb", test_evbuffer_file_segment_add_cleanup_cb, 0, NULL, NULL }, + { "pullup_with_empty", test_evbuffer_pullup_with_empty, 0, NULL, NULL }, #define ADDFILE_TEST(name, parameters) \ { name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \ diff --git a/test/regress_bufferevent.c b/test/regress_bufferevent.c index d4208c2090..c276a0e5d1 100644 --- a/test/regress_bufferevent.c +++ b/test/regress_bufferevent.c @@ -29,6 +29,19 @@ /* The old tests here need assertions to work. */ #undef NDEBUG +/** + * - clang supports __has_feature + * - gcc supports __SANITIZE_ADDRESS__ + * + * Let's set __SANITIZE_ADDRESS__ if __has_feature(address_sanitizer) + */ +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#if !defined(__SANITIZE_ADDRESS__) && __has_feature(address_sanitizer) +#define __SANITIZE_ADDRESS__ +#endif + #ifdef _WIN32 #include #include @@ -203,7 +216,7 @@ static void test_bufferevent_pair_flush_normal(void) { test_bufferevent_impl(1, static void test_bufferevent_pair_flush_flush(void) { test_bufferevent_impl(1, BEV_FLUSH); } static void test_bufferevent_pair_flush_finished(void) { test_bufferevent_impl(1, BEV_FINISHED); } -#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) && !defined(__SANITIZE_ADDRESS__) /** * Trace lock/unlock/alloc/free for locks. * (More heavier then evthread_debug*) @@ -787,16 +800,29 @@ test_bufferevent_connect(void *arg) bufferevent_free(bev2); } +static void +close_socket_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_socket_t *fdp = arg; + if (*fdp >= 0) { + evutil_closesocket(*fdp); + *fdp = -1; + } +} + static void test_bufferevent_connect_fail_eventcb(void *arg) { struct basic_test_data *data = arg; int flags = BEV_OPT_CLOSE_ON_FREE | (long)data->setup_data; + struct event close_listener_event; struct bufferevent *bev = NULL; struct evconnlistener *lev = NULL; struct sockaddr_in localhost; + struct timeval close_timeout = { 0, 300000 }; ev_socklen_t slen = sizeof(localhost); evutil_socket_t fake_listener = -1; + int r; fake_listener = fake_listener_create(&localhost); @@ -809,10 +835,22 @@ test_bufferevent_connect_fail_eventcb(void *arg) bufferevent_enable(bev, EV_READ|EV_WRITE); tt_int_op(n_events_invoked, ==, 0); tt_int_op(n_reads_invoked, ==, 0); + /** @see also test_bufferevent_connect_fail() */ - bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + r = bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + /* XXXX we'd like to test the '0' case everywhere, but FreeBSD tells + * detects the error immediately, which is not really wrong of it. */ + tt_want(r == 0 || r == -1); + tt_int_op(n_events_invoked, ==, 0); tt_int_op(n_reads_invoked, ==, 0); + + /* Close the listener socket after a delay. This should trigger + "connection refused" on some other platforms, including OSX. */ + evtimer_assign(&close_listener_event, data->base, close_socket_cb, + &fake_listener); + event_add(&close_listener_event, &close_timeout); + event_base_dispatch(data->base); tt_int_op(n_events_invoked, ==, 1); tt_int_op(n_reads_invoked, ==, 0); @@ -846,16 +884,6 @@ want_fail_eventcb(struct bufferevent *bev, short what, void *ctx) event_base_loopexit(base, NULL); } -static void -close_socket_cb(evutil_socket_t fd, short what, void *arg) -{ - evutil_socket_t *fdp = arg; - if (*fdp >= 0) { - evutil_closesocket(*fdp); - *fdp = -1; - } -} - static void test_bufferevent_connect_fail(void *arg) { @@ -863,7 +891,7 @@ test_bufferevent_connect_fail(void *arg) struct bufferevent *bev=NULL; struct event close_listener_event; int close_listener_event_added = 0; - struct timeval one_second = { 1, 0 }; + struct timeval close_timeout = { 0, 300000 }; struct sockaddr_in localhost; ev_socklen_t slen = sizeof(localhost); evutil_socket_t fake_listener = -1; @@ -882,11 +910,11 @@ test_bufferevent_connect_fail(void *arg) * detects the error immediately, which is not really wrong of it. */ tt_want(r == 0 || r == -1); - /* Close the listener socket after a second. This should trigger + /* Close the listener socket after a delay. This should trigger "connection refused" on some other platforms, including OSX. */ evtimer_assign(&close_listener_event, data->base, close_socket_cb, &fake_listener); - event_add(&close_listener_event, &one_second); + event_add(&close_listener_event, &close_timeout); close_listener_event_added = 1; event_base_dispatch(data->base); @@ -1336,7 +1364,7 @@ struct testcase_t bufferevent_testcases[] = { LEGACY(bufferevent_pair_flush_normal, TT_ISOLATED), LEGACY(bufferevent_pair_flush_flush, TT_ISOLATED), LEGACY(bufferevent_pair_flush_finished, TT_ISOLATED), -#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) && !defined(__SANITIZE_ADDRESS__) { "bufferevent_pair_release_lock", test_bufferevent_pair_release_lock, TT_FORK|TT_ISOLATED|TT_NEED_THREADS|TT_NEED_BASE|TT_LEGACY|TT_NO_LOGS, &basic_setup, NULL }, diff --git a/test/regress_dns.c b/test/regress_dns.c index d2084b70e2..9a8bff4f15 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -1265,27 +1265,9 @@ test_bufferevent_connect_hostname(void *arg) int n_accept=0, n_dns=0; char buf[128]; int emfile = data->setup_data && !strcmp(data->setup_data, "emfile"); - int success = BEV_EVENT_CONNECTED; - int default_error = 0; unsigned i; int ret; - if (emfile) { - success = BEV_EVENT_ERROR; -#if defined(__linux__) - /* on linux glibc/musl reports EAI_SYSTEM, when getaddrinfo() cannot - * open file for resolving service. */ - default_error = EVUTIL_EAI_SYSTEM; -#elif defined(__sun__) - /* on solaris it returns EAI_FAIL */ - default_error = EVUTIL_EAI_FAIL; - /** the DP_POLL can also fail with EINVAL under EMFILE */ -#else - /* on osx/freebsd it returns EAI_NONAME */ - default_error = EVUTIL_EAI_NONAME; -#endif - } - be_connect_hostname_base = data->base; /* Bind an address and figure out what port it's on. */ @@ -1376,12 +1358,16 @@ test_bufferevent_connect_hostname(void *arg) tt_int_op(be_outcome[0].what, ==, BEV_EVENT_ERROR); tt_int_op(be_outcome[0].dnserr, ==, EVUTIL_EAI_NONAME); - tt_int_op(be_outcome[1].what, ==, success); + tt_int_op(be_outcome[1].what, ==, !emfile ? BEV_EVENT_CONNECTED : BEV_EVENT_ERROR); tt_int_op(be_outcome[1].dnserr, ==, 0); - tt_int_op(be_outcome[2].what, ==, success); + tt_int_op(be_outcome[2].what, ==, !emfile ? BEV_EVENT_CONNECTED : BEV_EVENT_ERROR); tt_int_op(be_outcome[2].dnserr, ==, 0); - tt_int_op(be_outcome[3].what, ==, success); - tt_int_op(be_outcome[3].dnserr, ==, default_error); + tt_int_op(be_outcome[3].what, ==, !emfile ? BEV_EVENT_CONNECTED : BEV_EVENT_ERROR); + if (!emfile) { + tt_int_op(be_outcome[3].dnserr, ==, 0); + } else { + tt_int_op(be_outcome[3].dnserr, !=, 0); + } if (expect_err) { tt_int_op(be_outcome[4].what, ==, BEV_EVENT_ERROR); tt_int_op(be_outcome[4].dnserr, ==, expect_err); @@ -1822,7 +1808,8 @@ struct gaic_request_status { #define GAIC_MAGIC 0x1234abcd -static int pending = 0; +static int gaic_pending = 0; +static int gaic_freed = 0; static void gaic_cancel_request_cb(evutil_socket_t fd, short what, void *arg) @@ -1867,7 +1854,13 @@ gaic_getaddrinfo_cb(int result, struct evutil_addrinfo *res, void *arg) free(status); end: - if (--pending <= 0) + if (res) + { + TT_BLATHER(("evutil_freeaddrinfo(%p)", res)); + evutil_freeaddrinfo(res); + ++gaic_freed; + } + if (--gaic_pending <= 0) event_base_loopexit(base, NULL); } @@ -1885,7 +1878,7 @@ gaic_launch(struct event_base *base, struct evdns_base *dns_base) "foobar.bazquux.example.com", "80", NULL, gaic_getaddrinfo_cb, status); event_add(&status->cancel_event, &tv); - ++pending; + ++gaic_pending; } #ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED @@ -2108,6 +2101,9 @@ test_getaddrinfo_async_cancel_stress(void *ptr) event_base_dispatch(base); + // at least some was canceled via external event + tt_int_op(gaic_freed, !=, 1000); + end: if (dns_base) evdns_base_free(dns_base, 1); @@ -2124,6 +2120,7 @@ dns_client_fail_requests_test(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; + int limit_inflight = data->setup_data && !strcmp(data->setup_data, "limit-inflight"); struct evdns_base *dns = NULL; struct evdns_server_port *dns_port = NULL; ev_uint16_t portnum = 0; @@ -2141,6 +2138,9 @@ dns_client_fail_requests_test(void *arg) dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + if (limit_inflight) + tt_assert(!evdns_base_set_option(dns, "max-inflight:", "11")); + for (i = 0; i < 20; ++i) evdns_base_resolve_ipv4(dns, "foof.example.com", 0, generic_dns_callback, &r[i]); @@ -2372,6 +2372,71 @@ test_set_so_rcvbuf_so_sndbuf(void *arg) evdns_base_free(dns_base, 0); } +static void +test_set_option(void *arg) +{ +#define SUCCESS 0 +#define FAIL -1 + struct basic_test_data *data = arg; + struct evdns_base *dns_base; + size_t i; + /* Option names are allowed to have ':' at the end. + * So all test option names come in pairs. + */ + const char *int_options[] = { + "ndots", "ndots:", + "max-timeouts", "max-timeouts:", + "max-inflight", "max-inflight:", + "attempts", "attempts:", + "randomize-case", "randomize-case:", + "so-rcvbuf", "so-rcvbuf:", + "so-sndbuf", "so-sndbuf:", + }; + const char *timeval_options[] = { + "timeout", "timeout:", + "getaddrinfo-allow-skew", "getaddrinfo-allow-skew:", + "initial-probe-timeout", "initial-probe-timeout:", + }; + const char *addr_port_options[] = { + "bind-to", "bind-to:", + }; + + dns_base = evdns_base_new(data->base, 0); + tt_assert(dns_base); + + for (i = 0; i < ARRAY_SIZE(int_options); ++i) { + tt_assert(SUCCESS == evdns_base_set_option(dns_base, int_options[i], "0")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, int_options[i], "1")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, int_options[i], "10000")); + tt_assert(FAIL == evdns_base_set_option(dns_base, int_options[i], "foo")); + tt_assert(FAIL == evdns_base_set_option(dns_base, int_options[i], "3.14")); + } + + for (i = 0; i < ARRAY_SIZE(timeval_options); ++i) { + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "1")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "0.001")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "3.14")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, timeval_options[i], "10000")); + tt_assert(FAIL == evdns_base_set_option(dns_base, timeval_options[i], "0")); + tt_assert(FAIL == evdns_base_set_option(dns_base, timeval_options[i], "foo")); + } + + for (i = 0; i < ARRAY_SIZE(addr_port_options); ++i) { + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "8.8.8.8:80")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "1.2.3.4")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "::1:82")); + tt_assert(SUCCESS == evdns_base_set_option(dns_base, addr_port_options[i], "3::4")); + tt_assert(FAIL == evdns_base_set_option(dns_base, addr_port_options[i], "3.14")); + tt_assert(FAIL == evdns_base_set_option(dns_base, addr_port_options[i], "foo")); + } + +#undef SUCCESS +#undef FAIL +end: + if (dns_base) + evdns_base_free(dns_base, 0); +} + #define DNS_LEGACY(name, flags) \ { #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \ dns_##name } @@ -2432,6 +2497,8 @@ struct testcase_t dns_testcases[] = { { "client_fail_requests", dns_client_fail_requests_test, TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + { "client_fail_waiting_requests", dns_client_fail_requests_test, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, (char*)"limit-inflight" }, { "client_fail_requests_getaddrinfo", dns_client_fail_requests_getaddrinfo_test, TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, @@ -2443,6 +2510,8 @@ struct testcase_t dns_testcases[] = { { "set_SO_RCVBUF_SO_SNDBUF", test_set_so_rcvbuf_so_sndbuf, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "set_options", test_set_option, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, END_OF_TESTCASES }; diff --git a/test/regress_et.c b/test/regress_et.c index 5fa87a399a..1b1f819eda 100644 --- a/test/regress_et.c +++ b/test/regress_et.c @@ -102,7 +102,7 @@ test_edgetriggered(void *data_) "support edge-triggering", event_base_get_method(base), supports_et?"":"not ")); - /* Initalize one event */ + /* Initialize one event */ ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, read_cb, &ev); tt_assert(ev != NULL); tt_int_op(event_add(ev, NULL), ==, 0); diff --git a/test/regress_finalize.c b/test/regress_finalize.c index 552210fe9d..9e57188121 100644 --- a/test/regress_finalize.c +++ b/test/regress_finalize.c @@ -290,6 +290,53 @@ test_fin_within_cb(void *arg) ; } +static void +event_finalize_callback_free(struct event *ev, void *arg) +{ + struct event_base *base = arg; + int err; + if (base) { + err = event_assign(ev, base, -1, EV_TIMEOUT, NULL, NULL); + tt_int_op(err, ==, 0); + test_ok += 1; + } else { + free(ev); + test_ok += 1; + } + +end: + ; +} +static void +test_fin_debug_use_after_free(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev; + + tt_ptr_op(ev = event_new(base, -1, EV_TIMEOUT, NULL, base), !=, NULL); + tt_int_op(event_add(ev, NULL), ==, 0); + tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0); + + // Dispatch base to trigger callbacks + event_base_dispatch(base); + event_base_assert_ok_(base); + tt_int_op(test_ok, ==, 1); + + // Now add again, since we did event_assign in event_finalize_callback_free + // This used to fail in event_debug_assert_is_setup_ + tt_int_op(event_add(ev, NULL), ==, 0); + + // Finalize and dispatch again + tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0); + event_base_dispatch(base); + event_base_assert_ok_(base); + tt_int_op(test_ok, ==, 2); + +end: + ; +} + #if 0 static void timer_callback_3(evutil_socket_t *fd, short what, void *arg) @@ -339,6 +386,7 @@ struct testcase_t finalize_testcases[] = { TEST(cb_invoked, TT_FORK|TT_NEED_BASE), TEST(free_finalize, TT_FORK), TEST(within_cb, TT_FORK|TT_NEED_BASE), + TEST(debug_use_after_free, TT_FORK|TT_NEED_BASE|TT_ENABLE_DEBUG_MODE), // TEST(many, TT_FORK|TT_NEED_BASE), diff --git a/test/regress_http.c b/test/regress_http.c index 8f30b57b5a..4493907163 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -66,6 +66,8 @@ #include "regress.h" #include "regress_testutils.h" +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + /* set if a test needs to call loopexit on a base */ static struct event_base *exit_base; @@ -230,6 +232,8 @@ evbuffer_datacmp(struct evbuffer *buf, const char *s) return -1; d = evbuffer_pullup(buf, s_sz); + if (!d) + d = (unsigned char *)""; if ((r = memcmp(d, s, s_sz))) return r; @@ -457,9 +461,9 @@ http_chunked_cb(struct evhttp_request *req, void *arg) } static struct bufferevent * -create_bev(struct event_base *base, evutil_socket_t fd, int ssl_mask) +create_bev(struct event_base *base, evutil_socket_t fd, int ssl_mask, int flags_) { - int flags = BEV_OPT_DEFER_CALLBACKS; + int flags = BEV_OPT_DEFER_CALLBACKS | flags_; struct bufferevent *bev = NULL; if (!ssl_mask) { @@ -522,7 +526,7 @@ http_basic_test_impl(void *arg, int ssl, const char *request_line) fd = http_connect("127.0.0.1", port); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_readcb, http_half_writecb, http_errorcb, data->base); out = bufferevent_get_output(bev); @@ -538,12 +542,11 @@ http_basic_test_impl(void *arg, int ssl, const char *request_line) /* connect to the second port */ bufferevent_free(bev); - evutil_closesocket(fd); fd = http_connect("127.0.0.1", port2); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); out = bufferevent_get_output(bev); @@ -560,12 +563,11 @@ http_basic_test_impl(void *arg, int ssl, const char *request_line) /* Connect to the second port again. This time, send an absolute uri. */ bufferevent_free(bev); - evutil_closesocket(fd); fd = http_connect("127.0.0.1", port2); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base); @@ -1283,6 +1285,7 @@ http_autofree_connection_test(void *arg) struct evhttp_connection *evcon = NULL; struct evhttp_request *req[2] = { NULL }; struct evhttp *http = http_setup(&port, data->base, 0); + size_t i; test_ok = 0; @@ -1297,19 +1300,14 @@ http_autofree_connection_test(void *arg) req[1] = evhttp_request_new(http_request_empty_done, data->base); /* Add the information that we care about */ - evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Host", "somehost"); - evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Connection", "close"); - evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Empty", "itis"); - evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Host", "somehost"); - evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Connection", "close"); - evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Empty", "itis"); + for (i = 0; i < ARRAY_SIZE(req); ++i) { + evhttp_add_header(evhttp_request_get_output_headers(req[i]), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req[i]), "Connection", "close"); + evhttp_add_header(evhttp_request_get_output_headers(req[i]), "Empty", "itis"); - /* We give ownership of the request to the connection */ - if (evhttp_make_request(evcon, req[0], EVHTTP_REQ_GET, "/test") == -1) { - tt_abort_msg("couldn't make request"); - } - if (evhttp_make_request(evcon, req[1], EVHTTP_REQ_GET, "/test") == -1) { - tt_abort_msg("couldn't make request"); + if (evhttp_make_request(evcon, req[i], EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } } /* @@ -1320,7 +1318,8 @@ http_autofree_connection_test(void *arg) evhttp_connection_free_on_completion(evcon); evcon = NULL; - event_base_dispatch(data->base); + for (i = 0; i < ARRAY_SIZE(req); ++i) + event_base_dispatch(data->base); /* at this point, the http server should have no connection */ tt_assert(TAILQ_FIRST(&http->connections) == NULL); @@ -3119,7 +3118,7 @@ http_incomplete_test_(struct basic_test_data *data, int use_timeout, int ssl) tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, 0); bufferevent_setcb(bev, http_incomplete_readcb, http_incomplete_writecb, http_incomplete_errorcb, use_timeout ? NULL : &fd); @@ -3319,7 +3318,7 @@ static void http_chunk_out_test_impl(void *arg, int ssl) { struct basic_test_data *data = arg; - struct bufferevent *bev; + struct bufferevent *bev = NULL; evutil_socket_t fd; const char *http_request; ev_uint16_t port = 0; @@ -3336,7 +3335,7 @@ http_chunk_out_test_impl(void *arg, int ssl) tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ - bev = create_bev(data->base, fd, ssl); + bev = create_bev(data->base, fd, ssl, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, http_chunked_readcb, http_chunked_writecb, http_chunked_errorcb, data->base); @@ -3354,6 +3353,7 @@ http_chunk_out_test_impl(void *arg, int ssl) event_base_dispatch(data->base); bufferevent_free(bev); + bev = NULL; evutil_gettimeofday(&tv_end, NULL); evutil_timersub(&tv_end, &tv_start, &tv_end); @@ -3363,7 +3363,7 @@ http_chunk_out_test_impl(void *arg, int ssl) tt_int_op(test_ok, ==, 2); /* now try again with the regular connection object */ - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, BEV_OPT_CLOSE_ON_FREE); evcon = evhttp_connection_base_bufferevent_new( data->base, NULL, bev, "127.0.0.1", port); tt_assert(evcon); @@ -3371,14 +3371,13 @@ http_chunk_out_test_impl(void *arg, int ssl) /* make two requests to check the keepalive behavior */ for (i = 0; i < 2; i++) { test_ok = 0; - req = evhttp_request_new(http_chunked_request_done,data->base); + req = evhttp_request_new(http_chunked_request_done, data->base); /* Add the information that we care about */ evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); /* We give ownership of the request to the connection */ - if (evhttp_make_request(evcon, req, - EVHTTP_REQ_GET, "/chunked") == -1) { + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) { tt_abort_msg("Couldn't make request"); } @@ -3409,7 +3408,7 @@ http_stream_out_test_impl(void *arg, int ssl) test_ok = 0; exit_base = data->base; - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); evcon = evhttp_connection_base_bufferevent_new( data->base, NULL, bev, "127.0.0.1", port); tt_assert(evcon); @@ -3609,7 +3608,7 @@ http_connection_fail_test_impl(void *arg, int ssl) /* auto detect a port */ evhttp_free(http); - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); /* Pick an unroutable address. This administratively scoped multicast * address should do when working with TCP. */ evcon = evhttp_connection_base_bufferevent_new( @@ -3681,7 +3680,7 @@ http_simple_test_impl(void *arg, int ssl, int dirty, const char *uri) exit_base = data->base; test_ok = 0; - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); #ifdef EVENT__HAVE_OPENSSL bufferevent_openssl_set_allow_dirty_shutdown(bev, dirty); #endif @@ -3728,7 +3727,7 @@ http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base /* auto detect a port */ evhttp_free(http); - bev = create_bev(data->base, -1, ssl); + bev = create_bev(data->base, -1, ssl, 0); evcon = evhttp_connection_base_bufferevent_new(data->base, dns_base, bev, addr, hs.port); tt_assert(evcon); if (dns_base) @@ -4516,7 +4515,7 @@ http_write_during_read_test_impl(void *arg, int ssl) fd = http_connect("127.0.0.1", port); tt_assert(fd != EVUTIL_INVALID_SOCKET); - bev = create_bev(data->base, fd, 0); + bev = create_bev(data->base, fd, 0, 0); bufferevent_setcb(bev, NULL, NULL, NULL, data->base); bufferevent_disable(bev, EV_READ); @@ -4588,7 +4587,7 @@ static void http_run_bev_request(struct event_base *base, int port, tt_assert(fd != EVUTIL_INVALID_SOCKET); /* Stupid thing to send a request */ - bev = create_bev(base, fd, 0); + bev = create_bev(base, fd, 0, 0); bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, base); out = bufferevent_get_output(bev); diff --git a/test/regress_main.c b/test/regress_main.c index c9372825f4..266561214b 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -33,6 +33,14 @@ #include #endif +/* move_pthread_to_realtime_scheduling_class() */ +#ifdef EVENT__HAVE_MACH_MACH_H +#include +#endif +#ifdef EVENT__HAVE_MACH_MACH_TIME_H +#include +#endif + #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) @@ -81,10 +89,12 @@ #include "event2/event-config.h" #include "regress.h" +#include "regress_thread.h" #include "tinytest.h" #include "tinytest_macros.h" #include "../iocp-internal.h" #include "../event-internal.h" +#include "../evthread-internal.h" struct evutil_weakrand_state test_weakrand_state; @@ -186,6 +196,45 @@ ignore_log_cb(int s, const char *msg) { } +/** + * Put into the real time scheduling class for better timers latency. + * https://developer.apple.com/library/archive/technotes/tn2169/_index.html#//apple_ref/doc/uid/DTS40013172-CH1-TNTAG6000 + */ +#if defined(__APPLE__) +static void move_pthread_to_realtime_scheduling_class(pthread_t pthread) +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + + const uint64_t NANOS_PER_MSEC = 1000000ULL; + double clock2abs = + ((double)info.denom / (double)info.numer) * NANOS_PER_MSEC; + + thread_time_constraint_policy_data_t policy; + policy.period = 0; + policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work + policy.constraint = (uint32_t)(10 * clock2abs); + policy.preemptible = FALSE; + + int kr = thread_policy_set(pthread_mach_thread_np(pthread), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t)&policy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + if (kr != KERN_SUCCESS) { + mach_error("thread_policy_set:", kr); + exit(1); + } +} + +void thread_setup(THREAD_T pthread) +{ + move_pthread_to_realtime_scheduling_class(pthread); +} +#else /** \__APPLE__ */ +void thread_setup(THREAD_T pthread) {} +#endif /** \!__APPLE__ */ + + void * basic_test_setup(const struct testcase_t *testcase) { @@ -193,11 +242,19 @@ basic_test_setup(const struct testcase_t *testcase) evutil_socket_t spair[2] = { -1, -1 }; struct basic_test_data *data = NULL; + thread_setup(THREAD_SELF()); + #ifndef _WIN32 if (testcase->flags & TT_ENABLE_IOCP_FLAG) return (void*)TT_SKIP; #endif + if (testcase->flags & TT_ENABLE_DEBUG_MODE && + !libevent_tests_running_in_debug_mode) { + event_enable_debug_mode(); + libevent_tests_running_in_debug_mode = 1; + } + if (testcase->flags & TT_NEED_THREADS) { if (!(testcase->flags & TT_FORK)) return NULL; diff --git a/test/regress_ssl.c b/test/regress_ssl.c index 68c28114f7..37dc334dca 100644 --- a/test/regress_ssl.c +++ b/test/regress_ssl.c @@ -148,9 +148,9 @@ ssl_getcert(EVP_PKEY *key) X509_set_issuer_name(x509, name); X509_NAME_free(name); - X509_time_adj(X509_get_notBefore(x509), 0, &now); + X509_time_adj(X509_getm_notBefore(x509), 0, &now); now += 3600; - X509_time_adj(X509_get_notAfter(x509), 0, &now); + X509_time_adj(X509_getm_notAfter(x509), 0, &now); X509_set_pubkey(x509, key); tt_assert(0 != X509_sign(x509, key, EVP_sha1())); @@ -469,8 +469,8 @@ regress_bufferevent_openssl(void *arg) type = (enum regress_openssl_type)data->setup_data; if (type & REGRESS_OPENSSL_RENEGOTIATE) { - if (SSLeay() >= 0x10001000 && - SSLeay() < 0x1000104f) { + if (OPENSSL_VERSION_NUMBER >= 0x10001000 && + OPENSSL_VERSION_NUMBER < 0x1000104f) { /* 1.0.1 up to 1.0.1c has a bug where TLS1.1 and 1.2 * can't renegotiate with themselves. Disable. */ disable_tls_11_and_12 = 1; @@ -974,6 +974,7 @@ regress_bufferevent_openssl_wm(void *arg) tt_int_op(client.get, ==, client.limit); tt_int_op(server.get, ==, server.limit); + end: free(payload); evbuffer_free(client.data); @@ -981,6 +982,10 @@ regress_bufferevent_openssl_wm(void *arg) evconnlistener_free(listener); bufferevent_free(client.bev); bufferevent_free(server.bev); + + /* XXX: by some reason otherise there is a leak */ + if (!(type & REGRESS_OPENSSL_FILTER)) + event_base_loop(base, EVLOOP_ONCE); } struct testcase_t ssl_testcases[] = { diff --git a/test/regress_testutils.c b/test/regress_testutils.c index 959347ea71..b0ce7dbba2 100644 --- a/test/regress_testutils.c +++ b/test/regress_testutils.c @@ -110,7 +110,7 @@ regress_get_dnsserver(struct event_base *base, memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(*portnum); - my_addr.sin_addr.s_addr = htonl(0x7f000001UL); + my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { evutil_closesocket(sock); tt_abort_perror("bind"); diff --git a/test/regress_thread.h b/test/regress_thread.h index db0d8d19ab..a954fefa56 100644 --- a/test/regress_thread.h +++ b/test/regress_thread.h @@ -27,23 +27,30 @@ #ifndef REGRESS_THREAD_H_INCLUDED_ #define REGRESS_THREAD_H_INCLUDED_ -#ifdef EVENT__HAVE_PTHREADS +#if defined(_WIN32) /** _WIN32 */ +#define THREAD_T void * /* HANDLE */ +#define THREAD_FN unsigned __stdcall +#define THREAD_RETURN() return (0) +#define THREAD_SELF() GetCurrentThreadId() +#define THREAD_START(threadvar, fn, arg) do { \ + uintptr_t threadhandle = _beginthreadex(NULL,0,fn,(arg),0,NULL); \ + (threadvar) = (THREAD_T)threadhandle; \ + thread_setup(threadvar); \ +} while (0) +#define THREAD_JOIN(th) WaitForSingleObject(th, INFINITE) +#else /* !_WIN32 */ #include #define THREAD_T pthread_t #define THREAD_FN void * #define THREAD_RETURN() return (NULL) -#define THREAD_START(threadvar, fn, arg) \ - pthread_create(&(threadvar), NULL, fn, arg) +#define THREAD_SELF() pthread_self() +#define THREAD_START(threadvar, fn, arg) do { \ + if (!pthread_create(&(threadvar), NULL, fn, arg)) \ + thread_setup(threadvar); \ +} while (0) #define THREAD_JOIN(th) pthread_join(th, NULL) -#else -#define THREAD_T HANDLE -#define THREAD_FN unsigned __stdcall -#define THREAD_RETURN() return (0) -#define THREAD_START(threadvar, fn, arg) do { \ - uintptr_t threadhandle = _beginthreadex(NULL,0,fn,(arg),0,NULL); \ - (threadvar) = (HANDLE) threadhandle; \ - } while (0) -#define THREAD_JOIN(th) WaitForSingleObject(th, INFINITE) -#endif +#endif /* \!_WIN32 */ + +void thread_setup(THREAD_T pthread); #endif diff --git a/test/regress_util.c b/test/regress_util.c index 3da207039e..45caa2700a 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -211,6 +211,65 @@ regress_ipv6_parse(void *ptr) #endif } +static struct ipv6_entry_scope { + const char *addr; + ev_uint32_t res[4]; + unsigned scope; + enum entry_status status; +} ipv6_entries_scope[] = { + { "2001:DB8::", { 0x20010db8, 0, 0 }, 0, NORMAL }, + { "2001:DB8::%0", { 0x20010db8, 0, 0, 0 }, 0, NORMAL }, + { "2001:DB8::%1", { 0x20010db8, 0, 0, 0 }, 1, NORMAL }, + { "foobar.", { 0, 0, 0, 0 }, 0, BAD }, + { "2001:DB8::%does-not-exist", { 0, 0, 0, 0 }, 0, BAD }, + { NULL, { 0, 0, 0, 0, }, 0, BAD }, +}; +static void +regress_ipv6_parse_scope(void *ptr) +{ +#ifdef AF_INET6 + int i, j; + unsigned if_scope; + + for (i = 0; ipv6_entries_scope[i].addr; ++i) { + struct ipv6_entry_scope *ent = &ipv6_entries_scope[i]; + struct in6_addr in6; + int r; + r = evutil_inet_pton_scope(AF_INET6, ent->addr, &in6, + &if_scope); + if (r == 0) { + if (ent->status != BAD) + TT_FAIL(("%s did not parse, but it's a good address!", + ent->addr)); + continue; + } + if (ent->status == BAD) { + TT_FAIL(("%s parsed, but we expected an error", ent->addr)); + continue; + } + for (j = 0; j < 4; ++j) { + /* Can't use s6_addr32 here; some don't have it. */ + ev_uint32_t u = + ((ev_uint32_t)in6.s6_addr[j*4 ] << 24) | + ((ev_uint32_t)in6.s6_addr[j*4+1] << 16) | + ((ev_uint32_t)in6.s6_addr[j*4+2] << 8) | + ((ev_uint32_t)in6.s6_addr[j*4+3]); + if (u != ent->res[j]) { + TT_FAIL(("%s did not parse as expected.", ent->addr)); + continue; + } + } + if (if_scope != ent->scope) { + TT_FAIL(("%s did not parse as expected.", ent->addr)); + continue; + } + } +#else + TT_BLATHER(("Skipping IPv6 address parsing.")); +#endif +} + + static struct sa_port_ent { const char *parse; int safamily; @@ -925,6 +984,16 @@ test_evutil_rand(void *arg) ; } +static void +test_EVUTIL_IS_(void *arg) +{ + tt_int_op(EVUTIL_ISDIGIT_('0'), ==, 1); + tt_int_op(EVUTIL_ISDIGIT_('a'), ==, 0); + tt_int_op(EVUTIL_ISDIGIT_('\xff'), ==, 0); +end: + ; +} + static void test_evutil_getaddrinfo(void *arg) { @@ -1121,6 +1190,41 @@ test_evutil_getaddrinfo_live(void *arg) evutil_freeaddrinfo(ai); } +static void +test_evutil_getaddrinfo_AI_ADDRCONFIG(void *arg) +{ + struct evutil_addrinfo *ai = NULL; + struct evutil_addrinfo hints; + int r; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; + + /* IPv4 */ + r = evutil_getaddrinfo("127.0.0.1", "80", &hints, &ai); + tt_int_op(r, ==, 0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "127.0.0.1:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + + /* IPv6 */ + r = evutil_getaddrinfo("::1", "80", &hints, &ai); + tt_int_op(r, ==, 0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "[::1]:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + +end: + if (ai) + evutil_freeaddrinfo(ai); +} + #ifdef _WIN32 static void test_evutil_loadsyslib(void *arg) @@ -1539,6 +1643,7 @@ test_evutil_v6addr_is_local(void *arg) struct testcase_t util_testcases[] = { { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL }, { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL }, + { "ipv6_parse_scope", regress_ipv6_parse_scope, 0, NULL, NULL }, { "sockaddr_port_parse", regress_sockaddr_port_parse, 0, NULL, NULL }, { "sockaddr_port_format", regress_sockaddr_port_format, 0, NULL, NULL }, { "sockaddr_predicates", test_evutil_sockaddr_predicates, 0,NULL,NULL }, @@ -1551,8 +1656,10 @@ struct testcase_t util_testcases[] = { { "upcast", test_evutil_upcast, 0, NULL, NULL }, { "integers", test_evutil_integers, 0, NULL, NULL }, { "rand", test_evutil_rand, TT_FORK, NULL, NULL }, + { "EVUTIL_IS_", test_EVUTIL_IS_, 0, NULL, NULL }, { "getaddrinfo", test_evutil_getaddrinfo, TT_FORK, NULL, NULL }, { "getaddrinfo_live", test_evutil_getaddrinfo_live, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, + { "getaddrinfo_AI_ADDRCONFIG", test_evutil_getaddrinfo_AI_ADDRCONFIG, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, #ifdef _WIN32 { "loadsyslib", test_evutil_loadsyslib, TT_FORK, NULL, NULL }, #endif diff --git a/test/test-changelist.c b/test/test-changelist.c index 6e2466d5a5..fd1a17f5bd 100644 --- a/test/test-changelist.c +++ b/test/test-changelist.c @@ -182,11 +182,11 @@ main(int argc, char **argv) if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) return (1); - /* Initalize the event library */ + /* Initialize the event library */ if (!(base = event_base_new())) return (1); - /* Initalize a timeout to terminate the test */ + /* Initialize a timeout to terminate the test */ timeout = evtimer_new(base,timeout_cb,&timeout); /* and watch for writability on one end of the pipe */ ev = event_new(base,pair[1],EV_WRITE | EV_PERSIST, write_cb, &ev); diff --git a/test/test-closed.c b/test/test-closed.c index 1dd988592d..9e6050408d 100644 --- a/test/test-closed.c +++ b/test/test-closed.c @@ -104,6 +104,7 @@ main(int argc, char **argv) event_base_dispatch(base); /* Finalize library */ + event_free(ev); event_base_free(base); return 0; } diff --git a/test/test-eof.c b/test/test-eof.c index 284ead78ae..de2fd88b99 100644 --- a/test/test-eof.c +++ b/test/test-eof.c @@ -102,10 +102,10 @@ main(int argc, char **argv) return (1); shutdown(pair[0], EVUTIL_SHUT_WR); - /* Initalize the event library */ + /* Initialize the event library */ event_init(); - /* Initalize one event */ + /* Initialize one event */ event_set(&ev, pair[1], EV_READ | EV_TIMEOUT, read_cb, &ev); event_add(&ev, &timeout); diff --git a/test/test-init.c b/test/test-init.c index 92fbc6b146..aea49ee942 100644 --- a/test/test-init.c +++ b/test/test-init.c @@ -57,7 +57,7 @@ main(int argc, char **argv) (void) WSAStartup(wVersionRequested, &wsaData); #endif - /* Initalize the event library */ + /* Initialize the event library */ event_init(); return (0); diff --git a/test/test-ratelim.c b/test/test-ratelim.c index 9ee989bd82..34112e39e1 100644 --- a/test/test-ratelim.c +++ b/test/test-ratelim.c @@ -50,6 +50,10 @@ #include "event2/listener.h" #include "event2/thread.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + static struct evutil_weakrand_state weakrand_state; static int cfg_verbose = 0; @@ -85,6 +89,18 @@ struct client_state { }; static const struct timeval *ms100_common=NULL; +/* Timers bias for slow CPUs, affects: + * - cfg_connlimit_tolerance (--check-connlimit) + * - cfg_grouplimit_tolerance (--check-grouplimit) + * - cfg_stddev_tolerance (--check-stddev) + */ +static int timer_bias_events; +static struct timeval timer_bias_start; +double timer_bias_spend; +/* Real cost is less (approximately ~5 usec), + * this macros adjusted to make the bias less */ +#define TIMER_MAX_COST_USEC 10 + /* info from check_bucket_levels_cb */ static int total_n_bev_checks = 0; static ev_int64_t total_rbucket_level=0; @@ -244,6 +260,64 @@ group_drain_cb(evutil_socket_t fd, short events, void *arg) bufferevent_rate_limit_group_decrement_write(ratelim_group, cfg_group_drain); } +static void +timer_bias_cb(evutil_socket_t fd, short events, void *arg) +{ + struct event *event = arg; + struct timeval end; + struct timeval diff; + + /** XXX: use rdtsc? (portability issues?) */ + evutil_gettimeofday(&end, NULL); + evutil_timersub(&end, &timer_bias_start, &diff); + timer_bias_spend += diff.tv_sec + diff.tv_usec * 1e6; + timer_bias_start = end; + + if (++timer_bias_events == 100) + event_del(event); +} +static double +timer_bias_calculate(void) +{ + struct event_config *cfg = NULL; + struct event_base *base = NULL; + struct event *timer = NULL; + struct timeval tv = { 0, 1 }; + int done = 0; + + cfg = event_config_new(); + if (!cfg) + goto err; + if (event_config_set_flag(cfg, EVENT_BASE_FLAG_PRECISE_TIMER)) + goto err; + base = event_base_new_with_config(cfg); + if (!base) + goto err; + + timer = event_new(base, -1, EV_PERSIST, timer_bias_cb, event_self_cbarg()); + if (!timer || event_add(timer, &tv)) { + goto err; + } + + evutil_gettimeofday(&timer_bias_start, NULL); + event_base_dispatch(base); + done = 1; + +err: + if (cfg) + event_config_free(cfg); + if (timer) + event_free(timer); + if (base) + event_base_free(base); + + if (done) + return MIN(timer_bias_spend / 1e6 / timer_bias_events / TIMER_MAX_COST_USEC, 5); + + fprintf(stderr, "Couldn't create event for CPU cycle counter bias\n"); + return -1; +} + static int test_ratelimiting(void) { @@ -266,6 +340,7 @@ test_ratelimiting(void) struct event_config *base_cfg; struct event *periodic_level_check; struct event *group_drain_event=NULL; + double timer_bias; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -275,6 +350,16 @@ test_ratelimiting(void) if (0) event_enable_debug_mode(); + timer_bias = timer_bias_calculate(); + if (timer_bias > 1) { + fprintf(stderr, "CPU is slow, timers bias is %f\n", timer_bias); + cfg_connlimit_tolerance *= timer_bias; + cfg_grouplimit_tolerance *= timer_bias; + cfg_stddev_tolerance *= timer_bias; + } else { + printf("CPU is fast enough, timers bias is %f\n", timer_bias); + } + base_cfg = event_config_new(); #ifdef _WIN32 @@ -376,7 +461,7 @@ test_ratelimiting(void) ratelim_group = NULL; /* So no more responders get added */ event_free(periodic_level_check); if (group_drain_event) - event_del(group_drain_event); + event_free(group_drain_event); for (i = 0; i < cfg_n_connections; ++i) { bufferevent_free(bevs[i]); diff --git a/test/test-time.c b/test/test-time.c index c4d031e72d..a8b3846263 100644 --- a/test/test-time.c +++ b/test/test-time.c @@ -81,8 +81,10 @@ time_cb(evutil_socket_t fd, short event, void *arg) int main(int argc, char **argv) { + struct event_base *base; struct timeval tv; int i; + #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; @@ -94,23 +96,28 @@ main(int argc, char **argv) evutil_weakrand_seed_(&weakrand_state, 0); - /* Initalize the event library */ - event_init(); + if (getenv("EVENT_DEBUG_LOGGING_ALL")) { + event_enable_debug_logging(EVENT_DBG_ALL); + } - for (i = 0; i < NEVENT; i++) { - ev[i] = malloc(sizeof(struct event)); + base = event_base_new(); - /* Initalize one event */ - evtimer_set(ev[i], time_cb, ev[i]); + for (i = 0; i < NEVENT; i++) { + ev[i] = evtimer_new(base, time_cb, event_self_cbarg()); tv.tv_sec = 0; tv.tv_usec = rand_int(50000); evtimer_add(ev[i], &tv); } - event_dispatch(); + i = event_base_dispatch(base); + printf("event_base_dispatch=%d, called=%d, EVENT=%d\n", + i, called, NEVENT); - printf("%d, %d\n", called, NEVENT); - return (called < NEVENT); + if (i == 1 && called >= NEVENT) { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } } diff --git a/test/test-weof.c b/test/test-weof.c index 52c7afbd65..68e7cd4579 100644 --- a/test/test-weof.c +++ b/test/test-weof.c @@ -99,10 +99,10 @@ main(int argc, char **argv) if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) return (1); - /* Initalize the event library */ + /* Initialize the event library */ event_init(); - /* Initalize one event */ + /* Initialize one event */ event_set(&ev, pair[1], EV_WRITE, write_cb, &ev); event_add(&ev, NULL); diff --git a/test/tinytest.c b/test/tinytest.c index a94fb9d483..85dfe74a72 100644 --- a/test/tinytest.c +++ b/test/tinytest.c @@ -60,12 +60,8 @@ #include "tinytest_macros.h" #define LONGEST_TEST_NAME 16384 - -#ifndef _WIN32 #define DEFAULT_TESTCASE_TIMEOUT 30U -#else -#define DEFAULT_TESTCASE_TIMEOUT 0U -#endif +#define MAGIC_EXITCODE 42 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ static int n_ok = 0; /**< Number of tests that have passed */ @@ -86,33 +82,73 @@ const char *cur_test_prefix = NULL; /**< prefix of the current test group */ /** Name of the current test, if we haven't logged is yet. Used for --quiet */ const char *cur_test_name = NULL; +static void usage(struct testgroup_t *groups, int list_groups) + __attribute__((noreturn)); +static int process_test_option(struct testgroup_t *groups, const char *test); + #ifdef _WIN32 /* Copy of argv[0] for win32. */ static char commandname[MAX_PATH+1]; -#endif -static void usage(struct testgroup_t *groups, int list_groups) - __attribute__((noreturn)); -static int process_test_option(struct testgroup_t *groups, const char *test); +struct timeout_thread_args { + const testcase_fn *fn; + void *env; +}; +static DWORD WINAPI +timeout_thread_proc_(LPVOID arg) +{ + struct timeout_thread_args *args = arg; + (*(args->fn))(args->env); + ExitThread(cur_test_outcome == FAIL ? 1 : 0); +} + +static enum outcome +testcase_run_in_thread_(const struct testcase_t *testcase, void *env) +{ + /* We will never run testcase in a new thread when the + timeout is set to zero */ + assert(opt_timeout); + DWORD ret, tid; + HANDLE handle; + struct timeout_thread_args args = { + &(testcase->fn), + env + }; + + handle =CreateThread(NULL, 0, timeout_thread_proc_, + (LPVOID)&args, 0, &tid); + ret = WaitForSingleObject(handle, opt_timeout * 1000U); + if (ret == WAIT_OBJECT_0) { + ret = 0; + if (!GetExitCodeThread(handle, &ret)) { + printf("GetExitCodeThread failed\n"); + ret = 1; + } + } else if (ret == WAIT_TIMEOUT) { + printf("timeout\n"); + } else { + printf("Wait failed\n"); + } + CloseHandle(handle); + if (ret == 0) + return OK; + else if (ret == MAGIC_EXITCODE) + return SKIP; + else + return FAIL; +} +#else static unsigned int testcase_set_timeout_(void) { - if (!opt_timeout) - return 0; -#ifndef _WIN32 return alarm(opt_timeout); -#else - /** TODO: win32 support */ - fprintf(stderr, "You cannot set alarm on windows\n"); - exit(1); -#endif } + static unsigned int testcase_reset_timeout_(void) { -#ifndef _WIN32 return alarm(0); -#endif } +#endif static enum outcome testcase_run_bare_(const struct testcase_t *testcase) @@ -129,9 +165,17 @@ testcase_run_bare_(const struct testcase_t *testcase) cur_test_outcome = OK; { - testcase_set_timeout_(); - testcase->fn(env); - testcase_reset_timeout_(); + if (opt_timeout) { +#ifdef _WIN32 + cur_test_outcome = testcase_run_in_thread_(testcase, env); +#else + testcase_set_timeout_(); + testcase->fn(env); + testcase_reset_timeout_(); +#endif + } else { + testcase->fn(env); + } } outcome = cur_test_outcome; @@ -143,7 +187,6 @@ testcase_run_bare_(const struct testcase_t *testcase) return outcome; } -#define MAGIC_EXITCODE 42 #ifndef NO_FORKING @@ -164,7 +207,7 @@ testcase_run_forked_(const struct testgroup_t *group, char buffer[LONGEST_TEST_NAME+256]; STARTUPINFOA si; PROCESS_INFORMATION info; - DWORD exitcode; + DWORD ret; if (!in_tinytest_main) { printf("\nERROR. On Windows, testcase_run_forked_ must be" @@ -174,7 +217,7 @@ testcase_run_forked_(const struct testgroup_t *group, if (opt_verbosity>0) printf("[forking] "); - snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", + snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s", commandname, verbosity_flag, group->prefix, testcase->name); memset(&si, 0, sizeof(si)); @@ -185,15 +228,23 @@ testcase_run_forked_(const struct testgroup_t *group, 0, NULL, NULL, &si, &info); if (!ok) { printf("CreateProcess failed!\n"); - return 0; + return FAIL; + } + ret = WaitForSingleObject(info.hProcess, + (opt_timeout ? opt_timeout * 1000U : INFINITE)); + + if (ret == WAIT_OBJECT_0) { + GetExitCodeProcess(info.hProcess, &ret); + } else if (ret == WAIT_TIMEOUT) { + printf("timeout\n"); + } else { + printf("Wait failed\n"); } - WaitForSingleObject(info.hProcess, INFINITE); - GetExitCodeProcess(info.hProcess, &exitcode); CloseHandle(info.hProcess); CloseHandle(info.hThread); - if (exitcode == 0) + if (ret == 0) return OK; - else if (exitcode == MAGIC_EXITCODE) + else if (ret == MAGIC_EXITCODE) return SKIP; else return FAIL; @@ -228,7 +279,7 @@ testcase_run_forked_(const struct testgroup_t *group, return FAIL; /* unreachable */ } else { /* parent */ - int status, r; + int status, r, exitcode; char b[1]; /* Close this now, so that if the other side closes it, * our read fails. */ @@ -236,12 +287,20 @@ testcase_run_forked_(const struct testgroup_t *group, r = (int)read(outcome_pipe[0], b, 1); if (r == 0) { printf("[Lost connection!] "); - return 0; + return FAIL; } else if (r != 1) { perror("read outcome from pipe"); } waitpid(pid, &status, 0); + exitcode = WEXITSTATUS(status); close(outcome_pipe[0]); + if (opt_verbosity>1) + printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status); + if (exitcode != 0) + { + printf("[atexit failure!] "); + return FAIL; + } return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL); } #endif @@ -520,7 +579,7 @@ tinytest_set_test_failed_(void) printf("%s%s: ", cur_test_prefix, cur_test_name); cur_test_name = NULL; } - cur_test_outcome = 0; + cur_test_outcome = FAIL; } void diff --git a/test/tinytest_macros.h b/test/tinytest_macros.h index e34e74ec23..e01f5d56cb 100644 --- a/test/tinytest_macros.h +++ b/test/tinytest_macros.h @@ -113,8 +113,8 @@ #define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \ setup_block,cleanup_block,die_on_fail) \ TT_STMT_BEGIN \ - type val1_ = (a); \ - type val2_ = (b); \ + type val1_ = (type)(a); \ + type val2_ = (type)(b); \ int tt_status_ = (test); \ if (!tt_status_ || tinytest_get_verbosity_()>1) { \ printf_type print_; \ diff --git a/win32select.c b/win32select.c index 0ddfe4b499..d005b587d4 100644 --- a/win32select.c +++ b/win32select.c @@ -352,7 +352,6 @@ win32_dispatch(struct event_base *base, struct timeval *tv) } } if (win32op->writeset_out->fd_count) { - SOCKET s; i = evutil_weakrand_range_(&base->weakrand_seed, win32op->writeset_out->fd_count); for (j=0; jwriteset_out->fd_count; ++j) {