mirror of
https://github.com/haproxy/haproxy.git
synced 2026-06-13 19:00:25 -04:00
Compare commits
No commits in common. "master" and "v3.2-dev16" have entirely different histories.
master
...
v3.2-dev16
959 changed files with 27742 additions and 74287 deletions
18
.cirrus.yml
Normal file
18
.cirrus.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
FreeBSD_task:
|
||||
freebsd_instance:
|
||||
matrix:
|
||||
image_family: freebsd-14-2
|
||||
only_if: $CIRRUS_BRANCH =~ 'master|next'
|
||||
install_script:
|
||||
- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2
|
||||
script:
|
||||
- sudo sysctl kern.corefile=/tmp/%N.%P.core
|
||||
- sudo sysctl kern.sugid_coredump=1
|
||||
- scripts/build-vtest.sh
|
||||
- gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
|
||||
- ./haproxy -vv
|
||||
- ldd haproxy
|
||||
test_script:
|
||||
- env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel
|
||||
on_failure:
|
||||
debug_script: (for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done && ls /tmp/haproxy.*.core && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core)
|
||||
49
.github/actions/setup-vtest/action.yml
vendored
49
.github/actions/setup-vtest/action.yml
vendored
|
|
@ -1,49 +0,0 @@
|
|||
name: 'setup VTest'
|
||||
description: 'ssss'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
- name: Setup coredumps
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
shell: sh
|
||||
run: |
|
||||
sudo mkdir -p /tmp/core
|
||||
sudo sysctl fs.suid_dumpable=1
|
||||
sudo sysctl kernel.core_pattern=/tmp/core/core.%h.%e.%t
|
||||
|
||||
- name: Setup ulimit for core dumps
|
||||
shell: sh
|
||||
run: |
|
||||
# This is required for macOS which does not actually allow to increase
|
||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||
ulimit -n 65536
|
||||
ulimit -c unlimited
|
||||
|
||||
- name: Get VTest latest commit SHA
|
||||
id: vtest-sha
|
||||
shell: sh
|
||||
run: |
|
||||
echo "sha=$(git ls-remote https://code.vinyl-cache.org/vtest/VTest2 HEAD | cut -f1)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache VTest
|
||||
id: cache-vtest
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/vtest
|
||||
key: vtest-${{ runner.os }}-${{ runner.arch }}-${{ steps.vtest-sha.outputs.sha }}
|
||||
|
||||
- name: Install VTest
|
||||
if: ${{ steps.cache-vtest.outputs.cache-hit != 'true' }}
|
||||
shell: sh
|
||||
run: |
|
||||
DESTDIR=${{ github.workspace }}/vtest scripts/build-vtest.sh
|
||||
|
||||
- name: Install problem matcher for VTest
|
||||
shell: sh
|
||||
# This allows one to more easily see which tests fail.
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
|
||||
|
||||
|
||||
2
.github/h2spec.config
vendored
2
.github/h2spec.config
vendored
|
|
@ -19,7 +19,7 @@ defaults
|
|||
|
||||
frontend h2
|
||||
mode http
|
||||
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/certs/common.pem alpn h2,http/1.1
|
||||
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/common.pem alpn h2,http/1.1
|
||||
default_backend h2b
|
||||
|
||||
backend h2b
|
||||
|
|
|
|||
210
.github/matrix.py
vendored
210
.github/matrix.py
vendored
|
|
@ -12,7 +12,6 @@ import functools
|
|||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from os import environ
|
||||
from packaging import version
|
||||
|
|
@ -20,10 +19,9 @@ from packaging import version
|
|||
#
|
||||
# this CI is used for both development and stable branches of HAProxy
|
||||
#
|
||||
# naming convention used, if branch/tag name matches:
|
||||
# naming convention used, if branch name matches:
|
||||
#
|
||||
# "haproxy-" - stable branches
|
||||
# "vX.Y.Z" - release tags
|
||||
# otherwise - development branch (i.e. "latest" ssl variants, "latest" github images)
|
||||
#
|
||||
|
||||
|
|
@ -34,24 +32,13 @@ def get_all_github_tags(url):
|
|||
headers = {}
|
||||
if environ.get("GITHUB_TOKEN") is not None:
|
||||
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
||||
all_tags = []
|
||||
page = 1
|
||||
sep = "&" if "?" in url else "?"
|
||||
while True:
|
||||
paginated_url = "{}{}per_page=100&page={}".format(url, sep, page)
|
||||
request = urllib.request.Request(paginated_url, headers=headers)
|
||||
try:
|
||||
response = urllib.request.urlopen(request)
|
||||
except urllib.error.URLError:
|
||||
return all_tags if all_tags else None
|
||||
tags = json.loads(response.read().decode("utf-8"))
|
||||
if not tags:
|
||||
break
|
||||
all_tags.extend([tag['name'] for tag in tags])
|
||||
if len(tags) < 100:
|
||||
break
|
||||
page += 1
|
||||
return all_tags if all_tags else None
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
try:
|
||||
tags = urllib.request.urlopen(request)
|
||||
except:
|
||||
return None
|
||||
tags = json.loads(tags.read().decode("utf-8"))
|
||||
return [tag['name'] for tag in tags]
|
||||
|
||||
@functools.lru_cache(5)
|
||||
def determine_latest_openssl(ssl):
|
||||
|
|
@ -69,7 +56,7 @@ def aws_lc_version_string_to_num(version_string):
|
|||
return tuple(map(int, version_string[1:].split('.')))
|
||||
|
||||
def aws_lc_version_valid(version_string):
|
||||
return re.match(r'^v[0-9]+(\.[0-9]+)*$', version_string)
|
||||
return re.match('^v[0-9]+(\.[0-9]+)*$', version_string)
|
||||
|
||||
@functools.lru_cache(5)
|
||||
def determine_latest_aws_lc(ssl):
|
||||
|
|
@ -77,8 +64,6 @@ def determine_latest_aws_lc(ssl):
|
|||
if not tags:
|
||||
return "AWS_LC_VERSION=failed_to_detect"
|
||||
valid_tags = list(filter(aws_lc_version_valid, tags))
|
||||
if not valid_tags:
|
||||
return "AWS_LC_VERSION=failed_to_detect"
|
||||
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
||||
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
||||
|
||||
|
|
@ -86,16 +71,15 @@ def aws_lc_fips_version_string_to_num(version_string):
|
|||
return tuple(map(int, version_string[12:].split('.')))
|
||||
|
||||
def aws_lc_fips_version_valid(version_string):
|
||||
return re.match(r'^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
|
||||
return re.match('^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
|
||||
|
||||
@functools.lru_cache(5)
|
||||
def determine_latest_aws_lc_fips(ssl):
|
||||
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags")
|
||||
# the AWS-LC-FIPS tags are at the end of the list, so let's get a lot
|
||||
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags?per_page=200")
|
||||
if not tags:
|
||||
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
||||
if not valid_tags:
|
||||
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
||||
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
||||
|
||||
|
|
@ -103,7 +87,7 @@ def wolfssl_version_string_to_num(version_string):
|
|||
return tuple(map(int, version_string[1:].removesuffix('-stable').split('.')))
|
||||
|
||||
def wolfssl_version_valid(version_string):
|
||||
return re.match(r'^v[0-9]+(\.[0-9]+)*-stable$', version_string)
|
||||
return re.match('^v[0-9]+(\.[0-9]+)*-stable$', version_string)
|
||||
|
||||
@functools.lru_cache(5)
|
||||
def determine_latest_wolfssl(ssl):
|
||||
|
|
@ -136,20 +120,14 @@ def clean_compression(compression):
|
|||
def main(ref_name):
|
||||
print("Generating matrix for branch '{}'.".format(ref_name))
|
||||
|
||||
is_stable = "haproxy-" in ref_name or re.match(r'^v\d+\.\d+\.\d+$', ref_name)
|
||||
|
||||
matrix = []
|
||||
|
||||
# Ubuntu
|
||||
|
||||
if is_stable:
|
||||
os = "ubuntu-24.04" # stable branch
|
||||
os_arm = "ubuntu-24.04-arm" # stable branch
|
||||
os_i686 = "ubuntu-24.04" # stable branch
|
||||
if "haproxy-" in ref_name:
|
||||
os = "ubuntu-22.04" # stable branch
|
||||
else:
|
||||
os = "ubuntu-24.04" # development branch
|
||||
os_arm = "ubuntu-24.04-arm" # development branch
|
||||
os_i686 = "ubuntu-24.04" # development branch
|
||||
os = "ubuntu-24.04" # development branch
|
||||
|
||||
TARGET = "linux-glibc"
|
||||
for CC in ["gcc", "clang"]:
|
||||
|
|
@ -194,38 +172,36 @@ def main(ref_name):
|
|||
|
||||
# ASAN
|
||||
|
||||
for os_asan in [os, os_arm]:
|
||||
matrix.append(
|
||||
{
|
||||
"name": "{}, {}, ASAN, all features".format(os_asan, CC),
|
||||
"os": os_asan,
|
||||
"TARGET": TARGET,
|
||||
"CC": CC,
|
||||
"FLAGS": [
|
||||
"USE_OBSOLETE_LINKER=1",
|
||||
'ARCH_FLAGS="-g -fsanitize=address"',
|
||||
'OPT_CFLAGS="-O1"',
|
||||
"USE_ZLIB=1",
|
||||
"USE_OT=1",
|
||||
"DEBUG=-DDEBUG_STRICT=2",
|
||||
"OT_INC=${HOME}/opt-ot/include",
|
||||
"OT_LIB=${HOME}/opt-ot/lib",
|
||||
"OT_RUNPATH=1",
|
||||
"USE_PCRE2=1",
|
||||
"USE_PCRE2_JIT=1",
|
||||
"USE_LUA=1",
|
||||
"USE_OPENSSL=1",
|
||||
"USE_WURFL=1",
|
||||
"WURFL_INC=addons/wurfl/dummy",
|
||||
"WURFL_LIB=addons/wurfl/dummy",
|
||||
"USE_DEVICEATLAS=1",
|
||||
"DEVICEATLAS_SRC=addons/deviceatlas/dummy",
|
||||
"USE_PROMEX=1",
|
||||
"USE_51DEGREES=1",
|
||||
"51DEGREES_SRC=addons/51degrees/dummy/pattern",
|
||||
],
|
||||
}
|
||||
)
|
||||
matrix.append(
|
||||
{
|
||||
"name": "{}, {}, ASAN, all features".format(os, CC),
|
||||
"os": os,
|
||||
"TARGET": TARGET,
|
||||
"CC": CC,
|
||||
"FLAGS": [
|
||||
"USE_OBSOLETE_LINKER=1",
|
||||
'ARCH_FLAGS="-g -fsanitize=address"',
|
||||
'OPT_CFLAGS="-O1"',
|
||||
"USE_ZLIB=1",
|
||||
"USE_OT=1",
|
||||
"OT_INC=${HOME}/opt-ot/include",
|
||||
"OT_LIB=${HOME}/opt-ot/lib",
|
||||
"OT_RUNPATH=1",
|
||||
"USE_PCRE2=1",
|
||||
"USE_PCRE2_JIT=1",
|
||||
"USE_LUA=1",
|
||||
"USE_OPENSSL=1",
|
||||
"USE_WURFL=1",
|
||||
"WURFL_INC=addons/wurfl/dummy",
|
||||
"WURFL_LIB=addons/wurfl/dummy",
|
||||
"USE_DEVICEATLAS=1",
|
||||
"DEVICEATLAS_SRC=addons/deviceatlas/dummy",
|
||||
"USE_PROMEX=1",
|
||||
"USE_51DEGREES=1",
|
||||
"51DEGREES_SRC=addons/51degrees/dummy/pattern",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
for compression in ["USE_ZLIB=1"]:
|
||||
matrix.append(
|
||||
|
|
@ -242,14 +218,13 @@ def main(ref_name):
|
|||
"stock",
|
||||
"OPENSSL_VERSION=1.0.2u",
|
||||
"OPENSSL_VERSION=1.1.1s",
|
||||
"OPENSSL_VERSION=3.5.1",
|
||||
"QUICTLS_VERSION=OpenSSL_1_1_1w-quic1",
|
||||
"QUICTLS=yes",
|
||||
"WOLFSSL_VERSION=5.7.0",
|
||||
"AWS_LC_VERSION=1.39.0",
|
||||
# "BORINGSSL=yes",
|
||||
]
|
||||
|
||||
if not is_stable: # development branch
|
||||
if "haproxy-" not in ref_name: # development branch
|
||||
ssl_versions = ssl_versions + [
|
||||
"OPENSSL_VERSION=latest",
|
||||
"LIBRESSL_VERSION=latest",
|
||||
|
|
@ -257,7 +232,8 @@ def main(ref_name):
|
|||
|
||||
for ssl in ssl_versions:
|
||||
flags = ["USE_OPENSSL=1"]
|
||||
skipdup=0
|
||||
if ssl == "BORINGSSL=yes" or ssl == "QUICTLS=yes" or "LIBRESSL" in ssl or "WOLFSSL" in ssl or "AWS_LC" in ssl:
|
||||
flags.append("USE_QUIC=1")
|
||||
if "WOLFSSL" in ssl:
|
||||
flags.append("USE_OPENSSL_WOLFSSL=1")
|
||||
if "AWS_LC" in ssl:
|
||||
|
|
@ -267,23 +243,8 @@ def main(ref_name):
|
|||
flags.append("SSL_INC=${HOME}/opt/include")
|
||||
if "LIBRESSL" in ssl and "latest" in ssl:
|
||||
ssl = determine_latest_libressl(ssl)
|
||||
skipdup=1
|
||||
if "OPENSSL" in ssl and "latest" in ssl:
|
||||
ssl = determine_latest_openssl(ssl)
|
||||
skipdup=1
|
||||
|
||||
# if "latest" equals a version already in the list
|
||||
if ssl in ssl_versions and skipdup == 1:
|
||||
continue
|
||||
|
||||
openssl_supports_quic = False
|
||||
try:
|
||||
openssl_supports_quic = version.Version(ssl.split("OPENSSL_VERSION=",1)[1]) >= version.Version("3.5.0")
|
||||
except:
|
||||
pass
|
||||
|
||||
if ssl == "BORINGSSL=yes" or "QUICTLS" in ssl or "LIBRESSL" in ssl or "WOLFSSL" in ssl or "AWS_LC" in ssl or openssl_supports_quic:
|
||||
flags.append("USE_QUIC=1")
|
||||
|
||||
matrix.append(
|
||||
{
|
||||
|
|
@ -296,63 +257,24 @@ def main(ref_name):
|
|||
}
|
||||
)
|
||||
|
||||
# macOS on dev branches
|
||||
if not is_stable:
|
||||
os = "macos-26" # development branch
|
||||
# macOS
|
||||
|
||||
TARGET = "osx"
|
||||
for CC in ["clang"]:
|
||||
matrix.append(
|
||||
{
|
||||
"name": "{}, {}, no features".format(os, CC),
|
||||
"os": os,
|
||||
"TARGET": TARGET,
|
||||
"CC": CC,
|
||||
"FLAGS": [],
|
||||
}
|
||||
)
|
||||
if "haproxy-" in ref_name:
|
||||
os = "macos-13" # stable branch
|
||||
else:
|
||||
os = "macos-15" # development branch
|
||||
|
||||
# Alpine / musl
|
||||
|
||||
matrix.append(
|
||||
{
|
||||
"name": "Alpine+musl, gcc",
|
||||
"os": "ubuntu-latest",
|
||||
"container": {
|
||||
"image": "alpine:latest",
|
||||
"options": "--privileged --ulimit core=-1 --security-opt seccomp=unconfined",
|
||||
"volumes": ["/tmp/core:/tmp/core"],
|
||||
},
|
||||
"TARGET": "linux-musl",
|
||||
"CC": "gcc",
|
||||
"FLAGS": [
|
||||
"ARCH_FLAGS='-ggdb3'",
|
||||
"USE_LUA=1",
|
||||
"LUA_INC=/usr/include/lua5.3",
|
||||
"LUA_LIB=/usr/lib/lua5.3",
|
||||
"USE_OPENSSL=1",
|
||||
"USE_PCRE2=1",
|
||||
"USE_PCRE2_JIT=1",
|
||||
"USE_PROMEX=1",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# i686
|
||||
|
||||
matrix.append(
|
||||
{
|
||||
"name": "{}, i686-linux-gnu-gcc".format(os_i686),
|
||||
"os": os_i686,
|
||||
"TARGET": "linux-glibc",
|
||||
"CC": "i686-linux-gnu-gcc",
|
||||
"FLAGS": [
|
||||
"USE_OPENSSL=1",
|
||||
"USE_PCRE2=1",
|
||||
"USE_PCRE2_JIT=1",
|
||||
],
|
||||
}
|
||||
)
|
||||
TARGET = "osx"
|
||||
for CC in ["clang"]:
|
||||
matrix.append(
|
||||
{
|
||||
"name": "{}, {}, no features".format(os, CC),
|
||||
"os": os,
|
||||
"TARGET": TARGET,
|
||||
"CC": CC,
|
||||
"FLAGS": [],
|
||||
}
|
||||
)
|
||||
|
||||
# Print matrix
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
name: openssl master
|
||||
name: AWS-LC-FIPS
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * *"
|
||||
- cron: "0 0 * * 4"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -13,19 +13,33 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install VTest
|
||||
run: |
|
||||
scripts/build-vtest.sh
|
||||
- name: Determine latest AWS-LC release
|
||||
id: get_aws_lc_release
|
||||
run: |
|
||||
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))")
|
||||
echo $result
|
||||
echo "result=$result" >> $GITHUB_OUTPUT
|
||||
- name: Cache AWS-LC
|
||||
id: cache_aws_lc
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: '~/opt/'
|
||||
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||
sudo apt-get --no-install-recommends -y install socat gdb
|
||||
sudo apt-get --no-install-recommends -y install libpsl-dev
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
- name: Install OpenSSL master
|
||||
run: env OPENSSL_VERSION="git-master" GIT_TYPE="branch" scripts/build-ssl.sh
|
||||
sudo apt-get --no-install-recommends -y install socat gdb jose
|
||||
- name: Install AWS-LC
|
||||
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
|
||||
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
|
||||
- name: Compile HAProxy
|
||||
run: |
|
||||
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
||||
USE_QUIC=1 USE_OPENSSL=1 \
|
||||
USE_OPENSSL_AWSLC=1 USE_QUIC=1 \
|
||||
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
|
||||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
||||
|
|
@ -35,7 +49,7 @@ jobs:
|
|||
run: |
|
||||
ldd $(which haproxy)
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Install problem matcher for VTest
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest for HAProxy
|
||||
|
|
@ -46,7 +60,11 @@ jobs:
|
|||
ulimit -n 65536
|
||||
# allow to catch coredumps
|
||||
ulimit -c unlimited
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Run Unit tests
|
||||
id: unittests
|
||||
run: |
|
||||
make unit-tests
|
||||
- name: Show VTest results
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
|
|
@ -57,10 +75,6 @@ jobs:
|
|||
echo "::endgroup::"
|
||||
done
|
||||
exit 1
|
||||
- name: Run Unit tests
|
||||
id: unittests
|
||||
run: |
|
||||
make unit-tests
|
||||
- name: Show coredumps
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
|
|
@ -75,3 +89,13 @@ jobs:
|
|||
if [ "$failed" = true ]; then
|
||||
exit 1;
|
||||
fi
|
||||
- name: Show Unit-Tests results
|
||||
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
|
||||
run: |
|
||||
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
|
||||
printf "::group::"
|
||||
cat $result
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit 1
|
||||
|
||||
32
.github/workflows/aws-lc.yml
vendored
32
.github/workflows/aws-lc.yml
vendored
|
|
@ -9,28 +9,23 @@ permissions:
|
|||
contents: read
|
||||
|
||||
jobs:
|
||||
Test:
|
||||
name: ${{ matrix.name }}
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: AWS-LC
|
||||
command: "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))"
|
||||
- name: AWS-LC (FIPS)
|
||||
command: "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))"
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install VTest
|
||||
run: |
|
||||
scripts/build-vtest.sh
|
||||
- name: Determine latest AWS-LC release
|
||||
id: get_aws_lc_release
|
||||
run: |
|
||||
result=$(cd .github && python3 -c "${{ matrix.command }}")
|
||||
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))")
|
||||
echo $result
|
||||
echo "result=$result" >> $GITHUB_OUTPUT
|
||||
- name: Cache AWS-LC
|
||||
id: cache_aws_lc
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: '~/opt/'
|
||||
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
|
||||
|
|
@ -54,12 +49,18 @@ jobs:
|
|||
run: |
|
||||
ldd $(which haproxy)
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Install problem matcher for VTest
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest for HAProxy
|
||||
id: vtest
|
||||
run: |
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
# This is required for macOS which does not actually allow to increase
|
||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||
ulimit -n 65536
|
||||
# allow to catch coredumps
|
||||
ulimit -c unlimited
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Run Unit tests
|
||||
id: unittests
|
||||
run: |
|
||||
|
|
@ -97,3 +98,4 @@ jobs:
|
|||
echo "::endgroup::"
|
||||
done
|
||||
exit 1
|
||||
|
||||
|
|
|
|||
2
.github/workflows/codespell.yml
vendored
2
.github/workflows/codespell.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: codespell-project/codespell-problem-matcher@v1.2.0
|
||||
- uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
|
|
|
|||
4
.github/workflows/compliance.yml
vendored
4
.github/workflows/compliance.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install h2spec
|
||||
id: install-h2spec
|
||||
run: |
|
||||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
fi
|
||||
echo "::endgroup::"
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Launch HAProxy ${{ steps.show-version.outputs.version }}
|
||||
run: haproxy -f .github/h2spec.config -D
|
||||
- name: Run h2spec ${{ steps.install-h2spec.outputs.version }}
|
||||
|
|
|
|||
41
.github/workflows/contrib.yml
vendored
41
.github/workflows/contrib.yml
vendored
|
|
@ -7,30 +7,19 @@ permissions:
|
|||
contents: read
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-slim
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: admin/halog/
|
||||
targets:
|
||||
- admin/halog/halog
|
||||
- name: dev/flags/
|
||||
targets:
|
||||
- dev/flags/flags
|
||||
- name: dev/haring/
|
||||
targets:
|
||||
- dev/haring/haring
|
||||
- name: dev/hpack/
|
||||
targets:
|
||||
- dev/hpack/decode
|
||||
- dev/hpack/gen-enc
|
||||
- dev/hpack/gen-rht
|
||||
- name: dev/poll/
|
||||
targets:
|
||||
- dev/poll/poll
|
||||
fail-fast: false
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: make ${{ join(matrix.targets, ' ') }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile admin/halog/halog
|
||||
run: |
|
||||
make admin/halog/halog
|
||||
- name: Compile dev/flags/flags
|
||||
run: |
|
||||
make dev/flags/flags
|
||||
- name: Compile dev/poll/poll
|
||||
run: |
|
||||
make dev/poll/poll
|
||||
- name: Compile dev/hpack
|
||||
run: |
|
||||
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
||||
|
|
|
|||
6
.github/workflows/coverity.yml
vendored
6
.github/workflows/coverity.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
libsystemd-dev
|
||||
- name: Install QUICTLS
|
||||
run: |
|
||||
QUICTLS_VERSION=OpenSSL_1_1_1w-quic1 scripts/build-ssl.sh
|
||||
QUICTLS=yes scripts/build-ssl.sh
|
||||
- name: Download Coverity build tool
|
||||
run: |
|
||||
wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=Haproxy" -O coverity_tool.tar.gz
|
||||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
- name: Build with Coverity build tool
|
||||
run: |
|
||||
export PATH=`pwd`/coverity_tool/bin:$PATH
|
||||
cov-build --dir cov-int make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_QUIC=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern ADDLIB=\"-Wl,-rpath,$HOME/opt/lib/\" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include DEBUG+=-DDEBUG_STRICT=2 DEBUG+=-DDEBUG_USE_ABORT=1
|
||||
cov-build --dir cov-int make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_QUIC=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern ADDLIB=\"-Wl,-rpath,$HOME/opt/lib/\" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include DEBUG+=-DDEBUG_STRICT=1 DEBUG+=-DDEBUG_USE_ABORT=1
|
||||
- name: Submit build result to Coverity Scan
|
||||
run: |
|
||||
tar czvf cov.tar.gz cov-int
|
||||
|
|
|
|||
10
.github/workflows/cross-zoo.yml
vendored
10
.github/workflows/cross-zoo.yml
vendored
|
|
@ -5,7 +5,7 @@ name: Cross Compile
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * 1"
|
||||
- cron: "0 0 21 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -17,10 +17,6 @@ jobs:
|
|||
matrix:
|
||||
platform: [
|
||||
{
|
||||
arch: i686-linux-gnu,
|
||||
libs: libc6-dev-i386-cross,
|
||||
target: linux-x86
|
||||
}, {
|
||||
arch: aarch64-linux-gnu,
|
||||
libs: libc6-dev-arm64-cross,
|
||||
target: linux-aarch64
|
||||
|
|
@ -103,12 +99,12 @@ jobs:
|
|||
sudo apt-get -yq --force-yes install \
|
||||
gcc-${{ matrix.platform.arch }} \
|
||||
${{ matrix.platform.libs }}
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: install quictls
|
||||
run: |
|
||||
QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS_VERSION=openssl-3.1.7+quic scripts/build-ssl.sh
|
||||
QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS=yes scripts/build-ssl.sh
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
|
|
|||
42
.github/workflows/fedora-rawhide.yml
vendored
42
.github/workflows/fedora-rawhide.yml
vendored
|
|
@ -1,4 +1,4 @@
|
|||
name: Fedora/Rawhide/OpenSSL
|
||||
name: Fedora/Rawhide/QuicTLS
|
||||
|
||||
on:
|
||||
schedule:
|
||||
|
|
@ -13,28 +13,35 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
platform: [
|
||||
{ name: x64, cc: gcc, ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
||||
{ name: x64, cc: clang, ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
||||
{ name: x86, cc: gcc, ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" },
|
||||
{ name: x86, cc: clang, ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" }
|
||||
{ name: x64, cc: gcc, QUICTLS_EXTRA_ARGS: "", ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
||||
{ name: x64, cc: clang, QUICTLS_EXTRA_ARGS: "", ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
||||
{ name: x86, cc: gcc, QUICTLS_EXTRA_ARGS: "-m32 linux-generic32", ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" },
|
||||
{ name: x86, cc: clang, QUICTLS_EXTRA_ARGS: "-m32 linux-generic32", ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" }
|
||||
]
|
||||
fail-fast: false
|
||||
name: ${{ matrix.platform.cc }}.${{ matrix.platform.name }}
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
container:
|
||||
image: fedora:rawhide
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dnf -y install awk diffutils git zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang openssl-devel.x86_64 procps-ng
|
||||
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 libatomic.i686 openssl-devel.i686 pcre2-devel.i686
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
dnf -y install awk diffutils git pcre-devel zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang
|
||||
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 pcre-devel.i686 libatomic.i686
|
||||
- name: Install VTest
|
||||
run: scripts/build-vtest.sh
|
||||
- name: Install QuicTLS
|
||||
run: QUICTLS=yes QUICTLS_EXTRA_ARGS="${{ matrix.platform.QUICTLS_EXTRA_ARGS }}" scripts/build-ssl.sh
|
||||
- name: Build contrib tools
|
||||
run: |
|
||||
make admin/halog/halog
|
||||
make dev/flags/flags
|
||||
make dev/poll/poll
|
||||
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
||||
- name: Compile HAProxy with ${{ matrix.platform.cc }}
|
||||
run: |
|
||||
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_PROMEX=1 USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }}" ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
|
||||
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }} -Wl,-rpath,${HOME}/opt/lib" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
|
||||
make install
|
||||
- name: Show HAProxy version
|
||||
id: show-version
|
||||
|
|
@ -43,18 +50,11 @@ jobs:
|
|||
ldd $(command -v haproxy)
|
||||
echo "::endgroup::"
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
#
|
||||
# TODO: review this workaround later
|
||||
- name: relax crypto policies
|
||||
run: |
|
||||
dnf -y install crypto-policies-scripts
|
||||
echo LEGACY > /etc/crypto-policies/config
|
||||
update-crypto-policies
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
|
||||
id: vtest
|
||||
run: |
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Show VTest results
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
|
|
|
|||
38
.github/workflows/freebsd.yml
vendored
38
.github/workflows/freebsd.yml
vendored
|
|
@ -1,38 +0,0 @@
|
|||
name: FreeBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
clang:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- name: "Checkout repository"
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: "Build and test on FreeBSD"
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
release: "14.3"
|
||||
prepare: |
|
||||
pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2 python3
|
||||
run: |
|
||||
sysctl kern.corefile=/tmp/%N.%P.core
|
||||
sysctl kern.sugid_coredump=1
|
||||
scripts/build-vtest.sh
|
||||
gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
|
||||
./haproxy -vv
|
||||
ldd haproxy
|
||||
if ! env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel; then
|
||||
for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done
|
||||
ls /tmp/haproxy.*.core 2>/dev/null && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core
|
||||
exit 1
|
||||
fi
|
||||
9
.github/workflows/illumos.yml
vendored
9
.github/workflows/illumos.yml
vendored
|
|
@ -2,19 +2,18 @@ name: Illumos
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 1"
|
||||
- cron: "0 0 25 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
gcc:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: "Checkout repository"
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Build on VM"
|
||||
uses: vmactions/solaris-vm@v1
|
||||
|
|
|
|||
76
.github/workflows/musl.yml
vendored
Normal file
76
.github/workflows/musl.yml
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
name: alpine/musl
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
musl:
|
||||
name: gcc
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine:latest
|
||||
options: --privileged --ulimit core=-1 --security-opt seccomp=unconfined
|
||||
volumes:
|
||||
- /tmp/core:/tmp/core
|
||||
steps:
|
||||
- name: Setup coredumps
|
||||
run: |
|
||||
ulimit -c unlimited
|
||||
echo '/tmp/core/core.%h.%e.%t' > /proc/sys/kernel/core_pattern
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose
|
||||
- name: Install VTest
|
||||
run: scripts/build-vtest.sh
|
||||
- name: Build
|
||||
run: make -j$(nproc) TARGET=linux-musl DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" ARCH_FLAGS='-ggdb3' CC=cc V=1 USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 USE_OPENSSL=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_PROMEX=1
|
||||
- name: Show version
|
||||
run: ./haproxy -vv
|
||||
- name: Show linked libraries
|
||||
run: ldd haproxy
|
||||
- name: Install problem matcher for VTest
|
||||
# This allows one to more easily see which tests fail.
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest
|
||||
id: vtest
|
||||
run: make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Run Unit tests
|
||||
id: unittests
|
||||
run: |
|
||||
make unit-tests
|
||||
- name: Show coredumps
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
failed=false
|
||||
ls /tmp/core/
|
||||
for file in /tmp/core/core.*; do
|
||||
failed=true
|
||||
printf "::group::"
|
||||
gdb -ex 'thread apply all bt full' ./haproxy $file
|
||||
echo "::endgroup::"
|
||||
done
|
||||
if [ "$failed" = true ]; then
|
||||
exit 1;
|
||||
fi
|
||||
- name: Show results
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
for folder in /tmp/haregtests-*/vtc.*; do
|
||||
printf "::group::"
|
||||
cat $folder/INFO
|
||||
cat $folder/LOG
|
||||
echo "::endgroup::"
|
||||
done
|
||||
- name: Show Unit-Tests results
|
||||
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
|
||||
run: |
|
||||
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
|
||||
printf "::group::"
|
||||
cat $result
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit 1
|
||||
|
||||
7
.github/workflows/netbsd.yml
vendored
7
.github/workflows/netbsd.yml
vendored
|
|
@ -5,16 +5,15 @@ on:
|
|||
- cron: "0 0 25 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
gcc:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: "Checkout repository"
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Build on VM"
|
||||
uses: vmactions/netbsd-vm@v1
|
||||
|
|
|
|||
80
.github/workflows/openssl-ech.yml
vendored
80
.github/workflows/openssl-ech.yml
vendored
|
|
@ -1,80 +0,0 @@
|
|||
name: openssl ECH
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||
sudo apt-get --no-install-recommends -y install socat gdb
|
||||
sudo apt-get --no-install-recommends -y install libpsl-dev
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
- name: Install OpenSSL+ECH
|
||||
run: env OPENSSL_VERSION="git-feature/ech" GIT_TYPE="branch" scripts/build-ssl.sh
|
||||
- name: Install curl+ECH
|
||||
run: env SSL_LIB=${HOME}/opt/ scripts/build-curl.sh
|
||||
- name: Compile HAProxy
|
||||
run: |
|
||||
make -j$(nproc) CC=gcc TARGET=linux-glibc \
|
||||
USE_QUIC=1 USE_OPENSSL=1 USE_ECH=1 \
|
||||
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
|
||||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/" \
|
||||
ARCH_FLAGS="-ggdb3 -fsanitize=address"
|
||||
sudo make install
|
||||
- name: Show HAProxy version
|
||||
id: show-version
|
||||
run: |
|
||||
ldd $(which haproxy)
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
- name: Install problem matcher for VTest
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest for HAProxy
|
||||
id: vtest
|
||||
run: |
|
||||
# This is required for macOS which does not actually allow to increase
|
||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||
ulimit -n 65536
|
||||
# allow to catch coredumps
|
||||
ulimit -c unlimited
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Show VTest results
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
|
||||
printf "::group::"
|
||||
cat $folder/INFO
|
||||
cat $folder/LOG
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit 1
|
||||
- name: Run Unit tests
|
||||
id: unittests
|
||||
run: |
|
||||
make unit-tests
|
||||
- name: Show coredumps
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
failed=false
|
||||
shopt -s nullglob
|
||||
for file in /tmp/core.*; do
|
||||
failed=true
|
||||
printf "::group::"
|
||||
gdb -ex 'thread apply all bt full' ./haproxy $file
|
||||
echo "::endgroup::"
|
||||
done
|
||||
if [ "$failed" = true ]; then
|
||||
exit 1;
|
||||
fi
|
||||
34
.github/workflows/openssl-nodeprecated.yml
vendored
Normal file
34
.github/workflows/openssl-nodeprecated.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# special purpose CI: test against OpenSSL built in "no-deprecated" mode
|
||||
# let us run those builds weekly
|
||||
#
|
||||
# for example, OpenWRT uses such OpenSSL builds (those builds are smaller)
|
||||
#
|
||||
#
|
||||
# some details might be found at NL: https://www.mail-archive.com/haproxy@formilux.org/msg35759.html
|
||||
# GH: https://github.com/haproxy/haproxy/issues/367
|
||||
|
||||
name: openssl no-deprecated
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 4"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install VTest
|
||||
run: |
|
||||
scripts/build-vtest.sh
|
||||
- name: Compile HAProxy
|
||||
run: |
|
||||
make DEFINE="-DOPENSSL_API_COMPAT=0x10100000L -DOPENSSL_NO_DEPRECATED" -j3 CC=gcc ERR=1 TARGET=linux-glibc USE_OPENSSL=1
|
||||
- name: Run VTest
|
||||
run: |
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
96
.github/workflows/quic-interop-aws-lc.yml
vendored
96
.github/workflows/quic-interop-aws-lc.yml
vendored
|
|
@ -9,58 +9,96 @@ on:
|
|||
schedule:
|
||||
- cron: "0 0 * * 2"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
combined-build-and-run:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: https://github.com/haproxytech/haproxy-qns.git
|
||||
push: true
|
||||
build-args: |
|
||||
SSLLIB: AWS-LC
|
||||
tags: ghcr.io/${{ github.repository }}:aws-lc
|
||||
|
||||
- name: Cleanup registry
|
||||
uses: actions/delete-package-versions@v5
|
||||
with:
|
||||
owner: ${{ github.repository_owner }}
|
||||
package-name: 'haproxy'
|
||||
package-type: container
|
||||
min-versions-to-keep: 1
|
||||
delete-only-untagged-versions: 'true'
|
||||
|
||||
run:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
suite: [
|
||||
{ client: chrome, tests: "http3" },
|
||||
{ client: picoquic, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" },
|
||||
{ client: quic-go, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" },
|
||||
{ client: ngtcp2, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" }
|
||||
]
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.suite.client }}
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update Docker to the latest
|
||||
uses: docker/setup-docker-action@v4
|
||||
|
||||
- name: Build Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@v6
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
context: https://github.com/haproxytech/haproxy-qns.git
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
SSLLIB=AWS-LC
|
||||
tags: local:aws-lc
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install tshark
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install tshark
|
||||
|
||||
- name: Pull image
|
||||
run: |
|
||||
docker pull ghcr.io/${{ github.repository }}:aws-lc
|
||||
|
||||
- name: Run
|
||||
run: |
|
||||
git clone https://github.com/quic-interop/quic-interop-runner
|
||||
cd quic-interop-runner
|
||||
pip install -r requirements.txt --break-system-packages
|
||||
python run.py -j result.json -l logs-chrome -r haproxy=local:aws-lc -t "http3" -c chrome -s haproxy
|
||||
python run.py -j result.json -l logs-picoquic -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c picoquic -s haproxy
|
||||
python run.py -j result.json -l logs-quic-go -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c quic-go -s haproxy
|
||||
python run.py -j result.json -l logs-ngtcp2 -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c ngtcp2 -s haproxy
|
||||
python run.py -j result.json -l logs -r haproxy=ghcr.io/${{ github.repository }}:aws-lc -t ${{ matrix.suite.tests }} -c ${{ matrix.suite.client }} -s haproxy
|
||||
|
||||
- name: Delete succeeded logs
|
||||
if: ${{ failure() }}
|
||||
if: failure()
|
||||
run: |
|
||||
for client in chrome picoquic quic-go ngtcp2; do
|
||||
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
||||
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
||||
popd
|
||||
done
|
||||
cd quic-interop-runner/logs/haproxy_${{ matrix.suite.client }}
|
||||
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
||||
|
||||
- name: Logs upload
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs
|
||||
path: quic-interop-runner/logs*/
|
||||
name: logs-${{ matrix.suite.client }}
|
||||
path: quic-interop-runner/logs/
|
||||
retention-days: 6
|
||||
|
|
|
|||
92
.github/workflows/quic-interop-libressl.yml
vendored
92
.github/workflows/quic-interop-libressl.yml
vendored
|
|
@ -9,56 +9,94 @@ on:
|
|||
schedule:
|
||||
- cron: "0 0 * * 2"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
combined-build-and-run:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: https://github.com/haproxytech/haproxy-qns.git
|
||||
push: true
|
||||
build-args: |
|
||||
SSLLIB: LibreSSL
|
||||
tags: ghcr.io/${{ github.repository }}:libressl
|
||||
|
||||
- name: Cleanup registry
|
||||
uses: actions/delete-package-versions@v5
|
||||
with:
|
||||
owner: ${{ github.repository_owner }}
|
||||
package-name: 'haproxy'
|
||||
package-type: container
|
||||
min-versions-to-keep: 1
|
||||
delete-only-untagged-versions: 'true'
|
||||
|
||||
run:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
suite: [
|
||||
{ client: picoquic, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,v2" },
|
||||
{ client: quic-go, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,transferloss,transfercorruption,v2" }
|
||||
]
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.suite.client }}
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update Docker to the latest
|
||||
uses: docker/setup-docker-action@v4
|
||||
|
||||
- name: Build Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@v6
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
context: https://github.com/haproxytech/haproxy-qns.git
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
SSLLIB=LibreSSL
|
||||
tags: local:libressl
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install tshark
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install tshark
|
||||
|
||||
- name: Pull image
|
||||
run: |
|
||||
docker pull ghcr.io/${{ github.repository }}:libressl
|
||||
|
||||
- name: Run
|
||||
run: |
|
||||
git clone https://github.com/quic-interop/quic-interop-runner
|
||||
cd quic-interop-runner
|
||||
pip install -r requirements.txt --break-system-packages
|
||||
python run.py -j result.json -l logs-picoquic -r haproxy=local:libressl -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,v2" -c picoquic -s haproxy
|
||||
python run.py -j result.json -l logs-quic-go -r haproxy=local:libressl -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,transferloss,transfercorruption,v2" -c quic-go -s haproxy
|
||||
python run.py -j result.json -l logs -r haproxy=ghcr.io/${{ github.repository }}:libressl -t ${{ matrix.suite.tests }} -c ${{ matrix.suite.client }} -s haproxy
|
||||
|
||||
- name: Delete succeeded logs
|
||||
if: ${{ failure() }}
|
||||
if: failure()
|
||||
run: |
|
||||
for client in picoquic quic-go; do
|
||||
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
||||
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
||||
popd
|
||||
done
|
||||
cd quic-interop-runner/logs/haproxy_${{ matrix.suite.client }}
|
||||
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
||||
|
||||
- name: Logs upload
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs
|
||||
path: quic-interop-runner/logs*/
|
||||
name: logs-${{ matrix.suite.client }}
|
||||
path: quic-interop-runner/logs/
|
||||
retention-days: 6
|
||||
|
|
|
|||
19
.github/workflows/quictls.yml
vendored
19
.github/workflows/quictls.yml
vendored
|
|
@ -17,13 +17,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install VTest
|
||||
run: |
|
||||
scripts/build-vtest.sh
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||
sudo apt-get --no-install-recommends -y install socat gdb
|
||||
- name: Install QuicTLS
|
||||
run: env QUICTLS_VERSION=main QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
|
||||
run: env QUICTLS=yes QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
|
||||
- name: Compile HAProxy
|
||||
run: |
|
||||
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
||||
|
|
@ -38,12 +41,18 @@ jobs:
|
|||
run: |
|
||||
ldd $(which haproxy)
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Install problem matcher for VTest
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest for HAProxy
|
||||
id: vtest
|
||||
run: |
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
# This is required for macOS which does not actually allow to increase
|
||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||
ulimit -n 65536
|
||||
# allow to catch coredumps
|
||||
ulimit -c unlimited
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Show VTest results
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
|
|
|
|||
56
.github/workflows/vtest.yml
vendored
56
.github/workflows/vtest.yml
vendored
|
|
@ -19,11 +19,11 @@ jobs:
|
|||
# generated by .github/matrix.py.
|
||||
generate-matrix:
|
||||
name: Generate Build Matrix
|
||||
runs-on: ubuntu-slim
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Generate Build Matrix
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
@ -38,17 +38,22 @@ jobs:
|
|||
strategy:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
fail-fast: false
|
||||
container: ${{ matrix.container }}
|
||||
env:
|
||||
# Configure a short TMPDIR to prevent failures due to long unix socket
|
||||
# paths.
|
||||
TMPDIR: /tmp
|
||||
OT_CPP_VERSION: 1.6.0
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 100
|
||||
|
||||
- name: Setup coredumps
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||
run: |
|
||||
sudo sysctl -w fs.suid_dumpable=1
|
||||
sudo sysctl kernel.core_pattern=/tmp/core.%h.%e.%t
|
||||
|
||||
#
|
||||
# Github Action cache key cannot contain comma, so we calculate it based on job name
|
||||
#
|
||||
|
|
@ -58,9 +63,9 @@ jobs:
|
|||
echo "key=$(echo ${{ matrix.name }} | sha256sum | awk '{print $1}')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache SSL libs
|
||||
if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && !contains(matrix.ssl, 'QUICTLS') }}
|
||||
if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && matrix.ssl != 'QUICTLS=yes' }}
|
||||
id: cache_ssl
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: '~/opt/'
|
||||
key: ssl-${{ steps.generate-cache-key.outputs.key }}
|
||||
|
|
@ -68,34 +73,29 @@ jobs:
|
|||
- name: Cache OpenTracing
|
||||
if: ${{ contains(matrix.FLAGS, 'USE_OT=1') }}
|
||||
id: cache_ot
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: '~/opt-ot/'
|
||||
key: ${{ matrix.os }}-ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
|
||||
- name: Add i386 architecture
|
||||
if: ${{ matrix.CC == 'i686-linux-gnu-gcc' }}
|
||||
run: sudo dpkg --add-architecture i386
|
||||
key: ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
|
||||
- name: Install apt dependencies
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu-') && matrix.TARGET != 'linux-musl' }}
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||
run: |
|
||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||
sudo apt-get --no-install-recommends -y install \
|
||||
${{ case(contains(matrix.FLAGS, 'USE_LUA=1'), 'liblua5.4-dev', '') }} \
|
||||
${{ case(contains(matrix.FLAGS, 'USE_PCRE2=1'), 'libpcre2-dev', '') }} \
|
||||
${{ case(contains(matrix.ssl, 'BORINGSSL=yes'), 'ninja-build', '') }} \
|
||||
${{ case(matrix.CC == 'i686-linux-gnu-gcc', 'gcc-i686-linux-gnu libc6-dev-i386-cross libssl-dev:i386 libpcre2-dev:i386', '') }} \
|
||||
${{ contains(matrix.FLAGS, 'USE_LUA=1') && 'liblua5.4-dev' || '' }} \
|
||||
${{ contains(matrix.FLAGS, 'USE_PCRE2=1') && 'libpcre2-dev' || '' }} \
|
||||
${{ contains(matrix.ssl, 'BORINGSSL=yes') && 'ninja-build' || '' }} \
|
||||
socat \
|
||||
gdb \
|
||||
jose
|
||||
- name: Install apk dependencies
|
||||
if: ${{ matrix.TARGET == 'linux-musl' }}
|
||||
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose sudo
|
||||
- name: Install brew dependencies
|
||||
if: ${{ startsWith(matrix.os, 'macos-') }}
|
||||
run: |
|
||||
brew install socat
|
||||
brew install lua
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
- name: Install VTest
|
||||
run: |
|
||||
scripts/build-vtest.sh
|
||||
- name: Install SSL ${{ matrix.ssl }}
|
||||
if: ${{ matrix.ssl && matrix.ssl != 'stock' && steps.cache_ssl.outputs.cache-hit != 'true' }}
|
||||
run: env ${{ matrix.ssl }} scripts/build-ssl.sh
|
||||
|
|
@ -121,7 +121,7 @@ jobs:
|
|||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
||||
${{ join(matrix.FLAGS, ' ') }} \
|
||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
||||
sudo make install-bin
|
||||
sudo make install
|
||||
- name: Show HAProxy version
|
||||
id: show-version
|
||||
run: |
|
||||
|
|
@ -135,11 +135,18 @@ jobs:
|
|||
fi
|
||||
echo "::endgroup::"
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Install problem matcher for VTest
|
||||
# This allows one to more easily see which tests fail.
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
|
||||
id: vtest
|
||||
run: |
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
# This is required for macOS which does not actually allow to increase
|
||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||
ulimit -n 65536
|
||||
ulimit -c unlimited
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Show VTest results
|
||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
|
|
@ -167,7 +174,8 @@ jobs:
|
|||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||
run: |
|
||||
failed=false
|
||||
for file in /tmp/core/core.*; do
|
||||
shopt -s nullglob
|
||||
for file in /tmp/core.*; do
|
||||
failed=true
|
||||
printf "::group::"
|
||||
gdb -ex 'thread apply all bt full' ./haproxy $file
|
||||
|
|
|
|||
3
.github/workflows/windows.yml
vendored
3
.github/workflows/windows.yml
vendored
|
|
@ -18,7 +18,6 @@ jobs:
|
|||
msys2:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
|
@ -36,7 +35,7 @@ jobs:
|
|||
- USE_THREAD=1
|
||||
- USE_ZLIB=1
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
install: >-
|
||||
|
|
|
|||
19
.github/workflows/wolfssl.yml
vendored
19
.github/workflows/wolfssl.yml
vendored
|
|
@ -13,13 +13,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install VTest
|
||||
run: |
|
||||
scripts/build-vtest.sh
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||
sudo apt-get --no-install-recommends -y install socat gdb jose
|
||||
- name: Install WolfSSL
|
||||
run: env WOLFSSL_VERSION=git-master WOLFSSL_DEBUG=1 CFLAGS="-fsanitize=address -g" scripts/build-ssl.sh
|
||||
run: env WOLFSSL_VERSION=git-master WOLFSSL_DEBUG=1 scripts/build-ssl.sh
|
||||
- name: Compile HAProxy
|
||||
run: |
|
||||
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
||||
|
|
@ -34,12 +37,18 @@ jobs:
|
|||
run: |
|
||||
ldd $(which haproxy)
|
||||
haproxy -vv
|
||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||
- uses: ./.github/actions/setup-vtest
|
||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||
- name: Install problem matcher for VTest
|
||||
run: echo "::add-matcher::.github/vtest.json"
|
||||
- name: Run VTest for HAProxy
|
||||
id: vtest
|
||||
run: |
|
||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
# This is required for macOS which does not actually allow to increase
|
||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||
ulimit -n 65536
|
||||
# allow to catch coredumps
|
||||
ulimit -c unlimited
|
||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||
- name: Run Unit tests
|
||||
id: unittests
|
||||
run: |
|
||||
|
|
|
|||
14
BRANCHES
14
BRANCHES
|
|
@ -137,7 +137,7 @@ release. These branches were emitted at a pace of one per year since 1.5 in
|
|||
2014. As of 2019, 1.5 is still supported and widely used, even though it very
|
||||
rarely receives updates. After a few years these LTS branches enter a
|
||||
"critical fixes only" status, which means that they will rarely receive a fix
|
||||
but if that a critical issue affects them, a release will be made, with or
|
||||
but if that a critital issue affects them, a release will be made, with or
|
||||
without any other fix. Once a version is not supported anymore, it will not
|
||||
receive any fix at all and it will really be time for you to upgrade to a more
|
||||
recent branch. Please note that even when an upgrade is needed, a great care is
|
||||
|
|
@ -171,17 +171,7 @@ feedback for developers:
|
|||
as the previous releases that had 6 months to stabilize. In terms of
|
||||
stability it really means that the point zero version already accumulated
|
||||
6 months of fixes and that it is much safer to use even just after it is
|
||||
released. There is one exception though, features marked as "experimental"
|
||||
are not guaranteed to be maintained beyond the release of the next LTS
|
||||
branch. The rationale here is that the experimental status is made to
|
||||
expose an early preview of a feature, that is often incomplete, not always
|
||||
in its definitive form regarding configuration, and for which developers
|
||||
are seeking feedback from the users. It is even possible that changes will
|
||||
be brought within the stable branch and it may happen that the feature
|
||||
breaks. It is not imaginable to always be able to backport bug fixes too
|
||||
far in this context since the code and configuration may change quite a
|
||||
bit. Users who want to try experimental features are expected to upgrade
|
||||
quickly to benefit from the improvements made to that feature.
|
||||
released.
|
||||
|
||||
- for developers, given that the odd versions are solely used by highly
|
||||
skilled users, it's easier to get advanced traces and captures, and there
|
||||
|
|
|
|||
55
INSTALL
55
INSTALL
|
|
@ -11,7 +11,7 @@ this task seriously and are doing a good job at backporting important fixes.
|
|||
|
||||
If for any reason you would prefer a different version than the one packaged
|
||||
for your system, you want to be certain to have all the fixes or to get some
|
||||
commercial support, other choices are available at https://www.haproxy.com/.
|
||||
commercial support, other choices are available at http://www.haproxy.com/.
|
||||
|
||||
|
||||
Areas covered in this document
|
||||
|
|
@ -111,12 +111,12 @@ HAProxy requires a working GCC or Clang toolchain and GNU make :
|
|||
may want to retry with "gmake" which is the name commonly used for GNU make
|
||||
on BSD systems.
|
||||
|
||||
- GCC >= 4.7 (up to 16 tested). Older versions are no longer supported due to
|
||||
- GCC >= 4.7 (up to 14 tested). Older versions are no longer supported due to
|
||||
the latest mt_list update which only uses c11-like atomics. Newer versions
|
||||
may sometimes break due to compiler regressions or behaviour changes. The
|
||||
version shipped with your operating system is very likely to work with no
|
||||
trouble. Clang >= 3.0 is also known to work as an alternative solution, and
|
||||
versions up to 21 were successfully tested. Recent versions may emit a bit
|
||||
versions up to 19 were successfully tested. Recent versions may emit a bit
|
||||
more warnings that are worth reporting as they may reveal real bugs. TCC
|
||||
(https://repo.or.cz/tinycc.git) is also usable for developers but will not
|
||||
support threading and was found at least once to produce bad code in some
|
||||
|
|
@ -237,7 +237,7 @@ to forcefully enable it using "USE_LIBCRYPT=1".
|
|||
-----------------
|
||||
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
|
||||
supports the OpenSSL library, and is known to build and work with branches
|
||||
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 4.0. It is recommended to use
|
||||
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.4. It is recommended to use
|
||||
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
|
||||
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
|
||||
and each of the branches above receives its own fixes, without forcing you to
|
||||
|
|
@ -259,15 +259,11 @@ reported to work as well. While there are some efforts from the community to
|
|||
ensure they work well, OpenSSL remains the primary target and this means that
|
||||
in case of conflicting choices, OpenSSL support will be favored over other
|
||||
options. Note that QUIC is not fully supported when haproxy is built with
|
||||
OpenSSL < 3.5.2 version. In this case, QUICTLS or AWS-LC are the preferred
|
||||
alternatives. As of writing this, the QuicTLS project follows OpenSSL very
|
||||
closely and provides update simultaneously, but being a volunteer-driven
|
||||
project, its long-term future does not look certain enough to convince
|
||||
operating systems to package it, so it needs to be build locally. Recent
|
||||
versions of AWS-LC (>= 1.22 and the FIPS branches) are pretty complete and
|
||||
generally more performant than other OpenSSL derivatives, but may behave
|
||||
slightly differently, particularly when dealing with outdated setups. See
|
||||
the section about QUIC in this document.
|
||||
OpenSSL. In this case, QUICTLS is the preferred alternative. As of writing
|
||||
this, the QuicTLS project follows OpenSSL very closely and provides update
|
||||
simultaneously, but being a volunteer-driven project, its long-term future does
|
||||
not look certain enough to convince operating systems to package it, so it
|
||||
needs to be build locally. See the section about QUIC in this document.
|
||||
|
||||
A fifth option is wolfSSL (https://github.com/wolfSSL/wolfssl). It is the only
|
||||
supported alternative stack not based on OpenSSL, yet which implements almost
|
||||
|
|
@ -426,9 +422,9 @@ Lua is an embedded programming language supported by HAProxy to provide more
|
|||
advanced scripting capabilities. Only versions 5.3 and above are supported.
|
||||
In order to enable Lua support, please specify "USE_LUA=1" on the command line.
|
||||
Some systems provide this library under various names to avoid conflicts with
|
||||
previous versions. By default, HAProxy looks for "lua5.5", "lua55", "lua5.4",
|
||||
"lua54", "lua5.3", "lua53", "lua". If your system uses a different naming, you
|
||||
may need to set the library name in the "LUA_LIB_NAME" variable.
|
||||
previous versions. By default, HAProxy looks for "lua5.4", "lua54", "lua5.3",
|
||||
"lua53", "lua". If your system uses a different naming, you may need to set the
|
||||
library name in the "LUA_LIB_NAME" variable.
|
||||
|
||||
If Lua is not provided on your system, it can be very simply built locally. It
|
||||
can be downloaded from https://www.lua.org/, extracted and built, for example :
|
||||
|
|
@ -504,11 +500,10 @@ QUIC is the new transport layer protocol and is required for HTTP/3. This
|
|||
protocol stack is currently supported as an experimental feature in haproxy on
|
||||
the frontend side. In order to enable it, use "USE_QUIC=1 USE_OPENSSL=1".
|
||||
|
||||
Note that QUIC is not always fully supported by the OpenSSL library depending on
|
||||
its version. Indeed QUIC 0-RTT cannot be supported by OpenSSL for versions before
|
||||
3.5 contrary to others libraries with full QUIC support. The preferred option is
|
||||
to use QUICTLS. This is a fork of OpenSSL with a QUIC-compatible API. Its
|
||||
repository is available at this location:
|
||||
Note that QUIC is not fully supported by the OpenSSL library. Indeed QUIC 0-RTT
|
||||
cannot be supported by OpenSSL contrary to others libraries with full QUIC
|
||||
support. The preferred option is to use QUICTLS. This is a fork of OpenSSL with
|
||||
a QUIC-compatible API. Its repository is available at this location:
|
||||
|
||||
https://github.com/quictls/openssl
|
||||
|
||||
|
|
@ -536,18 +531,14 @@ way assuming that wolfSSL was installed in /opt/wolfssl-5.6.0 as shown in 4.5:
|
|||
SSL_INC=/opt/wolfssl-5.6.0/include SSL_LIB=/opt/wolfssl-5.6.0/lib
|
||||
LDFLAGS="-Wl,-rpath,/opt/wolfssl-5.6.0/lib"
|
||||
|
||||
As last resort, haproxy may be compiled against OpenSSL as follows from 3.5
|
||||
version with 0-RTT support:
|
||||
|
||||
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1
|
||||
|
||||
or as follows for all OpenSSL versions but without O-RTT support:
|
||||
As last resort, haproxy may be compiled against OpenSSL as follows:
|
||||
|
||||
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1 USE_QUIC_OPENSSL_COMPAT=1
|
||||
|
||||
In addition to this requirements, the QUIC listener bindings must be explicitly
|
||||
enabled with a specific QUIC tuning parameter. (see "limited-quic" global
|
||||
parameter of haproxy Configuration Manual).
|
||||
Note that QUIC 0-RTT is not supported by haproxy QUIC stack when built against
|
||||
OpenSSL. In addition to this compilation requirements, the QUIC listener
|
||||
bindings must be explicitly enabled with a specific QUIC tuning parameter.
|
||||
(see "limited-quic" global parameter of haproxy Configuration Manual).
|
||||
|
||||
|
||||
5) How to build HAProxy
|
||||
|
|
@ -563,9 +554,9 @@ It goes into more details with the main options.
|
|||
To build haproxy, you have to choose your target OS amongst the following ones
|
||||
and assign it to the TARGET variable :
|
||||
|
||||
- linux-glibc for Linux kernel 4.17 and above
|
||||
- linux-glibc for Linux kernel 2.6.28 and above
|
||||
- linux-glibc-legacy for Linux kernel 2.6.28 and above without new features
|
||||
- linux-musl for Linux kernel 4.17 and above with musl libc
|
||||
- linux-musl for Linux kernel 2.6.28 and above with musl libc
|
||||
- solaris for Solaris 10 and above
|
||||
- freebsd for FreeBSD 10 and above
|
||||
- dragonfly for DragonFlyBSD 4.3 and above
|
||||
|
|
|
|||
162
Makefile
162
Makefile
|
|
@ -35,7 +35,6 @@
|
|||
# USE_OPENSSL : enable use of OpenSSL. Recommended, but see below.
|
||||
# USE_OPENSSL_AWSLC : enable use of AWS-LC
|
||||
# USE_OPENSSL_WOLFSSL : enable use of wolfSSL with the OpenSSL API
|
||||
# USE_ECH : enable use of ECH with the OpenSSL API
|
||||
# USE_QUIC : enable use of QUIC with the quictls API (quictls, libressl, boringssl)
|
||||
# USE_QUIC_OPENSSL_COMPAT : enable use of QUIC with the standard openssl API (limited features)
|
||||
# USE_ENGINE : enable use of OpenSSL Engine.
|
||||
|
|
@ -44,7 +43,6 @@
|
|||
# USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic.
|
||||
# USE_PRCTL : enable use of prctl(). Automatic.
|
||||
# USE_PROCCTL : enable use of procctl(). Automatic.
|
||||
# USE_TRACE : enable trace subsystem. Always on.
|
||||
# USE_ZLIB : enable zlib library support and disable SLZ
|
||||
# USE_SLZ : enable slz library instead of zlib (default=enabled)
|
||||
# USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic.
|
||||
|
|
@ -64,8 +62,6 @@
|
|||
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
|
||||
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
||||
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
|
||||
# USE_SHM_OPEN : use shm_open() for features that can make use of shared memory
|
||||
# USE_KTLS : use kTLS.(requires at least Linux 4.17).
|
||||
#
|
||||
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
|
||||
# "USE_xxx=" (empty string). The list of enabled and disabled options for a
|
||||
|
|
@ -94,7 +90,6 @@
|
|||
# SILENT_DEFINE may be used to specify other defines which will not be
|
||||
# reported by "haproxy -vv".
|
||||
# EXTRA is used to force building or not building some extra tools.
|
||||
# EXTRA_MAKE space-separated list of external addons using a Makefile.inc
|
||||
# DESTDIR is not set by default and is used for installation only.
|
||||
# It might be useful to set DESTDIR if you want to install haproxy
|
||||
# in a sandbox.
|
||||
|
|
@ -124,7 +119,7 @@
|
|||
# LUA_LIB : force the lib path to lua
|
||||
# LUA_INC : force the include path to lua
|
||||
# LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of
|
||||
# priority: lua5.5, lua55, lua5.4, lua54, lua5.3, lua53, lua).
|
||||
# priority : lua5.4, lua54, lua5.3, lua53, lua).
|
||||
# OT_DEBUG : compile the OpenTracing filter in debug mode
|
||||
# OT_INC : force the include path to libopentracing-c-wrapper
|
||||
# OT_LIB : force the lib path to libopentracing-c-wrapper
|
||||
|
|
@ -216,8 +211,7 @@ UNIT_TEST_SCRIPT=./scripts/run-unittests.sh
|
|||
# undefined behavior to silently produce invalid code. For this reason we have
|
||||
# to use -fwrapv or -fno-strict-overflow to guarantee the intended behavior.
|
||||
# It is preferable not to change this option in order to avoid breakage.
|
||||
STD_CFLAGS := $(call cc-opt-alt,-fwrapv,-fno-strict-overflow) \
|
||||
$(call cc-opt,-fvect-cost-model=very-cheap)
|
||||
STD_CFLAGS := $(call cc-opt-alt,-fwrapv,-fno-strict-overflow)
|
||||
|
||||
#### Compiler-specific flags to enable certain classes of warnings.
|
||||
# Some are hard-coded, others are enabled only if supported.
|
||||
|
|
@ -344,16 +338,14 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
|
|||
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
|
||||
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
|
||||
USE_ECH USE_TRACE \
|
||||
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
|
||||
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
|
||||
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
|
||||
USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \
|
||||
USE_THREAD_DUMP USE_EVPORTS USE_OT USE_QUIC USE_PROMEX \
|
||||
USE_MEMORY_PROFILING USE_SHM_OPEN \
|
||||
USE_MEMORY_PROFILING \
|
||||
USE_STATIC_PCRE USE_STATIC_PCRE2 \
|
||||
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT \
|
||||
USE_QUIC_OPENSSL_COMPAT USE_KTLS
|
||||
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT USE_QUIC_OPENSSL_COMPAT
|
||||
|
||||
# preset all variables for all supported build options among use_opts
|
||||
$(reset_opts_vars)
|
||||
|
|
@ -367,9 +359,6 @@ $(warn_unknown_options)
|
|||
# on the make command line.
|
||||
USE_POLL = default
|
||||
|
||||
# traces are always enabled
|
||||
USE_TRACE = default
|
||||
|
||||
# SLZ is always supported unless explicitly disabled by passing USE_SLZ=""
|
||||
# or disabled by enabling ZLIB using USE_ZLIB=1
|
||||
ifeq ($(USE_ZLIB:0=),)
|
||||
|
|
@ -387,13 +376,13 @@ ifeq ($(TARGET),haiku)
|
|||
set_target_defaults = $(call default_opts,USE_POLL USE_TPROXY USE_OBSOLETE_LINKER)
|
||||
endif
|
||||
|
||||
# For linux >= 4.17 and glibc
|
||||
# For linux >= 2.6.28 and glibc
|
||||
ifeq ($(TARGET),linux-glibc)
|
||||
set_target_defaults = $(call default_opts, \
|
||||
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
||||
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
||||
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN USE_KTLS)
|
||||
USE_GETADDRINFO USE_BACKTRACE)
|
||||
INSTALL = install -v
|
||||
endif
|
||||
|
||||
|
|
@ -406,13 +395,13 @@ ifeq ($(TARGET),linux-glibc-legacy)
|
|||
INSTALL = install -v
|
||||
endif
|
||||
|
||||
# For linux >= 4.17 and musl
|
||||
# For linux >= 2.6.28 and musl
|
||||
ifeq ($(TARGET),linux-musl)
|
||||
set_target_defaults = $(call default_opts, \
|
||||
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
||||
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
||||
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN USE_KTLS)
|
||||
USE_GETADDRINFO USE_BACKTRACE)
|
||||
INSTALL = install -v
|
||||
endif
|
||||
|
||||
|
|
@ -606,10 +595,6 @@ ifneq ($(USE_BACKTRACE:0=),)
|
|||
BACKTRACE_CFLAGS = -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
ifneq ($(USE_MEMORY_PROFILING:0=),)
|
||||
MEMORY_PROFILING_CFLAGS = -fno-optimize-sibling-calls
|
||||
endif
|
||||
|
||||
ifneq ($(USE_CPU_AFFINITY:0=),)
|
||||
OPTIONS_OBJS += src/cpuset.o
|
||||
OPTIONS_OBJS += src/cpu_topo.o
|
||||
|
|
@ -648,7 +633,7 @@ ifneq ($(USE_OPENSSL:0=),)
|
|||
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
|
||||
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
|
||||
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
|
||||
src/acme_resolvers.o src/ssl_trace.o src/jwe.o
|
||||
src/ssl_trace.o
|
||||
endif
|
||||
|
||||
ifneq ($(USE_ENGINE:0=),)
|
||||
|
|
@ -671,12 +656,11 @@ OPTIONS_OBJS += src/mux_quic.o src/h3.o src/quic_rx.o src/quic_tx.o \
|
|||
src/quic_cc_bbr.o src/quic_retry.o \
|
||||
src/cfgparse-quic.o src/xprt_quic.o src/quic_token.o \
|
||||
src/quic_ack.o src/qpack-dec.o src/quic_cc_newreno.o \
|
||||
src/qcm_http.o src/qcm_trace.o src/quic_rules.o \
|
||||
src/qmux_http.o src/qmux_trace.o src/quic_rules.o \
|
||||
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
|
||||
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
|
||||
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
|
||||
src/quic_enc.o src/qcm_qmux.o src/xprt_qmux.o \
|
||||
src/mpring.o
|
||||
src/cbuf.o src/quic_enc.o
|
||||
endif
|
||||
|
||||
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
||||
|
|
@ -684,15 +668,15 @@ OPTIONS_OBJS += src/quic_openssl_compat.o
|
|||
endif
|
||||
|
||||
ifneq ($(USE_LUA:0=),)
|
||||
check_lua_inc = $(shell if [ ! -e /usr/include/lua.h -a -e $(2)$(1)/lua.h ]; then echo $(2)$(1); fi;)
|
||||
LUA_INC := $(firstword $(foreach lib,lua5.5 lua55 lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
|
||||
check_lua_inc = $(shell if [ -d $(2)$(1) ]; then echo $(2)$(1); fi;)
|
||||
LUA_INC := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
|
||||
|
||||
check_lua_lib = $(shell echo "int main(){}" | $(CC) $(if $(LUA_INC),-I$(LUA_INC)) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
|
||||
check_lua_lib = $(shell echo "int main(){}" | $(CC) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
|
||||
LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB))
|
||||
|
||||
# Try to automatically detect the Lua library if not set
|
||||
ifeq ($(LUA_LIB_NAME),)
|
||||
LUA_LIB_NAME := $(firstword $(foreach lib,lua lua5.5 lua55 lua5.4 lua54 lua5.3 lua53,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
|
||||
LUA_LIB_NAME := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
|
||||
endif
|
||||
|
||||
# Lua lib name must be set now (forced/detected above)
|
||||
|
|
@ -722,15 +706,70 @@ ifneq ($(USE_PROMEX:0=),)
|
|||
endif
|
||||
|
||||
ifneq ($(USE_DEVICEATLAS:0=),)
|
||||
EXTRA_MAKE += addons/deviceatlas
|
||||
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
|
||||
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
|
||||
# can be used to disable the cache support if needed (this also removes the necessity of having
|
||||
# a C++ toolchain installed).
|
||||
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
||||
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
||||
include addons/deviceatlas/Makefile.inc
|
||||
OPTIONS_OBJS += addons/deviceatlas/da.o
|
||||
endif
|
||||
|
||||
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
|
||||
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
|
||||
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
|
||||
# regardless of the version since they are mutually exclusive. The version
|
||||
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
|
||||
51DEGREES_INC = $(51DEGREES_SRC)
|
||||
51DEGREES_LIB = $(51DEGREES_SRC)
|
||||
51DEGREES_VER = 3
|
||||
|
||||
ifneq ($(USE_51DEGREES:0=),)
|
||||
EXTRA_MAKE += addons/51degrees
|
||||
ifeq ($(51DEGREES_VER),4) # v4 here
|
||||
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
|
||||
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
|
||||
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
|
||||
endif
|
||||
USE_LIBATOMIC = implicit
|
||||
endif # 51DEGREES_VER==4
|
||||
|
||||
ifeq ($(51DEGREES_VER),3) # v3 here
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
|
||||
else
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
|
||||
endif
|
||||
else
|
||||
ifneq ($(51DEGREES_VER),4)
|
||||
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
|
||||
endif
|
||||
endif # 51DEGREES_VER==3
|
||||
|
||||
OPTIONS_OBJS += addons/51degrees/51d.o
|
||||
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
|
||||
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
|
||||
USE_MATH = implicit
|
||||
endif # USE_51DEGREES
|
||||
|
||||
ifneq ($(USE_WURFL:0=),)
|
||||
EXTRA_MAKE += addons/wurfl
|
||||
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
|
||||
# to WURFL headers and libraries if needed.
|
||||
WURFL_INC = $(WURFL_SRC)
|
||||
WURFL_LIB = $(WURFL_SRC)
|
||||
OPTIONS_OBJS += addons/wurfl/wurfl.o
|
||||
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
|
||||
ifneq ($(WURFL_DEBUG),)
|
||||
WURFL_CFLAGS += -DWURFL_DEBUG
|
||||
endif
|
||||
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
|
||||
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
|
||||
endif
|
||||
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
|
||||
endif
|
||||
|
||||
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
|
||||
|
|
@ -810,14 +849,9 @@ ifneq ($(USE_LINUX_CAP:0=),)
|
|||
endif
|
||||
|
||||
ifneq ($(USE_OT:0=),)
|
||||
$(call warning, The opentracing filter was deprecated in haproxy 3.3 and will be removed in 3.5.)
|
||||
include addons/ot/Makefile
|
||||
endif
|
||||
|
||||
ifneq ($(EXTRA_MAKE),)
|
||||
include $(addsuffix /Makefile.mk,$(EXTRA_MAKE))
|
||||
endif
|
||||
|
||||
# better keep this one close to the end, as several libs above may need it
|
||||
ifneq ($(USE_DL:0=),)
|
||||
DL_LDFLAGS = -ldl
|
||||
|
|
@ -912,7 +946,6 @@ endif # obsolete targets
|
|||
endif # TARGET
|
||||
|
||||
OBJS =
|
||||
HATERM_OBJS =
|
||||
|
||||
ifneq ($(EXTRA_OBJS),)
|
||||
OBJS += $(EXTRA_OBJS)
|
||||
|
|
@ -926,15 +959,15 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
|
|||
src/cache.o src/stconn.o src/http_htx.o src/debug.o \
|
||||
src/check.o src/stats-html.o src/haproxy.o src/listener.o \
|
||||
src/applet.o src/pattern.o src/cfgparse-listen.o \
|
||||
src/flt_spoe.o src/cebis_tree.o src/http_ext.o \
|
||||
src/http_act.o src/http_fetch.o src/cebs_tree.o \
|
||||
src/cebib_tree.o src/http_client.o src/dns.o \
|
||||
src/cebb_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
|
||||
src/flt_spoe.o src/cebuis_tree.o src/http_ext.o \
|
||||
src/http_act.o src/http_fetch.o src/cebus_tree.o \
|
||||
src/cebuib_tree.o src/http_client.o src/dns.o \
|
||||
src/cebub_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
|
||||
src/trace.o src/stats-proxy.o src/pool.o src/stats.o \
|
||||
src/cfgparse-global.o src/filters.o src/mux_pt.o \
|
||||
src/flt_http_comp.o src/sock.o src/h1.o src/sink.o \
|
||||
src/ceba_tree.o src/session.o src/payload.o src/htx.o \
|
||||
src/cebl_tree.o src/ceb32_tree.o src/ceb64_tree.o \
|
||||
src/cebua_tree.o src/session.o src/payload.o src/htx.o \
|
||||
src/cebul_tree.o src/cebu32_tree.o src/cebu64_tree.o \
|
||||
src/server_state.o src/proto_rhttp.o src/flt_trace.o src/fd.o \
|
||||
src/task.o src/map.o src/fcgi-app.o src/h2.o src/mworker.o \
|
||||
src/tcp_sample.o src/mjson.o src/h1_htx.o src/tcp_act.o \
|
||||
|
|
@ -949,9 +982,9 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
|
|||
src/cfgcond.o src/proto_udp.o src/lb_fwlc.o src/ebmbtree.o \
|
||||
src/proto_uxdg.o src/cfgdiag.o src/sock_unix.o src/sha1.o \
|
||||
src/lb_fas.o src/clock.o src/sock_inet.o src/ev_select.o \
|
||||
src/lb_map.o src/shctx.o src/hpack-dec.o src/net_helper.o \
|
||||
src/lb_map.o src/shctx.o src/mworker-prog.o src/hpack-dec.o \
|
||||
src/arg.o src/signal.o src/fix.o src/dynbuf.o src/guid.o \
|
||||
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o src/counters.o \
|
||||
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o \
|
||||
src/cfgparse-unix.o src/regex.o src/fcgi.o src/uri_auth.o \
|
||||
src/eb64tree.o src/eb32tree.o src/eb32sctree.o src/lru.o \
|
||||
src/limits.o src/ebimtree.o src/wdt.o src/hpack-tbl.o \
|
||||
|
|
@ -959,15 +992,12 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
|
|||
src/ebsttree.o src/freq_ctr.o src/systemd.o src/init.o \
|
||||
src/http_acl.o src/dict.o src/dgram.o src/pipe.o \
|
||||
src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \
|
||||
src/httpclient_cli.o src/version.o src/ncbmbuf.o src/ech.o \
|
||||
src/cfgparse-peers.o src/haterm.o
|
||||
src/version.o
|
||||
|
||||
ifneq ($(TRACE),)
|
||||
OBJS += src/calltrace.o
|
||||
endif
|
||||
|
||||
HATERM_OBJS += $(OBJS) src/haterm_init.o
|
||||
|
||||
# Used only for forced dependency checking. May be cleared during development.
|
||||
INCLUDES = $(wildcard include/*/*.h)
|
||||
DEP = $(INCLUDES) .build_opts
|
||||
|
|
@ -999,11 +1029,11 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
|
|||
uninstall clean tags cscope tar git-tar version update-version \
|
||||
opts reg-tests reg-tests-help unit-tests admin/halog/halog dev/flags/flags \
|
||||
dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop \
|
||||
dev/term_events/term_events dev/gdb/pm-from-core dev/gdb/libs-from-core
|
||||
dev/term_events/term_events
|
||||
|
||||
ifneq ($(TARGET),)
|
||||
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
||||
build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(EXTRA_MAKE) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
|
||||
build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
|
||||
.build_opts: $(build_opts)
|
||||
else
|
||||
.build_opts:
|
||||
|
|
@ -1015,9 +1045,6 @@ endif # non-empty target
|
|||
haproxy: $(OPTIONS_OBJS) $(OBJS)
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
haterm: $(OPTIONS_OBJS) $(HATERM_OBJS)
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
objsize: haproxy
|
||||
$(Q)objdump -t $^|grep ' g '|grep -F '.text'|awk '{print $$5 FS $$6}'|sort
|
||||
|
||||
|
|
@ -1033,12 +1060,6 @@ admin/dyncookie/dyncookie: admin/dyncookie/dyncookie.o
|
|||
dev/flags/flags: dev/flags/flags.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
dev/gdb/libs-from-core: dev/gdb/libs-from-core.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
dev/gdb/pm-from-core: dev/gdb/pm-from-core.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
dev/haring/haring: dev/haring/haring.o
|
||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||
|
||||
|
|
@ -1092,11 +1113,6 @@ install-doc:
|
|||
$(INSTALL) -m 644 doc/$$x.txt "$(DESTDIR)$(DOCDIR)" ; \
|
||||
done
|
||||
|
||||
install-admin:
|
||||
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
|
||||
$(Q)$(INSTALL) admin/cli/haproxy-dump-certs "$(DESTDIR)$(SBINDIR)"
|
||||
$(Q)$(INSTALL) admin/cli/haproxy-reload "$(DESTDIR)$(SBINDIR)"
|
||||
|
||||
install-bin:
|
||||
$(Q)for i in haproxy $(EXTRA); do \
|
||||
if ! [ -e "$$i" ]; then \
|
||||
|
|
@ -1107,7 +1123,7 @@ install-bin:
|
|||
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
|
||||
$(Q)$(INSTALL) haproxy $(EXTRA) "$(DESTDIR)$(SBINDIR)"
|
||||
|
||||
install: install-bin install-admin install-man install-doc
|
||||
install: install-bin install-man install-doc
|
||||
|
||||
uninstall:
|
||||
$(Q)rm -f "$(DESTDIR)$(MANDIR)"/man1/haproxy.1
|
||||
|
|
@ -1118,7 +1134,7 @@ uninstall:
|
|||
$(Q)rm -f "$(DESTDIR)$(SBINDIR)"/haproxy
|
||||
|
||||
clean:
|
||||
$(Q)rm -f *.[oas] src/*.[oas] haproxy haterm test .build_opts .build_opts.new
|
||||
$(Q)rm -f *.[oas] src/*.[oas] haproxy test .build_opts .build_opts.new
|
||||
$(Q)for dir in . src dev/* admin/* addons/* include/* doc; do rm -f $$dir/*~ $$dir/*.rej $$dir/core; done
|
||||
$(Q)rm -f haproxy-$(VERSION).tar.gz haproxy-$(VERSION)$(SUBVERS)$(EXTRAVERSION).tar.gz
|
||||
$(Q)rm -f haproxy-$(VERSION) haproxy-$(VERSION)$(SUBVERS)$(EXTRAVERSION) nohup.out gmon.out
|
||||
|
|
@ -1137,7 +1153,7 @@ distclean: clean
|
|||
$(Q)rm -f admin/dyncookie/dyncookie
|
||||
$(Q)rm -f dev/haring/haring dev/ncpu/ncpu{,.so} dev/poll/poll dev/tcploop/tcploop
|
||||
$(Q)rm -f dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
||||
$(Q)rm -f dev/qpack/decode dev/gdb/pm-from-core dev/gdb/libs-from-core
|
||||
$(Q)rm -f dev/qpack/decode
|
||||
|
||||
tags:
|
||||
$(Q)find src include \( -name '*.c' -o -name '*.h' \) -print0 | \
|
||||
|
|
@ -1264,8 +1280,6 @@ unit-tests:
|
|||
# options for all commits within RANGE. RANGE may be either a git range
|
||||
# such as ref1..ref2 or a single commit, in which case all commits from
|
||||
# the master branch to this one will be tested.
|
||||
# Will execute TEST_CMD for each commit if defined, and will stop in case of
|
||||
# failure.
|
||||
|
||||
range:
|
||||
$(Q)[ -d .git/. ] || { echo "## Fatal: \"make $@\" may only be used inside a Git repository."; exit 1; }
|
||||
|
|
@ -1291,8 +1305,6 @@ range:
|
|||
echo "[ $$index/$$count ] $$commit #############################"; \
|
||||
git checkout -q $$commit || die 1; \
|
||||
$(MAKE) all || die 1; \
|
||||
set -- $(TEST_CMD); \
|
||||
[ "$$#" -eq 0 ] || "$$@" || die 1; \
|
||||
index=$$((index + 1)); \
|
||||
done; \
|
||||
echo;echo "Done! $${count} commit(s) built successfully for RANGE $${RANGE}" ; \
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
# HAProxy
|
||||
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/musl.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/aws-lc.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/openssl-nodeprecated.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml)
|
||||
[](https://cirrus-ci.com/github/haproxy/haproxy/)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml)
|
||||
|
||||

|
||||
|
|
|
|||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
|||
$Format:%ci$
|
||||
2026/06/03
|
||||
2025/05/14
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
3.5-dev0
|
||||
3.2-dev16
|
||||
|
|
|
|||
|
|
@ -127,16 +127,7 @@ static int _51d_property_name_list(char **args, int section_type, struct proxy *
|
|||
|
||||
while (*(args[cur_arg])) {
|
||||
name = calloc(1, sizeof(*name));
|
||||
if (!name) {
|
||||
memprintf(err, "'%s' failed to allocate memory.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
name->name = strdup(args[cur_arg]);
|
||||
if (!name->name) {
|
||||
free(name);
|
||||
memprintf(err, "'%s' failed to allocate memory.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
LIST_APPEND(&global_51degrees.property_names, &name->list);
|
||||
++cur_arg;
|
||||
}
|
||||
|
|
@ -312,7 +303,6 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
|
|||
|
||||
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
|
||||
{
|
||||
struct buffer *temp = get_trash_chunk();
|
||||
struct channel *chn;
|
||||
struct htx *htx;
|
||||
struct http_hdr_ctx ctx;
|
||||
|
|
@ -334,15 +324,7 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
|
|||
|
||||
if (http_find_header(htx, name, &ctx, 1)) {
|
||||
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
|
||||
/* Copy value into trash and NUL-terminate before passing to the
|
||||
* 51Degrees Trie API, which expects a C string.
|
||||
*/
|
||||
if (ctx.value.len >= temp->size)
|
||||
continue;
|
||||
memcpy(temp->area, ctx.value.ptr, ctx.value.len);
|
||||
temp->area[ctx.value.len] = '\0';
|
||||
temp->data = ctx.value.len + 1;
|
||||
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, temp->area);
|
||||
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
|
||||
offsets->size++;
|
||||
}
|
||||
}
|
||||
|
|
@ -568,8 +550,6 @@ static void _51d_process_match(const struct arg *args, struct sample *smp)
|
|||
char valuesBuffer[1024];
|
||||
#endif
|
||||
|
||||
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) || defined(FIFTYONE_DEGREES_HASH_INCLUDED)
|
||||
|
||||
char no_data[] = "NoData"; /* response when no data could be found */
|
||||
struct buffer *temp = get_trash_chunk();
|
||||
int i = 0, found;
|
||||
|
|
@ -656,7 +636,6 @@ static void _51d_process_match(const struct arg *args, struct sample *smp)
|
|||
smp->data.u.str.area = temp->area;
|
||||
smp->data.u.str.data = temp->data;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sets the sample data as a constant string. This ensures that the
|
||||
* string will be processed correctly.
|
||||
|
|
@ -937,10 +916,6 @@ static int init_51degrees(void)
|
|||
list_for_each_entry(name, &global_51degrees.property_names, list)
|
||||
++i;
|
||||
_51d_property_list = calloc(i, sizeof(*_51d_property_list));
|
||||
if (!_51d_property_list) {
|
||||
ha_alert("51Degrees: Failed to allocate property list.\n");
|
||||
return (ERR_FATAL | ERR_ALERT);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(name, &global_51degrees.property_names, list)
|
||||
|
|
@ -1075,7 +1050,7 @@ static int init_51degrees(void)
|
|||
|
||||
static void deinit_51degrees(void)
|
||||
{
|
||||
struct _51d_property_names *_51d_prop_name = NULL, *_51d_prop_nameb = NULL;
|
||||
struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
|
||||
|
||||
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED)
|
||||
free(global_51degrees.header_names);
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
|
||||
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
|
||||
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
|
||||
# regardless of the version since they are mutually exclusive. The version
|
||||
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
|
||||
51DEGREES_INC = $(51DEGREES_SRC)
|
||||
51DEGREES_LIB = $(51DEGREES_SRC)
|
||||
51DEGREES_VER = 3
|
||||
|
||||
ifeq ($(51DEGREES_VER),4) # v4 here
|
||||
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
|
||||
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
|
||||
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
|
||||
endif
|
||||
USE_LIBATOMIC = implicit
|
||||
endif # 51DEGREES_VER==4
|
||||
|
||||
ifeq ($(51DEGREES_VER),3) # v3 here
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
|
||||
else
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
|
||||
endif
|
||||
else
|
||||
ifneq ($(51DEGREES_VER),4)
|
||||
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
|
||||
endif
|
||||
endif # 51DEGREES_VER==3
|
||||
|
||||
OPTIONS_OBJS += addons/51degrees/51d.o
|
||||
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
|
||||
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
|
||||
USE_MATH = implicit
|
||||
|
|
@ -40,7 +40,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
typedef int bool;
|
||||
enum { false, true };
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,11 @@
|
|||
# DEVICEATLAS_SRC : DeviceAtlas API source root path
|
||||
|
||||
|
||||
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
|
||||
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
|
||||
# can be used to disable the cache support if needed (this also removes the necessity of having
|
||||
# a C++ toolchain installed).
|
||||
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
||||
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
||||
|
||||
CXX := c++
|
||||
CXXLIB := -lstdc++
|
||||
|
||||
ifeq ($(DEVICEATLAS_SRC),)
|
||||
OPTIONS_CFLAGS += -I$(DEVICEATLAS_INC)
|
||||
OPTIONS_LDFLAGS += -Wl,-rpath,$(DEVICEATLAS_LIB) -L$(DEVICEATLAS_LIB) -lda
|
||||
OPTIONS_LDFLAGS += -lda
|
||||
else
|
||||
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
||||
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
||||
|
|
@ -35,7 +27,5 @@ OPTIONS_OBJS += $(DEVICEATLAS_SRC)/dadwcurl.o
|
|||
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
|
||||
endif
|
||||
|
||||
OPTIONS_OBJS += addons/deviceatlas/da.o
|
||||
|
||||
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
|
||||
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
|
@ -31,7 +31,6 @@ static struct {
|
|||
da_atlas_t atlas;
|
||||
da_evidence_id_t useragentid;
|
||||
da_severity_t loglevel;
|
||||
size_t maxhdrlen;
|
||||
char separator;
|
||||
unsigned char daset:1;
|
||||
} global_deviceatlas = {
|
||||
|
|
@ -43,7 +42,6 @@ static struct {
|
|||
.atlasmap = NULL,
|
||||
.atlasfd = -1,
|
||||
.useragentid = 0,
|
||||
.maxhdrlen = 0,
|
||||
.daset = 0,
|
||||
.separator = '|',
|
||||
};
|
||||
|
|
@ -59,10 +57,6 @@ static int da_json_file(char **args, int section_type, struct proxy *curpx,
|
|||
return -1;
|
||||
}
|
||||
global_deviceatlas.jsonpath = strdup(args[1]);
|
||||
if (unlikely(global_deviceatlas.jsonpath == NULL)) {
|
||||
memprintf(err, "deviceatlas json file : out of memory.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +73,6 @@ static int da_log_level(char **args, int section_type, struct proxy *curpx,
|
|||
loglevel = atol(args[1]);
|
||||
if (loglevel < 0 || loglevel > 3) {
|
||||
memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
|
||||
return -1;
|
||||
} else {
|
||||
global_deviceatlas.loglevel = (da_severity_t)loglevel;
|
||||
}
|
||||
|
|
@ -108,10 +101,6 @@ static int da_properties_cookie(char **args, int section_type, struct proxy *cur
|
|||
return -1;
|
||||
} else {
|
||||
global_deviceatlas.cookiename = strdup(args[1]);
|
||||
if (unlikely(global_deviceatlas.cookiename == NULL)) {
|
||||
memprintf(err, "deviceatlas cookie name : out of memory.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
|
||||
return 0;
|
||||
|
|
@ -130,7 +119,6 @@ static int da_cache_size(char **args, int section_type, struct proxy *curpx,
|
|||
cachesize = atol(args[1]);
|
||||
if (cachesize < 0 || cachesize > DA_CACHE_MAX) {
|
||||
memprintf(err, "deviceatlas cache size : expects a cache size between 0 and %d, %s given.\n", DA_CACHE_MAX, args[1]);
|
||||
return -1;
|
||||
} else {
|
||||
#ifdef APINOCACHE
|
||||
fprintf(stdout, "deviceatlas cache size : no-op, its support is disabled.\n");
|
||||
|
|
@ -177,7 +165,7 @@ static int init_deviceatlas(void)
|
|||
da_status_t status;
|
||||
|
||||
jsonp = fopen(global_deviceatlas.jsonpath, "r");
|
||||
if (unlikely(jsonp == 0)) {
|
||||
if (jsonp == 0) {
|
||||
ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
|
||||
global_deviceatlas.jsonpath);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
|
|
@ -189,11 +177,9 @@ static int init_deviceatlas(void)
|
|||
status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
|
||||
&global_deviceatlas.atlasimgptr, &atlasimglen);
|
||||
fclose(jsonp);
|
||||
if (unlikely(status != DA_OK)) {
|
||||
if (status != DA_OK) {
|
||||
ha_alert("deviceatlas : '%s' json file is invalid.\n",
|
||||
global_deviceatlas.jsonpath);
|
||||
free(global_deviceatlas.atlasimgptr);
|
||||
da_fini();
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -201,10 +187,8 @@ static int init_deviceatlas(void)
|
|||
status = da_atlas_open(&global_deviceatlas.atlas, extraprops,
|
||||
global_deviceatlas.atlasimgptr, atlasimglen);
|
||||
|
||||
if (unlikely(status != DA_OK)) {
|
||||
if (status != DA_OK) {
|
||||
ha_alert("deviceatlas : data could not be compiled.\n");
|
||||
free(global_deviceatlas.atlasimgptr);
|
||||
da_fini();
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -213,28 +197,11 @@ static int init_deviceatlas(void)
|
|||
|
||||
if (global_deviceatlas.cookiename == 0) {
|
||||
global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT);
|
||||
if (unlikely(global_deviceatlas.cookiename == NULL)) {
|
||||
ha_alert("deviceatlas : out of memory.\n");
|
||||
da_atlas_close(&global_deviceatlas.atlas);
|
||||
free(global_deviceatlas.atlasimgptr);
|
||||
da_fini();
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
|
||||
}
|
||||
|
||||
global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
|
||||
"user-agent");
|
||||
{
|
||||
size_t hi;
|
||||
global_deviceatlas.maxhdrlen = 16;
|
||||
for (hi = 0; hi < global_deviceatlas.atlas.header_evidence_count; hi++) {
|
||||
size_t nl = strlen(global_deviceatlas.atlas.header_priorities[hi].name);
|
||||
if (nl > global_deviceatlas.maxhdrlen)
|
||||
global_deviceatlas.maxhdrlen = nl;
|
||||
}
|
||||
}
|
||||
if ((global_deviceatlas.atlasfd = shm_open(ATLASMAPNM, O_RDWR, 0660)) != -1) {
|
||||
global_deviceatlas.atlasmap = mmap(NULL, ATLASTOKSZ, PROT_READ | PROT_WRITE, MAP_SHARED, global_deviceatlas.atlasfd, 0);
|
||||
if (global_deviceatlas.atlasmap == MAP_FAILED) {
|
||||
|
|
@ -264,22 +231,24 @@ static void deinit_deviceatlas(void)
|
|||
free(global_deviceatlas.cookiename);
|
||||
da_atlas_close(&global_deviceatlas.atlas);
|
||||
free(global_deviceatlas.atlasimgptr);
|
||||
da_fini();
|
||||
}
|
||||
|
||||
if (global_deviceatlas.atlasfd != -1) {
|
||||
munmap(global_deviceatlas.atlasmap, ATLASTOKSZ);
|
||||
close(global_deviceatlas.atlasfd);
|
||||
shm_unlink(ATLASMAPNM);
|
||||
}
|
||||
|
||||
da_fini();
|
||||
}
|
||||
|
||||
static void da_haproxy_checkinst(void)
|
||||
{
|
||||
if (global_deviceatlas.atlasmap != 0) {
|
||||
char *base;
|
||||
base = (char *)global_deviceatlas.atlasmap;
|
||||
char *base;
|
||||
base = (char *)global_deviceatlas.atlasmap;
|
||||
|
||||
if (base[0] != 0) {
|
||||
if (base[0] != 0) {
|
||||
FILE *jsonp;
|
||||
void *cnew;
|
||||
da_status_t status;
|
||||
|
|
@ -289,10 +258,6 @@ static void da_haproxy_checkinst(void)
|
|||
da_property_decl_t extraprops[1] = {{NULL, 0}};
|
||||
#ifdef USE_THREAD
|
||||
HA_SPIN_LOCK(OTHER_LOCK, &dadwsch_lock);
|
||||
if (base[0] == 0) {
|
||||
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
strlcpy2(atlasp, base + sizeof(char), sizeof(atlasp));
|
||||
jsonp = fopen(atlasp, "r");
|
||||
|
|
@ -310,20 +275,10 @@ static void da_haproxy_checkinst(void)
|
|||
fclose(jsonp);
|
||||
if (status == DA_OK) {
|
||||
if (da_atlas_open(&inst, extraprops, cnew, atlassz) == DA_OK) {
|
||||
inst.config.cache_size = global_deviceatlas.cachesize;
|
||||
da_atlas_close(&global_deviceatlas.atlas);
|
||||
free(global_deviceatlas.atlasimgptr);
|
||||
global_deviceatlas.atlasimgptr = cnew;
|
||||
global_deviceatlas.atlas = inst;
|
||||
{
|
||||
size_t hi;
|
||||
global_deviceatlas.maxhdrlen = 16;
|
||||
for (hi = 0; hi < inst.header_evidence_count; hi++) {
|
||||
size_t nl = strlen(inst.header_priorities[hi].name);
|
||||
if (nl > global_deviceatlas.maxhdrlen)
|
||||
global_deviceatlas.maxhdrlen = nl;
|
||||
}
|
||||
}
|
||||
base[0] = 0;
|
||||
ha_notice("deviceatlas : new instance, data file date `%s`.\n",
|
||||
da_getdatacreationiso8601(&global_deviceatlas.atlas));
|
||||
|
|
@ -331,8 +286,6 @@ static void da_haproxy_checkinst(void)
|
|||
ha_alert("deviceatlas : instance update failed.\n");
|
||||
free(cnew);
|
||||
}
|
||||
} else {
|
||||
free(cnew);
|
||||
}
|
||||
#ifdef USE_THREAD
|
||||
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
|
||||
|
|
@ -344,7 +297,7 @@ static void da_haproxy_checkinst(void)
|
|||
static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo)
|
||||
{
|
||||
struct buffer *tmp;
|
||||
da_propid_t prop;
|
||||
da_propid_t prop, *pprop;
|
||||
da_status_t status;
|
||||
da_type_t proptype;
|
||||
const char *propname;
|
||||
|
|
@ -364,15 +317,13 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
|
|||
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
|
||||
continue;
|
||||
}
|
||||
if (unlikely(da_atlas_getproptype(&global_deviceatlas.atlas, prop, &proptype) != DA_OK)) {
|
||||
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
|
||||
continue;
|
||||
}
|
||||
pprop = ∝
|
||||
da_atlas_getproptype(&global_deviceatlas.atlas, *pprop, &proptype);
|
||||
|
||||
switch (proptype) {
|
||||
case DA_TYPE_BOOLEAN: {
|
||||
bool val;
|
||||
status = da_getpropboolean(devinfo, prop, &val);
|
||||
status = da_getpropboolean(devinfo, *pprop, &val);
|
||||
if (status == DA_OK) {
|
||||
chunk_appendf(tmp, "%d", val);
|
||||
}
|
||||
|
|
@ -381,7 +332,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
|
|||
case DA_TYPE_INTEGER:
|
||||
case DA_TYPE_NUMBER: {
|
||||
long val;
|
||||
status = da_getpropinteger(devinfo, prop, &val);
|
||||
status = da_getpropinteger(devinfo, *pprop, &val);
|
||||
if (status == DA_OK) {
|
||||
chunk_appendf(tmp, "%ld", val);
|
||||
}
|
||||
|
|
@ -389,7 +340,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
|
|||
}
|
||||
case DA_TYPE_STRING: {
|
||||
const char *val;
|
||||
status = da_getpropstring(devinfo, prop, &val);
|
||||
status = da_getpropstring(devinfo, *pprop, &val);
|
||||
if (status == DA_OK) {
|
||||
chunk_appendf(tmp, "%s", val);
|
||||
}
|
||||
|
|
@ -420,26 +371,29 @@ static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *pri
|
|||
{
|
||||
da_deviceinfo_t devinfo;
|
||||
da_status_t status;
|
||||
char useragentbuf[1024];
|
||||
const char *useragent;
|
||||
char useragentbuf[1024] = { 0 };
|
||||
int i;
|
||||
|
||||
if (unlikely(global_deviceatlas.daset == 0) || smp->data.u.str.data == 0) {
|
||||
if (global_deviceatlas.daset == 0 || smp->data.u.str.data == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
da_haproxy_checkinst();
|
||||
|
||||
i = smp->data.u.str.data > sizeof(useragentbuf) - 1 ? sizeof(useragentbuf) - 1 : smp->data.u.str.data;
|
||||
memcpy(useragentbuf, smp->data.u.str.area, i);
|
||||
useragentbuf[i] = 0;
|
||||
i = smp->data.u.str.data > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.data;
|
||||
memcpy(useragentbuf, smp->data.u.str.area, i - 1);
|
||||
useragentbuf[i - 1] = 0;
|
||||
|
||||
useragent = (const char *)useragentbuf;
|
||||
|
||||
status = da_search(&global_deviceatlas.atlas, &devinfo,
|
||||
global_deviceatlas.useragentid, useragentbuf, 0);
|
||||
global_deviceatlas.useragentid, useragent, 0);
|
||||
|
||||
return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
|
||||
}
|
||||
|
||||
#define DA_MAX_HEADERS 32
|
||||
#define DA_MAX_HEADERS 24
|
||||
|
||||
static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||
{
|
||||
|
|
@ -449,10 +403,10 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
|||
struct channel *chn;
|
||||
struct htx *htx;
|
||||
struct htx_blk *blk;
|
||||
char vbuf[DA_MAX_HEADERS][1024];
|
||||
char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }};
|
||||
int i, nbh = 0;
|
||||
|
||||
if (unlikely(global_deviceatlas.daset == 0)) {
|
||||
if (global_deviceatlas.daset == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -460,17 +414,18 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
|||
|
||||
chn = (smp->strm ? &smp->strm->req : NULL);
|
||||
htx = smp_prefetch_htx(smp, chn, NULL, 1);
|
||||
if (unlikely(!htx))
|
||||
if (!htx)
|
||||
return 0;
|
||||
|
||||
i = 0;
|
||||
for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
|
||||
size_t vlen;
|
||||
char *pval;
|
||||
da_evidence_id_t evid;
|
||||
enum htx_blk_type type;
|
||||
struct ist n, v;
|
||||
char hbuf[64];
|
||||
char tval[1024];
|
||||
char hbuf[24] = { 0 };
|
||||
char tval[1024] = { 0 };
|
||||
|
||||
type = htx_get_blk_type(blk);
|
||||
|
||||
|
|
@ -483,18 +438,20 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
|||
continue;
|
||||
}
|
||||
|
||||
if (n.len > global_deviceatlas.maxhdrlen || n.len >= sizeof(hbuf)) {
|
||||
/* The HTTP headers used by the DeviceAtlas API are not longer */
|
||||
if (n.len >= sizeof(hbuf)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(hbuf, n.ptr, n.len);
|
||||
hbuf[n.len] = 0;
|
||||
pval = v.ptr;
|
||||
vlen = v.len;
|
||||
evid = -1;
|
||||
i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
|
||||
memcpy(tval, v.ptr, i);
|
||||
tval[i] = 0;
|
||||
pval = tval;
|
||||
vlen = i;
|
||||
|
||||
if (strcasecmp(hbuf, "Accept-Language") == 0) {
|
||||
evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
|
||||
|
|
@ -512,7 +469,7 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
|||
continue;
|
||||
}
|
||||
|
||||
vlen = pl;
|
||||
vlen -= global_deviceatlas.cookienamelen - 1;
|
||||
pval = p;
|
||||
evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -141,11 +141,6 @@ enum {
|
|||
DA_INITIAL_MEMORY_ESTIMATE = 1024 * 1024 * 14
|
||||
};
|
||||
|
||||
struct header_evidence_entry {
|
||||
const char *name;
|
||||
da_evidence_id_t id;
|
||||
};
|
||||
|
||||
struct da_config {
|
||||
unsigned int cache_size;
|
||||
unsigned int __reserved[15]; /* enough reserved keywords for future use */
|
||||
|
|
|
|||
|
|
@ -70,4 +70,4 @@ OPTIONS_OBJS += \
|
|||
addons/ot/src/vars.o
|
||||
endif
|
||||
|
||||
OT_CFLAGS := $(OT_CFLAGS) $(OT_DEFINE)
|
||||
OT_CFLAGS := $(OT_CFLAGS) -Iaddons/ot/include $(OT_DEFINE)
|
||||
|
|
|
|||
|
|
@ -48,12 +48,13 @@ Currently, tracers that support this API include Datadog, Jaeger, LightStep
|
|||
and Zipkin.
|
||||
|
||||
Note: The OpenTracing filter shouldn't be used for new designs as OpenTracing
|
||||
itself is no longer maintained nor supported by its authors. As such
|
||||
OpenTracing will be deprecated in 3.3 and removed in 3.5. A replacement
|
||||
filter based on OpenTelemetry is available since 3.4 with complete build
|
||||
instructions currently at:
|
||||
itself is no longer maintained nor supported by its authors. A
|
||||
replacement filter base on OpenTelemetry is currently under development
|
||||
and is expected to be ready around HAProxy 3.2. As such OpenTracing will
|
||||
be deprecated in 3.3 and removed in 3.5.
|
||||
|
||||
https://github.com/haproxytech/haproxy-opentelemetry/
|
||||
The OT filter was primarily tested with the Jaeger tracer, while configurations
|
||||
for both Datadog and Zipkin tracers were also set in the test directory.
|
||||
|
||||
The OT filter is a standard HAProxy filter, so what applies to others also
|
||||
applies to this one (of course, by that I mean what is described in the
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@
|
|||
do { \
|
||||
if (!(l) || (flt_ot_debug.level & (1 << (l)))) \
|
||||
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*s" f "\n"), \
|
||||
flt_ot_dbg_indent_level, FLT_OT_DBG_INDENT, ##__VA_ARGS__); \
|
||||
dbg_indent_level, FLT_OT_DBG_INDENT, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
# define FLT_OT_FUNC(f, ...) do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); flt_ot_dbg_indent_level += 3; } while (0)
|
||||
# define FLT_OT_RETURN(a) do { flt_ot_dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
|
||||
# define FLT_OT_RETURN_EX(a,t,f) do { flt_ot_dbg_indent_level -= 3; { t _r = (a); FLT_OT_DBG(1, "} = " f, _r); return _r; } } while (0)
|
||||
# define FLT_OT_FUNC(f, ...) do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); dbg_indent_level += 3; } while (0)
|
||||
# define FLT_OT_RETURN(a) do { dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
|
||||
# define FLT_OT_RETURN_EX(a,t,f) do { dbg_indent_level -= 3; { t _r = (a); FLT_OT_DBG(1, "} = " f, _r); return _r; } } while (0)
|
||||
# define FLT_OT_RETURN_INT(a) FLT_OT_RETURN_EX((a), int, "%d")
|
||||
# define FLT_OT_RETURN_PTR(a) FLT_OT_RETURN_EX((a), void *, "%p")
|
||||
# define FLT_OT_DBG_IFDEF(a,b) a
|
||||
|
|
@ -54,7 +54,7 @@ struct flt_ot_debug {
|
|||
};
|
||||
|
||||
|
||||
extern THREAD_LOCAL int flt_ot_dbg_indent_level;
|
||||
extern THREAD_LOCAL int dbg_indent_level;
|
||||
extern struct flt_ot_debug flt_ot_debug;
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
/***
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
/***
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#define FLT_OT_EVENT_DEF(a,b,c,d,e,f) { AN_##b##_##a, SMP_OPT_DIR_##b, SMP_VAL_FE_##c, SMP_VAL_BE_##d, e, f },
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -155,18 +155,12 @@ static void flt_ot_return_void(const struct filter *f, char **err)
|
|||
*/
|
||||
static int flt_ot_init(struct proxy *p, struct flt_conf *fconf)
|
||||
{
|
||||
static int warnings_emitted = 0;
|
||||
struct flt_ot_conf *conf = FLT_OT_DEREF(fconf, conf, NULL);
|
||||
char *err = NULL;
|
||||
int retval = FLT_OT_RET_ERROR;
|
||||
|
||||
FLT_OT_FUNC("%p, %p", p, fconf);
|
||||
|
||||
if (!warnings_emitted && !deprecated_directives_allowed) {
|
||||
warnings_emitted++;
|
||||
ha_warning("The opentracing filter was deprecated in haproxy 3.3 and will be removed in 3.5.\n");
|
||||
}
|
||||
|
||||
if (conf == NULL)
|
||||
FLT_OT_RETURN_INT(retval);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#define FLT_OT_GROUP_DEF(a,b,c) { a, b, c },
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#ifdef DEBUG_OT
|
||||
|
|
@ -261,7 +261,7 @@ int flt_ot_http_header_set(struct channel *chn, const char *prefix, const char *
|
|||
if (value == NULL) {
|
||||
/* Do nothing. */
|
||||
}
|
||||
else if (http_add_header(htx, ist_name, ist(value), 1) == 1) {
|
||||
else if (http_add_header(htx, ist_name, ist(value)) == 1) {
|
||||
retval = 0;
|
||||
|
||||
FLT_OT_DBG(3, "HTTP header '%s: %s' added", ist_name.ptr, value);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
static struct pool_head *pool_head_ot_span_context __read_mostly = NULL;
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#ifdef DEBUG_OT
|
||||
struct flt_ot_debug flt_ot_debug;
|
||||
THREAD_LOCAL int flt_ot_dbg_indent_level = 0;
|
||||
THREAD_LOCAL int dbg_indent_level = 0;
|
||||
#endif
|
||||
|
||||
#ifdef OTC_DBG_MEM
|
||||
|
|
@ -359,7 +359,8 @@ static int flt_ot_parse_cfg_sample(const char *file, int linenum, char **args, s
|
|||
*/
|
||||
static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, struct list *head, char **err)
|
||||
{
|
||||
int i, retval = ERR_NONE;
|
||||
struct flt_ot_conf_str *str = NULL;
|
||||
int i, retval = ERR_NONE;
|
||||
|
||||
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
|
||||
|
||||
|
|
@ -367,6 +368,9 @@ static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, stru
|
|||
if (flt_ot_conf_str_init(args[i], linenum, head, err) == NULL)
|
||||
retval |= ERR_ABORT | ERR_ALERT;
|
||||
|
||||
if (retval & ERR_CODE)
|
||||
flt_ot_conf_str_free(&str);
|
||||
|
||||
FLT_OT_RETURN_INT(retval);
|
||||
}
|
||||
|
||||
|
|
@ -640,7 +644,7 @@ static int flt_ot_parse_cfg_group(const char *file, int linenum, char **args, in
|
|||
|
||||
if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
|
||||
flt_ot_current_group = flt_ot_conf_group_init(args[1], linenum, &(flt_ot_current_config->groups), &err);
|
||||
if (flt_ot_current_group == NULL)
|
||||
if (flt_ot_current_config == NULL)
|
||||
retval |= ERR_ABORT | ERR_ALERT;
|
||||
}
|
||||
else if (pdata->keyword == FLT_OT_PARSE_GROUP_SCOPES) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
/***
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
static struct pool_head *pool_head_ot_scope_span __read_mostly = NULL;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#ifdef DEBUG_OT
|
||||
|
|
@ -41,7 +41,7 @@ void flt_ot_args_dump(char **args)
|
|||
|
||||
argc = flt_ot_args_count(args);
|
||||
|
||||
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), flt_ot_dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
|
||||
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
(void)fprintf(stderr, "'%s' ", args[i]);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "../include/include.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#ifdef DEBUG_OT
|
||||
|
|
@ -46,10 +46,10 @@ static void flt_ot_vars_scope_dump(struct vars *vars, const char *scope)
|
|||
|
||||
vars_rdlock(vars);
|
||||
for (i = 0; i < VAR_NAME_ROOTS; i++) {
|
||||
struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
|
||||
struct ceb_node *node = cebu64_first(&(vars->name_root[i]));
|
||||
|
||||
for ( ; node != NULL; node = cebu64_imm_next(&(vars->name_root[i]), node)) {
|
||||
struct var *var = container_of(node, struct var, name_node);
|
||||
for ( ; node != NULL; node = cebu64_next(&(vars->name_root[i]), node)) {
|
||||
struct var *var = container_of(node, struct var, node);
|
||||
|
||||
FLT_OT_DBG(2, "'%s.%016" PRIx64 "' -> '%.*s'", scope, var->name_hash, (int)b_data(&(var->data.u.str)), b_orig(&(var->data.u.str)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -389,9 +389,6 @@ listed below. Metrics from extra counters are not listed.
|
|||
| haproxy_server_max_connect_time_seconds |
|
||||
| haproxy_server_max_response_time_seconds |
|
||||
| haproxy_server_max_total_time_seconds |
|
||||
| haproxy_server_agent_status |
|
||||
| haproxy_server_agent_code |
|
||||
| haproxy_server_agent_duration_seconds |
|
||||
| haproxy_server_internal_errors_total |
|
||||
| haproxy_server_unsafe_idle_connections_current |
|
||||
| haproxy_server_safe_idle_connections_current |
|
||||
|
|
@ -407,7 +404,6 @@ listed below. Metrics from extra counters are not listed.
|
|||
+----------------------------------------------------+
|
||||
| haproxy_sticktable_size |
|
||||
| haproxy_sticktable_used |
|
||||
| haproxy_sticktable_local_updates |
|
||||
+----------------------------------------------------+
|
||||
|
||||
* Resolvers metrics
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
/* Prometheus exporter flags (ctx->flags) */
|
||||
#define PROMEX_FL_METRIC_HDR 0x00000001
|
||||
#define PROMEX_FL_BODYLESS_RESP 0x00000002
|
||||
/* unused: 0x00000002 */
|
||||
/* unused: 0x00000004 */
|
||||
/* unused: 0x00000008 */
|
||||
/* unused: 0x00000010 */
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
#include <haproxy/stats.h>
|
||||
#include <haproxy/stconn.h>
|
||||
#include <haproxy/stream.h>
|
||||
#include <haproxy/stress.h>
|
||||
#include <haproxy/task.h>
|
||||
#include <haproxy/tools.h>
|
||||
#include <haproxy/version.h>
|
||||
|
|
@ -83,8 +82,6 @@ struct promex_ctx {
|
|||
unsigned field_num; /* current field number (ST_I_PX_* etc) */
|
||||
unsigned mod_field_num; /* first field number of the current module (ST_I_PX_* etc) */
|
||||
int obj_state; /* current state among PROMEX_{FRONT|BACK|SRV|LI}_STATE_* */
|
||||
struct watcher px_watch; /* watcher to automatically update next pointer */
|
||||
struct watcher srv_watch; /* watcher to automatically update next pointer */
|
||||
struct list modules; /* list of promex modules to export */
|
||||
struct eb_root filters; /* list of filters to apply on metrics name */
|
||||
};
|
||||
|
|
@ -176,8 +173,6 @@ const struct ist promex_st_metric_desc[ST_I_PX_MAX] = {
|
|||
[ST_I_PX_CTIME] = IST("Avg. connect time for last 1024 successful connections."),
|
||||
[ST_I_PX_RTIME] = IST("Avg. response time for last 1024 successful connections."),
|
||||
[ST_I_PX_TTIME] = IST("Avg. total time for last 1024 successful connections."),
|
||||
[ST_I_PX_AGENT_STATUS] = IST("Status of last agent check, per state label value."),
|
||||
[ST_I_PX_AGENT_DURATION] = IST("Total duration of the latest server agent check, in seconds."),
|
||||
[ST_I_PX_QT_MAX] = IST("Maximum observed time spent in the queue"),
|
||||
[ST_I_PX_CT_MAX] = IST("Maximum observed time spent waiting for a connection to complete"),
|
||||
[ST_I_PX_RT_MAX] = IST("Maximum observed time spent waiting for a server response"),
|
||||
|
|
@ -245,8 +240,8 @@ void promex_register_module(struct promex_module *m)
|
|||
}
|
||||
|
||||
/* Pools used to allocate ref on Promex modules and filters */
|
||||
DECLARE_STATIC_TYPED_POOL(pool_head_promex_mod_ref, "promex_module_ref", struct promex_module_ref);
|
||||
DECLARE_STATIC_TYPED_POOL(pool_head_promex_metric_flt, "promex_metric_filter", struct promex_metric_filter);
|
||||
DECLARE_STATIC_POOL(pool_head_promex_mod_ref, "promex_module_ref", sizeof(struct promex_module_ref));
|
||||
DECLARE_STATIC_POOL(pool_head_promex_metric_flt, "promex_metric_filter", sizeof(struct promex_metric_filter));
|
||||
|
||||
/* Return the server status. */
|
||||
enum promex_srv_state promex_srv_status(struct server *sv)
|
||||
|
|
@ -349,10 +344,6 @@ static int promex_dump_ts(struct appctx *appctx, struct ist prefix,
|
|||
istcat(&n, prefix, PROMEX_MAX_NAME_LEN);
|
||||
istcat(&n, name, PROMEX_MAX_NAME_LEN);
|
||||
|
||||
/* In stress mode, force yielding on each metric. */
|
||||
if (STRESS_RUN1(istlen(*out), 0))
|
||||
goto full;
|
||||
|
||||
if ((ctx->flags & PROMEX_FL_METRIC_HDR) &&
|
||||
!promex_dump_ts_header(n, desc, type, out, max))
|
||||
goto full;
|
||||
|
|
@ -434,8 +425,9 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
|
|||
static struct ist prefix = IST("haproxy_process_");
|
||||
struct promex_ctx *ctx = appctx->svcctx;
|
||||
struct field val;
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist name, desc, out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
int ret = 1;
|
||||
|
||||
if (!stats_fill_info(stat_line_info, ST_I_INF_MAX, 0))
|
||||
|
|
@ -501,6 +493,7 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
return ret;
|
||||
full:
|
||||
|
|
@ -517,8 +510,9 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
|
|||
struct proxy *px = ctx->p[0];
|
||||
struct stats_module *mod = ctx->p[1];
|
||||
struct field val;
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist name, desc, out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
|
||||
int ret = 1;
|
||||
enum promex_front_state state;
|
||||
|
|
@ -632,6 +626,8 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
|
|||
}
|
||||
|
||||
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
|
||||
void *counters;
|
||||
|
||||
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE))
|
||||
continue;
|
||||
|
||||
|
|
@ -668,7 +664,8 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_FE))
|
||||
goto next_px2;
|
||||
|
||||
if (!mod->fill_stats(mod, px->extra_counters_fe, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, mod);
|
||||
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
return -1;
|
||||
|
||||
val = stats[ctx->field_num + ctx->mod_field_num];
|
||||
|
|
@ -695,6 +692,7 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
|
||||
/* Save pointers (0=current proxy, 1=current stats module) of the current context */
|
||||
|
|
@ -716,8 +714,9 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
|
|||
struct listener *li = ctx->p[1];
|
||||
struct stats_module *mod = ctx->p[2];
|
||||
struct field val;
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist name, desc, out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
|
||||
int ret = 1;
|
||||
enum li_status status;
|
||||
|
|
@ -820,6 +819,8 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
|
|||
}
|
||||
|
||||
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
|
||||
void *counters;
|
||||
|
||||
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI))
|
||||
continue;
|
||||
|
||||
|
|
@ -865,7 +866,8 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
|
|||
labels[lb_idx+1].name = ist("mod");
|
||||
labels[lb_idx+1].value = ist2(mod->name, strlen(mod->name));
|
||||
|
||||
if (!mod->fill_stats(mod, li->extra_counters, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
counters = EXTRA_COUNTERS_GET(li->extra_counters, mod);
|
||||
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
return -1;
|
||||
|
||||
val = stats[ctx->field_num + ctx->mod_field_num];
|
||||
|
|
@ -895,6 +897,7 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
/* Save pointers (0=current proxy, 1=current listener, 2=current stats module) of the current context */
|
||||
ctx->p[0] = px;
|
||||
|
|
@ -916,8 +919,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
struct stats_module *mod = ctx->p[1];
|
||||
struct server *sv;
|
||||
struct field val;
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist name, desc, out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
|
||||
int ret = 1;
|
||||
double secs;
|
||||
|
|
@ -941,6 +945,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (promex_filter_metric(appctx, prefix, name))
|
||||
continue;
|
||||
|
||||
if (!px)
|
||||
px = proxies_list;
|
||||
|
||||
while (px) {
|
||||
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
|
||||
unsigned int srv_state_count[PROMEX_SRV_STATE_COUNT] = { 0 };
|
||||
|
|
@ -1095,16 +1102,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
&val, labels, &out, max))
|
||||
goto full;
|
||||
next_px:
|
||||
px = watcher_next(&ctx->px_watch, px->next);
|
||||
px = px->next;
|
||||
}
|
||||
watcher_detach(&ctx->px_watch);
|
||||
ctx->flags |= PROMEX_FL_METRIC_HDR;
|
||||
|
||||
/* Prepare a new iteration for the next stat column.
|
||||
* Update ctx.p[0] via watcher.
|
||||
*/
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
px = proxies_list;
|
||||
}
|
||||
|
||||
/* Skip extra counters */
|
||||
|
|
@ -1117,6 +1117,8 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
}
|
||||
|
||||
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
|
||||
void *counters;
|
||||
|
||||
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE))
|
||||
continue;
|
||||
|
||||
|
|
@ -1127,6 +1129,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (promex_filter_metric(appctx, prefix, name))
|
||||
continue;
|
||||
|
||||
if (!px)
|
||||
px = proxies_list;
|
||||
|
||||
while (px) {
|
||||
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
|
||||
struct promex_metric metric;
|
||||
|
|
@ -1150,7 +1155,8 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
|
||||
goto next_px2;
|
||||
|
||||
if (!mod->fill_stats(mod, px->extra_counters_be, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
counters = EXTRA_COUNTERS_GET(px->extra_counters_be, mod);
|
||||
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
return -1;
|
||||
|
||||
val = stats[ctx->field_num + ctx->mod_field_num];
|
||||
|
|
@ -1161,39 +1167,26 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
|
|||
goto full;
|
||||
|
||||
next_px2:
|
||||
px = watcher_next(&ctx->px_watch, px->next);
|
||||
px = px->next;
|
||||
}
|
||||
watcher_detach(&ctx->px_watch);
|
||||
ctx->flags |= PROMEX_FL_METRIC_HDR;
|
||||
|
||||
/* Prepare a new iteration for the next stat column.
|
||||
* Update ctx.p[0] via watcher.
|
||||
*/
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
px = proxies_list;
|
||||
}
|
||||
|
||||
ctx->field_num += mod->stats_count;
|
||||
ctx->mod_field_num = 0;
|
||||
}
|
||||
|
||||
px = NULL;
|
||||
mod = NULL;
|
||||
|
||||
end:
|
||||
if (ret) {
|
||||
watcher_detach(&ctx->px_watch);
|
||||
mod = NULL;
|
||||
}
|
||||
|
||||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out)) {
|
||||
watcher_detach(&ctx->px_watch);
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
}
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
|
||||
/* Save pointers of the current context for dump resumption :
|
||||
* 0=current proxy, 1=current stats module
|
||||
* Note that p[0] is already automatically updated via px_watch.
|
||||
*/
|
||||
/* Save pointers (0=current proxy, 1=current stats module) of the current context */
|
||||
ctx->p[0] = px;
|
||||
ctx->p[1] = mod;
|
||||
return ret;
|
||||
full:
|
||||
|
|
@ -1211,8 +1204,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
struct server *sv = ctx->p[1];
|
||||
struct stats_module *mod = ctx->p[2];
|
||||
struct field val;
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist name, desc, out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
|
||||
int ret = 1;
|
||||
double secs;
|
||||
|
|
@ -1235,6 +1229,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (promex_filter_metric(appctx, prefix, name))
|
||||
continue;
|
||||
|
||||
if (!px)
|
||||
px = proxies_list;
|
||||
|
||||
while (px) {
|
||||
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
|
||||
enum promex_mt_type type;
|
||||
|
|
@ -1254,12 +1251,15 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
|
||||
goto next_px;
|
||||
|
||||
if (!sv)
|
||||
sv = px->srv;
|
||||
|
||||
while (sv) {
|
||||
labels[lb_idx].name = ist("server");
|
||||
labels[lb_idx].value = ist2(sv->id, strlen(sv->id));
|
||||
|
||||
if (!stats_fill_sv_line(px, sv, 0, stats, ST_I_PX_MAX, &(ctx->field_num)))
|
||||
goto error;
|
||||
return -1;
|
||||
|
||||
if ((ctx->flags & PROMEX_FL_NO_MAINT_SRV) && (sv->cur_admin & SRV_ADMF_MAINT))
|
||||
goto next_sv;
|
||||
|
|
@ -1342,7 +1342,6 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
secs = (double)sv->check.duration / 1000.0;
|
||||
val = mkf_flt(FN_DURATION, secs);
|
||||
break;
|
||||
|
||||
case ST_I_PX_REQ_TOT:
|
||||
if (px->mode != PR_MODE_HTTP) {
|
||||
sv = NULL;
|
||||
|
|
@ -1365,36 +1364,6 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
labels[lb_idx+1].value = promex_hrsp_code[ctx->field_num - ST_I_PX_HRSP_1XX];
|
||||
break;
|
||||
|
||||
case ST_I_PX_AGENT_STATUS:
|
||||
if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
|
||||
goto next_sv;
|
||||
|
||||
for (; ctx->obj_state < HCHK_STATUS_SIZE; ctx->obj_state++) {
|
||||
if (get_check_status_result(ctx->obj_state) < CHK_RES_FAILED)
|
||||
continue;
|
||||
val = mkf_u32(FO_STATUS, sv->agent.status == ctx->obj_state);
|
||||
check_state = get_check_status_info(ctx->obj_state);
|
||||
labels[lb_idx+1].name = ist("state");
|
||||
labels[lb_idx+1].value = ist(check_state);
|
||||
if (!promex_dump_ts(appctx, prefix, name, desc,
|
||||
type,
|
||||
&val, labels, &out, max))
|
||||
goto full;
|
||||
}
|
||||
ctx->obj_state = 0;
|
||||
goto next_sv;
|
||||
case ST_I_PX_AGENT_CODE:
|
||||
if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
|
||||
goto next_sv;
|
||||
val = mkf_u32(FN_OUTPUT, (sv->agent.status < HCHK_STATUS_L57DATA) ? 0 : sv->agent.code);
|
||||
break;
|
||||
case ST_I_PX_AGENT_DURATION:
|
||||
if (sv->agent.status < HCHK_STATUS_CHECKED)
|
||||
goto next_sv;
|
||||
secs = (double)sv->agent.duration / 1000.0;
|
||||
val = mkf_flt(FN_DURATION, secs);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1404,30 +1373,13 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
&val, labels, &out, max))
|
||||
goto full;
|
||||
next_sv:
|
||||
sv = watcher_next(&ctx->srv_watch, sv->next);
|
||||
sv = sv->next;
|
||||
}
|
||||
|
||||
next_px:
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
px = watcher_next(&ctx->px_watch, px->next);
|
||||
if (px) {
|
||||
/* Update ctx.p[1] via watcher. */
|
||||
watcher_attach(&ctx->srv_watch, px->srv);
|
||||
sv = ctx->p[1];
|
||||
}
|
||||
px = px->next;
|
||||
}
|
||||
watcher_detach(&ctx->px_watch);
|
||||
ctx->flags |= PROMEX_FL_METRIC_HDR;
|
||||
|
||||
/* Prepare a new iteration for the next stat column.
|
||||
* Update ctx.p[0]/p[1] via px_watch/srv_watch.
|
||||
*/
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
px = proxies_list;
|
||||
if (likely(px)) {
|
||||
watcher_attach(&ctx->srv_watch, px->srv);
|
||||
sv = ctx->p[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip extra counters */
|
||||
|
|
@ -1440,6 +1392,8 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
}
|
||||
|
||||
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
|
||||
void *counters;
|
||||
|
||||
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV))
|
||||
continue;
|
||||
|
||||
|
|
@ -1450,6 +1404,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if (promex_filter_metric(appctx, prefix, name))
|
||||
continue;
|
||||
|
||||
if (!px)
|
||||
px = proxies_list;
|
||||
|
||||
while (px) {
|
||||
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
|
||||
struct promex_metric metric;
|
||||
|
|
@ -1470,6 +1427,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
|
||||
goto next_px2;
|
||||
|
||||
if (!sv)
|
||||
sv = px->srv;
|
||||
|
||||
while (sv) {
|
||||
labels[lb_idx].name = ist("server");
|
||||
labels[lb_idx].value = ist2(sv->id, strlen(sv->id));
|
||||
|
|
@ -1481,8 +1441,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
goto next_sv2;
|
||||
|
||||
|
||||
if (!mod->fill_stats(mod, sv->extra_counters, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
goto error;
|
||||
counters = EXTRA_COUNTERS_GET(sv->extra_counters, mod);
|
||||
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
|
||||
return -1;
|
||||
|
||||
val = stats[ctx->field_num + ctx->mod_field_num];
|
||||
metric.type = ((val.type == FN_GAUGE) ? PROMEX_MT_GAUGE : PROMEX_MT_COUNTER);
|
||||
|
|
@ -1492,62 +1453,43 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
|
|||
goto full;
|
||||
|
||||
next_sv2:
|
||||
sv = watcher_next(&ctx->srv_watch, sv->next);
|
||||
sv = sv->next;
|
||||
}
|
||||
|
||||
next_px2:
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
px = watcher_next(&ctx->px_watch, px->next);
|
||||
if (px) {
|
||||
/* Update ctx.p[1] via watcher. */
|
||||
watcher_attach(&ctx->srv_watch, px->srv);
|
||||
sv = ctx->p[1];
|
||||
}
|
||||
px = px->next;
|
||||
}
|
||||
watcher_detach(&ctx->px_watch);
|
||||
ctx->flags |= PROMEX_FL_METRIC_HDR;
|
||||
|
||||
/* Prepare a new iteration for the next stat column.
|
||||
* Update ctx.p[0]/p[1] via px_watch/srv_watch.
|
||||
*/
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
px = proxies_list;
|
||||
if (likely(px)) {
|
||||
watcher_attach(&ctx->srv_watch, px->srv);
|
||||
sv = ctx->p[1];
|
||||
}
|
||||
}
|
||||
|
||||
ctx->field_num += mod->stats_count;
|
||||
ctx->mod_field_num = 0;
|
||||
}
|
||||
|
||||
end:
|
||||
if (ret) {
|
||||
watcher_detach(&ctx->px_watch);
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
mod = NULL;
|
||||
}
|
||||
px = NULL;
|
||||
sv = NULL;
|
||||
mod = NULL;
|
||||
|
||||
end:
|
||||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
|
||||
/* Save pointers of the current context for dump resumption :
|
||||
* 0=current proxy, 1=current server, 2=current stats module
|
||||
* Note that p[0]/p[1] are already automatically updated via px_watch/srv_watch.
|
||||
*/
|
||||
/* Decrement server refcount if it was saved through ctx.p[1]. */
|
||||
srv_drop(ctx->p[1]);
|
||||
if (sv)
|
||||
srv_take(sv);
|
||||
|
||||
/* Save pointers (0=current proxy, 1=current server, 2=current stats module) of the current context */
|
||||
ctx->p[0] = px;
|
||||
ctx->p[1] = sv;
|
||||
ctx->p[2] = mod;
|
||||
return ret;
|
||||
full:
|
||||
ret = 0;
|
||||
goto end;
|
||||
|
||||
error:
|
||||
watcher_detach(&ctx->px_watch);
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Dump metrics of module <mod>. It returns 1 on success, 0 if <out> is full and
|
||||
|
|
@ -1628,8 +1570,9 @@ static int promex_dump_ref_modules_metrics(struct appctx *appctx, struct htx *ht
|
|||
{
|
||||
struct promex_ctx *ctx = appctx->svcctx;
|
||||
struct promex_module_ref *ref = ctx->p[0];
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
int ret = 1;
|
||||
|
||||
if (!ref) {
|
||||
|
|
@ -1653,6 +1596,7 @@ static int promex_dump_ref_modules_metrics(struct appctx *appctx, struct htx *ht
|
|||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
ctx->p[0] = ref;
|
||||
return ret;
|
||||
|
|
@ -1667,8 +1611,9 @@ static int promex_dump_all_modules_metrics(struct appctx *appctx, struct htx *ht
|
|||
{
|
||||
struct promex_ctx *ctx = appctx->svcctx;
|
||||
struct promex_module *mod = ctx->p[0];
|
||||
struct channel *chn = sc_ic(appctx_sc(appctx));
|
||||
struct ist out = ist2(trash.area, 0);
|
||||
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
|
||||
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
|
||||
int ret = 1;
|
||||
|
||||
if (!mod) {
|
||||
|
|
@ -1692,6 +1637,7 @@ static int promex_dump_all_modules_metrics(struct appctx *appctx, struct htx *ht
|
|||
if (out.len) {
|
||||
if (!htx_add_data_atonce(htx, out))
|
||||
return -1; /* Unexpected and unrecoverable error */
|
||||
channel_add_input(chn, out.len);
|
||||
}
|
||||
ctx->p[0] = mod;
|
||||
return ret;
|
||||
|
|
@ -1706,7 +1652,7 @@ static int promex_dump_all_modules_metrics(struct appctx *appctx, struct htx *ht
|
|||
* Uses <appctx.ctx.stats.px> as a pointer to the current proxy and <sv>/<li>
|
||||
* as pointers to the current server/listener respectively.
|
||||
*/
|
||||
static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
|
||||
static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct htx *htx)
|
||||
{
|
||||
struct promex_ctx *ctx = appctx->svcctx;
|
||||
int ret;
|
||||
|
|
@ -1768,11 +1714,6 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
|
|||
ctx->field_num = ST_I_PX_PXNAME;
|
||||
ctx->mod_field_num = 0;
|
||||
appctx->st1 = PROMEX_DUMPER_BACK;
|
||||
|
||||
if (ctx->flags & PROMEX_FL_SCOPE_BACK) {
|
||||
/* Update ctx.p[0] via watcher. */
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
}
|
||||
__fallthrough;
|
||||
|
||||
case PROMEX_DUMPER_BACK:
|
||||
|
|
@ -1790,15 +1731,6 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
|
|||
ctx->field_num = ST_I_PX_PXNAME;
|
||||
ctx->mod_field_num = 0;
|
||||
appctx->st1 = PROMEX_DUMPER_SRV;
|
||||
|
||||
if (ctx->flags & PROMEX_FL_SCOPE_SERVER) {
|
||||
/* Update ctx.p[0] via watcher. */
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
if (likely(proxies_list)) {
|
||||
/* Update ctx.p[1] via watcher. */
|
||||
watcher_attach(&ctx->srv_watch, proxies_list->srv);
|
||||
}
|
||||
}
|
||||
__fallthrough;
|
||||
|
||||
case PROMEX_DUMPER_SRV:
|
||||
|
|
@ -1844,7 +1776,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
|
|||
return 1;
|
||||
|
||||
full:
|
||||
applet_have_more_data(appctx);
|
||||
sc_need_room(sc, channel_htx_recv_max(sc_ic(appctx_sc(appctx)), htx) + 1);
|
||||
return 0;
|
||||
error:
|
||||
/* unrecoverable error */
|
||||
|
|
@ -1857,11 +1789,12 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
|
|||
|
||||
/* Parse the query string of request URI to filter the metrics. It returns 1 on
|
||||
* success and -1 on error. */
|
||||
static int promex_parse_uri(struct appctx *appctx)
|
||||
static int promex_parse_uri(struct appctx *appctx, struct stconn *sc)
|
||||
{
|
||||
struct promex_ctx *ctx = appctx->svcctx;
|
||||
struct buffer *outbuf;
|
||||
struct htx *req_htx;
|
||||
struct channel *req = sc_oc(sc);
|
||||
struct channel *res = sc_ic(sc);
|
||||
struct htx *req_htx, *res_htx;
|
||||
struct htx_sl *sl;
|
||||
char *p, *key, *value;
|
||||
const char *end;
|
||||
|
|
@ -1871,13 +1804,10 @@ static int promex_parse_uri(struct appctx *appctx)
|
|||
int len;
|
||||
|
||||
/* Get the query-string */
|
||||
req_htx = htxbuf(DISGUISE(applet_get_inbuf(appctx)));
|
||||
req_htx = htxbuf(&req->buf);
|
||||
sl = http_get_stline(req_htx);
|
||||
if (!sl)
|
||||
goto bad_req_error;
|
||||
if (sl->info.req.meth == HTTP_METH_HEAD)
|
||||
ctx->flags |= PROMEX_FL_BODYLESS_RESP;
|
||||
|
||||
goto error;
|
||||
p = http_find_param_list(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), '?');
|
||||
if (!p)
|
||||
goto end;
|
||||
|
|
@ -1910,27 +1840,27 @@ static int promex_parse_uri(struct appctx *appctx)
|
|||
*p = 0;
|
||||
len = url_decode(key, 1);
|
||||
if (len == -1)
|
||||
goto bad_req_error;
|
||||
goto error;
|
||||
|
||||
/* decode value */
|
||||
if (value) {
|
||||
while (p < end && *p != '=' && *p != '&' && *p != '#')
|
||||
++p;
|
||||
if (*p == '=')
|
||||
goto bad_req_error;
|
||||
goto error;
|
||||
if (*p == '&')
|
||||
*(p++) = 0;
|
||||
else if (*p == '#')
|
||||
*p = 0;
|
||||
len = url_decode(value, 1);
|
||||
if (len == -1)
|
||||
goto bad_req_error;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(key, "scope") == 0) {
|
||||
default_scopes = 0; /* at least a scope defined, unset default scopes */
|
||||
if (!value)
|
||||
goto bad_req_error;
|
||||
goto error;
|
||||
else if (*value == 0)
|
||||
ctx->flags &= ~PROMEX_FL_SCOPE_ALL;
|
||||
else if (*value == '*' && *(value+1) == 0)
|
||||
|
|
@ -1961,14 +1891,14 @@ static int promex_parse_uri(struct appctx *appctx)
|
|||
}
|
||||
}
|
||||
if (!(ctx->flags & PROMEX_FL_SCOPE_MODULE))
|
||||
goto bad_req_error;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (strcmp(key, "metrics") == 0) {
|
||||
struct ist args;
|
||||
|
||||
if (!value)
|
||||
goto bad_req_error;
|
||||
goto error;
|
||||
|
||||
for (args = ist(value); istlen(args); args = istadv(istfind(args, ','), 1)) {
|
||||
struct eb32_node *node;
|
||||
|
|
@ -2019,28 +1949,30 @@ static int promex_parse_uri(struct appctx *appctx)
|
|||
ctx->flags |= (default_scopes | default_metrics_filter);
|
||||
return 1;
|
||||
|
||||
bad_req_error:
|
||||
error:
|
||||
err = &http_err_chunks[HTTP_ERR_400];
|
||||
goto error;
|
||||
channel_erase(res);
|
||||
res->buf.data = b_data(err);
|
||||
memcpy(res->buf.area, b_head(err), b_data(err));
|
||||
res_htx = htx_from_buf(&res->buf);
|
||||
channel_add_input(res, res_htx->data);
|
||||
return -1;
|
||||
|
||||
internal_error:
|
||||
err = &http_err_chunks[HTTP_ERR_500];
|
||||
goto error;
|
||||
|
||||
error:
|
||||
outbuf = DISGUISE(applet_get_outbuf(appctx));
|
||||
b_reset(outbuf);
|
||||
outbuf->data = b_data(err);
|
||||
memcpy(outbuf->area, b_head(err), b_data(err));
|
||||
applet_set_eoi(appctx);
|
||||
applet_set_eos(appctx);
|
||||
err = &http_err_chunks[HTTP_ERR_400];
|
||||
channel_erase(res);
|
||||
res->buf.data = b_data(err);
|
||||
memcpy(res->buf.area, b_head(err), b_data(err));
|
||||
res_htx = htx_from_buf(&res->buf);
|
||||
channel_add_input(res, res_htx->data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Send HTTP headers of the response. It returns 1 on success and 0 if <htx> is
|
||||
* full. */
|
||||
static int promex_send_headers(struct appctx *appctx, struct htx *htx)
|
||||
static int promex_send_headers(struct appctx *appctx, struct stconn *sc, struct htx *htx)
|
||||
{
|
||||
struct channel *chn = sc_ic(sc);
|
||||
struct htx_sl *sl;
|
||||
unsigned int flags;
|
||||
|
||||
|
|
@ -2055,10 +1987,11 @@ static int promex_send_headers(struct appctx *appctx, struct htx *htx)
|
|||
!htx_add_endof(htx, HTX_BLK_EOH))
|
||||
goto full;
|
||||
|
||||
channel_add_input(chn, htx->data);
|
||||
return 1;
|
||||
full:
|
||||
htx_reset(htx);
|
||||
applet_have_more_data(appctx);
|
||||
sc_need_room(sc, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2076,8 +2009,6 @@ static int promex_appctx_init(struct appctx *appctx)
|
|||
LIST_INIT(&ctx->modules);
|
||||
ctx->filters = EB_ROOT;
|
||||
appctx->st0 = PROMEX_ST_INIT;
|
||||
watcher_init(&ctx->px_watch, &ctx->p[0], offsetof(struct proxy, watcher_list));
|
||||
watcher_init(&ctx->srv_watch, &ctx->p[1], offsetof(struct server, watcher_list));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2091,14 +2022,11 @@ static void promex_appctx_release(struct appctx *appctx)
|
|||
struct promex_metric_filter *flt;
|
||||
struct eb32_node *node, *next;
|
||||
|
||||
if (appctx->st1 == PROMEX_DUMPER_BACK ||
|
||||
appctx->st1 == PROMEX_DUMPER_SRV) {
|
||||
watcher_detach(&ctx->px_watch);
|
||||
if (appctx->st1 == PROMEX_DUMPER_SRV) {
|
||||
struct server *srv = objt_server(ctx->p[1]);
|
||||
srv_drop(srv);
|
||||
}
|
||||
|
||||
if (appctx->st1 == PROMEX_DUMPER_SRV)
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
|
||||
list_for_each_entry_safe(ref, back, &ctx->modules, list) {
|
||||
LIST_DELETE(&ref->list);
|
||||
pool_free(pool_head_promex_mod_ref, ref);
|
||||
|
|
@ -2117,51 +2045,52 @@ static void promex_appctx_release(struct appctx *appctx)
|
|||
/* The main I/O handler for the promex applet. */
|
||||
static void promex_appctx_handle_io(struct appctx *appctx)
|
||||
{
|
||||
struct promex_ctx *ctx = appctx->svcctx;
|
||||
struct buffer *outbuf;
|
||||
struct htx *res_htx;
|
||||
struct stconn *sc = appctx_sc(appctx);
|
||||
struct stream *s = __sc_strm(sc);
|
||||
struct channel *req = sc_oc(sc);
|
||||
struct channel *res = sc_ic(sc);
|
||||
struct htx *req_htx, *res_htx;
|
||||
int ret;
|
||||
|
||||
if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR)))
|
||||
res_htx = htx_from_buf(&res->buf);
|
||||
|
||||
if (unlikely(se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR|SE_FL_SHR|SE_FL_SHW))))
|
||||
goto out;
|
||||
|
||||
/* Check if the input buffer is available. */
|
||||
outbuf = applet_get_outbuf(appctx);
|
||||
if (outbuf == NULL) {
|
||||
applet_have_more_data(appctx);
|
||||
if (!b_size(&res->buf)) {
|
||||
sc_need_room(sc, 0);
|
||||
goto out;
|
||||
}
|
||||
res_htx = htx_from_buf(outbuf);
|
||||
|
||||
switch (appctx->st0) {
|
||||
case PROMEX_ST_INIT:
|
||||
if (!applet_get_inbuf(appctx) || !applet_htx_input_data(appctx)) {
|
||||
if (!co_data(req)) {
|
||||
applet_need_more_data(appctx);
|
||||
break;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = promex_parse_uri(appctx);
|
||||
ret = promex_parse_uri(appctx, sc);
|
||||
if (ret <= 0) {
|
||||
if (ret == -1)
|
||||
applet_set_error(appctx);
|
||||
break;
|
||||
goto error;
|
||||
goto out;
|
||||
}
|
||||
appctx->st0 = PROMEX_ST_HEAD;
|
||||
appctx->st1 = PROMEX_DUMPER_INIT;
|
||||
__fallthrough;
|
||||
|
||||
case PROMEX_ST_HEAD:
|
||||
if (!promex_send_headers(appctx, res_htx))
|
||||
break;
|
||||
appctx->st0 = ((ctx->flags & PROMEX_FL_BODYLESS_RESP) ? PROMEX_ST_DONE : PROMEX_ST_DUMP);
|
||||
if (!promex_send_headers(appctx, sc, res_htx))
|
||||
goto out;
|
||||
appctx->st0 = ((s->txn->meth == HTTP_METH_HEAD) ? PROMEX_ST_DONE : PROMEX_ST_DUMP);
|
||||
__fallthrough;
|
||||
|
||||
case PROMEX_ST_DUMP:
|
||||
ret = promex_dump_metrics(appctx, res_htx);
|
||||
ret = promex_dump_metrics(appctx, sc, res_htx);
|
||||
if (ret <= 0) {
|
||||
if (ret == -1)
|
||||
applet_set_error(appctx);
|
||||
break;
|
||||
goto error;
|
||||
goto out;
|
||||
}
|
||||
appctx->st0 = PROMEX_ST_DONE;
|
||||
__fallthrough;
|
||||
|
|
@ -2175,36 +2104,41 @@ static void promex_appctx_handle_io(struct appctx *appctx)
|
|||
*/
|
||||
if (htx_is_empty(res_htx)) {
|
||||
if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
|
||||
applet_have_more_data(appctx);
|
||||
break;
|
||||
sc_need_room(sc, sizeof(struct htx_blk) + 1);
|
||||
goto out;
|
||||
}
|
||||
channel_add_input(res, 1);
|
||||
}
|
||||
res_htx->flags |= HTX_FL_EOM;
|
||||
applet_set_eoi(appctx);
|
||||
se_fl_set(appctx->sedesc, SE_FL_EOI);
|
||||
appctx->st0 = PROMEX_ST_END;
|
||||
__fallthrough;
|
||||
|
||||
case PROMEX_ST_END:
|
||||
applet_set_eos(appctx);
|
||||
se_fl_set(appctx->sedesc, SE_FL_EOS);
|
||||
}
|
||||
|
||||
htx_to_buf(res_htx, outbuf);
|
||||
|
||||
out:
|
||||
htx_to_buf(res_htx, &res->buf);
|
||||
|
||||
/* eat the whole request */
|
||||
applet_reset_input(appctx);
|
||||
if (co_data(req)) {
|
||||
req_htx = htx_from_buf(&req->buf);
|
||||
co_htx_skip(req, req_htx, co_data(req));
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
se_fl_set(appctx->sedesc, SE_FL_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct applet promex_applet = {
|
||||
.obj_type = OBJ_TYPE_APPLET,
|
||||
.flags = APPLET_FL_NEW_API|APPLET_FL_HTX,
|
||||
.name = "<PROMEX>", /* used for logging */
|
||||
.init = promex_appctx_init,
|
||||
.release = promex_appctx_release,
|
||||
.fct = promex_appctx_handle_io,
|
||||
.rcv_buf = appctx_htx_rcv_buf,
|
||||
.snd_buf = appctx_htx_snd_buf,
|
||||
};
|
||||
|
||||
static enum act_parse_ret service_parse_prometheus_exporter(const char **args, int *cur_arg, struct proxy *px,
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
|
||||
# to WURFL headers and libraries if needed.
|
||||
WURFL_INC = $(WURFL_SRC)
|
||||
WURFL_LIB = $(WURFL_SRC)
|
||||
OPTIONS_OBJS += addons/wurfl/wurfl.o
|
||||
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
|
||||
ifneq ($(WURFL_DEBUG),)
|
||||
WURFL_CFLAGS += -DWURFL_DEBUG
|
||||
endif
|
||||
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
|
||||
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
|
||||
endif
|
||||
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Dump certificates from the HAProxy stats or master socket to the filesystem
|
||||
# Experimental script
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
export BASEPATH=${BASEPATH:-/etc/haproxy}/
|
||||
export SOCKET=${SOCKET:-/var/run/haproxy-master.sock}
|
||||
export DRY_RUN=0
|
||||
export DEBUG=
|
||||
export VERBOSE=
|
||||
export M="@1 "
|
||||
export TMP
|
||||
|
||||
vecho() {
|
||||
|
||||
[ -n "$VERBOSE" ] && echo "$@"
|
||||
return 0
|
||||
}
|
||||
|
||||
read_certificate() {
|
||||
name=$1
|
||||
crt_filename=
|
||||
key_filename=
|
||||
|
||||
OFS=$IFS
|
||||
IFS=":"
|
||||
|
||||
while read -r key value; do
|
||||
case "$key" in
|
||||
"Crt filename")
|
||||
crt_filename="${value# }"
|
||||
key_filename="${value# }"
|
||||
;;
|
||||
"Key filename")
|
||||
key_filename="${value# }"
|
||||
;;
|
||||
esac
|
||||
done < <(echo "${M}show ssl cert ${name}" | socat "${SOCKET}" -)
|
||||
IFS=$OFS
|
||||
|
||||
if [ -z "$crt_filename" ] || [ -z "$key_filename" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# handle fields without a crt-base/key-base
|
||||
[ "${crt_filename:0:1}" != "/" ] && crt_filename="${BASEPATH}${crt_filename}"
|
||||
[ "${key_filename:0:1}" != "/" ] && key_filename="${BASEPATH}${key_filename}"
|
||||
|
||||
vecho "name:$name"
|
||||
vecho "crt:$crt_filename"
|
||||
vecho "key:$key_filename"
|
||||
|
||||
export NAME="$name"
|
||||
export CRT_FILENAME="$crt_filename"
|
||||
export KEY_FILENAME="$key_filename"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
cmp_certkey() {
|
||||
prev=$1
|
||||
new=$2
|
||||
|
||||
if [ ! -f "$prev" ]; then
|
||||
return 1;
|
||||
fi
|
||||
|
||||
if ! cmp -s <(openssl x509 -in "$prev" -noout -fingerprint -sha256) <(openssl x509 -in "$new" -noout -fingerprint -sha256); then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
dump_certificate() {
|
||||
name=$1
|
||||
prev_crt=$2
|
||||
prev_key=$3
|
||||
r="tmp.${RANDOM}"
|
||||
d="old.$(date +%s)"
|
||||
new_crt="$TMP/$(basename "$prev_crt").${r}"
|
||||
new_key="$TMP/$(basename "$prev_key").${r}"
|
||||
|
||||
if ! touch "${new_crt}" || ! touch "${new_key}"; then
|
||||
echo "[ALERT] ($$) : can't dump \"$name\", can't create tmp files" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "${M}dump ssl cert ${name}" | socat "${SOCKET}" - | openssl pkey >> "${new_key}"
|
||||
# use crl2pkcs7 as a way to dump multiple x509, storeutl could be used in modern versions of openssl
|
||||
echo "${M}dump ssl cert ${name}" | socat "${SOCKET}" - | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs >> "${new_crt}"
|
||||
|
||||
if ! cmp -s <(openssl x509 -in "${new_crt}" -pubkey -noout) <(openssl pkey -in "${new_key}" -pubout); then
|
||||
echo "[ALERT] ($$) : Private key \"${new_key}\" and public key \"${new_crt}\" don't match" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if cmp_certkey "${prev_crt}" "${new_crt}"; then
|
||||
echo "[NOTICE] ($$) : ${crt_filename} is already up to date" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
# dry run will just return before trying to move the files
|
||||
if [ "${DRY_RUN}" != "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# move the current certificates to ".old.timestamp"
|
||||
if [ -f "${prev_crt}" ] && [ -f "${prev_key}" ]; then
|
||||
mv "${prev_crt}" "${prev_crt}.${d}"
|
||||
[ "${prev_crt}" != "${prev_key}" ] && mv "${prev_key}" "${prev_key}.${d}"
|
||||
fi
|
||||
|
||||
# move the new certificates to old place
|
||||
mv "${new_crt}" "${prev_crt}"
|
||||
[ "${prev_crt}" != "${prev_key}" ] && mv "${new_key}" "${prev_key}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
dump_all_certificates() {
|
||||
echo "${M}show ssl cert" | socat "${SOCKET}" - | grep -v '^#' | grep -v '^$' | while read -r line; do
|
||||
export NAME
|
||||
export CRT_FILENAME
|
||||
export KEY_FILENAME
|
||||
|
||||
if read_certificate "$line"; then
|
||||
dump_certificate "$NAME" "$CRT_FILENAME" "$KEY_FILENAME"
|
||||
else
|
||||
echo "[WARNING] ($$) : can't dump \"$name\", crt/key filename details not found in \"show ssl cert\"" >&2
|
||||
fi
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage:"
|
||||
echo " $0 [options]* [cert]*"
|
||||
echo ""
|
||||
echo " Dump certificates from the HAProxy stats or master socket to the filesystem"
|
||||
echo " Require socat and openssl"
|
||||
echo " EXPERIMENTAL script, backup your files!"
|
||||
echo " The script will move your previous files to FILE.old.unixtimestamp (ex: foo.com.pem.old.1759044998)"
|
||||
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
|
||||
echo " -s, --socket <path> Use the stats socket at <path>"
|
||||
echo " -p, --path <path> Specify a base path for relative files (default: ${BASEPATH})"
|
||||
echo " -n, --dry-run Read certificates on the socket but don't dump them"
|
||||
echo " -d, --debug Debug mode, set -x"
|
||||
echo " -v, --verbose Verbose mode"
|
||||
echo " -h, --help This help"
|
||||
echo " -- End of options"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 -v -p ${BASEPATH} -S ${SOCKET}"
|
||||
echo " $0 -v -p ${BASEPATH} -S ${SOCKET} bar.com.rsa.pem"
|
||||
echo " $0 -v -p ${BASEPATH} -S ${SOCKET} -- foo.com.ecdsa.pem bar.com.rsa.pem"
|
||||
}
|
||||
|
||||
main() {
|
||||
while [ -n "$1" ]; do
|
||||
case "$1" in
|
||||
-S|--master-socket)
|
||||
SOCKET="$2"
|
||||
M="@1 "
|
||||
shift 2
|
||||
;;
|
||||
-s|--socket)
|
||||
SOCKET="$2"
|
||||
M=
|
||||
shift 2
|
||||
;;
|
||||
-p|--path)
|
||||
BASEPATH="$2/"
|
||||
shift 2
|
||||
;;
|
||||
-n|--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
-d|--debug)
|
||||
DEBUG=1
|
||||
shift
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage "$@"
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
echo "[ALERT] ($$) : Unknown option '$1'" >&2
|
||||
usage "$@"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
TMP=${TMP:-$(mktemp -d)}
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
dump_all_certificates
|
||||
else
|
||||
# compute the certificates names at the end of the command
|
||||
while [ -n "$1" ]; do
|
||||
if ! read_certificate "$1"; then
|
||||
echo "[ALERT] ($$) : can't dump \"$1\", crt/key filename details not found in \"show ssl cert\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
[ "${DRY_RUN}" = "0" ] && dump_certificate "$NAME" "$CRT_FILENAME" "$KEY_FILENAME"
|
||||
shift
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
trap 'rm -rf -- "$TMP"' EXIT
|
||||
main "$@"
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
export VERBOSE=1
|
||||
export TIMEOUT=90
|
||||
export MASTER_SOCKET="${MASTER_SOCKET:-/var/run/haproxy-master.sock}"
|
||||
|
||||
alert() {
|
||||
if [ "$VERBOSE" -ge "1" ]; then
|
||||
echo "[ALERT] $*" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
reload() {
|
||||
if [ -S "$MASTER_SOCKET" ]; then
|
||||
socat_addr="UNIX-CONNECT:${MASTER_SOCKET}"
|
||||
else
|
||||
case "$MASTER_SOCKET" in
|
||||
*:[0-9]*)
|
||||
socat_addr="TCP:${MASTER_SOCKET}"
|
||||
;;
|
||||
*)
|
||||
alert "Invalid master socket address '${MASTER_SOCKET}': expected a UNIX socket file or <host>:<port>"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "reload" | socat -t"${TIMEOUT}" "$socat_addr" - | {
|
||||
read -r status || { alert "No status received (connection error or timeout after ${TIMEOUT}s)."; exit 1; }
|
||||
case "$status" in
|
||||
"Success=1") ret=0 ;;
|
||||
"Success=0") ret=1 ;;
|
||||
*) alert "Unexpected response: '$status'"; exit 1 ;;
|
||||
esac
|
||||
|
||||
read -r _ # consume "--"
|
||||
|
||||
if [ "$VERBOSE" -ge 3 ] || { [ "$ret" = 1 ] && [ "$VERBOSE" -ge 2 ]; }; then
|
||||
cat >&2
|
||||
else
|
||||
cat >/dev/null
|
||||
fi
|
||||
|
||||
exit "$ret"
|
||||
}
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage:"
|
||||
echo " $0 [options]*"
|
||||
echo ""
|
||||
echo " Trigger a reload from the master socket"
|
||||
echo " Require socat"
|
||||
echo " EXPERIMENTAL script!"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -S, --master-socket <addr> Unix socket path or <host>:<port> (default: ${MASTER_SOCKET})"
|
||||
echo " -d, --debug Debug mode, set -x"
|
||||
echo " -t, --timeout Timeout (socat -t) (default: ${TIMEOUT})"
|
||||
echo " -s, --silent Silent mode (no output)"
|
||||
echo " -v, --verbose Verbose output (output from haproxy on failure)"
|
||||
echo " -vv --verbose=all Very verbose output (output from haproxy on success and failure)"
|
||||
echo " -h, --help This help"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 -S ${MASTER_SOCKET} -d ${TIMEOUT}"
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
while [ -n "$1" ]; do
|
||||
case "$1" in
|
||||
-S|--master-socket)
|
||||
MASTER_SOCKET="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t|--timeout)
|
||||
TIMEOUT="$2"
|
||||
shift 2
|
||||
;;
|
||||
-s|--silent)
|
||||
VERBOSE=0
|
||||
shift
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=2
|
||||
shift
|
||||
;;
|
||||
-vv|--verbose=all)
|
||||
VERBOSE=3
|
||||
shift
|
||||
;;
|
||||
-d|--debug)
|
||||
DEBUG=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage "$@"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[ALERT] ($$) : Unknown option '$1'" >&2
|
||||
usage "$@"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
reload
|
||||
|
|
@ -123,22 +123,6 @@ struct url_stat {
|
|||
#define FILT2_PRESERVE_QUERY 0x02
|
||||
#define FILT2_EXTRACT_CAPTURE 0x04
|
||||
|
||||
#define FILT_OUTPUT_FMT (FILT_COUNT_ONLY| \
|
||||
FILT_COUNT_STATUS| \
|
||||
FILT_COUNT_SRV_STATUS| \
|
||||
FILT_COUNT_COOK_CODES| \
|
||||
FILT_COUNT_TERM_CODES| \
|
||||
FILT_COUNT_URL_ONLY| \
|
||||
FILT_COUNT_URL_COUNT| \
|
||||
FILT_COUNT_URL_ERR| \
|
||||
FILT_COUNT_URL_TAVG| \
|
||||
FILT_COUNT_URL_TTOT| \
|
||||
FILT_COUNT_URL_TAVGO| \
|
||||
FILT_COUNT_URL_TTOTO| \
|
||||
FILT_COUNT_URL_BAVG| \
|
||||
FILT_COUNT_URL_BTOT| \
|
||||
FILT_COUNT_IP_COUNT)
|
||||
|
||||
unsigned int filter = 0;
|
||||
unsigned int filter2 = 0;
|
||||
unsigned int filter_invert = 0;
|
||||
|
|
@ -208,7 +192,7 @@ void help()
|
|||
" you can also use -n to start from earlier then field %d\n"
|
||||
" -query preserve the query string for per-URL (-u*) statistics\n"
|
||||
"\n"
|
||||
"Output format - **only one** may be used at a time\n"
|
||||
"Output format - only one may be used at a time\n"
|
||||
" -c only report the number of lines that would have been printed\n"
|
||||
" -pct output connect and response times percentiles\n"
|
||||
" -st output number of requests per HTTP status code\n"
|
||||
|
|
@ -914,9 +898,6 @@ int main(int argc, char **argv)
|
|||
if (!filter && !filter2)
|
||||
die("No action specified.\n");
|
||||
|
||||
if ((filter & FILT_OUTPUT_FMT) & ((filter & FILT_OUTPUT_FMT) - 1))
|
||||
die("Please, set only one output filter.\n");
|
||||
|
||||
if (filter & FILT_ACC_COUNT && !filter_acc_count)
|
||||
filter_acc_count=1;
|
||||
|
||||
|
|
@ -1571,10 +1552,6 @@ void filter_count_srv_status(const char *accept_field, const char *time_field, s
|
|||
if (!srv_node) {
|
||||
/* server not yet in the tree, let's create it */
|
||||
srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
|
||||
if (unlikely(!srv)) {
|
||||
fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
srv_node = &srv->node;
|
||||
memcpy(&srv_node->key, b, e - b);
|
||||
srv_node->key[e - b] = '\0';
|
||||
|
|
@ -1684,10 +1661,6 @@ void filter_count_url(const char *accept_field, const char *time_field, struct t
|
|||
*/
|
||||
if (unlikely(!ustat))
|
||||
ustat = calloc(1, sizeof(*ustat));
|
||||
if (unlikely(!ustat)) {
|
||||
fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ustat->nb_err = err;
|
||||
ustat->nb_req = 1;
|
||||
|
|
@ -1801,8 +1774,6 @@ void filter_count_ip(const char *source_field, const char *accept_field, const c
|
|||
*/
|
||||
if (unlikely(!ustat))
|
||||
ustat = calloc(1, sizeof(*ustat));
|
||||
if (!ustat)
|
||||
return;
|
||||
|
||||
ustat->nb_err = err;
|
||||
ustat->nb_req = 1;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ Wants=network-online.target
|
|||
[Service]
|
||||
EnvironmentFile=-/etc/default/haproxy
|
||||
EnvironmentFile=-/etc/sysconfig/haproxy
|
||||
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "CFGDIR=/etc/haproxy/conf.d" "EXTRAOPTS=-S /run/haproxy-master.sock"
|
||||
ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -f $CFGDIR -p $PIDFILE $EXTRAOPTS
|
||||
ExecReload=@SBINDIR@/haproxy -Ws -f $CONFIG -f $CFGDIR -c $EXTRAOPTS
|
||||
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "EXTRAOPTS=-S /run/haproxy-master.sock"
|
||||
ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -p $PIDFILE $EXTRAOPTS
|
||||
ExecReload=@SBINDIR@/haproxy -Ws -f $CONFIG -c $EXTRAOPTS
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
KillMode=mixed
|
||||
Restart=always
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ end
|
|||
# returns $node filled with the first node of ebroot $arg0
|
||||
define ebtree_first
|
||||
# browse ebtree left until encountering leaf
|
||||
set $node = (struct eb_node *)((struct eb_root*)$arg0)->b[0]
|
||||
set $node = (struct eb_node *)$arg0->b[0]
|
||||
while 1
|
||||
_ebtree_set_tag_node $node
|
||||
if $tag == 0
|
||||
|
|
@ -41,7 +41,7 @@ end
|
|||
# finds next ebtree node after $arg0, and returns it in $node
|
||||
define ebtree_next
|
||||
# get parent
|
||||
set $node = (struct eb_root *)((struct eb_node *)$arg0)->leaf_p
|
||||
set $node = (struct eb_root *)$arg0->leaf_p
|
||||
# Walking up from right branch, so we cannot be below root
|
||||
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
|
||||
while 1
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
# list info about open FD
|
||||
define fd_dump
|
||||
set $f = 0
|
||||
while $f < $g.maxsock
|
||||
if fdtab[$f].owner != 0
|
||||
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx cb=%p ownr=%p st=%#x refc=%#x tkov=%u gen=%u\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].iocb, fdtab[$f].owner, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation
|
||||
end
|
||||
set $f = $f + 1
|
||||
end
|
||||
end
|
||||
|
||||
# only those attached to a listener
|
||||
define fd_dump_listener
|
||||
set $f = 0
|
||||
while $f < $g.maxsock
|
||||
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_accept_iocb
|
||||
set $c = (struct listener *)fdtab[$f].owner
|
||||
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u listener=%p(%s): flg=%#x state=%d fe=%p(%s) acc=%p\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->name, $c->flags, $c->state, $c->bind_conf.frontend, $c->bind_conf.frontend.id, $c->bind_conf.accept
|
||||
end
|
||||
set $f = $f + 1
|
||||
end
|
||||
end
|
||||
|
||||
# only those attached to a connection
|
||||
define fd_dump_conn
|
||||
set $f = 0
|
||||
while $f < $g.maxsock
|
||||
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_conn_iocb
|
||||
set $c = (struct connection *)fdtab[$f].owner
|
||||
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u conn=%p: flg=%#x err=%#x ctrl=%p xprt=%p mux=%p", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->flags, $c->err_code, $c->ctrl, $c->xprt, $c->mux
|
||||
if *$c->target == OBJ_TYPE_LISTENER
|
||||
set $s = (struct session *)$c->owner
|
||||
printf " sess=%p: fe=%p id=%s age=%dms", $s, $s->fe, $s->fe->id, (*global_now_ns - $s->accept_ts) / 1000000
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
set $f = $f + 1
|
||||
end
|
||||
end
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Extracts the libs archives from a core dump
|
||||
*
|
||||
* Copyright (C) 2026 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Note: builds with no option under glibc, and can be built as a minimal
|
||||
* uploadable static executable using nolibc as well:
|
||||
gcc -o libs-from-core -nostdinc -nostdlib -s -Os -static -fno-ident \
|
||||
-fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables \
|
||||
-Wl,--gc-sections,--orphan-handling=discard,-znoseparate-code \
|
||||
-I /path/to/nolibc-sysroot/include libs-from-core.c
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void usage(const char *progname)
|
||||
{
|
||||
const char *slash = strrchr(progname, '/');
|
||||
|
||||
if (slash)
|
||||
progname = slash + 1;
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-q] <core_file>\n"
|
||||
"Locate a libs archive from an haproxy core dump and dump it to stdout.\n"
|
||||
"Arguments:\n"
|
||||
" -q Query mode: only report offset and length, do not dump\n"
|
||||
" core_file Core dump produced by haproxy\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Elf64_Ehdr *ehdr;
|
||||
Elf64_Phdr *phdr;
|
||||
struct stat st;
|
||||
uint8_t *mem;
|
||||
int i, fd;
|
||||
const char *fname;
|
||||
int quiet = 0;
|
||||
int arg;
|
||||
|
||||
for (arg = 1; arg < argc; arg++) {
|
||||
if (*argv[arg] != '-')
|
||||
break;
|
||||
|
||||
if (strcmp(argv[arg], "-q") == 0)
|
||||
quiet = 1;
|
||||
else if (strcmp(argv[arg], "--") == 0) {
|
||||
arg++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg < argc) {
|
||||
fname = argv[arg];
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = open(fname, O_RDONLY);
|
||||
|
||||
/* Let's just map the core dump as an ELF header */
|
||||
fstat(fd, &st);
|
||||
mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* get the program headers */
|
||||
ehdr = (Elf64_Ehdr *)mem;
|
||||
|
||||
/* check that it's really a core. Should be "\x7fELF" */
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
|
||||
fprintf(stderr, "ELF magic not found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
fprintf(stderr, "Only 64-bit ELF supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ehdr->e_type != ET_CORE) {
|
||||
fprintf(stderr, "ELF type %d, not a core dump.\n", ehdr->e_type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* OK we can safely go with program headers */
|
||||
phdr = (Elf64_Phdr *)(mem + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
uint64_t size = phdr[i].p_filesz;
|
||||
uint64_t offset = phdr[i].p_offset;
|
||||
int ret = 0;
|
||||
|
||||
if (phdr[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
//fprintf(stderr, "Scanning segment %d...\n", ehdr->e_phnum);
|
||||
//fprintf(stderr, "\r%-5d: off=%lx va=%lx sz=%lx ", i, (long)offset, (long)phdr[i].p_vaddr, (long)size);
|
||||
if (!size)
|
||||
continue;
|
||||
|
||||
if (size < 512) // minimum for a tar header
|
||||
continue;
|
||||
|
||||
/* tar magic */
|
||||
if (memcmp(mem + offset + 257, "ustar\0""00", 8) != 0)
|
||||
continue;
|
||||
|
||||
/* uid, gid */
|
||||
if (memcmp(mem + offset + 108, "0000000\0""0000000\0", 16) != 0)
|
||||
continue;
|
||||
|
||||
/* link name */
|
||||
if (memcmp(mem + offset + 157, "haproxy-libs-dump\0", 18) != 0)
|
||||
continue;
|
||||
|
||||
/* OK that's really it */
|
||||
|
||||
if (quiet)
|
||||
printf("offset=%#lx size=%#lx\n", offset, size);
|
||||
else
|
||||
ret = (write(1, mem + offset, size) == size) ? 0 : 1;
|
||||
return ret;
|
||||
}
|
||||
//fprintf(stderr, "\r%75s\n", "\r");
|
||||
fprintf(stderr, "libs archive not found. Was 'set-dumpable' set to 'libs' ?\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# show non-null memprofile entries with method, alloc/free counts/tot and caller
|
||||
|
||||
define memprof_dump
|
||||
set $i = 0
|
||||
set $meth={ "UNKN", "MALL", "CALL", "REAL", "STRD", "FREE", "P_AL", "P_FR", "STND", "VALL", "ALAL", "PALG", "MALG", "PVAL" }
|
||||
while $i < sizeof(memprof_stats) / sizeof(memprof_stats[0])
|
||||
if memprof_stats[$i].alloc_calls || memprof_stats[$i].free_calls
|
||||
set $m = memprof_stats[$i].method
|
||||
printf "m:%s ac:%u fc:%u at:%u ft:%u ", $meth[$m], \
|
||||
memprof_stats[$i].alloc_calls, memprof_stats[$i].free_calls, \
|
||||
memprof_stats[$i].alloc_tot, memprof_stats[$i].free_tot
|
||||
output/a memprof_stats[$i].caller
|
||||
printf "\n"
|
||||
end
|
||||
set $i = $i + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Find the post-mortem offset from a core dump
|
||||
*
|
||||
* Copyright (C) 2026 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Note: builds with no option under glibc, and can be built as a minimal
|
||||
* uploadable static executable using nolibc as well:
|
||||
gcc -o pm-from-core -nostdinc -nostdlib -s -Os -static -fno-ident \
|
||||
-fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables \
|
||||
-Wl,--gc-sections,--orphan-handling=discard,-znoseparate-code \
|
||||
-I /path/to/nolibc-sysroot/include pm-from-core.c
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
# define my_memmem memmem
|
||||
#else
|
||||
void *my_memmem(const void *haystack, size_t haystacklen,
|
||||
const void *needle, size_t needlelen)
|
||||
{
|
||||
while (haystacklen >= needlelen) {
|
||||
if (!memcmp(haystack, needle, needlelen))
|
||||
return (void*)haystack;
|
||||
haystack++;
|
||||
haystacklen--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MAGIC "POST-MORTEM STARTS HERE+7654321\0"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Elf64_Ehdr *ehdr;
|
||||
Elf64_Phdr *phdr;
|
||||
struct stat st;
|
||||
uint8_t *mem;
|
||||
int i, fd;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s <core_file>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
|
||||
/* Let's just map the core dump as an ELF header */
|
||||
fstat(fd, &st);
|
||||
mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* get the program headers */
|
||||
ehdr = (Elf64_Ehdr *)mem;
|
||||
|
||||
/* check that it's really a core. Should be "\x7fELF" */
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
|
||||
fprintf(stderr, "ELF magic not found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
fprintf(stderr, "Only 64-bit ELF supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ehdr->e_type != ET_CORE) {
|
||||
fprintf(stderr, "ELF type %d, not a core dump.\n", ehdr->e_type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* OK we can safely go with program headers */
|
||||
phdr = (Elf64_Phdr *)(mem + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
uint64_t size = phdr[i].p_filesz;
|
||||
uint64_t offset = phdr[i].p_offset;
|
||||
uint64_t vaddr = phdr[i].p_vaddr;
|
||||
uint64_t found_ofs;
|
||||
uint8_t *found;
|
||||
|
||||
if (phdr[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
//printf("Scanning segment %d...\n", ehdr->e_phnum);
|
||||
//printf("\r%-5d: off=%lx va=%lx sz=%lx ", i, (long)offset, (long)vaddr, (long)size);
|
||||
if (!size)
|
||||
continue;
|
||||
|
||||
if (size >= 1048576) // don't scan large segments
|
||||
continue;
|
||||
|
||||
found = my_memmem(mem + offset, size, MAGIC, sizeof(MAGIC) - 1);
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
found_ofs = found - (mem + offset);
|
||||
|
||||
printf("Found post-mortem magic in segment %d:\n", i);
|
||||
printf(" Core File Offset: 0x%lx (0x%lx + 0x%lx)\n", offset + found_ofs, offset, found_ofs);
|
||||
printf(" Runtime VAddr: 0x%lx (0x%lx + 0x%lx)\n", vaddr + found_ofs, vaddr, found_ofs);
|
||||
printf(" Segment Size: 0x%lx\n", size);
|
||||
printf("\nIn gdb, copy-paste this line:\n\n pm_init 0x%lx\n\n", vaddr + found_ofs);
|
||||
return 0;
|
||||
}
|
||||
//printf("\r%75s\n", "\r");
|
||||
printf("post-mortem magic not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@ define pools_dump
|
|||
set $idx=$idx + 1
|
||||
end
|
||||
|
||||
set $mem = (unsigned long)$total * $e->size
|
||||
printf "list=%#lx pool_head=%p name=%s size=%u alloc=%u used=%u mem=%lu\n", $p, $e, $e->name, $e->size, $total, $used, $mem
|
||||
set $mem = $total * $e->size
|
||||
printf "list=%#lx pool_head=%p name=%s size=%u alloc=%u used=%u mem=%u\n", $p, $e, $e->name, $e->size, $total, $used, $mem
|
||||
set $p = *(void **)$p
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
# lists all tasks in the wait queue whose ebroot pointed to by $arg0
|
||||
# e.g.
|
||||
# task_dump_wq &ha_tgroup_ctx[0].timers
|
||||
# task_dump_wq &ha_thread_ctx[0].timers
|
||||
#
|
||||
define task_dump_rq
|
||||
set $tot=0
|
||||
ebtree_first ($arg0)
|
||||
while ($node != 0)
|
||||
set $tot = $tot+1
|
||||
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).rq)
|
||||
printf "task %p ",$p
|
||||
p -pretty off -- /a *$p
|
||||
ebtree_next $node
|
||||
end
|
||||
printf "Total: %d tasks.\n",$tot
|
||||
end
|
||||
|
||||
define task_dump_wq
|
||||
set $tot=0
|
||||
ebtree_first ($arg0)
|
||||
while ($node != 0)
|
||||
set $tot = $tot+1
|
||||
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).wq)
|
||||
printf "task %p ",$p
|
||||
p -pretty off -- /a *$p
|
||||
ebtree_next $node
|
||||
end
|
||||
printf "Total: %d tasks.\n",$tot
|
||||
end
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# list info about current threads (ptr, now_ms, queue, current)
|
||||
define thread_dump
|
||||
set $t = 0
|
||||
while $t < $g.nbthread
|
||||
set $i = $ti[$t].pth_id
|
||||
set $h = $tc[$t].current
|
||||
printf "Tid %4d: pth=%p mono=%llu now_ms=%u fl=0x%02x rq=%d cq=%d current=%p\n", $t, $i, $tc[$t].curr_mono_time, (unsigned)(($tc[$t].curr_mono_time + now_offset)/1000000), $tc[$t].flags, $tc[$t].current_queue, $tc[$t].rq_total, $h
|
||||
set $t = $t + 1
|
||||
end
|
||||
end
|
||||
|
|
@ -59,9 +59,9 @@ struct ring_v2 {
|
|||
struct ring_v2a {
|
||||
size_t size; // storage size
|
||||
size_t rsvd; // header length (used for file-backed maps)
|
||||
size_t tail ALIGNED(64); // storage tail
|
||||
size_t head ALIGNED(64); // storage head
|
||||
char area[0] ALIGNED(64); // storage area begins immediately here
|
||||
size_t tail __attribute__((aligned(64))); // storage tail
|
||||
size_t head __attribute__((aligned(64))); // storage head
|
||||
char area[0] __attribute__((aligned(64))); // storage area begins immediately here
|
||||
};
|
||||
|
||||
/* display the message and exit with the code */
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ maintenance model and what the user wants is passed, then the LLM is invited to
|
|||
provide its opinion on the need for a backport and an explanation of the reason
|
||||
for its choice. This often helps the user to find a quick summary about the
|
||||
patch. All these outputs are then converted to a long HTML page with colors and
|
||||
radio buttons, where patches are preselected based on this classification,
|
||||
radio buttons, where patches are pre-selected based on this classification,
|
||||
that the user can consult and adjust, read the commits if needed, and the
|
||||
selected patches finally provide some copy-pastable commands in a text-area to
|
||||
select commit IDs to work on, typically in a form that's suitable for a simple
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
BEGININPUT
|
||||
BEGINCONTEXT
|
||||
|
||||
HAProxy's development cycle consists in one development branch, and multiple
|
||||
maintenance branches.
|
||||
|
||||
All the development is made into the development branch exclusively. This
|
||||
includes mostly new features, doc updates, cleanups and or course, fixes.
|
||||
|
||||
The maintenance branches, also called stable branches, never see any
|
||||
development, and only receive ultra-safe fixes for bugs that affect them,
|
||||
that are picked from the development branch.
|
||||
|
||||
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
|
||||
release, the development branch enters maintenance and a new development branch
|
||||
is created with a new, higher version. The current development branch is
|
||||
3.3-dev, and maintenance branches are 3.2 and below.
|
||||
|
||||
Fixes created in the development branch for issues that were introduced in an
|
||||
earlier branch are applied in descending order to each and every version till
|
||||
that branch that introduced the issue: 3.2 first, then 3.1, then 3.0, then 2.9
|
||||
and so on. This operation is called "backporting". A fix for an issue is never
|
||||
backported beyond the branch that introduced the issue. An important point is
|
||||
that the project maintainers really aim at zero regression in maintenance
|
||||
branches, so they're never willing to take any risk backporting patches that
|
||||
are not deemed strictly necessary.
|
||||
|
||||
Fixes consist of patches managed using the Git version control tool and are
|
||||
identified by a Git commit ID and a commit message. For this reason we
|
||||
indistinctly talk about backporting fixes, commits, or patches; all mean the
|
||||
same thing. When mentioning commit IDs, developers always use a short form
|
||||
made of the first 8 characters only, and expect the AI assistant to do the
|
||||
same.
|
||||
|
||||
It seldom happens that some fixes depend on changes that were brought by other
|
||||
patches that were not in some branches and that will need to be backported as
|
||||
well for the fix to work. In this case, such information is explicitly provided
|
||||
in the commit message by the patch's author in natural language.
|
||||
|
||||
Developers are serious and always indicate if a patch needs to be backported.
|
||||
Sometimes they omit the exact target branch, or they will say that the patch is
|
||||
"needed" in some older branch, but it means the same. If a commit message
|
||||
doesn't mention any backport instructions, it means that the commit does not
|
||||
have to be backported. And patches that are not strictly bug fixes nor doc
|
||||
improvements are normally not backported. For example, fixes for design
|
||||
limitations, architectural improvements and performance optimizations are
|
||||
considered too risky for a backport. Finally, all bug fixes are tagged as
|
||||
"BUG" at the beginning of their subject line. Patches that are not tagged as
|
||||
such are not bugs, and must never be backported unless their commit message
|
||||
explicitly requests so.
|
||||
|
||||
ENDCONTEXT
|
||||
|
||||
A developer is reviewing the development branch, trying to spot which commits
|
||||
need to be backported to maintenance branches. This person is already expert
|
||||
on HAProxy and everything related to Git, patch management, and the risks
|
||||
associated with backports, so he doesn't want to be told how to proceed nor to
|
||||
review the contents of the patch.
|
||||
|
||||
The goal for this developer is to get some help from the AI assistant to save
|
||||
some precious time on this tedious review work. In order to do a better job, he
|
||||
needs an accurate summary of the information and instructions found in each
|
||||
commit message. Specifically he needs to figure if the patch fixes a problem
|
||||
affecting an older branch or not, if it needs to be backported, if so to which
|
||||
branches, and if other patches need to be backported along with it.
|
||||
|
||||
The indented text block below after an "id" line and starting with a Subject line
|
||||
is a commit message from the HAProxy development branch that describes a patch
|
||||
applied to that branch, starting with its subject line, please read it carefully.
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
ENDINPUT
|
||||
BEGININSTRUCTION
|
||||
|
||||
You are an AI assistant that follows instruction extremely well. Help as much
|
||||
as you can, responding to a single question using a single response.
|
||||
|
||||
The developer wants to know if he needs to backport the patch above to fix
|
||||
maintenance branches, for which branches, and what possible dependencies might
|
||||
be mentioned in the commit message. Carefully study the commit message and its
|
||||
backporting instructions if any (otherwise it should probably not be backported),
|
||||
then provide a very concise and short summary that will help the developer decide
|
||||
to backport it, or simply to skip it.
|
||||
|
||||
Start by explaining in one or two sentences what you recommend for this one and why.
|
||||
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
|
||||
where X is a single word among:
|
||||
- "yes", if you recommend to backport the patch right now either because
|
||||
it explicitly states this or because it's a fix for a bug that affects
|
||||
a maintenance branch (3.2 or lower);
|
||||
- "wait", if this patch explicitly mentions that it must be backported, but
|
||||
only after waiting some time.
|
||||
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
|
||||
lack of explicit backport instructions, or it's just an improvement);
|
||||
- "uncertain" otherwise for cases not covered above
|
||||
|
||||
ENDINSTRUCTION
|
||||
|
||||
Explanation:
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
BEGININPUT
|
||||
BEGINCONTEXT
|
||||
|
||||
HAProxy's development cycle consists in one development branch, and multiple
|
||||
maintenance branches.
|
||||
|
||||
All the development is made into the development branch exclusively. This
|
||||
includes mostly new features, doc updates, cleanups and or course, fixes.
|
||||
|
||||
The maintenance branches, also called stable branches, never see any
|
||||
development, and only receive ultra-safe fixes for bugs that affect them,
|
||||
that are picked from the development branch.
|
||||
|
||||
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
|
||||
release, the development branch enters maintenance and a new development branch
|
||||
is created with a new, higher version. The current development branch is
|
||||
3.4-dev, and maintenance branches are 3.3 and below.
|
||||
|
||||
Fixes created in the development branch for issues that were introduced in an
|
||||
earlier branch are applied in descending order to each and every version till
|
||||
that branch that introduced the issue: 3.3 first, then 3.2, then 3.1, then 3.0
|
||||
and so on. This operation is called "backporting". A fix for an issue is never
|
||||
backported beyond the branch that introduced the issue. An important point is
|
||||
that the project maintainers really aim at zero regression in maintenance
|
||||
branches, so they're never willing to take any risk backporting patches that
|
||||
are not deemed strictly necessary.
|
||||
|
||||
Fixes consist of patches managed using the Git version control tool and are
|
||||
identified by a Git commit ID and a commit message. For this reason we
|
||||
indistinctly talk about backporting fixes, commits, or patches; all mean the
|
||||
same thing. When mentioning commit IDs, developers always use a short form
|
||||
made of the first 8 characters only, and expect the AI assistant to do the
|
||||
same.
|
||||
|
||||
It seldom happens that some fixes depend on changes that were brought by other
|
||||
patches that were not in some branches and that will need to be backported as
|
||||
well for the fix to work. In this case, such information is explicitly provided
|
||||
in the commit message by the patch's author in natural language.
|
||||
|
||||
Developers are serious and always indicate if a patch needs to be backported.
|
||||
Sometimes they omit the exact target branch, or they will say that the patch is
|
||||
"needed" in some older branch, but it means the same. If a commit message
|
||||
doesn't mention any backport instructions, it means that the commit does not
|
||||
have to be backported. And patches that are not strictly bug fixes nor doc
|
||||
improvements are normally not backported. For example, fixes for design
|
||||
limitations, architectural improvements and performance optimizations are
|
||||
considered too risky for a backport. Finally, all bug fixes are tagged as
|
||||
"BUG" at the beginning of their subject line. Patches that are not tagged as
|
||||
such are not bugs, and must never be backported unless their commit message
|
||||
explicitly requests so.
|
||||
|
||||
ENDCONTEXT
|
||||
|
||||
A developer is reviewing the development branch, trying to spot which commits
|
||||
need to be backported to maintenance branches. This person is already expert
|
||||
on HAProxy and everything related to Git, patch management, and the risks
|
||||
associated with backports, so he doesn't want to be told how to proceed nor to
|
||||
review the contents of the patch.
|
||||
|
||||
The goal for this developer is to get some help from the AI assistant to save
|
||||
some precious time on this tedious review work. In order to do a better job, he
|
||||
needs an accurate summary of the information and instructions found in each
|
||||
commit message. Specifically he needs to figure if the patch fixes a problem
|
||||
affecting an older branch or not, if it needs to be backported, if so to which
|
||||
branches, and if other patches need to be backported along with it.
|
||||
|
||||
The indented text block below after an "id" line and starting with a Subject line
|
||||
is a commit message from the HAProxy development branch that describes a patch
|
||||
applied to that branch, starting with its subject line, please read it carefully.
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
ENDINPUT
|
||||
BEGININSTRUCTION
|
||||
|
||||
You are an AI assistant that follows instruction extremely well. Help as much
|
||||
as you can, responding to a single question using a single response.
|
||||
|
||||
The developer wants to know if he needs to backport the patch above to fix
|
||||
maintenance branches, for which branches, and what possible dependencies might
|
||||
be mentioned in the commit message. Carefully study the commit message and its
|
||||
backporting instructions if any (otherwise it should probably not be backported),
|
||||
then provide a very concise and short summary that will help the developer decide
|
||||
to backport it, or simply to skip it.
|
||||
|
||||
Start by explaining in one or two sentences what you recommend for this one and why.
|
||||
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
|
||||
where X is a single word among:
|
||||
- "yes", if you recommend to backport the patch right now either because
|
||||
it explicitly states this or because it's a fix for a bug that affects
|
||||
a maintenance branch (3.3 or lower);
|
||||
- "wait", if this patch explicitly mentions that it must be backported, but
|
||||
only after waiting some time.
|
||||
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
|
||||
lack of explicit backport instructions, or it's just an improvement);
|
||||
- "uncertain" otherwise for cases not covered above
|
||||
|
||||
ENDINSTRUCTION
|
||||
|
||||
Explanation:
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
BEGININPUT
|
||||
BEGINCONTEXT
|
||||
|
||||
HAProxy's development cycle consists in one development branch, and multiple
|
||||
maintenance branches.
|
||||
|
||||
All the development is made into the development branch exclusively. This
|
||||
includes mostly new features, doc updates, cleanups and or course, fixes.
|
||||
|
||||
The maintenance branches, also called stable branches, never see any
|
||||
development, and only receive ultra-safe fixes for bugs that affect them,
|
||||
that are picked from the development branch.
|
||||
|
||||
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
|
||||
release, the development branch enters maintenance and a new development branch
|
||||
is created with a new, higher version. The current development branch is
|
||||
3.5-dev, and maintenance branches are 3.4 and below.
|
||||
|
||||
Fixes created in the development branch for issues that were introduced in an
|
||||
earlier branch are applied in descending order to each and every version till
|
||||
that branch that introduced the issue: 3.4 first, then 3.2, then 3.1, then 3.0
|
||||
and so on. This operation is called "backporting". A fix for an issue is never
|
||||
backported beyond the branch that introduced the issue. An important point is
|
||||
that the project maintainers really aim at zero regression in maintenance
|
||||
branches, so they're never willing to take any risk backporting patches that
|
||||
are not deemed strictly necessary.
|
||||
|
||||
Fixes consist of patches managed using the Git version control tool and are
|
||||
identified by a Git commit ID and a commit message. For this reason we
|
||||
indistinctly talk about backporting fixes, commits, or patches; all mean the
|
||||
same thing. When mentioning commit IDs, developers always use a short form
|
||||
made of the first 8 characters only, and expect the AI assistant to do the
|
||||
same.
|
||||
|
||||
It seldom happens that some fixes depend on changes that were brought by other
|
||||
patches that were not in some branches and that will need to be backported as
|
||||
well for the fix to work. In this case, such information is explicitly provided
|
||||
in the commit message by the patch's author in natural language.
|
||||
|
||||
Developers are serious and always indicate if a patch needs to be backported.
|
||||
Sometimes they omit the exact target branch, or they will say that the patch is
|
||||
"needed" in some older branch, but it means the same. If a commit message
|
||||
doesn't mention any backport instructions, it means that the commit does not
|
||||
have to be backported. And patches that are not strictly bug fixes nor doc
|
||||
improvements are normally not backported. For example, fixes for design
|
||||
limitations, architectural improvements and performance optimizations are
|
||||
considered too risky for a backport. Finally, all bug fixes are tagged as
|
||||
"BUG" at the beginning of their subject line. Patches that are not tagged as
|
||||
such are not bugs, and must never be backported unless their commit message
|
||||
explicitly requests so.
|
||||
|
||||
ENDCONTEXT
|
||||
|
||||
A developer is reviewing the development branch, trying to spot which commits
|
||||
need to be backported to maintenance branches. This person is already expert
|
||||
on HAProxy and everything related to Git, patch management, and the risks
|
||||
associated with backports, so he doesn't want to be told how to proceed nor to
|
||||
review the contents of the patch.
|
||||
|
||||
The goal for this developer is to get some help from the AI assistant to save
|
||||
some precious time on this tedious review work. In order to do a better job, he
|
||||
needs an accurate summary of the information and instructions found in each
|
||||
commit message. Specifically he needs to figure if the patch fixes a problem
|
||||
affecting an older branch or not, if it needs to be backported, if so to which
|
||||
branches, and if other patches need to be backported along with it.
|
||||
|
||||
The indented text block below after an "id" line and starting with a Subject line
|
||||
is a commit message from the HAProxy development branch that describes a patch
|
||||
applied to that branch, starting with its subject line, please read it carefully.
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
ENDINPUT
|
||||
BEGININSTRUCTION
|
||||
|
||||
You are an AI assistant that follows instruction extremely well. Help as much
|
||||
as you can, responding to a single question using a single response.
|
||||
|
||||
The developer wants to know if he needs to backport the patch above to fix
|
||||
maintenance branches, for which branches, and what possible dependencies might
|
||||
be mentioned in the commit message. Carefully study the commit message and its
|
||||
backporting instructions if any (otherwise it should probably not be backported),
|
||||
then provide a very concise and short summary that will help the developer decide
|
||||
to backport it, or simply to skip it.
|
||||
|
||||
Start by explaining in one or two sentences what you recommend for this one and why.
|
||||
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
|
||||
where X is a single word among:
|
||||
- "yes", if you recommend to backport the patch right now either because
|
||||
it explicitly states this or because it's a fix for a bug that affects
|
||||
a maintenance branch (3.4 or lower);
|
||||
- "wait", if this patch explicitly mentions that it must be backported, but
|
||||
only after waiting some time.
|
||||
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
|
||||
lack of explicit backport instructions, or it's just an improvement);
|
||||
- "uncertain" otherwise for cases not covered above
|
||||
|
||||
ENDINSTRUCTION
|
||||
|
||||
Explanation:
|
||||
|
|
@ -22,8 +22,7 @@ STABLE=$(cd "$HAPROXY_DIR" && git describe --tags "v${BRANCH}-dev0^" |cut -f1,2
|
|||
PATCHES_DIR="$PATCHES_PFX"-"$BRANCH"
|
||||
|
||||
(cd "$HAPROXY_DIR"
|
||||
# avoid git pull, it chokes on forced push
|
||||
git remote update origin; git reset origin/master;git checkout -f
|
||||
git pull
|
||||
last_file=$(ls -1 "$PATCHES_DIR"/*.patch 2>/dev/null | tail -n1)
|
||||
if [ -n "$last_file" ]; then
|
||||
restart=$(head -n1 "$last_file" | cut -f2 -d' ')
|
||||
|
|
|
|||
BIN
dev/phash/a.out
Executable file
BIN
dev/phash/a.out
Executable file
Binary file not shown.
|
|
@ -30,10 +30,10 @@ static const char *tevt_fd_types[16] = {
|
|||
};
|
||||
|
||||
static const char *tevt_hs_types[16] = {
|
||||
[ 0] = "-", [ 1] = "-", [ 2] = "-", [ 3] = "-",
|
||||
[ 4] = "snd_err", [ 5] = "truncated_shutr", [ 6] = "truncated_rcv_err", [ 7] = "-",
|
||||
[ 8] = "-", [ 9] = "-", [10] = "-", [11] = "-",
|
||||
[12] = "-", [13] = "-", [14] = "-", [15] = "-",
|
||||
[ 0] = "-", [ 1] = "-", [ 2] = "-", [ 3] = "rcv_err",
|
||||
[ 4] = "snd_err", [ 5] = "-", [ 6] = "-", [ 7] = "-",
|
||||
[ 8] = "-", [ 9] = "-", [10] = "-", [11] = "-",
|
||||
[12] = "-", [13] = "-", [14] = "-", [15] = "-",
|
||||
};
|
||||
|
||||
static const char *tevt_xprt_types[16] = {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ DeviceAtlas Device Detection
|
|||
|
||||
In order to add DeviceAtlas Device Detection support, you would need to download
|
||||
the API source code from https://deviceatlas.com/deviceatlas-haproxy-module.
|
||||
Once extracted, two modes are supported :
|
||||
|
||||
1/ Build HAProxy and DeviceAtlas in one command
|
||||
Once extracted :
|
||||
|
||||
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_SRC=<path to the API root folder>
|
||||
|
||||
|
|
@ -16,6 +14,10 @@ directory. Also, in the case the api cache support is not needed and/or a C++ to
|
|||
|
||||
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_SRC=<path to the API root folder> DEVICEATLAS_NOCACHE=1
|
||||
|
||||
However, if the API had been installed beforehand, DEVICEATLAS_SRC
|
||||
can be omitted. Note that the DeviceAtlas C API version supported is from the 3.x
|
||||
releases series (3.2.1 minimum recommended).
|
||||
|
||||
For HAProxy developers who need to verify that their changes didn't accidentally
|
||||
break the DeviceAtlas code, it is possible to build a dummy library provided in
|
||||
the addons/deviceatlas/dummy directory and to use it as an alternative for the
|
||||
|
|
@ -25,29 +27,6 @@ validate API changes :
|
|||
|
||||
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_SRC=$PWD/addons/deviceatlas/dummy
|
||||
|
||||
2/ Build and install DeviceAtlas according to https://docs.deviceatlas.com/apis/enterprise/c/<release version>/README.html
|
||||
|
||||
For example :
|
||||
In the deviceatlas library folder :
|
||||
$ cmake .
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
In the HAProxy folder :
|
||||
$ make TARGET=<target> USE_DEVICEATLAS=1
|
||||
|
||||
Note that if the -DCMAKE_INSTALL_PREFIX cmake option had been used, it is necessary to set as well DEVICEATLAS_LIB and
|
||||
DEVICEATLAS_INC as follow :
|
||||
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_INC=<CMAKE_INSTALL_PREFIX value>/include DEVICEATLAS_LIB=<CMAKE_INSTALL_PREFIX value>/lib
|
||||
|
||||
For example :
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX=/opt/local
|
||||
$ make
|
||||
$ sudo make install
|
||||
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_INC=/opt/local/include DEVICEATLAS_LIB=/opt/local/lib
|
||||
|
||||
Note that DEVICEATLAS_SRC is omitted in this case.
|
||||
|
||||
These are supported DeviceAtlas directives (see doc/configuration.txt) :
|
||||
- deviceatlas-json-file <path to the DeviceAtlas JSON data file>.
|
||||
- deviceatlas-log-level <number> (0 to 3, level of information returned by
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
A number of contributors are often embarrassed with coding style issues, they
|
||||
don't always know if they're doing it right, especially since the coding style
|
||||
has evolved along the years. What is explained here is not necessarily what is
|
||||
has elvoved along the years. What is explained here is not necessarily what is
|
||||
applied in the code, but new code should as much as possible conform to this
|
||||
style. Coding style fixes happen when code is replaced. It is useless to send
|
||||
patches to fix coding style only, they will be rejected, unless they belong to
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -526,6 +526,12 @@ In addition, some variables are related to the global runqueue:
|
|||
unsigned int grq_total; /* total number of entries in the global run queue, atomic */
|
||||
static unsigned int global_rqueue_ticks; /* insertion count in the grq, use rq_lock */
|
||||
|
||||
And others to the global wait queue:
|
||||
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
|
||||
__decl_aligned_rwlock(wq_lock); /* RW lock related to the wait queue */
|
||||
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
|
||||
|
||||
|
||||
2022-06-14 - progress on task affinity
|
||||
==========
|
||||
|
||||
|
|
|
|||
140
doc/haterm.txt
140
doc/haterm.txt
|
|
@ -1,140 +0,0 @@
|
|||
------
|
||||
HATerm
|
||||
------
|
||||
HAProxy's dummy HTTP
|
||||
server for benchmarks
|
||||
|
||||
1. Background
|
||||
-------------
|
||||
|
||||
HATerm is a dummy HTTP server that leverages the flexible and scalable
|
||||
architecture of HAProxy to ease benchmarking of HTTP agents in all versions of
|
||||
HTTP currently supported by HAProxy (HTTP/1, HTTP/2, HTTP/3), and both in clear
|
||||
and TLS / QUIC. It follows the same principle as its ancestor HTTPTerm [1],
|
||||
consisting in producing HTTP responses entirely configured by the request
|
||||
parameters (size, response time, status etc). It also preserves the spirit
|
||||
HTTPTerm which does not require any configuration beyond an optional listening
|
||||
address and a port number, though it also supports advanced configurations with
|
||||
the full spectrum of HAProxy features for specific testing. The goal remains
|
||||
to make it almost as fast as the original HTTPTerm so that it can become a
|
||||
de-facto replacement, with a compatible command line and request parameters
|
||||
that will not change users' habits.
|
||||
|
||||
[1] https://github.com/wtarreau/httpterm
|
||||
|
||||
|
||||
2. Compilation
|
||||
--------------
|
||||
|
||||
HATerm may be compiled in the same way as HAProxy but with "haterm" as Makefile
|
||||
target to provide on the "make" command line as follows:
|
||||
|
||||
$ make -j $(nproc) TARGET=linux-glibc haterm
|
||||
|
||||
HATerm supports HTTPS/SSL/TCP:
|
||||
|
||||
$ make TARGET=linux-glibc USE_OPENSSL=1
|
||||
|
||||
It also supports QUIC:
|
||||
|
||||
$ make -j $(nproc) TARGET=linux-glibc USE_OPENSSL=1 USE_QUIC=1 haterm
|
||||
|
||||
Technically speaking, it uses the regular HAProxy source and object code with a
|
||||
different command line parser. As such, all build options supported by HAProxy
|
||||
also apply to HATerm. See INSTALL for more details about how to compile them.
|
||||
|
||||
|
||||
3. Execution
|
||||
------------
|
||||
|
||||
HATerm is a very easy to use HTTP server with supports for all the HTTP
|
||||
versions. It displays its usage when run without argument or wrong arguments:
|
||||
|
||||
$ ./haterm
|
||||
Usage : haterm -L [<ip>]:<clear port>[:<TCP&QUIC SSL port>] [-L...]* [opts]
|
||||
where <opts> may be any combination of:
|
||||
-G <line> : multiple option; append <line> to the "global" section
|
||||
-F <line> : multiple option; append <line> to the "frontend" section
|
||||
-T <line> : multiple option; append <line> to the "traces" section
|
||||
-C : dump the configuration and exit
|
||||
-D : goes daemon
|
||||
-b <keysize> : RSA key size in bits (ex: "2048", "4096"...)
|
||||
-c <curves> : ECDSA curves (ex: "P-256", "P-384"...)
|
||||
-v : shows version
|
||||
-d : enable the traces for all http protocols
|
||||
--quic-bind-opts <opts> : append options to QUIC "bind" lines
|
||||
--tcp-bind-opts <opts> : append options to TCP "bind" lines
|
||||
|
||||
|
||||
Arguments -G, -F, -T permit to append one or multiple lines at the end of their
|
||||
respective sections. A tab character ('\t') is prepended at the beginning of
|
||||
the argument, and a line feed ('\n') is appended at the end. It is also
|
||||
possible to insert multiple lines at once using escape sequences '\n' and '\t'
|
||||
inside the string argument.
|
||||
|
||||
As HAProxy, HATerm may listen on several TCP/UDP addresses which can be
|
||||
provided by multiple "-L" options. To be functional, it needs at least one
|
||||
correct "-L" option to be set.
|
||||
|
||||
Examples:
|
||||
|
||||
$ ./haterm -L 127.0.0.1:8888 # listen on 127.0.0.1:8888 TCP address
|
||||
|
||||
$ ./haterm -L 127.0.0.1:8888:8889 # listen on 127.0.0.1:8888 TCP address,
|
||||
# 127.0.01:8889 SSL/TCP address,
|
||||
# and 127.0.01:8889 QUIC/UDP address
|
||||
|
||||
$ ./haterm -L 127.0.0.1:8888:8889 -L [::1]:8888:8889
|
||||
|
||||
With USE_QUIC_OPENSSL_COMPAT support, the user must configure a global
|
||||
section as for HAProxy. HATerm sets internally its configuration in.
|
||||
memory as this is done by HAProxy from configuration files:
|
||||
|
||||
$ ./haterm -L 127.0.0.1:8888:8889
|
||||
[NOTICE] (1371578) : haproxy version is 3.4-dev4-ba5eab-28
|
||||
[NOTICE] (1371578) : path to executable is ./haterm
|
||||
[ALERT] (1371578) : Binding [haterm cfgfile:12] for frontend
|
||||
___haterm_frontend___: this SSL library does not
|
||||
support the QUIC protocol. A limited compatibility
|
||||
layer may be enabled using the "limited-quic" global
|
||||
option if desired.
|
||||
|
||||
Such an alert may be fixed with "-G' option:
|
||||
|
||||
$ ./haterm -L 127.0.0.1:8888:8889 -G "limited-quic"
|
||||
|
||||
|
||||
When the SSL support is not compiled in, the second port is ignored. This is
|
||||
also the case for the QUIC support.
|
||||
|
||||
HATerm adjusts its responses depending on the requests it receives. An empty
|
||||
query string provides the information about how the URIs are understood by
|
||||
HATerm:
|
||||
|
||||
$ curl http://127.0.0.1:8888/?
|
||||
HAProxy's dummy HTTP server for benchmarks - version 3.4-dev4.
|
||||
All integer argument values are in the form [digits]*[kmgr] (r=random(0..1))
|
||||
The following arguments are supported to override the default objects :
|
||||
- /?s=<size> return <size> bytes.
|
||||
E.g. /?s=20k
|
||||
- /?r=<retcode> present <retcode> as the HTTP return code.
|
||||
E.g. /?r=404
|
||||
- /?c=<cache> set the return as not cacheable if <1.
|
||||
E.g. /?c=0
|
||||
- /?A=<req-after> drain the request body after sending the response.
|
||||
E.g. /?A=1
|
||||
- /?C=<close> force the response to use close if >0.
|
||||
E.g. /?C=1
|
||||
- /?K=<keep-alive> force the response to use keep-alive if >0.
|
||||
E.g. /?K=1
|
||||
- /?t=<time> wait <time> milliseconds before responding.
|
||||
E.g. /?t=500
|
||||
- /?k=<enable> Enable transfer encoding chunked with only one chunk
|
||||
if >0.
|
||||
- /?R=<enable> Enable sending random data if >0.
|
||||
|
||||
Note that those arguments may be cumulated on one line separated by a set of
|
||||
delimiters among [&?,;/] :
|
||||
- GET /?s=20k&c=1&t=700&K=30r HTTP/1.0
|
||||
- GET /?r=500?s=0?c=0?t=1000 HTTP/1.0
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
The buffer list API allows one to share a certain amount of buffers between
|
||||
multiple entities, which will each see their own as lists of buffers, while
|
||||
keeping a shared free list. The immediate use case is for muxes, which may
|
||||
keeping a sharedd free list. The immediate use case is for muxes, which may
|
||||
want to allocate up to a certain number of buffers per connection, shared
|
||||
among all streams. In this case, each stream will first request a new list
|
||||
for its own use, then may request extra entries from the free list. At any
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
-----------------------------------------
|
||||
Filters Guide - version 3.4
|
||||
Filters Guide - version 2.9
|
||||
( Last update: 2021-02-24 )
|
||||
------------------------------------------
|
||||
Author : Christopher Faulet
|
||||
|
|
@ -738,10 +738,10 @@ For instance :
|
|||
switch (an_bit) {
|
||||
case AN_REQ_WAIT_HTTP:
|
||||
if (/* A test on received headers before any other treatment */) {
|
||||
msg = ((chn->flags & CF_ISRESP) ? &s->txn.http->rsp : &s->txn.http->req);
|
||||
msg = ((chn->flags & CF_ISRESP) ? &s->txn->rsp : &s->txn->req);
|
||||
txn->status = 400;
|
||||
msg->msg_state = HTTP_MSG_ERROR;
|
||||
http_reply_and_close(s, s->txn.http->status, http_error_message(s));
|
||||
http_reply_and_close(s, s->txn->status, http_error_message(s));
|
||||
return -1; /* This is an error ! */
|
||||
}
|
||||
break;
|
||||
|
|
@ -1161,7 +1161,7 @@ Then, to finish, there are 2 informational callbacks :
|
|||
if we're retrying to send the request to the server after it failed. It
|
||||
could be useful to reset the filter context before receiving the true
|
||||
response.
|
||||
By checking s->txn.http->status, it is possible to know why this callback is
|
||||
By checking s->txn->status, it is possible to know why this callback is
|
||||
called. If it's a 1xx, we're called because of an informational
|
||||
message. Otherwise, it is a L7 retry.
|
||||
|
||||
|
|
|
|||
|
|
@ -539,22 +539,10 @@ message. These functions are used by HTX analyzers or by multiplexers.
|
|||
with the first block not removed, or NULL if everything was removed, and
|
||||
the amount of data drained.
|
||||
|
||||
- htx_xfer() transfers HTX blocks from an HTX message to another, stopping
|
||||
when a specific amount of bytes, including meta-data, was copied. If the
|
||||
tail block is a DATA block, it may be partially copied. All other block
|
||||
are transferred at once. By default, copied blocks are removed from the
|
||||
original HTX message and headers and trailers parts cannot be partially
|
||||
copied. But flags can be set to change the default behavior:
|
||||
|
||||
- HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||
- HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers
|
||||
part can be xferred
|
||||
- HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||
|
||||
- htx_xfer_blks() [DEPRECATED] transfers HTX blocks from an HTX message to
|
||||
another, stopping after the first block of a specified type is transferred
|
||||
or when a specific amount of bytes, including meta-data, was moved. If the
|
||||
tail block is a DATA block, it may be partially moved. All other block are
|
||||
- htx_xfer_blks() transfers HTX blocks from an HTX message to another,
|
||||
stopping after the first block of a specified type is transferred or when
|
||||
a specific amount of bytes, including meta-data, was moved. If the tail
|
||||
block is a DATA block, it may be partially moved. All other block are
|
||||
transferred at once or kept. This function returns a mixed value, with the
|
||||
last block moved, or NULL if nothing was moved, and the amount of data
|
||||
transferred. When HEADERS or TRAILERS blocks must be transferred, this
|
||||
|
|
|
|||
|
|
@ -52,16 +52,6 @@ make sure to respect this ordering when adding new ones.
|
|||
cast and freed. The const char* is here to leave more freedom to use consts
|
||||
when making such options lists.
|
||||
|
||||
- void hap_register_hlua_state_init(int (*fct)())
|
||||
|
||||
This adds a call to function <fct> to the list of functions to be called each
|
||||
time a new Lua state is created by hlua_init_state(). This allows source
|
||||
files other than hlua.c to register objects and functions into the Lua API
|
||||
without modifying hlua.c directly. The function <fct> must return an ERR_*
|
||||
code. If the errmsg pointer is set on return, it is printed as an alert,
|
||||
warning, or notice depending on the error code. If ERR_ABORT or ERR_FATAL is
|
||||
returned, haproxy will exit with status 1.
|
||||
|
||||
- void hap_register_per_thread_alloc(int (*fct)())
|
||||
|
||||
This adds a call to function <fct> to the list of functions to be called when
|
||||
|
|
@ -334,18 +324,6 @@ alphanumerically ordered:
|
|||
is that it allows to register multiple <post> callbacks and to register them
|
||||
elsewhere in the code.
|
||||
|
||||
- REGISTER_HLUA_STATE_INIT(fct)
|
||||
|
||||
Registers function <fct> to be called for each new Lua state created by
|
||||
hlua_init_state(). This allows source files other than hlua.c to register
|
||||
objects and functions into the Lua API without modifying hlua.c directly.
|
||||
This is done by registering a call to hap_register_hlua_state_init(fct) at
|
||||
stage STG_REGISTER. The function <fct> must be of type
|
||||
(int (*fct)(lua_State *L, char **errmsg)) and must return an ERR_* code.
|
||||
If the errmsg pointer is set on return, it is printed as an alert, warning,
|
||||
or notice depending on the error code. If ERR_ABORT or ERR_FATAL is returned,
|
||||
haproxy will exit with status 1.
|
||||
|
||||
- REGISTER_PER_THREAD_ALLOC(fct)
|
||||
|
||||
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
2025-08-13 - Memory allocation in HAProxy 3.3
|
||||
|
||||
The vast majority of dynamic memory allocations are performed from pools. Pools
|
||||
are optimized to store pre-calibrated objects of the right size for a given
|
||||
usage, try to favor locality and hot objects as much as possible, and are
|
||||
heavily instrumented to detect and help debug a wide class of bugs including
|
||||
buffer overflows, use-after-free, etc.
|
||||
|
||||
For objects of random sizes, or those used only at configuration time, pools
|
||||
are not suited, and the regular malloc/free family is available, in addition of
|
||||
a few others.
|
||||
|
||||
The standard allocation calls are intercepted at the code level (#define) when
|
||||
the code is compiled with -DDEBUG_MEM_STATS. For this reason, these calls are
|
||||
redefined as macros in "bug.h", and one must not try to use the pointers to
|
||||
such functions, as this may break DEBUG_MEM_STATS. This provides fine-grained
|
||||
stats about allocation/free per line of source code using locally implemented
|
||||
counters that can be consulted by "debug dev memstats". The calls are
|
||||
categorized into one of "calloc", "free", "malloc", "realloc", "strdup",
|
||||
"p_alloc", "p_free", the latter two designating pools. Extra calls such as
|
||||
memalign() and similar are also intercepted and counted as malloc.
|
||||
|
||||
Due to the nature of this replacement, DEBUG_MEM_STATS cannot see operations
|
||||
performed in libraries or dependencies.
|
||||
|
||||
In addition to DEBUG_MEM_STATS, when haproxy is built with USE_MEMORY_PROFILING
|
||||
the standard functions are wrapped by new ones defined in "activity.c", which
|
||||
also hold counters by call place. These ones are able to trace activity in
|
||||
libraries because the functions check the return pointer to figure where the
|
||||
call was made. The approach is different and relies on a large hash table. The
|
||||
files, function names and line numbers are not know, but by passing the pointer
|
||||
to dladdr(), we can often resolve most of these symbols. These operations are
|
||||
consulted via "show profiling memory". It must first be enabled either in the
|
||||
global config "profiling.memory on" or the CLI using "set profiling memory on".
|
||||
Memory profiling can also track pool allocations and frees thanks to knowing
|
||||
the size of the element and knowing a place where to store it. Some future
|
||||
evolutions might consider making this possible as well for pure malloc/free
|
||||
too by leveraging malloc_usable_size() a bit more.
|
||||
|
||||
Finally, 3.3 brought aligned allocations. These are made available via a new
|
||||
family of functions around ha_aligned_alloc() that simply map to either
|
||||
posix_memalign(), memalign() or _aligned_malloc() for CYGWIN, depending on
|
||||
which one is available. This latter one requires to pass the pointer to
|
||||
_aligned_free() instead of free(), so for this reason, all aligned allocations
|
||||
have to be released using ha_aligned_free(). Since this mostly happens on
|
||||
configuration elements, in practice it's not as inconvenient as it can sound.
|
||||
These functions are in reality macros handled in "bug.h" like the previous
|
||||
ones in order to deal with DEBUG_MEM_STATS. All "alloc" variants are reported
|
||||
in memstats as "malloc". All "zalloc" variants are reported in memstats as
|
||||
"calloc".
|
||||
|
||||
The currently available allocators are the following:
|
||||
|
||||
- void *ha_aligned_alloc(size_t align, size_t size)
|
||||
- void *ha_aligned_zalloc(size_t align, size_t size)
|
||||
|
||||
Equivalent of malloc() but aligned to <align> bytes. The alignment MUST be
|
||||
at least as large as one word and MUST be a power of two. The "zalloc"
|
||||
variant also zeroes the area on success. Both return NULL on failure.
|
||||
|
||||
- void *ha_aligned_alloc_safe(size_t align, size_t size)
|
||||
- void *ha_aligned_zalloc_safe(size_t align, size_t size)
|
||||
|
||||
Equivalent of malloc() but aligned to <align> bytes. The alignment is
|
||||
automatically adjusted to the nearest larger power of two that is at least
|
||||
as large as a word. The "zalloc" variant also zeroes the area on
|
||||
success. Both return NULL on failure.
|
||||
|
||||
- (type *)ha_aligned_alloc_typed(size_t count, type)
|
||||
(type *)ha_aligned_zalloc_typed(size_t count, type)
|
||||
|
||||
This macro returns an area aligned to the required alignment for type
|
||||
<type>, large enough for <count> objects of this type, and the result is a
|
||||
pointer of this type. The goal is to ease allocation of known structures
|
||||
whose alignment is not necessarily known to the developer (and to avoid
|
||||
encouraging to hard-code alignment). The cast in return also provides a
|
||||
last-minute control in case a wrong type is mistakenly used due to a poor
|
||||
copy-paste or an extra "*" after the type. When DEBUG_MEM_STATS is in use,
|
||||
the type is stored as a string in the ".extra" field so that it can be
|
||||
displayed in "debug dev memstats". The "zalloc" variant also zeroes the
|
||||
area on success. Both return NULL on failure.
|
||||
|
||||
- void ha_aligned_free(void *ptr)
|
||||
|
||||
Frees the area pointed to by ptr. It is the equivalent of free() but for
|
||||
objects allocated using one of the functions above.
|
||||
|
|
@ -245,30 +245,6 @@ mt_list_pop(l)
|
|||
#=========#
|
||||
|
||||
|
||||
mt_list_pop_locked(l)
|
||||
Removes the list's first element, returns it locked. If the list was empty,
|
||||
NULL is returned. A macro MT_LIST_POP_LOCKED() is provided for a
|
||||
more convenient use; instead of returning the list element, it will return
|
||||
the structure holding the element, taking care of preserving the NULL.
|
||||
|
||||
before:
|
||||
+---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||
#=>| L |<===>| A |<===>| B |<===>| C |<===>| D |<===>| E |<===>| F |<=#
|
||||
# +---+ +---+ +---+ +---+ +---+ +---+ +---+ #
|
||||
#=====================================================================#
|
||||
|
||||
after:
|
||||
+---+ +---+ +---+ +---+ +---+ +---+
|
||||
#=>| L |<===>| B |<===>| C |<===>| D |<===>| E |<===>| F |<=#
|
||||
# +---+ +---+ +---+ +---+ +---+ +---+ #
|
||||
#===========================================================#
|
||||
|
||||
+---+
|
||||
# x| A |x #
|
||||
# +---+ #
|
||||
#=========#
|
||||
|
||||
|
||||
_mt_list_lock_next(elt)
|
||||
Locks the link that starts at the next pointer of the designated element.
|
||||
The link is replaced by two locked pointers, and a pointer to the next
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
2025-08-11 - Pools structure and API
|
||||
2022-02-24 - Pools structure and API
|
||||
|
||||
1. Background
|
||||
-------------
|
||||
|
|
@ -239,6 +239,10 @@ currently in use:
|
|||
+------------+ +------------+ / is set at build time
|
||||
or -dMtag at boot time
|
||||
|
||||
Right now no provisions are made to return objects aligned on larger boundaries
|
||||
than those currently covered by malloc() (i.e. two pointers). This need appears
|
||||
from time to time and the layout above might evolve a little bit if needed.
|
||||
|
||||
|
||||
4. Storage in the process-wide shared pool
|
||||
------------------------------------------
|
||||
|
|
@ -353,22 +357,6 @@ struct pool_head *create_pool(char *name, uint size, uint flags)
|
|||
returned pointer is the new (or reused) pool head, or NULL upon error.
|
||||
Pools created this way must be destroyed using pool_destroy().
|
||||
|
||||
struct pool_head *create_aligned_pool(char *name, uint size, uint align, uint flags)
|
||||
Create a new pool named <name> for objects of size <size> bytes and
|
||||
aligned to <align> bytes (0 meaning use the platform's default). Pool
|
||||
names are truncated to their first 11 characters. Pools of very similar
|
||||
size will usually be merged if both have set the flag MEM_F_SHARED in
|
||||
<flags>. When DEBUG_DONT_SHARE_POOLS was set at build time, or
|
||||
"-dMno-merge" is passed on the executable's command line, the pools
|
||||
also need to have the exact same name to be merged. In addition, unless
|
||||
MEM_F_EXACT is set in <flags>, the object size will usually be rounded
|
||||
up to the size of pointers (16 or 32 bytes). MEM_F_UAF may be set on a
|
||||
per-pool basis to enable the UAF detection only for this specific pool,
|
||||
saving the massive overhead of global usage. The name that will appear
|
||||
in the pool upon merging is the name of the first created pool. The
|
||||
returned pointer is the new (or reused) pool head, or NULL upon error.
|
||||
Pools created this way must be destroyed using pool_destroy().
|
||||
|
||||
void *pool_destroy(struct pool_head *pool)
|
||||
Destroy pool <pool>, that is, all of its unused objects are freed and
|
||||
the structure is freed as well if the pool didn't have any used objects
|
||||
|
|
@ -482,20 +470,6 @@ complicate maintenance.
|
|||
|
||||
A few macros exist to ease the declaration of pools:
|
||||
|
||||
DECLARE_ALIGNED_POOL(ptr, name, size, align)
|
||||
Placed at the top level of a file, this declares a global memory pool
|
||||
as variable <ptr>, name <name> and size <size> bytes per element, all
|
||||
of which will be aligned to <align> bytes. The alignment will be
|
||||
rounded up to the next power of two and will be at least as large as a
|
||||
word on the platform. This is made via a call to REGISTER_ALIGNED_POOL()
|
||||
and by assigning the resulting pointer to variable <ptr>. <ptr> will be
|
||||
created of type "struct pool_head *". If the pool needs to be visible
|
||||
outside of the function (which is likely), it will also need to be
|
||||
declared somewhere as "extern struct pool_head *<ptr>;". It is
|
||||
recommended to place such declarations very early in the source file so
|
||||
that the variable is already known to all subsequent functions which
|
||||
may use it.
|
||||
|
||||
DECLARE_POOL(ptr, name, size)
|
||||
Placed at the top level of a file, this declares a global memory pool
|
||||
as variable <ptr>, name <name> and size <size> bytes per element. This
|
||||
|
|
@ -507,17 +481,6 @@ DECLARE_POOL(ptr, name, size)
|
|||
declarations very early in the source file so that the variable is
|
||||
already known to all subsequent functions which may use it.
|
||||
|
||||
DECLARE_STATIC_ALIGNED_POOL(ptr, name, size, align)
|
||||
Placed at the top level of a file, this declares a global memory pool
|
||||
as variable <ptr>, name <name> and size <size> bytes per element, all
|
||||
of which will be aligned to <align> bytes. The alignment will be
|
||||
rounded up to the next power of two and will be at least as large as a
|
||||
word on the platform. This is made via a call to REGISTER_ALIGNED_POOL()
|
||||
and by assigning the resulting pointer to local variable <ptr>. <ptr>
|
||||
will be created of type "static struct pool_head *". It is recommended
|
||||
to place such declarations very early in the source file so that the
|
||||
variable is already known to all subsequent functions which may use it.
|
||||
|
||||
DECLARE_STATIC_POOL(ptr, name, size)
|
||||
Placed at the top level of a file, this declares a static memory pool
|
||||
as variable <ptr>, name <name> and size <size> bytes per element. This
|
||||
|
|
@ -527,42 +490,6 @@ DECLARE_STATIC_POOL(ptr, name, size)
|
|||
early in the source file so that the variable is already known to all
|
||||
subsequent functions which may use it.
|
||||
|
||||
DECLARE_STATIC_TYPED_POOL(ptr, name, type[, extra[, align]])
|
||||
Placed at the top level of a file, this declares a global memory pool
|
||||
as variable <ptr>, name <name>, and configured to allocate objects of
|
||||
type <type>. It is optionally possible to grow these objects by <extra>
|
||||
bytes (e.g. if they contain some variable length data at the end), and
|
||||
to force them to be aligned to <align> bytes. If only alignment is
|
||||
desired without extra data, pass 0 as <extra>. Alignment must be at
|
||||
least as large as the type's, and a control is enforced at declaration
|
||||
time so that objects cannot be less aligned than what is promised to
|
||||
the compiler. The default alignment of zero indicates that the default
|
||||
one (from the type) should be used. This is made via a call to
|
||||
REGISTER_ALIGNED_POOL() and by assigning the resulting pointer to local
|
||||
variable <ptr>. <ptr> will be created of type "static struct pool_head
|
||||
*". It is recommended to place such declarations very early in the
|
||||
source file so that the variable is already known to all subsequent
|
||||
functions which may use it.
|
||||
|
||||
DECLARE_TYPED_POOL(ptr, name, type[, extra[, align]])
|
||||
Placed at the top level of a file, this declares a global memory pool
|
||||
as variable <ptr>, name <name>, and configured to allocate objects of
|
||||
type <type>. It is optionally possible to grow these objects by <extra>
|
||||
bytes (e.g. if they contain some variable length data at the end), and
|
||||
to force them to be aligned to <align> bytes. If only alignment is
|
||||
desired without extra data, pass 0 as <extra>. Alignment must be at
|
||||
least as large as the type's, and a control is enforced at declaration
|
||||
time so that objects cannot be less aligned than what is promised to
|
||||
the compiler. The default alignment of zero indicates that the default
|
||||
one (from the type) should be used. This is made via a call to
|
||||
REGISTER_ALIGNED_POOL() and by assigning the resulting pointer to
|
||||
variable <ptr>. <ptr> will be created of type "struct pool_head *". If
|
||||
the pool needs to be visible outside of the function (which is likely),
|
||||
it will also need to be declared somewhere as "extern struct pool_head
|
||||
*<ptr>;". It is recommended to place such declarations very early in
|
||||
the source file so that the variable is already known to all subsequent
|
||||
functions which may use it.
|
||||
|
||||
|
||||
6. Build options
|
||||
----------------
|
||||
|
|
|
|||
|
|
@ -1,238 +0,0 @@
|
|||
HAPROXY CORE PRINCIPLES
|
||||
|
||||
0. RULE ZERO: EXCEPTIONS AND JUSTIFICATION
|
||||
- These rules are mandatory; violations are bugs unless explicitly justified.
|
||||
- A violation is acceptable if accompanied by a comment explaining WHY the
|
||||
standard approach was insufficient (e.g., "Performance-critical bypass").
|
||||
- Reviews should flag unjustified violations but accept commented ones.
|
||||
|
||||
1. PROJECT ORGANIZATION
|
||||
- header files all under "include/", and split between haproxy/<file>-t.h for
|
||||
type definitions (types, enums, structures), and haproxy/<file>.h for static
|
||||
definitions and exported symbols. A few imported libs under include/import.
|
||||
- C source files in src/.
|
||||
- some API doc in doc/internals/api/ (not always up to date, check date or
|
||||
version at the top).
|
||||
|
||||
2. ENVIRONMENT AND DATA TYPES
|
||||
- The project targets 32/64-bit POSIX systems (little or big endian).
|
||||
- Char is signed or unsigned 8-bit, short signed 16-bit, int signed 32-bit.
|
||||
- Long and pointers always match the native word size. Long long is 64-bit.
|
||||
- Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long),
|
||||
ushort (unsigned short), ullong (unsigned long long), llong (long long),
|
||||
schar (signed char).
|
||||
- size_t is always the same size as long, but its underlying type is often
|
||||
uint on 32-bit and ulong on 64-bit. This is a frequent source of build
|
||||
errors on 32-bit platforms (e.g. passing a size_t where a long* is
|
||||
expected, or printing one with "%lu"); always cast in printf() (ulong
|
||||
with "%lu").
|
||||
- Main platforms are x86_64 and aarch64 with high thread counts (>=64).
|
||||
- Unaligned accesses are permitted for archs that support them; portable
|
||||
wrappers in net_helper.h (read_u32(), write_u32() etc).
|
||||
- signed integer wrapping well-defined via -fwrapv.
|
||||
- arch-specific asm() statements OK as long as equivalent C-code exists for
|
||||
generic archs.
|
||||
- Pointer arithmetics used a lot via container_of(), offset_of(), and void*
|
||||
casts.
|
||||
- Floating point not used.
|
||||
|
||||
3. MEMORY MANAGEMENT AND POOLS
|
||||
- Pools are used for runtime allocation; malloc/free are for boot code only.
|
||||
- pool_alloc() semantics match malloc(); the return must always be tested.
|
||||
- pool_alloc() and malloc() are not interchangeable: memory obtained from one
|
||||
must not be released using the other's free function.
|
||||
- pool_free() semantics match free(); it is a no-op on NULL.
|
||||
- pool_free() makes the pointer invalid immediately; it must not be touched
|
||||
or passed to pool_free() again.
|
||||
- Memory allocated from one pool must be released to the same pool.
|
||||
- ha_free() calls free() and sets the pointer to NULL before returning.
|
||||
- my_realloc2() frees the original pointer if the allocation fails.
|
||||
- never leave dangling pointers in structs after free().
|
||||
|
||||
4. BUFFER INVARIANTS (struct buffer)
|
||||
- Buffers are 4-word inline structs used for data in transit (wrapping,
|
||||
sliding window).
|
||||
- Members: area (storage), size (capacity), head (offset), data (count).
|
||||
- The area pointer is allowed to be NULL when size is zero.
|
||||
- always true: 0<=data<=size; always true when size>0: 0<=head<size.
|
||||
- contents start at <head>, for <data> bytes, and may wrap at the end of the
|
||||
storage area (area+size).
|
||||
- API (b_*, in buf.h and dynbuf.h) supports empty or unallocated buffers.
|
||||
- idempotent functions b_alloc() and b_free() use pools to manage the
|
||||
storage area and check <size> to know if alloc/free still needed.
|
||||
- a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere
|
||||
in data. ncbuf mandates holes of at least 8 bytes, while ncbmbuf relies on
|
||||
a bitmap of populated places.
|
||||
- another string API exists, "ist", representing a pointer and a length in a
|
||||
struct that is returned by inline functions and macros. It is described in
|
||||
doc/internals/api/ist.txt
|
||||
- buffers can switch to and from HTX, which is an internal representation of
|
||||
HTTP elements, with an API supporting header addition/modification/removal,
|
||||
start-line manipulation, data appending/consumption etc. HTX functions are
|
||||
all prefixed with "htx_". Between htx_from_buf() and htx_to_buf(), only the
|
||||
HTX API may be used, not the b_* API.
|
||||
|
||||
5. DATA MANIPULATION (CHUNKS, TRASH, LISTS, TREES)
|
||||
- Chunks use the buffer API but are NOT allowed to wrap.
|
||||
- Chunks are used for linear operations like chunk_printf().
|
||||
- Trash is a thread-local temporary buffer; scope stays within the caller.
|
||||
- trash always the same size as a buffer (global.tune.bufsize).
|
||||
- get_trash_chunk() provides rotating thread-local trash chunks. Since almost
|
||||
any function may itself call get_trash_chunk(), a returned chunk is only
|
||||
guaranteed valid until the next call into another function and must not be
|
||||
held across such a call. The rotation lets a single function safely use up
|
||||
to 3 distinct chunks at once for its own data manipulation.
|
||||
- For longer lived trash chunks, alloc_trash_chunk() is available but must be
|
||||
released using free_trash_chunk() on leaving.
|
||||
- standard doubly-linked lists (struct list) are provided via macros LIST_*.
|
||||
- LIST_INIT() must be used on new heads and elements. LIST_DELETE() only
|
||||
removes the element and does not reinitialize it, so the idempotent
|
||||
LIST_DEL_INIT() is generally preferred. Iterators like list_for_each_* are
|
||||
available, some safe against item removal. See doc/internals/api/list.txt
|
||||
for details (grep -i "^list_" to list available macros).
|
||||
- thread-safe doubly-linked lists (struct mt_list) are provided via macros
|
||||
mt_list_*. They work like lists and use compatible storage, though they may
|
||||
not be mixed. See doc/internals/api/mt_list.txt (grep -i "^mt_list_" to
|
||||
list available operations).
|
||||
- elastic binary trees (ebtree) are used for fast access (O(logN) operations,
|
||||
O(1) deletion). Idempotent deletion. Main functions are lookup, insert,
|
||||
delete, first, next, with type-based prefix eb{32,64,st,mb,pt}_*().
|
||||
- compact elastic binary trees (cebtree) are used for read-mostly focusing on
|
||||
space savings (O(logN) operations, but higher cost than ebtree). Same ops
|
||||
as ebtree, with type-based prefix ceb{32,u32,64,u64,s,is}_*.
|
||||
|
||||
6. THREAD SYNCHRONIZATION
|
||||
- Threads are started at boot (one per CPU) and persist for the process life,
|
||||
arranged in thread groups (tg) by cache locality.
|
||||
- Each thread has its own polling loop and scheduler. Total parallelism.
|
||||
- thread_isolate()/thread_release() for total thread isolation (very heavy).
|
||||
- "tid" always current thread number, "th_ctx" always current thread's context,
|
||||
"ti" current thread info.
|
||||
- "tgid" always current tg number, "tg_ctx" current tg context.
|
||||
- HA_ATOMIC_* for atomic operations on integers and pointers (includes load
|
||||
and store). DWCAS is available on some platforms but requires an equivalent
|
||||
fallback on the others (possibly a more complex operation, e.g. emulation
|
||||
using two or more CAS).
|
||||
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
|
||||
must be explicit (__ha_barrier_*).
|
||||
- Atomic loops must use CPU relaxation or exponential back-off.
|
||||
- For multiple changes at once, threads may use spinlocks (HA_SPIN_LOCK()/
|
||||
HA_SPIN_UNLOCK/HA_SPIN_TRYLOCK), and upgradable RW locks (HA_RWLOCK_*) if
|
||||
read accesses dominate.
|
||||
- No sleeping locks (mutex etc), only spinning/rwlocks/atomic loops.
|
||||
|
||||
7. SCHEDULING AND LATENCY
|
||||
- Latency is critical.
|
||||
- No runtime filesystem access, no blocking calls, no long loops.
|
||||
- Complex processing must be split into small steps; the task must yield.
|
||||
- CPUs are not dedicated to haproxy, high risk of a thread being interrupted
|
||||
by another process if it works too long, catastrophic if it happens with a
|
||||
lock held.
|
||||
- A watchdog kills the process if a task hogs a CPU for > few milliseconds.
|
||||
- Tasks vs Tasklets: Tasks have tree storage (rq) and timers (wq); tasklets
|
||||
use list elements instead of rq and are smaller (no wq). Only task.c/h may
|
||||
distinguish rq vs list access.
|
||||
- Tasks are aliased to tasklet while they are running (hence why some
|
||||
functions cast task to tasklets and conversely to access certain fields).
|
||||
- inter-thread task/tasklet wakeups always safe using the task_* API.
|
||||
- task/tasklet->state field must always be accessed atomically.
|
||||
|
||||
8. ARCHITECTURAL LAYERS (MUX AND STREAMS)
|
||||
- Naming: Lower layer (multiplexed), attached to the connection uses suffix
|
||||
'c' (h1c, h2c, qcc, muxc); Upper layer (demultiplexed/application, often a
|
||||
stream) uses suffix 's' (h1s, h2s, qcs, muxs).
|
||||
- Application layer stream (struct stream) has two stream connectors (stconn):
|
||||
front (scf) and back (scb). Responsible for processing requests/responses,
|
||||
deciding which server to route it, finding a backend connection or creating
|
||||
one, and exchanging data between the two sides.
|
||||
- Stream connectors link to a muxs or applet via a stream endpoint descriptor
|
||||
(sedesc/sd), and exchange data via buffers, which for an HTTP muxs are HTX
|
||||
buffers containing HTX blocks.
|
||||
- The sd carries the shared context between layers.
|
||||
- When a stream detaches from a mux, a new sd is allocated for the stream and
|
||||
the mux keeps its previous sd: stconn and muxs both always have a valid sd.
|
||||
- Front connections/streams are tied to the creator thread forever.
|
||||
- Idle back connections can be stolen via mux->takeover(), but become
|
||||
thread-bound once a stream attaches. => all streams of a mux are on the
|
||||
same thread.
|
||||
- session vs connection vs stream: connection is transport; session lasts for
|
||||
the client connection's life; stream are request/response pairs.
|
||||
- applets carry a context specific to the service being executed or the CLI
|
||||
command in appctx->svcctx, and this one is always zeroed before the handler
|
||||
is first called.
|
||||
|
||||
9. FUNCTION RETURN CONVENTIONS
|
||||
- Boolean style: Functions named as actions/sentences return 0 (failure) or
|
||||
non-zero (success).
|
||||
- Integer style: some syscall-like functions return <0 (error) or >=0 (success).
|
||||
- Tri-state style, e.g. counts: <0 (error), 0 (no progress), >0 (success).
|
||||
|
||||
10. DIAGNOSTICS AND SAFETY
|
||||
- When DEBUG_STRICT is set, ABORT_NOW() crashes the program immediately, and
|
||||
BUG_ON(cond[,msg]) crashes the program if the condition is true.
|
||||
- COUNT_IF() / CHECK_IF() only track if a condition occurs (non-fatal).
|
||||
- Glitches are counters for uncommon events used to detect hostile behavior.
|
||||
- strcpy(), strcat() and sprintf() are totally forbidden (the program will
|
||||
not build).
|
||||
|
||||
11. BASIC CODING STYLE
|
||||
- Linux Kernel-like, but uses tabs for indent, spaces for alignment. Function
|
||||
definitions have their opening brace on a new line, never on the same line.
|
||||
- All local variables must be declared at the beginning of the function
|
||||
block, before any executable statements (gnu89-like).
|
||||
- Avoid variable shadowing in code blocks.
|
||||
- Beware of local static and global variables.
|
||||
- Use const arguments whenever possible.
|
||||
- Avoid static storage when persistence is not needed.
|
||||
- Macros in uppercase unless they're used to wrap functions which then get a
|
||||
leading underscore.
|
||||
- Explicitly compare against 0 the return of functions that yield an integer
|
||||
which is not a boolean (e.g. strcmp), unless they return a boolean (e.g.
|
||||
isalnum) or a pointer (e.g. strchr).
|
||||
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
|
||||
mistakes.
|
||||
- turn non-zero integer to boolean using "!" or "!!".
|
||||
|
||||
12. BUILD AND TEST
|
||||
- Preferred build command:
|
||||
$ make -j$(nproc) TARGET=linux-glibc OPT_CFLAGS='-std=gnu89 -Os' \
|
||||
USE_OPENSSL=1 USE_QUIC_OPENSSL_COMPAT=1 USE_QUIC=1 USE_LUA=1
|
||||
- Individual files can be tested by passing src/file.o as a make argument.
|
||||
- Compiler warnings are not permitted for new code.
|
||||
|
||||
13. COMMIT MESSAGES AND DOCUMENTATION
|
||||
- Commit messages must follow the project's strict format below. Do not try
|
||||
to learn better from previous commits, which might be wrong during reviews.
|
||||
- Structure: <TAG>: <location>: <subject> (max ~70 chars), then blank line,
|
||||
then description.
|
||||
- Tags:
|
||||
- CLEANUP: spelling fixes, refactoring, no new code nor functional change.
|
||||
- MINOR: new feature or low-impact change, may be backported if needed.
|
||||
- MEDIUM: new feature or change with moderate severity/impact/risk.
|
||||
- MAJOR: new feature or change with important severity/impact/risk.
|
||||
- OPTIM: Performance improvements, may always be reverted if it breaks.
|
||||
- DOC: Documentation updates or fixes.
|
||||
- BUG/<severity>: Fixes a bug. Specify if regression or long-standing.
|
||||
Valid severities are MINOR (low impact), MEDIUM (perf/stability risk
|
||||
in uncommon configs, MAJOR (most configs), CRITICAL (stability risk
|
||||
without workaround).
|
||||
- Regressions: Find original commit via `git blame`; designate using
|
||||
`git log -1 --format='%h ("%s")'` and version via `git describe --tags`.
|
||||
- Location: subsystem (stream, tasks, mux-h2, qpack etc).
|
||||
- Description: Explain technical "WHY", "HOW", and technical impact. Explain
|
||||
how to trigger the bug for developer testing.
|
||||
- Backports: only for fixes, mention versions ("Must be backported to 3.0").
|
||||
- Style: No generic messages like "fix(xxx): blah". Be technically precise.
|
||||
- Do not mix spelling fixes in comments (not important) with other changes.
|
||||
However it's preferred to have a single commit for many typo fixes at once.
|
||||
- Spelling mistakes in user-visible parts (doc, logs, traces, error messages)
|
||||
must be in their own commit (may need backport).
|
||||
- One commit per bug.
|
||||
- Example:
|
||||
BUG/MEDIUM: sample: fix null pointer dereference in h1_parse_line
|
||||
|
||||
When parsing malformed headers, the line buffer was not initialized.
|
||||
This caused a crash on certain edge cases. Let's fix this by always
|
||||
initializing the line buffer when first calling the parser. This was
|
||||
brought by commit 04c9e8f5 ("MINOR: add h1_parse_line") in latest -dev
|
||||
so no backport is needed.
|
||||
|
|
@ -11,7 +11,7 @@ default init, this was controversial but fedora and archlinux already uses it.
|
|||
At this time HAProxy still had a multi-process model, and the way haproxy is
|
||||
working was incompatible with the daemon mode.
|
||||
|
||||
Systemd is compatible with traditional forking services, but somehow HAProxy
|
||||
Systemd is compatible with traditionnal forking services, but somehow HAProxy
|
||||
is different. To work correctly, systemd needs a main PID, this is the PID of
|
||||
the process that systemd will supervises.
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ However the wrapper suffered from several problems:
|
|||
|
||||
### mworker V1
|
||||
|
||||
HAProxy 1.8 got rid of the wrapper which was replaced by the master worker
|
||||
HAProxy 1.8 got ride of the wrapper which was replaced by the master worker
|
||||
mode. This first version was basically a reintegration of the wrapper features
|
||||
within HAProxy. HAProxy is launched with the -W flag, read the configuration and
|
||||
then fork. In mworker mode, the master is usually launched as a root process,
|
||||
|
|
@ -86,7 +86,7 @@ retrieved automatically.
|
|||
The master is supervising the workers, when a current worker (not a previous one
|
||||
from before the reload) is exiting without being asked for a reload, the master
|
||||
will emit an "exit-on-failure" error and will kill every workers with a SIGTERM
|
||||
and exits with the same error code than the failed worker, this behavior can be
|
||||
and exits with the same error code than the failed master, this behavior can be
|
||||
changed by using the "no exit-on-failure" option in the global section.
|
||||
|
||||
While the master is supervising the workers using the wait() function, the
|
||||
|
|
@ -186,8 +186,8 @@ number that can be found in HAPROXY_PROCESSES. With this change the stats socket
|
|||
in the configuration is less useful and everything can be done from the master
|
||||
CLI.
|
||||
|
||||
With 2.7, the reload mechanism of the master CLI evolved, with previous versions,
|
||||
this mechanism was asynchronous, so once the `reload` command was received, the
|
||||
With 2.7, the reload mecanism of the master CLI evolved, with previous versions,
|
||||
this mecanism was asynchronous, so once the `reload` command was received, the
|
||||
master would reload, the active master CLI connection was closed, and there was
|
||||
no way to return a status as a response to the `reload` command. To achieve a
|
||||
synchronous reload, a dedicated sockpair is used, one side uses a master CLI
|
||||
|
|
@ -208,38 +208,3 @@ starts with -st to achieve a hard stop on the previous worker.
|
|||
Version 3.0 got rid of the libsystemd dependencies for sd_notify() after the
|
||||
events of xz/openssh, the function is now implemented directly in haproxy in
|
||||
src/systemd.c.
|
||||
|
||||
### mworker V3
|
||||
|
||||
This version was implemented with HAProxy 3.1, the goal was to stop parsing and
|
||||
applying the configuration in the master process.
|
||||
|
||||
One of the caveats of the previous implementation was that the parser could take
|
||||
a lot of time, and the master process would be stuck in the parser instead of
|
||||
handling its polling loop, signals etc. Some parts of the configuration parsing
|
||||
could also be less reliable with third-party code (EXTRA_OBJS), it could, for
|
||||
example, allow opening FDs and not closing them before the reload which
|
||||
would crash the master after a few reloads.
|
||||
|
||||
The startup of the master-worker was reorganized this way:
|
||||
|
||||
- the "discovery" mode, which is a lighter configuration parsing step, only
|
||||
applies the configuration which need to be effective for the master process.
|
||||
For example, "master-worker", "mworker-max-reloads" and less than 20 other
|
||||
keywords that are identified by KWF_DISCOVERY in the code. It is really fast
|
||||
as it don't need all the configuration to be applied in the master process.
|
||||
|
||||
- the master will then fork a worker, with a PROC_O_INIT flag. This worker has
|
||||
a temporary sockpair connected to the master CLI. Once the worker is forked,
|
||||
the master initializes its configuration and starts its polling loop.
|
||||
|
||||
- The newly forked worker will try to parse the configuration, which could
|
||||
result in a failure (exit 1), or any bad error code. In case of success, the
|
||||
worker will send a "READY" message to the master CLI then close this FD. At
|
||||
this step everything was initialized and the worker can enter its polling
|
||||
loop.
|
||||
|
||||
- The master then waits for the worker, it could:
|
||||
* receive the READY message over the mCLI, resulting in a successful loading
|
||||
of haproxy
|
||||
* receive a SIGCHLD, meaning the worker exited and couldn't load
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ SHUT RDY ACT
|
|||
1 1 1 => shut pending
|
||||
|
||||
PB: we can land into final shut if one thread disables the FD while another
|
||||
one that was waiting on it reports it as shut. Theoretically it should be
|
||||
one that was waiting on it reports it as shut. Theorically it should be
|
||||
implicitly ready though, since reported. But if no data is reported, it
|
||||
will be reportedly shut only. And no event will be reported then. This
|
||||
might still make sense since it's not active, thus we don't want events.
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
2025/09/16 - SHM stats file storage description and hints
|
||||
|
||||
Shm stats file (used to share thread-groupable statistics over multiple
|
||||
process through the "shm-stats-file" directive) is made of:
|
||||
|
||||
- a main header which describes the file version, the processes making
|
||||
use of it, the common clock source and hints about the number of
|
||||
objects that are currently stored or provisionned in the file.
|
||||
- an indefinite number of "objects" blocks coming right after the
|
||||
main header, all blocks have the same size which is the size of the
|
||||
maximum underlying object that may be stored. The main header tells
|
||||
how many objects are stored in the file.
|
||||
|
||||
File header looks like this (32/64 bits systems):
|
||||
|
||||
0 8 16 32 48 64
|
||||
+-------+---------+----------------+-------------------+-------------------+
|
||||
| VERSION | 2 bytes | global_now_ms (global mono date in ms)|
|
||||
|MAJOR | MINOR | hole | |
|
||||
+----------------------------------+---------------------------------------+
|
||||
| global_now_ns (global mono date in ns) |
|
||||
+--------------------------------------------------------------------------+
|
||||
| now_offset (offset applied to global monotonic date |
|
||||
| on startup) |
|
||||
+--------------------------------------------------------------------------+
|
||||
| Process slot : | 1byte x 64
|
||||
| pid | heartbeat (ticks) |
|
||||
+----------------------------------+---------------------------------------+
|
||||
| objects | objects slots |
|
||||
| (used objects) | (available for use) |
|
||||
+----------------------------------+---------------------------------------+
|
||||
| padding (for future use) | 128 bytes
|
||||
+--------------------------------------------------------------------------+
|
||||
|
||||
Object block looks like this:
|
||||
|
||||
0 8 16 32 48 64
|
||||
+-------+---------+----------------+-------------------+-------------------+
|
||||
| GUID | 128 bytes
|
||||
+ (zero terminated) +
|
||||
| |
|
||||
+-------+---------+--------------------------------------------------------+
|
||||
| tgid | type | padding |
|
||||
+-------+---------+--------------------------------------------------------+
|
||||
| users (bitmask of process slots making use of the obj) |
|
||||
+--------------------------------------------------------------------------+
|
||||
| object data |
|
||||
| (version dependent) |
|
||||
| struct be_counters_shared_tg or |
|
||||
| struct fe_counters_shared_tg |
|
||||
+--------------------------------------------------------------------------+
|
||||
| padding (to anticipate evolutions) | 64 bytes
|
||||
+--------------------------------------------------------------------------+
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
2026-03-12 - thread execution context
|
||||
|
||||
Thread execution context (thread_exec_ctx) is a combination of type and pointer
|
||||
that are set in the current running thread at th_ctx->exec_ctx when entering
|
||||
certain processing (tasks, sample fetch functions, actions, CLI keywords etc).
|
||||
They're refined along execution, so that a task such as process_stream could
|
||||
temporarily switch to a converter while evaluating an expression and switch
|
||||
back to process_stream. They are reported in thread dumps and are mixed with
|
||||
caller locations for memory profiling. As such they are intentionally not too
|
||||
precise in order to avoid an explosion of the number of buckets. At the moment,
|
||||
the level of granularity it provides is sufficient to try to narrow a
|
||||
misbehaving origin down to a list of keywords. The context types can currently
|
||||
be:
|
||||
|
||||
- something registered via an initcall, with the initcall's location
|
||||
- something registered via an ha_caller, with the caller's location
|
||||
- an explicit sample fetch / converter / action / CLI keyword list
|
||||
- an explicit function (mainly used for actions without keywords)
|
||||
- a task / tasklet (no distinction is made), using the ->process pointer
|
||||
- a filter (e.g. compression), via flt_conf, reporting name
|
||||
- a mux (via the mux_ops, reporting the name)
|
||||
- an applet (e.g. cache, stats, CLI)
|
||||
|
||||
A macro EXEC_CTX_MAKE(type, pointer) makes a thread_exec_ctx from such
|
||||
values.
|
||||
|
||||
A macro EXEC_CTX_NO_RET(ctx, statement) calls a void statement under the
|
||||
specified context.
|
||||
|
||||
A macro EXEC_CTX_WITH_RET(ctx, expr) calls an expression under the specified
|
||||
context.
|
||||
|
||||
Most locations were modified to directly use these macros on the fly, by
|
||||
retrieving the context from where it was set on the element being evaluated
|
||||
(e.g. an action rule contains the context inherited by the action keyword
|
||||
that was used to create it).
|
||||
|
||||
In tools.c, chunk_append_thread_ctx() tries to decode the given exec_ctx and
|
||||
appends it into the provided buffer. It's used by ha_thread_dump_one() and
|
||||
cli_io_handler_show_activity() for memory profiling. In this latter case,
|
||||
the detected thread_ctx are reported in the output under brackets prefixed
|
||||
with "[via ...]" to distinguish call paths to the same allocators.
|
||||
|
||||
A good way to test if a context is properly reported is to place a bleeding
|
||||
malloc() call into one of the monitored functions, e.g.:
|
||||
|
||||
DISGUISE(malloc(8));
|
||||
|
||||
and issue "show profiling memory" after stressing the function. Its context
|
||||
must appear on the right with the number of calls.
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
HAProxy Threat Model & Trust Boundaries
|
||||
|
||||
This document defines the security boundaries of HAProxy, explicitly outlining
|
||||
what does and does not constitute a security vulnerability. Its purpose is to
|
||||
give reporters, developers and reviewers a single, predictable basis for
|
||||
judging an issue's real-world impact.
|
||||
|
||||
The project's strong preference is to fix issues quickly and in the open.
|
||||
Public handling gets fixes to users sooner and spares the ecosystem
|
||||
(distributions in particular) the heavy cost of embargo coordination, which in
|
||||
practice has rarely served users. Private, coordinated disclosure is reserved
|
||||
for the few cases whose real-world impact genuinely warrants it, judged from
|
||||
the severity ordering (section 6) and the mitigations (section 5). An issue
|
||||
that is technically in scope but contained in practice does not, by itself,
|
||||
call for an embargo.
|
||||
|
||||
These boundaries apply strictly to officially supported, documented builds
|
||||
running under a sane, production-ready configuration. Security guarantees are
|
||||
explicitly voided when using opt-in unsafe knobs, undocumented behavior, or
|
||||
experimental features. A configuration that merely lacks a recommended
|
||||
hardening step (for instance, no chroot) does not by itself move a
|
||||
client-triggered bug out of scope; the missing mitigation only widens the
|
||||
blast radius (sections 5 and 6).
|
||||
|
||||
1. ASSETS TO PROTECT
|
||||
HAProxy sits on the critical path of the services it fronts, so its
|
||||
availability and the integrity and confidentiality of the configuration and
|
||||
secrets it holds are all essential to protect. The assets below are not
|
||||
ranked here; their relative severity is ranked in section 6.
|
||||
- Integrity and confidentiality of the host and configuration: a compromise
|
||||
of the network-facing worker must not extend to the filesystem, nor to the
|
||||
configuration and its dependencies (private keys, Lua scripts, maps,
|
||||
crt-lists, ACLs). On a properly configured system the default structural
|
||||
mitigations prevent this, leaving only a compromise of the master process
|
||||
as a residual path (see section 5).
|
||||
- Confidentiality of long-lived secrets: TLS private keys and certificates
|
||||
above all. Unlike transient client data, their disclosure is permanent and
|
||||
systemic (impersonation and traffic decryption until every key is rotated
|
||||
and revoked).
|
||||
- Availability of the proxied service: being on the critical path, keeping
|
||||
HAProxy serving is paramount. A small, cheap amount of attacker input
|
||||
must neither consume a disproportionate amount of CPU or memory
|
||||
(asymmetric DoS, see section 3) nor crash or stall the process.
|
||||
- Confidentiality and isolation of client data: data belonging to one
|
||||
connection, stream or client must never leak to another, and process
|
||||
memory (including uninitialized memory) must never leak to a client.
|
||||
- Process integrity (memory safety): no RCE, memory corruption or undefined
|
||||
behaviour (UB) reachable from untrusted input.
|
||||
- Correct enforcement of the configured policy: access controls, routing and
|
||||
header manipulations decided by the configuration must not be bypassable
|
||||
by crafted input.
|
||||
|
||||
2. ATTACKER AND ENTRY POINTS
|
||||
- The reference attacker is an untrusted client able to send arbitrary
|
||||
bytes to a frontend: raw TCP payloads, HTTP/1, HTTP/2 and HTTP/3 (QUIC)
|
||||
traffic, and arbitrary TLS handshake records.
|
||||
- Entry points in scope are therefore the listeners and everything that
|
||||
parses or transforms client-supplied data: TLS, the HTTP muxes, HTX,
|
||||
header/URL processing, sample fetches and converters acting on request
|
||||
data, stick-tables fed by client data, the cache, and the QUIC/H3 stack.
|
||||
- A secondary untrusted source is the DNS resolver path: even though
|
||||
nameservers are configured, their answers arrive over UDP and can be
|
||||
spoofed by an off-path attacker, so the response parser handles
|
||||
attacker-influenced input.
|
||||
|
||||
3. WHAT QUALIFIES AS A SECURITY BUG (IN SCOPE)
|
||||
- Memory-safety issues (overflow, out-of-bounds, use-after-free, type
|
||||
confusion, UB) reachable from untrusted client input.
|
||||
- Cross-client or cross-stream effects: HTTP request smuggling, response
|
||||
splitting, cache poisoning, and any mixing of data between concurrent
|
||||
streams or connections (notably in the H2/H3 multiplexers).
|
||||
- Disclosure of process memory or of another client's data to a client.
|
||||
- Bypass of a policy that the configuration is meant to enforce (e.g.
|
||||
defeating an http-request deny/acl through request crafting).
|
||||
- Asymmetric / algorithmic denial of service: a single or a few cheap
|
||||
requests causing disproportionate CPU or memory usage (hash-collision
|
||||
flooding, catastrophic regex backtracking, quadratic parsing, unbounded
|
||||
allocation, etc). This is distinct from volumetric DoS (see 4).
|
||||
- Misuse of a third-party library on untrusted input: feeding malformed
|
||||
client data into OpenSSL, PCRE, Lua, zlib, etc. in a way that corrupts
|
||||
memory or crashes the process is in scope. A vulnerability inside the
|
||||
library itself is handled by that library's project, not here.
|
||||
- Mishandling of spoofable DNS responses: memory corruption, crashes or
|
||||
cache/state poisoning in the resolver caused by a crafted DNS answer are
|
||||
in scope, despite nameservers being nominally trusted (see section 2).
|
||||
|
||||
4. WHAT DOES NOT QUALIFY (OUT OF SCOPE)
|
||||
The following do not fall into the security-bug category.
|
||||
|
||||
Trusted peers, servers and protocols:
|
||||
- attacks that require a non-compliant or malicious server: in a reverse
|
||||
proxy, servers are trusted, or ejected. This covers server-to-client
|
||||
attacks in general.
|
||||
- attacks on protocols only used with trusted peers: peers, PROXY protocol,
|
||||
CIP (NetScaler Client-IP insertion), SOCKS, a local server reached over
|
||||
an ABNS or UNIX socket, an FCGI server, etc., as well as TLS servers
|
||||
contacted by the internal httpclient.
|
||||
- malfunction of a trusted auxiliary service (log server, ring output,
|
||||
CLI API consumer, etc.).
|
||||
|
||||
Privileged or local access (the actor is already trusted):
|
||||
- any problem triggered through admin access to the CLI.
|
||||
- anything requiring access to the master CLI.
|
||||
- anything requiring access to the command line.
|
||||
- anything requiring write access to the configuration file or any of its
|
||||
dependencies (Lua scripts, certificates, crt-list, acl, map, etc.).
|
||||
- anything requiring a configuration running as root, or chrooted to "/"
|
||||
(i.e. with no effective chroot).
|
||||
|
||||
Opt-in unsafe or experimental knobs (the operator disabled a safety):
|
||||
- anything requiring "experimental-mode on" on the CLI.
|
||||
- anything requiring "insecure-fork-wanted".
|
||||
- anything requiring "accept-unsafe-violations-*".
|
||||
- anything requiring "expose-experimental-directives".
|
||||
|
||||
Misconfiguration:
|
||||
- anything requiring a configuration that emits warnings at boot.
|
||||
- anything requiring a nonsensical configuration, e.g. a server looping back
|
||||
to the frontend, non-standard header processing or URL rewriting, or an
|
||||
excessively large number of headers or excessively large header/body
|
||||
sizes.
|
||||
|
||||
Volumetric or otherwise detectable activity:
|
||||
- anything requiring such a high and sustained level of activity that it
|
||||
would be detected and blocked in production (e.g. billions of requests or
|
||||
connections). This is volumetric DoS, as opposed to the asymmetric DoS of
|
||||
section 3.
|
||||
|
||||
Inherent protocol limitations:
|
||||
- anything that is a limitation of a standard protocol rather than an
|
||||
implementation flaw. For example, HTTP/1 has no way to abort a single
|
||||
transfer without closing the connection, so a client aborting a transfer
|
||||
will necessarily cause the corresponding server-side connection to be
|
||||
closed; this is by design of the protocol, not a vulnerability.
|
||||
|
||||
Features that are not security boundaries:
|
||||
- the stats page, including its admin mode, relies on HTTP basic
|
||||
authentication and was never meant to be a security boundary. Exposing a
|
||||
public-facing, admin-enabled stats page is therefore not covered.
|
||||
- configuring a listener to accept the PROXY protocol or CIP from senders
|
||||
that are not restricted to trusted ones is a misconfiguration: these
|
||||
headers are believed on trust, so the listener must be reachable only by
|
||||
the trusted L4/L7 component that prepends them.
|
||||
|
||||
Side channels:
|
||||
- cryptographic and micro-architectural side channels (timing, cache,
|
||||
speculative execution, etc.) are out of scope. Constant-time handling of
|
||||
secrets is pursued on a best-effort basis as ordinary hardening where it
|
||||
clearly matters, but observable timing or resource variations are not
|
||||
handled as security bugs.
|
||||
|
||||
Log integrity:
|
||||
- escaping of data emitted to logs is a configuration responsibility.
|
||||
Injection of control characters or forged fields through logged client
|
||||
data (e.g. when default escaping is disabled, or when a downstream log
|
||||
consumer mis-parses) is not covered.
|
||||
|
||||
5. DEFENSE IN DEPTH (DEFAULT HARDENING)
|
||||
A correctly deployed HAProxy combines several built-in mitigations that
|
||||
bound the impact of a successful compromise. These are deliberately taken
|
||||
into account when assessing the real-world severity of an issue and the
|
||||
handling it deserves: when one of them contains the practical impact of a
|
||||
bug, that bug rarely warrants a coordinated embargo and is usually better
|
||||
fixed quickly and in the open, where users get the fix sooner. They lower
|
||||
severity, not the obligation to fix: an exploitable memory-safety bug
|
||||
reachable from client input is still corrected as a bug.
|
||||
- No fork()/exec() in the worker: the worker never forks nor runs external
|
||||
programs, so an attacker who achieves code execution has little ability
|
||||
to spawn a shell or launch persistent background code. ("insecure-fork-
|
||||
wanted" deliberately disables this and is itself out of scope, see
|
||||
section 4.)
|
||||
- chroot and privilege drop: in the sane configuration this document
|
||||
assumes, the worker drops to an unprivileged user/group and chroots into
|
||||
an empty, unwritable directory. Injected code therefore has no filesystem
|
||||
access and very limited means to act on the host.
|
||||
- Activity watchdog: a thread that stops making progress, e.g. hijacked
|
||||
into an attacker-controlled loop or otherwise stuck, no longer services
|
||||
the event loop; the watchdog detects this lack of activity and kills the
|
||||
process after a few seconds rather than letting it be silently held.
|
||||
- Master/worker separation: only the worker is exposed to the network and
|
||||
runs the parsers reachable by clients, and it is the unprivileged,
|
||||
chrooted process. The master keeps privileges and filesystem access but
|
||||
has no network exposure. The master must therefore be protected as the
|
||||
trusted, more privileged component; an attacker is assumed to face only
|
||||
the worker. The master must under no circumstances be reachable from the
|
||||
worker (e.g. a master CLI bound to a TCP socket such as localhost is
|
||||
trivially reachable from compromised worker code and defeats this critical
|
||||
separation).
|
||||
|
||||
6. SEVERITY ORDERING
|
||||
The worst-case outcomes below are ranked by their realistic impact on a
|
||||
standard configuration, from most to least severe, and the effort spent
|
||||
guarding against each is proportional to that severity. The ranking reflects
|
||||
the master/worker privilege split and the containment provided by the
|
||||
section-5 mitigations.
|
||||
1. Remote code execution in the master process. The master is privileged
|
||||
and has filesystem access, so compromising it defeats every
|
||||
containment, leaks every secret, and can subvert or take down the
|
||||
whole service.
|
||||
2. Chosen disclosure of long-lived secrets, TLS private keys and
|
||||
certificates above all. Unlike an outage the damage is permanent and
|
||||
silent: stolen keys allow impersonation, interception and, absent
|
||||
forward secrecy, decryption of captured traffic, until every affected
|
||||
key is rotated and revoked across the ecosystem; a restart does not
|
||||
undo it. "Chosen" sets this rank, not scope: any disclosure of process
|
||||
memory or of another client's data to a client is in scope (section 3);
|
||||
this top rank is reserved for a targeted exfiltration, where the
|
||||
attacker steers the read to a known secret. A leak that cannot be
|
||||
steered toward a specific secret is still an in-scope disclosure bug,
|
||||
but ranks far lower - often no worse than the crash such a read tends
|
||||
to cause first.
|
||||
3. Crash of the master process. It brings the entire service down and
|
||||
prevents workers from being respawned: a full but recoverable outage.
|
||||
4. Crash of the worker process. A transient outage: in-flight connections
|
||||
are lost and traffic is interrupted for the fraction of a second it
|
||||
takes to respawn.
|
||||
5. Remote code execution in the worker process. Contained by no-fork,
|
||||
chroot, privilege drop and the watchdog, its availability impact is
|
||||
usually below a worker crash, except in the unlikely case where it
|
||||
unlocks the chosen disclosure of level 2, which is hard to reach
|
||||
through the internals from injected code.
|
||||
6. Policy bypass. Serious, but with no direct availability impact.
|
||||
|
||||
7. SECURITY-RELEVANT INVARIANTS AND DEFAULTS
|
||||
The values below define the conditions HAProxy is designed to operate
|
||||
within, and may be relied upon by parsing and processing code. A suspected
|
||||
vulnerability that can only be triggered by conditions outside them
|
||||
(typically values pushed beyond the stated limits) does not qualify as
|
||||
security-relevant:
|
||||
- trash buffers and struct buffer storage are always at least a few kB.
|
||||
- default buffer size is 16 kB (15 kB max input, as 1 kB is reserved for
|
||||
rewrites), tunable up to <256 MB.
|
||||
- default log line is 1 kB, tunable up to <=64 kB.
|
||||
|
|
@ -21,7 +21,7 @@ falls back to CLOCK_REALTIME. The former is more accurate as it really counts
|
|||
the time spent in the process, while the latter might also account for time
|
||||
stuck on paging in etc.
|
||||
|
||||
Then wdt_ping() is called to arm the timer. It's set to trigger every
|
||||
Then wdt_ping() is called to arm the timer. t's set to trigger every
|
||||
<wdt_warn_blocked_traffic_ns> interval. It is also called by wdt_handler()
|
||||
to reprogram a new wakeup after it has ticked.
|
||||
|
||||
|
|
@ -37,18 +37,15 @@ If the thread was not marked as stuck, it's verified that no progress was made
|
|||
for at least one second, in which case the TH_FL_STUCK flag is set. The lack of
|
||||
progress is measured by the distance between the thread's current cpu_time and
|
||||
its prev_cpu_time. If the lack of progress is at least as large as the warning
|
||||
threshold, then the signal is bounced to the faulty thread if it's not the
|
||||
current one. Since this bounce is based on the time spent without update, it
|
||||
already doesn't happen often.
|
||||
threshold and no context switch happened since last call, ha_stuck_warning() is
|
||||
called to emit a warning about that thread. In any case the context switch
|
||||
counter for that thread is updated.
|
||||
|
||||
Once on the faulty thread, two checks are performed:
|
||||
1) if the thread was already marked as stuck, then the thread is considered
|
||||
as definitely stuck, and ha_panic() is called. It will not return.
|
||||
|
||||
2) a check is made to verify if the scheduler is still ticking, by reading
|
||||
and setting a variable that only the scheduler can clear when leaving a
|
||||
task. If the scheduler didn't make any progress, ha_stuck_warning() is
|
||||
called to emit a warning about that thread.
|
||||
If the thread was already marked as stuck, then the thread is considered as
|
||||
definitely stuck. Then ha_panic() is directly called if the thread is the
|
||||
current one, otherwise ha_kill() is used to resend the signal directly to the
|
||||
target thread, which will in turn go through this handler and handle the panic
|
||||
itself.
|
||||
|
||||
Most of the time there's no panic of course, and a wdt_ping() is performed
|
||||
before leaving the handler to reprogram a check for that thread.
|
||||
|
|
@ -64,12 +61,12 @@ set TAINTED_WARN_BLOCKED_TRAFFIC.
|
|||
|
||||
ha_panic() uses the current thread's trash buffer to produce the messages, as
|
||||
we don't care about its contents since that thread will never return. However
|
||||
ha_stuck_warning() instead uses a local 8kB buffer in the thread's stack.
|
||||
ha_stuck_warning() instead uses a local 4kB buffer in the thread's stack.
|
||||
ha_panic() will call ha_thread_dump_fill() for each thread, to complete the
|
||||
buffer being filled with each thread's dump messages. ha_stuck_warning() only
|
||||
calls ha_thread_dump_one(), which works on the current thread. In both cases
|
||||
the message is then directly sent to fd #2 (stderr) and ha_thread_dump_done()
|
||||
is called to release the dumped thread.
|
||||
calls the function for the current thread. In both cases the message is then
|
||||
directly sent to fd #2 (stderr) and ha_thread_dump_one() is called to release
|
||||
the dumped thread.
|
||||
|
||||
Both print a few extra messages, but ha_panic() just ends by looping on abort()
|
||||
until the process dies.
|
||||
|
|
@ -113,19 +110,13 @@ ha_dump_backtrace() before returning.
|
|||
ha_dump_backtrace() produces a backtrace into a local buffer (100 entries max),
|
||||
then dumps the code bytes nearby the crashing instrution, dumps pointers and
|
||||
tries to resolve function names, and sends all of that into the target buffer.
|
||||
On some architectures (x86_64, arm64), it will also try to detect and decode
|
||||
call instructions and resolve them to called functions.
|
||||
|
||||
3. Improvements
|
||||
---------------
|
||||
|
||||
The symbols resolution is extremely expensive, particularly for the warnings
|
||||
which should be fast. But we need it, it's just unfortunate that it strikes at
|
||||
the wrong moment. At least ha_dump_backtrace() does disable signals while it's
|
||||
resolving, in order to avoid unwanted re-entrance. In addition, the called
|
||||
function resolve_sym_name() uses some locking and refrains from calling the
|
||||
dladdr family of functions in a re-entrant way (in the worst case only well
|
||||
known symbols will be resolved)..
|
||||
the wrong moment.
|
||||
|
||||
In an ideal case, ha_dump_backtrace() would dump the pointers to a local array,
|
||||
which would then later be resolved asynchronously in a tasklet. This can work
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
-----------------------
|
||||
HAProxy Starter Guide
|
||||
-----------------------
|
||||
version 3.5
|
||||
version 3.2
|
||||
|
||||
|
||||
This document is an introduction to HAProxy for all those who don't know it, as
|
||||
|
|
@ -97,9 +97,6 @@ to the mailing list whose responses are present in these documents.
|
|||
protocol which is implemented by HAProxy and a number of third party
|
||||
products.
|
||||
|
||||
- security.txt : how to report a security issue, and what does and does not
|
||||
qualify as a vulnerability.
|
||||
|
||||
- README : how to build HAProxy from sources
|
||||
|
||||
|
||||
|
|
@ -1689,7 +1686,15 @@ information you might later regret. Since the issue tracker presents itself as
|
|||
a very long thread, please avoid pasting very long dumps (a few hundreds lines
|
||||
or more) and attach them instead.
|
||||
|
||||
If you believe you may have found a security issue, please refer to the file
|
||||
doc/security.txt. It explains what does and does not qualify as a vulnerability
|
||||
in HAProxy, and how to report a genuine one privately. Most suspected issues
|
||||
turn out to be ordinary bugs that are better reported as described above.
|
||||
If you've found what you're absolutely certain can be considered a critical
|
||||
security issue that would put many users in serious trouble if discussed in a
|
||||
public place, then you can send it with the reproducer to security@haproxy.org.
|
||||
A small team of trusted developers will receive it and will be able to propose
|
||||
a fix. We usually don't use embargoes and once a fix is available it gets
|
||||
merged. In some rare circumstances it can happen that a release is coordinated
|
||||
with software vendors. Please note that this process usually messes up with
|
||||
eveyone's work, and that rushed up releases can sometimes introduce new bugs,
|
||||
so it's best avoided unless strictly necessary; as such, there is often little
|
||||
consideration for reports that needlessly cause such extra burden, and the best
|
||||
way to see your work credited usually is to provide a working fix, which will
|
||||
appear in changelogs.
|
||||
|
|
|
|||
|
|
@ -893,9 +893,7 @@ Core class
|
|||
|
||||
**context**: init, task, action
|
||||
|
||||
This function returns a new object of a *httpclient* class. An *httpclient*
|
||||
object must be used to process one and only one request. It must never be
|
||||
reused to process several requests.
|
||||
This function returns a new object of a *httpclient* class.
|
||||
|
||||
:returns: A :ref:`httpclient_class` object.
|
||||
|
||||
|
|
@ -935,7 +933,7 @@ Core class
|
|||
Give back the hand at the HAProxy scheduler. Unlike :js:func:`core.yield`
|
||||
the task will not be woken up automatically to resume as fast as possible.
|
||||
Instead, it will wait for an event to wake the task. If milliseconds argument
|
||||
is provided then the Lua execution will be automatically resumed passed this
|
||||
is provided then the Lua excecution will be automatically resumed passed this
|
||||
delay even if no event caused the task to wake itself up.
|
||||
|
||||
:param integer milliseconds: automatic wakeup passed this delay. (optional)
|
||||
|
|
@ -945,7 +943,7 @@ Core class
|
|||
**context**: task, action
|
||||
|
||||
Give back the hand at the HAProxy scheduler. It is used when the LUA
|
||||
processing consumes a lot of processing time. Lua execution will be resumed
|
||||
processing consumes a lot of processing time. Lua excecution will be resumed
|
||||
automatically (automatic reschedule).
|
||||
|
||||
.. js:function:: core.parse_addr(address)
|
||||
|
|
@ -1031,12 +1029,6 @@ Core class
|
|||
Be careful when subscribing to this type since many events might be
|
||||
generated.
|
||||
|
||||
**ACME** Family:
|
||||
|
||||
* **ACME_DEPLOY**: when a dns-01 challenge TXT record must be deployed
|
||||
externally before HAProxy can proceed with the ACME challenge
|
||||
* **ACME_NEWCERT**: when a new certificate is successfully installed
|
||||
|
||||
.. Note::
|
||||
Use **SERVER** in **event_types** to subscribe to all server events types
|
||||
at once. Note that this should only be used for testing purposes since a
|
||||
|
|
@ -1054,8 +1046,7 @@ Core class
|
|||
* **event** (*string*): the event type (one of the **event_types** specified
|
||||
when subscribing)
|
||||
* **event_data**: specific to each event family (For **SERVER** family,
|
||||
a :ref:`server_event_class` object; for **ACME** family,
|
||||
a :ref:`acme_event_class` object)
|
||||
a :ref:`server_event_class` object)
|
||||
* **sub**: class to manage the subscription from within the event
|
||||
(a :ref:`event_sub_class` object)
|
||||
* **when**: timestamp corresponding to the date when the event was generated.
|
||||
|
|
@ -1096,13 +1087,18 @@ Core class
|
|||
perform the heavy job in a dedicated task and allow remaining events to be
|
||||
processed more quickly.
|
||||
|
||||
.. js:function:: core.use_native_mailers_config()
|
||||
.. js:function:: core.disable_legacy_mailers()
|
||||
|
||||
**context**: body
|
||||
**LEGACY**
|
||||
|
||||
Inform haproxy that the script will make use of the native "mailers"
|
||||
config section (although legacy). In other words, inform haproxy that
|
||||
:js:func:`Proxy.get_mailers()` will be used later in the program.
|
||||
**context**: body, init
|
||||
|
||||
Disable the sending of email alerts through the legacy email sending
|
||||
function when mailers are used in the configuration.
|
||||
|
||||
Use this when sending email alerts directly from lua.
|
||||
|
||||
:see: :js:func:`Proxy.get_mailers()`
|
||||
|
||||
.. _proxy_class:
|
||||
|
||||
|
|
@ -1231,14 +1227,8 @@ Proxy class
|
|||
|
||||
**LEGACY**
|
||||
|
||||
Returns a table containing legacy mailers config (from haproxy configuration
|
||||
file) for the current proxy or nil if mailers are not available for the proxy.
|
||||
|
||||
.. warning::
|
||||
When relying on :js:func:`Proxy.get_mailers()` to retrieve mailers
|
||||
configuration, :js:func:`core.use_native_mailers_config()` must be called
|
||||
first from body or init context to inform haproxy that Lua makes use of the
|
||||
legacy mailers config.
|
||||
Returns a table containing mailers config for the current proxy or nil
|
||||
if mailers are not available for the proxy.
|
||||
|
||||
:param class_proxy px: A :ref:`proxy_class` which indicates the manipulated
|
||||
proxy.
|
||||
|
|
@ -1255,6 +1245,10 @@ ProxyMailers class
|
|||
|
||||
This class provides mailers config for a given proxy.
|
||||
|
||||
If sending emails directly from lua, please consider
|
||||
:js:func:`core.disable_legacy_mailers()` to disable the email sending from
|
||||
haproxy. (Or email alerts will be sent twice...)
|
||||
|
||||
.. js:attribute:: ProxyMailers.track_server_health
|
||||
|
||||
Boolean set to true if the option "log-health-checks" is configured on
|
||||
|
|
@ -2587,9 +2581,7 @@ HTTPClient class
|
|||
.. js:class:: HTTPClient
|
||||
|
||||
The httpclient class allows issue of outbound HTTP requests through a simple
|
||||
API without the knowledge of HAProxy internals. Any instance must be used to
|
||||
process one and only one request. It must never be reused to process several
|
||||
requests.
|
||||
API without the knowledge of HAProxy internals.
|
||||
|
||||
.. js:function:: HTTPClient.get(httpclient, request)
|
||||
.. js:function:: HTTPClient.head(httpclient, request)
|
||||
|
|
@ -3924,25 +3916,21 @@ AppletTCP class
|
|||
*size* is missing, the function tries to read all the content of the stream
|
||||
until the end. An optional timeout may be specified in milliseconds. In this
|
||||
case the function will return no longer than this delay, with the amount of
|
||||
available data, or nil if there is no data. An empty string is returned if the
|
||||
connection is closed.
|
||||
available data (possibly none).
|
||||
|
||||
:param class_AppletTCP applet: An :ref:`applettcp_class`
|
||||
:param integer size: the required read size.
|
||||
:returns: return nil if the timeout has expired and no data was available but
|
||||
can still be received. Otherwise, a string is returned, possibly an empty
|
||||
string if the connection is closed.
|
||||
:returns: always return a string, the string can be empty if the connection is
|
||||
closed.
|
||||
|
||||
.. js:function:: AppletTCP.try_receive(applet)
|
||||
|
||||
Reads available data from the TCP stream and returns immediately. Returns a
|
||||
string containing read bytes or nil if no bytes are available at that time. An
|
||||
empty string is returned if the connection is closed.
|
||||
string containing read bytes that may possibly be empty if no bytes are
|
||||
available at that time.
|
||||
|
||||
:param class_AppletTCP applet: An :ref:`applettcp_class`
|
||||
:returns: return nil if no data was available but can still be
|
||||
received. Otherwise, a string is returned, possibly an empty string if the
|
||||
connection is closed.
|
||||
:returns: always return a string, the string can be empty.
|
||||
|
||||
.. js:function:: AppletTCP.send(appletmsg)
|
||||
|
||||
|
|
@ -4619,27 +4607,6 @@ HTTPMessage class
|
|||
data by default.
|
||||
:returns: an integer containing the amount of bytes copied or -1.
|
||||
|
||||
.. js:function:: HTTPMessage.set_body_len(http_msg, length)
|
||||
|
||||
This function changes the expected payload length of the HTTP message
|
||||
**http_msg**. **length** can be an integer value. In that case, a
|
||||
"Content-Length" header is added with the given value. It is also possible to
|
||||
pass the **"chunked"** string instead of an integer value to force the HTTP
|
||||
message to be chunk-encoded. In that case, a "Transfer-Encoding" header is
|
||||
added with the "chunked" value. In both cases, all existing "Content-Length"
|
||||
and "Transfer-Encoding" headers are removed.
|
||||
|
||||
This function should be used in the filter context to be able to alter the
|
||||
payload of the HTTP message. The internal state of the HTTP message is updated
|
||||
accordingly. :js:func:`HTTPMessage.add_header()` or
|
||||
:js:func:`HTTPMessage.set_header()` functions must be used in that case.
|
||||
|
||||
:param class_httpmessage http_msg: The manipulated HTTP message.
|
||||
:param type length: The new payload length to set. It can be an integer or
|
||||
the string "chunked".
|
||||
:returns: true if the payload length was successfully updated, false
|
||||
otherwise.
|
||||
|
||||
.. js:function:: HTTPMessage.set_eom(http_msg)
|
||||
|
||||
This function set the end of message for the HTTP message **http_msg**.
|
||||
|
|
@ -4746,75 +4713,6 @@ CertCache class
|
|||
CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt}
|
||||
|
||||
|
||||
ACME class
|
||||
==========
|
||||
|
||||
.. js:class:: ACME
|
||||
|
||||
This class provides access to the ACME (Automatic Certificate Management
|
||||
Environment) subsystem. It allows Lua scripts to interact with ongoing ACME
|
||||
certificate challenges.
|
||||
|
||||
.. js:function:: ACME.challenge_ready(crt, dns)
|
||||
|
||||
Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
|
||||
Returns the number of remaining challenges, or 0 if all challenges are ready
|
||||
and validation has been triggered. Raises a Lua error if the certificate or
|
||||
domain is not found.
|
||||
|
||||
:param string crt: The filename of the certificate.
|
||||
:param string dns: The domain name for which the challenge is ready.
|
||||
:returns: The number of remaining challenges (integer), or 0 when all
|
||||
challenges are done and validation has been triggered.
|
||||
|
||||
.. _acme_event_class:
|
||||
|
||||
AcmeEvent class
|
||||
===============
|
||||
|
||||
.. js:class:: AcmeEvent
|
||||
|
||||
This class is provided with every **ACME** event.
|
||||
|
||||
See :js:func:`core.event_sub()` for more info.
|
||||
|
||||
.. js:attribute:: AcmeEvent.crtname
|
||||
|
||||
Contains the certificate store name.
|
||||
|
||||
.. js:attribute:: AcmeEvent.domain
|
||||
|
||||
Contains the domain being challenged.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.thumbprint
|
||||
|
||||
Contains the account key JWK thumbprint.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.dns_record
|
||||
|
||||
Contains the DNS TXT record value that must be set at
|
||||
``_acme-challenge.<domain>``.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.provider
|
||||
|
||||
Contains the DNS provider name configured in the ACME section.
|
||||
Only set if a provider was configured.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.vars
|
||||
|
||||
Contains the ACME vars string configured in the ACME section.
|
||||
Only set if vars were configured.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
External Lua libraries
|
||||
======================
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue