pgbouncer-1.24.1/0000755000000000000000000000000014777762567010566 500000000000000pgbouncer-1.24.1/config.guess0000755000175000017500000014051214777762315013052 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-09' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_X32 >/dev/null then LIBCABI=${LIBC}x32 fi fi GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; x86_64:Haiku:*:*) GUESS=x86_64-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pgbouncer-1.24.1/requirements.txt0000644000175000000000000000014714777762222013775 00000000000000pytest pytest-asyncio pytest-timeout pytest-xdist psycopg filelock contextlib2; python_version < "3.7" pgbouncer-1.24.1/README.md0000644000175000000000000001165514777762222011776 00000000000000PgBouncer ========= Lightweight connection pooler for PostgreSQL. Homepage: Sources, bug tracking: Building --------- PgBouncer depends on few things to get compiled: * [GNU Make] 3.81+ * [Libevent] 2.0+ * [pkg-config] * [OpenSSL] 1.0.1+ for TLS support * (optional) [c-ares] as alternative to Libevent's evdns * (optional) PAM libraries [GNU Make]: https://www.gnu.org/software/make/ [Libevent]: http://libevent.org/ [pkg-config]: https://www.freedesktop.org/wiki/Software/pkg-config/ [OpenSSL]: https://www.openssl.org/ [c-ares]: http://c-ares.haxx.se/ When dependencies are installed just run: $ ./configure --prefix=/usr/local $ make $ make install If you are building from Git, or are building for Windows, please see separate build instructions below. DNS lookup support ------------------ PgBouncer does host name lookups at connect time instead of just once at configuration load time. This requires an asynchronous DNS implementation. The following table shows supported backends and their probing order: | backend | parallel | EDNS0 (1) | /etc/hosts | SOA lookup (2) | note | |----------------------------|----------|-----------|------------|----------------|---------------------------------------| | c-ares | yes | yes | yes | yes | IPv6+CNAME buggy in <=1.10 | | evdns, libevent 2.x | yes | no | yes | no | does not check /etc/hosts updates | | getaddrinfo_a, glibc 2.9+ | yes | yes (3) | yes | no | N/A on non-glibc | | getaddrinfo, libc | no | yes (3) | yes | no | requires pthreads | 1. EDNS0 is required to have more than 8 addresses behind one host name. 2. SOA lookup is needed to re-check host names on zone serial change. 3. To enable EDNS0, add `options edns0` to `/etc/resolv.conf`. c-ares is the most fully-featured implementation and is recommended for most uses and binary packaging (if a sufficiently new version is available). Libevent's built-in evdns is also suitable for many uses, with the listed restrictions. The other backends are mostly legacy options at this point and don't receive much testing anymore. By default, c-ares is used if it can be found. Its use can be forced with `configure --with-cares` or disabled with `--without-cares`. If c-ares is not used (not found or disabled), then Libevent is used. Specify `--disable-evdns` to disable the use of Libevent's evdns and fall back to a libc-based implementation. PAM authentication ------------------ To enable PAM authentication, `./configure` has a flag `--with-pam` (default value is no). When compiled with PAM support, a new global authentication type `pam` is available to validate users through PAM. systemd integration ------------------- To enable systemd integration, use the `configure` option `--with-systemd`. This allows using `Type=notify` (or `Type=notify-reload` if you are using systemd 253 or later) as well as socket activation. See `etc/pgbouncer.service` and `etc/pgbouncer.socket` for examples. Building from Git ----------------- Building PgBouncer from Git requires that you fetch the libusual and uthash submodules and generate the header and configuration files before you can run `configure`: $ git clone https://github.com/pgbouncer/pgbouncer.git $ cd pgbouncer $ git submodule init $ git submodule update $ ./autogen.sh $ ./configure $ make $ make install All files will be installed under `/usr/local` by default. You can supply one or more command-line options to `configure`. Run `./configure --help` to list the available options and the environment variables that customizes the configuration. Additional packages required: autoconf, automake, libtool, pandoc Testing ------- See the [`README.md` file in the test directory][1] on how to run the tests. [1]: https://github.com/pgbouncer/pgbouncer/blob/master/test/README.md Building on Windows ------------------- The only supported build environment on Windows is MinGW. Cygwin and Visual $ANYTHING are not supported. To build on MinGW, do the usual: $ ./configure $ make If cross-compiling from Unix: $ ./configure --host=i586-mingw32msvc Running on Windows ------------------ Running from the command line goes as usual, except that the `-d` (daemonize), `-R` (reboot), and `-u` (switch user) switches will not work. To run PgBouncer as a Windows service, you need to configure the `service_name` parameter to set a name for the service. Then: $ pgbouncer -regservice config.ini To uninstall the service: $ pgbouncer -unregservice config.ini To use the Windows event log, set `syslog = 1` in the configuration file. But before that, you need to register `pgbevent.dll`: $ regsvr32 pgbevent.dll To unregister it, do: $ regsvr32 /u pgbevent.dll pgbouncer-1.24.1/test/0000755000000000000000000000000014777762567011545 500000000000000pgbouncer-1.24.1/test/ssl/0000755000000000000000000000000014777762567012346 500000000000000pgbouncer-1.24.1/test/ssl/test.ini0000644000175000000000000000132714777762222013752 00000000000000[databases] p0 = port=6666 host=localhost dbname=p0 user=bouncer pool_size=2 p1 = port=6666 host=localhost dbname=p1 user=bouncer p7a= port=6666 host=localhost dbname=p7 pTxnPool = port=6666 host=127.0.0.1 dbname=p0 user=bouncer pool_mode=transaction [pgbouncer] logfile = test.log pidfile = test.pid listen_addr = 127.0.0.1 listen_port = 6667 unix_socket_dir = /tmp auth_type = trust auth_file = tmp/userlist.txt pool_mode = statement server_check_delay = 10 max_client_conn = 10 default_pool_size = 5 server_lifetime = 120 server_idle_timeout = 60 server_login_retry = 1 ;; Accept even old TLS versions so that builds with older OpenSSL can ;; run the test suite. client_tls_protocols = all server_tls_protocols = all pgbouncer-1.24.1/test/ssl/lib.sh0000644000175000000000000000147614777762222013401 00000000000000#! /bin/sh # PEM format # req fields # C = Country # ST = State/Province # L = Locality # O = Organization # OU = Org Unit # CN = commonName # ? = emailAddress umask 077 run() { echo '$' "$@" "$@" 2>&1 | sed 's/^/ > /' } # key -> csr run_req() { tmp="csr.template" args="" while test "$1" != '--'; do args="$args $1" shift done shift ( echo "[req]" echo "prompt=no" echo "distinguished_name=req_distinguished_name" echo "[req_distinguished_name]" for arg; do echo "$arg"; done ) > "$tmp" run openssl req $args -config "$tmp" rm -f csr.template } run_ca() { ser=`cat ${CaName}/serial` run openssl ca -batch -config "${CaName}/config.ini" "$@" while test "$1" != '-out'; do shift done if test "$1" = '-out'; then cp "${CaName}/certs/$ser.pem" "$2" fi } pgbouncer-1.24.1/test/ssl/newca.sh0000755000175000000000000000267514777762222013735 00000000000000#! /bin/sh # create new CA set -e test -n "$1" || { echo "usage: $0 CaName [K=V]*" exit 1 } test -d "$1" && { echo "CA '$1' already exists" exit 1 } name="$1" shift mkdir -p "$name"/certs mkdir -p "$name"/sites touch "$name"/index.txt echo 01 > "$name"/serial . ./lib.sh days=10240 #run openssl genrsa -out "$name/ca.key" $ksize run openssl ecparam -name prime256v1 -genkey -out "$name/ca.key" # self-signed cert # the -addext option is not required for old OpenSSL versions openssl_version=`openssl version | awk '{print $2}'` if expr "X$openssl_version" : 'X1.*.*' >/dev/null; then run_req -new -x509 -days $days -key "$name/ca.key" -out "$name/ca.crt" -- "$@" else run_req -new -x509 -days $days -key "$name/ca.key" -out "$name/ca.crt" -addext basicConstraints=critical,CA:TRUE,pathlen:1 -- "$@" fi cat > "$name"/config.ini < " exit 1 } test -f "$1/ca.key" || { echo "CA $1 does not exist" exit 1 } days=1024 . ./lib.sh CaName="$1" DstName="$2" shift 2 ser=`cat $CaName/serial` pfx=$CaName/sites/${ser}-$DstName run openssl ecparam -genkey -name prime256v1 -out $pfx.key # cert reqs run_req -new -key "$pfx.key" -out "$pfx.csr" -- CN="$DstName" "$@" # accept certs run_ca -days $days -policy pol-server -in "$pfx.csr" -out "$pfx.crt" pgbouncer-1.24.1/test/ssl/create_certs.sh0000755000175000000000000000066614777762222015301 00000000000000#! /bin/sh set -e rm -rf TestCA1 TestCA2 ./newca.sh TestCA1 C=QQ O=Org1 CN="TestCA1" ./newsite.sh TestCA1 localhost C=QQ O=Org1 L=computer OU=db ./newsite.sh TestCA1 bouncer C=QQ O=Org1 L=computer OU=Dev ./newsite.sh TestCA1 random C=QQ O=Org1 L=computer OU=Dev ./newsite.sh TestCA1 pgbouncer.acme.org C=QQ O=Org1 L=computer OU=Dev ./newca.sh TestCA2 C=QQ O=Org2 CN="TestCA2" ./newsite.sh TestCA2 localhost C=QQ O=Org1 L=computer OU=db pgbouncer-1.24.1/test/ssl/Makefile0000644000175000000000000000025714777762222013733 00000000000000EXTRA_DIST = lib.sh newca.sh newsite.sh create_certs.sh test.ini Makefile SUBLOC = test/ssl include ../../config.mak include ../../lib/mk/antimake.mk check: all ./test.sh pgbouncer-1.24.1/test/test.ini0000644000175000000000000001061214777762222013146 00000000000000[databases] p0 = port=6666 host=127.0.0.1 dbname=p0 user=bouncer pool_size=2 reserve_pool_size=2 p0a= port=6666 host=127.0.0.1 dbname=p0 pool_size=2 reserve_pool=2 p0x= port=6666 host=127.0.0.1 dbname=p0 min_pool_size=5 pool_size=5 p0y= port=6666 host=127.0.0.1 dbname=p0 min_pool_size=5 pool_size=5 max_db_connections=2 ; for testing 'min_pool_size' with forced user ;p0z= port=6666 host=127.0.0.1 dbname=p0 min_pool_size=3 user=pswcheck p1 = port=6666 host=127.0.0.1 dbname=p1 user=bouncer p2 = port=6666 host=127.0.0.1 dbname=p0 max_db_connections=4 p3 = port=6666 host=127.0.0.1 dbname=p0 user=bouncer pool_mode=session p4 = port=6666 host=127.0.0.1 dbname=p4 user=puser1 password=foo p4x= port=6666 host=127.0.0.1 dbname=p4 user=puser1 password=wrong p4y= port=6666 host=127.0.0.1 dbname=p4 user=puser1 p4z= port=6666 host=127.0.0.1 dbname=p4 user=puser2 p4l= port=6666 host=127.0.0.1 dbname=p4 user=longpass p5 = port=6666 host=127.0.0.1 dbname=p5 user=muser1 password=foo p5x= port=6666 host=127.0.0.1 dbname=p5 user=muser1 password=wrong p5y= port=6666 host=127.0.0.1 dbname=p5 user=muser1 p5z= port=6666 host=127.0.0.1 dbname=p5 user=muser2 p6 = port=6666 host=127.0.0.1 dbname=p6 user=scramuser1 password=foo p6x= port=6666 host=127.0.0.1 dbname=p6 user=scramuser1 password=wrong p6y= port=6666 host=127.0.0.1 dbname=p6 user=scramuser1 p6z= port=6666 host=127.0.0.1 dbname=p6 user=scramuser2 p61= port=6666 host=127.0.0.1 dbname=p6 user=scramuser3 p62= port=6666 host=127.0.0.1 dbname=p6 p7a= port=6666 host=127.0.0.1 dbname=p7 p7b= port=6666 host=127.0.0.1 dbname=p7 p7c= port=6666 host=127.0.0.1 dbname=p7 p8 = port=6666 host=127.0.0.1 dbname=p0 connect_query='set enable_seqscan=off; set enable_nestloop=off' p9 = port=6666 host=127.0.0.1 dbname=postgres server_lifetime=2 user=bouncer user_passthrough = port=6666 host=127.0.0.1 dbname=p0 user_passthrough2 = port=6666 host=127.0.0.1 dbname=p2 user_passthrough_pool_size2 = port=6666 host=127.0.0.1 dbname=p0 pool_size=2 user_passthrough_pool_size5 = port=6666 host=127.0.0.1 dbname=p0 pool_size=2 pauthz = port=6666 host=127.0.0.1 dbname=p7 auth_user=pswcheck auth_dbname=authdb authdb = port=6666 host=127.0.0.1 dbname=p1 auth_user=pswcheck hostlist1 = port=6666 host=127.0.0.1,::1 dbname=p0 user=bouncer hostlist2 = port=6666 host=127.0.0.1,127.0.0.1 dbname=p0 user=bouncer hostlist_good_first = port=6666 host=127.0.0.1,127.0.0.3 dbname=p0 user=bouncer load_balance_hosts=disable hostlist_bad_first = port=6666 host=unresolvable-hostname,127.0.0.1 dbname=p0 user=bouncer load_balance_hosts=disable load_balance_hosts_update = port=6666 host=127.0.0.1,127.0.0.3 dbname=p0 user=bouncer load_balance_hosts=disable varcache_change = port=6666 host=127.0.0.1 dbname=p0 client_encoding=SQL_ASCII non_existing_pg_db = port=6666 host=127.0.0.1 dbname=non_existing_pg_db conn_limit_db = port=6666 host=127.0.0.1 dbname=p0 max_db_client_connections=2 ; Needed for pg_receivewal and pg_basebackup tests until this patch is merged ; in PostgreSQL (and we don't support PG16 anymore): ; https://www.postgresql.org/message-id/flat/CAGECzQTw-dZkVT_RELRzfWRzY714-VaTjoBATYfZq93R8C-auA@mail.gmail.com replication = port=6666 host=127.0.0.1 dbname=replication client_limit_db= port=6666 host=127.0.0.1 dbname=p0 max_db_client_connections=2 client_limit_db_auth_passthrough = port=6666 host=127.0.0.1 dbname=p0 auth_user=pswcheck max_db_client_connections=2 ; commented out except for auto-database tests ;* = port=6666 host=127.0.0.1 [users] maxedout = max_user_connections=3 maxedout2 = max_user_connections=2 max_user_client_connections=2 maxedout3 = max_user_client_connections=2 poolsize1 = pool_size=1 respoolsize1 = pool_size=1 reserve_pool_size=2 pswcheck_not_in_auth_file = max_user_client_connections=2 [pgbouncer] logfile = test.log pidfile = test.pid listen_addr = 127.0.0.1 listen_port = 6667 unix_socket_dir = /tmp auth_type = trust auth_file = userlist.txt pool_mode = statement server_check_delay = 10 max_client_conn = 10 default_pool_size = 5 server_lifetime = 120 server_idle_timeout = 60 tcp_defer_accept = 0 tcp_keepalive = 0 ; use a small tcp_socket_buffer so that we trigger EAGAIN more often in tests. ; Otherwise bugs caused by EAGAIN are hard to debug. tcp_socket_buffer = 4096 ;track_extra_parameters is case sensitive. ;bgbouncer will only track the parameters with GUC_REPORT flag set in Postgres track_extra_parameters = search_path, intervalstyle ignore_startup_parameters = extra_float_digits pgbouncer-1.24.1/test/test_auth.py0000644000175000000000000011451314777762222014045 00000000000000import getpass import re import subprocess import time import psycopg import pytest from .utils import ( FREEBSD, LONG_PASSWORD, MACOS, PG_SUPPORTS_SCRAM, TLS_SUPPORT, WINDOWS, ) @pytest.fixture def test_message_fixture(bouncer, pg): yield bouncer, pg pg.sql("ALTER USER test_error_message_user WITH LOGIN;") @pytest.mark.skipif("FREEBSD", reason="FreeBSD error reporting broken") @pytest.mark.skipif("WINDOWS", reason="Windows error reporting broken") def test_message(test_message_fixture): bouncer, pg = test_message_fixture test_user = "test_error_message_user" connection_params = {"user": test_user, "dbname": "p0a"} # Connect to database as User, creates existing pool _ = bouncer.conn(**connection_params) # Change user to nologin pg.sql(f"ALTER USER {test_user} WITH NOLOGIN;") # Kill process on postgres terminate_string = f""" SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND usename = '{test_user}' """ pg.sql(terminate_string) # login, check error message # login again, check error message for _ in range(2): with pytest.raises( psycopg.OperationalError, match=r"is not permitted to log in" ): bouncer.test(**connection_params) @pytest.mark.md5 def test_auth_user(pg, bouncer): bouncer.default_db = "authdb" bouncer.admin(f"set auth_type='md5'") bouncer.test(user="someuser", password="anypasswd") with pytest.raises(psycopg.OperationalError, match="no such user"): bouncer.test(user="nouser", password="anypasswd") with pytest.raises( psycopg.OperationalError, match="(SASL|password) authentication failed" ): bouncer.test(user="someuser", password="badpasswd") pg.sql("ALTER USER someuser VALID UNTIL '1999-01-01'") with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(user="someuser", password="anypasswd") pg.sql("ALTER USER someuser VALID UNTIL 'infinity'") bouncer.test(user="someuser", password="anypasswd") @pytest.mark.md5 def test_auth_dbname_global(bouncer): bouncer.admin(f"set auth_dbname='authdb'") bouncer.admin(f"set auth_user='pswcheck'") bouncer.admin(f"set auth_type='md5'") bouncer.test(dbname="p7a", user="someuser", password="anypasswd") bouncer.test(dbname="p7a", user="pswcheck", password="pgbouncer-check") @pytest.mark.md5 def test_auth_dbname_global_invalid(bouncer): bouncer.admin(f"set auth_dbname='p_unconfigured_auth_dbname'") bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains( 'authentication database "p_unconfigured_auth_dbname" is not configured' ): with pytest.raises(psycopg.OperationalError, match="bouncer config error"): bouncer.test(dbname="authdb", user="someuser", password="anypasswd") # test if auth_dbname specified in connection string takes precedence over # global setting. This automatically tests that the local logic works. bouncer.test(dbname="pauthz", user="someuser", password="anypasswd") def test_auth_dbname_disabled(bouncer): bouncer.admin("disable authdb") bouncer.admin(f"set auth_type='md5'") with pytest.raises( psycopg.OperationalError, match='authentication database "authdb" is disabled' ): bouncer.test(dbname="pauthz", user="someuser", password="anypasswd") @pytest.mark.md5 def test_auth_dbname_with_auto_database(bouncer): with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: # uncomment the auto-database line and add auth_dbname to it new = re.sub( r"^;\* = ", "* = auth_dbname=authdb ", original, flags=re.MULTILINE ) print(new) f.write(new) bouncer.admin("reload") bouncer.admin("set verbose=2") bouncer.admin("set auth_user='pswcheck'") bouncer.admin(f"set auth_type='md5'") # postgres is not defined in test.ini bouncer.test(dbname="postgres", user="someuser", password="anypasswd") bouncer.test(dbname="postgres", user="pswcheck", password="pgbouncer-check") @pytest.mark.md5 def test_unconfigured_auth_database_with_auto_database(bouncer): """ Tests the scenario where the authentication database does not have a connection string configured under [databases] section. However, there is an auto-datatabase, '*', configured. The expectation is to use the wild card connection string for the auth_database. """ with bouncer.ini_path.open() as f: original = f.read() assert ( re.search(r"^unconfigured_auth_database", original, flags=re.MULTILINE) is None ) with bouncer.ini_path.open("w") as f: # uncomment the auto-database line new = re.sub(r"^;\* = ", "* = ", original, flags=re.MULTILINE) print(new) f.write(new) # configure the auth_dbname to a database that is not configured # expected behavior is to fallback to auto-database and auto-register. bouncer.admin("set auth_dbname=unconfigured_auth_database") bouncer.admin("reload") bouncer.admin("set auth_user='pswcheck'") bouncer.admin(f"set auth_type='md5'") # test a database that does not exist on the server, it should fail. # but this error will only surface when we attempt to make the connection to client's # database. Hence, we can conclude that we were able to look up the password using # auth_dbname with pytest.raises( psycopg.OperationalError, match='database "this_database_doesnt_exist" does not exist', ): bouncer.test(dbname="this_database_doesnt_exist", user="muser1", password="foo") # do a final sanity check that we can connect. bouncer.test(user="muser1", password="foo") def run_server_auth_test(bouncer, dbname): bouncer.admin(f"set auth_type='trust'") # good password from ini bouncer.test(dbname=dbname) # bad password from ini with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname=f"{dbname}x") # good password from auth_file bouncer.test(dbname=f"{dbname}y") # bad password from auth_file with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname=f"{dbname}z") # Test plain-text password authentication from PgBouncer to PostgreSQL server # # The PostgreSQL server no longer supports storing plain-text # passwords, so the server-side user actually uses md5 passwords in # this test case, but the communication is still in plain text. def test_password_server(bouncer): run_server_auth_test(bouncer, "p4") # long password from auth_file bouncer.test(dbname="p4l") @pytest.mark.md5 def test_md5_server(bouncer): run_server_auth_test(bouncer, "p5") @pytest.mark.skipif("not PG_SUPPORTS_SCRAM") def test_scram_server(bouncer): # good password from ini bouncer.test(dbname="p6") # bad password from ini with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="p6x") # good password from auth_file, but it is not supported with SCRAM with pytest.raises(psycopg.OperationalError, match="wrong password type"): bouncer.test(dbname="p6y") # bad password from auth_file with pytest.raises(psycopg.OperationalError, match="wrong password type"): bouncer.test(dbname="p6z") @pytest.mark.md5 def connect_with_password_client_users(bouncer): # good password bouncer.test(user="puser1", password="foo") # bad password with pytest.raises( psycopg.OperationalError, match="(password|SASL) authentication failed" ): bouncer.test(user="puser1", password="wrong") def connect_with_md5_client_users(bouncer): # good password bouncer.test(user="muser1", password="foo") # bad password with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(user="muser1", password="wrong") def connect_with_scram_client_users(bouncer): # users with a stored SCRAM password bouncer.test(user="scramuser1", password="foo") # bad password with pytest.raises( psycopg.OperationalError, match="(password|SASL) authentication failed" ): bouncer.test(user="scramuser1", password="wrong") # Test plain-text password authentication from client to PgBouncer @pytest.mark.md5 def test_password_client(bouncer): bouncer.admin(f"set auth_type='plain'") connect_with_password_client_users(bouncer) connect_with_md5_client_users(bouncer) connect_with_scram_client_users(bouncer) # long password bouncer.test(user="longpass", password=LONG_PASSWORD) # too long password with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(user="longpass", password="X" + LONG_PASSWORD) @pytest.mark.md5 def test_md5_client(bouncer): bouncer.admin(f"set auth_type='md5'") connect_with_password_client_users(bouncer) connect_with_md5_client_users(bouncer) connect_with_scram_client_users(bouncer) def test_scram_client(bouncer): bouncer.admin(f"set auth_type='scram-sha-256'") connect_with_password_client_users(bouncer) connect_with_scram_client_users(bouncer) # cannot authenticate to MD5 stored passwords with SCRAM auth # good password with pytest.raises( psycopg.OperationalError, match="(password|SASL) authentication failed" ): bouncer.test(user="muser1", password="foo") # bad password with pytest.raises( psycopg.OperationalError, match="(password|SASL) authentication failed" ): bouncer.test(user="muser1", password="wrong") @pytest.mark.skipif("not PG_SUPPORTS_SCRAM") def test_scram_both(bouncer): bouncer.admin(f"set auth_type='scram-sha-256'") # plain-text password in userlist.txt bouncer.test(dbname="p61", user="scramuser3", password="baz") # SCRAM password in userlist.txt bouncer.test(dbname="p62", user="scramuser1", password="foo") @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_auth_dbname_usage( bouncer, ): """ Check that the pgbouncer handles correctly the reserved pgbouncer database usage as an authentication database """ config = f""" [databases] pgbouncer_test = host={bouncer.pg.host} port={bouncer.pg.port} auth_dbname=pgbouncer * = host={bouncer.host} port={bouncer.port} auth_dbname=pgbouncer [pgbouncer] auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pswcheck auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} """ # We expect that stats user does not exist in userlist.txt with bouncer.log_contains( 'cannot use the reserved "pgbouncer" database as an auth_dbname', 3 ): with bouncer.run_with_config(config): # Check the pgbouncer does not crash when we connect to pgbouncer admin db with pytest.raises(psycopg.OperationalError, match="bouncer config error"): bouncer.sql( query="show stats", user="stats", password="stats", dbname="pgbouncer", ) # Check the pgbouncer does not crash when explicitly pgbouncer database # (admin DB) was set in auth_dbname in the databases definition section with pytest.raises(psycopg.OperationalError, match="bouncer config error"): bouncer.sql( query="show stats", user="stats", password="stats", dbname="pgbouncer_test", ) # Check the pgbouncer does not crash when explicitly pgbouncer database # (admin DB) was set in auth_dbname in the autodb definition with pytest.raises(psycopg.OperationalError, match="bouncer config error"): bouncer.sql( query="show stats", user="stats", password="stats", dbname="p4" ) @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_auth_dbname_usage_global_setting( bouncer, ): """ Check that the pgbouncer does not apply config which contains explicitly "pgbouncer" database (admin DB) set in [pgbouncer] section """ config = f""" [databases] * = host={bouncer.host} port={bouncer.port} [pgbouncer] auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pswcheck auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = pgbouncer """ with bouncer.log_contains( 'cannot use the reserved "pgbouncer" database as an auth_dbname', 1 ): with bouncer.run_with_config(config): pass @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_auth_query_database_setting( bouncer, ): """ Check the pgbouncer can use auth_query in database section to get password """ config = f""" [databases] postgres = auth_query='SELECT usename, passwd FROM pg_shadow where usename = $1'\ host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] auth_query = SELECT 1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres """ with bouncer.run_with_config(config): with bouncer.run_with_config(config): bouncer.sql( query="select version()", user="stats", password="stats", dbname="postgres", ) config = f""" [databases] postgres = auth_query='SELECT usename, substring(passwd,1,3) FROM pg_shadow where usename = $1'\ host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres """ with bouncer.run_with_config(config): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): with bouncer.run_with_config(config): bouncer.sql( query="select version()", user="stats", password="stats", dbname="postgres", ) @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_auth_query_works_with_configured_users(bouncer): """ Check that when a user is configured with per-user options, but missing from auth_file pgBouncer will still attempt to valididate passwords if auth_query is configured. """ config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres pool_mode = session [users] puser1 = pool_mode=statement """ # As a sanity check, make sure that a user with a password in auth_file cannot run transactions # while configured to be in statement pooling mode with bouncer.run_with_config(config): with pytest.raises(psycopg.OperationalError): with bouncer.log_contains( "closing because: transaction blocks not allowed in statement pooling mode" ): bouncer.sql( query="begin", user="puser1", password="foo", dbname="postgres", ) config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres pool_mode = session [users] stats = pool_mode=statement """ # While pgbouncer is set to use session mode by default, the stats user # is set to use statement pooling. pgBouncer should fail to allow a begin # statement while in statement pooling mode, but still be able to authenticate # using auth_query. with bouncer.run_with_config(config): with pytest.raises(psycopg.OperationalError): with bouncer.log_contains( "closing because: transaction blocks not allowed in statement pooling mode" ): bouncer.sql( query="begin", user="stats", password="stats", dbname="postgres", ) @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_auth_query_logs_server_error( bouncer, ): """ Check that when the auth_query response has an error, pgbouncer logs the error message provided by postgres. """ config = f""" [databases] postgres = auth_query='SELECT usename, passwd FROM not_pg_shadow where usename = $1'\ host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] auth_query = SELECT 1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres """ with bouncer.log_contains('"not_pg_shadow" does not exist'): with bouncer.run_with_config(config): with pytest.raises(psycopg.OperationalError, match="bouncer config error"): bouncer.sql( query="select version()", user="stats", password="stats", dbname="postgres", ) @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") @pytest.mark.md5 def test_auth_dbname_works_fine( bouncer, ): """ Check that we handle correctly all positive cases of auth_dbname usage """ config = f""" [databases] postgres_authdb1 = host={bouncer.pg.host} port={bouncer.pg.port} dbname=postgres auth_dbname=postgres postgres_authdb2 = host={bouncer.pg.host} port={bouncer.pg.port} dbname=postgres auth_dbname=postgres pgbouncer2pgbpouncer = host={bouncer.host} port={bouncer.port} dbname=pgbouncer auth_dbname=postgres_authdb2 pgbouncer2pgbpouncer_global = host={bouncer.host} port={bouncer.port} dbname=pgbouncer postgres_test = host={bouncer.host} port={bouncer.port} * = host={bouncer.pg.host} port={bouncer.pg.port} auth_dbname=postgres [pgbouncer] auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1 auth_user = pswcheck stats_users = stats listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = md5 auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres_authdb1 """ with bouncer.run_with_config(config): # The client connects to pgbouncer (admin DB) using userlist.txt file match bouncer.sql( query="show stats", user="pgbouncer", password="fake", dbname="pgbouncer" ) # The client connects to pgbouncer (admin DB) using auth_query, pgbouncer must # use postgres_authdb1 as an auth DB, that defined in [pgbouncer] section bouncer.sql( query="show stats", user="stats", password="stats", dbname="pgbouncer" ) # The client connects to pgbouncer2pgbpouncer DB which redirects # to pgbouncer (admin DB) itself, pgbouncer must use postgres_authdb2, which # is defined in the database definition bouncer.sql( query="show stats", user="stats", password="stats", dbname="pgbouncer2pgbpouncer", ) # The client connects to pgbouncer2pgbpouncer_global DB which redirects # to pgbouncer (admin DB) itself, pgbouncer must use postgres_authdb1, which # is defined in [pgbouncer] section bouncer.sql( query="show stats", user="stats", password="stats", dbname="pgbouncer2pgbpouncer_global", ) # The client connects to admin DB directly # pgbouncer must use postgres_authdb1, which is defined in [pgbouncer] section bouncer.sql( query="show stats", user="stats", password="stats", dbname="pgbouncer" ) # The client connects to postgres DB that matches with autodb # pgbouncer must use postgres_authdb1, which is defined in [pgbouncer] section bouncer.test(user="stats", password="stats", dbname="postgres") def test_hba_leak(bouncer): """ Don't actually check if HBA auth works, but check that it doesn't leak memory when using the feature. """ bouncer.write_ini(f"auth_type = hba") bouncer.write_ini(f"auth_hba_file = hba_test.rules") bouncer.admin("reload") bouncer.write_ini(f"auth_type = trust") bouncer.admin("reload") bouncer.write_ini(f"auth_type = hba") bouncer.admin("reload") bouncer.admin("reload") async def test_change_server_password_reconnect(bouncer, pg): bouncer.default_db = "p4" bouncer.admin(f"set default_pool_size=1") bouncer.admin(f"set pool_mode=transaction") try: # good password, opens connection bouncer.test() pg.sql("ALTER USER puser1 PASSWORD 'bar'") # works fine because server connection is still open bouncer.test() with bouncer.transaction() as cur1: # Claim the connection cur1.execute("select 1") # Because of our fast client closure on server auth failures (see # kill_pool_logins), we should only have one connection failing at # the postgres side. But we should still have 3 failing at the # pgbouncer side. with pg.log_contains( r"password authentication failed", times=1 ), bouncer.log_contains( r"closing because: password authentication failed for user", times=4 ): result1 = bouncer.atest() result2 = bouncer.atest() result3 = bouncer.atest() # Mark the old connection as dirty bouncer.admin("reconnect") # Trigger new connection creation bouncer.admin(f"set default_pool_size=2") with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): await result1 with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): await result2 with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): await result3 time.sleep(3) finally: pg.sql("ALTER USER puser1 PASSWORD 'foo'") async def test_change_server_password_server_lifetime(bouncer, pg): bouncer.default_db = "p4" bouncer.admin(f"set default_pool_size=1") bouncer.admin(f"set pool_mode=transaction") bouncer.admin(f"set server_lifetime=1") try: # good password, opens connection bouncer.test() pg.sql("ALTER USER puser1 PASSWORD 'bar'") # wait until server disconnect time.sleep(3) # Because of our fast client closure on server auth failures (see # kill_pool_logins), we should only have one connection failing at # the postgres side. But we should still have 3 failing at the # pgbouncer side. with pg.log_contains( r"password authentication failed", times=1 ), bouncer.log_contains( r"closing because: password authentication failed for user", times=4 ): result1 = bouncer.atest() result2 = bouncer.atest() result3 = bouncer.atest() with pytest.raises(psycopg.OperationalError): await result1 with pytest.raises(psycopg.OperationalError): await result2 with pytest.raises(psycopg.OperationalError): await result3 time.sleep(3) finally: pg.sql("ALTER USER puser1 PASSWORD 'foo'") @pytest.mark.skipif("MACOS", reason="SSL tests are broken on OSX in CI #1031") @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") @pytest.mark.skipif(not TLS_SUPPORT, reason="pgbouncer is built without TLS support") def test_client_hba_cert(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.write_ini(f"auth_type = hba") bouncer.write_ini( f"auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1" ) bouncer.write_ini(f"auth_user = pswcheck") bouncer.write_ini(f"auth_file = {bouncer.auth_path}") bouncer.write_ini(f"auth_hba_file = pgbouncer_hba.conf") bouncer.write_ini(f"auth_ident_file = pgident.conf") bouncer.admin("reload") client_key = cert_dir / "TestCA1" / "sites" / "04-pgbouncer.acme.org.key" client_cert = cert_dir / "TestCA1" / "sites" / "04-pgbouncer.acme.org.crt" # The client connects to p0x using a client certificate with CN=pgbouncer.acme.org. # hba_eval returns the following line: # hostssl p0x all 0.0.0.0/0 cert map=test # where "test" map is defined in pgident.conf as # test pgbouncer.acme.org someuser # test pgbouncer.acme.org anotheruser # hence the test succeeds. bouncer.psql_test( dbname="p0x", host="localhost", user="someuser", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) bouncer.pg.sql("create user anotheruser with login;") # The client connects to p0x using a client certificate with CN=pgbouncer.acme.org. # hba_eval returns the following line: # hostssl p0x all 0.0.0.0/0 cert map=test # where "test" map is defined in pgident.conf as # test pgbouncer.acme.org someuser # test pgbouncer.acme.org anotheruser # hence the test succeeds. bouncer.psql_test( dbname="p0x", host="localhost", user="anotheruser", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) # The client connects to p0x using a client certificate with CN=pgbouncer.acme.org. # hba_eval returns the following line: # hostssl p0x all 0.0.0.0/0 cert map=test # where "test" map is defined in pgident.conf as # test pgbouncer.acme.org someuser # test pgbouncer.acme.org anotheruser # the username 'bouncer' does not match any mapped pg-username. # hence the test fails. with pytest.raises( subprocess.CalledProcessError, ): with bouncer.log_contains( "p0x/bouncer@127.0.0.1:43544 ident map: test does not have a match" ): bouncer.psql_test( dbname="p0x", host="localhost", user="bouncer", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) client_key = cert_dir / "TestCA1" / "sites" / "02-bouncer.key" client_cert = cert_dir / "TestCA1" / "sites" / "02-bouncer.crt" # The client connects to p0 using a client certificate with CN=bouncer. # hba_eval returns the following line: # hostssl p0 bouncer 0.0.0.0/0 cert # CN expected in map is "bouncer" which matches the CN in the client cert # hence the test succeeds. bouncer.psql_test( dbname="p0", host="localhost", user="bouncer", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) # The client connects to p0y using a client certificate with CN=bouncer. # hba_eval returns the following line: # hostssl p0y all 0.0.0.0/0 cert map=test2 # where # test2 bouncer all # test2 pgbouncer.acme.org "anotheruser" # test2 mapping allows any client with CN="bouncer" to connect using any user name. # Hence the test succeeds. bouncer.psql_test( dbname="p0y", host="localhost", user="someuser", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) client_key = cert_dir / "TestCA1" / "sites" / "04-pgbouncer.acme.org.key" client_cert = cert_dir / "TestCA1" / "sites" / "04-pgbouncer.acme.org.crt" # The client connects to p0y using a client certificate with CN=pgbouncer.acme.org. # hba_eval returns the following line: # hostssl p0y all 0.0.0.0/0 cert map=test2 # where # test2 bouncer all # test2 pgbouncer.acme.org "anotheruser" # for CN=pgbouncer.acme.org, test2 allows to use anotheruser. Hence the test fails. with pytest.raises( subprocess.CalledProcessError, ): with bouncer.log_contains( "p0y/someuser@127.0.0.1:39712 ident map: test2 does not have a match" ): bouncer.psql_test( dbname="p0y", host="localhost", user="someuser", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) # The client connects to p0y using a client certificate with CN=pgbouncer.acme.org. # hba_eval returns the following line: # hostssl p0y all 0.0.0.0/0 cert map=test2 # where # test2 bouncer all # test2 pgbouncer.acme.org "anotheruser" # for CN=pgbouncer.acme.org, test2 allows to use anotheruser. Hence the test succeeds. bouncer.psql_test( dbname="p0y", host="localhost", user="anotheruser", sslmode="verify-full", sslkey=client_key, sslcert=client_cert, sslrootcert=root, ) @pytest.mark.skipif("WINDOWS", reason="Windows does not have peer authentication") def test_peer_auth_ident_map(bouncer): cur_user = getpass.getuser() ident_conf_file = bouncer.config_dir / "ident.conf" hba_conf_file = bouncer.config_dir / "hba.conf" with open(ident_conf_file, "w") as f: f.write(f"mymap {cur_user} postgres\n") f.write(f"mymap {cur_user} someuser\n") with open(hba_conf_file, "w") as f: f.write(f"local all all peer map=mymap") bouncer.write_ini(f"auth_type = hba") bouncer.write_ini( f"auth_query = SELECT usename, passwd FROM pg_shadow where usename = $1" ) bouncer.write_ini(f"auth_user = pswcheck") bouncer.write_ini(f"auth_file = {bouncer.auth_path}") bouncer.write_ini(f"auth_hba_file = {hba_conf_file}") bouncer.write_ini(f"auth_ident_file = {ident_conf_file}") bouncer.admin("reload") bouncer.psql_test( dbname="p0y", host=f"{bouncer.admin_host}", user="postgres", ) bouncer.psql_test( dbname="p0y", host=f"{bouncer.admin_host}", user="someuser", ) with pytest.raises( subprocess.CalledProcessError, ): with bouncer.log_contains( "p0y/bouncer@unix(6202):10202 ident map mymap cannot be matched" ): bouncer.psql_test( dbname="p0y", host=f"{bouncer.admin_host}", user="bouncer", ) with open(ident_conf_file, "w") as f: f.write(f"mymap {cur_user} all") bouncer.admin("reload") bouncer.psql_test( dbname="p0", host=f"{bouncer.admin_host}", user="bouncer", ) async def test_auth_user_trust_auth_without_auth_file_set(bouncer) -> None: """ This is a regression test for issue #1116, using the SET command """ bouncer.admin("set auth_user='pswcheck_not_in_auth_file'") bouncer.admin("set auth_type='trust'") with bouncer.conn( dbname="p7a", user="pswcheck_not_in_auth_file", ) as cn: with cn.cursor() as cur: cur.execute("select 1") def test_auth_user_trust_auth_without_auth_file_reload(bouncer) -> None: """ This is a regression test for issue #1116, using the RELOAD command """ config = f""" [databases] postgres = host={bouncer.pg.host} dbname=postgres port={bouncer.pg.port} min_pool_size=2 [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust auth_user = pswcheck_not_in_auth_file auth_dbname = postgres admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} """ with bouncer.run_with_config(config): with bouncer.conn( dbname="postgres", user="postgres", ) as cn: with cn.cursor() as cur: cur.execute("select 1") def test_auth_user_at_db_level_trust_auth_without_auth_file_reload(bouncer) -> None: """ This is a regression test for issue #1116, when auth_user was set at the database level """ config = f""" [databases] postgres = host={bouncer.pg.host} dbname=postgres port={bouncer.pg.port} min_pool_size=2 auth_user=pswcheck_not_in_auth_file [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust auth_dbname = postgres admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} """ with bouncer.run_with_config(config): with bouncer.conn( dbname="postgres", user="pswcheck_not_in_auth_file", ) as cn: with cn.cursor() as cur: cur.execute("select 1") def test_auth_user_with_same_forced_user(bouncer): """ Check that the pgbouncer correctly handles multiple credentials with the same name with a global auth_user (isue #1103). """ config = f""" [databases] * = host={bouncer.pg.host} port={bouncer.pg.port} user=postgres min_pool_size=2 [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust auth_user = postgres auth_dbname = postgres admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} """ with bouncer.run_with_config(config): # Let's get an error "no such user" with pytest.raises(psycopg.OperationalError, match="no such user"): bouncer.conn(dbname="dummydb2", user="dummyuser2", password="dummypswd2") # Let's wait a few seconds for the janitor to kick in and crash pgbouncer time.sleep(2) # Now we will try to connect with OK parameters with bouncer.conn(dbname="p3", user="postgres", password="asdasd") as cn: with cn.cursor() as cur: cur.execute("select 1") def test_auth_user_at_db_level_with_same_forced_user(bouncer): """ Check that the pgbouncer correctly handles multiple credentials with the same name with auth_user for the specific database (isue #1103). """ config = f""" [databases] * = host={bouncer.pg.host} port={bouncer.pg.port} auth_user=postgres user=postgres min_pool_size=2 [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust auth_dbname = postgres admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} """ with bouncer.run_with_config(config): # Let's get an error "no such user" with pytest.raises(psycopg.OperationalError, match="no such user"): bouncer.conn(dbname="dummydb2", user="dummyuser2", password="dummypswd2") # Let's wait a few seconds for the janitor to kick in and crash pgbouncer time.sleep(2) # Now we will try to connect with OK parameters with bouncer.conn(dbname="p3", user="postgres", password="asdasd") as cn: with cn.cursor() as cur: cur.execute("select 1") pgbouncer-1.24.1/test/__init__.py0000644000175000000000000000000014777762222013565 00000000000000pgbouncer-1.24.1/test/test_admin.py0000644000175000000000000001765514777762222014205 00000000000000import time import psycopg import pytest from psycopg.rows import dict_row from .utils import capture, run def test_show(bouncer): show_items = [ "clients", "config", "databases", # Calling SHOW FDS on MacOS leaks the returned file descriptors to the # python test runner. So we don't test this one directly. SHOW FDS is # still tested indirectly by the takeover tests. # "fds", "help", "lists", "peers", "peer_pools", "pools", "servers", "sockets", "active_sockets", "state", "stats", "stats_totals", "stats_averages", "users", "totals", "mem", "dns_hosts", "dns_zones", ] for item in show_items: bouncer.admin(f"SHOW {item}") def test_socket_id(bouncer) -> None: """Test that PgSocket id is assigned as expected for sockets.""" config = f""" [databases] p1 = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} pool_mode = session server_lifetime = 0 """ with bouncer.run_with_config(config): with bouncer.cur( dbname="pgbouncer", user="pgbouncer", row_factory=dict_row ) as admin_cursor: admin_cursor.execute("SHOW SOCKETS") servers = admin_cursor.fetchall() initial_id = max([i["id"] for i in servers]) for i in range(1, 4): conn_2 = bouncer.conn(dbname="p1") curr = conn_2.cursor() _ = curr.execute("SELECT 1") time.sleep(2) clients = admin_cursor.execute("SHOW SOCKETS").fetchall() assert len(clients) == 3 assert set( [ initial_id, initial_id + i * 2 - 1, initial_id + i * 2, ] ) == set([client["id"] for client in clients]) conn_2.close() time.sleep(2) def test_server_id(bouncer) -> None: """Test that PgSocket id is assigned as expected for servers.""" config = f""" [databases] p1 = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} server_lifetime = 0 """ with bouncer.run_with_config(config): with bouncer.cur( dbname="pgbouncer", user="pgbouncer", row_factory=dict_row ) as admin_cursor: admin_cursor.execute("SHOW SOCKETS") servers = admin_cursor.fetchall() initial_id = max([i["id"] for i in servers]) for i in range(1, 4): conn_2 = bouncer.conn(dbname="p1") curr = conn_2.cursor() _ = curr.execute("SELECT 1") time.sleep(2) clients = admin_cursor.execute("SHOW SERVERS").fetchall() assert [ initial_id + i * 2, ] == [client["id"] for client in clients] conn_2.close() time.sleep(2) def test_client_id(bouncer) -> None: """Test that PgSocket id is assigned as expected for clients.""" config = f""" [databases] p1 = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust admin_users = pgbouncer logfile = {bouncer.log_path} auth_file = {bouncer.auth_path} server_lifetime = 0 """ with bouncer.run_with_config(config): initial_id = bouncer.admin("SHOW CLIENTS", row_factory=dict_row)[0]["id"] for i in range(1, 4): clients = bouncer.admin("SHOW CLIENTS", row_factory=dict_row) assert [ initial_id + i, ] == [client["id"] for client in clients] def test_kill_client_nonexisting(bouncer): # Connect to client as user A conn_1 = bouncer.conn(dbname="p0", user="maxedout") # Validate count clients = bouncer.admin("SHOW CLIENTS", row_factory=dict_row) assert len(clients) == 2 # Issue kill client command with pytest.raises( psycopg.errors.ProtocolViolation, match=r"client not found", ): clients = bouncer.admin(f"KILL_CLIENT 1000") # Validate count clients = bouncer.admin("SHOW CLIENTS") assert len(clients) == 2 conn_1.close() def test_kill_client_invalid(bouncer): # Connect to client as user A conn_1 = bouncer.conn(dbname="p0", user="maxedout") # Validate count clients = bouncer.admin("SHOW CLIENTS") assert len(clients) == 2 # Issue kill client command with pytest.raises( psycopg.errors.ProtocolViolation, match=r"invalid client pointer supplied", ): clients = bouncer.admin("KILL_CLIENT non_existant_client_id") # Validate count clients = bouncer.admin("SHOW CLIENTS") assert len(clients) == 2 conn_1.close() def test_kill_client(bouncer): # Connect to client as user A conn_1 = bouncer.conn(dbname="p0", user="maxedout") # Validate count clients = bouncer.admin("SHOW CLIENTS", row_factory=dict_row) assert len(clients) == 2 # Get clients id client_id = [client for client in clients if client["database"] == "p0"][0]["id"] # Issue kill client command clients = bouncer.admin(f"KILL_CLIENT {client_id}") # Validate count clients = bouncer.admin("SHOW CLIENTS") assert len(clients) == 1 conn_1.close() def test_show_version(bouncer): admin_version = bouncer.admin_value(f"SHOW VERSION") subprocess_result = capture( [*bouncer.base_command(), "--version"], ) subprocess_version = subprocess_result.split("\n")[0] assert admin_version == subprocess_version def test_help(bouncer): run([*bouncer.base_command(), "--help"]) def test_show_stats(bouncer): # Use session pooling database to see differenecs between transactions and # server assignments bouncer.default_db = "p3" bouncer.test() bouncer.test() bouncer.test() bouncer.test() with bouncer.cur() as cur: with cur.connection.transaction(): cur.execute("SELECT 1") cur.execute("SELECT 1") cur.execute("SELECT 1") with cur.connection.transaction(): cur.execute("SELECT 1") cur.execute("SELECT 1") cur.execute("SELECT 1") stats = bouncer.admin("SHOW STATS", row_factory=dict_row) p3_stats = next(s for s in stats if s["database"] == "p3") assert p3_stats is not None # 5 connection attempts (and thus assignments) assert p3_stats["total_server_assignment_count"] == 5 # 4 autocommit queries + 2 transactions assert p3_stats["total_xact_count"] == 6 # 11 SELECT 1 + 2 times COMMIT and ROLLBACK assert p3_stats["total_query_count"] == 15 stats = bouncer.admin("SHOW STATS_TOTALS", row_factory=dict_row) p3_stats = next(s for s in stats if s["database"] == "p3") assert p3_stats is not None # 5 connection attempts (and thus assignments) assert p3_stats["server_assignment_count"] == 5 # 4 autocommit queries + 2 transactions assert p3_stats["xact_count"] == 6 # 11 SELECT 1 + 2 times COMMIT and ROLLBACK assert p3_stats["query_count"] == 15 totals = bouncer.admin("SHOW TOTALS") # 5 connection attempts (and thus assignments) assert ("total_server_assignment_count", 5) in totals # 4 autocommit queries + 2 transactions + 4 admin commands assert ("total_xact_count", 10) in totals # 11 SELECT 1 + 2 times COMMIT and ROLLBACK + 4 admin commands assert ("total_query_count", 19) in totals pgbouncer-1.24.1/test/test_no_user.py0000644000175000000000000000671014777762222014555 00000000000000import psycopg import pytest # Several tests that check the behavior when connecting with a # nonexistent user under various authentication types. Database p1 # has a forced user, p2 does not; these exercise slightly different # code paths. def test_no_user_trust(bouncer): bouncer.admin(f"set auth_type='trust'") with bouncer.log_contains(r'closing because: "trust" authentication failed'): with pytest.raises( psycopg.OperationalError, match='"trust" authentication failed' ): bouncer.test(dbname="p2", user="nosuchuser") def test_no_user_trust_forced_user(bouncer): bouncer.admin(f"set auth_type='trust'") with bouncer.log_contains(r'closing because: "trust" authentication failed'): with pytest.raises( psycopg.OperationalError, match='"trust" authentication failed' ): bouncer.test(dbname="p1", user="nosuchuser") def test_no_user_password(bouncer): bouncer.admin(f"set auth_type='plain'") with bouncer.log_contains(r"closing because: password authentication failed"): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="p2", user="nosuchuser", password="whatever") def test_no_user_password_forced_user(bouncer): bouncer.admin(f"set auth_type='plain'") with bouncer.log_contains(r"closing because: password authentication failed"): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="p1", user="nosuchuser", password="whatever") def test_no_user_md5(bouncer): bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains(r"closing because: password authentication failed"): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="p2", user="nosuchuser", password="whatever") def test_no_user_md5_forced_user(bouncer): bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains(r"closing because: password authentication failed"): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="p1", user="nosuchuser", password="whatever") def test_no_user_scram(bouncer): bouncer.admin(f"set auth_type='scram-sha-256'") with bouncer.log_contains(r"closing because: SASL authentication failed"): with pytest.raises( psycopg.OperationalError, match="SASL authentication failed" ): bouncer.test(dbname="p2", user="nosuchuser", password="whatever") def test_no_user_scram_forced_user(bouncer): bouncer.admin(f"set auth_type='scram-sha-256'") with bouncer.log_contains(r"closing because: SASL authentication failed"): with pytest.raises( psycopg.OperationalError, match="SASL authentication failed" ): bouncer.test(dbname="p1", user="nosuchuser", password="whatever") def test_no_user_auth_user(bouncer): bouncer.admin(f"set auth_type='md5'") # Currently no mock authentication when using # auth_query/auth_user. See TODO in # handle_auth_query_response(). with bouncer.log_contains(r"closing because: no such user \(age"): with pytest.raises(psycopg.OperationalError, match="no such user"): bouncer.test(dbname="authdb", user="nosuchuser", password="whatever") pgbouncer-1.24.1/test/userlist.txt0000644000175000000000000000140614777762222014102 00000000000000"marko" "kama" "postgres" "asdasd" ;Commented out line should be ignored. "pgbouncer" "fake" "pswcheck" "pgbouncer-check" "maxedout" "" "maxedout2" "" "maxedout3" "" "maxedout4" "" "maxedout5" "" "poolsize1" "" "respoolsize1" "" "bouncer" "zzzz" ;the following pairs of passwords are "foo" and "bar" "puser1" "foo" "puser2" "bar" "muser1" "md5ab8b744ff66bee42dc47bae34ca17959" "muser2" "md598455b3585818e23c2653a59f6d84551" "scramuser1" "SCRAM-SHA-256$4096:D76gvGUVj9Z4DNiGoabOBg==$RukL0Xo3Ql/2F9FsD7mcQ3GATG2fD3PA71qY1JagGDs=:BhKUwyyivFm7Tq2jDJVXSVRbRDgTWyBilZKgg6DDuYU=" "scramuser2" "SCRAM-SHA-256$4096:BUYIT9YAsWf036OHLPfv2Q==$zuk7pzdEc5tvftsRIuwlStNxVc+wIE/xfDs7ahsKN0k=:Lov6mndE0DmlgtY60e1LyDsLctQyoH+Bvkm9K9njSa8=" "scramuser3" "baz" "test_error_message_user" "" pgbouncer-1.24.1/test/hba_test.rules0000644000175000000000000000337514777762222014343 00000000000000# METHOD: trust, reject, md5, password, peer, cert # local DATABASE USER METHOD [OPTIONS] # host DATABASE USER ADDRESS METHOD [OPTIONS] # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] # hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] # The following lines test that weird whitespace does not break parsing # ws # z # testing # Allow access to pgbouncer admin database from localhost for windows tests host pgbouncer pgbouncer 127.0.0.1/32 trust # actual tests local dbp all peer local all userp password local dbz userz trust local dbs users scram-sha-256 local all all md5 hostssl all all 10.0.0.0/8 cert hostnossl all all 11.0.0.0/8 md5 host all all 127.0.0.0 255.255.255.0 password host db1 all 0.0.0.0/0 md5 host all user1 15.0.0.0/8 md5 host tmp1,all user1,user2 , user3 16.0.0.0/8 md5 host tmp2,all u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u2 17.0.0.0/8 md5 host d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11 t18user 18.0.0.0/8 trust # comment host "all" "all" 19.0.0.0/8 cert host "q1""q2" "a , b" 19.0.0.0/8 cert # mask host mdb muser 199.199.199.199/32 cert host mdb2 muser 128.0.0.0/9 trust host mdb2 muser 128.0.0.0/8 md5 host mdb2 muser 128.0.0.0/7 cert host mdb2 muser 128.0.0.0/6 password host mdb2 muser 128.0.0.0/5 cert host mdb2 muser 128.0.0.0/4 trust host mdb2 muser 128.0.0.0/3 md5 host mdb2 muser 128.0.0.0/2 password host mdb2 muser 128.0.0.0/1 cert # ipv6 host mdb muser ff11::0/16 md5 host mdb muser ff20::/12 md5 host mdb muser ::1/128 trust # "all" address host mdb3 muser all scram-sha-256 host mdb4 muser all reject # replication host replication admin ::1/128 trust host db2,replication admin2 ::1/128 trust pgbouncer-1.24.1/test/stress.py0000755000175000000000000000503614777762222013372 00000000000000#! /usr/bin/env python3 import random import threading import time import psycopg2 n_thread = 100 longtx = False tx_sleep = 8 conn_data = { "dbname": "marko", #'host': '127.0.0.1', "host": "/tmp", "port": "6432", "user": "marko", #'password': '', "connect_timeout": "5", } def get_connstr(): tmp = [] for k, v in conn_data.items(): tmp.append(k + "=" + v) return " ".join(tmp) class WorkThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.setDaemon(True) self.stat_lock = threading.Lock() self.query_cnt = 0 def inc_cnt(self): self.stat_lock.acquire() self.query_cnt += 1 self.stat_lock.release() def fetch_cnt(self): self.stat_lock.acquire() val = self.query_cnt self.query_cnt = 0 self.stat_lock.release() return val def run(self): try: time.sleep(random.random() * 10.0) except Exception: pass while 1: try: self.main_loop() except KeyboardInterrupt: break except SystemExit: break except Exception as d: print(d) try: time.sleep(5) except Exception: pass def main_loop(self): db = psycopg2.connect(get_connstr()) if not longtx: db.autocommit = True n = 0 while n < 10: self.do_work(db) self.inc_cnt() n += 1 def do_work(self, db): curs = db.cursor() q = "select pg_sleep(%.02f)" % (random.random() * 1) curs.execute(q) time.sleep(tx_sleep * random.random() + 1) if longtx: db.commit() def main(): print("connstr %s" % get_connstr()) thread_list = [] while len(thread_list) < n_thread: t = WorkThread() t.start() thread_list.append(t) print("started %d threads" % len(thread_list)) last = time.time() while 1: time.sleep(1) now = time.time() dur = now - last if dur >= 5: last = now cnt = 0 for t in thread_list: cnt += t.fetch_cnt() avg = cnt / dur print("avg %s" % avg) if __name__ == "__main__": try: main() except SystemExit: pass except KeyboardInterrupt: pass # except Exception as d: # print d pgbouncer-1.24.1/test/conntest.sh0000755000175000000000000000165414777762222013670 00000000000000#!/bin/sh fw_drop_port() { echo "fw_drop_port" case `uname` in Linux) sudo iptables -A OUTPUT -p tcp --dport $1 -j DROP;; Darwin|OpenBSD) echo "block drop out proto tcp from any to 127.0.0.1 port $1" \ | sudo pfctl -a pgbouncer -f -;; *) echo "Unknown OS"; exit 1;; esac } fw_reject_port() { echo "fw_reject_port" case `uname` in Linux) sudo iptables -A OUTPUT -p tcp --dport $1 -j REJECT --reject-with tcp-reset;; Darwin|OpenBSD) echo "block return-rst out proto tcp from any to 127.0.0.1 port $1" \ | sudo pfctl -a pgbouncer -f -;; *) echo "Unknown OS"; exit 1;; esac } fw_reset() { echo "fw_reset" case `uname` in Linux) sudo iptables -F;; Darwin|OpenBSD) sudo pfctl -a pgbouncer -F all;; *) echo "Unknown OS"; exit 1;; esac } port=5432 port=7000 fw_reset while true; do fw_drop_port $port sleep 12 fw_reset sleep 12 fw_reject_port $port sleep 3 fw_reset sleep 6 done pgbouncer-1.24.1/test/test_replication.py0000644000175000000000000002355014777762222015415 00000000000000import asyncio import signal import subprocess import time import psycopg import psycopg.errors import pytest from psycopg import sql from .utils import PG_MAJOR_VERSION, WINDOWS, run def test_logical_rep(bouncer): connect_args = { "dbname": "user_passthrough", "replication": "database", "user": "postgres", "application_name": "abc", "options": "-c enable_seqscan=off", } # Starting in PG10 you can do other commands over logical rep connections if PG_MAJOR_VERSION >= 10: bouncer.test(**connect_args) assert bouncer.sql_value("SHOW application_name", **connect_args) == "abc" assert bouncer.sql_value("SHOW enable_seqscan", **connect_args) == "off" bouncer.sql("IDENTIFY_SYSTEM", **connect_args) # Do a normal connection to the same pool, to ensure that that doesn't # break anything bouncer.test(dbname="user_passthrough", user="postgres") bouncer.sql("IDENTIFY_SYSTEM", **connect_args) def test_logical_rep_auth_query(bouncer): connect_args = { "dbname": "pauthz", "replication": "database", "user": "pswcheck_not_in_auth_file", "application_name": "abc", "options": "-c enable_seqscan=off", } # Starting in PG10 you can do other commands over logical rep connections if PG_MAJOR_VERSION >= 10: bouncer.test(**connect_args) assert bouncer.sql_value("SHOW application_name", **connect_args) == "abc" assert bouncer.sql_value("SHOW enable_seqscan", **connect_args) == "off" bouncer.sql("IDENTIFY_SYSTEM", **connect_args) # Do a normal connection to the same pool, to ensure that that doesn't # break anything bouncer.test(dbname="user_passthrough", user="postgres") bouncer.sql("IDENTIFY_SYSTEM", **connect_args) def test_logical_rep_unprivileged(bouncer): if PG_MAJOR_VERSION < 10: expected_log = "no pg_hba.conf entry for replication connection" elif PG_MAJOR_VERSION < 16: expected_log = "must be superuser or replication role to start walsender" else: expected_log = "permission denied to start WAL sender" with bouncer.log_contains( expected_log, ), bouncer.log_contains( r"closing because: login failed \(age", times=2 ), pytest.raises(psycopg.OperationalError, match=r"login failed"): bouncer.sql("IDENTIFY_SYSTEM", replication="database") @pytest.mark.skipif( "PG_MAJOR_VERSION < 10", reason="logical replication was introduced in PG10" ) def test_logical_rep_subscriber(bouncer): bouncer.admin("set pool_mode=transaction") # First write create a table and insert a row in the source database. # Also create the replication slot and publication bouncer.default_db = "user_passthrough" bouncer.create_schema("test_logical_rep_subscriber") bouncer.sql("CREATE TABLE test_logical_rep_subscriber.table(a int)") bouncer.sql("INSERT INTO test_logical_rep_subscriber.table values (1)") assert ( bouncer.sql_value("SELECT count(*) FROM test_logical_rep_subscriber.table") == 1 ) bouncer.create_publication( "mypub", sql.SQL("FOR TABLE test_logical_rep_subscriber.table") ) bouncer.create_logical_replication_slot("test_logical_rep_subscriber", "pgoutput") # Create an equivalent, but empty schema in the target database. # And setup the subscription bouncer.default_db = "user_passthrough2" bouncer.create_schema("test_logical_rep_subscriber") bouncer.sql("CREATE TABLE test_logical_rep_subscriber.table(a int)") conninfo = bouncer.make_conninfo(dbname="user_passthrough") bouncer.create_subscription( "mysub", sql.SQL( """ CONNECTION {} PUBLICATION mypub WITH (slot_name=test_logical_rep_subscriber, create_slot=false) """ ).format(sql.Literal(conninfo)), ) # The initial copy should now copy over the row time.sleep(2) assert ( bouncer.sql_value("SELECT count(*) FROM test_logical_rep_subscriber.table") >= 1 ) # Insert another row and logical replication should replicate it correctly bouncer.sql( "INSERT INTO test_logical_rep_subscriber.table values (2)", dbname="user_passthrough", ) time.sleep(2) assert ( bouncer.sql_value("SELECT count(*) FROM test_logical_rep_subscriber.table") >= 2 ) @pytest.mark.skipif( "WINDOWS", reason="MINGW does not have contrib package containing test_decoding" ) def test_logical_rep_pg_recvlogical(bouncer): bouncer.default_db = "user_passthrough" bouncer.create_schema("test_logical_rep_pg_recvlogical") bouncer.sql("CREATE TABLE test_logical_rep_pg_recvlogical.table(a int)") bouncer.create_logical_replication_slot( "test_logical_rep_pg_recvlogical", "test_decoding" ) process = subprocess.Popen( [ "pg_recvlogical", "--dbname", bouncer.default_db, "--host", bouncer.host, "--port", str(bouncer.port), "--user", bouncer.default_user, "--slot=test_logical_rep_pg_recvlogical", "--file=-", "--no-loop", "--start", ], stdout=subprocess.PIPE, ) assert process.stdout is not None bouncer.sql("INSERT INTO test_logical_rep_pg_recvlogical.table values (1)") try: assert process.stdout.readline().startswith(b"BEGIN ") assert ( process.stdout.readline() == b'table test_logical_rep_pg_recvlogical."table": INSERT: a[integer]:1\n' ) assert process.stdout.readline().startswith(b"COMMIT ") finally: process.kill() process.communicate(timeout=5) def test_physical_rep(bouncer): connect_args = { "dbname": "user_passthrough", "replication": "yes", "user": "postgres", "application_name": "abc", "options": "-c enable_seqscan=off", } # Starting in PG10 you can do SHOW commands if PG_MAJOR_VERSION >= 10: with pytest.raises( psycopg.errors.FeatureNotSupported, match="cannot execute SQL commands in WAL sender for physical replication", ): bouncer.test(**connect_args) assert bouncer.sql_value("SHOW application_name", **connect_args) == "abc" assert bouncer.sql_value("SHOW enable_seqscan", **connect_args) == "off" bouncer.sql("IDENTIFY_SYSTEM", **connect_args) # Do a normal connection to the same pool, to ensure that that doesn't # break anything bouncer.test(dbname="user_passthrough", user="postgres") bouncer.sql("IDENTIFY_SYSTEM", **connect_args) def test_physcal_rep_unprivileged(bouncer): with bouncer.log_contains( r"no pg_hba.conf entry for replication connection from host" ), bouncer.log_contains( r"closing because: login failed \(age", times=2 ), pytest.raises( psycopg.OperationalError, match=r"login failed" ): bouncer.test(replication="yes") @pytest.mark.skipif("PG_MAJOR_VERSION < 10", reason="pg_receivewal was added in PG10") def test_physical_rep_pg_receivewal(bouncer, tmp_path): bouncer.default_db = "user_passthrough" bouncer.create_physical_replication_slot("test_physical_rep_pg_receivewal") wal_dump_dir = tmp_path / "wal-dump" wal_dump_dir.mkdir() process = subprocess.Popen( [ "pg_receivewal", "--dbname", bouncer.make_conninfo(), "--slot=test_physical_rep_pg_receivewal", "--directory", str(wal_dump_dir), ], ) time.sleep(3) if WINDOWS: process.terminate() else: process.send_signal(signal.SIGINT) process.communicate(timeout=5) if WINDOWS: assert process.returncode == 1 else: assert process.returncode == 0 children = list(wal_dump_dir.iterdir()) assert len(children) > 0 def test_physical_rep_pg_basebackup(bouncer, tmp_path): bouncer.default_db = "user_passthrough" dump_dir = tmp_path / "db-dump" dump_dir.mkdir() run( [ "pg_basebackup", "--dbname", bouncer.make_conninfo(), "--checkpoint=fast", "--pgdata", dump_dir, ], ) children = list(dump_dir.iterdir()) assert len(children) > 0 print(children) @pytest.mark.asyncio @pytest.mark.skipif( "PG_MAJOR_VERSION < 10", reason="normal SQL commands are only supported in PG10+ on logical replication connections", ) async def test_replication_pool_size(pg, bouncer): connect_args = { "dbname": "user_passthrough_pool_size2", "replication": "database", "user": "postgres", "connect_timeout": 10, } start = time.time() await bouncer.asleep(0.5, times=10, **connect_args) assert time.time() - start > 2.5 # Replication connections always get closed right away assert pg.connection_count("p0") == 0 connect_args["dbname"] = "user_passthrough_pool_size5" start = time.time() await bouncer.asleep(0.5, times=10, **connect_args) assert time.time() - start > 1 # Replication connections always get closed right away assert pg.connection_count("p0") == 0 @pytest.mark.asyncio @pytest.mark.skipif( "PG_MAJOR_VERSION < 10", reason="normal SQL commands are only supported in PG10+ on logical replication connections", ) async def test_replication_pool_size_mixed_clients(bouncer): connect_args = { "dbname": "user_passthrough_pool_size2", "user": "postgres", } # Fill the pool with normal connections await bouncer.asleep(0.5, times=2, **connect_args) # Then try to open a replication connection and ensure that it causes # eviction of one of the normal connections with bouncer.log_contains("closing because: evicted"): bouncer.test(**connect_args, replication="database") pgbouncer-1.24.1/test/test_prepared.py0000644000175000000000000006547514777762222014722 00000000000000import random import time import psycopg import pytest from psycopg import pq, sql from psycopg.rows import dict_row from .utils import LIBPQ_SUPPORTS_PIPELINING, LINUX, PKT_BUF_SIZE, USE_SUDO def test_prepared_statement(bouncer): bouncer.admin(f"set pool_mode=transaction") prepared_query = "SELECT 1" with bouncer.cur() as cur1: with bouncer.cur() as cur2: # prepare query on server 1 and client 1 cur1.execute(prepared_query, prepare=True) # Run the prepared query again on same server and client cur1.execute(prepared_query) with cur2.connection.transaction(): # Claim server 1 with client 2 cur2.execute("SELECT 2") # Client 1 now runs the prepared query, and it's automatically # prepared on server 2 cur1.execute(prepared_query) # Client 2 now prepares the same query that was already # prepared on server 1. And PgBouncer reuses that already # prepared query for this different client. cur2.execute(prepared_query, prepare=True) def test_prepared_statement_params(bouncer): bouncer.admin(f"set pool_mode=transaction") prepared_query = "SELECT %s" with bouncer.cur() as cur1: with bouncer.cur() as cur2: # prepare query on server 1 and client 1 cur1.execute(prepared_query, params=(1,), prepare=True) # Run the prepared query again on same server and client cur1.execute(prepared_query, params=(1,)) with cur2.connection.transaction(): # Claim server 1 with client 2 cur2.execute("SELECT 2") # Client 1 now runs the prepared query, and it's automatically # prepared on server 2 cur1.execute(prepared_query, params=(1,)) # Client 2 now prepares the same query that was already # prepared on server 1. And PgBouncer reuses that already # prepared query for this different client. cur2.execute(prepared_query, params=(1,), prepare=True) def test_deallocate_all(bouncer): bouncer.admin(f"set pool_mode=transaction") prepared_query = "SELECT 1" with bouncer.cur() as cur1: with bouncer.cur() as cur2: # prepare query on client 1 cur1.execute(prepared_query, prepare=True) # Run the prepared query again on same server and client cur1.execute(prepared_query) # prepared query for client 2 cur2.execute(prepared_query, prepare=True) # execute DEALLOCATE ALL on client 1 cur1.execute("DEALLOCATE ALL") # Run the prepared query again on server 2 and client 2 cur2.execute(prepared_query) # Confirm that the prepared query is not available anymore on # client 1 with bouncer.log_contains("prepared statement did not exist"): with pytest.raises( psycopg.OperationalError, match="prepared statement did not exist|server closed the connection unexpectedly", ): cur1.execute(prepared_query) def test_discard_all(bouncer): bouncer.admin(f"set pool_mode=transaction") prepared_query = "SELECT 1" with bouncer.cur() as cur1: with bouncer.cur() as cur2: # prepare query on client 1 cur1.execute(prepared_query, prepare=True) # Run the prepared query again on same server and client cur1.execute(prepared_query) # prepared query for client 2 cur2.execute(prepared_query, prepare=True) # execute DISCARD ALL on client 1 cur1.execute("DISCARD ALL") # Run the prepared query again on server 2 and client 2 cur2.execute(prepared_query) # Confirm that the prepared query is not available anymore on # client 1 with bouncer.log_contains("prepared statement did not exist"): with pytest.raises( psycopg.OperationalError, match="prepared statement did not exist|server closed the connection unexpectedly", ): cur1.execute(prepared_query) def test_parse_larger_than_pkt_buf(bouncer): long_string = "1" * PKT_BUF_SIZE * 10 prepared_query = "SELECT '" + long_string + "'" with bouncer.cur() as cur1: result = cur1.execute(prepared_query, prepare=True).fetchone()[0] assert result == long_string def test_bind_larger_than_pkt_buf(bouncer): long_string = "1" * PKT_BUF_SIZE * 10 prepared_query = "SELECT %s::text" with bouncer.cur() as cur1: result = cur1.execute( prepared_query, params=(long_string,), prepare=True ).fetchone()[0] assert result == long_string # The 4x larger than pkt_buf amount is special, because if the extra_packets # buffer becomes larger than that it will be freed. # (see sbuf_process_pending in sbuf.c) def test_parse_larger_than_pkt_buf_but_smaller_than_4x(bouncer): long_string = "1" * PKT_BUF_SIZE * 2 prepared_query = "SELECT '" + long_string + "'" with bouncer.cur() as cur1: result = cur1.execute(prepared_query, prepare=True).fetchone()[0] assert result == long_string def test_bind_larger_than_pkt_buf_but_smaller_than_4x(bouncer): long_string = "1" * PKT_BUF_SIZE * 2 prepared_query = "SELECT %s::text" with bouncer.cur() as cur1: result = cur1.execute( prepared_query, params=(long_string,), prepare=True ).fetchone()[0] assert result == long_string # In one of the initial implementation of prepared statement support there was # a bug, that if a varcache change was needed, then the callback would not be # called again correctly later. def test_parse_larger_than_pkt_buf_with_varcache_change(bouncer): long_string = "1" * PKT_BUF_SIZE * 10 prepared_query = "SELECT '" + long_string + "'" with bouncer.cur(dbname="varcache_change") as cur1: result = cur1.execute(prepared_query, prepare=True).fetchone()[0] assert result == long_string def test_evict_statement_cache(bouncer): bouncer.admin(f"set max_prepared_statements=1") with bouncer.cur() as cur: for i in range(5): prepared_query = f"SELECT '{i}'" result = cur.execute(prepared_query, prepare=True).fetchone()[0] assert result == str(i) n_statements = cur.execute( "SELECT count(*) FROM pg_prepared_statements" ).fetchone()[0] assert n_statements == 1 bouncer.admin(f"set max_prepared_statements=5") for i in range(5, 10): prepared_query = f"SELECT '{i}'" result = cur.execute(prepared_query, prepare=True).fetchone()[0] assert result == str(i) n_statements = cur.execute( "SELECT count(*) FROM pg_prepared_statements" ).fetchone()[0] assert n_statements == 5 bouncer.admin(f"set max_prepared_statements=2") n_statements = cur.execute( "SELECT count(*) FROM pg_prepared_statements" ).fetchone()[0] assert n_statements == 5 result = cur.execute("SELECT '10'", prepare=True).fetchone()[0] assert result == "10" n_statements = cur.execute( "SELECT count(*) FROM pg_prepared_statements" ).fetchone()[0] assert n_statements == 2 # Test behaviour when disabling prepared statement handling bouncer.admin(f"set max_prepared_statements=0") # Since we disabled prepared statement handling, this should now fail # because we forward the client its prepared statement name to the # server and the server doesn't know about that name. with pytest.raises(psycopg.errors.InvalidSqlStatementName): cur.execute("SELECT '10'", prepare=True) # While setting the cache size to 0 disables prepared statement # handling completely, but it doesn't clear any of existing caches. # Preferably we would clear the existing caches, but that's not easy to # implement. Right now we only evict statements from the cache when we # insert into a cache, and since we disabled prepared statement # handling we never insert into a cache. n_statements = cur.execute( "SELECT count(*) FROM pg_prepared_statements" ).fetchone()[0] assert n_statements == 2 @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_evict_statement_cache_pipeline_failure(bouncer): bouncer.admin(f"set max_prepared_statements=1") with bouncer.conn() as conn: with conn.pipeline() as p: curs = [conn.cursor() for _ in range(4)] curs[0].execute("SELECT 1", prepare=True) curs[1].execute("bad query", prepare=True) with pytest.raises(psycopg.errors.SyntaxError): p.sync() assert curs[0].fetchall() == [(1,)] curs[0].execute("SELECT 1", prepare=True) p.sync() assert curs[0].fetchall() == [(1,)] @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_prepared_statement_pipeline(bouncer): # Prepare query on the server connection and then disconnect again prepared_query = "SELECT 1" with bouncer.cur() as cur: result = cur.execute(prepared_query, prepare=True).fetchone()[0] assert result == 1 # Try with a prepared query first and a unprepared query second with bouncer.conn() as conn: with conn.pipeline(): curs = [conn.cursor() for _ in range(4)] curs[0].execute("SELECT 2", prepare=True) curs[1].execute(prepared_query, prepare=True) curs[2].execute("SELECT 3") curs[3].execute(prepared_query, prepare=True) assert curs[0].fetchall() == [(2,)] assert curs[1].fetchall() == [(1,)] assert curs[2].fetchall() == [(3,)] assert curs[3].fetchall() == [(1,)] # Try with a unprepared query first and a prepared query second with bouncer.conn() as conn: with conn.pipeline(), conn.cursor() as cur: curs = [conn.cursor() for _ in range(4)] curs[0].execute("SELECT 2", prepare=True) curs[1].execute(prepared_query, prepare=True) curs[2].execute("SELECT 3") curs[3].execute(prepared_query, prepare=True) assert curs[0].fetchall() == [(2,)] assert curs[1].fetchall() == [(1,)] assert curs[2].fetchall() == [(3,)] assert curs[3].fetchall() == [(1,)] @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") @pytest.mark.timeout(300) def test_prepared_statement_pipeline_stress(bouncer): max_pipeline_length = 10 max_prepared_stmt = 100 n_iterations = 100 max_prepared_statements = max_prepared_stmt * 2 // 3 size_of_param = 512 bouncer.admin(f"set max_prepared_statements={max_prepared_statements}") for pipeline_length in range(max_pipeline_length): # Try with a prepared query first and a unprepared query second with bouncer.conn() as conn: with conn.pipeline(): curs = [conn.cursor() for _ in range(pipeline_length)] for _ in range(n_iterations): for i in range(pipeline_length): stmt_id = random.randint(1, max_prepared_stmt) curs[i].execute( sql.SQL("SELECT %s, %s::text as {}").format( sql.Identifier(f"s{stmt_id}") ), params=(i, str(i).zfill(size_of_param)), prepare=True, ) for i in range(pipeline_length): assert curs[i].fetchall() == [(i, str(i).zfill(size_of_param))] def test_describe_non_existent_prepared_statement(bouncer): bouncer.admin(f"set max_prepared_statements=100") with bouncer.conn() as conn: result = conn.pgconn.describe_prepared(b"doesnotexist") assert result.status == pq.ExecStatus.FATAL_ERROR assert b"server closed the connection unexpectedly" in result.error_message # libpq before PG17 does not support sending Close messages @pytest.mark.skipif("psycopg.pq.version() < 170000") @pytest.mark.skipif( "psycopg.__version__ < '3.2.0'", reason="Debian oldstable doesn't support a version of psycopg with 'close_prepared' support", ) def test_close_prepared_statement(bouncer): with bouncer.conn() as conn: result = conn.pgconn.prepare(b"test", b"SELECT 1") assert result.status == pq.ExecStatus.COMMAND_OK result = conn.pgconn.close_prepared(b"test") assert result.status == pq.ExecStatus.COMMAND_OK # closing a non-existent prepared statement should not raise an error result = conn.pgconn.close_prepared(b"test") assert result.status == pq.ExecStatus.COMMAND_OK # ensure that the prepared statement is actually closed by trying to # describe it. result = conn.pgconn.describe_prepared(b"test") assert result.status == pq.ExecStatus.FATAL_ERROR def test_statement_name_longer_than_pkt_buf(bouncer): name = b"a" * PKT_BUF_SIZE * 4 with bouncer.conn() as conn: result = conn.pgconn.prepare(name, b"SELECT $1::text") assert result.status == pq.ExecStatus.COMMAND_OK result = conn.pgconn.describe_prepared(name) assert result.status == pq.ExecStatus.COMMAND_OK result = conn.pgconn.exec_prepared(name, (b"abc",)) assert result.status == pq.ExecStatus.TUPLES_OK assert result.get_value(0, 0) == b"abc" if psycopg.pq.version() >= 170000 and psycopg.__version__ >= "3.2.0": # libpq before PG17 does not support sending Close messages # Debian oldstable does not package psycopg with 'close_prepared' result = conn.pgconn.close_prepared(name) assert result.status == pq.ExecStatus.COMMAND_OK # Ensure that the close was successful result = conn.pgconn.describe_prepared(name) assert result.status == pq.ExecStatus.FATAL_ERROR @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_prepared_statement_pipeline_error(bouncer): # Prepare query on the server connection and then disconnect again prepared_query = "SELECT 1" with bouncer.cur() as cur: result = cur.execute(prepared_query, prepare=True).fetchone()[0] assert result == 1 # Make sure queue is cleared after error with bouncer.conn() as conn: with conn.pipeline() as p, conn.cursor() as cur: cur.execute("SELECT aaaa") with pytest.raises( psycopg.errors.UndefinedColumn, match='column "aaaa" does not exist' ): p.sync() cur.execute(prepared_query, prepare=True) assert cur.fetchall() == [(1,)] @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_prepared_statement_pipeline_error_delayed_sync(bouncer): # Prepare query on the server connection and then disconnect again prepared_query = "SELECT 1" with bouncer.cur() as cur: result = cur.execute(prepared_query, prepare=True).fetchone()[0] assert result == 1 # Make sure queue is fully cleared until Sync on error, even future # messages that have not yet been received by PgBouncer (including the Sync # itself) with bouncer.conn() as conn: with conn.pipeline() as p, conn.cursor() as cur: cur.execute("SELECT aaaa") time.sleep(0.1) with pytest.raises( psycopg.errors.UndefinedColumn, match='column "aaaa" does not exist' ): cur.execute("SELECT 123") with pytest.raises(psycopg.errors.PipelineAborted): cur.fetchall() p.sync() cur.execute(prepared_query, prepare=True) assert cur.fetchall() == [(1,)] def test_prepared_failed_prepare(bouncer): with bouncer.cur() as cur: with pytest.raises(psycopg.errors.UndefinedTable): cur.execute("SELECT * FROM doesnotexistyet", prepare=True) cur.execute("CREATE TABLE doesnotexistyet (a int)") cur.execute("SELECT * FROM doesnotexistyet", prepare=True) cur.execute("DROP TABLE doesnotexistyet") @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_prepared_failed_prepare_pipeline(bouncer): with bouncer.conn() as conn: with conn.pipeline() as p, conn.cursor() as cur: cur.execute("SELECT 1", prepare=True) cur.execute("SELECT * FROM doesnotexistyet", prepare=True) with pytest.raises(psycopg.errors.UndefinedTable): # Either of these two commands might fail due to timing # differences, usually it's the sync. If the execute fails we # still want it to sync though. try: cur.execute("SELECT 2", prepare=True) finally: p.sync() cur.execute("SELECT 1", prepare=True) p.sync() cur.execute("SELECT 2", prepare=True) p.sync() cur.execute("CREATE TABLE doesnotexistyet (a int)") cur.execute("SELECT * FROM doesnotexistyet", prepare=True) p.sync() cur.execute("DROP TABLE doesnotexistyet") def test_prepared_disallow_name_reuse(bouncer): with bouncer.conn() as conn: result = conn.pgconn.prepare(b"test", b"SELECT 1") assert result.status == pq.ExecStatus.COMMAND_OK with bouncer.log_contains("prepared statement 'test' was already prepared"): result = conn.pgconn.prepare(b"test", b"SELECT 1") assert result.status == pq.ExecStatus.FATAL_ERROR # This reproduces a bug that was found by running the JDBC test suite. We would # only remove old data from the packet buffer when the amount of unparsed data # was less than SMALL_PACKET_SIZE bytes left in the buffer. This meant that if # we had a prepared statement larger than that it would loop ininitely. Now we # remove old data from the buffer whenever the callback reports that it needs # more data. @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_pipeline_with_half_pkt_buf_prepare(bouncer): long_string1 = "1" * (PKT_BUF_SIZE // 2) long_string2 = "2" * (PKT_BUF_SIZE // 2) with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_prepare(b"p1", f"SELECT 'a{long_string1}'".encode()) conn.pgconn.send_prepare(b"p2", f"SELECT 'b{long_string2}'".encode()) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() # This reproduces a bug that was found by running the JDBC test suite. The # problem was that when we could not fit the entire prepare massage in pkt_buf # anymore, but there was some data in the buffer that was not yet sent then # PgBouncer would loop infinitely. Now we flush already parsed messages from # the buffer, when the parsing of the next packet informs that it needs more # data to do so. @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_pipeline_flushes_on_full_pkt_buf(bouncer): query = b"SELECT 1" # We want to construct a Parse packet that is exactly the size of pkt_buf, # so we don't trigger the logic to use the callback buffering logic, but do # need the whole sbuf buffer to be available. So let's calculate the exact # length of the statement name that we need to make this happen. size_type = 1 # 'P' size_length = 4 # int32 size_query = len(query) + 1 # +1 for the null terminator size_param_count = 2 # int16 size_non_statement_name = size_type + size_length + size_query + size_param_count # So now we construct a statement name that makes all this add up pkt_buf # (-1 for null terminator of the statement name) statement_name = b"p" * (PKT_BUF_SIZE - size_non_statement_name - 1) with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() # First send a tiny packet to use some space in the sbuf, Flush is used # arbitrarily since it only takes 5 bytes conn.pgconn.send_flush_request() conn.pgconn.send_prepare(statement_name, query) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() # This resolves a bug where we would incorrectly release a server connection # even though there were still requests in flight. This was causing a weird # errors in Npgsql, because halfway through the second transaction its # connection could be changed, thus removing any state such as portals. # The following test reproduces a minimal version of this bug. # See #714 for the initial report @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_pause_before_last_sync(bouncer): bouncer.admin(f"set pool_mode=transaction") with bouncer.conn() as conn1, bouncer.cur() as cur2: conn1.pgconn.enter_pipeline_mode() conn1.pgconn.send_prepare(b"", b"SELECT $1::text") conn1.pgconn.send_query_prepared(b"", [b"a"]) # This sync triggers a ready for query, which would release the # connection (before the fix). conn1.pgconn.pipeline_sync() # But not before the next commands were forwarded to the server # After the fix these commands cause the server to stay linked to the # client. conn1.pgconn.send_prepare(b"", b"SELECT $1::text") conn1.pgconn.flush() with cur2.connection.transaction(): # Sleep a little bit to ensure the server would be released # (without the fix). time.sleep(2) # Then cur2 would claim the server connection that still had # commands from conn1 on it. cur2.execute("SELECT 1") # The execution of the prepared statement command would then open a # new connection without the expected prepared query on it conn1.pgconn.send_query_prepared(b"", [b"b"]) conn1.pgconn.pipeline_sync() assert conn1.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn1.pgconn.get_result() is None assert conn1.pgconn.get_result().status == pq.ExecStatus.TUPLES_OK assert conn1.pgconn.get_result() is None assert conn1.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn1.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn1.pgconn.get_result() is None assert conn1.pgconn.get_result().status == pq.ExecStatus.TUPLES_OK assert conn1.pgconn.get_result() is None assert conn1.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC @pytest.mark.skipif("not LINUX", reason="add_latency only supports Linux") @pytest.mark.skipif("not USE_SUDO") @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_prepared_statement_pipeline_latency(bouncer, pg): with bouncer.conn() as conn1: with conn1.pipeline() as p1: with pg.add_latency(): start = time.time() num_queries = 7 curs = [conn1.cursor() for _ in range(num_queries)] for i in range(num_queries): curs[i].execute(f"SELECT '{i}'", prepare=True) p1.sync() # Each query takes at least 1 second due to the latency # introduced by the add_latency contextmanager. But because of # pipelining the latency the whole series of queries should be # a lot less. end = time.time() duration = end - start assert duration < num_queries # The results should be correct too for i in range(num_queries): assert curs[i].fetchone()[0] == str(i) def test_prepared_statement_counters(bouncer): bouncer.default_db = "p0" bouncer.admin(f"set pool_mode=transaction") # Explicitly disable prepared statement support bouncer.admin(f"set max_prepared_statements=0") with bouncer.cur() as cur: with cur.connection.transaction(): cur.execute("SELECT 1") cur.execute("SELECT 1") cur.execute("SELECT 1") with cur.connection.transaction(): cur.execute("SELECT 1") cur.execute("SELECT 1") cur.execute("SELECT 1") stats = bouncer.admin("SHOW STATS", row_factory=dict_row) p0_stats = next(s for s in stats if s["database"] == "p0") # All the prepared statement counters should be 0, as they are not applicable assert p0_stats["total_client_parse_count"] == 0 assert p0_stats["total_server_parse_count"] == 0 assert p0_stats["total_bind_count"] == 0 assert p0_stats["avg_client_parse_count"] == 0 assert p0_stats["avg_server_parse_count"] == 0 assert p0_stats["avg_bind_count"] == 0 # Explicitly enable prepared statement support bouncer.admin(f"set max_prepared_statements=10") prepared_query = "SELECT 1" with bouncer.cur() as cur1: with bouncer.cur() as cur2: # prepare query on server 1 and client 1 cur1.execute(prepared_query, prepare=True) # Run the prepared query twice on same server and client cur1.execute(prepared_query) cur1.execute(prepared_query) with cur2.connection.transaction(): # Claim server 1 with client 2 cur2.execute("SELECT 2") # Client 1 now runs the prepared query, and it's automatically # prepared on server 2 cur1.execute(prepared_query) # Client 2 now prepares the same query that was already # prepared on server 1. And PgBouncer reuses that already # prepared query for this different client. cur2.execute(prepared_query, prepare=True) stats = bouncer.admin("SHOW STATS", row_factory=dict_row) p0_stats = next(s for s in stats if s["database"] == "p0") # 2 prepare=True executions assert p0_stats["total_client_parse_count"] == 2 # server 1 and server 2 have to prepare the query assert p0_stats["total_server_parse_count"] == 2 # 2 executions with prepare=True + 3 re-use executions assert p0_stats["total_bind_count"] == 5 pgbouncer-1.24.1/test/conftest.py0000644000175000000000000001600614777762222013670 00000000000000import os import shutil import filelock import pytest from .utils import ( LINUX, LONG_PASSWORD, PG_SUPPORTS_SCRAM, TEST_DIR, TLS_SUPPORT, USE_SUDO, Bouncer, Postgres, run, sudo, ) def add_qdisc(): if not LINUX or not USE_SUDO: return # Add the all zeros priomap to prio so all regular traffic flows # through a single band. By default prio assigns traffic to different # band according to the DSCP value of the packet. This means that some # traffic that doesn't match your filter might end up in the same class # as the delayed traffic. # Source: https://stackoverflow.com/a/40203517/2570866 sudo( "tc qdisc add dev lo root handle 1: prio bands 2 priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" ) # Add one band with additional latency sudo("tc qdisc add dev lo parent 1:2 handle 20: netem delay 1000ms") def delete_qdisc(): if not LINUX or not USE_SUDO: return sudo("tc qdisc del dev lo parent 1:2 handle 20:") sudo("tc qdisc del dev lo root") def create_certs(cert_dir): run( "sh create_certs.sh", cwd=TEST_DIR / "ssl", silent=True, ) if not TLS_SUPPORT: return cert_dir.mkdir() shutil.move(TEST_DIR / "ssl" / "TestCA1", cert_dir / "TestCA1") shutil.move(TEST_DIR / "ssl" / "TestCA2", cert_dir / "TestCA2") @pytest.fixture(autouse=True, scope="session", name="cert_dir") def shared_setup(tmp_path_factory, worker_id): """Does some setup that's shared between workers This setup should only be done once and should only be cleaned up once, at the end of the last finished worker process. It currently sets up 2 things: 1. A cert directory, for TLS tests 2. A queueing disciplines (qdisc), for tests that require latency It yields the certificate directory, which is why the fixture name is cert_dir. """ if worker_id == "master": # not executing in with multiple workers, just do the setup without any # file locking. cert_dir = tmp_path_factory.getbasetemp() / "certs" add_qdisc() create_certs(cert_dir) yield cert_dir delete_qdisc() return total_workers = int(os.environ.get("PYTEST_XDIST_WORKER_COUNT", "")) # get the temp directory shared by all workers root_tmp_dir = tmp_path_factory.getbasetemp().parent cert_dir = root_tmp_dir / "certs" lock_name = root_tmp_dir / "worker.lock" finished_count_file = root_tmp_dir / "finished_workers" with filelock.FileLock(lock_name): if not cert_dir.is_dir(): finished_count_file.write_text("0") add_qdisc() create_certs(cert_dir) try: yield cert_dir finally: with filelock.FileLock(lock_name): finished_count = int(finished_count_file.read_text()) + 1 if finished_count == total_workers: delete_qdisc() else: finished_count_file.write_text(str(finished_count)) @pytest.fixture(autouse=True, scope="session") def pg(tmp_path_factory, cert_dir): """Starts a new Postgres db that is shared for tests in this process""" pg = Postgres(tmp_path_factory.getbasetemp() / "pgdata") pg.initdb() os.truncate(pg.hba_path, 0) if TLS_SUPPORT: with pg.conf_path.open("a") as f: cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" f.write(f"ssl_cert_file='{cert}'\n") f.write(f"ssl_key_file='{key}'\n") pg.nossl_access("replication", "trust", user="postgres") pg.nossl_access("all", "trust") pg.nossl_access("p4", "password") pg.nossl_access("p5", "md5") if PG_SUPPORTS_SCRAM: pg.nossl_access("p6", "scram-sha-256") pg.commit_hba() pg.start() for i in range(8): pg.sql(f"create database p{i}") pg.sql("create database unconfigured_auth_database") pg.sql("create user bouncer") pg.sql("create user pswcheck_not_in_auth_file with superuser;") pg.sql("create user pswcheck with superuser createdb password 'pgbouncer-check';") pg.sql("create user someuser with password 'anypasswd';") pg.sql("create user maxedout;") pg.sql("create user maxedout2;") pg.sql("create user maxedout3;") pg.sql("create user maxedout4;") pg.sql("create user maxedout5;") pg.sql("create user poolsize1;") pg.sql("create user respoolsize1;") pg.sql("create user test_error_message_user;") pg.sql(f"create user longpass with password '{LONG_PASSWORD}';") pg.sql("create user stats password 'stats';") pg.sql("grant all on schema public to public", dbname="p0") pg.sql("create table test_copy(i int)", dbname="p0") pg.sql("grant all on table test_copy to public", dbname="p0") if PG_SUPPORTS_SCRAM: pg.sql("set password_encryption = 'md5'; create user muser1 password 'foo';") pg.sql("set password_encryption = 'md5'; create user muser2 password 'wrong';") pg.sql("set password_encryption = 'md5'; create user puser1 password 'foo';") pg.sql("set password_encryption = 'md5'; create user puser2 password 'wrong';") pg.sql( "set password_encryption = 'scram-sha-256'; create user scramuser1 password '" "SCRAM-SHA-256$4096:D76gvGUVj9Z4DNiGoabOBg==$RukL0Xo3Ql/2F9FsD7mcQ3GATG2fD3PA71qY1JagGDs=:BhKUwyyivFm7Tq2jDJVXSVRbRDgTWyBilZKgg6DDuYU=" "'" ) pg.sql( "set password_encryption = 'scram-sha-256'; create user scramuser3 password 'baz';" ) else: pg.sql("set password_encryption = 'on'; create user muser1 password 'foo';") pg.sql("set password_encryption = 'on'; create user muser2 password 'wrong';") pg.sql("set password_encryption = 'on'; create user puser1 password 'foo';") pg.sql("set password_encryption = 'on'; create user puser2 password 'wrong';") yield pg pg.cleanup() @pytest.mark.asyncio @pytest.fixture async def bouncer(pg, tmp_path): """Starts a new PgBouncer process""" bouncer = Bouncer(pg, tmp_path / "bouncer") await bouncer.start() yield bouncer await bouncer.cleanup() @pytest.fixture(autouse=True) def pg_log(pg): """Prints the Postgres logs that were created during the test This can be useful for debugging a failure. """ with pg.log_path.open() as f: f.seek(0, os.SEEK_END) yield print("\n\nPG_LOG\n") print(f.read()) @pytest.fixture(autouse=True) def pg_reset(pg): """Resets any changes to Postgres settings from previous tests""" pg.reset_hba() os.truncate(pg.pgdata / "postgresql.auto.conf", 0) # If a previous test restarted postgres, it was probably because of some # config that could only be changed across restarts. To reset those, we'll # have to restart it again. In other cases a reload should be enough to # reset the configuration. if pg.restarted: pg.restart() pg.restarted = False else: pg.reload() yield pgbouncer-1.24.1/test/utils.py0000644000175000000000000011733714777762222013214 00000000000000import subprocess from contextlib import closing, contextmanager from pathlib import Path try: from contextlib import asynccontextmanager except ImportError: # Fallback for python3.6 from contextlib2 import asynccontextmanager import asyncio import os import platform import re import shlex import signal import socket import sys import time import typing from tempfile import gettempdir import filelock import psycopg import psycopg.sql from psycopg import sql TEST_DIR = Path(os.path.dirname(os.path.realpath(__file__))) os.chdir(TEST_DIR) PGDATA = TEST_DIR / "pgdata" PGHOST = "127.0.0.1" BOUNCER_LOG = TEST_DIR / "test.log" BOUNCER_INI = TEST_DIR / "test.ini" BOUNCER_AUTH = TEST_DIR / "userlist.txt" BOUNCER_PID = TEST_DIR / "test.pid" BOUNCER_PORT = 6667 BOUNCER_EXE = TEST_DIR / "../pgbouncer" NEW_CA_SCRIPT = TEST_DIR / "ssl" / "newca.sh" NEW_SITE_SCRIPT = TEST_DIR / "ssl" / "newsite.sh" ENABLE_VALGRIND = bool(os.environ.get("ENABLE_VALGRIND")) HAVE_IPV6_LOCALHOST = bool(os.environ.get("HAVE_IPV6_LOCALHOST")) USE_SUDO = bool(os.environ.get("USE_SUDO")) # The tests require that psql can connect to the PgBouncer admin # console. On platforms that have getpeereid(), this works by # connecting as user pgbouncer over the Unix socket. On other # platforms, we have to rely on "trust" authentication, but then we # have to skip any tests that use authentication methods other than # "trust". if os.name == "nt": USE_UNIX_SOCKETS = False HAVE_GETPEEREID = False # psycopg only supports WindowsSelectorEventLoopPolicy from asyncio import WindowsSelectorEventLoopPolicy asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy()) WINDOWS = True else: USE_UNIX_SOCKETS = True HAVE_GETPEEREID = True WINDOWS = False LINUX = False MACOS = False FREEBSD = False OPENBSD = False if platform.system() == "Linux": LINUX = True elif platform.system() == "Darwin": MACOS = True elif platform.system() == "FreeBSD": FREEBSD = True elif platform.system() == "OpenBSD": OPENBSD = True BSD = MACOS or FREEBSD or OPENBSD def eprint(*args, **kwargs): """eprint prints to stderr""" print(*args, file=sys.stderr, **kwargs) def run(command, *args, check=True, shell=None, silent=False, **kwargs): """run runs the given command and prints it to stderr""" if shell is None: shell = isinstance(command, str) if not shell: command = list(map(str, command)) if not silent: if shell: eprint(f"+ {command}") else: # We could normally use shlex.join here, but it's not available in # Python 3.6 which we still like to support unsafe_string_cmd = " ".join(map(shlex.quote, command)) eprint(f"+ {unsafe_string_cmd}") if silent: kwargs.setdefault("stdout", subprocess.DEVNULL) return subprocess.run(command, *args, check=check, shell=shell, **kwargs) def sudo(command, *args, shell=None, **kwargs): """ A version of run that prefixes the command with sudo when the process is not already run as root """ effective_user_id = os.geteuid() if effective_user_id == 0: return run(command, *args, shell=shell, **kwargs) if shell is None: shell = isinstance(command, str) if shell: return run(f"sudo {command}", *args, shell=shell, **kwargs) else: return run(["sudo", *command], *args, shell=shell, **kwargs) def capture(command, *args, stdout=subprocess.PIPE, encoding="utf-8", **kwargs): return run(command, *args, stdout=stdout, encoding=encoding, **kwargs).stdout def get_pg_major_version(): full_version_string = capture("initdb --version", silent=True) major_version_string = re.search("[0-9]+", full_version_string) assert major_version_string is not None return int(major_version_string.group(0)) PG_MAJOR_VERSION = get_pg_major_version() def get_max_password_length(): with open("../include/bouncer.h", encoding="utf-8") as f: match = re.search(r"#define MAX_PASSWORD\s+([0-9].*)", f.read()) assert match is not None max_password_length = int(match.group(1)) assert max_password_length >= 996 if max_password_length > 996 and PG_MAJOR_VERSION < 14: return 996 return max_password_length PKT_BUF_SIZE = 4096 MAX_PASSWORD_LENGTH = get_max_password_length() LONG_PASSWORD = "a" * (MAX_PASSWORD_LENGTH - 1) PG_SUPPORTS_SCRAM = PG_MAJOR_VERSION >= 10 # psycopg.Pipeline.is_supported() does not work on rocky:8 in CI, so we create # our own check here that works on all our supported systems LIBPQ_SUPPORTS_PIPELINING = psycopg.pq.version() >= 140000 def get_tls_support(): with open("../config.mak", encoding="utf-8") as f: match = re.search(r"tls_support = (\w+)", f.read()) assert match is not None return match.group(1) == "yes" TLS_SUPPORT = get_tls_support() # this is out of ephemeral port range for many systems hence # it is a lower change that it will conflict with "in-use" ports PORT_LOWER_BOUND = 10200 # ephemeral port start on many Linux systems PORT_UPPER_BOUND = 32768 next_port = PORT_LOWER_BOUND def cleanup_test_leftovers(*nodes): """ Cleaning up test leftovers needs to be done in a specific order, because some of these leftovers depend on others having been removed. They might even depend on leftovers on other nodes being removed. So this takes a list of nodes, so that we can clean up all test leftovers globally in the correct order. """ for node in nodes: node.cleanup_subscriptions() for node in nodes: node.cleanup_publications() for node in nodes: node.cleanup_replication_slots() for node in nodes: node.cleanup_schemas() for node in nodes: node.cleanup_users() class PortLock: def __init__(self): global next_port while True: next_port += 1 if next_port >= PORT_UPPER_BOUND: next_port = PORT_LOWER_BOUND self.lock = filelock.FileLock(Path(gettempdir()) / f"port-{next_port}.lock") try: self.lock.acquire(timeout=0) except filelock.Timeout: continue with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: try: s.bind(("127.0.0.1", next_port)) self.port = next_port break except Exception: continue def release(self): self.lock.release() def notice_handler(diag: psycopg.errors.Diagnostic): print(f"{diag.severity}: {diag.message_primary}") if diag.message_detail: print(f"DETAIL: {diag.message_detail}") if diag.message_hint: print(f"HINT: {diag.message_hint}") if diag.context: print(f"CONTEXT: {diag.context}") class QueryRunner: def __init__(self, host, port): self.host = host self.port = port self.default_db = "postgres" self.default_user = "postgres" # Used to track objects that we want to clean up at the end of a test self.subscriptions = set() self.publications = set() self.replication_slots = set() self.schemas = set() self.users = set() def set_default_connection_options(self, options): """Sets the default connection options on the given options dictionary""" options.setdefault("dbname", self.default_db) options.setdefault("user", self.default_user) options.setdefault("host", self.host) options.setdefault("port", self.port) if ENABLE_VALGRIND: # If valgrind is enabled PgBouncer is a significantly slower to # respond to connection requests, so we wait a little longer. options.setdefault("connect_timeout", 20) else: options.setdefault("connect_timeout", 3) # Always required for Ubuntu 18.04, but also needed for any tests # involving the varcache_change database. The difference between the # client_encoding specified in the config and client_encoding by the # client will force a varcache change when a connection is given. options.setdefault("client_encoding", "UTF8") return options def make_conninfo(self, **kwargs) -> str: self.set_default_connection_options(kwargs) return psycopg.conninfo.make_conninfo(**kwargs) def conn(self, *, autocommit=True, **kwargs): """Open a psycopg connection to this server""" self.set_default_connection_options(kwargs) conn = psycopg.connect( autocommit=autocommit, **kwargs, ) conn.add_notice_handler(notice_handler) return conn def aconn(self, *, autocommit=True, **kwargs): """Open an asynchronous psycopg connection to this server""" self.set_default_connection_options(kwargs) return psycopg.AsyncConnection.connect( autocommit=autocommit, **kwargs, ) @contextmanager def cur(self, autocommit=True, **kwargs): """Open an psycopg cursor to this server The connection and the cursors automatically close once you leave the "with" block """ with self.conn( autocommit=autocommit, **kwargs, ) as conn: with conn.cursor() as cur: yield cur @asynccontextmanager async def acur(self, **kwargs): """Open an asynchronous psycopg cursor to this server The connection and the cursors automatically close once you leave the "async with" block """ async with await self.aconn(**kwargs) as conn: async with conn.cursor() as cur: yield cur def sql(self, query, params=None, **kwargs): """Run an SQL query This opens a new connection and closes it once the query is done """ with self.cur(**kwargs) as cur: cur.execute(query, params=params) try: return cur.fetchall() except psycopg.ProgrammingError as e: if "the last operation didn't produce a result" == str(e): return None raise def sql_value(self, query, params=None, **kwargs): """Run an SQL query that returns a single cell and return this value This opens a new connection and closes it once the query is done """ with self.cur(**kwargs) as cur: cur.execute(query, params=params) result = cur.fetchall() assert len(result) == 1 assert len(result[0]) == 1 value = result[0][0] return value def asql(self, query, **kwargs): """Run an SQL query in asynchronous task This opens a new connection and closes it once the query is done """ return asyncio.ensure_future(self.asql_coroutine(query, **kwargs)) async def asql_coroutine( self, query, params=None, **kwargs ) -> typing.Optional[typing.List[typing.Any]]: async with self.acur(**kwargs) as cur: await cur.execute(query, params=params) try: return await cur.fetchall() except psycopg.ProgrammingError as e: if "the last operation didn't produce a result" == str(e): return None raise def psql(self, query, **kwargs): """Run an SQL query using psql instead of psycopg This opens a new connection and closes it once the query is done """ self.set_default_connection_options(kwargs) connect_options = " ".join([f"{k}={v}" for k, v in kwargs.items()]) run(["psql", f"port={self.port} {connect_options}", "-c", query], shell=False) @contextmanager def transaction(self, **kwargs): with self.cur(**kwargs) as cur: with cur.connection.transaction(): yield cur def sleep(self, duration=3, **kwargs): """Run pg_sleep""" return self.sql(f"select pg_sleep({duration})", **kwargs) def asleep(self, duration=3, times=1, sequentially=False, **kwargs): """Run pg_sleep asynchronously in a task. times: You can create a single task that opens multiple connections, which run pg_sleep concurrently. The asynchronous task will only complete once all these pg_sleep calls are finished. sequentially: Instead of running all pg_sleep calls spawned by providing times > 1 concurrently, this will run them sequentially. """ return asyncio.ensure_future( self.asleep_coroutine( duration=duration, times=times, sequentially=sequentially, **kwargs ) ) async def asleep_coroutine(self, duration=3, times=1, sequentially=False, **kwargs): """This is the coroutine that the asleep task runs internally""" if not sequentially: await asyncio.gather( *[ self.asql(f"select pg_sleep({duration})", **kwargs) for _ in range(times) ] ) else: for _ in range(times): await self.asql(f"select pg_sleep({duration})", **kwargs) def test(self, **kwargs): """Test if you can connect""" return self.sql("select 1", **kwargs) def atest(self, **kwargs): """Test if you can connect asynchronously""" return self.asql("select 1", **kwargs) def psql_test(self, **kwargs): """Test if you can connect with psql instead of psycopg""" return self.psql("select 1", **kwargs) @contextmanager def enable_firewall(self): """Enables the firewall for the platform that you are running Normally this should not be called directly, and instead drop_traffic or reject_traffic should be used. """ fw_token = None if BSD: if MACOS: command_stderr = sudo( f"pfctl -E", stderr=subprocess.PIPE, text=True ).stderr match = re.search(r"^Token : (\d+)", command_stderr, flags=re.MULTILINE) assert match is not None fw_token = match.group(1) sudo( 'bash -c "' f"echo 'anchor \\\"port_{self.port}\\\"'" f' | pfctl -a pgbouncer_test -f -"' ) try: yield finally: if MACOS: sudo(f"pfctl -X {fw_token}") @contextmanager def drop_traffic(self): """Drops all TCP packets to this query runner""" with self.enable_firewall(): if LINUX: sudo( "iptables --append OUTPUT " "--protocol tcp " f"--destination {self.host} " f"--destination-port {self.port} " "--jump DROP " ) elif BSD: sudo( "bash -c '" f'echo "block drop out proto tcp from any to {self.host} port {self.port}"' f"| pfctl -a pgbouncer_test/port_{self.port} -f -'" ) else: raise Exception("This OS cannot run this test") try: yield finally: if LINUX: sudo( "iptables --delete OUTPUT " "--protocol tcp " f"--destination {self.host} " f"--destination-port {self.port} " "--jump DROP " ) elif BSD: sudo(f"pfctl -a pgbouncer_test/port_{self.port} -F all") @contextmanager def reject_traffic(self): """Rejects all traffic to this query runner with a TCP RST message""" with self.enable_firewall(): if LINUX: sudo( "iptables --append OUTPUT " "--protocol tcp " f"--destination {self.host} " f"--destination-port {self.port} " "--jump REJECT " "--reject-with tcp-reset" ) elif BSD: sudo( "bash -c '" f'echo "block return-rst out out proto tcp from any to {self.host} port {self.port}"' f"| pfctl -a pgbouncer_test/port_{self.port} -f -'" ) else: raise Exception("This OS cannot run this test") try: yield finally: if LINUX: sudo( "iptables --delete OUTPUT " "--protocol tcp " f"--destination {self.host} " f"--destination-port {self.port} " "--jump REJECT " "--reject-with tcp-reset" ) elif BSD: sudo(f"pfctl -a pgbouncer_test/port_{self.port} -F all") @contextmanager def add_latency(self): """Adds one second of latency to all packets to this query runner""" if not LINUX: raise Exception("This OS cannot run this test") sudo( f"tc filter add dev lo parent 1:0 protocol ip prio {self.port} u32 match ip dport {self.port} 0xffff flowid 1:2" ) try: yield finally: sudo(f"tc filter del dev lo parent 1: prio {self.port}") pass def create_user(self, name, args: typing.Optional[psycopg.sql.Composable] = None): self.users.add(name) if args is None: args = sql.SQL("") self.sql(sql.SQL("CREATE USER {} {}").format(sql.Identifier(name), args)) def create_schema(self, name, dbname=None): dbname = dbname or self.default_db self.schemas.add((dbname, name)) self.sql(sql.SQL("CREATE SCHEMA {}").format(sql.Identifier(name))) def create_publication(self, name: str, args: psycopg.sql.Composable, dbname=None): dbname = dbname or self.default_db self.publications.add((dbname, name)) self.sql(sql.SQL("CREATE PUBLICATION {} {}").format(sql.Identifier(name), args)) def create_logical_replication_slot(self, name, plugin): self.replication_slots.add(name) self.sql( "SELECT pg_catalog.pg_create_logical_replication_slot(%s,%s)", (name, plugin), ) def create_physical_replication_slot(self, name): self.replication_slots.add(name) self.sql( "SELECT pg_catalog.pg_create_physical_replication_slot(%s)", (name,), ) def create_subscription(self, name: str, args: psycopg.sql.Composable, dbname=None): dbname = dbname or self.default_db self.subscriptions.add((dbname, name)) self.sql( sql.SQL("CREATE SUBSCRIPTION {} {}").format(sql.Identifier(name), args) ) def cleanup_users(self): for user in self.users: self.sql(sql.SQL("DROP USER IF EXISTS {}").format(sql.Identifier(user))) def cleanup_schemas(self): for dbname, schema in self.schemas: self.sql( sql.SQL("DROP SCHEMA IF EXISTS {} CASCADE").format( sql.Identifier(schema) ), dbname=dbname, ) def cleanup_publications(self): for dbname, publication in self.publications: self.sql( sql.SQL("DROP PUBLICATION IF EXISTS {}").format( sql.Identifier(publication) ), dbname=dbname, ) def cleanup_replication_slots(self): for slot in self.replication_slots: start = time.time() while True: try: self.sql( "SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = %s", (slot,), ) except psycopg.errors.ObjectInUse: if time.time() < start + 10: time.sleep(0.5) continue raise break def cleanup_subscriptions(self): for dbname, subscription in self.subscriptions: try: self.sql( sql.SQL("ALTER SUBSCRIPTION {} DISABLE").format( sql.Identifier(subscription) ), dbname=dbname, ) except psycopg.errors.UndefinedObject: # Subscription didn't exist already continue self.sql( sql.SQL("ALTER SUBSCRIPTION {} SET (slot_name = NONE)").format( sql.Identifier(subscription) ), dbname=dbname, ) self.sql( sql.SQL("DROP SUBSCRIPTION {}").format(sql.Identifier(subscription)), dbname=dbname, ) def debug(self): print("Connect manually to:\n ", repr(self.make_conninfo())) print("Press Enter to continue running the test...") input() def psql_debug(self, **kwargs): conninfo = self.make_conninfo(**kwargs) run( ["psql", conninfo], silent=True, ) class Postgres(QueryRunner): def __init__(self, pgdata): self.port_lock = PortLock() super().__init__("127.0.0.1", self.port_lock.port) self.pgdata = pgdata self.log_path = self.pgdata / "pg.log" self.connections = {} self.cursors = {} self.restarted = False def initdb(self): run( f"initdb -A trust --nosync --username postgres --pgdata {self.pgdata}", stdout=subprocess.DEVNULL, ) with self.conf_path.open(mode="a") as pgconf: if USE_UNIX_SOCKETS: pgconf.write("unix_socket_directories = '/tmp'\n") pgconf.write("log_connections = on\n") pgconf.write("log_disconnections = on\n") pgconf.write("logging_collector = off\n") # Allow CREATE SUBSCRIPTION to work pgconf.write("wal_level = 'logical'\n") # Faster logical replication status update so tests with logical replication # run faster pgconf.write("wal_receiver_status_interval = 1\n") # Faster logical replication apply worker launch so tests with logical # replication run faster. This is used in ApplyLauncherMain in # src/backend/replication/logical/launcher.c. pgconf.write("wal_retrieve_retry_interval = '250ms'\n") # Make sure there's enough logical replication resources for our # tests if PG_MAJOR_VERSION >= 10: pgconf.write("max_logical_replication_workers = 5\n") pgconf.write("max_wal_senders = 5\n") pgconf.write("max_replication_slots = 10\n") pgconf.write("max_worker_processes = 20\n") # We need to make the log go to stderr so that the tests can # check what is being logged. This should be the default, but # some packagings change the default configuration. pgconf.write("log_destination = stderr\n") # This makes tests run faster and we don't care about crash safety # of our test data. pgconf.write("fsync = false\n") # Use a consistent value across postgres versions, so test results # are the same. pgconf.write("extra_float_digits = 1\n") # Make sure this is consistent across platforms pgconf.write("datestyle = 'iso, mdy'\n") # Make PostgreSQL listen on both IPv4 and IPv6 (if supported) if HAVE_IPV6_LOCALHOST: pgconf.write("listen_addresses='127.0.0.1,::1'\n") def pgctl(self, command, **kwargs): run(f"pg_ctl -w --pgdata {self.pgdata} {command}", **kwargs) def apgctl(self, command, **kwargs): return asyncio.create_subprocess_shell( f"pg_ctl -w --pgdata {self.pgdata} {command}", **kwargs ) def start(self): try: self.pgctl(f'-o "-p {self.port}" -l {self.log_path} start') except Exception: print("\n\nPG_LOG\n") with self.log_path.open() as f: print(f.read()) raise def stop(self): self.pgctl("-m fast stop", check=False) def cleanup(self): self.stop() self.port_lock.release() def restart(self): self.restarted = True self.stop() self.start() def reload(self): if WINDOWS: # SIGHUP and thus reload don't exist on Windows self.restart() else: self.pgctl("reload") time.sleep(1) async def arestart(self): process = await self.apgctl("-m fast restart") await process.communicate() def nossl_access(self, dbname, auth_type, user="all"): """Prepends a local non-SSL access to the HBA file""" with self.hba_path.open() as pghba: old_contents = pghba.read() with self.hba_path.open(mode="w") as pghba: if USE_UNIX_SOCKETS: pghba.write(f"local {dbname} {user} {auth_type}\n") pghba.write(f"hostnossl {dbname} {user} 127.0.0.1/32 {auth_type}\n") pghba.write(f"hostnossl {dbname} {user} ::1/128 {auth_type}\n") pghba.write(old_contents) def ssl_access(self, dbname, auth_type, user="all"): """Prepends a local SSL access rule to the HBA file""" with self.hba_path.open() as pghba: old_contents = pghba.read() with self.hba_path.open(mode="w") as pghba: pghba.write(f"hostssl {dbname} {user} 127.0.0.1/32 {auth_type}\n") pghba.write(f"hostssl {dbname} {user} ::1/128 {auth_type}\n") pghba.write(old_contents) @property def hba_path(self): return self.pgdata / "pg_hba.conf" @property def conf_path(self): return self.pgdata / "postgresql.conf" def commit_hba(self): """Mark the current HBA contents as non-resettable by reset_hba""" with self.hba_path.open() as pghba: old_contents = pghba.read() with self.hba_path.open(mode="w") as pghba: pghba.write("# committed-rules\n") pghba.write(old_contents) def reset_hba(self): """Remove any HBA rules that were added after the last call to commit_hba""" with self.hba_path.open() as f: hba_contents = f.read() committed = hba_contents[hba_contents.find("# committed-rules\n") :] with self.hba_path.open("w") as f: f.write(committed) def connection_count(self, dbname=None, users=("bouncer",)): """Returns the number of connections that are active You can pass values for dbname and users to only count connections for a certain database and/or user(s). """ dbname_filter = "" if dbname: dbname_filter = f" and datname='{dbname}'" return self.sql_value( f"select count(1) from pg_stat_activity where usename = ANY(%s) {dbname_filter}", params=(list(users),), ) async def delayed_start(self, delay=1): """Start Postgres after a delay NOTE: The sleep is asynchronous, but while waiting for Postgres to start the pg_ctl start command will block the event loop. This is currently acceptable for our usage of this method in the existing tests and this way it was easiest to implement. However, it seems totally reasonable to change this behaviour in the future if necessary. """ await asyncio.sleep(delay) self.start() def configure(self, config): """Configure specific Postgres settings using ALTER SYSTEM SET NOTE: after configuring a call to reload or restart is needed for the settings to become effective. """ self.sql(f"alter system set {config}") @contextmanager def log_contains(self, re_string, times=None): """Checks if during this with block the log matches re_string re_string: The regex to search for. times: If None, any number of matches is accepted. If a number, only that specific number of matches is accepted. """ with self.log_path.open() as f: f.seek(0, os.SEEK_END) yield content = f.read() if times is None: assert re.search(re_string, content) else: match_count = len(re.findall(re_string, content)) assert match_count == times class Bouncer(QueryRunner): def __init__( self, pg: Postgres, config_dir: Path, base_ini_path=BOUNCER_INI, base_auth_path=BOUNCER_AUTH, port=None, ): if port: self.port_lock = None super().__init__("127.0.0.1", port) else: self.port_lock = PortLock() super().__init__("127.0.0.1", self.port_lock.port) self.process: typing.Optional[subprocess.Popen] = None self.aprocess: typing.Optional[asyncio.subprocess.Process] = None config_dir.mkdir() self.config_dir = config_dir self.ini_path = self.config_dir / "test.ini" self.log_path = self.config_dir / "test.log" self.auth_path = self.config_dir / "userlist.txt" self.default_db = "p0" self.pg = pg if USE_UNIX_SOCKETS: if LINUX: # On Linux we do so_reuseport tests with multiple pgbouncer # processes listening on the same port. This requires that each # of the pgbouncer processes should have a unique # unix_socket_dir. We use the known-unique config_dir for this. # You would expect we could do the same for other platforms # too. But UNIX sockets cannot have paths longer than 103 # characters on and the config_dir chosen by pytest on MacOS # exceeds this limit. So we use /tmp everywhere except for # Linux. self.admin_host = str(self.config_dir) else: self.admin_host = "/tmp" else: self.admin_host = "127.0.0.1" self.admin_runner = QueryRunner(self.admin_host, self.port) self.admin_runner.default_db = "pgbouncer" self.admin_runner.default_user = "pgbouncer" with open(base_auth_path) as base_auth: with self.auth_path.open("w") as auth: auth.write(base_auth.read()) auth.write(f'"longpass" "{LONG_PASSWORD}"\n') auth.flush() with open(base_ini_path) as base_ini: with self.ini_path.open("w") as ini: ini.write(base_ini.read().replace("port=6666", f"port={pg.port}")) ini.write("\n") ini.write(f"logfile = {self.log_path}\n") ini.write(f"auth_file = {self.auth_path}\n") ini.write("pidfile = \n") # Uncomment for much more noise but, more detailed debugging # ini.write("verbose = 3\n") if not USE_UNIX_SOCKETS: ini.write(f"unix_socket_dir = \n") ini.write(f"admin_users = pgbouncer\n") else: ini.write(f"unix_socket_dir = {self.admin_host}\n") ini.write(f"listen_port = {self.port}\n") ini.flush() def base_command(self): """returns the basecommand that is used to run PgBouncer This includes valgrind and all its arguments when ENABLE_VALGRIND is set """ if ENABLE_VALGRIND: valgrind_log_file = self.config_dir / "valgrind.%p.log" return [ "valgrind", "--quiet", "--leak-check=full", "--show-reachable=no", "--track-origins=yes", "--error-markers=VALGRIND-ERROR-BEGIN,VALGRIND-ERROR-END", f"--log-file={valgrind_log_file}", str(BOUNCER_EXE), ] return [str(BOUNCER_EXE)] async def start(self): # Due to using WindowsSelectorEventLoopPolicy for support with psycopg # we cannot use asyncio subprocesses. Since this eventloop does not # support it. We fall back to regular subprocesses. if WINDOWS: self.process = subprocess.Popen( [*self.base_command(), "--quiet", self.ini_path], close_fds=True ) else: self.aprocess = await asyncio.create_subprocess_exec( *self.base_command(), "--quiet", str(self.ini_path), close_fds=True ) await self.wait_until_running() async def wait_until_running(self): tries = 1 while True: try: await self.aadmin("show version") except psycopg.Error: if tries > 50: self.print_logs() raise tries += 1 time.sleep(0.1) continue break def admin(self, query, **kwargs): """Run an SQL query on the PgBouncer admin database""" return self.admin_runner.sql(query, **kwargs) def admin_value(self, query, **kwargs): """Run an SQL query on the PgBouncer admin database that returns only a single cell and return this value""" return self.admin_runner.sql_value(query, **kwargs) def aadmin(self, query, **kwargs): """Run an SQL query on the PgBouncer admin database in an asynchronous task""" return self.admin_runner.asql(query, **kwargs) def running(self): if self.process: return self.process.poll() is None if self.aprocess: return self.aprocess.returncode is None return False async def wait_for_exit(self): if self.process is not None: self.process.communicate() self.process.wait() if self.aprocess is not None: await self.aprocess.communicate() await self.aprocess.wait() self.process = None self.aprocess = None async def stop(self): if not WINDOWS: self.sigquit() else: # Windows does not have SIGQUIT, so call terminate() twice to # trigger fast exit if self.process is not None: self.process.terminate() self.process.terminate() if self.aprocess is not None: self.aprocess.terminate() self.aprocess.terminate() await self.wait_for_exit() async def reboot(self): """Starts a new PgBouncer with the --reboot flag This new PgBouncer process will replace the current process and take over its non-SSL sockets. """ assert self.aprocess is not None or self.process is not None if self.aprocess: old_process = self.aprocess old_pid = old_process.pid self.aprocess = await asyncio.create_subprocess_exec( *self.base_command(), "--reboot", "--quiet", str(self.ini_path), close_fds=True, ) await old_process.communicate() await old_process.wait() await self.wait_until_running() assert self.aprocess.pid != old_pid if self.process: old_process = self.process old_pid = old_process.pid self.process = subprocess.Popen( [*self.base_command(), "--reboot", "--quiet", self.ini_path], close_fds=True, ) old_process.communicate() old_process.wait() await self.wait_until_running() assert self.process.pid != old_pid def send_signal(self, sig): if self.aprocess: self.aprocess.send_signal(sig) if self.process: self.process.send_signal(sig) def sighup(self): self.send_signal(signal.SIGHUP) time.sleep(1) def sigterm(self): self.send_signal(signal.SIGTERM) def sigint(self): self.send_signal(signal.SIGINT) def sigquit(self): self.send_signal(signal.SIGQUIT) def sigusr2(self): self.send_signal(signal.SIGUSR2) def print_logs(self): print(f"\n\nBOUNCER_LOG {self.config_dir}\n") log_contents = "" try: with self.log_path.open() as f: log_contents = f.read() print(log_contents) except Exception: pass # Most reliable way to detect Assert failures. Otherwise we might miss # Assert failures at the end of the test run. assert not re.search("FATAL.*Assert", log_contents) # None of our tests should have a query in progress on the server when # the client disconnects. If this fails it almost certainly indicates a # bug in our outstanding request tracking. assert "client disconnected with query in progress" not in log_contents if ENABLE_VALGRIND: failed_valgrind = False for valgrind_log in self.config_dir.glob("valgrind.*.log"): with valgrind_log.open() as f: contents = f.read() if "VALGRIND-ERROR" in contents: failed_valgrind = True print(f"\n\nVALGRIND LOG {valgrind_log}\n") print(contents) assert not failed_valgrind async def cleanup(self): try: cleanup_test_leftovers(self) await self.stop() finally: self.print_logs() if self.port_lock: self.port_lock.release() def write_ini(self, config): """Writes a config to the ini file of this PgBouncer It appends a newline automatically. To apply these changes PgBouncer still needs to be reloaded or restarted. To reload in a cross platform way you need can use admin("reload"). """ with self.ini_path.open("a") as f: f.write(config + "\n") @contextmanager def log_contains(self, re_string, times=None): """Checks if during this with block the log matches re_string re_string: The regex to search for. times: If None, any number of matches is accepted. If a number, only that specific number of matches is accepted. """ with self.log_path.open() as f: f.seek(0, os.SEEK_END) yield content = f.read() if times is None: assert re.search(re_string, content) else: match_count = len(re.findall(re_string, content)) assert match_count == times @contextmanager def run_with_config(self, config): """Run the pgbouncer instance with provided config and restore the previous config after execution config: A new pgbouncer config in ini format """ with self.ini_path.open("r") as f: config_old = f.read() with self.ini_path.open("w") as f: f.write(config) try: self.admin("RELOAD") yield self finally: # Code to release resource, e.g.: with self.ini_path.open("w") as f: f.write(config_old) self.admin("RELOAD") pgbouncer-1.24.1/test/test_peering.py0000644000175000000000000001157014777762222014534 00000000000000import asyncio import time from concurrent.futures import ThreadPoolExecutor from typing import Dict import psycopg import pytest from .utils import LINUX, Bouncer if not LINUX: pytest.skip(allow_module_level=True, reason="peering tests require so_reuseport") @pytest.mark.asyncio @pytest.fixture async def peers(pg, tmp_path): peers: Dict[int, Bouncer] = {} peers[1] = Bouncer(pg, tmp_path / "bouncer1") peers[2] = Bouncer(pg, tmp_path / "bouncer2", port=peers[1].port) peers[3] = Bouncer(pg, tmp_path / "bouncer3", port=peers[1].port) for own_index, bouncer in peers.items(): with bouncer.ini_path.open("a") as f: f.write("so_reuseport=1\n") f.write(f"peer_id={own_index}\n") f.write("[peers]\n") for other_index, peer in peers.items(): if own_index == other_index: continue f.write(f"{other_index} = host={peer.admin_host} port={peer.port}\n") await asyncio.gather(*[p.start() for p in peers.values()]) yield peers await asyncio.gather(*[p.cleanup() for p in peers.values()]) def test_peering_without_own_index(peers): with peers[1].cur() as cur: with ThreadPoolExecutor(max_workers=2) as pool: for _ in range(10): query = pool.submit(cur.execute, "select pg_sleep(5)") time.sleep(0.5) cancel = pool.submit(cur.connection.cancel) cancel.result() with pytest.raises( psycopg.errors.QueryCanceled, match="due to user request" ): query.result() def test_peering_with_own_index(peers): for own_index, bouncer in peers.items(): with bouncer.ini_path.open("a") as f: f.write(f"{own_index} = host={bouncer.admin_host} port={bouncer.port}\n") bouncer.admin("reload") with peers[1].cur() as cur: with ThreadPoolExecutor(max_workers=2) as pool: for _ in range(10): query = pool.submit(cur.execute, "select pg_sleep(5)") time.sleep(0.5) cancel = pool.submit(cur.connection.cancel) cancel.result() with pytest.raises( psycopg.errors.QueryCanceled, match="due to user request" ): query.result() async def test_rolling_restart_admin(peers): # Stop 2 of the 3 peers, so that we know we connect to peer 1 await peers[2].stop() await peers[3].stop() with peers[1].cur() as cur: cur.execute("select 1") # Trigger a shutdown, but the process should keep running until we # close the connection peers[1].admin("shutdown wait_for_clients") time.sleep(1) assert peers[1].running() # New connection attempts are now expected to fail, because no process # is listening on the port. Under normal usage you would continue to # leave at least one running. But for testing purposes this is the # easiest way to show that peer[1] is not accepting new connections # anymore. with pytest.raises(psycopg.OperationalError, match="Connection refused"): peers[1].test() # But the existing connection is still be allowed to execute any # queries. cur.execute("select 1") await peers[2].start() # Now that peer[2] is running again, new connections should start # working too. peers[1].test() # Now that the connection is closed, peer[1] should exit automatically. await peers[1].wait_for_exit() assert not peers[1].running() async def test_rolling_restart_sigterm(peers): # Stop 2 of the 3 peers, so that we know we connect to peer 1 await peers[2].stop() await peers[3].stop() with peers[1].cur() as cur: cur.execute("select 1") # Trigger a shutdown, but the process should keep running until we # close the connection peers[1].sigterm() time.sleep(1) assert peers[1].running() # New connection attempts are now expected to fail, because no process # is listening on the port. Under normal usage you would continue to # leave at least one running. But for testing purposes this is the # easiest way to show that peer[1] is not accepting new connections # anymore. with pytest.raises(psycopg.OperationalError, match="Connection refused"): peers[1].test() # But the existing connection is still be allowed to execute any # queries. cur.execute("select 1") await peers[2].start() # Now that peer[2] is running again, new connections should start # working too. peers[1].test() # Now that the connection is closed, peer[1] should exit automatically. await peers[1].wait_for_exit() assert not peers[1].running() pgbouncer-1.24.1/test/test_load_balance_hosts.py0000644000175000000000000000343314777762222016706 00000000000000import re import psycopg import pytest @pytest.mark.asyncio async def test_load_balance_hosts_disable_good_first(bouncer): with bouncer.log_contains(r"127.0.0.1:\d+ new connection to server", 2): await bouncer.asleep(dbname="hostlist_good_first", duration=0.5, times=2) @pytest.mark.asyncio async def test_load_balance_hosts_disable_bad_first(bouncer): bouncer.admin(f"set server_login_retry=1") with bouncer.log_contains(r"closing because: server DNS lookup failed", 1): with bouncer.log_contains(r"127.0.0.1:\d+ new connection to server", 2): # Execute two concurrent sleeps to force two backend connections. # The first connection will attempt the "bad" host and retry on # the "good" host. # The second connection will honor `load_balance_hosts` and use the # `disable` host. await bouncer.asleep(dbname="hostlist_bad_first", duration=0.5, times=2) def test_load_balance_hosts_reload(bouncer): with bouncer.admin_runner.cur() as cur: results = cur.execute("show databases").fetchall() result = [r for r in results if r[0] == "load_balance_hosts_update"][0] assert "disable" in result with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: f.write( re.sub( r"^(load_balance_hosts_update.*load_balance_hosts=)disable", "\\1round-robin", original, flags=re.MULTILINE, ) ) bouncer.admin("reload") with bouncer.admin_runner.cur() as cur: results = cur.execute("show databases").fetchall() result = [r for r in results if r[0] == "load_balance_hosts_update"][0] assert "round-robin" in result pgbouncer-1.24.1/test/test_operations.py0000644000175000000000000000654414777762222015273 00000000000000import asyncio import time import psycopg import pytest from .utils import PG_SUPPORTS_SCRAM, WINDOWS @pytest.mark.asyncio @pytest.mark.skipif("WINDOWS", reason="gets stuck for some reason during takeover") async def test_online_restart(bouncer): for _ in range(5): # max_client_conn = 10 # default_pool_size = 5 task = bouncer.asleep(2, dbname="p1", times=5) await asyncio.sleep(0.5) await bouncer.reboot() await task @pytest.mark.asyncio async def test_pause_resume(bouncer): task = bouncer.asleep(0.1, times=50, sequentially=True, connect_timeout=30) for _ in range(5): await bouncer.aadmin("pause") await asyncio.sleep(1) await bouncer.aadmin("resume") await asyncio.sleep(1) await task @pytest.mark.asyncio async def test_suspend_resume(bouncer): task = bouncer.asleep(0.1, times=50, sequentially=True) for _ in range(5): async with bouncer.admin_runner.acur() as cur: await cur.execute("suspend") await asyncio.sleep(1) await cur.execute("resume") await asyncio.sleep(1) await task def test_enable_disable(bouncer): bouncer.test() bouncer.admin("disable p0") with pytest.raises( psycopg.OperationalError, match=r'database "p0" is disabled', ): bouncer.test() bouncer.admin("enable p0") bouncer.test() @pytest.mark.asyncio async def test_database_restart(pg, bouncer): bouncer.admin("set server_login_retry=1") bouncer.test() pg.restart() bouncer.test() tasks = [] for i in range(1, 6): tasks.append(bouncer.asleep(i, dbname="p0")) tasks.append(bouncer.asleep(i, dbname="p1")) await asyncio.sleep(0.5) if WINDOWS: # WindowsSelectorEventLoopPolicy does not support async subprocesses, # so we fall back to regular suprocesses here. pg.restart() else: await pg.arestart() for task in tasks: try: await task except psycopg.OperationalError: pass bouncer.test(dbname="p0") bouncer.test(dbname="p1") def test_database_change(bouncer): bouncer.admin("set server_lifetime=2") bouncer.default_db = "p1" assert bouncer.sql_value("select current_database()") == "p1" with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: f.write(original.replace("dbname=p1", "dbname=p0")) bouncer.admin("reload") time.sleep(3) assert bouncer.sql_value("select current_database()") == "p0" def test_reconnect(bouncer): pid1 = bouncer.sql_value("select pg_backend_pid()") bouncer.admin("reconnect") time.sleep(1) pid2 = bouncer.sql_value("select pg_backend_pid()") assert pid1 != pid2 @pytest.mark.asyncio @pytest.mark.skipif("not PG_SUPPORTS_SCRAM") @pytest.mark.skipif("WINDOWS", reason="gets stuck for some reason during takeover") async def test_scram_takeover(bouncer): bouncer.admin("set pool_mode=transaction") bouncer.admin("set server_lifetime=3") bouncer.admin("set auth_type='scram-sha-256'") async with bouncer.acur(dbname="p62", user="scramuser1", password="foo") as cur: await cur.execute("select 1") await asyncio.sleep(4) # wait for server_lifetime await bouncer.reboot() await cur.execute("select 1") pgbouncer-1.24.1/test/test_no_database.py0000644000175000000000000001140114777762222015334 00000000000000import re import psycopg import pytest def test_no_database(bouncer): with bouncer.log_contains(r"closing because: no such database: nosuchdb"): with pytest.raises( psycopg.OperationalError, match="no such database: nosuchdb" ): bouncer.test(dbname="nosuchdb") def test_no_database_authfail(bouncer): bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains(r"closing because: password authentication failed"): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="nosuchdb", password="wrong") def test_no_database_auth_user(bouncer): bouncer.admin(f"set auth_user='pswcheck'") bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains(r"closing because: password authentication failed"): with pytest.raises( psycopg.OperationalError, match="password authentication failed" ): bouncer.test(dbname="nosuchdb", user="someuser", password="wrong") def test_no_database_pg(bouncer): with bouncer.log_contains( r'server login failed: FATAL database "non_existing_pg_db" does not exist' ), bouncer.log_contains( r'closing because: database "non_existing_pg_db" does not exist' ): with pytest.raises( psycopg.OperationalError, match='database "non_existing_pg_db" does not exist', ): bouncer.test(dbname="non_existing_pg_db") def test_no_database_auto_database(bouncer): with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: # uncomment the auto-database line f.write(re.sub(r"^;\*", "*", original, flags=re.MULTILINE)) bouncer.admin("reload") with bouncer.log_contains( r'server login failed: FATAL database "nosuchdb" does not exist' ), bouncer.log_contains(r'closing because: database "nosuchdb" does not exist'): with pytest.raises( psycopg.OperationalError, match='database "nosuchdb" does not exist' ): bouncer.test(dbname="nosuchdb") def test_no_database_auto_database_auth_user(bouncer): with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: # uncomment the auto-database line f.write(re.sub(r"^;\*", "*", original, flags=re.MULTILINE)) bouncer.admin("reload") bouncer.admin(f"set auth_user='pswcheck'") bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains( r'server login failed: FATAL database "nosuchdb" does not exist' ), bouncer.log_contains(r'closing because: database "nosuchdb" does not exist'): with pytest.raises( psycopg.OperationalError, match='database "nosuchdb" does not exist' ): bouncer.test(dbname="nosuchdb", user="nonexistinguser") def test_no_database_md5_auth_scram_pw_success(bouncer): # Testing what happens on successful SCRAM auth connection to non-existent # DB Segfaults have been seen after mock authentication was put in place # with md5 auth and a scram PW when saving SCRAM credentials. Including # this test to check for the condition repeating. bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains(r"closing because: no such database: nosuchdb"): with pytest.raises( psycopg.OperationalError, match="no such database: nosuchdb" ): bouncer.test(dbname="nosuchdb", user="scramuser1", password="foo") def test_no_database_scram_auth_scram_pw_success(bouncer): # Testing what happens on successful SCRAM auth with a SCRAM PW connection # to non-existent DB. Segfaults have been seen after mock authentication # was put in place with md5 auth and a scram PW. Including this test for # completeness. bouncer.admin(f"set auth_type='scram-sha-256'") with bouncer.log_contains(r"closing because: no such database: nosuchdb"): with pytest.raises( psycopg.OperationalError, match="no such database: nosuchdb" ): bouncer.test(dbname="nosuchdb", user="scramuser1", password="foo") @pytest.mark.md5 def test_no_database_md5_auth_md5_pw_success(bouncer): # Testing what happens on successful MD5 auth with a MD5 pw connection to # non-existent DB Segfaults have been seen after mock authentication was # put in place with md5 auth and a scram PW. Including this test for # completeness. bouncer.admin(f"set auth_type='md5'") with bouncer.log_contains(r"closing because: no such database: nosuchdb"): with pytest.raises( psycopg.OperationalError, match="no such database: nosuchdb" ): bouncer.test(dbname="nosuchdb", user="muser1", password="foo") pgbouncer-1.24.1/test/pgbouncer_hba.conf0000644000175000000000000000127114777762222015134 00000000000000# TYPE DATABASE USER ADDRESS METHOD local all postgres peer # do not let the "postgres" superuser login via a certificate hostssl all postgres ::/0 reject hostssl all postgres 0.0.0.0/0 reject # hostssl all all ::/0 md5 hostssl p0y all 0.0.0.0/0 cert map=test2 hostssl p0x all 0.0.0.0/0 cert map=test hostssl p0 bouncer 0.0.0.0/0 cert hostssl all all 192.168.1.1/0 md5 pgbouncer-1.24.1/test/test_limits.py0000644000175000000000000005633714777762222014416 00000000000000import asyncio import os import re import psycopg import pytest from psycopg.rows import dict_row @pytest.mark.asyncio async def test_max_client_conn(bouncer): bouncer.default_db = "p1" bouncer.admin(f"set max_client_conn=5") result = bouncer.asleep(3, times=4) await asyncio.sleep(1) # should still be allowed, since it's the last allowed connection await bouncer.atest() result_last = bouncer.asleep(3) await asyncio.sleep(1) with pytest.raises(psycopg.OperationalError, match=r"max_client_conn"): await bouncer.atest() await result await result_last def test_max_db_client_connections_local_override_global(bouncer): """Test that database level max_db_client_connections overrides server level max_db_client_connections.""" test_db = "conn_limit_db" connect_args = {"dbname": test_db, "user": "muser1"} conns = [bouncer.conn(**connect_args) for _ in range(2)] dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 2 assert db["max_client_connections"] == 2 with pytest.raises(psycopg.OperationalError, match=r"max_db_client_connections"): _ = bouncer.conn(**connect_args) with pytest.raises(psycopg.OperationalError, match=r"max_db_client_connections"): _ = bouncer.conn(**connect_args) for conn in conns: conn.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "muser1"), ("pgbouncer", "pgbouncer"), ("pgbouncer", "muser1"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_db_client_connections_global_negative( bouncer, test_db: str, test_user: str ) -> None: """Negative test of server wide max_db_client_connections setting.""" bouncer.admin("SET max_db_client_connections = 2") bouncer.admin("SET stats_users = 'muser1'") bouncer.admin("SET admin_users = 'pgbouncer'") connect_args = {"dbname": test_db, "user": test_user} conns = [bouncer.conn(**connect_args) for _ in range(2)] dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 2 if test_db == "p0" else 3 assert db["max_client_connections"] == 2 if test_db == "pgbouncer" and test_user == "pgbouncer": _ = bouncer.conn(**connect_args) else: with pytest.raises( psycopg.OperationalError, match=r"max_db_client_connections" ): _ = bouncer.conn(**connect_args) for conn in conns: conn.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "muser1"), ("pgbouncer", "pgbouncer"), ("pgbouncer", "muser1"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_db_client_connections_global_positive( bouncer, test_db: str, test_user: str ) -> None: """Positive test of server wide max_db_client_connections setting.""" # with bouncer.run_with_config(config): bouncer.admin("SET max_db_client_connections = 2") bouncer.admin("SET stats_users = 'muser1'") bouncer.admin("SET admin_users = 'pgbouncer'") connect_args = {"dbname": test_db, "user": test_user} conn = bouncer.conn(**connect_args) # should still be allowed, since it's the last allowed connection dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 1 if test_db == "p0" else 2 assert db["max_client_connections"] == 2 _ = bouncer.conn(**connect_args) conn.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "muser1"), ("pgbouncer", "pgbouncer"), ("pgbouncer", "muser1"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_db_client_connections_decrement( bouncer, test_db: str, test_user: str ) -> None: """Test that max_db_connections is correctly decremented when user closes connection.""" bouncer.admin("SET stats_users = 'muser1'") bouncer.admin("SET admin_users = 'pgbouncer'") connect_args = {"dbname": test_db, "user": test_user} [conn_1, conn_2] = [bouncer.conn(**connect_args) for _ in range(2)] dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 2 if test_db == "p0" else 3 conn_2.close() dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 1 if test_db == "p0" else 2 @pytest.mark.parametrize( ("test_db", "test_user"), [ ("client_limit_db", "muser1"), ("client_limit_db_auth_passthrough", "pswcheck_not_in_auth_file"), ], ) def test_max_db_client_connections_negative( bouncer, test_db: str, test_user: str ) -> None: """Negative test of database specific max_db_client_connections setting.""" connect_args = {"dbname": test_db, "user": test_user} # with bouncer.run_with_config(config): conns = [bouncer.conn(**connect_args) for _ in range(2)] dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 2 if test_db == "p0" else 3 assert db["max_client_connections"] == 2 with pytest.raises(psycopg.OperationalError, match=r"max_db_client_connections"): bouncer.conn(**connect_args) for conn in conns: conn.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("client_limit_db", "muser1"), ("client_limit_db_auth_passthrough", "pswcheck_not_in_auth_file"), ], ) def test_max_db_client_connections_positive(bouncer, test_db: str, test_user) -> None: """Positive test of database specific max_db_client_connections setting.""" connect_args = {"dbname": test_db, "user": test_user} # with bouncer.run_with_config(config): conn = bouncer.conn(**connect_args) # should still be allowed, since it's the last allowed connection dbs = bouncer.admin("SHOW DATABASES", row_factory=dict_row) db = [db for db in dbs if db["name"] == test_db][0] assert db["current_client_connections"] == 1 if test_db == "p0" else 2 assert db["max_client_connections"] == 2 _ = bouncer.conn(**connect_args) conn.close() @pytest.mark.asyncio async def test_pool_size(pg, bouncer): # per user pool_size await bouncer.asleep(0.5, dbname="p0a", user="poolsize1", times=3) assert pg.connection_count(dbname="p0", users=("poolsize1",)) == 1 # even though we connect using user poolsize1 its setting do not apply is forced user is configured for db await bouncer.asleep(0.5, dbname="p0", user="poolsize1", times=5) assert pg.connection_count(dbname="p0", users=("bouncer",)) == 2 # per db pool_size await bouncer.asleep(0.5, times=5) assert pg.connection_count("p0") == 2 # global pool_size bouncer.default_db = "p1" await bouncer.asleep(0.5, times=10) assert pg.connection_count("p1") == 5 # test reload (GH issue #248) bouncer.admin("set default_pool_size = 7") await bouncer.asleep(0.5, times=10) assert pg.connection_count("p1") == 7 @pytest.mark.asyncio async def test_min_pool_size(pg, bouncer): # uncommenting the db that has "forced" maintenance enabled # by not having this db enabled we avoid polluting other tests # with connections getting autocreated with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: # uncomment the relevant db new = re.sub(r"^;p0z= (.+)", r"p0z= \g<1>", original, flags=re.MULTILINE) print(new) f.write(new) bouncer.admin("reload") # having to wait a little to give janitor time to create connection to satisfy min_pool_size await asyncio.sleep(2) # ensure db without min_pool_size has no connections # p0 assert pg.connection_count(dbname="p0", users=("bouncer",)) == 0 # ensure db with min_pool_size and forced user (p0z) has the required # backend connections assert pg.connection_count(dbname="p0", users=("pswcheck",)) == 3 # ensure db with min_pool_size and no forced user (p0x) has no backend # connections assert pg.connection_count(dbname="p0", users=("postgres",)) == 0 # client connecting to p0x should trigger backend connection creation up to # min_pool_size. # # NOTE: It's a bit tricky to get the timing of this test to work # robustly: Full maintenance runs three times a second, so we # need to wait at least 1/3 seconds for it to notice for sure # that the pool is in use. When it does, it will launch one # connection per round, so we need to wait at least 3 * 1/3 # second before all the min pool connections are launched. # Also, we need to keep the query running while this is # happening so that the pool doesn't become momentarily # unused. result = bouncer.asleep(2, dbname="p0x") await asyncio.sleep(2) await result assert pg.connection_count(dbname="p0", users=("postgres",)) == 5 @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "maxedout3"), ("pgbouncer", "maxedout3"), ("pgbouncer", "maxedout2"), ("pauthz", "pswcheck_not_in_auth_file"), ], ) def test_max_user_client_connections_local_override_global( bouncer, test_db: str, test_user: str ) -> None: """Test that user level overrides global connection limit. 1. Set global client connection limit 2. Grab user data from `SHOW USERS` for user with max_user_client_connections set 3. Validate that the user level max_user_client_connections is returned Tests 4 users: normal, admin, stats, and auth pass through """ bouncer.admin("set max_user_client_connections=1") bouncer.admin("set admin_users='maxedout3,pgbouncer'") bouncer.admin("set stats_users=maxedout2") connect_args = {"dbname": test_db, "user": test_user} conn_1 = bouncer.conn(**connect_args) users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["max_user_client_connections"] == 2 assert user["current_client_connections"] == 1 bouncer.conn(**connect_args) conn_1.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "maxedout4"), ("pgbouncer", "maxedout4"), ("pgbouncer", "maxedout5"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_user_client_connections_global_positive( bouncer, test_db: str, test_user: str ) -> None: """Positive test for global max_user_client_connections setting.""" bouncer.admin("SET max_user_client_connections=2") bouncer.admin("SET admin_users='maxedout4,pgbouncer'") bouncer.admin("SET stats_users='maxedout5'") connect_args = {"dbname": test_db, "user": test_user} conn_1 = bouncer.conn(**connect_args) users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["max_user_client_connections"] == 2 assert user["current_client_connections"] == 1 # should still be allowed, since it's the last allowed connection bouncer.conn(**connect_args) conn_1.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "maxedout4"), ("pgbouncer", "maxedout4"), ("pgbouncer", "maxedout5"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_user_client_connections_global_negative( bouncer, test_db: str, test_user: str ) -> None: """Negative test for max_user_client_connections setting. Test that default user level connection limit correctly rejects connection after 2 users are connected. Also checks that user counts are correctly reflected in SHOW USERS stats command. Test covers admin db and real db """ bouncer.admin("SET max_user_client_connections=2") bouncer.admin("SET admin_users='maxedout4,pgbouncer'") bouncer.admin("SET stats_users='maxedout5'") connect_args = {"dbname": test_db, "user": test_user} conns = [bouncer.conn(**connect_args) for _ in range(2)] users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["max_user_client_connections"] == 2 assert user["current_client_connections"] == 2 # Make sure error is correctly 2 times sequentially for _ in range(2): if test_db == "pgbouncer" and test_user == "maxedout4": bouncer.conn(**connect_args) else: with pytest.raises( psycopg.OperationalError, match=r"max_user_client_connections" ): bouncer.conn(**connect_args) for conn in conns: conn.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "maxedout3"), ("pgbouncer", "maxedout3"), ("pgbouncer", "maxedout2"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_user_client_connections_positive( bouncer, test_db: str, test_user: str ) -> None: """Positive test of user level max_user_client_connections setting. Test that user level connection limits allow users to connect up to the limit level. Also test that SHOW USERS stats correctly reflect this number. """ bouncer.admin("SET max_user_client_connections=2") bouncer.admin("SET admin_users='maxedout2,pgbouncer'") bouncer.admin("SET stats_users='maxedout3'") connect_args = {"dbname": test_db, "user": test_user} conn_1 = bouncer.conn(**connect_args) users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["max_user_client_connections"] == 2 assert user["current_client_connections"] == 1 # should still be allowed, since it's the last allowed connection conn_2 = bouncer.conn(**connect_args) for conn in [conn_1, conn_2]: conn.close() @pytest.mark.parametrize( ("test_db", "test_user"), [ ("p0", "maxedout3"), ("pgbouncer", "maxedout3"), ("pgbouncer", "maxedout2"), ("authdb", "pswcheck_not_in_auth_file"), ], ) def test_max_user_client_connections_negative( bouncer, test_db: str, test_user: str ) -> None: """Negative test of user level max_user_client_connections setting. Test that user level connection limit correctly rejects connection after 2 users are connected. Also checks that user counts are correctly reflected in SHOW USERS stats command. Test covers admin db and real db """ bouncer.admin("SET max_user_client_connections=2") bouncer.admin("SET admin_users='maxedout2,pgbouncer'") bouncer.admin("SET stats_users='maxedout3'") connect_args = {"dbname": test_db, "user": test_user} conns = [bouncer.conn(**connect_args) for _ in range(2)] users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["max_user_client_connections"] == 2 assert user["current_client_connections"] == 2 if test_db == "pgbouncer" and test_user == "maxedout2": bouncer.conn(**connect_args) else: with pytest.raises( psycopg.OperationalError, match=r"max_user_client_connections" ): bouncer.conn(**connect_args) for conn in conns: conn.close() def test_user_client_count_db_connect_fail(pg, bouncer) -> None: test_user = "maxedout3" test_dbname = "user_passthrough" pg.nossl_access(dbname="p0", auth_type="reject", user=test_user) pg.ssl_access(dbname="p0", auth_type="reject", user=test_user) pg.reload() users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["current_client_connections"] == 0 connect_args = {"dbname": test_dbname, "user": test_user} with pytest.raises( psycopg.OperationalError, match="pg_hba.conf rejects connection" ): _ = bouncer.conn(**connect_args) users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["current_client_connections"] == 0 def test_user_client_count_db_connect_fail_2(bouncer) -> None: test_user = "pswcheck_not_in_auth_file" test_dbname = "user_passthrough" users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["current_client_connections"] == 0 connect_args = {"dbname": test_dbname, "user": test_user} with pytest.raises(psycopg.OperationalError, match="authentication failed"): _ = bouncer.conn(**connect_args) users = bouncer.admin("SHOW USERS", row_factory=dict_row) user = next(user for user in users if user["name"] == test_user) assert user["current_client_connections"] == 0 def test_user_client_count_db_connect_fail_3(bouncer) -> None: test_user = "non_existent_user" test_dbname = "user_passthrough" users = bouncer.admin("SHOW USERS", row_factory=dict_row) assert len([user for user in users if user["name"] == test_user]) == 0 connect_args = {"dbname": test_dbname, "user": test_user} with pytest.raises(psycopg.OperationalError, match="authentication failed"): _ = bouncer.conn(**connect_args) users = bouncer.admin("SHOW USERS", row_factory=dict_row) # We don't expect non-existent users which cannot authentiticate to show up # in the stats at all. This would just pollute the stats output with people # making typos or attackers trying random user accounts. assert len([user for user in users if user["name"] == test_user]) == 0 def test_min_pool_size_with_lower_max_user_connections(bouncer): # The p0x in test.init has min_pool_size set to 5. This should make # the PgBouncer try to create a pool for maxedout2 user of size 5 after a # client connects to the PgBouncer. However maxedout2 user has # max_user_connections set to 2, so the final pool size should be only 2. # Running a query for sufficient time for us to reach the final # connection count in the pool and detect any evictions. with bouncer.log_contains(r"new connection to server \(from", times=2): with bouncer.log_contains("closing because: evicted", times=0): bouncer.sleep(2, dbname="p0x", user="maxedout2") def test_min_pool_size_with_lower_max_db_connections(bouncer): # The p0x in test.init has min_pool_size set to 5. This should make # the PgBouncer try to create a pool for puser1 user of size 5 after a client # connects to the PgBouncer. However the db also has max_db_connections set # to 2, so the final pool size should be only 2. # Running a query for sufficient time for us to reach the final # connection count in the pool and detect any evictions. with bouncer.log_contains(r"new connection to server \(from", times=2): with bouncer.log_contains("closing because: evicted", times=0): bouncer.sleep(2, dbname="p0y", user="puser1") @pytest.mark.asyncio async def test_reserve_pool_size(pg, bouncer): bouncer.admin("set reserve_pool_size = 3") bouncer.admin("set reserve_pool_timeout = 2") # Disable tls to get more consistent timings bouncer.admin("set server_tls_sslmode = disable") with bouncer.log_contains("taking connection from reserve_pool", times=3): # default_pool_size is 5, so half of the connections will need to wait # until the reserve_pool_timeout (2 seconds) is reached. At that point # 3 more connections should be allowed to continue. result = bouncer.asleep(10, dbname="p1", times=10) await asyncio.sleep(1) assert pg.connection_count("p1") == 5 await asyncio.sleep(8) assert pg.connection_count("p1") == 8 await result @pytest.mark.asyncio async def test_user_reserve_pool_size(pg, bouncer): bouncer.admin("set reserve_pool_timeout = 2") # Disable tls to get more consistent timings bouncer.admin("set server_tls_sslmode = disable") with bouncer.log_contains("taking connection from reserve_pool", times=2): # respoolsize1 user has a pool_size of 1 and reserve_pool_size of 2 # this means 1 connection should happen immediately while 2 out of # the 3 remaining connections happen after reserve_pool_timeout result = bouncer.asleep(10, dbname="p0a", user="respoolsize1", times=4) await asyncio.sleep(1) assert pg.connection_count(dbname="p0", users=("respoolsize1",)) == 1 await asyncio.sleep(8) assert pg.connection_count(dbname="p0", users=("respoolsize1",)) == 3 await result @pytest.mark.asyncio async def test_database_reserve_pool_size(pg, bouncer): bouncer.admin("set reserve_pool_timeout = 2") # Disable tls to get more consistent timings bouncer.admin("set server_tls_sslmode = disable") with bouncer.log_contains("taking connection from reserve_pool", times=2): # p0 db has a pool_size of 2 and reserve_pool_size of 2 # this means 2 connections should happen immediately while 2 out of # the 3 remaining connections happen after reserve_pool_timeout result = bouncer.asleep(10, dbname="p0", user="bouncer", times=5) await asyncio.sleep(1) assert pg.connection_count(dbname="p0", users=("bouncer",)) == 2 await asyncio.sleep(8) assert pg.connection_count(dbname="p0", users=("bouncer",)) == 4 await result @pytest.mark.asyncio async def test_database_reserve_pool_size_old_param(pg, bouncer): bouncer.admin("set reserve_pool_timeout = 2") # Disable tls to get more consistent timings bouncer.admin("set server_tls_sslmode = disable") with bouncer.log_contains("taking connection from reserve_pool", times=2): # p0a db has a pool_size of 2 and reserve_pool of 2 # this means 2 connections should happen immediately while 2 out of # the 3 remaining connections happen after reserve_pool_timeout result = bouncer.asleep(10, dbname="p0a", user="bouncer", times=5) await asyncio.sleep(1) assert pg.connection_count(dbname="p0", users=("bouncer",)) == 2 await asyncio.sleep(8) assert pg.connection_count(dbname="p0", users=("bouncer",)) == 4 await result @pytest.mark.asyncio async def test_max_db_connections(pg, bouncer): # some users, doesn't matter which ones users = ["muser1", "muser2", "puser1", "puser2", "postgres"] # p2 has max_db_connections=4 await asyncio.gather( *[bouncer.asleep(0.5, dbname="p2", user=u, times=2) for u in users] ) # p2 in PgBouncer maps to p0 in Postgres assert pg.connection_count("p0", users=users) == 4 @pytest.mark.asyncio async def test_max_user_connections(pg, bouncer): # some users, doesn't matter which ones dbnames = ["p7a", "p7b", "p7c"] await asyncio.gather( *[ bouncer.asleep(0.5, dbname=db, user="maxedout", times=3, connect_timeout=10) for db in dbnames ] ) assert pg.connection_count("p7", users=["maxedout"]) == 3 pgbouncer-1.24.1/test/run-conntest.sh0000644000175000000000000000022214777762222014455 00000000000000#! /bin/sh createdb conntest ../pgbouncer -d ctest6000.ini ../pgbouncer -d ctest7000.ini ./asynctest # now run conntest.sh on another console pgbouncer-1.24.1/test/test_ssl.py0000644000175000000000000003667014777762222013714 00000000000000import subprocess import time import psycopg import pytest from .utils import MACOS, PG_MAJOR_VERSION, TEST_DIR, TLS_SUPPORT, WINDOWS, Bouncer if not TLS_SUPPORT: pytest.skip(allow_module_level=True) # XXX: These test use psql to connect using sslmode=verify-full instead of # using psycopg. The reason for this is that psycopg has a bug on Apple # silicon when enabling SSL: https://github.com/psycopg/psycopg/discussions/270 # override regular bouncer fixture with one that uses the special SSL config @pytest.mark.asyncio @pytest.fixture async def bouncer(pg, tmp_path): bouncer = Bouncer( pg, tmp_path / "bouncer", base_ini_path=TEST_DIR / "ssl" / "test.ini" ) await bouncer.start() yield bouncer await bouncer.cleanup() def test_server_ssl(pg, bouncer, cert_dir): bouncer.admin("set server_tls_sslmode = require") pg.ssl_access("all", "trust") pg.configure("ssl=on") root = cert_dir / "TestCA1" / "ca.crt" pg.configure(f"ssl_ca_file='{root}'") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() bouncer.test() def test_server_ssl_set_disable(pg, bouncer, cert_dir): bouncer.admin("set server_tls_sslmode = require") pg.ssl_access("all", "trust") pg.configure("ssl=on") root = cert_dir / "TestCA1" / "ca.crt" pg.configure(f"ssl_ca_file='{root}'") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() bouncer.test() pg.reset_hba() if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() bouncer.test() # connection is still cached bouncer.admin("reconnect") with pytest.raises( psycopg.OperationalError, match="no pg_hba.conf entry for .*, (SSL encryption|SSL on)", ): bouncer.test() # XXX: It would be nice if this reset server_login_retry, but it currently # doesn't. So we have server_login_retry=1 in the ini file. bouncer.admin("set server_tls_sslmode = disable") bouncer.test() def test_server_ssl_set_enable(pg, bouncer, cert_dir): bouncer.admin("set server_tls_sslmode = disable") pg.configure("ssl=on") root = cert_dir / "TestCA1" / "ca.crt" pg.configure(f"ssl_ca_file='{root}'") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() bouncer.test() pg.nossl_access("all", "reject") pg.ssl_access("all", "trust") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() bouncer.test() # connection is still cached bouncer.admin("reconnect") with pytest.raises( psycopg.OperationalError, match="pg_hba.conf rejects connection for .*, (no encryption|SSL off)", ): bouncer.test() # XXX: It would be nice if this reset server_login_retry, but it currently # doesn't. So we have server_login_retry=1 in the ini file. bouncer.admin("set server_tls_sslmode = require") bouncer.test() def test_server_ssl_verify(pg, bouncer, cert_dir): bouncer.admin("set server_tls_sslmode = 'verify-full'") root = cert_dir / "TestCA1" / "ca.crt" wrong_root = cert_dir / "TestCA2" / "ca.crt" bouncer.admin(f"set server_tls_ca_file = '{wrong_root}'") pg.ssl_access("all", "trust") pg.configure("ssl=on") pg.configure(f"ssl_ca_file='{root}'") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() with bouncer.log_contains(r"certificate verify failed"): with pytest.raises( psycopg.OperationalError, match="connection timeout expired", ): bouncer.test(connect_timeout=4) bouncer.admin(f"set server_tls_ca_file = '{root}'") bouncer.test() def test_server_ssl_auth(pg, bouncer, cert_dir): bouncer.admin("set server_tls_sslmode = 'verify-full'") root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "02-bouncer.key" cert = cert_dir / "TestCA1" / "sites" / "02-bouncer.crt" bouncer.admin(f"set server_tls_ca_file = '{root}'") bouncer.admin(f"set server_tls_key_file = '{key}'") bouncer.admin(f"set server_tls_cert_file = '{cert}'") pg.ssl_access("all", "cert") pg.configure("ssl=on") pg.configure(f"ssl_ca_file='{root}'") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() bouncer.test() def test_client_ssl(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.admin(f"set client_tls_key_file = '{key}'") bouncer.admin(f"set client_tls_cert_file = '{cert}'") bouncer.admin(f"set client_tls_ca_file = '{root}'") bouncer.admin(f"set client_tls_sslmode = require") bouncer.psql_test(host="localhost", sslmode="require") def test_client_ssl_set_enable_disable(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.admin(f"set client_tls_key_file = '{key}'") bouncer.admin(f"set client_tls_cert_file = '{cert}'") bouncer.admin(f"set client_tls_ca_file = '{root}'") bouncer.admin(f"set client_tls_sslmode = require") bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) bouncer.admin(f"set client_tls_sslmode = disable") bouncer.test(sslmode="disable") bouncer.admin(f"set client_tls_sslmode = require") bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) def test_client_ssl_set_change_ca(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.admin(f"set client_tls_key_file = '{key}'") bouncer.admin(f"set client_tls_cert_file = '{cert}'") bouncer.admin(f"set client_tls_ca_file = '{root}'") bouncer.admin(f"set client_tls_sslmode = require") bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) new_root = cert_dir / "TestCA2" / "ca.crt" new_key = cert_dir / "TestCA2" / "sites" / "01-localhost.key" new_cert = cert_dir / "TestCA2" / "sites" / "01-localhost.crt" bouncer.admin(f"set client_tls_key_file = '{new_key}'") bouncer.admin(f"set client_tls_cert_file = '{new_cert}'") bouncer.admin(f"set client_tls_ca_file = '{new_root}'") with pytest.raises( subprocess.CalledProcessError, ): bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=new_root) @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_client_ssl_sighup_enable_disable(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.sighup() bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) bouncer.write_ini(f"client_tls_sslmode = disable") bouncer.sighup() bouncer.test(sslmode="disable") @pytest.mark.skipif("WINDOWS", reason="Windows does not have SIGHUP") def test_client_ssl_sighup_change_ca(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.sighup() bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) new_root = cert_dir / "TestCA2" / "ca.crt" new_key = cert_dir / "TestCA2" / "sites" / "01-localhost.key" new_cert = cert_dir / "TestCA2" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {new_key}") bouncer.write_ini(f"client_tls_cert_file = {new_cert}") bouncer.write_ini(f"client_tls_ca_file = {new_root}") bouncer.sighup() with pytest.raises( subprocess.CalledProcessError, ): bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=new_root) def test_client_ssl_reload_enable_disable(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.admin("reload") bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) bouncer.write_ini(f"client_tls_sslmode = disable") bouncer.admin("reload") bouncer.test(sslmode="disable") def test_client_ssl_reload_change_ca(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.admin("reload") bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) new_root = cert_dir / "TestCA2" / "ca.crt" new_key = cert_dir / "TestCA2" / "sites" / "01-localhost.key" new_cert = cert_dir / "TestCA2" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {new_key}") bouncer.write_ini(f"client_tls_cert_file = {new_cert}") bouncer.write_ini(f"client_tls_ca_file = {new_root}") bouncer.admin("reload") with pytest.raises( subprocess.CalledProcessError, ): bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=root) bouncer.psql_test(host="localhost", sslmode="verify-full", sslrootcert=new_root) def test_client_ssl_auth(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = verify-full") bouncer.write_ini(f"auth_type = cert") bouncer.admin("reload") client_key = cert_dir / "TestCA1" / "sites" / "02-bouncer.key" client_cert = cert_dir / "TestCA1" / "sites" / "02-bouncer.crt" bouncer.psql_test( host="localhost", sslmode="verify-full", user="bouncer", sslrootcert=root, sslkey=client_key, sslcert=client_cert, ) def test_client_ssl_scram(bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.write_ini(f"auth_type = scram-sha-256") bouncer.admin("reload") bouncer.psql_test( host="localhost", user="bouncer", password="zzzz", sslmode="verify-full", sslrootcert=root, ) def test_ssl_replication(pg, bouncer, cert_dir): root = cert_dir / "TestCA1" / "ca.crt" key = cert_dir / "TestCA1" / "sites" / "01-localhost.key" cert = cert_dir / "TestCA1" / "sites" / "01-localhost.crt" bouncer.write_ini(f"server_tls_sslmode = verify-full") bouncer.write_ini(f"server_tls_ca_file = {root}") bouncer.write_ini(f"client_tls_sslmode = require") bouncer.write_ini(f"client_tls_key_file = {key}") bouncer.write_ini(f"client_tls_cert_file = {cert}") bouncer.write_ini(f"client_tls_ca_file = {root}") bouncer.admin("reload") pg.ssl_access("all", "trust") pg.ssl_access("replication", "trust", user="postgres") pg.configure("ssl=on") pg.configure(f"ssl_ca_file='{root}'") if PG_MAJOR_VERSION < 10 or WINDOWS: pg.restart() else: pg.reload() # Logical rep connect_args = { "host": "localhost", "dbname": "p7a", "replication": "database", "user": "postgres", "application_name": "abc", "sslmode": "verify-full", "sslrootcert": root, } bouncer.psql("IDENTIFY_SYSTEM", **connect_args) # physical rep connect_args["replication"] = "true" bouncer.psql("IDENTIFY_SYSTEM", **connect_args) def test_servers_no_disconnect_on_reload_with_no_tls_change(bouncer, pg, cert_dir): bouncer.default_db = "pTxnPool" with bouncer.cur() as cur: assert pg.connection_count(dbname="p0") == 1 with bouncer.log_contains( r"pTxnPool.*database configuration changed|pTxnPool.*obsolete connection", 0 ): # change nothing and RELOAD bouncer.admin("RELOAD") # keep cursor open for > full_maint_period # full_maint_period = 3x/s https://github.com/pgbouncer/pgbouncer/blob/master/src/janitor.c#L28 time.sleep(0.5) assert pg.connection_count(dbname="p0") == 1 cur.execute("SELECT 1") def test_servers_disconnect_when_changing_tls_config(bouncer, pg, cert_dir): bouncer.default_db = "pTxnPool" bouncer.write_ini(f"server_tls_protocols = tlsv1.0") bouncer.admin("RELOAD") with bouncer.cur() as cur: assert pg.connection_count(dbname="p0") == 1 bouncer.write_ini(f"server_tls_protocols = secure") with bouncer.log_contains( r"pTxnPool.*database configuration changed|pTxnPool.*obsolete connection", 1 ): bouncer.admin("RELOAD") time.sleep(0.5) assert pg.connection_count(dbname="p0") == 0 cur.execute("SELECT 1") def test_servers_disconnect_when_enabling_ssl(bouncer, pg, cert_dir): bouncer.default_db = "pTxnPool" bouncer.write_ini(f"server_tls_sslmode = disable") bouncer.admin("RELOAD") with bouncer.cur() as cur: assert pg.connection_count(dbname="p0") == 1 bouncer.write_ini(f"server_tls_sslmode = allow") with bouncer.log_contains( r"pTxnPool.*database configuration changed|pTxnPool.*obsolete connection" ): bouncer.admin("RELOAD") cur.execute("SELECT 1") def test_servers_disconnect_when_changing_sslmode(bouncer, pg, cert_dir): bouncer.default_db = "pTxnPool" with bouncer.cur() as cur: assert pg.connection_count(dbname="p0") == 1 bouncer.write_ini(f"server_tls_sslmode = allow") with bouncer.log_contains( r"pTxnPool.*database configuration changed|pTxnPool.*obsolete connection" ): bouncer.admin("RELOAD") cur.execute("SELECT 1") pgbouncer-1.24.1/test/pgident.conf0000644000175000000000000000036314777762222013771 00000000000000# PostgreSQL User Name Maps # ========================= # MAPNAME SYSTEM-USERNAME PG-USERNAME test pgbouncer.acme.org someuser test pgbouncer.acme.org anotheruser test2 "bouncer" all test2 pgbouncer.acme.org "anotheruser" pgbouncer-1.24.1/test/test_timeouts.py0000644000175000000000000003440214777762222014753 00000000000000import asyncio import platform import time from concurrent.futures import ThreadPoolExecutor import psycopg import pytest from .utils import USE_SUDO def test_server_lifetime(pg, bouncer): bouncer.admin(f"set server_lifetime=2") bouncer.test() assert pg.connection_count() == 1 time.sleep(3) assert pg.connection_count() == 0 bouncer.test() def test_server_lifetime_per_pool(pg, bouncer): bouncer.test(dbname="p9") assert pg.connection_count() == 1 time.sleep(3) assert pg.connection_count() == 0 bouncer.test(dbname="p9") def test_server_idle_timeout(pg, bouncer): bouncer.admin(f"set server_idle_timeout=2") bouncer.test() assert pg.connection_count() == 1 time.sleep(3) assert pg.connection_count() == 0 bouncer.test() def test_user_idle_transaction_timeout_negative(bouncer): config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = trust auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} pool_mode = session [users] puser1 = pool_mode=transaction idle_transaction_timeout=6 """ # while configured to be in statement pooling mode with bouncer.run_with_config(config): with bouncer.transaction(dbname="postgres", user="puser1") as cur: time.sleep(3) cur.execute("select 1") def test_user_idle_transaction_timeout_override_global(bouncer): config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = trust auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} pool_mode = session idle_transaction_timeout=100000 [users] puser1 = pool_mode=transaction idle_transaction_timeout=1 """ # while configured to be in statement pooling mode with bouncer.run_with_config(config): with bouncer.transaction(dbname="postgres", user="puser1") as cur: with bouncer.log_contains(r"idle transaction timeout"): time.sleep(3) with pytest.raises( psycopg.OperationalError, match=r"idle transaction timeout|Software caused connection abort|server closed the connection unexpectedly", ): cur.execute("select 1") def test_user_idle_transaction_timeout(bouncer): config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = trust auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} pool_mode = session [users] puser1 = pool_mode=transaction idle_transaction_timeout=1 """ # while configured to be in statement pooling mode with bouncer.run_with_config(config): with bouncer.transaction(dbname="postgres", user="puser1") as cur: with bouncer.log_contains(r"idle transaction timeout"): time.sleep(3) with pytest.raises( psycopg.OperationalError, match=r"idle transaction timeout|Software caused connection abort|server closed the connection unexpectedly", ): cur.execute("select 1") def test_user_query_timeout_override_global(bouncer): config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = trust auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} pool_mode = session query_timeout=100000 [users] puser1 = pool_mode=statement query_timeout=1 """ # while configured to be in statement pooling mode with bouncer.run_with_config(config): with bouncer.log_contains(r"query timeout"): with pytest.raises( psycopg.OperationalError, match=r"query timeout|server closed the connection unexpectedly", ): bouncer.sleep(5, user="puser1", dbname="postgres") def test_user_query_timeout_negative(bouncer): config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = trust auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} pool_mode = session [users] puser1 = pool_mode=statement query_timeout=10 """ # while configured to be in statement pooling mode with bouncer.run_with_config(config): bouncer.sleep(5, user="puser1", dbname="postgres") def test_user_query_timeout(bouncer): config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} admin_users = pgbouncer auth_type = trust auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} pool_mode = session [users] puser1 = pool_mode=statement query_timeout=1 """ # while configured to be in statement pooling mode with bouncer.run_with_config(config): with bouncer.log_contains(r"query timeout"): with pytest.raises( psycopg.OperationalError, match=r"query timeout|server closed the connection unexpectedly", ): bouncer.sleep(5, user="puser1", dbname="postgres") def test_query_timeout(bouncer): bouncer.admin(f"set query_timeout=1") with bouncer.log_contains(r"query timeout"): with pytest.raises( psycopg.OperationalError, match=r"query timeout|server closed the connection unexpectedly", ): bouncer.sleep(5) def test_user_level_idle_client_timeout_negative(bouncer): """Test user level client_idle_timeout correctly closes connection.""" bouncer.admin("set pool_mode=transaction") config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} auth_type = trust admin_users = pgbouncer auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres pool_mode = session [users] puser1 = pool_mode=transaction client_idle_timeout=2 """ with bouncer.run_with_config(config): bouncer.admin("RELOAD") with bouncer.cur(dbname="postgres", user="puser1") as cur: cur.execute("SELECT 1") with bouncer.log_contains(r"client_idle_timeout"): time.sleep(3) with pytest.raises( psycopg.OperationalError, match=r"client_idle_timeout|Software caused connection abort|server closed the connection unexpectedly", ): cur.execute("SELECT 1") def test_user_level_idle_client_timeout(bouncer): """Test that user level client_idle_timeout allows connection to stay open.""" bouncer.admin("set pool_mode=transaction") config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} auth_type = trust admin_users = pgbouncer auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres pool_mode = session [users] puser1 = pool_mode=transaction client_idle_timeout=6 """ with bouncer.run_with_config(config): bouncer.admin("RELOAD") with bouncer.cur(dbname="postgres", user="puser1") as cur: cur.execute("SELECT 1") time.sleep(3) cur.execute("SELECT 1") def test_user_level_idle_client_timeout_override(bouncer): """Test that user level client_idle_timeout overrides global level setting.""" bouncer.admin("set pool_mode=transaction") config = f""" [databases] postgres = host={bouncer.pg.host} port={bouncer.pg.port} [pgbouncer] listen_addr = {bouncer.host} auth_type = trust admin_users = pgbouncer auth_file = {bouncer.auth_path} listen_port = {bouncer.port} logfile = {bouncer.log_path} auth_dbname = postgres pool_mode = session client_idle_timeout=1000000 [users] puser1 = pool_mode=transaction client_idle_timeout=2 """ with bouncer.run_with_config(config): bouncer.admin("RELOAD") with bouncer.cur(dbname="postgres", user="puser1") as cur: cur.execute("SELECT 1") with bouncer.log_contains(r"client_idle_timeout"): time.sleep(3) with pytest.raises( psycopg.OperationalError, match=r"client_idle_timeout|Software caused connection abort|server closed the connection unexpectedly", ): cur.execute("SELECT 1") def test_idle_transaction_timeout(bouncer): bouncer.admin(f"set pool_mode=transaction") bouncer.admin(f"set idle_transaction_timeout=2") with bouncer.transaction() as cur: with bouncer.log_contains(r"idle transaction timeout"): time.sleep(3) with pytest.raises( psycopg.OperationalError, match=r"idle transaction timeout|Software caused connection abort|server closed the connection unexpectedly", ): cur.execute("select 1") # test for GH issue #125 with bouncer.transaction() as cur: cur.execute("select pg_sleep(2)").fetchone() time.sleep(1) cur.execute("select 1") def test_client_idle_timeout(bouncer): bouncer.admin(f"set client_idle_timeout=2") with bouncer.cur() as cur: cur.execute("select 1") with bouncer.log_contains(r"client_idle_timeout"): time.sleep(3) with pytest.raises( psycopg.OperationalError, match=r"client_idle_timeout|Software caused connection abort|server closed the connection unexpectedly", ): cur.execute("select 1") @pytest.mark.asyncio async def test_server_login_retry(pg, bouncer): bouncer.admin(f"set query_timeout=10") bouncer.admin(f"set server_login_retry=3") # Disable tls to get more consistent timings bouncer.admin("set server_tls_sslmode = disable") pg.stop() if platform.system() == "FreeBSD": # XXX: For some reason FreeBSD logs don't contain connect failed # For now we simply remove this check. But this warants further # investigation. await asyncio.gather( bouncer.atest(connect_timeout=10), pg.delayed_start(1), ) else: with bouncer.log_contains("connect failed"): await asyncio.gather( bouncer.atest(connect_timeout=10), pg.delayed_start(1), ) def test_server_connect_timeout_establish(pg, bouncer): pg.configure("pre_auth_delay to '5s'") pg.reload() bouncer.admin("set query_timeout=3") bouncer.admin("set server_connect_timeout=2") with bouncer.log_contains(r"closing because: connect timeout"): with pytest.raises(psycopg.errors.OperationalError, match="query_timeout"): bouncer.test(connect_timeout=10) @pytest.mark.skipif("not USE_SUDO") def test_server_connect_timeout_drop_traffic(pg, bouncer): bouncer.admin("set query_timeout=3") bouncer.admin("set server_connect_timeout=2") with bouncer.log_contains(r"closing because: connect failed"): with pg.drop_traffic(): with pytest.raises(psycopg.errors.OperationalError, match="query_timeout"): bouncer.test(connect_timeout=10) @pytest.mark.skipif("not USE_SUDO") @pytest.mark.skipif( "platform.system() != 'Linux'", reason="tcp_user_timeout is only supported on Linux" ) def test_tcp_user_timeout(pg, bouncer): bouncer.admin("set tcp_user_timeout=1000") bouncer.admin("set query_timeout=5") # Make PgBouncer cache a connection to Postgres bouncer.test() # without tcp_user_timeout, you get a different error message # about "query timeout" instead with bouncer.log_contains(r"closing because: server conn crashed?"): with pg.reject_traffic(): with pytest.raises( psycopg.OperationalError, match=r"query timeout|Software caused connection abort|server closed the connection unexpectedly", ): bouncer.test(connect_timeout=10) @pytest.mark.skipif("not USE_SUDO") @pytest.mark.asyncio async def test_server_check_delay(pg, bouncer): bouncer.admin("set server_check_delay=2") bouncer.admin("set server_login_retry=3") bouncer.admin("set query_timeout=10") with pg.drop_traffic(): time.sleep(3) query_task = bouncer.atest(connect_timeout=10) # We wait for 1 second to show that the query is blocked while traffic # is dropped. done, pending = await asyncio.wait([query_task], timeout=1) assert done == set() assert pending == {query_task} await query_task @pytest.mark.skipif("not USE_SUDO") def test_cancel_wait_timeout(pg, bouncer): bouncer.admin("set cancel_wait_timeout=1") with bouncer.cur() as cur: with ThreadPoolExecutor(max_workers=2) as pool: query = pool.submit(cur.execute, "select pg_sleep(3)") time.sleep(1) with pg.drop_traffic(): with bouncer.log_contains(r"closing because: cancel_wait_timeout"): cancel = pool.submit(cur.connection.cancel) cancel.result() query.result() pgbouncer-1.24.1/test/hba_test.eval0000644000175000000000000000416014777762222014131 00000000000000# peer md5 db user unix peer dbp user unix password db userp unix trust dbz userz unix scram-sha-256 dbs users unix # hostssl cert db user 10.1.1.1 tls reject db user 10.1.1.1 reject db user 13.1.1.1 # hostnossl reject db user 11.1.1.1 tls md5 db user 11.1.1.1 reject db user 13.1.1.1 # host password db user 127.0.0.2 tls password db user 127.0.0.3 reject db user 127.0.1.4 # db1 filt reject db1x user 127.0.1.4 md5 db1 user 127.0.1.4 # user1 filt md5 db1z user1 15.0.0.1 reject db1z user2 15.0.0.1 # someusers reject db2 user 16.0.0.1 md5 db2 user1 16.0.0.1 md5 db2 user2 16.0.0.1 md5 db2 user3 16.0.0.1 reject db2 user4 16.0.0.1 # manyusers md5 db2 u1 17.0.0.1 md5 db2 u2 17.0.0.1 md5 db2 u3 17.0.0.1 md5 db2 u4 17.0.0.1 md5 db2 u5 17.0.0.1 md5 db2 u6 17.0.0.1 md5 db2 u7 17.0.0.1 md5 db2 u8 17.0.0.1 md5 db2 u9 17.0.0.1 md5 db2 u10 17.0.0.1 md5 db2 u11 17.0.0.1 # manydbs reject d1 user 18.0.0.2 trust d1 t18user 18.0.0.2 trust d2 t18user 18.0.0.2 trust d3 t18user 18.0.0.2 trust d4 t18user 18.0.0.2 trust d5 t18user 18.0.0.2 trust d6 t18user 18.0.0.2 trust d7 t18user 18.0.0.2 trust d8 t18user 18.0.0.2 trust d9 t18user 18.0.0.2 trust d10 t18user 18.0.0.2 trust d11 t18user 18.0.0.2 # quoting reject db t19user 19.0.0.2 cert all all 19.0.0.2 cert q1"q2 a , b 19.0.0.2 # bitmask cert mdb muser 199.199.199.199 reject mdb muser 199.199.199.198 reject mdb muser 199.199.199.200 cert mdb2 muser 254.1.1.1 # ipv6 md5 mdb muser ff11:2::1 md5 mdb muser ff22:3::1 trust mdb muser ::1 reject mdb muser ::2 # "all" address scram-sha-256 mdb3 muser 1.2.3.4 scram-sha-256 mdb3 muser face::1 reject mdb4 muser 1.2.3.4 reject mdb4 muser face::1 # replication reject mdb muser ::1 replication reject db userp unix replication reject replication userp unix replication trust db admin ::1 replication trust replication admin ::1 replication reject db admin ::1 reject replication admin ::1 trust db admin2 ::1 replication trust replication admin2 ::1 replication trust db2 admin2 ::1 reject replication admin2 ::1 pgbouncer-1.24.1/test/ctest6000.ini0000644000175000000000000000137414777762222013624 00000000000000;; database name = connect string [databases] ; redirect bardb to bazdb on localhost conntest = host=127.0.0.1 port=7000 dbname=conntest ;; Configuration section [pgbouncer] logfile = ctest6000.log pidfile = ctest6000.pid listen_addr = 127.0.0.1 listen_port = 6000 unix_socket_dir = /tmp auth_type = md5 auth_file = userlist.txt admin_users = marko stats_users = stats pool_mode = transaction server_reset_query = reset all server_check_query = select 1 server_check_delay = 2 max_client_conn = 1000 default_pool_size = 20 log_connections = 0 log_disconnections = 0 log_pooler_errors = 0 server_lifetime = 30 server_idle_timeout = 3 server_connect_timeout = 2 server_login_retry = 5 query_timeout = 5 client_idle_timeout = 10 client_login_timeout = 50 pgbouncer-1.24.1/test/test_cancel.py0000644000175000000000000000763314777762222014335 00000000000000import time from concurrent.futures import ThreadPoolExecutor import psycopg import pytest def test_cancel(bouncer): with bouncer.cur(dbname="p3") as cur: with ThreadPoolExecutor(max_workers=2) as pool: query = pool.submit(cur.execute, "select pg_sleep(5)") time.sleep(1) cancel = pool.submit(cur.connection.cancel) cancel.result() with pytest.raises( psycopg.errors.QueryCanceled, match="due to user request" ): query.result() # Test for waiting connections handling for cancel requests. # # The bug fixed by GH PR #542 was: When the connection pool is full, # cancel requests cannot get through (that is normal), but then when # unused connections close and pool slots are available, those are not # used for waiting cancel requests. def test_cancel_wait(bouncer): # default_pool_size=5 bouncer.admin(f"set server_idle_timeout=2") with bouncer.cur(dbname="p3") as cur: with ThreadPoolExecutor(max_workers=6) as pool: q1 = pool.submit(cur.execute, "select pg_sleep(5)") others = [pool.submit(bouncer.sleep, 2, dbname="p3") for _ in range(4)] cancel = pool.submit(cur.connection.cancel) cancel.result() with pytest.raises( psycopg.errors.QueryCanceled, match="due to user request" ): q1.result() for q in others: q.result() # Test that cancel requests can exceed the pool size # # Cancel request connections can use twice the pool size. See also GH # PR #543. def test_cancel_pool_size(bouncer): # default_pool_size=5 bouncer.admin(f"set server_idle_timeout=2") with ThreadPoolExecutor(max_workers=10) as pool: conns = [bouncer.conn(dbname="p3") for _ in range(5)] try: queries = [ pool.submit(conn.cursor().execute, "select pg_sleep(20)") for conn in conns ] time.sleep(1) cancels = [pool.submit(conn.cancel) for conn in conns] for c in cancels: c.result() for q in queries: with pytest.raises( psycopg.errors.QueryCanceled, match="due to user request" ): q.result() finally: for conn in conns: conn.close() # Test that cancel requests connections don't trigger cancellation of a query # from a different client. # # See also GH PR #717. Prior to this change it was possible to that a query was # cancelled on client A by a cancellation for client B, if the server was # released by client B and then reused by client A while the cancellation was # already in flight. def test_cancel_race(bouncer): # Make sure only one query can run at the same time so that its ensured # that both clients will use the same server connection. bouncer.admin("set default_pool_size=1") bouncer.admin("set server_idle_timeout=2") bouncer.admin("set verbose=1") conn1 = bouncer.conn(dbname="p1") cur1 = conn1.cursor() conn2 = bouncer.conn(dbname="p1") cur2 = conn2.cursor() try: with ThreadPoolExecutor(max_workers=100) as pool: q1 = pool.submit(cur1.execute, "select pg_sleep(5)") time.sleep(1) q2 = pool.submit(cur2.execute, "select pg_sleep(1)") time.sleep(1) cancels = [pool.submit(conn1.cancel) for _ in range(100)] # Spam many concurrent cancel requests to try and with the goal of # triggering race conditions for c in cancels: c.result() with pytest.raises( psycopg.errors.QueryCanceled, match="due to user request" ): q1.result() q2.result() bouncer.print_logs() finally: conn1.close() conn2.close() pgbouncer-1.24.1/test/hba_test.c0000644000175000000000000000601014777762222013420 00000000000000 #include "bouncer.h" #include #include #include #include #include #include #include #include int cf_tcp_keepcnt; int cf_tcp_keepintvl; int cf_tcp_keepidle; int cf_tcp_keepalive; int cf_tcp_user_timeout; int cf_tcp_socket_buffer; int cf_listen_port; static const char *method2string(int method) { switch (method) { case AUTH_TYPE_TRUST: return "trust"; case AUTH_TYPE_PLAIN: return "password"; case AUTH_TYPE_MD5: return "md5"; case AUTH_TYPE_CERT: return "cert"; case AUTH_TYPE_PEER: return "peer"; case AUTH_TYPE_HBA: return "hba"; case AUTH_TYPE_REJECT: return "reject"; case AUTH_TYPE_PAM: return "pam"; case AUTH_TYPE_SCRAM_SHA_256: return "scram-sha-256"; default: return "???"; } } static char *get_token(char **ln_p) { char *ln = *ln_p, *tok, *end; while (*ln && *ln == '\t') ln++; tok = ln; while (*ln && *ln != '\t') ln++; end = ln; while (*ln && *ln == '\t') ln++; *ln_p = ln; if (tok == end) return NULL; *end = 0; return tok; } static int hba_test_eval(struct HBA *hba, char *ln, int linenr) { const char *addr=NULL, *user=NULL, *db=NULL, *modifier=NULL, *exp=NULL; PgAddr pgaddr; struct HBARule *rule; int res = 0; bool tls; ReplicationType replication; if (ln[0] == '#') return 0; exp = get_token(&ln); db = get_token(&ln); user = get_token(&ln); addr = get_token(&ln); modifier = get_token(&ln); tls = strcmpeq(modifier, "tls"); replication = strcmpeq(modifier, "replication") ? REPLICATION_PHYSICAL : REPLICATION_NONE; if (!exp) return 0; if (!db || !user) die("hbatest: invalid line #%d", linenr); if (!pga_pton(&pgaddr, addr, 9999)) die("hbatest: invalid addr on line #%d", linenr); rule = hba_eval(hba, &pgaddr, !!tls, replication, db, user); if (!rule) { if (strcmp("reject", exp) == 0) { res = 0; } else { log_warning("FAIL on line %d: No rule for user=%s db=%s addr=%s", linenr, user, db, addr); res = 1; } } else { if (strcmp(method2string(rule->rule_method), exp) == 0) { res = 0; } else { log_warning("FAIL on line %d: expected '%s' got '%s' - user=%s db=%s addr=%s", linenr, exp, method2string(rule->rule_method), user, db, addr); res = 1; } } return res; } static void hba_test(void) { struct HBA *hba; FILE *f; char *ln = NULL; size_t lnbuf = 0; ssize_t len; int linenr; int nfailed = 0; hba = hba_load_rules("hba_test.rules", NULL); if (!hba) die("hbatest: did not find config"); f = fopen("hba_test.eval", "r"); if (!f) die("hbatest: cannot open eval"); for (linenr = 1; ; linenr++) { len = getline(&ln, &lnbuf, f); if (len < 0) break; if (len && ln[len-1] == '\n') ln[len-1] = 0; nfailed += hba_test_eval(hba, ln, linenr); } free(ln); fclose(f); hba_free(hba); if (nfailed) errx(1, "HBA test failures: %d", nfailed); else printf("HBA test OK\n"); } int main(void) { hba_test(); return 0; } pgbouncer-1.24.1/test/test_misc.py0000644000175000000000000003243214777762222014036 00000000000000import asyncio import re import time import psycopg import pytest from .utils import HAVE_IPV6_LOCALHOST, PG_MAJOR_VERSION, PKT_BUF_SIZE, WINDOWS def test_connect_query(bouncer): # The p8 database definition in test.ini has some GUC settings # in connect_query. Check that they get set. (The particular # settings don't matter; just use some that are easy to set # and read.) assert bouncer.sql_value("show enable_seqscan", dbname="p8") == "off" assert bouncer.sql_value("show enable_nestloop", dbname="p8") == "off" def test_fast_close(bouncer): with bouncer.cur(dbname="p3") as cur: cur.execute("select 1") bouncer.admin("set server_fast_close = 1") with bouncer.log_contains(r"closing because: database configuration changed"): bouncer.admin("reconnect p3") time.sleep(1) with pytest.raises( psycopg.OperationalError, match=r"database configuration changed|server closed the connection unexpectedly|Software caused connection abort", ): cur.execute("select 1") def test_track_extra_parameters(bouncer): # test.ini has track_extra_parameters set to a list of Postgres # parameters. Test that the parameters in the list in addition to the # default hardcoded list of parameters are cached per client. bouncer.admin(f"set pool_mode=transaction") test_set = { "intervalstyle": ["sql_standard", "postgres"], "standard_conforming_strings": ["ON", "OFF"], "timezone": ["'Europe/Amsterdam'", "'Europe/Rome'"], "datestyle": ["PostgreSQL,European", "ISO,US"], "application_name": ["client1", "client2"], } if not WINDOWS: test_set["client_encoding"] = ["LATIN1", "LATIN5"] test_expected = { "intervalstyle": ["sql_standard", "postgres"], "standard_conforming_strings": ["on", "off"], "timezone": ["Europe/Amsterdam", "Europe/Rome"], "datestyle": ["Postgres, DMY", "ISO, MDY"], "application_name": ["client1", "client2"], } if not WINDOWS: test_expected["client_encoding"] = ["LATIN1", "LATIN5"] with bouncer.cur(dbname="p1") as cur1: with bouncer.cur(dbname="p1") as cur2: for key in test_set: stmt1 = "SET " + key + " TO " + test_set[key][0] stmt2 = "SET " + key + " TO " + test_set[key][1] cur1.execute(stmt1) cur2.execute(stmt2) stmt = "SHOW " + key cur1.execute(stmt) cur2.execute(stmt) result1 = cur1.fetchone() assert result1[0] == test_expected[key][0] result2 = cur2.fetchone() assert result2[0] == test_expected[key][1] @pytest.mark.asyncio async def test_wait_close(bouncer): with bouncer.cur(dbname="p3") as cur: cur.execute("select 1") await bouncer.aadmin("reconnect p3") wait_close_task = bouncer.aadmin("wait_close p3") # We wait for 1 second to show that wait_close continues unless the # connection is closed. done, pending = await asyncio.wait([wait_close_task], timeout=1) assert done == set() assert pending == {wait_close_task} await wait_close_task def test_auto_database(bouncer): with bouncer.ini_path.open() as f: original = f.read() with bouncer.ini_path.open("w") as f: # uncomment the auto-database line f.write(re.sub(r"^;\*", "*", original, flags=re.MULTILINE)) bouncer.admin("reload") with bouncer.log_contains(r"registered new auto-database"): # p7 is not defined in test.ini bouncer.test(dbname="p7") # This test checks database specifications with host lists. The way # we test this here is to have a host list containing an IPv4 and an # IPv6 representation of localhost, and then we check the log that # both connections were made. Some CI environments don't have IPv6 # localhost configured. Therefore, this test is skipped by default # and needs to be enabled explicitly by setting HAVE_IPV6_LOCALHOST to # non-empty. @pytest.mark.asyncio @pytest.mark.skipif("not HAVE_IPV6_LOCALHOST") async def test_host_list(bouncer): with bouncer.log_contains(r"new connection to server \(from 127.0.0.1", times=1): with bouncer.log_contains(r"new connection to server \(from \[::1\]", times=1): await bouncer.asleep(1, dbname="hostlist1", times=2) # This is the same test as above, except it doesn't use any IPv6 # addresses. So we can't actually tell apart that two separate # connections are made. But the test is useful to get some test # coverage (valgrind etc.) of the host list code on systems without # IPv6 enabled. @pytest.mark.asyncio async def test_host_list_dummy(bouncer): with bouncer.log_contains(r"new connection to server \(from 127.0.0.1", times=2): await bouncer.asleep(1, dbname="hostlist2", times=2) def test_options_startup_param(bouncer): assert ( bouncer.sql_value("SHOW datestyle", options=" -c datestyle=German,\\ YMD") == "German, YMD" ) assert ( bouncer.sql_value( "SHOW datestyle", options="-c timezone=Portugal -c datestyle=German,\\ YMD", ) == "German, YMD" ) assert ( bouncer.sql_value( "SHOW timezone", options="-c timezone=Portugal -c datestyle=German,\\ YMD", ) == "Portugal" ) assert ( bouncer.sql_value( "SHOW timezone", options="-ctimezone=Portugal -cdatestyle=German,\\ YMD" ) == "Portugal" ) assert ( bouncer.sql_value( "SHOW timezone", options="--timezone=Portugal --datestyle=German,\\ YMD" ) == "Portugal" ) assert ( bouncer.sql_value( "SHOW timezone", options="-c t\\imezone=\\P\\o\\r\\t\\ugal -c dat\\estyle\\=\\Ge\\rman,\\ YMD", ) == "Portugal" ) # extra_float_digits is in ignore_startup_parameters so setting it has no # effect, and the default of 1 will still be used. assert ( bouncer.sql_value("SHOW extra_float_digits", options="-c extra_float_digits=2") == "1" ) with pytest.raises( psycopg.OperationalError, match="unsupported options startup parameter: only '-c config=val' and '--config=val' are allowed", ): bouncer.test(options="-d") with pytest.raises( psycopg.OperationalError, match="unsupported options startup parameter: only '-c config=val' and '--config=val' are allowed", ): bouncer.test(options="-c timezone") with pytest.raises( psycopg.OperationalError, match="unsupported startup parameter in options: enable_seqscan", ): bouncer.test(options="-c enable_seqscan=false") bouncer.admin("set ignore_startup_parameters = options") # Unsupported values should be ignored, so it shouldn't error but it should # have its default value instead. assert ( bouncer.sql_value( "SHOW enable_seqscan", options="-c enable_seqscan=false", ) == "on" ) # Even though we have options in ignore_startup_parameters, we still parse # and configure any values in it that we support assert ( bouncer.sql_value( "SHOW timezone", options="-ctimezone=Portugal -cdatestyle=German,\\ YMD" ) == "Portugal" ) def test_startup_packet_larger_than_pktbuf(bouncer): long_string = "1" * PKT_BUF_SIZE bouncer.test(options=f"-c extra_float_digits={long_string}") def test_empty_application_name(bouncer): with bouncer.cur(dbname="p1", application_name="") as cur: assert cur.execute("SHOW application_name").fetchone()[0] == "" cur.execute("SET application_name = test") assert cur.execute("SHOW application_name").fetchone()[0] == "test" with bouncer.cur(dbname="p1", application_name="") as cur: assert cur.execute("SHOW application_name").fetchone()[0] == "" cur.execute("SET application_name = test") assert cur.execute("SHOW application_name").fetchone()[0] == "test" def test_equivalent_startup_param(bouncer): bouncer.admin("set verbose=2") canonical_expected_times = 1 if PG_MAJOR_VERSION >= 14 else 0 with bouncer.cur(options="-c DateStyle=ISO") as cur: with bouncer.log_contains( "varcache_apply: .*SET DateStyle='ISO'", times=1 ), bouncer.log_contains( "varcache_set_canonical: setting DateStyle to its canonical version ISO -> ISO, MDY", times=canonical_expected_times, ): cur.execute("SELECT 1") cur.execute("SELECT 1") @pytest.mark.skipif("WINDOWS", reason="Windows doesn't support sending SIGTERM") async def test_repeated_sigterm(bouncer): with bouncer.cur() as cur: cur.execute("SELECT 1") bouncer.sigterm() # Single sigterm should wait for clients time.sleep(1) cur.execute("SELECT 1") assert bouncer.running() # Second sigterm should cause fast exit bouncer.sigterm() await bouncer.wait_for_exit() with pytest.raises( psycopg.OperationalError, match="database removed|server closed the connection unexpectedly", ): cur.execute("SELECT 1") assert not bouncer.running() @pytest.mark.skipif("WINDOWS", reason="Windows doesn't support sending SIGINT") async def test_repeated_sigint(bouncer): bouncer.admin(f"set pool_mode=session") with bouncer.cur() as cur: cur.execute("SELECT 1") bouncer.sigint() # Single sigint should wait for servers to be released time.sleep(1) cur.execute("SELECT 1") assert bouncer.running() # But new clients should be rejected, because we stopped listening for # new connections. with pytest.raises(psycopg.OperationalError, match="Connection refused"): bouncer.test() # Second sigint should cause fast exit bouncer.sigint() await bouncer.wait_for_exit() with pytest.raises( psycopg.OperationalError, match="database removed|server closed the connection unexpectedly", ): cur.execute("SELECT 1") assert not bouncer.running() def test_newly_paused_client_during_wait_for_servers_shutdown(bouncer): bouncer.admin(f"set pool_mode=transaction") with bouncer.transaction() as cur1, bouncer.cur() as cur2: cur1.execute("SELECT 1") bouncer.admin("SHUTDOWN WAIT_FOR_SERVERS") # Still in the same transaction, so this should work cur1.execute("SELECT 1") # New transaction so this should fail with bouncer.log_contains(r"closing because: server shutting down"): with pytest.raises(psycopg.OperationalError): cur2.execute("SELECT 1") async def test_already_paused_client_during_wait_for_servers_shutdown(bouncer): bouncer.admin(f"set pool_mode=transaction") bouncer.admin(f"set default_pool_size=1") bouncer.default_db = "p1" with bouncer.transaction() as cur1: conn2 = await bouncer.aconn() cur2 = conn2.cursor() cur1.execute("SELECT 1") # start the request before the shutdown task = asyncio.ensure_future(cur2.execute("SELECT 1")) # We wait so that the client goes to CL_WAITING state done, pending = await asyncio.wait([task], timeout=3) assert done == set() assert pending == {task} bouncer.admin("SHUTDOWN WAIT_FOR_SERVERS") # Still in the same transaction, so this should work cur1.execute("SELECT 1") # New transaction so this should fail with bouncer.log_contains(r"closing because: server shutting down"): with pytest.raises(psycopg.OperationalError): await task def test_resume_during_shutdown(bouncer): with bouncer.cur() as cur, bouncer.admin_runner.cur() as admin_cur: cur.execute("SELECT 1") bouncer.admin("SHUTDOWN WAIT_FOR_CLIENTS") with pytest.raises( psycopg.errors.ProtocolViolation, match="pooler is shutting down" ): admin_cur.execute("RESUME") def test_sigusr2_during_shutdown(bouncer): with bouncer.cur() as cur: cur.execute("SELECT 1") bouncer.admin("SHUTDOWN WAIT_FOR_CLIENTS") if not WINDOWS: with bouncer.log_contains(r"got SIGUSR2 while shutting down, ignoring"): bouncer.sigusr2() time.sleep(1) def test_issue_1104(bouncer): # regression test for GitHub issue #1104 [PgCredentials objects are freed incorrectly] for i in range(1, 15): config = """ [databases] """ for j in range(1, 10 * i): config += f""" testdb_{i}_{j} = host={bouncer.pg.host} port={bouncer.pg.port} user=dummy_user_{i}_{j} """ config += f""" [pgbouncer] listen_addr = {bouncer.host} listen_port = {bouncer.port} auth_type = trust auth_file = {bouncer.auth_path} admin_users = pgbouncer logfile = {bouncer.log_path} """ with bouncer.run_with_config(config): bouncer.admin("RELOAD") pgbouncer-1.24.1/test/test_copy.py0000644000175000000000000002432314777762222014055 00000000000000import time import pytest from psycopg import pq from .utils import LIBPQ_SUPPORTS_PIPELINING def test_copy_stdin_success_simple(bouncer): with bouncer.conn() as conn: conn.pgconn.send_query(f"COPY test_copy(i) FROM STDIN".encode()) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN conn.pgconn.put_copy_data(b"1\n") conn.pgconn.put_copy_end() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None def test_copy_stdin_error_before_copy_done_simple(bouncer): with bouncer.conn() as conn: conn.pgconn.send_query(f"COPY test_copy(i) FROM STDIN".encode()) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN # Send bad row conn.pgconn.put_copy_data(b"\n") # Flush and wait a bit so PgBouncer can receive the error conn.pgconn.flush() time.sleep(1) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN conn.pgconn.put_copy_end() assert conn.pgconn.get_result().status == pq.ExecStatus.FATAL_ERROR assert conn.pgconn.get_result() is None def test_copy_stdin_error_after_copy_done_simple(bouncer): with bouncer.conn() as conn: conn.pgconn.send_query(f"COPY test_copy(i) FROM STDIN".encode()) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN # Send bad row conn.pgconn.put_copy_data(b"\n") conn.pgconn.put_copy_end() assert conn.pgconn.get_result().status == pq.ExecStatus.FATAL_ERROR assert conn.pgconn.get_result() is None def test_copy_stdout_simple(bouncer): bouncer.sql("TRUNCATE test_copy") bouncer.sql("INSERT INTO test_copy VALUES (1), (2)") with bouncer.conn() as conn: conn.pgconn.send_query( f"COPY (SELECT i FROM test_copy ORDER BY i) TO STDOUT (FORMAT TEXT)".encode() ) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_OUT assert conn.pgconn.get_copy_data(0) == (2, b"1\n") assert conn.pgconn.get_copy_data(0) == (2, b"2\n") assert conn.pgconn.get_copy_data(0) == (-1, b"") assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdin_success_extended(bouncer): with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_query_params(f"COPY test_copy(i) FROM STDIN".encode(), []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN conn.pgconn.put_copy_data(b"1\n") conn.pgconn.put_copy_end() conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdin_error_before_copy_done_extended(bouncer): with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_query_params(f"COPY test_copy(i) FROM STDIN".encode(), []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN # Send bad row conn.pgconn.put_copy_data(b"\n") # Flush and wait a bit so PgBouncer can receive the error conn.pgconn.flush() time.sleep(1) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN conn.pgconn.put_copy_end() conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.FATAL_ERROR assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdin_error_after_copy_done_extended(bouncer): with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_query_params(f"COPY test_copy(i) FROM STDIN".encode(), []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN # Send bad row conn.pgconn.put_copy_data(b"\n") conn.pgconn.put_copy_end() conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.FATAL_ERROR assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdout_extended(bouncer): bouncer.sql("TRUNCATE test_copy") bouncer.sql("INSERT INTO test_copy VALUES (1), (2)") with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_query_params( f"COPY (SELECT i FROM test_copy ORDER BY i) TO STDOUT (FORMAT TEXT)".encode(), [], ) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_OUT assert conn.pgconn.get_copy_data(0) == (2, b"1\n") assert conn.pgconn.get_copy_data(0) == (2, b"2\n") assert conn.pgconn.get_copy_data(0) == (-1, b"") assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdin_success_prepared(bouncer): bouncer.admin(f"set max_prepared_statements=100") with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_prepare(b"p1", f"COPY test_copy(i) FROM STDIN".encode()) conn.pgconn.send_query_prepared(b"p1", []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN conn.pgconn.put_copy_data(b"1\n") conn.pgconn.put_copy_end() conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdin_error_before_copy_done_prepared(bouncer): bouncer.admin(f"set max_prepared_statements=100") with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_prepare(b"p1", f"COPY test_copy(i) FROM STDIN".encode()) conn.pgconn.send_query_prepared(b"p1", []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN # Send bad row conn.pgconn.put_copy_data(b"\n") # Flush and wait a bit so PgBouncer can receive the error conn.pgconn.flush() time.sleep(1) assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN conn.pgconn.put_copy_end() conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.FATAL_ERROR assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdin_error_after_copy_done_prepared(bouncer): bouncer.admin(f"set max_prepared_statements=100") with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_prepare(b"p1", f"COPY test_copy(i) FROM STDIN".encode()) conn.pgconn.send_query_prepared(b"p1", []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_IN # Send bad row conn.pgconn.put_copy_data(b"\n") conn.pgconn.put_copy_end() conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.FATAL_ERROR assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() @pytest.mark.skipif("not LIBPQ_SUPPORTS_PIPELINING") def test_copy_stdout_prepared(bouncer): bouncer.admin(f"set max_prepared_statements=100") bouncer.sql("TRUNCATE test_copy") bouncer.sql("INSERT INTO test_copy VALUES (1), (2)") with bouncer.conn() as conn: conn.pgconn.enter_pipeline_mode() conn.pgconn.send_prepare( b"p1", f"COPY (SELECT i FROM test_copy ORDER BY i) TO STDOUT (FORMAT TEXT)".encode(), ) conn.pgconn.send_query_prepared(b"p1", []) conn.pgconn.pipeline_sync() assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.COPY_OUT assert conn.pgconn.get_copy_data(0) == (2, b"1\n") assert conn.pgconn.get_copy_data(0) == (2, b"2\n") assert conn.pgconn.get_copy_data(0) == (-1, b"") assert conn.pgconn.get_result().status == pq.ExecStatus.COMMAND_OK assert conn.pgconn.get_result() is None assert conn.pgconn.get_result().status == pq.ExecStatus.PIPELINE_SYNC conn.pgconn.exit_pipeline_mode() pgbouncer-1.24.1/test/Makefile0000644000175000000000000000237414777762222013134 00000000000000 PG_CPPFLAGS = -I$(shell pg_config --includedir) PG_LIBS = -lpq PG_LDFLAGS = -L$(shell pg_config --libdir) USUAL_DIR = ../lib SUBLOC = test DIST_SUBDIRS = ssl EXTRA_DIST = conntest.sh ctest6000.ini ctest7000.ini run-conntest.sh \ hba_test.eval hba_test.rules Makefile pgident.conf\ test.ini stress.py userlist.txt pgbouncer_hba.conf \ __init__.py conftest.py utils.py \ test_admin.py test_auth.py test_cancel.py test_copy.py test_limits.py \ test_load_balance_hosts.py test_misc.py test_no_database.py \ test_no_user.py test_operations.py test_peering.py test_prepared.py \ test_ssl.py test_timeouts.py test_replication.py UTHASH = ../uthash noinst_PROGRAMS = hba_test hba_test_CPPFLAGS = -I../include $(LIBEVENT_CFLAGS) -I$(UTHASH)/src hba_test_LDADD = $(LIBEVENT_LIBS) $(TLS_LIBS) hba_test_CFLAGS = -O0 hba_test_SOURCES = hba_test.c ../src/hba.c ../src/util.c hba_test_EMBED_LIBUSUAL = 1 EXTRA_PROGRAMS = asynctest asynctest_CPPFLAGS = -I../include $(PG_CPPFLAGS) $(LIBEVENT_CFLAGS) asynctest_LDFLAGS = $(PG_LDFLAGS) asynctest_LDADD = $(PG_LIBS) $(LIBEVENT_LIBS) asynctest_SOURCES = asynctest.c asynctest_EMBED_LIBUSUAL = 1 AM_FEATURES = libusual include ../config.mak include ../lib/mk/antimake.mk check: all ./hba_test pgbouncer-1.24.1/test/asynctest.c0000644000175000000000000002640414777762222013655 00000000000000/* * Things to test: * - Conn per query * - show tx * - long tx * - variable-size query */ #ifdef WIN32 #undef strerror #undef main #endif #include #include #include #include #include #include #include #include #include #include static char *simple_query = "select 1"; static struct event_base *evbase; typedef struct DbConn { struct List head; const char *connstr; struct event ev; PGconn *con; bool logged_in; /* time_t connect_time; */ int query_count; /* const char *query; */ int _arglen; } DbConn; #define QT_SIMPLE 1 #define QT_BIGDATA 2 #define QT_SLEEP 4 static unsigned QueryTypes = 0; static uint64_t LoginOkCount = 0; static uint64_t LoginFailedCount = 0; static uint64_t SqlErrorCount = 0; static uint64_t QueryCount = 0; static char *bulk_data; static int bulk_data_max = 128*1024; /* power of 2 */ static int verbose = 0; static int throttle_connects = 0; static int throttle_queries = 0; static int per_conn_queries = 1; static STATLIST(idle_list); static STATLIST(active_list); /* * utility functions */ /* fill mem with random junk */ static void init_bulk_data(void) { int i; bulk_data = malloc(bulk_data_max + 1); for (i = 0; i < bulk_data_max; i++) { bulk_data[i] = 'a' + (i % 26); } bulk_data[i] = 0; } static DbConn *new_db(const char *connstr) { DbConn *db = malloc(sizeof(*db)); memset(db, 0, sizeof(*db)); list_init(&db->head); db->connstr = connstr; return db; } static void set_idle(DbConn *db) { statlist_remove(&active_list, &db->head); statlist_append(&idle_list, &db->head); log_debug("%p: set_idle", db); } static void set_active(DbConn *db) { statlist_remove(&idle_list, &db->head); statlist_append(&active_list, &db->head); log_debug("%p: set_active", db); } static void wait_event(DbConn *db, short ev, event_callback_fn fn) { event_assign(&db->ev, evbase, PQsocket(db->con), ev, fn, db); if (event_add(&db->ev, NULL) < 0) fatal_perror("event_add"); } _PRINTF(3, 4) static void disconnect(DbConn *db, bool is_err, const char *reason, ...) { char buf[1024]; va_list ap; if (is_err) { if (db->logged_in) SqlErrorCount++; else LoginFailedCount++; } if (db->con) { va_start(ap, reason); vsnprintf(buf, sizeof(buf), reason, ap); va_end(ap); log_debug("disconnect because: %s", buf); PQfinish(db->con); db->con = NULL; db->logged_in = false; set_idle(db); } } /* some error happened */ static void conn_error(DbConn *db, const char *desc) { static int ecount = 0; if (db->con) { if (ecount++ < 3) printf("\r%s (arglen=%d)\n", PQerrorMessage(db->con), db->_arglen); disconnect(db, true, "%s: %s", desc, PQerrorMessage(db->con)); } else { printf("random error: %s\n", desc); exit(1); } } /* * Connection has a resultset available, fetch it. * * Returns true if there may be more results coming, * false if all done. */ static bool another_result(DbConn *db) { PGresult *res; /* got one */ res = PQgetResult(db->con); if (res == NULL) { QueryCount++; set_idle(db); return false; } switch (PQresultStatus(res)) { case PGRES_TUPLES_OK: /* TODO: check result */ if (db->_arglen > 0) { int curlen = strlen(PQgetvalue(res, 0, 0)); if (curlen != db->_arglen) { printf("result does not match: sent=%d got=%d\n", db->_arglen, curlen); } } /* fallthrough */ case PGRES_COMMAND_OK: PQclear(res); break; default: PQclear(res); conn_error(db, "weird result"); return false; } return true; } /* * Called when select() told that conn is avail for reading/writing. * * It should call postgres handlers and then change state if needed. */ static void result_cb(int sock, short flags, void *arg) { DbConn *db = arg; int res; res = PQconsumeInput(db->con); if (res == 0) { conn_error(db, "PQconsumeInput"); return; } /* loop until PQgetResult returns NULL */ while (1) { /* if PQisBusy, then incomplete result */ if (PQisBusy(db->con)) { wait_event(db, EV_READ, result_cb); break; } /* got one */ if (!another_result(db)) break; } } static void send_cb(int sock, short flags, void *arg) { int res; DbConn *db = arg; res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, "PQflush"); } static int send_query_bigdata(DbConn *db) { const char *values[1]; int lengths[1]; int fmts[1]; int arglen; char *q = "select $1::text"; arglen = random() % bulk_data_max; db->_arglen = arglen; values[0] = bulk_data + bulk_data_max - arglen; lengths[0] = arglen; fmts[0] = 1; return PQsendQueryParams(db->con, q, 1, NULL, values, lengths, fmts, 1); } static int send_query_sleep(DbConn *db) { const char *q = "select pg_sleep(0.2)"; return PQsendQueryParams(db->con, q, 0, NULL, NULL, NULL, NULL, 0); } static int send_query_simple(DbConn *db) { const char *q = simple_query; return PQsendQueryParams(db->con, q, 0, NULL, NULL, NULL, NULL, 0); } /* send the query to server connection */ static void send_query(DbConn *db) { int res; if (db->query_count >= per_conn_queries) { disconnect(db, false, "query count full"); return; } db->query_count++; /* send query */ if (QueryTypes & QT_SLEEP) { res = send_query_sleep(db); } else if (QueryTypes & QT_BIGDATA) { res = send_query_bigdata(db); } else { res = send_query_simple(db); } if (!res) { conn_error(db, "PQsendQueryParams"); return; } /* flush it down */ res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, "PQflush"); } static void connect_cb(int sock, short flags, void *arg) { DbConn *db = arg; PostgresPollingStatusType poll_res; poll_res = PQconnectPoll(db->con); switch (poll_res) { case PGRES_POLLING_WRITING: wait_event(db, EV_WRITE, connect_cb); break; case PGRES_POLLING_READING: wait_event(db, EV_READ, connect_cb); break; case PGRES_POLLING_OK: log_debug("login ok: fd=%d", PQsocket(db->con)); LoginOkCount++; db->logged_in = true; send_query(db); break; default: conn_error(db, "PQconnectPoll"); } } static void launch_connect(DbConn *db) { /* launch new connection */ db->logged_in = false; db->query_count = 0; db->con = PQconnectStart(db->connstr); if (db->con == NULL) { log_error("PQconnectStart: no mem"); exit(1); } if (PQstatus(db->con) == CONNECTION_BAD) { conn_error(db, "PQconnectStart"); return; } wait_event(db, EV_WRITE, connect_cb); } #define ACT_ONCE 10 static void handle_idle(void) { DbConn *db; struct List *item, *tmp; int allow_connects = 100000; int allow_queries = 100000; static usec_t startup_time = 0; usec_t now = get_cached_time(); usec_t diff; int once; if (startup_time == 0) startup_time = get_cached_time(); diff = now - startup_time; if (diff < USEC) diff = USEC; if (throttle_connects > 0) { allow_connects = throttle_connects - LoginOkCount * USEC / diff; once = throttle_connects / ACT_ONCE; if (!once) once = 1; if (once < allow_connects) allow_connects = once; } if (throttle_queries > 0) { allow_queries = throttle_queries - QueryCount * USEC / diff; once = throttle_connects / ACT_ONCE; if (!once) once = 1; if (once < allow_connects) allow_connects = once; } statlist_for_each_safe(item, &idle_list, tmp) { db = container_of(item, DbConn, head); if (db->con && allow_queries > 0) { set_active(db); send_query(db); allow_queries--; } else if (allow_connects > 0) { set_active(db); launch_connect(db); allow_connects--; } } } static void run_stats(int fd, short ev, void *arg) { static struct event ev_stats; struct timeval period = { 5, 0 }; static usec_t last_time = 0; static uint64_t last_query_count = 0; static uint64_t last_login_failed_count = 0; static uint64_t last_login_ok_count = 0; static uint64_t last_sql_error_count = 0; double time_diff, qcount_diff, loginerr_diff, loginok_diff, sqlerr_diff; usec_t now = get_cached_time(); time_diff = now - last_time; if (last_time && time_diff) { qcount_diff = QueryCount - last_query_count; loginerr_diff = LoginFailedCount - last_login_failed_count; sqlerr_diff = SqlErrorCount - last_sql_error_count; loginok_diff = LoginOkCount - last_login_ok_count; if (verbose == 0) { printf(">> loginok,loginerr,sqlerr,qcount: %6.1f / %6.1f / %6.1f / %6.1f active/idle: %3d / %3d \r", USEC * loginok_diff / time_diff, USEC * loginerr_diff / time_diff, USEC * sqlerr_diff / time_diff, USEC * qcount_diff / time_diff, statlist_count(&active_list), statlist_count(&idle_list)); fflush(stdout); } } if (!last_time) evtimer_assign(&ev_stats, evbase, run_stats, NULL); if (evtimer_add(&ev_stats, &period) < 0) fatal_perror("evtimer_add"); last_query_count = QueryCount; last_login_failed_count = LoginFailedCount; last_sql_error_count = SqlErrorCount; last_login_ok_count = LoginOkCount; last_time = now; } static const char usage_str [] = "usage: asynctest [-d connstr][-n numconn][-s seed][-t ][-C maxconn][-Q maxquery][-q perconnq]\n" " -d connstr libpq connect string\n" " -n num number of connections\n" " -s seed random number seed\n" " -t type of queries query type, see below\n" " -C maxcps max number of connects per sec\n" " -Q maxqps max number of queries per sec\n" " -q num queries per connection (default 1)\n" " -S sql set simple query\n" "accepted query types:\n" " B - bigdata\n" " S - sleep occasionally\n" " 1 - simple 'select 1'\n"; int main(int argc, char *argv[]) { int i, c; DbConn *db; unsigned seed = time(NULL) ^ getpid(); char *cstr = ""; int numcon = 50; #ifdef WIN32 int wsresult; WSADATA wsaData; #endif while ((c = getopt(argc, argv, "S:d:n:s:t:hvC:Q:q:")) != EOF) { switch (c) { default: case 'h': printf("%s", usage_str); return 0; case 'S': simple_query = optarg; break; case 'd': cstr = optarg; break; case 'C': throttle_connects = atoi(optarg); break; case 'Q': throttle_queries = atoi(optarg); break; case 'n': numcon = atoi(optarg); break; case 's': seed = atoi(optarg); break; case 'v': verbose++; break; case 'q': per_conn_queries = atoi(optarg); break; case 't': for (i = 0; optarg[i]; i++) { switch (optarg[i]) { case 'B': QueryTypes = QT_BIGDATA; break; case 'S': QueryTypes = QT_SLEEP; break; case '1': QueryTypes = QT_SIMPLE; break; default: log_error("bad type"); break; } } } } #ifdef WIN32 wsresult = WSAStartup(MAKEWORD(2,0),&wsaData); if (wsresult != 0) fatal("cannot start the network subsystem: -%d", wsresult); #endif if (throttle_connects < 0 || throttle_queries < 0 || numcon < 0) fatal("invalid parameter"); if (QueryTypes == 0) QueryTypes = QT_SIMPLE; printf("using seed: %u\n", seed); srandom(seed); init_bulk_data(); for (i = 0; i < numcon; i++) { db = new_db(cstr); statlist_append(&idle_list, &db->head); } evbase = event_base_new(); run_stats(0, 0, NULL); printf("running..\n"); while (1) { handle_idle(); reset_time_cache(); if (event_base_loop(evbase, EVLOOP_ONCE) < 0) log_error("event_loop: %s", strerror(errno)); } return 0; } pgbouncer-1.24.1/test/ctest7000.ini0000644000175000000000000000140714777762222013622 00000000000000;; database name = connect string [databases] ; redirect bardb to bazdb on localhost conntest = host=127.0.0.1 port=5432 dbname=marko password=kama ;; Configuration section [pgbouncer] logfile = ctest7000.log pidfile = ctest7000.pid listen_addr = 127.0.0.1 listen_port = 7000 unix_socket_dir = /tmp auth_type = md5 auth_file = userlist.txt admin_users = marko stats_users = stats pool_mode = transaction server_reset_query = reset all server_check_query = select 1 server_check_delay = 2 max_client_conn = 5000 default_pool_size = 20 log_connections = 0 log_disconnections = 0 log_pooler_errors = 0 server_lifetime = 30 server_idle_timeout = 3 server_connect_timeout = 2 server_login_retry = 5 query_timeout = 5 client_idle_timeout = 10 client_login_timeout = 50 pgbouncer-1.24.1/config.mak.in0000644000175000000000000000310414777762222013051 00000000000000PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PORTNAME = @PORTNAME@ EXEEXT = @EXEEXT@ HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ CC = @CC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ WFLAGS = @WFLAGS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ AR = @AR@ ARFLAGS = @ARFLAGS@ RANLIB = @RANLIB@ LIBTOOL = @LIBTOOL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ MKDIR_P = @MKDIR_P@ SED = @SED@ AWK = @AWK@ GREP = @GREP@ EGREP = @EGREP@ STRIP = @STRIP@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datarootdir = @datarootdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ docdir = @docdir@ mandir = @mandir@ libdir = @libdir@ localedir = @localedir@ pkgdatadir = @pkgdatadir@ pkgconfigdir = @pkgconfigdir@ abs_top_srcdir ?= @abs_top_srcdir@ abs_top_builddir ?= @abs_top_builddir@ nosub_top_srcdir ?= @top_srcdir@ nosub_top_builddir ?= @top_builddir@ CARES_CFLAGS = @CARES_CFLAGS@ CARES_LIBS = @CARES_LIBS@ LIBEVENT_CFLAGS = @LIBEVENT_CFLAGS@ LIBEVENT_LIBS = @LIBEVENT_LIBS@ TLS_CPPFLAGS = @TLS_CPPFLAGS@ TLS_LDFLAGS = @TLS_LDFLAGS@ TLS_LIBS = @TLS_LIBS@ PANDOC = @PANDOC@ PYTHON = @PYTHON@ DLLWRAP = @DLLWRAP@ DLLTOOL = @DLLTOOL@ WINDRES = @WINDRES@ enable_debug = @enable_debug@ tls_support = @tls_support@ host_cpu = @host_cpu@ pgbouncer-1.24.1/install-sh0000755000175000017500000003577614777762315012555 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: pgbouncer-1.24.1/COPYRIGHT0000644000175000000000000000147714777762222012013 00000000000000PgBouncer - Lightweight connection pooler for PostgreSQL. ISC License Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. pgbouncer-1.24.1/win32/0000755000000000000000000000000014777762567011530 500000000000000pgbouncer-1.24.1/win32/eventmsg.rc0000644000175000000000000000004714777762222013630 00000000000000LANGUAGE 0x9,0x1 1 11 "MSG00001.bin" pgbouncer-1.24.1/win32/pgbevent.c0000644000175000000000000000512414777762222013431 00000000000000/*------------------------------------------------------------------------- * pgbevent.c * Defines the entry point for pgbevent dll. * The DLL defines event source for pgbouncer tools *------------------------------------------------------------------------- */ #define WIN32_LEAN_AND_MEAN #include #include #include #define APP_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\pgbouncer" /* Global variables */ static HANDLE g_module = NULL; /* hModule of DLL */ /* Prototypes */ STDAPI DllRegisterServer(void); STDAPI DllUnregisterServer(void); BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); /* * DllRegisterServer --- Instructs DLL to create its registry entries */ STDAPI DllRegisterServer(void) { HKEY key; DWORD data; char buffer[_MAX_PATH]; /* Set the name of DLL full path name. */ if (!GetModuleFileName((HMODULE)g_module, buffer, sizeof(buffer))) { MessageBox(NULL, "Could not retrieve DLL filename", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } /* * Add our source name as a subkey under the Application key in * the EventLog registry key. */ if (RegCreateKey(HKEY_LOCAL_MACHINE, APP_KEY, &key)) { MessageBox(NULL, "Could not create the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } /* Add the name to the EventMessageFile subkey. */ if (RegSetValueEx(key, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)buffer, strlen(buffer) + 1)) { MessageBox(NULL, "Could not set the event message file.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } /* Set the supported event types in the TypesSupported subkey. */ data = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; if (RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD))) { MessageBox(NULL, "Could not set the supported types.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } RegCloseKey(key); return S_OK; } /* * DllUnregisterServer --- Instructs DLL to remove only those entries created through DllRegisterServer */ STDAPI DllUnregisterServer(void) { if (RegDeleteKey(HKEY_LOCAL_MACHINE, APP_KEY)) { MessageBox(NULL, "Could not delete the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } return S_OK; } /* * DllMain --- is an optional entry point into a DLL. */ BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if (ul_reason_for_call == DLL_PROCESS_ATTACH) g_module = hModule; return TRUE; } pgbouncer-1.24.1/win32/win32support.h0000644000175000000000000000013614777762222014221 00000000000000/* redirect main() */ #define main(a,b) real_main(a,b) int real_main(int argc, char *argv[]); pgbouncer-1.24.1/win32/eventmsg.mc0000644000175000000000000000007514777762222013624 00000000000000MessageId=0 SymbolicName=MSG_PGBOUNCER Language=English %1 . pgbouncer-1.24.1/win32/MSG00001.bin0000644000175000000000000000004014777762222013144 00000000000000%1 pgbouncer-1.24.1/win32/win32ver.rc0000644000175000000000000000145014777762222013456 00000000000000#include #include "usual/config.h" // https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource VS_VERSION_INFO VERSIONINFO FILEVERSION PACKAGE_VERSION_4B PRODUCTVERSION PACKAGE_VERSION_4B FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0x0L FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" // U.S. English, Unicode BEGIN VALUE "CompanyName", "PgBouncer developers" VALUE "FileDescription", "connection pooler for PostgreSQL" VALUE "FileVersion", PACKAGE_VERSION VALUE "ProductName", PACKAGE_NAME VALUE "ProductVersion", PACKAGE_VERSION END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1200 // U.S. English, Unicode END END pgbouncer-1.24.1/win32/win32support.c0000644000175000000000000001676514777762222014233 00000000000000/*------------------------------------------------------------------------- * win32service.c * * Windows service integration and eventlog * * Copyright (c) 2005, PostgreSQL Global Development Group * Authors: Magnus Hagander, Hiroshi Saito, Marko Kreen *------------------------------------------------------------------------- */ #include "bouncer.h" #include "win32support.h" #if defined(UNICODE) || defined(_UNICODE) #error This code does not support wide characters. #endif /* Globals for service control */ static SERVICE_STATUS_HANDLE hStatus = 0; static SERVICE_STATUS svcStatus = { .dwServiceType = SERVICE_WIN32_OWN_PROCESS, .dwControlsAccepted = 0, .dwWin32ExitCode = NO_ERROR, .dwCheckPoint = 0, .dwWaitHint = 0, .dwCurrentState = SERVICE_START_PENDING, }; /* Event source name for ReportEvent. * * Also used as placeholder for service handling API's, but it is ignored * because our service is defined as WIN32_OWN_PROCESS. */ static char *servicename = "pgbouncer"; static char *service_username = NULL; static char *service_password = NULL; static char *serviceDescription = "Lightweight connection pooler for PostgreSQL."; static int exec_real_main(int argc, char *argv[]) { /* win32 stdio seems to be fully buffered by default */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); /* call actual main() */ return real_main(argc, argv); } /* Set the current service status */ static void win32_setservicestatus(DWORD state) { svcStatus.dwCurrentState = state; switch (state) { case SERVICE_START_PENDING: case SERVICE_STOP_PENDING: svcStatus.dwControlsAccepted = 0; svcStatus.dwWaitHint = 5000; break; default: svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; svcStatus.dwWaitHint = 0; } SetServiceStatus(hStatus, &svcStatus); } /* * Handle any events sent by the service control manager * NOTE! Events are sent on a different thread! And it's * not a pthreads thread, so avoid calling anything that * may use pthreads - like pgbouncer_log() */ static void WINAPI win32_servicehandler(DWORD request) { switch (request) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: win32_setservicestatus(SERVICE_STOP_PENDING); cf_shutdown = SHUTDOWN_IMMEDIATE; break; case SERVICE_CONTROL_INTERROGATE: SetServiceStatus(hStatus, &svcStatus); break; } } /* notify control thread about stop */ static void win32_service_cleanup(void) { if (hStatus) win32_setservicestatus(SERVICE_STOPPED); hStatus = 0; /* may get called twice from atexit */ } /* * Entrypoint for actual service work. * * Service is set-up and then actual main() is called. */ static void WINAPI win32_servicemain(DWORD argc, LPSTR *argv) { int new_argc = 2; char *new_argv[] = { servicename, cf_config_file, NULL }; /* register control request handler */ hStatus = RegisterServiceCtrlHandler(servicename, win32_servicehandler); if (hStatus == 0) die("could not connect to service control handler: %s", strerror(GetLastError())); /* Tell SCM we are running before we make any API calls */ win32_setservicestatus(SERVICE_RUNNING); /* register with system atexit(), in case somebody calls exit() */ atexit(win32_service_cleanup); /* Execute actual main() */ exec_real_main(new_argc, new_argv); win32_service_cleanup(); } /* Start running as a service */ static void win32_servicestart(void) { SERVICE_TABLE_ENTRY st[] = { {servicename, win32_servicemain}, {NULL, NULL} }; if (StartServiceCtrlDispatcher(st) == 0) { fprintf(stderr, "could not start service control dispatcher: %s\n", strerror(GetLastError())); exit(1); } } /* Open Service Control Manager */ static SC_HANDLE openSCM(void) { SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!manager) { fprintf(stderr, "Failed to open service control manager: %s\n", strerror(GetLastError())); exit(1); } return manager; } /* Full path to current config file. */ static const char *get_config_fullpath(void) { DWORD r; static char buf[PATH_MAX]; r = GetFullPathName(cf_config_file, sizeof(buf), buf, NULL); if (r == 0 || r >= sizeof(buf)) { fprintf(stderr, "Failed to get full pathname for '%s': %s\n", cf_config_file, strerror(GetLastError())); exit(1); } return buf; } /* Register a service with the specified name with the local service control manager */ static void RegisterService(void) { char self[1024]; char cmdline[2048]; const char *config_fn = get_config_fullpath(); SC_HANDLE manager; SC_HANDLE service; SERVICE_DESCRIPTION sd; DWORD r; r = GetModuleFileName(NULL, self, sizeof(self)); if (!r || r >= sizeof(self)) { fprintf(stderr, "Failed to determine path name: %s\n", strerror(GetLastError())); exit(1); } snprintf(cmdline, sizeof(cmdline), "%s --service \"%s\"", self, config_fn); manager = openSCM(); service = CreateService(manager, cf_jobname, cf_jobname, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdline, NULL, NULL, "RPCSS\0", service_username, service_password); if (!service) { fprintf(stderr, "Failed to create service: %s\n", strerror(GetLastError())); exit(1); } /* explain the service purpose */ sd.lpDescription = serviceDescription; ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd); CloseServiceHandle(service); CloseServiceHandle(manager); printf("Service registered.\n"); if (service_username == NULL) { printf("\nWARNING! Service is registered to run as Local System. You are\n"); printf("encouraged to change this to a low privilege account to increase\n"); printf("system security. (Eg. NT AUTHORITY\\Local Service)\n"); } } /* Remove a service with the specified name from the local service control manager */ static void UnRegisterService(void) { SC_HANDLE manager; SC_HANDLE service; manager = openSCM(); service = OpenService(manager, cf_jobname, SC_MANAGER_ALL_ACCESS); if (!service) { fprintf(stderr, "Failed to open service: %s\n", strerror(GetLastError())); exit(1); } if (!DeleteService(service)) { fprintf(stderr, "Failed to delete service: %s\n", strerror(GetLastError())); exit(1); } CloseServiceHandle(service); CloseServiceHandle(manager); printf("Service removed.\n"); } /* config loader for service register/unregister */ static void win32_load_config(char *conf) { cf_config_file = conf; init_objects(); load_config(); } /* * Wrapper around actual main() that handles win32 hacks. */ #undef main int main(int argc, char *argv[]) { WSADATA wsaData; /* initialize socket subsystem */ if (WSAStartup(MAKEWORD(2,0), &wsaData)) die("could not start the network subsystem"); /* service cmdline */ if (argc >= 3) { if (strcmp(argv[1], "--service") == 0 || strcmp(argv[1], "-service") == 0) { cf_quiet = 1; cf_config_file = argv[2]; win32_servicestart(); return 0; } if (strcmp(argv[1], "--regservice") == 0 || strcmp(argv[1], "-regservice") == 0) { int i; win32_load_config(argv[2]); for (i = 3; i < argc; i++) { if (strcmp(argv[i], "-U") == 0 && i + 1 < argc) { service_username = argv[++i]; } else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) { service_password = argv[++i]; } else { fprintf(stderr, "unknown arg: %s\n", argv[i]); fprintf(stderr, "Try \"%s --help\" for more information.\n", argv[0]); exit(1); } } RegisterService(); return 0; } if (strcmp(argv[1], "--unregservice") == 0 || strcmp(argv[1], "-unregservice") == 0) { win32_load_config(argv[2]); UnRegisterService(); return 0; } } return exec_real_main(argc, argv); } pgbouncer-1.24.1/win32/Makefile0000644000175000000000000000011514777762222013106 00000000000000# create eventmsg.rc + MSG*.bin eventmsg.rc: eventmsg.mc mc.exe eventmsg.mc pgbouncer-1.24.1/etc/0000755000000000000000000000000014777762567011341 500000000000000pgbouncer-1.24.1/etc/pgbouncer.socket0000644000175000000000000000126314777762222014462 00000000000000# Example systemd socket unit for PgBouncer # # Note that /var/run/postgresql/ would typically be owned or managed # by the respective PostgreSQL package. If you specify it here, # systemd will create it first, which might upset the PostgreSQL # package (or not). See pgbouncer.service for further context. # [Unit] Description=sockets for PgBouncer [Socket] ListenStream=/tmp/.s.PGSQL.6432 #ListenStream=/var/run/postgresql/.s.PGSQL.6432 #ListenStream=6432 # additional settings that might be useful #Backlog= #SocketUser= #SocketGroup= #SocketMode= #KeepAlive= #KeepAliveTimeSec= #KeepAliveIntervalSec= #KeepAliveProbes= #DeferAcceptSec= #ReusePort= [Install] WantedBy=sockets.target pgbouncer-1.24.1/etc/example.debian.init.sh0000644000175000000000000000205414777762222015435 00000000000000#!/bin/bash # # pgbouncer Start the PgBouncer PostgreSQL pooler. # # The variables below are NOT to be changed. They are there to make the # script more readable. NAME=pgbouncer DAEMON=/usr/bin/$NAME PIDFILE=/var/run/$NAME.pid CONF=/etc/$NAME.ini OPTS="-d $CONF" # note: SSD is required only at startup of the daemon. SSD=`command -v start-stop-daemon` ENV="env -i LANG=C PATH=/bin:/usr/bin:/usr/local/bin" trap "" 1 # Check if configuration exists test -f $CONF || exit 0 case "$1" in start) echo -n "Starting server: $NAME" $ENV $SSD --start --pidfile $PIDFILE --exec $DAEMON -- $OPTS > /dev/null ;; stop) echo -n "Stopping server: $NAME" start-stop-daemon --stop --pidfile $PIDFILE ;; reload | force-reload) echo -n "Reloading $NAME configuration" start-stop-daemon --stop --pidfile $PIDFILE --signal HUP ;; restart) $0 stop $0 start ;; *) echo "Usage: /etc/init.d/$NAME {start|stop|reload|restart}" exit 1 ;; esac if [ $? -eq 0 ]; then echo . exit 0 else echo " failed" exit 1 fi pgbouncer-1.24.1/etc/pgbouncer.service0000644000175000000000000000270314777762222014632 00000000000000# Example systemd service unit for PgBouncer # # - Adjust the paths in ExecStart for your installation. # # - For systemd 253 and later, PgBouncer supports Type=notify-reload # (instead of Type=notify with ExecReload= command). # # - The User setting requires careful consideration. PgBouncer needs # to be able to place a Unix-domain socket file where PostgreSQL # clients will look for it. In the olden days, this was in /tmp, # but systems using systemd now prefer something like # /var/run/postgresql/. But then some systems also lock down that # directory so that only the postgres user can write to it. That # means you need to either # # - run PgBouncer as the postgres user, or # # - create a separate user and add it to the postgres group and # make /var/run/postgresql/ group-writable, or # # - use systemd to create the sockets; see pgbouncer.socket nearby. # # For packagers and deployment systems, this requires some # coordination between the PgBouncer and the PostgreSQL # packages/components. # [Unit] Description=connection pooler for PostgreSQL Documentation=man:pgbouncer(1) Documentation=https://www.pgbouncer.org/ After=network-online.target Wants=network-online.target #Requires=pgbouncer.socket [Service] Type=notify User=postgres ExecStart=/usr/bin/pgbouncer /etc/pgbouncer/pgbouncer.ini ExecReload=/bin/kill -HUP $MAINPID KillSignal=SIGINT Restart=on-failure #LimitNOFILE=1024 [Install] WantedBy=multi-user.target pgbouncer-1.24.1/etc/userlist.txt0000644000175000000000000000007014777762222013672 00000000000000"marko" "asdasd" "postgres" "asdasd" "pgbouncer" "fake" pgbouncer-1.24.1/etc/optscan.sh0000755000175000000000000000060214777762222013266 00000000000000#! /bin/sh # Check if all options in main.c are defined in sample ini and docs sources="src/main.c" targets="doc/config.md etc/pgbouncer.ini" status=0 for opt in `grep CF_ABS "$sources" | sed -r 's/^[^"]*"([^"]*)".*/\1/'`; do for conf in $targets; do if ! grep -q "$opt" "$conf"; then echo "$opt is missing in $conf" 1>&2 status=1 fi done done exit $status pgbouncer-1.24.1/etc/pgbouncer.ini0000644000175000000000000002463714777762222013763 00000000000000;;; ;;; PgBouncer configuration file ;;; ;; database name = connect string ;; ;; connect string params: ;; dbname= host= port= user= password= auth_user= ;; client_encoding= datestyle= timezone= ;; pool_size= reserve_pool_size= max_db_connections= ;; pool_mode= connect_query= application_name= [databases] ;; foodb over Unix socket ;foodb = ;; redirect bardb to bazdb on localhost ;bardb = host=localhost dbname=bazdb ;; access to dest database will go with single user ;forcedb = host=localhost port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO connect_query='SELECT 1' ;; use custom pool sizes ;nondefaultdb = pool_size=50 reserve_pool_size=10 ;; use auth_user with auth_query if user not present in auth_file ;; auth_user must exist in auth_file ; foodb = auth_user=bar ;; run auth_query on a specific database. ; bardb = auth_dbname=foo max_db_client_connections=10 ;; fallback connect string ;* = host=testserver ;; User-specific configuration [users] ;user1 = pool_size=5 reserve_pool_size=2 pool_mode=transaction max_user_connections=10 max_user_client_connections=20 ;; Configuration section [pgbouncer] ;;; ;;; Administrative settings ;;; logfile = /var/log/pgbouncer/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid ;;; ;;; Where to wait for clients ;;; ;; IP address or * which means all IPs listen_addr = localhost listen_port = 6432 ;; Unix socket is also used for -R. ;; On Debian it should be /var/run/postgresql ;unix_socket_dir = /tmp ;unix_socket_mode = 0777 ;unix_socket_group = ;; The peer id used to identify this pgbouncer process in a group of pgbouncer ;; processes that are peered together. When set to 0 pgbouncer peering is disabled ;peer_id = 0 ;;; ;;; TLS settings for accepting clients ;;; ;; disable, allow, require, verify-ca, verify-full ;client_tls_sslmode = disable ;; Path to file that contains trusted CA certs ;client_tls_ca_file = ;; Private key and cert to present to clients. ;; Required for accepting TLS connections from clients. ;client_tls_key_file = ;client_tls_cert_file = ;; default, secure, fast, normal, ;client_tls_ciphers = default ;; all, secure, tlsv1.0, tlsv1.1, tlsv1.2, tlsv1.3 ;client_tls_protocols = secure ;; none, auto, legacy ;client_tls_dheparams = auto ;; none, auto, ;client_tls_ecdhcurve = auto ;;; ;;; TLS settings for connecting to backend databases ;;; ;; disable, allow, prefer, require, verify-ca, verify-full ;server_tls_sslmode = prefer ;; Path to that contains trusted CA certs ;server_tls_ca_file = ;; Private key and cert to present to backend. ;; Needed only if backend server require client cert. ;server_tls_key_file = ;server_tls_cert_file = ;; all, secure, tlsv1.0, tlsv1.1, tlsv1.2, tlsv1.3 ;server_tls_protocols = secure ;; default, secure, fast, normal, ;server_tls_ciphers = default ;;; ;;; Authentication settings ;;; ;; any, trust, plain, md5, cert, hba, pam auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt ;; Path to HBA-style auth config ;auth_hba_file = ;; Path to Pg-ident-style map file ; auth_ident_file = ;; Query to use to fetch password from database. Result ;; must have 2 columns - username and password hash. ;auth_query = SELECT rolname, CASE WHEN rolvaliduntil < pg_catalog.now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolname=$1 AND rolcanlogin ;; Authentication database that can be set globally to run "auth_query". ;auth_dbname = ;;; ;;; Users allowed into database 'pgbouncer' ;;; ;; comma-separated list of users who are allowed to change settings ;admin_users = user2, someadmin, otheradmin ;; comma-separated list of users who are just allowed to use SHOW command ;stats_users = stats, root ;;; ;;; Pooler personality questions ;;; ;; When server connection is released back to pool: ;; session - after client disconnects (default) ;; transaction - after transaction finishes ;; statement - after statement finishes ;pool_mode = session ;; Number of prepared statements to cache on a server connection (zero value ;; disables support of prepared statements). ;max_prepared_statements = 0 ;; Query for cleaning connection immediately after releasing from ;; client. No need to put ROLLBACK here, pgbouncer does not reuse ;; connections where transaction is left open. ;server_reset_query = DISCARD ALL ;; Whether server_reset_query should run in all pooling modes. If it ;; is off, server_reset_query is used only for session-pooling. ;server_reset_query_always = 0 ;; Comma-separated list of parameters to track per client. The ;; Postgres parameters listed here will be cached per client by ;; pgbouncer and restored in server every time the client runs a query. ;track_extra_parameters = IntervalStyle ;; Comma-separated list of parameters to ignore when given in startup ;; packet. Newer JDBC versions require the extra_float_digits here. ;ignore_startup_parameters = extra_float_digits ;; When taking idle server into use, this query is run first. ;server_check_query = select 1 ;; If server was used more recently that this many seconds ago, ;; skip the check query. Value 0 may or may not run in immediately. ;server_check_delay = 30 ;; Close servers in session pooling mode after a RECONNECT, RELOAD, ;; etc. when they are idle instead of at the end of the session. ;server_fast_close = 0 ;; Use as application_name on server. ;application_name_add_host = 0 ;; Period for updating aggregated stats. ;stats_period = 60 ;;; ;;; Connection limits ;;; ;; Total number of clients that can connect ;max_client_conn = 100 ;; Default pool size. 20 is good number when transaction pooling ;; is in use, in session pooling it needs to be the number of ;; max clients you want to handle at any moment ;default_pool_size = 20 ;; Minimum number of server connections to keep in pool. ;min_pool_size = 0 ; how many additional connection to allow in case of trouble ;reserve_pool_size = 0 ;; If a clients needs to wait more than this many seconds, use reserve ;; pool. ;reserve_pool_timeout = 5 ;; Maximum number of server connections for a database ;max_db_connections = 0 ;; Maximum number of server connections for a user ;max_user_connections = 0 ;; If off, then server connections are reused in LIFO manner ;server_round_robin = 0 ;;; ;;; Logging ;;; ;; Syslog settings ;syslog = 0 ;syslog_facility = daemon ;syslog_ident = pgbouncer ;; log if client connects or server connection is made ;log_connections = 1 ;; log if and why connection was closed ;log_disconnections = 1 ;; log error messages pooler sends to clients ;log_pooler_errors = 1 ;; write aggregated stats into log ;log_stats = 1 ;; Logging verbosity. Same as -v switch on command line. ;verbose = 0 ;;; ;;; Timeouts ;;; ;; Close server connection if its been connected longer. ;server_lifetime = 3600 ;; Close server connection if its not been used in this time. Allows ;; to clean unnecessary connections from pool after peak. ;server_idle_timeout = 600 ;; Cancel connection attempt if server does not answer takes longer. ;server_connect_timeout = 15 ;; If server login failed (server_connect_timeout or auth failure) ;; then wait this many second before trying again. ;server_login_retry = 15 ;; Dangerous. Server connection is closed if query does not return in ;; this time. Should be used to survive network problems, _not_ as ;; statement_timeout. (default: 0) ;query_timeout = 0 ;; Dangerous. Client connection is closed if the query is not ;; assigned to a server in this time. Should be used to limit the ;; number of queued queries in case of a database or network ;; failure. (default: 120) ;query_wait_timeout = 120 ;; Dangerous. Client connection is closed if the cancellation request ;; is not assigned to a server in this time. Should be used to limit ;; the time a client application blocks on a queued cancel request in ;; case of a database or network failure. (default: 10) ;cancel_wait_timeout = 10 ;; Dangerous. Client connection is closed if no activity in this ;; time. Should be used to survive network problems. (default: 0) ;client_idle_timeout = 0 ;; Disconnect clients who have not managed to log in after connecting ;; in this many seconds. ;client_login_timeout = 60 ;; Clean automatically created database entries (via "*") if they stay ;; unused in this many seconds. ;autodb_idle_timeout = 3600 ;; Close connections which are in "IDLE in transaction" state longer ;; than this many seconds. ;idle_transaction_timeout = 0 ;; How long SUSPEND/-R waits for buffer flush before closing ;; connection. ;suspend_timeout = 10 ;;; ;;; Low-level tuning options ;;; ;; buffer for streaming packets ;pkt_buf = 4096 ;; man 2 listen ;listen_backlog = 128 ;; Max number pkt_buf to process in one event loop. ;sbuf_loopcnt = 5 ;; Maximum PostgreSQL protocol packet size. ;max_packet_size = 2147483647 ;; Set SO_REUSEPORT socket option ;so_reuseport = 0 ;; networking options, for info: man 7 tcp ;; Linux: Notify program about new connection only if there is also ;; data received. (Seconds to wait.) On Linux the default is 45, on ;; other OS'es 0. ;tcp_defer_accept = 0 ;; In-kernel buffer size (Linux default: 4096) ;tcp_socket_buffer = 0 ;; whether tcp keepalive should be turned on (0/1) ;tcp_keepalive = 1 ;; The following options are Linux-specific. They also require ;; tcp_keepalive=1. ;; Count of keepalive packets ;tcp_keepcnt = 0 ;; How long the connection can be idle before sending keepalive ;; packets ;tcp_keepidle = 0 ;; The time between individual keepalive probes ;tcp_keepintvl = 0 ;; How long may transmitted data remain unacknowledged before TCP ;; connection is closed (in milliseconds) ;tcp_user_timeout = 0 ;; DNS lookup caching time ;dns_max_ttl = 15 ;; DNS zone SOA lookup period ;dns_zone_check_period = 0 ;; DNS negative result caching time ;dns_nxdomain_ttl = 15 ;; Custom resolv.conf file, to set custom DNS servers or other options ;; (default: empty = use OS settings) ;resolv_conf = /etc/pgbouncer/resolv.conf ;;; ;;; Random stuff ;;; ;; Hackish security feature. Helps against SQL injection: when PQexec ;; is disabled, multi-statement cannot be made. ;disable_pqexec = 0 ;; Config file to use for next RELOAD/SIGHUP ;; By default contains config file from command line. ;conffile ;; Windows service name to register as. job_name is alias for ;; service_name, used by some Skytools scripts. ;service_name = pgbouncer ;job_name = pgbouncer ;; Read additional config from other file ;%include /etc/pgbouncer/pgbouncer-other.ini pgbouncer-1.24.1/etc/mkauth.py0000755000175000000000000000162314777762222013132 00000000000000#! /usr/bin/env python3 import os import sys import tempfile import psycopg2 if len(sys.argv) != 3: print("usage: mkauth DSTFN CONNSTR") sys.exit(1) # read old file fn = sys.argv[1] try: old = open(fn, "r").read() except IOError: old = "" # create new file data db = psycopg2.connect(sys.argv[2]) curs = db.cursor() curs.execute( "SELECT rolname, CASE WHEN rolvaliduntil < pg_catalog.now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolcanlogin order by 1" ) lines = [] for user, psw in curs.fetchall(): user = user.replace('"', '""') if not psw: psw = "" psw = psw.replace('"', '""') lines.append('"%s" "%s" ""\n' % (user, psw)) db.commit() cur = "".join(lines) # if changed, replace data securely if old != cur: fd, tmpfn = tempfile.mkstemp(dir=os.path.split(fn)[0]) f = os.fdopen(fd, "w") f.write(cur) f.close() os.rename(tmpfn, fn) pgbouncer-1.24.1/etc/pgbouncer-minimal.ini0000644000175000000000000000136114777762222015374 00000000000000;;; This is an almost minimal starter configuration file that only ;;; contains the settings that are either mandatory or almost always ;;; useful. All settings show their default value. [databases] ;; add yours here ;; fallback ;* = [pgbouncer] ;; required in daemon mode unless syslog is used ;logfile = ;; required in daemon mode ;pidfile = ;syslog = 0 ;; set to enable TCP/IP connections ;listen_addr = ;; PgBouncer port ;listen_port = 6432 ;; some systems prefer /var/run/postgresql ;unix_socket_dir = /tmp ;; change to taste ;auth_type = md5 ;; probably need this ;auth_file = ;; pool settings are perhaps best done per pool ;pool_mode = session ;default_pool_size = 20 ;; should probably be raised for production ;max_client_conn = 100 pgbouncer-1.24.1/src/0000755000000000000000000000000014777762567011355 500000000000000pgbouncer-1.24.1/src/system.c0000644000175000000000000000657714777762222013005 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Compat functions for OSes where libc does not provide them. */ #include "bouncer.h" #include #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_UCRED_H #include #endif #ifdef HAVE_SYS_UCRED_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_GRP_H #include #endif void change_user(const char *user) { const struct passwd *pw; gid_t gset[1]; #ifdef WIN32 die("option --user (-u) is not supported on this platform"); #endif /* check for a valid username */ pw = getpwnam(user); if (pw == NULL) die("could not find user '%s' to switch to", user); gset[0] = pw->pw_gid; if (getuid() == 0) { if (setgroups(1, gset) < 0) die("failed to reset groups: %s", strerror(errno)); } if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) die("failed to assume identity of user '%s': %s", user, strerror(errno)); if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) die("setuid() failed to work"); } /* set permissions & mode for file */ void change_file_mode(const char *fn, mode_t mode, const char *user_name, const char *group_name) { int res; uid_t uid = -1; gid_t gid = -1; unsigned long val; char *end; /* user lookup */ if (user_name && user_name[0]) { const struct passwd *pw; val = strtoul(user_name, &end, 0); if (*end == 0) { uid = val; } else { /* check for a valid username */ pw = getpwnam(user_name); if (!pw) { die("could not find user '%s': %s", user_name, strerror(errno)); } uid = pw->pw_uid; } } /* group lookup */ if (group_name && group_name[0]) { struct group *gr; val = strtoul(group_name, &end, 0); if (*end == 0) { gid = val; } else { gr = getgrnam(group_name); if (!gr) { die("could not find group '%s': %s", group_name, strerror(errno)); } gid = gr->gr_gid; } } /* change user/group */ if (uid != (uid_t)-1 || gid != (gid_t)-1) { res = chown(fn, uid, gid); if (res != 0) { die("chown(%s, %u, %u) failed: %s", fn, uid, gid, strerror(errno)); } } /* change mode */ res = chmod(fn, mode); if (res != 0) { die("failure to chmod(%s, 0%o): %s", fn, mode, strerror(errno)); } } /* * UNIX socket helper. */ bool check_unix_peer_name(int fd, const char *username) { int res; uid_t peer_uid = -1; gid_t peer_gid = -1; struct passwd *pw; res = getpeereid(fd, &peer_uid, &peer_gid); if (res < 0) return false; pw = getpwuid(peer_uid); if (!pw) return false; return strcmp(pw->pw_name, username) == 0; } pgbouncer-1.24.1/src/pooler.c0000644000175000000000000003513114777762222012745 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Handling of pooler listening sockets */ #include "bouncer.h" #include #include #include struct ListenSocket { struct List node; int fd; bool active; struct event ev; PgAddr addr; }; static STATLIST(sock_list); /* hints for getaddrinfo(listen_addr) */ static const struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, .ai_flags = AI_PASSIVE, }; /* should listening sockets be active or suspended? */ static bool need_active = false; /* is it actually active or suspended? */ static bool pooler_active = false; /* is listen_addr set, we can only know this by parsing it */ static bool listen_addr_empty = true; /* on accept() failure sleep 5 seconds */ static struct event ev_err; static struct timeval err_timeout = {5, 0}; static void tune_accept(int sock, bool on); /* atexit() cleanup func */ void cleanup_sockets(void) { struct ListenSocket *ls; struct List *el; /* avoid cleanup if exit() while suspended */ if (cf_pause_mode == P_SUSPEND) return; while ((el = statlist_pop(&sock_list)) != NULL) { ls = container_of(el, struct ListenSocket, node); if (event_del(&ls->ev) < 0) { log_warning("cleanup_sockets, event_del: %s", strerror(errno)); } if (ls->fd > 0) { safe_close(ls->fd); ls->fd = 0; } if (pga_is_unix(&ls->addr) && cf_unix_socket_dir[0] != '@') { char buf[sizeof(struct sockaddr_un) + 20]; snprintf(buf, sizeof(buf), "%s/.s.PGSQL.%d", cf_unix_socket_dir, cf_listen_port); unlink(buf); } statlist_remove(&sock_list, &ls->node); free(ls); } } /* * initialize another listening socket. */ static bool add_listen(int af, const struct sockaddr *sa, int salen) { struct ListenSocket *ls; int sock, res; char buf[128]; const char *errpos; log_debug("add_listen: %s", sa2str(sa, buf, sizeof(buf))); /* create socket */ errpos = "socket"; sock = socket(af, SOCK_STREAM, 0); if (sock < 0) goto failed; /* SO_REUSEADDR behaviour it default in WIN32. */ #ifndef WIN32 /* relaxed binding */ if (af != AF_UNIX) { int val = 1; errpos = "setsockopt"; res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (res < 0) goto failed; } #endif #ifdef IPV6_V6ONLY /* avoid ipv6 socket's attempt to takeover ipv4 port */ if (af == AF_INET6) { int val = 1; errpos = "setsockopt/IPV6_V6ONLY"; res = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); if (res < 0) goto failed; } #endif /* * If configured, set SO_REUSEPORT or equivalent. If it's not * enabled, just leave the socket alone. (We could also unset * the socket option in that case, but this area is fairly * unportable, so perhaps better to avoid it.) */ if (af != AF_UNIX && cf_so_reuseport) { #if defined(SO_REUSEPORT_LB) int val = 1; errpos = "setsockopt/SO_REUSEPORT_LB"; res = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT_LB, &val, sizeof(val)); if (res < 0) goto failed; #elif defined(SO_REUSEPORT) int val = 1; errpos = "setsockopt/SO_REUSEPORT"; res = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); if (res < 0) goto failed; #else die("so_reuseport not supported on this platform"); #endif } /* bind it */ errpos = "bind"; res = bind(sock, sa, salen); if (res < 0) goto failed; /* set common options */ errpos = "tune_socket"; if (!tune_socket(sock, (af == AF_UNIX))) goto failed; /* finally, accept connections */ errpos = "listen"; res = listen(sock, cf_listen_backlog); if (res < 0) goto failed; errpos = "calloc"; ls = calloc(1, sizeof(*ls)); if (!ls) goto failed; list_init(&ls->node); ls->fd = sock; if (sa->sa_family == AF_UNIX) { pga_set(&ls->addr, AF_UNIX, cf_listen_port); } else { pga_copy(&ls->addr, sa); } if (af == AF_UNIX) { #ifndef WIN32 if (cf_unix_socket_dir[0] != '@') { struct sockaddr_un *un = (struct sockaddr_un *)sa; change_file_mode(un->sun_path, cf_unix_socket_mode, NULL, cf_unix_socket_group); } #endif } else { tune_accept(sock, cf_tcp_defer_accept); } log_info("listening on %s", sa2str(sa, buf, sizeof(buf))); statlist_append(&sock_list, &ls->node); return true; failed: log_warning("cannot listen on %s: %s(): %s", sa2str(sa, buf, sizeof(buf)), errpos, strerror(errno)); if (sock >= 0) safe_close(sock); return false; } static void create_unix_socket(const char *socket_dir, int listen_port) { struct sockaddr_un un; int addrlen; int res; char lockfile[sizeof(struct sockaddr_un) + 10]; struct stat st; /* fill sockaddr struct */ memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s/.s.PGSQL.%d", socket_dir, listen_port); if (socket_dir[0] == '@') { /* * By convention, for abstract Unix sockets, only the * length of the string is the sockaddr length. */ addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path); un.sun_path[0] = '\0'; } else { addrlen = sizeof(un); } if (socket_dir[0] != '@') { /* check for lockfile */ snprintf(lockfile, sizeof(lockfile), "%s.lock", un.sun_path); res = lstat(lockfile, &st); if (res == 0) die("unix port %d is in use", listen_port); /* expect old bouncer gone */ unlink(un.sun_path); } /* * The user expects a socket to be created in this directory and a simple * typo might cause this to fail. So we fail hard to notify the user that * we were unable to create the socket. Not creating the socket is * especially bad when so_reuseport is used in combination with peering, * because the peer list would then contain sockets that don't exist and * forwarding cancels would wait for timeout and then fail silently. * * The exact directory is already listed in a warning created by * add_listen, so we don't show it here again. */ if (!add_listen(AF_UNIX, (const struct sockaddr *)&un, addrlen)) die("failed to create unix socket"); } /* * Notify pooler only when also data is arrived. * * optval specifies how long after connection attempt to wait for data. * * Related to tcp_synack_retries sysctl, default 5 (corresponds 180 secs). */ static void tune_accept(int sock, bool on) { const char *act = on ? "install" : "uninstall"; int res = 0; #ifdef TCP_DEFER_ACCEPT int val = 45; /* FIXME: proper value */ socklen_t vlen = sizeof(val); if (getsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, &vlen) == 0) log_noise("old TCP_DEFER_ACCEPT on %d = %d", sock, val); val = on ? 1 : 0; log_noise("%s TCP_DEFER_ACCEPT on %d", act, sock); res = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)); #else if (on) { errno = EINVAL; res = -1; } #endif if (res < 0) { log_warning("tune_accept: %s TCP_DEFER_ACCEPT: %s", act, strerror(errno)); } } void pooler_tune_accept(bool on) { struct List *el; struct ListenSocket *ls; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); if (!pga_is_unix(&ls->addr)) tune_accept(ls->fd, on); } } static void err_wait_func(evutil_socket_t sock, short flags, void *arg) { if (cf_pause_mode != P_SUSPEND) resume_pooler(); } static const char *addrpair(const PgAddr *src, const PgAddr *dst) { static char ip1buf[PGADDR_BUF], ip2buf[PGADDR_BUF], buf[2*PGADDR_BUF + 16]; const char *ip1, *ip2; if (pga_is_unix(src)) return "unix->unix"; ip1 = pga_ntop(src, ip1buf, sizeof(ip1buf)); ip2 = pga_ntop(dst, ip2buf, sizeof(ip2buf)); snprintf(buf, sizeof(buf), "%s:%d -> %s:%d", ip1, pga_port(src), ip2, pga_port(dst)); return buf; } static const char *conninfo(const PgSocket *sk) { if (is_server_socket(sk)) { return addrpair(&sk->local_addr, &sk->remote_addr); } else { return addrpair(&sk->remote_addr, &sk->local_addr); } } /* got new connection, associate it with client struct */ static void pool_accept(evutil_socket_t sock, short flags, void *arg) { struct ListenSocket *ls = arg; int fd; PgSocket *client; union { struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr sa; } raddr; socklen_t len = sizeof(raddr); bool is_unix = pga_is_unix(&ls->addr); if (!(flags & EV_READ)) { log_warning("no EV_READ in pool_accept"); return; } loop: /* get fd */ fd = safe_accept(sock, &raddr.sa, &len); if (fd < 0) { if (errno == EAGAIN) return; else if (errno == ECONNABORTED) return; /* * probably fd limit, pointless to try often * wait a bit, hope that admin resolves somehow */ log_error("accept() failed: %s", strerror(errno)); evtimer_assign(&ev_err, pgb_event_base, err_wait_func, NULL); safe_evtimer_add(&ev_err, &err_timeout); suspend_pooler(); return; } log_noise("new fd from accept=%d", fd); if (is_unix) { client = accept_client(fd, true); } else { client = accept_client(fd, false); } if (client) slog_debug(client, "P: got connection: %s", conninfo(client)); /* * there may be several clients waiting, * avoid context switch by looping */ goto loop; } bool use_pooler_socket(int sock, bool is_unix) { struct ListenSocket *ls; int res; char buf[PGADDR_BUF]; if (!tune_socket(sock, is_unix)) return false; ls = calloc(1, sizeof(*ls)); if (!ls) return false; ls->fd = sock; if (is_unix) { pga_set(&ls->addr, AF_UNIX, cf_listen_port); } else { struct sockaddr_storage ss; socklen_t len = sizeof(ss); res = getsockname(sock, (struct sockaddr *)&ss, &len); if (res < 0) { log_error("getsockname failed"); free(ls); return false; } pga_copy(&ls->addr, (struct sockaddr *)&ss); } log_info("got pooler socket: %s", pga_str(&ls->addr, buf, sizeof(buf))); statlist_append(&sock_list, &ls->node); return true; } void suspend_pooler(void) { struct List *el; struct ListenSocket *ls; need_active = false; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); if (!ls->active) continue; if (event_del(&ls->ev) < 0) { log_warning("suspend_pooler, event_del: %s", strerror(errno)); return; } ls->active = false; } pooler_active = false; } void resume_pooler(void) { struct List *el; struct ListenSocket *ls; need_active = true; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); if (ls->active) continue; event_assign(&ls->ev, pgb_event_base, ls->fd, EV_READ | EV_PERSIST, pool_accept, ls); if (event_add(&ls->ev, NULL) < 0) { log_warning("event_add failed: %s", strerror(errno)); return; } ls->active = true; } pooler_active = true; } /* retry previously failed suspend_pooler() / resume_pooler() */ void per_loop_pooler_maint(void) { if (need_active && !pooler_active) resume_pooler(); else if (!need_active && pooler_active) suspend_pooler(); } static bool parse_addr(void *arg, const char *addr) { int res; char service[64]; struct addrinfo *ai, *gaires = NULL; if (!*addr) return true; listen_addr_empty = false; if (strcmp(addr, "*") == 0) addr = NULL; snprintf(service, sizeof(service), "%d", cf_listen_port); res = getaddrinfo(addr, service, &hints, &gaires); if (res != 0) { die("getaddrinfo('%s', '%d') = %s [%d]", addr ? addr : "*", cf_listen_port, gai_strerror(res), res); } for (ai = gaires; ai; ai = ai->ai_next) { /* * add_listen() will log a warning if there is a * problem. We don't use the return value to fail the * whole thing, because that might lead to problems in * practice with overlapping host names or address * families and other weird stuff. If no address at all * can be listened on though, we do fail hard later. */ add_listen(ai->ai_family, ai->ai_addr, ai->ai_addrlen); } freeaddrinfo(gaires); return true; } /* listen on socket - should happen after all other initializations */ void pooler_setup(void) { int n; n = sd_listen_fds(0); if (n > 0) { if (cf_listen_addr && *cf_listen_addr) log_warning("sockets passed from service manager, cf_listen_addr ignored"); if (cf_unix_socket_dir && *cf_unix_socket_dir && strcmp(cf_unix_socket_dir, DEFAULT_UNIX_SOCKET_DIR) != 0) log_warning("sockets passed from service manager, cf_unix_socket_dir ignored"); for (int i = 0; i < n; i++) { int fd = SD_LISTEN_FDS_START + i; struct ListenSocket *ls; bool ok = true; ls = calloc(1, sizeof(*ls)); if (!ls) die("out of memory"); list_init(&ls->node); ls->fd = fd; if (sd_is_socket(fd, AF_UNIX, 0, -1)) { pga_set(&ls->addr, AF_UNIX, 0); if (!tune_socket(fd, true)) ok = false; } else if (sd_is_socket(fd, AF_INET, 0, -1)) { pga_set(&ls->addr, AF_INET, 0); if (!tune_socket(fd, false)) ok = false; tune_accept(fd, cf_tcp_defer_accept); } else if (sd_is_socket(fd, AF_INET6, 0, -1)) { pga_set(&ls->addr, AF_INET6, 0); if (!tune_socket(fd, false)) ok = false; tune_accept(fd, cf_tcp_defer_accept); } if (!ok) die("failed to set up socket passed from service manager (fd %d)", fd); log_info("socket passed from service manager (fd %d)", fd); statlist_append(&sock_list, &ls->node); } } else { bool ok; static bool init_done = false; if (!init_done) { /* remove socket on shutdown */ atexit(cleanup_sockets); init_done = true; } ok = parse_word_list(cf_listen_addr, parse_addr, NULL); if (!ok) die("failed to parse listen_addr list: %s", cf_listen_addr); if (!listen_addr_empty && !statlist_count(&sock_list)) die("failed to listen on any address in listen_addr list: %s", cf_listen_addr); if (cf_unix_socket_dir && *cf_unix_socket_dir) create_unix_socket(cf_unix_socket_dir, cf_listen_port); } if (!statlist_count(&sock_list)) die("nowhere to listen on"); resume_pooler(); } bool for_each_pooler_fd(pooler_cb cbfunc, void *arg) { struct List *el; struct ListenSocket *ls; bool ok; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); ok = cbfunc(arg, ls->fd, &ls->addr); if (!ok) return false; } return true; } pgbouncer-1.24.1/src/sbuf.c0000644000175000000000000011626014777762222012407 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Stream buffer * * The task is to copy data from one socket to another * efficiently, while allowing callbacks to look * at packet headers. */ #include "bouncer.h" #include #include #include #ifdef USUAL_LIBSSL_FOR_TLS #define USE_TLS #endif /* sbuf_main_loop() skip_recv values */ #define DO_RECV false #define SKIP_RECV true #define ACT_UNSET 0 #define ACT_SEND 1 #define ACT_SKIP 2 #define ACT_CALL 3 enum TLSState { SBUF_TLS_NONE, SBUF_TLS_DO_HANDSHAKE, SBUF_TLS_IN_HANDSHAKE, SBUF_TLS_OK, }; enum WaitType { W_NONE = 0, W_CONNECT, W_RECV, W_SEND, W_ONCE }; #define AssertSanity(sbuf) do { \ Assert(iobuf_sane((sbuf)->io)); \ } while (0) #define AssertActive(sbuf) do { \ Assert((sbuf)->sock > 0); \ AssertSanity(sbuf); \ } while (0) /* declare static stuff */ static bool sbuf_queue_send(SBuf *sbuf) _MUSTCHECK; static bool sbuf_send_pending_iobuf(SBuf *sbuf) _MUSTCHECK; static bool sbuf_process_pending(SBuf *sbuf) _MUSTCHECK; static void sbuf_connect_cb(evutil_socket_t sock, short flags, void *arg); static void sbuf_recv_cb(evutil_socket_t sock, short flags, void *arg); static void sbuf_send_cb(evutil_socket_t sock, short flags, void *arg); static void sbuf_try_resync(SBuf *sbuf, bool release); static bool sbuf_wait_for_data(SBuf *sbuf) _MUSTCHECK; static void sbuf_main_loop(SBuf *sbuf, bool skip_recv); static bool sbuf_call_proto(SBuf *sbuf, int event) /* _MUSTCHECK */; static bool sbuf_actual_recv(SBuf *sbuf, size_t len) _MUSTCHECK; static bool sbuf_after_connect_check(SBuf *sbuf) _MUSTCHECK; static bool handle_tls_handshake(SBuf *sbuf) _MUSTCHECK; /* regular I/O */ static ssize_t raw_sbufio_recv(struct SBuf *sbuf, void *dst, size_t len); static ssize_t raw_sbufio_send(struct SBuf *sbuf, const void *data, size_t len); static int raw_sbufio_close(struct SBuf *sbuf); static const SBufIO raw_sbufio_ops = { raw_sbufio_recv, raw_sbufio_send, raw_sbufio_close }; /* I/O over TLS */ #ifdef USE_TLS static ssize_t tls_sbufio_recv(struct SBuf *sbuf, void *dst, size_t len); static ssize_t tls_sbufio_send(struct SBuf *sbuf, const void *data, size_t len); static int tls_sbufio_close(struct SBuf *sbuf); static const SBufIO tls_sbufio_ops = { tls_sbufio_recv, tls_sbufio_send, tls_sbufio_close }; static void sbuf_tls_handshake_cb(evutil_socket_t fd, short flags, void *_sbuf); #endif /* ********************************* * Public functions ********************************* */ /* initialize SBuf with proto handler */ void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn) { memset(sbuf, 0, sizeof(SBuf)); sbuf->proto_cb = proto_fn; sbuf->ops = &raw_sbufio_ops; } /* got new socket from accept() */ bool sbuf_accept(SBuf *sbuf, int sock, bool is_unix) { bool res; Assert(iobuf_empty(sbuf->io) && sbuf->sock == 0); AssertSanity(sbuf); sbuf->sock = sock; if (!tune_socket(sock, is_unix)) goto failed; if (!cf_reboot) { res = sbuf_wait_for_data(sbuf); if (!res) goto failed; /* socket should already have some data (linux only) */ if (cf_tcp_defer_accept && !is_unix) { sbuf_main_loop(sbuf, DO_RECV); if (!sbuf->sock) return false; } } return true; failed: sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } /* need to connect() to get a socket */ bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, socklen_t sa_len, time_t timeout_sec) { int res, sock; struct timeval timeout; char buf[128]; bool is_unix = sa->sa_family == AF_UNIX; Assert(iobuf_empty(sbuf->io) && sbuf->sock == 0); AssertSanity(sbuf); /* * common stuff */ sock = socket(sa->sa_family, SOCK_STREAM, 0); if (sock < 0) { /* probably fd limit */ goto failed; } if (!tune_socket(sock, is_unix)) goto failed; sbuf->sock = sock; timeout.tv_sec = timeout_sec; timeout.tv_usec = 0; /* launch connection */ res = safe_connect(sock, sa, sa_len); if (res == 0) { /* unix socket gives connection immediately */ sbuf_connect_cb(sock, EV_WRITE, sbuf); return true; } else if (errno == EINPROGRESS || errno == EAGAIN) { /* tcp socket needs waiting */ event_assign(&sbuf->ev, pgb_event_base, sock, EV_WRITE, sbuf_connect_cb, sbuf); res = event_add(&sbuf->ev, &timeout); if (res >= 0) { sbuf->wait_type = W_CONNECT; return true; } } failed: log_warning("sbuf_connect failed to connect to %s: %s", sa2str(sa, buf, sizeof(buf)), strerror(errno)); if (sock >= 0) safe_close(sock); sbuf->sock = 0; sbuf_call_proto(sbuf, SBUF_EV_CONNECT_FAILED); return false; } /* don't wait for data on this socket */ bool sbuf_pause(SBuf *sbuf) { AssertActive(sbuf); Assert(sbuf->wait_type == W_RECV); if (event_del(&sbuf->ev) < 0) { log_warning("event_del: %s", strerror(errno)); return false; } sbuf->wait_type = W_NONE; return true; } /* resume from pause, start waiting for data */ void sbuf_continue(SBuf *sbuf) { bool do_recv = DO_RECV; bool res; AssertActive(sbuf); res = sbuf_wait_for_data(sbuf); if (!res) { /* drop if problems */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return; } /* * It's tempting to try to avoid the recv() but that would * only work if no code wants to see full packet. * * This is not true in ServerParameter case. */ /* * if (sbuf->recv_pos - sbuf->pkt_pos >= SBUF_SMALL_PKT) * do_recv = false; */ sbuf_main_loop(sbuf, do_recv); } /* * Resume from pause and give socket over to external * callback function. * * The callback will be called with arg given to sbuf_init. */ bool sbuf_continue_with_callback(SBuf *sbuf, event_callback_fn user_cb) { int err; AssertActive(sbuf); event_assign(&sbuf->ev, pgb_event_base, sbuf->sock, EV_READ | EV_PERSIST, user_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_continue_with_callback: %s", strerror(errno)); return false; } sbuf->wait_type = W_RECV; return true; } bool sbuf_use_callback_once(SBuf *sbuf, short ev, event_callback_fn user_cb) { int err; AssertActive(sbuf); if (sbuf->wait_type != W_NONE) { err = event_del(&sbuf->ev); sbuf->wait_type = W_NONE; /* make sure its called only once */ if (err < 0) { log_warning("sbuf_queue_once: event_del failed: %s", strerror(errno)); return false; } } /* setup one one-off event handler */ event_assign(&sbuf->ev, pgb_event_base, sbuf->sock, ev, user_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_queue_once: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_ONCE; return true; } /* socket cleanup & close: keeps .handler and .arg values */ bool sbuf_close(SBuf *sbuf) { if (sbuf->wait_type) { Assert(sbuf->sock); /* event_del() acts funny occasionally, debug it */ errno = 0; if (event_del(&sbuf->ev) < 0) { if (errno) { log_warning("event_del: %s", strerror(errno)); } else { log_warning("event_del: libevent error"); } /* we can retry whole sbuf_close() if needed */ /* if (errno == ENOMEM) return false; */ } } sbuf_op_close(sbuf); sbuf->dst = NULL; sbuf->sock = 0; sbuf->pkt_remain = 0; sbuf->pkt_action = sbuf->wait_type = 0; if (sbuf->io) { slab_free(iobuf_cache, sbuf->io); sbuf->io = NULL; } mbuf_free(&sbuf->extra_packets); return true; } /* proto_fn tells to send some bytes to socket */ void sbuf_prepare_send(SBuf *sbuf, SBuf *dst, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || sbuf->pkt_action == ACT_SEND || iobuf_amount_pending(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_SEND; sbuf->pkt_remain = amount; sbuf->dst = dst; } /* proto_fn tells to skip some amount of bytes */ void sbuf_prepare_skip(SBuf *sbuf, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || iobuf_send_pending_avail(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_SKIP; sbuf->skip_remain = amount; sbuf->pkt_remain = amount; } /* * proto_fn tells to send some bytes to socket, but before doing that skip * some of the bytes first. */ void sbuf_prepare_skip_then_send_leftover(SBuf *sbuf, SBuf *dst, unsigned skip_amount, unsigned total_amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || sbuf->pkt_action == ACT_SEND || iobuf_amount_pending(&sbuf->io)); */ Assert(total_amount > 0); Assert(total_amount >= skip_amount); sbuf->pkt_action = ACT_SKIP; sbuf->pkt_remain = total_amount; sbuf->skip_remain = skip_amount; sbuf->dst = dst; } /* * proto_fn tells to skip some amount of bytes and call a callback with those * bytes instead */ void sbuf_prepare_fetch(SBuf *sbuf, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || iobuf_send_pending_avail(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_CALL; sbuf->skip_remain = amount; sbuf->pkt_remain = amount; } /* * queue a packet for sending and free it too (on both failure and success) * * This is used to inject custom packets into the stream of packets from source * to clients. The packet is sent before the packet that is currently being * processed, unless src->extra_packet_queue_after is set to true (then it is * sent right after the current one). By calling sbuf_prepare_skip after * sbuf_queue_packet, you effectively repalce the current packet with the one * that you're passing into sbuf_queue_packet. */ bool sbuf_queue_packet(SBuf *src, SBuf *dst, PktBuf *pkt) { bool res; AssertActive(src); Assert(dst); /* * If we're queueing the packet before the packet that we're currently * handling, we need to make sure that any pending data in iobuf is * flushed. One important reason to do so is because we might want to * call sbuf_prepare_skip later (to skip sending the current packet and * completely replace it with this packet). Then the resulting ACT_SKIP * will trigger a flush of iobuf too. By making sure we're flush here * already, we make sure that its impossible for the flush caused by * ACT_SKIP to fail. And knowing that significantly reduces the already * complex failure scenarios we need to consider, because any failure * will cause a rerun of the code that queues these packets. */ Assert(src->extra_packet_queue_after || src->io == NULL || iobuf_amount_pending(src->io) == 0); if (!pkt || pkt->failed) { pktbuf_free(pkt); return false; } src->dst = dst; res = mbuf_write(&src->extra_packets, pkt->buf, pkt->write_pos); pktbuf_free(pkt); return res; } /* * queue the packet in PktHdr for sending * * This is pretty much the same as sbuf_queue_packet, but it works with a PktHdr * instead of a PktBuf. Apart from that the only difference is that it does not * free the passed in packet. */ bool sbuf_queue_full_packet(SBuf *src, SBuf *dst, PktHdr *pkt) { bool res; AssertActive(src); Assert(dst); /* * If we're queueing the packet before the packet that we're currently * handling, we need to make sure that any pending data in iobuf is * flushed. One important reason to do so is because we might want to * call sbuf_prepare_skip later (to skip sending the current packet and * completely replace it with this packet). Then the resulting ACT_SKIP * will trigger a flush of iobuf too. By making sure we're flush here * already, we make sure that its impossible for the flush caused by * ACT_SKIP to fail. And knowing that significantly reduces the already * complex failure scenarios we need to consider, because any failure * will cause a rerun of the code that queues these packets. */ Assert(src->extra_packet_queue_after || src->io == NULL || iobuf_amount_pending(src->io) == 0); Assert(!incomplete_pkt(pkt)); Assert(pkt->data.read_pos == 0); src->dst = dst; res = mbuf_write(&src->extra_packets, pkt->data.data, pkt->data.write_pos); return res; } /* ************************* * Internal functions ************************* */ /* * Call proto callback with proper struct MBuf. * * If callback returns true it used one of sbuf_prepare_* on sbuf, * and processing can continue. * * If it returned false it used sbuf_pause(), sbuf_close() or simply * wants to wait for next event loop (e.g. too few data available). * Callee should not touch sbuf in that case and just return to libevent. */ static bool sbuf_call_proto(SBuf *sbuf, int event) { struct MBuf mbuf; IOBuf *io = sbuf->io; bool res; AssertSanity(sbuf); Assert(event != SBUF_EV_READ || iobuf_amount_parse(io) > 0); /* if pkt callback, limit only with current packet */ if (event == SBUF_EV_PKT_CALLBACK) { iobuf_parse_limit(io, &mbuf, sbuf->pkt_remain); } else if (event == SBUF_EV_READ) { iobuf_parse_all(io, &mbuf); } else { memset(&mbuf, 0, sizeof(mbuf)); } res = sbuf->proto_cb(sbuf, event, &mbuf); AssertSanity(sbuf); Assert(event != SBUF_EV_READ || !res || sbuf->sock > 0); return res; } /* let's wait for new data */ static bool sbuf_wait_for_data(SBuf *sbuf) { int err; event_assign(&sbuf->ev, pgb_event_base, sbuf->sock, EV_READ | EV_PERSIST, sbuf_recv_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_wait_for_data: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_RECV; return true; } static void sbuf_recv_forced_cb(evutil_socket_t sock, short flags, void *arg) { SBuf *sbuf = arg; sbuf->wait_type = W_NONE; if (sbuf_wait_for_data(sbuf)) { sbuf_recv_cb(sock, flags, arg); } else { sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); } } static bool sbuf_wait_for_data_forced(SBuf *sbuf) { int err; struct timeval tv_min; tv_min.tv_sec = 0; tv_min.tv_usec = 1; if (sbuf->wait_type != W_NONE) { event_del(&sbuf->ev); sbuf->wait_type = W_NONE; } event_assign(&sbuf->ev, pgb_event_base, sbuf->sock, EV_READ, sbuf_recv_forced_cb, sbuf); err = event_add(&sbuf->ev, &tv_min); if (err < 0) { log_warning("sbuf_wait_for_data: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_ONCE; return true; } /* libevent EV_WRITE: called when dest socket is writable again */ static void sbuf_send_cb(evutil_socket_t sock, short flags, void *arg) { SBuf *sbuf = arg; bool res; log_noise("Socket is writable again"); /* sbuf was closed before in this loop */ if (!sbuf->sock) return; AssertSanity(sbuf); Assert(sbuf->wait_type == W_SEND); sbuf->wait_type = W_NONE; /* prepare normal situation for sbuf_main_loop */ res = sbuf_wait_for_data(sbuf); if (res) { /* here we should certainly skip recv() */ sbuf_main_loop(sbuf, SKIP_RECV); } else { /* drop if problems */ sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } } /* socket is full, wait until it's writable again */ static bool sbuf_queue_send(SBuf *sbuf) { int err; AssertActive(sbuf); Assert(sbuf->wait_type == W_RECV); /* if false is returned, the socket will be closed later */ /* stop waiting for read events */ err = event_del(&sbuf->ev); sbuf->wait_type = W_NONE; /* make sure its called only once */ if (err < 0) { log_warning("sbuf_queue_send: event_del failed: %s", strerror(errno)); return false; } /* instead wait for EV_WRITE on destination socket */ event_assign(&sbuf->ev, pgb_event_base, sbuf->dst->sock, EV_WRITE, sbuf_send_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_queue_send: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_SEND; return true; } /* * flush all pending data in the iobuf. This should be called before calling * needed before calling sbuf_queue_packet. */ bool sbuf_flush(SBuf *sbuf) { if (sbuf->io) { log_noise("sbuf_flush"); return sbuf_send_pending_iobuf(sbuf); } return true; } /* * There's data in buffer to be sent. Returns bool if processing can continue. * * Does not look at pkt_pos/remain fields, expects them to be merged to send_* */ static bool sbuf_send_pending_iobuf(SBuf *sbuf) { int avail; ssize_t res; IOBuf *io = sbuf->io; AssertActive(sbuf); Assert(sbuf->dst || iobuf_amount_pending(io) == 0); log_noise("sbuf_send_pending_iobuf"); try_more: /* how much data is available for sending */ avail = iobuf_amount_pending(io); if (avail == 0) return true; if (sbuf->dst->sock == 0) { log_error("sbuf_send_pending_iobuf: no dst sock?"); sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); return false; } /* actually send it */ //res = iobuf_send_pending(io, sbuf->dst->sock); res = sbuf_op_send(sbuf->dst, io->buf + io->done_pos, avail); if (res > 0) { io->done_pos += res; } else if (res < 0) { if (errno == EAGAIN) { if (!sbuf_queue_send(sbuf)) { /* drop if queue failed */ sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } } else { sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } return false; } AssertActive(sbuf); /* * Should do sbuf_queue_send() immediately? * * To be sure, let's run into EAGAIN. */ goto try_more; } /* * There's data in extra_packets buffer to be sent. Returns bool if processing * can continue. */ static bool sbuf_send_pending_extra_packets(SBuf *sbuf) { int avail; ssize_t res; struct MBuf *mbuf = &sbuf->extra_packets; AssertActive(sbuf); Assert(sbuf->dst || mbuf_avail_for_read(mbuf) == 0); log_noise("sbuf_send_pending_extra_packets "); try_more: /* how much data is available for sending */ avail = mbuf_avail_for_read(mbuf); if (avail == 0) return true; if (sbuf->dst->sock == 0) { log_error("sbuf_send_pending_extra_packets: no dst sock?"); sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); return false; } /* actually send it */ //res = iobuf_send_pending(io, sbuf->dst->sock); res = sbuf_op_send(sbuf->dst, mbuf->data + mbuf->read_pos, avail); if (res > 0) { mbuf->read_pos += res; } else if (res < 0) { if (errno == EAGAIN) { if (!sbuf_queue_send(sbuf)) { /* drop if queue failed */ sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } } else { sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } return false; } AssertActive(sbuf); /* * Should do sbuf_queue_send() immediately? * * To be sure, let's run into EAGAIN. */ goto try_more; } /* process as much data as possible */ static bool sbuf_process_pending(SBuf *sbuf) { unsigned avail; IOBuf *io = sbuf->io; struct MBuf *extra_packets = &sbuf->extra_packets; bool full = iobuf_amount_recv(io) <= 0; int loop_number = 0; log_noise("sbuf_process_pending: start"); while (1) { /* * If there's still queued extra packets from a previous packet, make * sure to flush those first. Otherwise the packet we process next * might add even more packets there, which would be bad because it * would mean they get delivered out of order. */ if (mbuf_avail_for_read(extra_packets)) { if (sbuf->extra_packet_queue_after) { if (!sbuf_send_pending_iobuf(sbuf)) { log_noise("sbuf_process_pending failed to send all pending data"); return false; } } if (!sbuf_send_pending_extra_packets(sbuf)) { log_noise("sbuf_process_pending ended early because of not being able to send the queued extra packets"); return false; } /* * To avoid frequent allocations we try to reuse the * extra_packets MBuf. But if it has grown to more than * 4 times pkt_buf, we free it to avoid wasting memory. * Otherwise one huge packet can cause a lot of memory * to stay allocated for the lifetime of the * connection. The most common case where this might * occur is a huge query in a prepared statement. * * We use 4 times pkt_buf as an arbitrary but * reosanable limit. */ if (extra_packets->alloc_len > (unsigned) cf_sbuf_len * 4) { mbuf_free(extra_packets); } else { mbuf_rewind_writer(extra_packets); } } AssertActive(sbuf); loop_number++; log_noise("sbuf_process_pending: loop %d", loop_number); /* * Enough for now? * * The (avail <= SBUF_SMALL_PKT) check is to avoid partial pkts. * As SBuf should not assume knowledge about packets, * the check is not done in !full case. Packet handler can * then still notify about partial packet by returning false. */ avail = iobuf_amount_parse(io); if (avail == 0 || (full && avail <= SBUF_SMALL_PKT)) break; /* * If start of packet, process packet header. */ if (sbuf->pkt_remain == 0) { if (!sbuf_call_proto(sbuf, SBUF_EV_READ)) { goto need_more_data; } Assert(sbuf->pkt_remain > 0); } if (sbuf->pkt_action == ACT_SKIP || sbuf->pkt_action == ACT_CALL) { /* send any pending data before skipping */ if (iobuf_amount_pending(io) > 0) { if (!sbuf_send_pending_iobuf(sbuf)) return false; } } if (avail > sbuf->pkt_remain) avail = sbuf->pkt_remain; switch (sbuf->pkt_action) { case ACT_SEND: iobuf_tag_send(io, avail); break; case ACT_CALL: if (!sbuf_call_proto(sbuf, SBUF_EV_PKT_CALLBACK)) { goto need_more_data; } /* fallthrough */ /* after callback, skip pkt */ case ACT_SKIP: if (sbuf->skip_remain >= avail) { iobuf_tag_skip(io, avail); sbuf->skip_remain -= avail; } else { if (sbuf->skip_remain != 0) { iobuf_tag_skip(io, sbuf->skip_remain); } iobuf_tag_send(io, avail - sbuf->skip_remain); sbuf->skip_remain = 0; } break; } sbuf->pkt_remain -= avail; } log_noise("sbuf_process_pending: done looping"); if (!sbuf_send_pending_iobuf(sbuf)) { log_noise("sbuf_process_pending failed to send all pending data"); return false; } log_noise("sbuf_process_pending: end"); return true; need_more_data: /* * We need to wait for more data before we can handle the current * packet. We'll call the handler for this packet again after receiving * more data and then all of the extra packets that were generated this * time will be regenerated again, so clean the ones up that were * generated this time. */ mbuf_rewind_writer(extra_packets); if (sbuf->sock && io && sbuf->wait_type == W_RECV) { /* * There might still be some previous packets that we're able * to send though. Let's do that now to create some extra space * in the buffer. */ if (iobuf_amount_pending(io) > 0) { if (!sbuf_send_pending_iobuf(sbuf)) return false; } /* * If we've filled the whole buffer, but the packet handler * still needs more data, we should force a resync to make some * space. */ if (io && io->recv_pos == (unsigned) cf_sbuf_len) { log_noise("resync(%d): done=%u, parse=%u, recv=%u, forced", sbuf->sock, io->done_pos, io->parse_pos, io->recv_pos); iobuf_try_resync(io, cf_sbuf_len); } } return false; } /* reposition at buffer start again */ static void sbuf_try_resync(SBuf *sbuf, bool release) { IOBuf *io = sbuf->io; if (io) { log_noise("resync(%d): done=%u, parse=%u, recv=%u", sbuf->sock, io->done_pos, io->parse_pos, io->recv_pos); } AssertActive(sbuf); if (!io) return; if (release && iobuf_empty(io)) { slab_free(iobuf_cache, io); sbuf->io = NULL; } else { iobuf_try_resync(io, SBUF_SMALL_PKT); } } /* actually ask kernel for more data */ static bool sbuf_actual_recv(SBuf *sbuf, size_t len) { ssize_t got; IOBuf *io = sbuf->io; uint8_t *dst = io->buf + io->recv_pos; unsigned avail = iobuf_amount_recv(io); if (len > avail) len = avail; got = sbuf_op_recv(sbuf, dst, len); if (got > 0) { io->recv_pos += got; } else if (got == 0) { /* eof from socket */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } else if (got < 0 && errno != EAGAIN) { /* some error occurred */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } return true; } /* callback for libevent EV_READ */ static void sbuf_recv_cb(evutil_socket_t sock, short flags, void *arg) { SBuf *sbuf = arg; sbuf_main_loop(sbuf, DO_RECV); } static bool allocate_iobuf(SBuf *sbuf) { if (sbuf->io == NULL) { sbuf->io = slab_alloc(iobuf_cache); if (sbuf->io == NULL) { sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } iobuf_reset(sbuf->io); } return true; } /* * Main recv-parse-send-repeat loop. * * Reason for skip_recv is to avoid extra recv(). The problem with it * is EOF from socket. Currently that means that the pending data is * dropped. Fortunately server sockets are not paused and dropping * data from client is no problem. So only place where skip_recv is * important is sbuf_send_cb(). */ static void sbuf_main_loop(SBuf *sbuf, bool skip_recv) { unsigned free, ok; int loopcnt = 0; /* sbuf was closed before in this event loop */ if (!sbuf->sock) return; /* reading should be disabled when waiting */ Assert(sbuf->wait_type == W_RECV); AssertSanity(sbuf); if (!allocate_iobuf(sbuf)) return; /* avoid recv() if asked */ if (skip_recv) goto skip_recv; try_more: /* make room in buffer */ sbuf_try_resync(sbuf, false); /* avoid spending too much time on single socket */ if (cf_sbuf_loopcnt > 0 && loopcnt >= cf_sbuf_loopcnt) { bool _ignore; log_debug("loopcnt full"); /* * sbuf_process_pending() avoids some data if buffer is full, * but as we exit processing loop here, we need to retry * after resync to process all data. (result is ignored) */ _ignore = sbuf_process_pending(sbuf); (void) _ignore; sbuf_wait_for_data_forced(sbuf); return; } loopcnt++; /* * here used to be if (free > SBUF_SMALL_PKT) check * but with skip_recv switch its should not be needed anymore. */ free = iobuf_amount_recv(sbuf->io); if (free > 0) { /* * When suspending, try to hit packet boundary ASAP. */ if (cf_pause_mode == P_SUSPEND && sbuf->pkt_remain > 0 && sbuf->pkt_remain < free) { free = sbuf->pkt_remain; } /* now fetch the data */ ok = sbuf_actual_recv(sbuf, free); if (!ok) return; } skip_recv: /* now handle it */ ok = sbuf_process_pending(sbuf); if (!ok) return; /* if the buffer is full, there can be more data available */ if (iobuf_amount_recv(sbuf->io) <= 0) goto try_more; /* clean buffer */ sbuf_try_resync(sbuf, true); /* notify proto that all is sent */ if (sbuf_is_empty(sbuf)) sbuf_call_proto(sbuf, SBUF_EV_FLUSH); if (sbuf->tls_state == SBUF_TLS_DO_HANDSHAKE) { sbuf->pkt_action = SBUF_TLS_IN_HANDSHAKE; if (!handle_tls_handshake(sbuf)) sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); } } /* check if there is any error pending on socket */ static bool sbuf_after_connect_check(SBuf *sbuf) { int optval = 0, err; socklen_t optlen = sizeof(optval); err = getsockopt(sbuf->sock, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen); if (err < 0) { log_debug("sbuf_after_connect_check: getsockopt: %s", strerror(errno)); return false; } if (optval != 0) { log_debug("sbuf_after_connect_check: pending error: %s", strerror(optval)); return false; } return true; } /* callback for libevent EV_WRITE when connecting */ static void sbuf_connect_cb(evutil_socket_t sock, short flags, void *arg) { SBuf *sbuf = arg; Assert(sbuf->wait_type == W_CONNECT || sbuf->wait_type == W_NONE); sbuf->wait_type = W_NONE; if (flags & EV_WRITE) { if (!sbuf_after_connect_check(sbuf)) goto failed; if (!sbuf_call_proto(sbuf, SBUF_EV_CONNECT_OK)) return; if (!sbuf_wait_for_data(sbuf)) goto failed; return; } failed: sbuf_call_proto(sbuf, SBUF_EV_CONNECT_FAILED); } /* send some data to listening socket */ bool sbuf_answer(SBuf *sbuf, const void *buf, size_t len) { ssize_t res; if (sbuf->sock <= 0) return false; res = sbuf_op_send(sbuf, buf, len); if (res < 0) { log_debug("sbuf_answer: error sending: %s", strerror(errno)); } else if ((unsigned)res != len) { log_debug("sbuf_answer: partial send: len=%zu sent=%zd", len, res); } return (unsigned)res == len; } /* * Standard IO ops. */ static ssize_t raw_sbufio_recv(struct SBuf *sbuf, void *dst, size_t len) { return safe_recv(sbuf->sock, dst, len, 0); } static ssize_t raw_sbufio_send(struct SBuf *sbuf, const void *data, size_t len) { return safe_send(sbuf->sock, data, len, 0); } static int raw_sbufio_close(struct SBuf *sbuf) { if (sbuf->sock > 0) { safe_close(sbuf->sock); sbuf->sock = 0; } return 0; } /* * TLS support. */ #ifdef USE_TLS /* * These global variables contain the currently applied TLS configurations. * They might differ from the current configuration if there was an error * applying the configured parameters (e.g. cert file not found). */ static struct tls *client_accept_base; static struct tls_config *client_accept_conf; int client_accept_sslmode; static struct tls_config *server_connect_conf; int server_connect_sslmode; /* * TLS setup */ static bool setup_tls(struct tls_config *conf, const char *pfx, int sslmode, const char *protocols, const char *ciphers, const char *keyfile, const char *certfile, const char *cafile, const char *dheparams, const char *ecdhecurve, bool does_connect) { int err; if (*protocols) { uint32_t protos = TLS_PROTOCOLS_ALL; err = tls_config_parse_protocols(&protos, protocols); if (err) { log_error("invalid %s_protocols: %s", pfx, protocols); return false; } tls_config_set_protocols(conf, protos); } if (*ciphers) { err = tls_config_set_ciphers(conf, ciphers); if (err) { log_error("invalid %s_ciphers: %s", pfx, ciphers); return false; } } if (*dheparams) { err = tls_config_set_dheparams(conf, dheparams); if (err) { log_error("invalid %s_dheparams: %s", pfx, dheparams); return false; } } if (*ecdhecurve) { err = tls_config_set_ecdhecurve(conf, ecdhecurve); if (err) { log_error("invalid %s_ecdhecurve: %s", pfx, ecdhecurve); return false; } } if (*cafile) { err = tls_config_set_ca_file(conf, cafile); if (err) { log_error("invalid %s_ca_file: %s", pfx, cafile); return false; } } if (*keyfile) { err = tls_config_set_key_file(conf, keyfile); if (err) { log_error("invalid %s_key_file: %s", pfx, keyfile); return false; } } if (*certfile) { err = tls_config_set_cert_file(conf, certfile); if (err) { log_error("invalid %s_cert_file: %s", pfx, certfile); return false; } } if (does_connect) { /* TLS client, check server? */ if (sslmode == SSLMODE_VERIFY_FULL) { tls_config_verify(conf); } else if (sslmode == SSLMODE_VERIFY_CA) { tls_config_verify(conf); tls_config_insecure_noverifyname(conf); } else { tls_config_insecure_noverifycert(conf); tls_config_insecure_noverifyname(conf); } } else { /* TLS server, check client? */ if (sslmode == SSLMODE_VERIFY_FULL) { tls_config_verify_client(conf); } else if (sslmode == SSLMODE_VERIFY_CA) { tls_config_verify_client(conf); } else { tls_config_verify_client_optional(conf); } } return true; } static bool tls_change_requires_reconnect(struct tls_config *new_server_connect_conf) { if (server_connect_sslmode != cf_server_tls_sslmode) { log_noise("new server_tls_sslmode detected"); return true; } else if (server_connect_conf == NULL) { log_noise("no existing server tls config detected"); return true; } else if (tls_config_equal(new_server_connect_conf, server_connect_conf)) { log_noise("no server tls config change detected"); return false; } else { log_noise("server tls config change detected"); return true; } } bool sbuf_tls_setup(void) { int err; /* * These variables store the new TLS configurations, based on the latest * settings provided by the user. Once they have been configured completely * without errors they are assigned to the globals at the end of this * function. This way the globals never contain partially configured TLS * configurations. */ struct tls_config *new_client_accept_conf = NULL; struct tls_config *new_server_connect_conf = NULL; struct tls *new_client_accept_base = NULL; if (cf_client_tls_sslmode != SSLMODE_DISABLED) { if (!*cf_client_tls_key_file || !*cf_client_tls_cert_file) { log_error("To allow TLS connections from clients, client_tls_key_file and client_tls_cert_file must be set."); return false; } } if (cf_auth_type == AUTH_TYPE_CERT) { if (cf_client_tls_sslmode != SSLMODE_VERIFY_FULL) { log_error("auth_type=cert requires client_tls_sslmode=SSLMODE_VERIFY_FULL"); return false; } if (*cf_client_tls_ca_file == '\0') { log_error("auth_type=cert requires client_tls_ca_file"); return false; } } else if (cf_client_tls_sslmode > SSLMODE_VERIFY_CA && *cf_client_tls_ca_file == '\0') { log_error("client_tls_sslmode requires client_tls_ca_file"); return false; } err = tls_init(); if (err) fatal("tls_init failed"); if (cf_server_tls_sslmode != SSLMODE_DISABLED) { new_server_connect_conf = tls_config_new(); if (!new_server_connect_conf) { log_error("tls_config_new failed 1"); return false; } if (!setup_tls(new_server_connect_conf, "server_tls", cf_server_tls_sslmode, cf_server_tls_protocols, cf_server_tls_ciphers, cf_server_tls_key_file, cf_server_tls_cert_file, cf_server_tls_ca_file, "", "", true)) goto failed; } if (cf_client_tls_sslmode != SSLMODE_DISABLED) { new_client_accept_conf = tls_config_new(); if (!new_client_accept_conf) { log_error("tls_config_new failed 2"); goto failed; } if (!setup_tls(new_client_accept_conf, "client_tls", cf_client_tls_sslmode, cf_client_tls_protocols, cf_client_tls_ciphers, cf_client_tls_key_file, cf_client_tls_cert_file, cf_client_tls_ca_file, cf_client_tls_dheparams, cf_client_tls_ecdhecurve, false)) goto failed; new_client_accept_base = tls_server(); if (!new_client_accept_base) { log_error("server_base failed"); goto failed; } err = tls_configure(new_client_accept_base, new_client_accept_conf); if (err) { log_error("TLS setup failed: %s", tls_error(new_client_accept_base)); goto failed; } } /* * To change server TLS settings all connections are marked as dirty. This * way they are recycled and the new TLS settings will be used. Otherwise * old TLS settings, possibly less secure, could be used for old * connections indefinitely. If TLS is disabled, and it was disabled before * as well then recycling connections is not necessary, since we know none * of the settings have changed. */ if ((server_connect_conf || new_server_connect_conf) && tls_change_requires_reconnect(new_server_connect_conf)) { struct List *item; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); tag_pool_dirty(pool); } } usual_tls_free(client_accept_base); tls_config_free(client_accept_conf); tls_config_free(server_connect_conf); client_accept_base = new_client_accept_base; client_accept_conf = new_client_accept_conf; client_accept_sslmode = cf_client_tls_sslmode; server_connect_conf = new_server_connect_conf; server_connect_sslmode = cf_server_tls_sslmode; return true; failed: usual_tls_free(new_client_accept_base); tls_config_free(new_client_accept_conf); tls_config_free(new_server_connect_conf); return false; } /* * TLS handshake */ static bool handle_tls_handshake(SBuf *sbuf) { int err; err = tls_handshake(sbuf->tls); log_noise("tls_handshake: err=%d", err); if (err == TLS_WANT_POLLIN) { return sbuf_use_callback_once(sbuf, EV_READ, sbuf_tls_handshake_cb); } else if (err == TLS_WANT_POLLOUT) { return sbuf_use_callback_once(sbuf, EV_WRITE, sbuf_tls_handshake_cb); } else if (err == 0) { sbuf->tls_state = SBUF_TLS_OK; sbuf_call_proto(sbuf, SBUF_EV_TLS_READY); return true; } else { log_warning("TLS handshake error: %s", tls_error(sbuf->tls)); return false; } } static void sbuf_tls_handshake_cb(evutil_socket_t fd, short flags, void *_sbuf) { SBuf *sbuf = _sbuf; sbuf->wait_type = W_NONE; if (!handle_tls_handshake(sbuf)) sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); } /* * Accept TLS connection. */ bool sbuf_tls_accept(SBuf *sbuf) { int err; if (!sbuf_pause(sbuf)) return false; sbuf->ops = &tls_sbufio_ops; err = tls_accept_fds(client_accept_base, &sbuf->tls, sbuf->sock, sbuf->sock); log_noise("tls_accept_fds: err=%d", err); if (err < 0) { log_warning("TLS accept error: %s", tls_error(sbuf->tls)); return false; } sbuf->tls_state = SBUF_TLS_DO_HANDSHAKE; return true; } /* * Connect to remote TLS host. */ bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) { struct tls *ctls; int err; if (!sbuf_pause(sbuf)) return false; if (cf_server_tls_sslmode != SSLMODE_VERIFY_FULL) hostname = NULL; ctls = tls_client(); if (!ctls) return false; err = tls_configure(ctls, server_connect_conf); if (err < 0) { log_error("tls client config failed: %s", tls_error(ctls)); usual_tls_free(ctls); return false; } sbuf->tls = ctls; sbuf->tls_host = hostname; sbuf->ops = &tls_sbufio_ops; err = tls_connect_fds(sbuf->tls, sbuf->sock, sbuf->sock, sbuf->tls_host); if (err < 0) { log_warning("TLS connect error: %s", tls_error(sbuf->tls)); return false; } sbuf->tls_state = SBUF_TLS_DO_HANDSHAKE; return true; } /* * TLS IO ops. */ static ssize_t tls_sbufio_recv(struct SBuf *sbuf, void *dst, size_t len) { ssize_t out = 0; if (sbuf->tls_state != SBUF_TLS_OK) { errno = EIO; return -1; } out = tls_read(sbuf->tls, dst, len); log_noise("tls_read: req=%zu out=%zd", len, out); if (out >= 0) { return out; } else if (out == TLS_WANT_POLLIN) { errno = EAGAIN; } else if (out == TLS_WANT_POLLOUT) { log_warning("tls_sbufio_recv: got TLS_WANT_POLLOUT"); errno = EIO; } else { log_warning("tls_sbufio_recv: %s", tls_error(sbuf->tls)); errno = EIO; } return -1; } static ssize_t tls_sbufio_send(struct SBuf *sbuf, const void *data, size_t len) { ssize_t out; if (sbuf->tls_state != SBUF_TLS_OK) { errno = EIO; return -1; } out = tls_write(sbuf->tls, data, len); log_noise("tls_write: req=%zu out=%zd", len, out); if (out >= 0) { return out; } else if (out == TLS_WANT_POLLOUT) { errno = EAGAIN; } else if (out == TLS_WANT_POLLIN) { log_warning("tls_sbufio_send: got TLS_WANT_POLLIN"); errno = EIO; } else { log_warning("tls_sbufio_send: %s", tls_error(sbuf->tls)); errno = EIO; } return -1; } static int tls_sbufio_close(struct SBuf *sbuf) { log_noise("tls_close"); if (sbuf->tls) { tls_close(sbuf->tls); usual_tls_free(sbuf->tls); sbuf->tls = NULL; } if (sbuf->sock > 0) { safe_close(sbuf->sock); sbuf->sock = 0; } return 0; } void sbuf_cleanup(void) { usual_tls_free(client_accept_base); tls_config_free(client_accept_conf); tls_config_free(server_connect_conf); client_accept_conf = NULL; server_connect_conf = NULL; client_accept_base = NULL; } #else int client_accept_sslmode = SSLMODE_DISABLED; int server_connect_sslmode = SSLMODE_DISABLED; bool sbuf_tls_setup(void) { return true; } bool sbuf_tls_accept(SBuf *sbuf) { return false; } bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) { return false; } void sbuf_cleanup(void) { } static bool handle_tls_handshake(SBuf *sbuf) { return false; } #endif pgbouncer-1.24.1/src/admin.c0000644000175000000000000015112214777762222012534 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Admin console commands. */ #include "bouncer.h" #include #include #include #include #include #include /* regex elements */ #define WS0 "[ \t\n\r]*" #define WS1 "[ \t\n\r]+" #define WORD "(\"([^\"]+|\"\")*\"|[0-9a-z_]+)" #define STRING "('([^']|'')*')" /* possible max + 1 */ #define MAX_GROUPS 10 /* group numbers */ #define CMD_NAME 1 #define CMD_ARG 4 #define SET_KEY 1 #define SET_VAL 4 typedef bool (*cmd_func_t)(PgSocket *admin, const char *arg); struct cmd_lookup { const char *word; cmd_func_t func; }; /* CMD [arg]; */ static const char cmd_normal_rx[] = "^" WS0 WORD "(" WS1 WORD ")?" WS0 "(;" WS0 ")?$"; /* SET with simple value */ static const char cmd_set_word_rx[] = "^" WS0 "set" WS1 WORD WS0 "(=|to)" WS0 WORD WS0 "(;" WS0 ")?$"; /* SET with quoted value */ static const char cmd_set_str_rx[] = "^" WS0 "set" WS1 WORD WS0 "(=|to)" WS0 STRING WS0 "(;" WS0 ")?$"; /* compiled regexes */ static regex_t rc_cmd; static regex_t rc_set_word; static regex_t rc_set_str; static PgPool *admin_pool; /* only valid during processing */ static const char *current_query; void admin_cleanup(void) { regfree(&rc_cmd); regfree(&rc_set_str); regfree(&rc_set_word); admin_pool = NULL; } static bool syntax_error(PgSocket *admin) { return admin_error(admin, "invalid command '%s', use SHOW HELP;", current_query ? current_query : ""); } static bool exec_cmd(struct cmd_lookup *lookup, PgSocket *admin, const char *cmd, const char *arg) { for (; lookup->word; lookup++) { if (strcasecmp(lookup->word, cmd) == 0) return lookup->func(admin, arg); } return syntax_error(admin); } bool admin_error(PgSocket *admin, const char *fmt, ...) { char str[1024]; va_list ap; bool res = true; va_start(ap, fmt); vsnprintf(str, sizeof(str), fmt, ap); va_end(ap); log_error("%s", str); if (admin) res = send_pooler_error(admin, true, NULL, false, str); return res; } static int count_paused_databases(void) { struct List *item; PgDatabase *db; int cnt = 0; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); cnt += db->db_paused; } return cnt; } static int count_db_active(PgDatabase *db) { struct List *item; PgPool *pool; int cnt = 0; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db != db) continue; cnt += pool_server_count(pool); } return cnt; } bool admin_flush(PgSocket *admin, PktBuf *buf, const char *desc) { pktbuf_write_CommandComplete(buf, desc); pktbuf_write_ReadyForQuery(buf); return pktbuf_send_queued(buf, admin); } bool admin_ready(PgSocket *admin, const char *desc) { PktBuf buf; uint8_t tmp[512]; pktbuf_static(&buf, tmp, sizeof(tmp)); pktbuf_write_CommandComplete(&buf, desc); pktbuf_write_ReadyForQuery(&buf); return pktbuf_send_immediate(&buf, admin); } /* * some silly clients start actively messing with server parameters * without checking if that's necessary. Fake some env for them. */ struct FakeParam { const char *name; const char *value; }; static const struct FakeParam fake_param_list[] = { { "client_encoding", "UTF-8" }, { "default_transaction_isolation", "read committed" }, { "standard_conforming_strings", "on" }, { "datestyle", "ISO" }, { "timezone", "GMT" }, { NULL }, }; /* fake result send, returns if handled */ static bool fake_show(PgSocket *admin, const char *name) { PktBuf *buf; const struct FakeParam *p; bool got = false; for (p = fake_param_list; p->name; p++) { if (strcasecmp(name, p->name) == 0) { got = true; break; } } if (got) { buf = pktbuf_dynamic(256); if (buf) { pktbuf_write_RowDescription(buf, "s", p->name); pktbuf_write_DataRow(buf, "s", p->value); admin_flush(admin, buf, "SHOW"); } else { admin_error(admin, "no mem"); } } return got; } static bool fake_set(PgSocket *admin, const char *key, const char *val) { PktBuf *buf; const struct FakeParam *p; bool got = false; for (p = fake_param_list; p->name; p++) { if (strcasecmp(key, p->name) == 0) { got = true; break; } } if (got) { buf = pktbuf_dynamic(256); if (buf) { pktbuf_write_Notice(buf, "SET ignored"); admin_flush(admin, buf, "SET"); } else { admin_error(admin, "no mem"); } } return got; } /* Command: SET key = val; */ static bool admin_set(PgSocket *admin, const char *key, const char *val) { char tmp[512]; bool ok; if (fake_set(admin, key, val)) return true; if (admin->admin_user) { ok = set_config_param(key, val); if (ok) { PktBuf *buf = pktbuf_dynamic(256); if (!buf) { return admin_error(admin, "no mem"); } if (strstr(key, "_tls_") != NULL) { if (!sbuf_tls_setup()) pktbuf_write_Notice(buf, "TLS settings could not be applied, still using old configuration"); } snprintf(tmp, sizeof(tmp), "SET %s=%s", key, val); return admin_flush(admin, buf, tmp); } else { return admin_error(admin, "SET failed"); } } else { return admin_error(admin, "admin access needed"); } } /* send a row with sendmsg, optionally attaching a fd */ static bool send_one_fd(PgSocket *admin, int fd, const char *task, const char *user, const char *db, const char *addr, int port, uint64_t ckey, int link, const char *client_enc, const char *std_strings, const char *datestyle, const char *timezone, const char *password, const uint8_t *scram_client_key, int scram_client_key_len, const uint8_t *scram_server_key, int scram_server_key_len) { struct msghdr msg; struct cmsghdr *cmsg; struct iovec iovec; ssize_t res; uint8_t cntbuf[CMSG_SPACE(sizeof(int))]; struct PktBuf *pkt = pktbuf_temp(); pktbuf_write_DataRow(pkt, "issssiqisssssbb", fd, task, user, db, addr, port, ckey, link, client_enc, std_strings, datestyle, timezone, password, scram_client_key_len, scram_client_key, scram_server_key_len, scram_server_key); if (pkt->failed) return false; iovec.iov_base = pkt->buf; iovec.iov_len = pktbuf_written(pkt); /* sending fds */ memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iovec; msg.msg_iovlen = 1; /* attach a fd */ if (pga_is_unix(&admin->remote_addr) && admin->own_user && !admin->sbuf.tls) { msg.msg_control = cntbuf; msg.msg_controllen = sizeof(cntbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; } slog_debug(admin, "sending socket list: fd=%d, len=%d", fd, (int)msg.msg_controllen); if (msg.msg_controllen) { res = safe_sendmsg(sbuf_socket(&admin->sbuf), &msg, 0); } else { res = sbuf_op_send(&admin->sbuf, pkt->buf, pktbuf_written(pkt)); } if (res < 0) { log_error("send_one_fd: sendmsg error: %s", strerror(errno)); return false; } else if ((size_t)res != iovec.iov_len) { log_error("send_one_fd: partial sendmsg"); return false; } return true; } /* send a row with sendmsg, optionally attaching a fd */ static bool show_one_fd(PgSocket *admin, PgSocket *sk) { PgAddr *addr = &sk->remote_addr; struct MBuf tmp; VarCache *v = &sk->vars; uint64_t ckey; const struct PStr *client_encoding = v->var_list[VClientEncoding]; const struct PStr *std_strings = v->var_list[VStdStr]; const struct PStr *datestyle = v->var_list[VDateStyle]; const struct PStr *timezone = v->var_list[VTimeZone]; char addrbuf[PGADDR_BUF]; const char *password = NULL; bool send_scram_keys = false; /* Skip TLS sockets */ if (sk->sbuf.tls || (sk->link && sk->link->sbuf.tls)) return true; mbuf_init_fixed_reader(&tmp, sk->cancel_key, 8); if (!mbuf_get_uint64be(&tmp, &ckey)) return false; if (sk->pool && sk->pool->db->auth_user_credentials && sk->login_user_credentials && !find_global_user(sk->login_user_credentials->name)) password = sk->login_user_credentials->passwd; /* PAM requires passwords as well since they are not stored externally */ if (cf_auth_type == AUTH_TYPE_PAM && !find_global_user(sk->login_user_credentials->name)) password = sk->login_user_credentials->passwd; if (sk->pool && sk->pool->user_credentials && sk->pool->user_credentials->has_scram_keys) send_scram_keys = true; return send_one_fd(admin, sbuf_socket(&sk->sbuf), is_server_socket(sk) ? "server" : "client", sk->login_user_credentials ? sk->login_user_credentials->name : NULL, sk->pool ? sk->pool->db->name : NULL, pga_ntop(addr, addrbuf, sizeof(addrbuf)), pga_port(addr), ckey, sk->link ? sbuf_socket(&sk->link->sbuf) : 0, client_encoding ? client_encoding->str : NULL, std_strings ? std_strings->str : NULL, datestyle ? datestyle->str : NULL, timezone ? timezone->str : NULL, password, send_scram_keys ? sk->pool->user_credentials->scram_ClientKey : NULL, send_scram_keys ? (int) sizeof(sk->pool->user_credentials->scram_ClientKey) : -1, send_scram_keys ? sk->pool->user_credentials->scram_ServerKey : NULL, send_scram_keys ? (int) sizeof(sk->pool->user_credentials->scram_ServerKey) : -1); } static bool show_pooler_cb(void *arg, int fd, const PgAddr *a) { char buf[PGADDR_BUF]; return send_one_fd(arg, fd, "pooler", NULL, NULL, pga_ntop(a, buf, sizeof(buf)), pga_port(a), 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, -1, NULL, -1); } /* send a row with sendmsg, optionally attaching a fd */ static bool show_pooler_fds(PgSocket *admin) { return for_each_pooler_fd(show_pooler_cb, admin); } static bool show_fds_from_list(PgSocket *admin, struct StatList *list) { struct List *item; PgSocket *sk; bool res = true; statlist_for_each(item, list) { sk = container_of(item, PgSocket, head); res = show_one_fd(admin, sk); if (!res) break; } return res; } /* * Command: SHOW FDS * * If privileged connection, send also actual fds */ static bool admin_show_fds(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; bool res; /* * Dangerous to show to everybody: * - can lock pooler as code flips async option * - show cancel keys for all users * - shows passwords (md5) for dynamic users */ if (!admin->admin_user) return admin_error(admin, "admin access needed"); /* * It's very hard to send it reliably over in async manner, * so turn async off for this resultset. */ socket_set_nonblocking(sbuf_socket(&admin->sbuf), 0); /* * send resultset */ SEND_RowDescription(res, admin, "issssiqisssssbb", "fd", "task", "user", "database", "addr", "port", "cancel", "link", "client_encoding", "std_strings", "datestyle", "timezone", "password", "scram_client_key", "scram_server_key"); if (res) res = show_pooler_fds(admin); if (res) res = show_fds_from_list(admin, &login_client_list); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; res = res && show_fds_from_list(admin, &pool->active_client_list); res = res && show_fds_from_list(admin, &pool->waiting_client_list); res = res && show_fds_from_list(admin, &pool->active_server_list); res = res && show_fds_from_list(admin, &pool->idle_server_list); res = res && show_fds_from_list(admin, &pool->used_server_list); res = res && show_fds_from_list(admin, &pool->tested_server_list); res = res && show_fds_from_list(admin, &pool->new_server_list); if (!res) break; } if (res) res = admin_ready(admin, "SHOW"); /* turn async back on */ socket_set_nonblocking(sbuf_socket(&admin->sbuf), 1); return res; } /* Command: SHOW DATABASES */ static bool admin_show_databases(PgSocket *admin, const char *arg) { PgDatabase *db; struct List *item; const char *f_user; PktBuf *buf; struct CfValue cv; struct CfValue load_balance_hosts_lookup; const char *pool_mode_str; usec_t server_lifetime_secs; const char *load_balance_hosts_str; cv.extra = pool_mode_map; load_balance_hosts_lookup.extra = load_balance_hosts_map; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "ssissiiiissiiiiii", "name", "host", "port", "database", "force_user", "pool_size", "min_pool_size", "reserve_pool_size", "server_lifetime", "pool_mode", "load_balance_hosts", "max_connections", "current_connections", "max_client_connections", "current_client_connections", "paused", "disabled"); statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); server_lifetime_secs = (db->server_lifetime > 0 ? db->server_lifetime : cf_server_lifetime) / USEC; f_user = db->forced_user_credentials ? db->forced_user_credentials->name : NULL; pool_mode_str = NULL; load_balance_hosts_str = NULL; cv.value_p = &db->pool_mode; load_balance_hosts_lookup.value_p = &db->load_balance_hosts; if (db->pool_mode != POOL_INHERIT) pool_mode_str = cf_get_lookup(&cv); if (db->host && strchr(db->host, ',')) load_balance_hosts_str = cf_get_lookup(&load_balance_hosts_lookup); pktbuf_write_DataRow(buf, "ssissiiiissiiiiii", db->name, db->host, db->port, db->dbname, f_user, db->pool_size >= 0 ? db->pool_size : cf_default_pool_size, db->min_pool_size >= 0 ? db->min_pool_size : cf_min_pool_size, db->res_pool_size >= 0 ? db->res_pool_size : cf_res_pool_size, server_lifetime_secs, pool_mode_str, load_balance_hosts_str, database_max_connections(db), db->connection_count, database_max_client_connections(db), db->client_connection_count, db->db_paused, db->db_disabled); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW PEERS */ static bool admin_show_peers(PgSocket *admin, const char *arg) { PgDatabase *peer; struct List *item; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "isii", "peer_id", "host", "port", "pool_size"); statlist_for_each(item, &peer_list) { peer = container_of(item, PgDatabase, head); pktbuf_write_DataRow(buf, "isii", peer->peer_id, peer->host, peer->port, peer->pool_size >= 0 ? peer->pool_size : cf_default_pool_size); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW LISTS */ static bool admin_show_lists(PgSocket *admin, const char *arg) { PktBuf *buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "si", "list", "items"); #define SENDLIST(name, size) pktbuf_write_DataRow(buf, "si", (name), (size)) SENDLIST("databases", statlist_count(&database_list)); SENDLIST("users", statlist_count(&user_list)); SENDLIST("peers", statlist_count(&peer_list)); SENDLIST("pools", statlist_count(&pool_list)); SENDLIST("peer_pools", statlist_count(&peer_pool_list)); SENDLIST("free_clients", slab_free_count(client_cache)); SENDLIST("used_clients", slab_active_count(client_cache)); SENDLIST("login_clients", statlist_count(&login_client_list)); SENDLIST("free_servers", slab_free_count(server_cache)); SENDLIST("used_servers", slab_active_count(server_cache)); { int names, zones, qry, pend; adns_info(adns, &names, &zones, &qry, &pend); SENDLIST("dns_names", names); SENDLIST("dns_zones", zones); SENDLIST("dns_queries", qry); SENDLIST("dns_pending", pend); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW USERS */ static bool admin_show_users(PgSocket *admin, const char *arg) { struct List *item; PktBuf *buf = pktbuf_dynamic(256); struct CfValue cv; char pool_size_str[12] = ""; char res_pool_size_str[12] = ""; const char *pool_mode_str; if (!buf) { admin_error(admin, "no mem"); return true; } cv.extra = pool_mode_map; pktbuf_write_RowDescription( buf, "ssssiiii", "name", "pool_size", "reserve_pool_size", "pool_mode", "max_user_connections", "current_connections", "max_user_client_connections", "current_client_connections"); statlist_for_each(item, &user_list) { PgGlobalUser *user = container_of(item, PgGlobalUser, head); if (user->pool_size >= 0) snprintf(pool_size_str, sizeof(pool_size_str), "%9d", user->pool_size); if (user->res_pool_size >= 0) snprintf(res_pool_size_str, sizeof(res_pool_size_str), "%9d", user->res_pool_size); pool_mode_str = NULL; cv.value_p = &user->pool_mode; if (user->pool_mode != POOL_INHERIT) pool_mode_str = cf_get_lookup(&cv); pktbuf_write_DataRow(buf, "ssssiiii", user->credentials.name, pool_size_str, res_pool_size_str, pool_mode_str, user_max_connections(user), user->connection_count, user_client_max_connections(user), user->client_connection_count ); } admin_flush(admin, buf, "SHOW"); return true; } #define SKF_STD "ssssssisiTTiiississii" #define SKF_DBG "ssssssisiTTiiississiiiiiiiii" static void socket_header(PktBuf *buf, bool debug) { pktbuf_write_RowDescription(buf, debug ? SKF_DBG : SKF_STD, "type", "user", "database", "replication", "state", "addr", "port", "local_addr", "local_port", "connect_time", "request_time", "wait", "wait_us", "close_needed", "ptr", "link", "remote_pid", "tls", "application_name", "prepared_statements", "id", /* debug follows */ "recv_pos", "pkt_pos", "pkt_remain", "send_pos", "send_remain", "pkt_avail", "send_avail"); } static void adr2txt(const PgAddr *adr, char *dst, unsigned dstlen) { pga_ntop(adr, dst, dstlen); } static void socket_row(PktBuf *buf, PgSocket *sk, const char *state, bool debug) { int pkt_avail = 0, send_avail = 0; int remote_pid; int prepared_statement_count = 0; char ptrbuf[128], linkbuf[128]; char l_addr[PGADDR_BUF], r_addr[PGADDR_BUF]; IOBuf *io = sk->sbuf.io; char infobuf[96] = ""; VarCache *v = &sk->vars; const struct PStr *application_name = v->var_list[VAppName]; usec_t now = get_cached_time(); usec_t wait_time = sk->query_start ? now - sk->query_start : 0; char *replication; if (io) { pkt_avail = iobuf_amount_parse(sk->sbuf.io); send_avail = iobuf_amount_pending(sk->sbuf.io); } adr2txt(&sk->remote_addr, r_addr, sizeof(r_addr)); adr2txt(&sk->local_addr, l_addr, sizeof(l_addr)); snprintf(ptrbuf, sizeof(ptrbuf), "%p", sk); if (sk->link) snprintf(linkbuf, sizeof(linkbuf), "%p", sk->link); else linkbuf[0] = 0; /* get pid over unix socket */ if (pga_is_unix(&sk->remote_addr)) remote_pid = sk->remote_addr.scred.pid; else remote_pid = 0; /* if that failed, get it from cancel key */ if (is_server_socket(sk) && remote_pid == 0) remote_pid = be32dec(sk->cancel_key); if (sk->sbuf.tls) tls_get_connection_info(sk->sbuf.tls, infobuf, sizeof infobuf); if (is_server_socket(sk)) prepared_statement_count = HASH_COUNT(sk->server_prepared_statements); else prepared_statement_count = HASH_COUNT(sk->client_prepared_statements); if (sk->replication == REPLICATION_NONE) replication = "none"; else if (sk->replication == REPLICATION_LOGICAL) replication = "logical"; else replication = "physical"; pktbuf_write_DataRow(buf, debug ? SKF_DBG : SKF_STD, is_server_socket(sk) ? "S" : "C", sk->login_user_credentials ? sk->login_user_credentials->name : "(nouser)", sk->pool && !sk->pool->db->peer_id ? sk->pool->db->name : "(nodb)", replication, state, r_addr, pga_port(&sk->remote_addr), l_addr, pga_port(&sk->local_addr), sk->connect_time, sk->request_time, (int)(wait_time / USEC), (int)(wait_time % USEC), sk->close_needed, ptrbuf, linkbuf, remote_pid, infobuf, application_name ? application_name->str : "", prepared_statement_count, sk->id, /* debug */ io ? io->recv_pos : 0, io ? io->parse_pos : 0, sk->sbuf.pkt_remain, io ? io->done_pos : 0, 0, pkt_avail, send_avail); } /* Helper for SHOW CLIENTS/SERVERS/SOCKETS */ static void show_socket_list(PktBuf *buf, struct StatList *list, const char *state, bool debug) { struct List *item; PgSocket *sk; statlist_for_each(item, list) { sk = container_of(item, PgSocket, head); socket_row(buf, sk, state, debug); } } /* Command: SHOW CLIENTS */ static bool admin_show_clients(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, false); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_client_list, "active", false); show_socket_list(buf, &pool->waiting_client_list, "waiting", false); show_socket_list(buf, &pool->active_cancel_req_list, "active_cancel_req", false); show_socket_list(buf, &pool->waiting_cancel_req_list, "waiting_cancel_req", false); } statlist_for_each(item, &peer_pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_cancel_req_list, "active_cancel_req", false); show_socket_list(buf, &pool->waiting_cancel_req_list, "waiting_cancel_req", false); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW SERVERS */ static bool admin_show_servers(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, false); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_server_list, "active", false); show_socket_list(buf, &pool->idle_server_list, "idle", false); show_socket_list(buf, &pool->used_server_list, "used", false); show_socket_list(buf, &pool->tested_server_list, "tested", false); show_socket_list(buf, &pool->new_server_list, "new", false); show_socket_list(buf, &pool->active_cancel_server_list, "active_cancel", false); show_socket_list(buf, &pool->being_canceled_server_list, "being_canceled", false); } statlist_for_each(item, &peer_pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->new_server_list, "new", false); show_socket_list(buf, &pool->active_cancel_server_list, "active_cancel", false); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW SOCKETS */ static bool admin_show_sockets(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, true); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_client_list, "cl_active", true); show_socket_list(buf, &pool->waiting_client_list, "cl_waiting", true); show_socket_list(buf, &pool->active_server_list, "sv_active", true); show_socket_list(buf, &pool->idle_server_list, "sv_idle", true); show_socket_list(buf, &pool->used_server_list, "sv_used", true); show_socket_list(buf, &pool->tested_server_list, "sv_tested", true); show_socket_list(buf, &pool->new_server_list, "sv_login", true); } show_socket_list(buf, &login_client_list, "cl_login", true); admin_flush(admin, buf, "SHOW"); return true; } static void show_active_socket_list(PktBuf *buf, struct StatList *list, const char *state) { struct List *item; statlist_for_each(item, list) { PgSocket *sk = container_of(item, PgSocket, head); if (!sbuf_is_empty(&sk->sbuf)) socket_row(buf, sk, state, true); } } /* Command: SHOW ACTIVE_SOCKETS */ static bool admin_show_active_sockets(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, true); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_active_socket_list(buf, &pool->active_client_list, "cl_active"); show_active_socket_list(buf, &pool->waiting_client_list, "cl_waiting"); show_active_socket_list(buf, &pool->active_server_list, "sv_active"); show_active_socket_list(buf, &pool->idle_server_list, "sv_idle"); show_active_socket_list(buf, &pool->used_server_list, "sv_used"); show_active_socket_list(buf, &pool->tested_server_list, "sv_tested"); show_active_socket_list(buf, &pool->new_server_list, "sv_login"); } show_active_socket_list(buf, &login_client_list, "cl_login"); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW POOLS */ static bool admin_show_pools(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; PgSocket *waiter; usec_t now = get_cached_time(); usec_t max_wait; struct CfValue cv; struct CfValue load_balance_hosts_lookup; int pool_mode; const char *load_balance_hosts_str; cv.extra = pool_mode_map; cv.value_p = &pool_mode; load_balance_hosts_lookup.extra = load_balance_hosts_map; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "ssiiiiiiiiiiiiiss", "database", "user", "cl_active", "cl_waiting", "cl_active_cancel_req", "cl_waiting_cancel_req", "sv_active", "sv_active_cancel", "sv_being_canceled", "sv_idle", "sv_used", "sv_tested", "sv_login", "maxwait", "maxwait_us", "pool_mode", "load_balance_hosts"); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); waiter = first_socket(&pool->waiting_client_list); max_wait = (waiter && waiter->query_start) ? now - waiter->query_start : 0; pool_mode = probably_wrong_pool_pool_mode(pool); load_balance_hosts_str = NULL; load_balance_hosts_lookup.value_p = &pool->db->load_balance_hosts; if (pool->db->host && strchr(pool->db->host, ',')) load_balance_hosts_str = cf_get_lookup(&load_balance_hosts_lookup); pktbuf_write_DataRow(buf, "ssiiiiiiiiiiiiiss", pool->db->name, pool->user_credentials->name, statlist_count(&pool->active_client_list), statlist_count(&pool->waiting_client_list), statlist_count(&pool->active_cancel_req_list), statlist_count(&pool->waiting_cancel_req_list), statlist_count(&pool->active_server_list), statlist_count(&pool->active_cancel_server_list), statlist_count(&pool->being_canceled_server_list), statlist_count(&pool->idle_server_list), statlist_count(&pool->used_server_list), statlist_count(&pool->tested_server_list), statlist_count(&pool->new_server_list), /* how long is the oldest client waited */ (int)(max_wait / USEC), (int)(max_wait % USEC), cf_get_lookup(&cv), load_balance_hosts_str); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW PEER_POOLS */ static bool admin_show_peer_pools(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "iiiii", "peer_id", "cl_active_cancel_req", "cl_waiting_cancel_req", "sv_active_cancel", "sv_login"); statlist_for_each(item, &peer_pool_list) { pool = container_of(item, PgPool, head); pktbuf_write_DataRow(buf, "iiiii", pool->db->peer_id, statlist_count(&pool->active_cancel_req_list), statlist_count(&pool->waiting_cancel_req_list), statlist_count(&pool->active_cancel_server_list), statlist_count(&pool->new_server_list)); } admin_flush(admin, buf, "SHOW"); return true; } static void slab_stat_cb(void *arg, const char *slab_name, unsigned size, unsigned free, unsigned total) { PktBuf *buf = arg; unsigned alloc = total * size; pktbuf_write_DataRow(buf, "siiii", slab_name, size, total - free, free, alloc); } /* Command: SHOW MEM */ static bool admin_show_mem(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "siiii", "name", "size", "used", "free", "memtotal"); slab_stats(slab_stat_cb, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW STATE */ static bool admin_show_state(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(64); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "ss", "key", "value"); pktbuf_write_DataRow(buf, "ss", "active", (cf_pause_mode == P_NONE) ? "yes" : "no"); pktbuf_write_DataRow(buf, "ss", "paused", (cf_pause_mode == P_PAUSE) ? "yes" : "no"); pktbuf_write_DataRow(buf, "ss", "suspended", (cf_pause_mode == P_SUSPEND) ? "yes" : "no"); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW DNS_HOSTS */ static void dns_name_cb(void *arg, const char *name, const struct addrinfo *ai, usec_t ttl) { PktBuf *buf = arg; char *s, *end; char adrs[1024]; usec_t now = get_cached_time(); end = adrs + sizeof(adrs) - 2; for (s = adrs; ai && s < end; ai = ai->ai_next) { if (s != adrs) *s++ = ','; sa2str(ai->ai_addr, s, end - s); s += strlen(s); } *s = 0; /* * Ttl can be smaller than now if we are waiting for dns reply for long. * * It's better to show 0 in that case as otherwise it confuses users into * thinking that there is large ttl for the name. */ pktbuf_write_DataRow(buf, "sqs", name, ttl < now ? 0 : (ttl - now) / USEC, adrs); } static bool admin_show_dns_hosts(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sqs", "hostname", "ttl", "addrs"); adns_walk_names(adns, dns_name_cb, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW DNS_ZONES */ static void dns_zone_cb(void *arg, const char *name, uint32_t serial, int nhosts) { PktBuf *buf = arg; pktbuf_write_DataRow(buf, "sqi", name, (uint64_t)serial, nhosts); } static bool admin_show_dns_zones(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sqi", "zonename", "serial", "count"); adns_walk_zones(adns, dns_zone_cb, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW CONFIG */ static void show_one_param(void *arg, const char *name, const char *val, const char *defval, bool reloadable) { PktBuf *buf = arg; pktbuf_write_DataRow(buf, "ssss", name, val, defval, reloadable ? "yes" : "no"); } static bool admin_show_config(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "ssss", "key", "value", "default", "changeable"); config_for_each(show_one_param, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: RELOAD */ static bool admin_cmd_reload(PgSocket *admin, const char *arg) { if (arg && *arg) return syntax_error(admin); if (!admin->admin_user) return admin_error(admin, "admin access needed"); log_info("RELOAD command issued"); load_config(); if (!sbuf_tls_setup()) log_error("TLS configuration could not be reloaded, keeping old configuration"); return admin_ready(admin, "RELOAD"); } /* Command: SHUTDOWN */ static bool admin_cmd_shutdown(PgSocket *admin, const char *arg) { enum ShutDownMode mode = SHUTDOWN_IMMEDIATE; if (arg && *arg) { if (strcasecmp(arg, "WAIT_FOR_CLIENTS") == 0) mode = SHUTDOWN_WAIT_FOR_CLIENTS; else if (strcasecmp(arg, "WAIT_FOR_SERVERS") == 0) mode = SHUTDOWN_WAIT_FOR_SERVERS; else return syntax_error(admin); } if (!admin->admin_user) return admin_error(admin, "admin access needed"); /* * note: new pooler expects unix socket file gone when it gets * event from fd. Currently atexit() cleanup should be called * before closing open sockets. */ cf_shutdown = mode; if (mode == SHUTDOWN_IMMEDIATE) { log_info("SHUTDOWN command issued"); event_base_loopbreak(pgb_event_base); /* * By not running admin_ready the connection is kept open * until the process is actually shut down. */ return true; } else { if (mode == SHUTDOWN_WAIT_FOR_SERVERS) { cf_pause_mode = P_PAUSE; log_info("SHUTDOWN WAIT_FOR_SERVERS command issued"); } else { log_info("SHUTDOWN WAIT_FOR_CLIENTS command issued"); } cleanup_sockets(); return admin_ready(admin, "SHUTDOWN"); } } static void full_resume(void) { int tmp_mode = cf_pause_mode; cf_pause_mode = P_NONE; if (tmp_mode == P_SUSPEND) resume_all(); } /* Command: RESUME */ static bool admin_cmd_resume(PgSocket *admin, const char *arg) { if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) { log_info("RESUME command issued"); if (cf_shutdown) { return admin_error(admin, "pooler is shutting down"); } else if (cf_pause_mode != P_NONE) { full_resume(); } else { return admin_error(admin, "pooler is not paused/suspended"); } } else { PgDatabase *db = find_database(arg); log_info("RESUME '%s' command issued", arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (!db->db_paused) return admin_error(admin, "database %s is not paused", arg); db->db_paused = false; } return admin_ready(admin, "RESUME"); } /* Command: SUSPEND */ static bool admin_cmd_suspend(PgSocket *admin, const char *arg) { if (arg && *arg) return syntax_error(admin); if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (cf_pause_mode) return admin_error(admin, "already suspended/paused"); /* suspend needs to be able to flush buffers */ if (count_paused_databases() > 0) return admin_error(admin, "cannot suspend with paused databases"); log_info("SUSPEND command issued"); cf_pause_mode = P_SUSPEND; admin->wait_for_response = true; suspend_pooler(); g_suspend_start = get_cached_time(); return true; } /* Command: PAUSE */ static bool admin_cmd_pause(PgSocket *admin, const char *arg) { if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (cf_pause_mode) return admin_error(admin, "already suspended/paused"); if (!arg[0]) { log_info("PAUSE command issued"); cf_pause_mode = P_PAUSE; admin->wait_for_response = true; } else { PgDatabase *db; log_info("PAUSE '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db == admin->pool->db) return admin_error(admin, "cannot pause admin db: %s", arg); db->db_paused = true; if (count_db_active(db) > 0) admin->wait_for_response = true; else return admin_ready(admin, "PAUSE"); } return true; } /* Command: RECONNECT */ static bool admin_cmd_reconnect(PgSocket *admin, const char *arg) { if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) { struct List *item; PgPool *pool; log_info("RECONNECT command issued"); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; tag_database_dirty(pool->db); } } else { PgDatabase *db; log_info("RECONNECT '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db == admin->pool->db) return admin_error(admin, "cannot reconnect admin db: %s", arg); tag_database_dirty(db); } return admin_ready(admin, "RECONNECT"); } /* Command: DISABLE */ static bool admin_cmd_disable(PgSocket *admin, const char *arg) { PgDatabase *db; if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) return admin_error(admin, "a database is required"); log_info("DISABLE '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db->admin) return admin_error(admin, "cannot disable admin db: %s", arg); db->db_disabled = true; return admin_ready(admin, "DISABLE"); } /* Command: ENABLE */ static bool admin_cmd_enable(PgSocket *admin, const char *arg) { PgDatabase *db; if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) return admin_error(admin, "a database is required"); log_info("ENABLE '%s' command issued", arg); db = find_database(arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db->admin) return admin_error(admin, "cannot disable admin db: %s", arg); db->db_disabled = false; return admin_ready(admin, "ENABLE"); } static PgSocket *find_socket_in_list(unsigned long long int target_id, struct StatList *sockets) { struct List *item; PgSocket *socket; statlist_for_each(item, sockets) { socket = container_of(item, PgSocket, head); if (target_id == socket->id) { return socket; } } return NULL; } static PgSocket *find_client_global(unsigned long long int target_id) { PgSocket *kill_client; struct List *item; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); kill_client = find_socket_in_list(target_id, &pool->active_client_list); if (kill_client != NULL) { return kill_client; } kill_client = find_socket_in_list(target_id, &pool->waiting_client_list); if (kill_client != NULL) { return kill_client; } kill_client = find_socket_in_list(target_id, &pool->active_cancel_req_list); if (kill_client != NULL) { return kill_client; } kill_client = find_socket_in_list(target_id, &pool->waiting_cancel_req_list); if (kill_client != NULL) { return kill_client; } } statlist_for_each(item, &peer_pool_list) { pool = container_of(item, PgPool, head); kill_client = find_socket_in_list(target_id, &pool->active_cancel_req_list); if (kill_client != NULL) { return kill_client; } kill_client = find_socket_in_list(target_id, &pool->waiting_cancel_req_list); if (kill_client != NULL) { return kill_client; } } return NULL; } /* Command: KILL_CLIENT */ static bool admin_cmd_kill_client(PgSocket *admin, const char *arg) { PgSocket *kill_client; unsigned long long int target_id = 0; if (sscanf(arg, "%llu", &target_id) != 1) { return admin_error(admin, "invalid client pointer supplied"); } kill_client = find_client_global((unsigned long long int) target_id); if (kill_client == NULL) { return admin_error(admin, "client not found"); } disconnect_client(kill_client, true, "admin forced disconnect"); return admin_ready(admin, "KILL_CLIENT"); } /* Command: KILL */ static bool admin_cmd_kill(PgSocket *admin, const char *arg) { struct List *item, *tmp; PgDatabase *db; PgPool *pool; if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (cf_pause_mode) return admin_error(admin, "already suspended/paused"); if (!arg[0]) return admin_error(admin, "a database is required"); log_info("KILL '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db == admin->pool->db) return admin_error(admin, "cannot kill admin db: %s", arg); db->db_paused = true; statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db == db) kill_pool(pool); } return admin_ready(admin, "KILL"); } /* Command: WAIT_CLOSE */ static bool admin_cmd_wait_close(PgSocket *admin, const char *arg) { if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) { struct List *item; PgPool *pool; int active = 0; log_info("WAIT_CLOSE command issued"); statlist_for_each(item, &pool_list) { PgDatabase *db; pool = container_of(item, PgPool, head); db = pool->db; db->db_wait_close = true; active += count_db_active(db); } if (active > 0) admin->wait_for_response = true; else return admin_ready(admin, "WAIT_CLOSE"); } else { PgDatabase *db; log_info("WAIT_CLOSE '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db == admin->pool->db) return admin_error(admin, "cannot wait in admin db: %s", arg); db->db_wait_close = true; if (count_db_active(db) > 0) admin->wait_for_response = true; else return admin_ready(admin, "WAIT_CLOSE"); } return true; } /* extract substring from regex group */ static bool copy_arg(const char *src, regmatch_t *glist, int gnum, char *dst, unsigned dstmax, char qchar) { regmatch_t *g = &glist[gnum]; unsigned len; const char *s; char *d = dst; unsigned i; /* no match, if regex allows, it must be fine */ if (g->rm_so < 0 || g->rm_eo < 0) { dst[0] = 0; return true; } len = g->rm_eo - g->rm_so; s = src + g->rm_so; /* too big value */ if (len >= dstmax) { dst[0] = 0; return false; } /* copy and unquote */ if (*s == qchar) { for (i = 1; i < len - 1; i++) { if (s[i] == qchar && s[i + 1] == qchar) i++; *d++ = s[i]; } len = d - dst; } else { memcpy(dst, s, len); } dst[len] = 0; return true; } static bool admin_show_help(PgSocket *admin, const char *arg) { bool res; SEND_generic(res, admin, PqMsg_NoticeResponse, "sssss", "SNOTICE", "C00000", "MConsole usage", "D\n\tSHOW HELP|CONFIG|DATABASES" "|POOLS|CLIENTS|SERVERS|USERS|VERSION\n" "\tSHOW PEERS|PEER_POOLS\n" "\tSHOW FDS|SOCKETS|ACTIVE_SOCKETS|LISTS|MEM|STATE\n" "\tSHOW DNS_HOSTS|DNS_ZONES\n" "\tSHOW STATS|STATS_TOTALS|STATS_AVERAGES|TOTALS\n" "\tSET key = arg\n" "\tRELOAD\n" "\tPAUSE []\n" "\tRESUME []\n" "\tDISABLE \n" "\tENABLE \n" "\tRECONNECT []\n" "\tKILL \n" "\tKILL_CLIENT \n" "\tSUSPEND\n" "\tSHUTDOWN\n" "\tSHUTDOWN WAIT_FOR_SERVERS|WAIT_FOR_CLIENTS\n" "\tWAIT_CLOSE []", ""); if (res) res = admin_ready(admin, "SHOW"); return res; } static bool admin_show_version(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(128); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "s", "version"); pktbuf_write_DataRow(buf, "s", PACKAGE_STRING); admin_flush(admin, buf, "SHOW"); return true; } static bool admin_show_stats(PgSocket *admin, const char *arg) { return admin_database_stats(admin, &pool_list); } static bool admin_show_stats_totals(PgSocket *admin, const char *arg) { return admin_database_stats_totals(admin, &pool_list); } static bool admin_show_stats_averages(PgSocket *admin, const char *arg) { return admin_database_stats_averages(admin, &pool_list); } static bool admin_show_totals(PgSocket *admin, const char *arg) { return show_stat_totals(admin, &pool_list); } static struct cmd_lookup show_map [] = { {"clients", admin_show_clients}, {"config", admin_show_config}, {"databases", admin_show_databases}, {"fds", admin_show_fds}, {"help", admin_show_help}, {"lists", admin_show_lists}, {"peers", admin_show_peers}, {"peer_pools", admin_show_peer_pools}, {"pools", admin_show_pools}, {"servers", admin_show_servers}, {"sockets", admin_show_sockets}, {"active_sockets", admin_show_active_sockets}, {"stats", admin_show_stats}, {"stats_totals", admin_show_stats_totals}, {"stats_averages", admin_show_stats_averages}, {"users", admin_show_users}, {"version", admin_show_version}, {"totals", admin_show_totals}, {"mem", admin_show_mem}, {"dns_hosts", admin_show_dns_hosts}, {"dns_zones", admin_show_dns_zones}, {"state", admin_show_state}, {NULL, NULL} }; static bool admin_cmd_show(PgSocket *admin, const char *arg) { if (fake_show(admin, arg)) return true; return exec_cmd(show_map, admin, arg, NULL); } static struct cmd_lookup cmd_list [] = { {"disable", admin_cmd_disable}, {"enable", admin_cmd_enable}, {"kill", admin_cmd_kill}, {"kill_client", admin_cmd_kill_client}, {"pause", admin_cmd_pause}, {"reconnect", admin_cmd_reconnect}, {"reload", admin_cmd_reload}, {"resume", admin_cmd_resume}, {"select", admin_cmd_show}, {"show", admin_cmd_show}, {"shutdown", admin_cmd_shutdown}, {"suspend", admin_cmd_suspend}, {"wait_close", admin_cmd_wait_close}, {NULL, NULL} }; /* handle user query */ static bool admin_parse_query(PgSocket *admin, const char *q) { regmatch_t grp[MAX_GROUPS]; char cmd[16]; char arg[64]; char val[256]; bool res; bool ok; current_query = q; if (regexec(&rc_cmd, q, MAX_GROUPS, grp, 0) == 0) { ok = copy_arg(q, grp, CMD_NAME, cmd, sizeof(cmd), '"'); if (!ok) goto failed; ok = copy_arg(q, grp, CMD_ARG, arg, sizeof(arg), '"'); if (!ok) goto failed; res = exec_cmd(cmd_list, admin, cmd, arg); } else if (regexec(&rc_set_str, q, MAX_GROUPS, grp, 0) == 0) { ok = copy_arg(q, grp, SET_KEY, arg, sizeof(arg), '"'); if (!ok || !arg[0]) goto failed; ok = copy_arg(q, grp, SET_VAL, val, sizeof(val), '\''); if (!ok) goto failed; res = admin_set(admin, arg, val); } else if (regexec(&rc_set_word, q, MAX_GROUPS, grp, 0) == 0) { ok = copy_arg(q, grp, SET_KEY, arg, sizeof(arg), '"'); if (!ok || !arg[0]) goto failed; ok = copy_arg(q, grp, SET_VAL, val, sizeof(val), '"'); if (!ok) goto failed; res = admin_set(admin, arg, val); } else { res = syntax_error(admin); } done: current_query = NULL; if (!res) disconnect_client(admin, true, "failure"); return res; failed: res = admin_error(admin, "bad arguments"); goto done; } /* handle packets */ bool admin_handle_client(PgSocket *admin, PktHdr *pkt) { const char *q; bool res; /* don't tolerate partial packets */ if (incomplete_pkt(pkt)) { disconnect_client(admin, true, "incomplete pkt"); return false; } switch (pkt->type) { case PqMsg_Query: if (!mbuf_get_string(&pkt->data, &q)) { disconnect_client(admin, true, "incomplete query"); return false; } log_debug("got admin query: %s", q); res = admin_parse_query(admin, q); if (res) sbuf_prepare_skip(&admin->sbuf, pkt->len); return res; case PqMsg_Terminate: disconnect_client(admin, false, "close req"); break; case PqMsg_Parse: case PqMsg_Bind: case PqMsg_Execute: /* * Effectively the same as the default case, but give * a more helpful error message in these cases. */ admin_error(admin, "extended query protocol not supported by admin console"); disconnect_client(admin, true, "bad packet"); break; default: admin_error(admin, "unsupported packet type for admin console: %d", pkt_desc(pkt)); disconnect_client(admin, true, "bad packet"); break; } return false; } /** * Client is unauthenticated, look if it wants to connect * to special "pgbouncer" user. */ bool admin_pre_login(PgSocket *client, const char *username) { client->admin_user = false; client->own_user = false; /* tag same uid as special */ if (pga_is_unix(&client->remote_addr)) { uid_t peer_uid; gid_t peer_gid; int res; res = getpeereid(sbuf_socket(&client->sbuf), &peer_uid, &peer_gid); if (res >= 0 && peer_uid == getuid() && strcmp("pgbouncer", username) == 0) { client->login_user_credentials = admin_pool->db->forced_user_credentials; client->own_user = true; client->admin_user = true; if (!check_db_connection_count(client)) return false; if (cf_log_connections) slog_info(client, "pgbouncer access from unix socket"); return true; } } /* * auth_type=any does not keep original username around, * so username based check has to take place here */ if (cf_auth_type == AUTH_TYPE_ANY) { if (strlist_contains(cf_admin_users, username)) { client->login_user_credentials = admin_pool->db->forced_user_credentials; client->admin_user = true; if (!check_db_connection_count(client)) return false; return true; } else if (strlist_contains(cf_stats_users, username)) { client->login_user_credentials = admin_pool->db->forced_user_credentials; if (!check_db_connection_count(client)) return false; if (!check_user_connection_count(client)) return false; return true; } } return false; } bool admin_post_login(PgSocket *client) { const char *username = client->login_user_credentials->name; if (cf_auth_type == AUTH_TYPE_ANY) return true; if (client->admin_user || strlist_contains(cf_admin_users, username)) { client->admin_user = true; return true; } else if (strlist_contains(cf_stats_users, username)) { return true; } disconnect_client(client, true, "not allowed"); return false; } /* init special database and query parsing */ void admin_setup(void) { PgDatabase *db; PgPool *pool; PgGlobalUser *user; PktBuf *msg; int res; /* fake database */ db = add_database("pgbouncer"); if (!db) die("no memory for admin database"); db->port = cf_listen_port; db->pool_size = 2; db->admin = true; db->pool_mode = POOL_STMT; if (!force_user_credentials(db, "pgbouncer", "")) die("no mem on startup - cannot alloc pgbouncer user"); /* fake pool */ pool = get_pool(db, db->forced_user_credentials); if (!pool) die("cannot create admin pool?"); admin_pool = pool; /* find an existing user or create a new fake user with disabled password */ user = find_or_add_new_global_user("pgbouncer", ""); if (!user) { die("cannot create admin user?"); } /* prepare welcome */ msg = pktbuf_dynamic(128); if (!msg) die("out of memory"); pktbuf_write_AuthenticationOk(msg); pktbuf_write_ParameterStatus(msg, "server_version", PACKAGE_VERSION "/bouncer"); pktbuf_write_ParameterStatus(msg, "client_encoding", "UTF8"); pktbuf_write_ParameterStatus(msg, "server_encoding", "UTF8"); pktbuf_write_ParameterStatus(msg, "DateStyle", "ISO"); pktbuf_write_ParameterStatus(msg, "TimeZone", "GMT"); pktbuf_write_ParameterStatus(msg, "standard_conforming_strings", "on"); pktbuf_write_ParameterStatus(msg, "is_superuser", "on"); if (msg->failed) die("admin welcome failed"); pool->welcome_msg = msg; pool->welcome_msg_ready = true; msg = pktbuf_dynamic(128); if (!msg) die("cannot create admin startup pkt"); db->startup_params = msg; pktbuf_put_string(msg, "database"); db->dbname = "pgbouncer"; pktbuf_put_string(msg, db->dbname); /* initialize regexes */ res = regcomp(&rc_cmd, cmd_normal_rx, REG_EXTENDED | REG_ICASE); if (res != 0) fatal("cmd regex compilation error"); res = regcomp(&rc_set_word, cmd_set_word_rx, REG_EXTENDED | REG_ICASE); if (res != 0) fatal("set/word regex compilation error"); res = regcomp(&rc_set_str, cmd_set_str_rx, REG_EXTENDED | REG_ICASE); if (res != 0) fatal("set/str regex compilation error"); } void admin_pause_done(void) { struct List *item, *tmp; PgSocket *admin; bool res; statlist_for_each_safe(item, &admin_pool->active_client_list, tmp) { admin = container_of(item, PgSocket, head); if (!admin->wait_for_response) continue; switch (cf_pause_mode) { case P_PAUSE: res = admin_ready(admin, "PAUSE"); break; case P_SUSPEND: res = admin_ready(admin, "SUSPEND"); break; default: if (count_paused_databases() > 0) { res = admin_ready(admin, "PAUSE"); } else { /* FIXME */ fatal("admin_pause_done: bad state"); res = false; } } if (!res) disconnect_client(admin, false, "dead admin"); else admin->wait_for_response = false; } if (statlist_empty(&admin_pool->active_client_list) && cf_pause_mode == P_SUSPEND) { log_info("admin disappeared when suspended, doing RESUME"); cf_pause_mode = P_NONE; resume_all(); } } void admin_wait_close_done(void) { struct List *item, *tmp; PgSocket *admin; bool res; statlist_for_each_safe(item, &admin_pool->active_client_list, tmp) { admin = container_of(item, PgSocket, head); if (!admin->wait_for_response) continue; res = admin_ready(admin, "WAIT_CLOSE"); if (!res) disconnect_client(admin, false, "dead admin"); else admin->wait_for_response = false; } } /* admin on console has pressed ^C */ void admin_handle_cancel(PgSocket *admin) { /* weird, but no reason to fail */ if (!admin->wait_for_response) slog_warning(admin, "admin cancel request for non-waiting client?"); if (cf_shutdown) return; if (cf_pause_mode != P_NONE) full_resume(); } pgbouncer-1.24.1/src/varcache.c0000644000175000000000000001645514777762222013231 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Operations with server config parameters. */ #include "bouncer.h" #include #include #include "common/uthash_lowercase.h" static int num_var_cached = 0; struct var_lookup { const char *name; /* key (string is WITHIN the structure) */ int idx; UT_hash_handle hh; /* makes this structure hashable */ }; static struct var_lookup *lookup_map; static struct StrPool *vpool; static inline struct PStr *get_value(VarCache *cache, const struct var_lookup *lk) { return cache->var_list[lk->idx]; } static bool sl_add(void *arg, const char *s) { return strlist_append(arg, s); } int get_num_var_cached(void) { return num_var_cached; } static void init_var_lookup_from_config(const char *cf_track_extra_parameters, int *num_vars) { char *var_name = NULL; struct var_lookup *lookup = NULL; struct StrList *sl = strlist_new(NULL); if (!parse_word_list(cf_track_extra_parameters, sl_add, sl)) die("failed to parse track_extra_parameters in config %s", cf_track_extra_parameters); while (!strlist_empty(sl)) { var_name = strlist_pop(sl); if (!var_name) continue; HASH_FIND_STR(lookup_map, var_name, lookup); /* If the var name is already on the hash map, do not update its idx */ if (lookup != NULL) continue; lookup = (struct var_lookup *)malloc(sizeof *lookup); lookup->name = strdup(var_name); lookup->idx = (*num_vars)++; HASH_ADD_KEYPTR(hh, lookup_map, lookup->name, strlen(lookup->name), lookup); free(var_name); } strlist_free(sl); } void init_var_lookup(const char *cf_track_extra_parameters) { const char *names[] = { "DateStyle", "client_encoding", "TimeZone", "standard_conforming_strings", "application_name", NULL }; int idx = 0; struct var_lookup *lookup = NULL; /* Always add the static list of names for compatibility */ for (; names[idx]; idx++) { lookup = (struct var_lookup *)malloc(sizeof *lookup); lookup->name = names[idx]; lookup->idx = idx; HASH_ADD_KEYPTR(hh, lookup_map, lookup->name, strlen(lookup->name), lookup); } init_var_lookup_from_config(cf_track_extra_parameters, &idx); num_var_cached = idx; } bool varcache_set(VarCache *cache, const char *key, const char *value) { const struct var_lookup *lk = NULL; struct PStr *pstr = NULL; if (!vpool) { vpool = strpool_create(USUAL_ALLOC); if (!vpool) return false; } HASH_FIND_STR(lookup_map, key, lk); if (lk == NULL) return false; /* drop old value */ strpool_decref(cache->var_list[lk->idx]); cache->var_list[lk->idx] = NULL; /* NULL value? */ if (!value) return false; /* set new value */ pstr = strpool_get(vpool, value, strlen(value)); if (!pstr) return false; cache->var_list[lk->idx] = pstr; return true; } static bool variable_is_guc_list_quote(const char *key) { if (strcasecmp("search_path", key) == 0) return true; return false; } static int apply_var(PktBuf *pkt, const char *key, const struct PStr *cval, const struct PStr *sval) { char buf[300]; char qbuf[128]; unsigned len; const char *tmp; /* if unset, skip */ if (!cval || !sval) return 0; /* if equal, skip */ if (cval == sval) return 0; /* ignore case difference */ if (strcasecmp(cval->str, sval->str) == 0) return 0; /* parameters that are marked GUC_LIST_QUOTE are returned already fully quoted * re-quoting them using pg_quote_literal will result in malformed values. */ if (variable_is_guc_list_quote(key)) { /* zero length elements of the form "" should be specially handled.*/ if (strcmp(cval->str, "\"\"") == 0) { tmp = "''"; } else { tmp = cval->str; } } else if (pg_quote_literal(qbuf, cval->str, sizeof(qbuf))) { tmp = qbuf; } else { return 0; } /* add SET statement to packet */ len = snprintf(buf, sizeof(buf), "SET %s=%s;", key, tmp); if (len < sizeof(buf)) { pktbuf_put_bytes(pkt, buf, len); } else { char *buf2 = malloc(sizeof(char)*len); if (!buf2) die("failed to allocate memory in apply_var"); snprintf(buf2, len, "SET %s=%s;", key, tmp); pktbuf_put_bytes(pkt, buf2, len); free(buf2); } return 1; } bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p) { int changes = 0; struct PStr *cval, *sval; const struct var_lookup *lk, *tmp; int sql_ofs; struct PktBuf *pkt = pktbuf_temp(); pktbuf_start_packet(pkt, PqMsg_Query); /* grab query position inside pkt */ sql_ofs = pktbuf_written(pkt); HASH_ITER(hh, lookup_map, lk, tmp) { sval = get_value(&server->vars, lk); cval = get_value(&client->vars, lk); changes += apply_var(pkt, lk->name, cval, sval); } *changes_p = changes > 0; if (!changes) return true; pktbuf_put_char(pkt, 0); pktbuf_finish_packet(pkt); slog_debug(server, "varcache_apply: %s", pkt->buf + sql_ofs); return pktbuf_send_immediate(pkt, server); } void varcache_set_canonical(PgSocket *server, PgSocket *client) { struct PStr *server_val, *client_val; const struct var_lookup *lk, *tmp; HASH_ITER(hh, lookup_map, lk, tmp) { server_val = server->vars.var_list[lk->idx]; client_val = client->vars.var_list[lk->idx]; if (client_val && server_val && client_val != server_val) { slog_debug(client, "varcache_set_canonical: setting %s to its canonical version %s -> %s", lk->name, client_val->str, server_val->str); strpool_incref(server_val); strpool_decref(client_val); client->vars.var_list[lk->idx] = server_val; } } } void varcache_apply_startup(PktBuf *pkt, PgSocket *client) { const struct var_lookup *lk, *tmp; HASH_ITER(hh, lookup_map, lk, tmp) { struct PStr *val = get_value(&client->vars, lk); if (!val) continue; slog_debug(client, "varcache_apply_startup: %s=%s", lk->name, val->str); pktbuf_put_string(pkt, lk->name); pktbuf_put_string(pkt, val->str); } } void varcache_fill_unset(VarCache *src, PgSocket *dst) { struct PStr *srcval, *dstval; const struct var_lookup *lk, *tmp; HASH_ITER(hh, lookup_map, lk, tmp) { srcval = src->var_list[lk->idx]; dstval = dst->vars.var_list[lk->idx]; if (!dstval) { strpool_incref(srcval); dst->vars.var_list[lk->idx] = srcval; } } } void varcache_clean(VarCache *cache) { for (int i = 0; i < num_var_cached; i++) { strpool_decref(cache->var_list[i]); cache->var_list[i] = NULL; } } void varcache_add_params(PktBuf *pkt, VarCache *vars) { struct PStr *val; const struct var_lookup *lk, *tmp; HASH_ITER(hh, lookup_map, lk, tmp) { val = vars->var_list[lk->idx]; if (val) pktbuf_write_ParameterStatus(pkt, lk->name, val->str); } } void varcache_deinit(void) { strpool_free(vpool); vpool = NULL; } pgbouncer-1.24.1/src/messages.c0000644000175000000000000001307714777762222013261 00000000000000#include "bouncer.h" /* Inspect Parse packet to see if it defines a named prepared statement */ PreparedStatementAction inspect_parse_packet(PgSocket *client, PktHdr *pkt) { const char *statement; if (!mbuf_get_string(&pkt->data, &statement)) return PS_INSPECT_FAILED; if (!*statement) { /* ignore empty statements */ slog_noise(client, "inspect_parse_packet: type=%c, len=%d, statement=", pkt->type, pkt->len); return PS_IGNORE; } slog_noise(client, "inspect_parse_packet: type=%c, len=%d, statement=%s", pkt->type, pkt->len, statement); return PS_HANDLE_FULL_PACKET; } /* Inspect Bind packet to see if it defines a named prepared statement */ PreparedStatementAction inspect_bind_packet(PgSocket *client, PktHdr *pkt) { const char *portal; const char *statement; if (!mbuf_get_string(&pkt->data, &portal)) return PS_INSPECT_FAILED; if (!mbuf_get_string(&pkt->data, &statement)) return PS_INSPECT_FAILED; if (!*statement) { /* ignore empty statements */ slog_noise(client, "inspect_bind_packet: type=%c, len=%d, statement=", pkt->type, pkt->len); return PS_IGNORE; } slog_noise(client, "inspect_bind_packet: type=%c, len=%d, statement=%s", pkt->type, pkt->len, statement); return PS_HANDLE; } /* Inspect Describe packet to see if it defines a named prepared statement */ PreparedStatementAction inspect_describe_or_close_packet(PgSocket *client, PktHdr *pkt) { char describe; const char *statement; if (!mbuf_get_char(&pkt->data, &describe)) return PS_INSPECT_FAILED; if (describe != 'S') { slog_noise(client, "inspect_describe_or_close_packet: type=%c, len=%d, P/S=%c", pkt->type, pkt->len, describe); return PS_IGNORE; } if (!mbuf_get_string(&pkt->data, &statement)) return PS_INSPECT_FAILED; if (!*statement) { /* ignore empty statements */ slog_noise(client, "inspect_describe_or_close_packet: type=%c, len=%d, P/S=%c, statement=", pkt->type, pkt->len, describe); return PS_IGNORE; } slog_noise(client, "inspect_describe_or_close_packet: type=%c, len=%d, P/S=%c, statement=%s", pkt->type, pkt->len, describe, statement); return PS_HANDLE; } /* * Unmarshall Parse packet into PgParsePacket struct for further processing. * * Note: The PgParsePacket still references the data stored in the PktHdr. So * the PktHdr should not be freed until the PgParsePacket is not necessary * anymore. */ bool unmarshall_parse_packet(PgSocket *client, PktHdr *pkt, PgParsePacket *parse_packet) { const char *statement; const char *query; uint16_t num_parameters; const uint8_t *parameter_types_bytes; size_t parameters_length; if (!mbuf_get_string(&pkt->data, &statement)) goto failed; if (!mbuf_get_string(&pkt->data, &query)) goto failed; /* number of parameter data types */ if (!mbuf_get_uint16be(&pkt->data, &num_parameters)) goto failed; parameters_length = (size_t) num_parameters * 4; if (!mbuf_get_bytes(&pkt->data, parameters_length, ¶meter_types_bytes)) goto failed; parse_packet->len = pkt->len; parse_packet->name = statement; parse_packet->query_and_parameters_len = strlen(query) + 1 /* \0 */ + sizeof(num_parameters) + parameters_length; parse_packet->query_and_parameters = query; return true; failed: disconnect_client(client, true, "broken Parse packet"); return false; } /* * Unmarshall (partial) Bind packet into PgBindPacket struct for further processing * * Note: The PgBindPacket still references the data stored in the PktHdr. So * the PktHdr should not be freed until the PgBindPacket is not necessary * anymore. */ bool unmarshall_bind_packet(PgSocket *client, PktHdr *pkt, PgBindPacket *bind_packet) { const char *portal; const char *statement; if (!mbuf_get_string(&pkt->data, &portal)) goto failed; if (!mbuf_get_string(&pkt->data, &statement)) goto failed; bind_packet->len = pkt->len; bind_packet->portal = portal; bind_packet->name = statement; return true; failed: disconnect_client(client, true, "broken Bind packet"); return false; } /* * Unmarshall Describe packet into PgDescribePacket struct for further processing * * Note: The PgDescribePacket still references the data stored in the PktHdr. So * the PktHdr should not be freed until the PgDescribePacket is not necessary * anymore. */ bool unmarshall_describe_packet(PgSocket *client, PktHdr *pkt, PgDescribePacket *describe_packet) { char describe; const char *statement; if (incomplete_pkt(pkt)) return false; if (!mbuf_get_char(&pkt->data, &describe)) goto failed; if (!mbuf_get_string(&pkt->data, &statement)) goto failed; describe_packet->type = describe; describe_packet->name = statement; return true; failed: disconnect_client(client, true, "broken Describe packet"); return false; } /* * Unmarshall Close packet into PgClosePacket struct for further processing * * Note: The PgClosePacket still references the data stored in the PktHdr. So * the PktHdr should not be freed until the PgClosePacket is not necessary * anymore. */ bool unmarshall_close_packet(PgSocket *client, PktHdr *pkt, PgClosePacket *close_packet) { char type; const char *name; if (incomplete_pkt(pkt)) return false; if (!mbuf_get_char(&pkt->data, &type)) goto failed; if (!mbuf_get_string(&pkt->data, &name)) goto failed; close_packet->type = type; close_packet->name = name; slog_noise(client, "unmarshall_close_packet: type=%c, len=%d, S/P=%c, name=%s", pkt->type, pkt->len, type, name); return true; failed: disconnect_client(client, true, "broken Close packet"); return false; } bool is_close_named_statement_packet(PgClosePacket *close_packet) { return close_packet->type == 'S' && *close_packet->name; } pgbouncer-1.24.1/src/proto.c0000644000175000000000000005077314777762222012621 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Pieces that need to have detailed info about protocol. */ #include "bouncer.h" #include "scram.h" /* * parse protocol header from struct MBuf */ /* * Parses pkt header from buffer, returns false if failed. * * This handles both regular packets as well as startup/special * packets (which are actually v2-style packets). Afterwards, the * type and the length is available in pkt independent of what kind * this packet is. */ bool get_header(struct MBuf *data, PktHdr *pkt) { unsigned type; uint32_t len; unsigned got; unsigned avail; uint16_t len16; uint8_t type8; uint32_t code; struct MBuf hdr; const uint8_t *ptr; mbuf_copy(data, &hdr); if (mbuf_avail_for_read(&hdr) < NEW_HEADER_LEN) { log_noise("get_header: less than %d bytes available", NEW_HEADER_LEN); return false; } if (!mbuf_get_byte(&hdr, &type8)) return false; type = type8; if (type != 0) { /* * Regular (v3) packet, starts with type byte and * 4-byte length. */ /* wire length does not include type byte */ if (!mbuf_get_uint32be(&hdr, &len)) return false; len++; got = NEW_HEADER_LEN; } else { /* * Startup/special (formerly v2) packet, formally * starts with 4-byte length. We assume the first * byte is zero because in current use they shouldn't * be that long to have more than zero in the MSB. */ /* second byte should also be zero */ if (!mbuf_get_byte(&hdr, &type8)) return false; if (type8 != 0) { log_noise("get_header: unknown special pkt"); return false; } /* don't tolerate partial pkt */ if (mbuf_avail_for_read(&hdr) < OLD_HEADER_LEN - 2) { log_noise("get_header: less than %d bytes for special pkt", OLD_HEADER_LEN); return false; } if (!mbuf_get_uint16be(&hdr, &len16)) return false; len = len16; /* 4-byte code follows */ if (!mbuf_get_uint32be(&hdr, &code)) return false; if (code == PKT_CANCEL) { type = PKT_CANCEL; } else if (code == PKT_SSLREQ) { type = PKT_SSLREQ; } else if (code == PKT_GSSENCREQ) { type = PKT_GSSENCREQ; } else if (code >= PKT_STARTUP_V3 && code < PKT_STARTUP_V3_UNSUPPORTED) { type = PKT_STARTUP_V3; } else if (code >= PKT_STARTUP_V3_UNSUPPORTED && code < PKT_STARTUP_V4) { type = PKT_STARTUP_V3_UNSUPPORTED; } else if (code == PKT_STARTUP_V2) { type = PKT_STARTUP_V2; } else { log_noise("get_header: unknown special pkt: len=%u code=%u", len, code); return false; } got = OLD_HEADER_LEN; } /* don't believe nonsense */ if (len < got || len > cf_max_packet_size) return false; /* store pkt info */ pkt->type = type; pkt->len = len; /* fill pkt with only data for this packet */ if (len > mbuf_avail_for_read(data)) { avail = mbuf_avail_for_read(data); } else { avail = len; } if (!mbuf_slice(data, avail, &pkt->data)) return false; /* tag header as read */ return mbuf_get_bytes(&pkt->data, got, &ptr); } /* * Send error message packet to client. * * If level_fatal is true, use severity "FATAL", else "ERROR". Although it is * not technically part of the protocol specification, some clients expect the * connection to be closed after receiving a FATAL error, and don't expect it * to be closed after an ERROR-level error. So to be nice, level_fatal should * be true if the caller plans to close the connection after sending this * error. * Error code 08P01 (ERRCODE_PROTOCOL_VIOLATION) is used as default error code * if no SQLSTATE is provided. */ bool send_pooler_error(PgSocket *client, bool send_ready, const char *sqlstate, bool level_fatal, const char *msg) { uint8_t tmpbuf[512]; PktBuf buf; if (cf_log_pooler_errors) slog_warning(client, "pooler error: %s", msg); pktbuf_static(&buf, tmpbuf, sizeof(tmpbuf)); pktbuf_write_generic(&buf, PqMsg_ErrorResponse, "cscscsc", 'S', level_fatal ? "FATAL" : "ERROR", 'C', sqlstate ? sqlstate : "08P01", 'M', msg, 0); if (send_ready) pktbuf_write_ReadyForQuery(&buf); return pktbuf_send_immediate(&buf, client); } /* * Parse server error message and log it. */ void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p, const char **sqlstate_p) { const char *level = NULL, *msg = NULL, *sqlstate = NULL, *val; uint8_t type; while (mbuf_avail_for_read(&pkt->data)) { if (!mbuf_get_byte(&pkt->data, &type)) break; if (type == 0) break; if (!mbuf_get_string(&pkt->data, &val)) break; if (type == 'S') { level = val; } else if (type == 'M') { msg = val; } else if (type == 'C') { sqlstate = val; } } *level_p = level; *msg_p = msg; *sqlstate_p = sqlstate; } void log_server_error(const char *note, PktHdr *pkt) { const char *level = NULL, *msg = NULL, *sqlstate = NULL; parse_server_error(pkt, &level, &msg, &sqlstate); if (!msg || !level) { log_error("%s: partial error message, cannot log", note); } else { log_error("%s: %s: %s", note, level, msg); } } /* * Preparation of welcome message for client connection. */ /* add another server parameter packet to cache */ bool add_welcome_parameter(PgPool *pool, const char *key, const char *val) { PktBuf *msg = pool->welcome_msg; if (pool->welcome_msg_ready) return true; if (!msg) { msg = pktbuf_dynamic(128); if (!msg) return false; pool->welcome_msg = msg; } /* first packet must be AuthOk */ if (msg->write_pos == 0) pktbuf_write_AuthenticationOk(msg); /* if not stored in ->orig_vars, write full packet */ if (!varcache_set(&pool->orig_vars, key, val)) pktbuf_write_ParameterStatus(msg, key, val); return !msg->failed; } /* all parameters processed */ void finish_welcome_msg(PgSocket *server) { PgPool *pool = server->pool; if (pool->welcome_msg_ready) return; pool->welcome_msg_ready = true; } bool welcome_client(PgSocket *client) { int res; PgPool *pool = client->pool; const PktBuf *pmsg = pool->welcome_msg; PktBuf *msg; slog_noise(client, "P: welcome_client"); /* copy prepared stuff around */ msg = pktbuf_temp(); pktbuf_put_bytes(msg, pmsg->buf, pmsg->write_pos); /* fill vars */ varcache_fill_unset(&pool->orig_vars, client); varcache_add_params(msg, &client->vars); /* give each client its own cancel key */ get_random_bytes(client->cancel_key, 8); /* * If pgbouncer peering is enabled we change some of the random bits of the * cancel key to non random values, otherwise the peering feature cannot be * implemented in an efficient way. This reduces the randomness of the key * somewhat, but it still leaves us with 45 bits of randomness. This should * be enough for all practical attacks to be mitigated (there are still * ~35 trillion random combinations of these bits). */ if (cf_peer_id > 0) { /* * The 2nd and 3rd byte represent the peer id. Pgbouncers that are * peered with this one can forward the request to us by reading this * peer id when they receive this cancellation. */ client->cancel_key[1] = cf_peer_id & 0xFF; client->cancel_key[2] = (cf_peer_id >> 8) & 0xFF; /* * Initially we set the two TTL mask bits to a 1, so that the cancel * message can be forwarded to peers up to 3 times. */ client->cancel_key[7] |= CANCELLATION_TTL_MASK; } /* * The first 32 bits of the cancel_key are considered a PID. Since * actual PIDs are always positive we clear the sign bit. Most clients * work fine when receiving a negative number in this PID part, but it * turned out that pg_basebackup did not. This is fixed in * pg_basebackup, but to avoid similar future problems with other * clients we clear the sign bit. See this thread for more details: * https://www.postgresql.org/message-id/flat/CAGECzQQOGvYfp8ziF4fWQ_o8s2K7ppaoWBQnTmdakn3s-4Z%3D5g%40mail.gmail.com */ client->cancel_key[0] &= 0x7F; pktbuf_write_BackendKeyData(msg, client->cancel_key); /* finish */ pktbuf_write_ReadyForQuery(msg); if (msg->failed) { disconnect_client(client, true, "failed to prepare welcome message"); return false; } /* send all together */ res = pktbuf_send_immediate(msg, client); if (!res) { disconnect_client(client, true, "failed to send welcome message"); return false; } return true; } /* * Password authentication for server */ static PgCredentials *get_srv_psw(PgSocket *server) { PgDatabase *db = server->pool->db; PgCredentials *credentials = server->pool->user_credentials; /* if forced user without password, use userlist psw */ if (!credentials->passwd[0] && db->forced_user_credentials) { PgCredentials *c2 = find_global_credentials(credentials->name); if (c2) return c2; } return credentials; } /* actual packet send */ static bool send_password(PgSocket *server, const char *enc_psw) { bool res; SEND_PasswordMessage(res, server, enc_psw); return res; } static bool login_clear_psw(PgSocket *server) { PgCredentials *credentials = get_srv_psw(server); slog_debug(server, "P: send clear password"); return send_password(server, credentials->passwd); } static bool login_md5_psw(PgSocket *server, const uint8_t *salt) { char txt[MD5_PASSWD_LEN + 1], *src; PgCredentials *credentials = get_srv_psw(server); slog_debug(server, "P: send md5 password"); switch (get_password_type(credentials->passwd)) { case PASSWORD_TYPE_PLAINTEXT: if (!pg_md5_encrypt(credentials->passwd, credentials->name, strlen(credentials->name), txt)) return false; src = txt + 3; break; case PASSWORD_TYPE_MD5: src = credentials->passwd + 3; break; default: slog_error(server, "cannot do MD5 authentication: wrong password type"); kill_pool_logins(server->pool, NULL, "server login failed: wrong password type"); return false; } if (!pg_md5_encrypt(src, (char *)salt, 4, txt)) return false; return send_password(server, txt); } static bool login_scram_sha_256(PgSocket *server) { PgCredentials *credentials = get_srv_psw(server); bool res; char *client_first_message = NULL; switch (get_password_type(credentials->passwd)) { case PASSWORD_TYPE_PLAINTEXT: /* ok */ break; case PASSWORD_TYPE_SCRAM_SHA_256: if (!credentials->has_scram_keys) { slog_error(server, "cannot do SCRAM authentication: password is SCRAM secret but client authentication did not provide SCRAM keys"); kill_pool_logins(server->pool, NULL, "server login failed: wrong password type"); return false; } break; default: slog_error(server, "cannot do SCRAM authentication: wrong password type"); kill_pool_logins(server->pool, NULL, "server login failed: wrong password type"); return false; } if (server->scram_state.client_nonce) { slog_error(server, "protocol error: duplicate AuthenticationSASL message from server"); return false; } client_first_message = build_client_first_message(&server->scram_state); if (!client_first_message) return false; slog_debug(server, "SCRAM client-first-message = \"%s\"", client_first_message); slog_debug(server, "P: send SASLInitialResponse"); SEND_SASLInitialResponseMessage(res, server, "SCRAM-SHA-256", client_first_message); free(client_first_message); return res; } static bool login_scram_sha_256_cont(PgSocket *server, unsigned datalen, const uint8_t *data) { PgCredentials *credentials = get_srv_psw(server); char *ibuf = NULL; char *input; char *server_nonce; int saltlen; char *salt = NULL; int iterations; bool res; char *client_final_message = NULL; if (!server->scram_state.client_nonce) { slog_error(server, "protocol error: AuthenticationSASLContinue without prior AuthenticationSASL"); return false; } if (server->scram_state.server_first_message) { slog_error(server, "SCRAM exchange protocol error: received second AuthenticationSASLContinue"); return false; } ibuf = malloc(datalen + 1); if (ibuf == NULL) return false; memcpy(ibuf, data, datalen); ibuf[datalen] = '\0'; input = ibuf; slog_debug(server, "SCRAM server-first-message = \"%s\"", input); if (!read_server_first_message(server, input, &server_nonce, &salt, &saltlen, &iterations)) goto failed; client_final_message = build_client_final_message(&server->scram_state, credentials, server_nonce, salt, saltlen, iterations); free(salt); free(ibuf); slog_debug(server, "SCRAM client-final-message = \"%s\"", client_final_message); slog_debug(server, "P: send SASLResponse"); SEND_SASLResponseMessage(res, server, client_final_message); free(client_final_message); return res; failed: free(salt); free(ibuf); free(client_final_message); return false; } static bool login_scram_sha_256_final(PgSocket *server, unsigned datalen, const uint8_t *data) { PgCredentials *credentials = get_srv_psw(server); char *ibuf = NULL; char *input; char ServerSignature[SHA256_DIGEST_LENGTH]; if (!server->scram_state.server_first_message) { slog_error(server, "protocol error: AuthenticationSASLFinal without prior AuthenticationSASLContinue"); return false; } ibuf = malloc(datalen + 1); if (ibuf == NULL) return false; memcpy(ibuf, data, datalen); ibuf[datalen] = '\0'; input = ibuf; slog_debug(server, "SCRAM server-final-message = \"%s\"", input); if (!read_server_final_message(server, input, ServerSignature)) goto failed; if (!verify_server_signature(&server->scram_state, credentials, ServerSignature)) { slog_error(server, "invalid server signature"); kill_pool_logins(server->pool, NULL, "server login failed: invalid server signature"); goto failed; } free(ibuf); return true; failed: free(ibuf); return false; } /* answer server authentication request */ bool answer_authreq(PgSocket *server, PktHdr *pkt) { uint32_t cmd; const uint8_t *salt; bool res = false; /* authreq body must contain 32bit cmd */ if (mbuf_avail_for_read(&pkt->data) < 4) return false; if (!mbuf_get_uint32be(&pkt->data, &cmd)) return false; switch (cmd) { case AUTH_REQ_OK: slog_debug(server, "S: auth ok"); res = true; break; case AUTH_REQ_PASSWORD: slog_debug(server, "S: req cleartext password"); res = login_clear_psw(server); break; case AUTH_REQ_MD5: slog_debug(server, "S: req md5-crypted psw"); if (!mbuf_get_bytes(&pkt->data, 4, &salt)) return false; res = login_md5_psw(server, salt); break; case AUTH_REQ_SASL: { bool selected_mechanism = false; slog_debug(server, "S: req SASL"); do { const char *mech; if (!mbuf_get_string(&pkt->data, &mech)) return false; if (!mech[0]) break; slog_debug(server, "S: SASL advertised mechanism: %s", mech); if (strcmp(mech, "SCRAM-SHA-256") == 0) selected_mechanism = true; } while (!selected_mechanism); if (!selected_mechanism) { slog_error(server, "none of the server's SASL authentication mechanisms are supported"); kill_pool_logins(server->pool, NULL, "server login failed: none of the server's SASL authentication mechanisms are supported"); } else { res = login_scram_sha_256(server); } break; } case AUTH_REQ_SASL_CONT: { unsigned len; const uint8_t *data; slog_debug(server, "S: SASL cont"); len = mbuf_avail_for_read(&pkt->data); if (!mbuf_get_bytes(&pkt->data, len, &data)) return false; res = login_scram_sha_256_cont(server, len, data); break; } case AUTH_REQ_SASL_FIN: { unsigned len; const uint8_t *data; slog_debug(server, "S: SASL final"); len = mbuf_avail_for_read(&pkt->data); if (!mbuf_get_bytes(&pkt->data, len, &data)) return false; res = login_scram_sha_256_final(server, len, data); free_scram_state(&server->scram_state); break; } default: slog_error(server, "unknown/unsupported auth method: %u", cmd); res = false; break; } return res; } bool send_startup_packet(PgSocket *server) { PgPool *pool = server->pool; PgDatabase *db = pool->db; const char *username = server->pool->user_credentials->name; PktBuf *pkt = pktbuf_temp(); PgSocket *client = NULL; pktbuf_start_packet(pkt, PKT_STARTUP_V3); pktbuf_put_bytes(pkt, db->startup_params->buf, db->startup_params->write_pos); /* * If the next client in the list is a replication connection, we need * to do some special stuff for it. */ client = first_socket(&pool->waiting_client_list); if (client && client->replication && !sending_auth_query(client)) { server->replication = client->replication; pktbuf_put_string(pkt, "replication"); slog_debug(server, "send_startup_packet: creating replication connection"); pktbuf_put_string(pkt, replication_type_parameters[server->replication]); /* * For a replication connection we apply the varcache in the * startup instead of through SET commands after connecting. * The main reason to do so is because physical replication * connections don't allow SET commands. A second reason is * because it allows us to skip running the SET logic * completely, which normally requires waiting on multiple * server responses. This SET logic is normally executed in the * codepath where we link the client to the server * (find_server), but because we link the client here already * we don't run that code for replication connections. Adding * the varcache parameters to the startup message allows us to * skip the dance that involves sending Query packets and * waiting for responses. */ varcache_apply_startup(pkt, client); if (client->startup_options) { pktbuf_put_string(pkt, "options"); pktbuf_put_string(pkt, client->startup_options); } } pktbuf_put_string(pkt, "user"); pktbuf_put_string(pkt, username); pktbuf_put_string(pkt, ""); /* terminator required in StartupMessage */ pktbuf_finish_packet(pkt); if (!pktbuf_send_immediate(pkt, server)) { return false; } if (server->replication) { /* * We link replication connections to a client directly when they are * created. One reason for is because the startup parameters need to be * forwarded, because physical replication connections don't allow SET * commands. Another reason is so that we don't need a separate state. */ client->link = server; server->link = client; } return true; } bool send_sslreq_packet(PgSocket *server) { int res; SEND_wrap(16, pktbuf_write_SSLRequest, res, server); return res; } /* * decode DataRow packet (opposite of pktbuf_write_DataRow) * * tupdesc keys: * 'i' - int4 * 'q' - int8 * 's' - text to string * 'b' - bytea to bytes (result is malloced) */ int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...) { uint16_t ncol; unsigned asked; va_list ap; asked = strlen(tupdesc); if (!mbuf_get_uint16be(pkt, &ncol)) return -1; va_start(ap, tupdesc); for (unsigned i = 0; i < asked; i++) { const char *val = NULL; uint32_t len; if (i < ncol) { if (!mbuf_get_uint32be(pkt, &len)) { goto failed; } if ((int32_t)len < 0) { val = NULL; } else { if (!mbuf_get_chars(pkt, len, &val)) { goto failed; } } /* hack to zero-terminate the result */ if (val) { char *xval = (char *)val - 1; memmove(xval, val, len); xval[len] = 0; val = xval; } } else { /* tuple was shorter than requested */ val = NULL; len = -1; } switch (tupdesc[i]) { case 'i': { int *int_p; int_p = va_arg(ap, int *); *int_p = val ? atoi(val) : 0; break; } case 'q': { uint64_t *long_p; long_p = va_arg(ap, uint64_t *); *long_p = val ? atoll(val) : 0; break; } case 's': { const char **str_p; str_p = va_arg(ap, const char **); *str_p = val; break; } case 'b': { int *len_p = va_arg(ap, int *); uint8_t **bytes_p = va_arg(ap, uint8_t **); if (val) { int newlen; if (strncmp(val, "\\x", 2) != 0) { log_warning("invalid bytea value"); goto failed; } newlen = (len - 2) / 2; *len_p = newlen; *bytes_p = malloc(newlen); if (!(*bytes_p)) { goto failed; } for (int j = 0; j < newlen; j++) { unsigned int b; sscanf(val + 2 + 2 * j, "%2x", &b); (*bytes_p)[j] = b; } } else { *len_p = -1; *bytes_p = NULL; } break; } default: fatal("bad tupdesc: %s", tupdesc); } } va_end(ap); return ncol; failed: va_end(ap); return -1; } pgbouncer-1.24.1/src/pam.c0000644000175000000000000002767014777762222012233 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * PAM authentication support. */ #include "bouncer.h" #ifdef HAVE_PAM #include #include /* The request is waiting in the queue or being authenticated */ #define PAM_STATUS_IN_PROGRESS 1 /* The request was successfully authenticated */ #define PAM_STATUS_SUCCESS 2 /* The request failed authentication */ #define PAM_STATUS_FAILED 3 /* * How many microseconds to sleep between calls to pam_poll in * pam_auth_begin when the queue is full. * Default is 100 milliseconds. */ #define PAM_QUEUE_WAIT_SLEEP_MCS (100*1000) struct pam_auth_request { /* The socket we check authentication for */ PgSocket *client; /* CHECKME: The socket can be closed and reused while the request is waiting * in the queue. Thus we need something to check the socket validity, and * combination of its state and connect_time seems to be the good one. */ usec_t connect_time; /* Same as in client->remote_addr. * We want to minimize synchronization between the authentication thread and * the rest of pgbouncer, so the username and remote_addr are explicitly stored here. */ PgAddr remote_addr; /* The request status, one of the PAM_STATUS_* constants */ int status; /* The username (same as in client->login_user_credentials->name). * See the comment for remote_addr. */ char username[MAX_USERNAME]; /* password we should check for validity together with the socket's username */ char password[MAX_PASSWORD]; }; /* * All incoming requests are kept in a queue which is implemented using a ring buffer. * Such structure allows to avoid memory reallocation thus minimizing amount of * synchronization to be done between threads. * * pam_first_taken_slot points to the first element in the queue; * pam_first_free_slot points to the next slot after the last element in the queue. * * if pam_first_taken_slot == pam_first_free_slot then the queue is considered empty; * */ volatile int pam_first_taken_slot; volatile int pam_first_free_slot; struct pam_auth_request pam_auth_queue[PAM_REQUEST_QUEUE_SIZE]; pthread_t pam_worker_thread; /* * Mutex serializes access to the queue's tail when we add new requests or * check that we reach the end of the queue in the worker thread. * * Head and tail are modified only in the main thread. In theory, being sure that they * are properly aligned we can access them directly without any risk for data races. * Practically, it is better to secure them anyway to increase overall stability and * provide faster notification of new requests via the condition variable. */ pthread_mutex_t pam_queue_tail_mutex; pthread_cond_t pam_data_available; /* Forward declarations */ static void * pam_auth_worker(void *arg); static bool is_valid_socket(const struct pam_auth_request *request); static void pam_auth_finish(struct pam_auth_request *request); static bool pam_check_passwd(struct pam_auth_request *request); /* * Initialize PAM subsystem. */ void pam_init(void) { int rc; pam_first_taken_slot = 0; pam_first_free_slot = 0; rc = pthread_mutex_init(&pam_queue_tail_mutex, NULL); if (rc != 0) { die("failed to initialize a mutex: %s", strerror(errno)); } rc = pthread_cond_init(&pam_data_available, NULL); if (rc != 0) { die("failed to initialize a condition variable: %s", strerror(errno)); } rc = pthread_create(&pam_worker_thread, NULL, &pam_auth_worker, NULL); if (rc != 0) { die("failed to create the authentication thread: %s", strerror(errno)); } } /* * Initiate the authentication request using PAM. The request result will be * available during next calls to pam_poll(). The function might block if the * request queue is full until there are free slots available. * The function is called only from the main thread. */ void pam_auth_begin(PgSocket *client, const char *passwd) { int next_free_slot = (pam_first_free_slot + 1) % PAM_REQUEST_QUEUE_SIZE; struct pam_auth_request *request; slog_debug( client, "pam_auth_begin(): pam_first_taken_slot=%d, pam_first_free_slot=%d", pam_first_taken_slot, pam_first_free_slot); client->wait_for_auth = true; /* Check that we have free slots in the queue, and if no * then block until one is available. */ if (next_free_slot == pam_first_taken_slot) slog_debug(client, "PAM queue is full, waiting"); while (next_free_slot == pam_first_taken_slot) { if (pam_poll() == 0) { /* Sleep a bit between consequent queue checks to avoid consuming too much CPU */ usleep(PAM_QUEUE_WAIT_SLEEP_MCS); } } pthread_mutex_lock(&pam_queue_tail_mutex); request = &pam_auth_queue[pam_first_free_slot]; request->client = client; request->connect_time = client->connect_time; request->status = PAM_STATUS_IN_PROGRESS; memcpy(&request->remote_addr, &client->remote_addr, sizeof(client->remote_addr)); safe_strcpy(request->username, client->login_user_credentials->name, MAX_USERNAME); safe_strcpy(request->password, passwd, MAX_PASSWORD); pam_first_free_slot = next_free_slot; pthread_mutex_unlock(&pam_queue_tail_mutex); pthread_cond_signal(&pam_data_available); } /* * Checks for completed auth requests, returns amount of requests handled. * The function is called only from the main thread. */ int pam_poll(void) { struct pam_auth_request *request; int count = 0; while (pam_first_taken_slot != pam_first_free_slot) { request = &pam_auth_queue[pam_first_taken_slot]; if (request->status == PAM_STATUS_IN_PROGRESS) { /* When still-in-progress slot is found there is no need to continue * the loop since all further requests will be in progress too. */ break; } if (is_valid_socket(request)) { pam_auth_finish(request); } count++; pam_first_taken_slot = (pam_first_taken_slot + 1) % PAM_REQUEST_QUEUE_SIZE; } return count; } /* * The authentication thread function. * Performs scanning the queue for new requests and calling PAM for them. */ static void * pam_auth_worker(void *arg) { int current_slot = pam_first_taken_slot; struct pam_auth_request *request; while (true) { /* Wait for new data in the queue */ pthread_mutex_lock(&pam_queue_tail_mutex); while (current_slot == pam_first_free_slot) { pthread_cond_wait(&pam_data_available, &pam_queue_tail_mutex); } pthread_mutex_unlock(&pam_queue_tail_mutex); log_debug("pam_auth_worker(): processing slot %d", current_slot); /* We have at least one request in the queue */ request = &pam_auth_queue[current_slot]; current_slot = (current_slot + 1) % PAM_REQUEST_QUEUE_SIZE; /* If the socket is already in the wrong state or reused then ignore it. * This check is not safe and should not be trusted (the socket state * might change exactly after it), but it helps to quickly filter out invalid * sockets and thus save some time. */ if (!is_valid_socket(request)) { log_debug("pam_auth_worker(): invalid socket in slot %d", current_slot); request->status = PAM_STATUS_FAILED; continue; } if (pam_check_passwd(request)) { request->status = PAM_STATUS_SUCCESS; } else { request->status = PAM_STATUS_FAILED; } log_debug("pam_auth_worker(): authentication completed, status=%d", request->status); } return NULL; } /* * Checks that the socket is still valid to be processed. * By validity we mean that it is still waiting in the login phase * and was not reused for other connections. */ static bool is_valid_socket(const struct pam_auth_request *request) { if (request->client->state != CL_LOGIN || request->client->connect_time != request->connect_time) return false; return true; } /* * Finishes the handshake after successful or unsuccessful authentication. * The function is only called from the main thread. */ static void pam_auth_finish(struct pam_auth_request *request) { PgSocket *client = request->client; bool authenticated = (request->status == PAM_STATUS_SUCCESS); if (authenticated) { safe_strcpy(client->login_user_credentials->passwd, request->password, sizeof(client->login_user_credentials->passwd)); sbuf_continue(&client->sbuf); } else { disconnect_client(client, true, "PAM authentication failed"); } } static int pam_conversation(int msgc, const struct pam_message **msgv, struct pam_response **rspv, void *authdata) { struct pam_auth_request *request = (struct pam_auth_request *)authdata; int i, rc; if (msgc < 1 || msgv == NULL || request == NULL) { log_debug( "pam_conversation(): wrong input, msgc=%d, msgv=%p, authdata=%p", msgc, msgv, authdata); return PAM_CONV_ERR; } /* Allocate and fill with zeroes an array of responses. * By filling with zeroes we automatically set resp_retcode to * zero and simplify freeing resp on errors. */ *rspv = malloc(msgc * sizeof(struct pam_response)); if (*rspv == NULL) { log_warning("pam_conversation(): not enough memory for responses"); return PAM_CONV_ERR; } memset(*rspv, 0, msgc * sizeof(struct pam_response)); rc = PAM_SUCCESS; for (i = 0; i < msgc; i++) { if (rc != PAM_SUCCESS) break; switch (msgv[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: (*rspv)[i].resp = strdup(request->password); if ((*rspv)[i].resp == NULL) { log_warning("pam_conversation(): not enough memory for password"); rc = PAM_CONV_ERR; } break; case PAM_ERROR_MSG: log_warning( "pam_conversation(): PAM error: %s", msgv[i]->msg); break; default: log_debug( "pam_conversation(): unhandled message, msg_style=%d", msgv[i]->msg_style); break; } } if (rc != PAM_SUCCESS) { for (i = 0; i < msgc; i++) free((*rspv)[i].resp); free(*rspv); } return rc; } static bool pam_check_passwd(struct pam_auth_request *request) { pam_handle_t *hpam; char raddr[PGADDR_BUF]; int rc; struct pam_conv pam_conv = { .conv = pam_conversation, .appdata_ptr = request }; rc = pam_start(PGBOUNCER_PAM_SERVICE, request->username, &pam_conv, &hpam); if (rc != PAM_SUCCESS) { log_warning("pam_start() failed: %s", pam_strerror(NULL, rc)); return false; } /* Set rhost too in case if some PAM modules want to take it into account (and for logging too) */ pga_ntop(&request->remote_addr, raddr, sizeof(raddr)); rc = pam_set_item(hpam, PAM_RHOST, raddr); if (rc != PAM_SUCCESS) { log_warning("pam_set_item(): can't set PAM_RHOST to '%s'", raddr); pam_end(hpam, rc); return false; } /* Here the authentication is performed */ rc = pam_authenticate(hpam, PAM_SILENT); if (rc != PAM_SUCCESS) { log_warning("pam_authenticate() failed: %s", pam_strerror(hpam, rc)); pam_end(hpam, rc); return false; } /* And here we check that the account is not expired, verifies access hours, etc */ rc = pam_acct_mgmt(hpam, PAM_SILENT); if (rc != PAM_SUCCESS) { log_warning("pam_acct_mgmt() failed: %s", pam_strerror(hpam, rc)); pam_end(hpam, rc); return false; } rc = pam_end(hpam, rc); if (rc != PAM_SUCCESS) { log_warning("pam_end() failed: %s", pam_strerror(hpam, rc)); } return true; } #else /* !HAVE_PAM */ /* If PAM is not supported then this dummy functions is used which always rejects passwords */ void pam_init(void) { /* do nothing */ } void pam_auth_begin(PgSocket *client, const char *passwd) { die("PAM authentication is not supported"); } int pam_poll(void) { /* do nothing */ return 0; } #endif pgbouncer-1.24.1/src/prepare.c0000644000175000000000000005662414777762222013115 00000000000000/* * Contains code for handling prepared statement related packets. * * The main entrypoints are the handle_xxx_command functions. They do the * translation from prepared statement names of the client to the equivalent * prepared statement name on the server. */ #include "bouncer.h" #include #include #include static uint64_t next_unique_query_id; /* * Track allocation failures in uthash, so that we can fail more gracefully * than a full process crash. Instead we will just disconnect the client and * server. */ static bool uthash_alloc_failed; #undef uthash_nonfatal_oom #define uthash_nonfatal_oom(elt) uthash_alloc_failed = true /* * Like uthash HASH_DELETE, but doesn't remove item from hash table just * unlink element from the doubly-linked-list. */ #define HASH_UNLINK(hh, head, delptr) \ do { \ struct UT_hash_handle *_hd_hh_del = &(delptr)->hh; \ if (_hd_hh_del->prev != NULL) { \ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ } else { \ DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ } \ if (_hd_hh_del->next != NULL) { \ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ } else { \ _hd_hh_del->tbl->tail = HH_FROM_ELMT(_hd_hh_del->tbl, _hd_hh_del->prev); \ } \ } while (0) #define HASH_FIND_UINT64(head, findint, out) \ HASH_FIND(hh, head, findint, sizeof(uint64_t), out) #define HASH_ADD_UINT64(head, intfield, add) \ HASH_ADD(hh, head, intfield, sizeof(uint64_t), add) /* * Benchmarking showed that HASH_BER is one of the fastest hash functions for our * usecases */ #undef HASH_FUNCTION #define HASH_FUNCTION HASH_BER /* * Converts a PgParsePacket to a malloc-ed PgPreparedStatement. The * PgPreparedStatement can be stored in the global prepared statement cache. */ static PgPreparedStatement *create_prepared_statement(PgParsePacket *pkt) { PgPreparedStatement *ps = malloc( sizeof(PgPreparedStatement) + pkt->query_and_parameters_len); if (ps == NULL) return NULL; next_unique_query_id += 1; ps->query_id = next_unique_query_id; ps->use_count = 0; ps->query_and_parameters_len = pkt->query_and_parameters_len; memcpy(ps->query_and_parameters, pkt->query_and_parameters, pkt->query_and_parameters_len); return ps; } /* * Creates a PgClientPreparedStatement from a PgPreparedStatement. The * PgClientPreparedStatement can be stored inside the client its prepared * statement hashmap. */ static PgClientPreparedStatement *create_client_prepared_statement(char const *name, PgPreparedStatement *ps) { size_t name_len = strlen(name) + 1; PgClientPreparedStatement *client_ps = malloc( sizeof(PgClientPreparedStatement) + name_len); if (client_ps == NULL) return NULL; memcpy(client_ps->stmt_name, name, name_len); client_ps->ps = ps; ps->use_count += 1; return client_ps; } /* * Creates a PgServerPreparedStatement from a PgPreparedStatement. The * PgClientPreparedStatement can be stored inside the server its prepared * statement hashmap. */ static PgServerPreparedStatement *create_server_prepared_statement(PgPreparedStatement *ps) { PgServerPreparedStatement *server_ps = slab_alloc(server_prepared_statement_cache); if (server_ps == NULL) return NULL; server_ps->ps = ps; server_ps->query_id = ps->query_id; ps->use_count += 1; return server_ps; } /* * Gets a prepared statement from the global cache. If it doesn't exist yet, it * is created and added. */ static PgPreparedStatement *get_prepared_statement(PgParsePacket *pkt, bool *found) { PgPreparedStatement *ps = NULL; HASH_FIND(hh, prepared_statements, pkt->query_and_parameters, pkt->query_and_parameters_len, ps); if (ps != NULL) { *found = true; return ps; } ps = create_prepared_statement(pkt); if (ps == NULL) return NULL; HASH_ADD(hh, prepared_statements, query_and_parameters, ps->query_and_parameters_len, ps); if (uthash_alloc_failed) { uthash_alloc_failed = false; free(ps); return NULL; } ps->stmt_name_len = (uint8_t)snprintf( ps->stmt_name, sizeof ps->stmt_name, PREPARED_STMT_NAME_FORMAT, ps->query_id); *found = false; return ps; } /* * This is equivalent of sbuf_prepare_skip() but it also handles the case where * we used our special callbacke packet buffering logic. */ static void skip_possibly_completely_buffered_packet(PgSocket *client, PktHdr *pkt) { /* * Now we need to make sure the original packet is not sent to the server. */ if (client->packet_cb_state.flag == CB_HANDLE_COMPLETE_PACKET) { /* * If we used special callback packet buffering, then we don't need to * do anything. Because the callback already "consumed" the data from * the SBuf. */ return; } /* * If we used the regular packet buffering, then we do still need to tell * SBuf that we handled the packet. */ sbuf_prepare_skip(&client->sbuf, pkt->len); } /* * Unregister prepared statement at server */ void free_server_prepared_statement(PgServerPreparedStatement *server_ps) { if (server_ps == NULL) return; if (--server_ps->ps->use_count == 0) { HASH_DEL(prepared_statements, server_ps->ps); free(server_ps->ps); } slab_free(server_prepared_statement_cache, server_ps); } /* * Unregister prepared statement from the server its cache. */ void unregister_prepared_statement(PgSocket *server, uint64_t query_id) { PgServerPreparedStatement *server_ps; HASH_FIND_UINT64(server->server_prepared_statements, &query_id, server_ps); if (server_ps) { HASH_DEL(server->server_prepared_statements, server_ps); free_server_prepared_statement(server_ps); } } /* * Add the prepared statement to the server its cache. */ bool add_prepared_statement(PgSocket *server, PgServerPreparedStatement *server_ps) { HASH_ADD_UINT64(server->server_prepared_statements, query_id, server_ps); if (uthash_alloc_failed) { uthash_alloc_failed = false; return false; } return true; } /* * Register prepared statement in the server its cache. If the cache is full, we * evict the least recently used query/queries before adding a new one. * * NOTE: Before calling this a matching outstanding request should have been * added to the server its queue. */ static bool register_prepared_statement(PgSocket *client, PgSocket *server, PgServerPreparedStatement *server_ps) { struct PgServerPreparedStatement *current, *tmp; OutstandingRequest *outstanding_request; struct List *el; int res; Assert(server_ps); /* * Now we need to link the outstanding request to the server_ps, so * that it can be unregistered if the request fails. */ el = statlist_last(&server->outstanding_requests); Assert(el); outstanding_request = container_of(el, OutstandingRequest, node); Assert(outstanding_request->type == PqMsg_Parse); Assert(outstanding_request->server_ps_query_id == 0); outstanding_request->server_ps_query_id = server_ps->ps->query_id; if (!add_prepared_statement(server, server_ps)) return false; slog_noise(server, "prepared statement " PREPARED_STMT_NAME_FORMAT " added to server cache, %d cached items", server_ps->ps->query_id, HASH_COUNT(server->server_prepared_statements)); /* * Ensure the cache is not larger than the intended size. To do so we * can simply remove the first N items from the hash table, because we * maintain its order as an LRU. (see * ensure_statement_is_prepared_on_server for details) */ HASH_ITER(hh, server->server_prepared_statements, current, tmp) { if (HASH_COUNT(server->server_prepared_statements) <= (unsigned int)cf_max_prepared_statements) { break; } QUEUE_CloseStmt(res, client, server, current->ps->stmt_name); if (!res) { return false; } if (!add_outstanding_request(client, PqMsg_Close, RA_SKIP)) { return false; } el = statlist_last(&server->outstanding_requests); Assert(el); outstanding_request = container_of(el, OutstandingRequest, node); outstanding_request->server_ps = current; /* * We remove the statement from the cache, but we don't * free the memory yet. Because we might still need to * add it back if the Close fails. */ slog_noise(server, "prepared statement '%s' deleted from server cache", current->ps->stmt_name); HASH_DEL(server->server_prepared_statements, current); } return true; } /* * Handle a Parse packet for a named prepared statement * * This adds the prepared statement to the client's hash of prepared statements. * If the exact same prepared statement (query + argument types) was previously * prepared by another client on this server, then we don't actually send * anything to the server but instead reuse that one. Otherwise we prepare it on * the server and add it to the server's hash of prepared statements. */ bool handle_parse_command(PgSocket *client, PktHdr *pkt) { PgSocket *server = client->link; PgParsePacket pp; PgServerPreparedStatement *server_ps = NULL; PgClientPreparedStatement *client_ps = NULL; PgPreparedStatement *ps; PktBuf *buf; bool found = false; Assert(server); if (!unmarshall_parse_packet(client, pkt, &pp)) return false; HASH_FIND_STR(client->client_prepared_statements, pp.name, client_ps); if (client_ps) { slog_error(client, "prepared statement '%s' was already prepared", pp.name); /* * It would be nice if we would not completely close the client * connection here, but instead only sent an error after which * the client could continue. This is what Postgres does. * However, doing that in a way which works with query * pipelining is not trivial. So for now we take the easy way * out and simply close the connection. This is no problem for * most clients anyway, since they will only issue Parse for * with currently unused names. */ disconnect_client(client, true, "prepared statement name is already in use"); return false; } /* update stats */ client->pool->stats.ps_client_parse_count++; /* Lookup query in global hash */ ps = get_prepared_statement(&pp, &found); if (ps == NULL) goto oom; /* Register statement on client */ client_ps = create_client_prepared_statement(pp.name, ps); if (client_ps == NULL) goto oom; HASH_ADD_STR(client->client_prepared_statements, stmt_name, client_ps); if (uthash_alloc_failed) { uthash_alloc_failed = false; goto oom; } if (found) { /* Such query was already prepared */ HASH_FIND_UINT64(server->server_prepared_statements, &ps->query_id, server_ps); if (server_ps) { /* Statement was already prepared on this server, do not forward packet */ slog_debug(client, "handle_parse_command: mapping statement '%s' to '%s' (query '%s')", client_ps->stmt_name, ps->stmt_name, ps->query_and_parameters); /* * Insert an entry into the request queue, so we can send a fake * response to the client at the point where they expect it. */ if (!add_outstanding_request(client, PqMsg_Parse, RA_FAKE)) goto oom; goto success; } } /* Statement was not prepared on this server, sent modified P packet */ slog_debug(client, "handle_parse_command: creating mapping for statement '%s' to '%s' (query '%s')", client_ps->stmt_name, ps->stmt_name, ps->query_and_parameters); buf = pktbuf_temp(); pktbuf_write_Parse(buf, ps->stmt_name, ps->query_and_parameters, ps->query_and_parameters_len); if (!sbuf_queue_packet(&client->sbuf, &server->sbuf, buf)) goto oom; /* update stats */ client->pool->stats.ps_server_parse_count++; /* * Track the Parse command that we send to server and forward the response * to the client, because they expect one. */ if (!add_outstanding_request(client, PqMsg_Parse, RA_FORWARD)) goto oom; /* Register statement on server */ server_ps = create_server_prepared_statement(ps); if (!server_ps) goto oom; if (!register_prepared_statement(client, server, server_ps)) goto oom; success: skip_possibly_completely_buffered_packet(client, pkt); return true; oom: free(client_ps); free_server_prepared_statement(server_ps); disconnect_client(client, true, "out of memory"); /* * We also disconnect the server, because we probably messed with its state * at this prepared statement state at this point. And rolling that back is * hard. */ disconnect_server(client->link, true, "out of memory"); return false; } /* * Get the given prepared statement from the client hash map. This returns NULL * and closes the client connection if no prepared statement with this name * could not be found. */ static PgClientPreparedStatement *get_client_prepared_statement(PgSocket *client, const char *name) { PgClientPreparedStatement *client_ps; HASH_FIND_STR(client->client_prepared_statements, name, client_ps); if (!client_ps) { slog_error(client, "prepared statement '%s' not found", name); /* * It would be nice if we would not completely close the client * connection here, but instead only sent an error after which * the client could continue. This is what Postgres does. * However, doing that in a way which works with query * pipelining is not trivial. So for now we take the easy way * out and simply close the connection. This is no problem for * most clients anyway, since they will only issue * Bind/Describe for prepared statements that actually exist. */ disconnect_client(client, true, "prepared statement did not exist"); } return client_ps; } /* * Prepare the given prepared statement on the server, if it isn't prepared * there yet. If it's already prepared on the server this call is essentially a * no-op, except that we mark the prepared statement as used in the LRU cache * of the server. * * This returns false if it cannot allocate any needed memory. */ static bool ensure_statement_is_prepared_on_server(PgSocket *server, PgPreparedStatement *ps) { PgSocket *client = server->link; PgServerPreparedStatement *server_ps = NULL; PktBuf *buf; HASH_FIND_UINT64(server->server_prepared_statements, &ps->query_id, server_ps); if (server_ps) { /* * The statement is already prepared. Move it to the start of * the server its LRU double-linked list, except if there's * only one statement prepared (because then it's already at * the start by definition). */ if (HASH_COUNT(server->server_prepared_statements) != 1) { HASH_UNLINK(hh, server->server_prepared_statements, server_ps); HASH_APPEND_LIST(hh, server->server_prepared_statements, server_ps); } return true; } /* Statement is not prepared on this link, sent P packet now */ slog_debug(server, "handle_bind_command: prepared statement '%s' (query '%s') not available on server, preparing '%s' before bind", ps->stmt_name, ps->query_and_parameters, ps->stmt_name); /* update stats */ client->pool->stats.ps_server_parse_count++; /* * Track Parse command that we sent to server. But make sure the client * does not receive the respective response, because they did not * actually send a Parse request. */ if (!add_outstanding_request(client, PqMsg_Parse, RA_SKIP)) return false; buf = pktbuf_temp(); pktbuf_write_Parse(buf, ps->stmt_name, ps->query_and_parameters, ps->query_and_parameters_len); if (!sbuf_queue_packet(&client->sbuf, &server->sbuf, buf)) return false; /* Register statement on server link */ server_ps = create_server_prepared_statement(ps); if (!server_ps) return false; if (!register_prepared_statement(client, server, server_ps)) { free_server_prepared_statement(server_ps); return false; } return true; } /* * Handle a Bind packet for a named prepared statement * * Rewrites the given Bind packet to use the server-side statement name instead * of the client-side one. If the statement is not yet prepared on the server, * then we first send a Parse command to the server for the prepared statement * in question. */ bool handle_bind_command(PgSocket *client, PktHdr *pkt) { PgSocket *server = client->link; PgBindPacket bp; PgClientPreparedStatement *client_ps = NULL; PgPreparedStatement *ps; PktBuf *buf; int diff; Assert(server); if (!unmarshall_bind_packet(client, pkt, &bp)) return false; /* update stats */ client->pool->stats.ps_bind_count++; client_ps = get_client_prepared_statement(client, bp.name); if (!client_ps) return false; ps = client_ps->ps; if (!ensure_statement_is_prepared_on_server(server, ps)) goto oom; slog_debug(client, "handle_bind_command: mapped statement '%s' (query '%s') to '%s'", bp.name, ps->query_and_parameters, ps->stmt_name); /* * Track the Bind command that we're going send to server and forward * the response to the client, because they expect one. */ if (!add_outstanding_request(client, PqMsg_Bind, RA_FORWARD)) goto oom; /* * The Bind packet that we received from the client won't be sent as-is * to the server. Instead we replace the statement name with our mapped * name and also change the length if statement name is different * length. Those are the only changes that we wish to make though, and * the rest of the packet can be forwarded as is. */ diff = strlen(bp.name) - ps->stmt_name_len; buf = pktbuf_temp(); if (buf == NULL) goto oom; pktbuf_put_char(buf, pkt->type); /* * the -1 is because the length field does not include type byte, but * pkt->len does */ pktbuf_put_uint32(buf, pkt->len - diff - 1); pktbuf_put_string(buf, bp.portal); pktbuf_put_string(buf, ps->stmt_name); if (client->packet_cb_state.flag == CB_HANDLE_COMPLETE_PACKET) { /* * If we used special callback buffering for this packet then * we need to include all the bytes following the statement * name, because our SBuf logic considers those already * consumed by the callback. This is an exceptional case. It * only happens when the statement name does not fit in * pkt_buf. */ pktbuf_put_bytes(buf, pkt->data.data + pkt->data.read_pos, pkt->data.write_pos - pkt->data.read_pos); if (!sbuf_queue_packet(&client->sbuf, &server->sbuf, buf)) goto oom; return true; } /* * If we didn't use special callback buffering then we tell SBuf to * skip over all the bytes up to and including the statement name. But * for all the bytes after the statement name we use normal packet * forwarding logic. The reason for this is that our normal packet * forwarding logic is more efficient at sending data than the * sbuf_queue_packet logic, especially for large amounts of data. And * since the data after the statement name include the arguments to the * prepared statement, the remaining amount of data can be quite large. */ if (!sbuf_queue_packet(&client->sbuf, &server->sbuf, buf)) goto oom; sbuf_prepare_skip_then_send_leftover(&client->sbuf, &server->sbuf, pkt->data.read_pos, pkt->len); return true; oom: disconnect_client(client, true, "out of memory"); /* * We also disconnect the server, because we probably messed with its state * at this prepared statement state at this point. And rolling that back is * hard. */ disconnect_server(client->link, true, "out of memory"); return false; } /* * Handle a Describe packet for a named prepared statement * * Rewrites the given Describe packet to use the server-side statement name * instead of the client-side one. If the statement is not yet prepared on the * server, then we first send a Parse command to the server for the prepared * statement in question. */ bool handle_describe_command(PgSocket *client, PktHdr *pkt) { PgSocket *server = client->link; PgDescribePacket dp; PgClientPreparedStatement *client_ps = NULL; PgPreparedStatement *ps; bool res; Assert(server); if (!unmarshall_describe_packet(client, pkt, &dp) || dp.type != 'S') return false; client_ps = get_client_prepared_statement(client, dp.name); if (!client_ps) return false; ps = client_ps->ps; if (!ensure_statement_is_prepared_on_server(server, ps)) goto oom; slog_debug(client, "handle_describe_command: mapped statement '%s' (query '%s') to '%s'", dp.name, ps->query_and_parameters, ps->stmt_name); /* * Track the Describe command that we send to server and forward the * response to the client, because they expect one. */ if (!add_outstanding_request(client, PqMsg_Describe, RA_FORWARD)) goto oom; skip_possibly_completely_buffered_packet(client, pkt); QUEUE_DescribeStmt(res, client, server, ps->stmt_name); return res; oom: disconnect_client(client, true, "out of memory"); /* * We also disconnect the server, because we probably messed with its state * at this prepared statement state at this point. And rolling that back is * hard. */ disconnect_server(client->link, true, "out of memory"); return false; } /* * Handle a Close packet for a named prepared statement * * This does not actually close the mapped prepared statement on the server. But * it does remove the mapping from the client, so that the client can reuse the * name of this prepared statement for a new one. */ bool handle_close_statement_command(PgSocket *client, PktHdr *pkt, PgClosePacket *close_packet) { PgClientPreparedStatement *client_ps = NULL; bool res = true; HASH_FIND_STR(client->client_prepared_statements, close_packet->name, client_ps); if (client_ps) { slog_noise(client, "handle_close_command: removed '%s' from cached prepared statements, items remaining %u", close_packet->name, HASH_COUNT(client->client_prepared_statements)); HASH_DEL(client->client_prepared_statements, client_ps); if (--client_ps->ps->use_count == 0) { HASH_DEL(prepared_statements, client_ps->ps); free(client_ps->ps); } free(client_ps); } /* Do not forward packet to server */ skip_possibly_completely_buffered_packet(client, pkt); if (!client->link || statlist_count(&client->link->outstanding_requests) == 0) { slog_debug(client, "handle_close_statement_command: no outstanding requests so instantly answering client"); SEND_CloseComplete(res, client); return res; } if (!add_outstanding_request(client, PqMsg_Close, RA_FAKE)) { return false; } return true; } /* * Frees all the prepared statements that are cached on the client. */ void free_client_prepared_statements(PgSocket *client) { PgClientPreparedStatement *client_ps, *tmp; HASH_ITER(hh, client->client_prepared_statements, client_ps, tmp) { HASH_DEL(client->client_prepared_statements, client_ps); if (--client_ps->ps->use_count == 0) { HASH_DEL(prepared_statements, client_ps->ps); free(client_ps->ps); } free(client_ps); } free(client->client_prepared_statements); client->client_prepared_statements = NULL; } /* * Frees all the prepared statements that are cached on the server. */ void free_server_prepared_statements(PgSocket *server) { struct PgServerPreparedStatement *current, *tmp_s; HASH_ITER(hh, server->server_prepared_statements, current, tmp_s) { HASH_DEL(server->server_prepared_statements, current); free_server_prepared_statement(current); } free(server->server_prepared_statements); server->server_prepared_statements = NULL; } pgbouncer-1.24.1/src/dnslookup.c0000644000175000000000000006343114777762222013467 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" #include #include #if !defined(USE_EVDNS) && !defined(USE_CARES) #define USE_GETADDRINFO_A #endif #ifdef USE_EVDNS #include #define addrinfo evutil_addrinfo #define freeaddrinfo evutil_freeaddrinfo #endif /* USE_EVDNS */ #ifdef USE_CARES #define CARES_NO_DEPRECATED #include #include #ifdef HAVE_ARES_NAMESER_H #include #else #include #endif #define ZONE_RECHECK 1 #else /* only c-ares requires this */ #define impl_per_loop(ctx) #endif #ifndef ZONE_RECHECK #define ZONE_RECHECK 0 /* no implementation, also avoid 'unused' warning */ #define impl_query_soa_serial(ctx, name) do { if (0) got_zone_serial(ctx, NULL); } while (0) #define cf_dns_zone_check_period (0) #endif /* * There can be several client request (tokens) * attached to single actual request. */ struct DNSToken { struct List node; adns_callback_f cb_func; void *cb_arg; }; /* * Cached DNS query (hostname). */ struct DNSRequest { struct AANode node; /* DNSContext->req_tree */ struct List znode; /* DNSZone->host_list */ struct DNSContext *ctx; struct DNSZone *zone; struct List ucb_list; /* DNSToken->node */ char *name; int namelen; bool done; struct addrinfo *result; struct addrinfo *current; struct addrinfo *oldres; usec_t res_ttl; }; /* zone name serial */ struct DNSZone { struct List lnode; /* DNSContext->zone_list */ struct AANode tnode; /* DNSContext->zone_tree */ struct StatList host_list; /* DNSRequest->znode */ char *zonename; uint32_t serial; }; /* * Top struct for DNS data. */ struct DNSContext { struct AATree req_tree; void *edns; struct AATree zone_tree; /* DNSZone->tnode */ struct List zone_list; /* DNSZone->lnode */ struct DNSZone *cur_zone; struct event ev_zone_timer; int zone_state; int active; /* number of in-flight queries */ }; static void deliver_info(struct DNSRequest *req); static void got_result_gai(int result, struct addrinfo *res, void *arg); static void zone_register(struct DNSContext *ctx, struct DNSRequest *req); static void zone_init(struct DNSContext *ctx); static void zone_free(struct DNSContext *ctx); static void got_zone_serial(struct DNSContext *ctx, uint32_t *serial); /* * Custom addrinfo generation */ #if defined(USE_CARES) static struct addrinfo *mk_addrinfo(const void *adr, int af) { struct addrinfo *ai; ai = calloc(1, sizeof(*ai)); if (!ai) return NULL; if (af == AF_INET) { struct sockaddr_in *sa4; sa4 = calloc(1, sizeof(*sa4)); if (!sa4) goto failed; memcpy(&sa4->sin_addr, adr, 4); sa4->sin_family = af; ai->ai_addr = (struct sockaddr *)sa4; ai->ai_addrlen = sizeof(*sa4); } else if (af == AF_INET6) { struct sockaddr_in6 *sa6; sa6 = calloc(1, sizeof(*sa6)); if (!sa6) goto failed; memcpy(&sa6->sin6_addr, adr, sizeof(sa6->sin6_addr)); sa6->sin6_family = af; ai->ai_addr = (struct sockaddr *)sa6; ai->ai_addrlen = sizeof(*sa6); } ai->ai_protocol = IPPROTO_TCP; ai->ai_socktype = SOCK_STREAM; ai->ai_family = af; return ai; failed: free(ai); return NULL; } #define freeaddrinfo(x) local_freeaddrinfo(x) static void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *cur; while (ai) { cur = ai; ai = ai->ai_next; free(cur->ai_addr); free(cur); } } #ifdef USE_CARES static inline struct addrinfo *convert_hostent(const struct hostent *h) { struct addrinfo *ai, *first = NULL, *last = NULL; int i; for (i = 0; h->h_addr_list[i]; i++) { ai = mk_addrinfo(h->h_addr_list[i], h->h_addrtype); if (!ai) goto failed; if (!first) first = ai; else last->ai_next = ai; last = ai; } return first; failed: freeaddrinfo(first); return NULL; } #endif /* USE_CARES */ #endif /* custom addrinfo */ /* * ADNS with glibc's getaddrinfo_a() */ #ifdef USE_GETADDRINFO_A const char *adns_get_backend(void) { #ifdef HAVE_GETADDRINFO_A return "libc" #ifdef __GLIBC__ "-" STR(__GLIBC__) "." STR(__GLIBC_MINOR__); #endif ; #else return "compat"; #endif } struct GaiRequest { struct List node; struct DNSRequest *req; struct gaicb gairq; }; struct GaiContext { struct DNSContext *ctx; struct List gairq_list; struct event ev; struct sigevent sev; }; static void dns_signal(int f, short ev, void *arg) { struct GaiContext *gctx = arg; struct List *el, *tmp; struct GaiRequest *rq; int e; list_for_each_safe(el, &gctx->gairq_list, tmp) { rq = container_of(el, struct GaiRequest, node); e = gai_error(&rq->gairq); if (e == EAI_INPROGRESS) continue; /* got one */ list_del(&rq->node); got_result_gai(e, rq->gairq.ar_result, rq->req); free(rq); } } static bool impl_init(struct DNSContext *ctx) { struct GaiContext *gctx; if (cf_resolv_conf && cf_resolv_conf[0]) { log_error("resolv_conf setting is not supported by libc adns"); return false; } gctx = calloc(1, sizeof(*gctx)); if (!gctx) return false; list_init(&gctx->gairq_list); gctx->ctx = ctx; gctx->sev.sigev_notify = SIGEV_SIGNAL; gctx->sev.sigev_signo = SIGALRM; evsignal_assign(&gctx->ev, pgb_event_base, SIGALRM, dns_signal, gctx); if (evsignal_add(&gctx->ev, NULL) < 0) { free(gctx); return false; } ctx->edns = gctx; return true; } static void impl_launch_query(struct DNSRequest *req) { static const struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; struct GaiContext *gctx = req->ctx->edns; struct GaiRequest *grq; int res; struct gaicb *cb; grq = calloc(1, sizeof(*grq)); if (!grq) goto failed2; list_init(&grq->node); grq->req = req; grq->gairq.ar_name = req->name; grq->gairq.ar_request = &hints; list_append(&gctx->gairq_list, &grq->node); cb = &grq->gairq; res = getaddrinfo_a(GAI_NOWAIT, &cb, 1, &gctx->sev); if (res != 0) goto failed; return; failed: if (res == EAI_SYSTEM) { log_warning("dns: getaddrinfo_a(%s)=%d, errno=%d (%s)", req->name, res, errno, strerror(errno)); } else { log_warning("dns: getaddrinfo_a(%s)=%d", req->name, res); } list_del(&grq->node); free(grq); failed2: req->done = true; deliver_info(req); } static void impl_release(struct DNSContext *ctx) { struct GaiContext *gctx = ctx->edns; if (gctx) { evsignal_del(&gctx->ev); free(gctx); ctx->edns = NULL; } } #endif /* USE_GETADDRINFO_A */ /* * ADNS with libevent2 */ #ifdef USE_EVDNS const char *adns_get_backend(void) { return "evdns2"; } /* * Confusingly, this is not the same as evdns_err_to_string(). */ static const char *_evdns_base_resolv_conf_parse_err_to_string(int err) { switch (err) { case 0: return "no error"; case 1: return "failed to open file"; case 2: return "failed to stat file"; case 3: return "file too large"; case 4: return "out of memory"; case 5: return "short read from file"; case 6: return "no nameservers listed in the file"; default: return "[Unknown error code]"; } } static bool impl_init(struct DNSContext *ctx) { if (cf_resolv_conf && cf_resolv_conf[0]) { int err; ctx->edns = evdns_base_new(pgb_event_base, 0); if (!ctx->edns) { log_error("evdns_base_new failed"); return false; } err = evdns_base_resolv_conf_parse(ctx->edns, DNS_OPTIONS_ALL, cf_resolv_conf); if (err) { log_error("evdns parsing of \"%s\" failed: %s", cf_resolv_conf, _evdns_base_resolv_conf_parse_err_to_string(err)); return false; } } else { ctx->edns = evdns_base_new(pgb_event_base, 1); if (!ctx->edns) { log_error("evdns_base_new failed"); return false; } } return true; } static void impl_launch_query(struct DNSRequest *req) { static const struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; struct evdns_getaddrinfo_request *gai_req; struct evdns_base *dns = req->ctx->edns; gai_req = evdns_getaddrinfo(dns, req->name, NULL, &hints, got_result_gai, req); log_noise("dns: evdns_getaddrinfo(%s)=%p", req->name, gai_req); } static void impl_release(struct DNSContext *ctx) { struct evdns_base *dns = ctx->edns; evdns_base_free(dns, 0); } #endif /* USE_EVDNS */ /* * ADNS with */ #ifdef USE_CARES #define MAX_CARES_FDS 16 struct XaresFD { struct event ev; /* fd event state is persistent */ struct XaresMeta *meta; /* pointer to parent context */ ares_socket_t sock; /* socket value */ short wait; /* EV_READ / EV_WRITE */ bool in_use; /* is this slot assigned */ }; struct XaresMeta { /* c-ares descriptor */ ares_channel chan; /* how many elements in fds array are in use */ int max_fds; /* static array for fds */ struct XaresFD fds[MAX_CARES_FDS]; /* timer event is one-shot */ struct event ev_timer; /* is timer activated? */ bool timer_active; /* If dns events happened during event loop, timer may need recalibration. */ bool got_events; }; const char *adns_get_backend(void) { return "c-ares " ARES_VERSION_STR; } /* called by libevent on timer timeout */ static void xares_timer_cb(evutil_socket_t sock, short flags, void *arg) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; ares_process_fd(meta->chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD); meta->timer_active = false; meta->got_events = true; } /* called by libevent on fd event */ static void xares_fd_cb(evutil_socket_t sock, short flags, void *arg) { struct XaresFD *xfd = arg; struct XaresMeta *meta = xfd->meta; ares_socket_t r, w; r = (flags & EV_READ) ? xfd->sock : ARES_SOCKET_BAD; w = (flags & EV_WRITE) ? xfd->sock : ARES_SOCKET_BAD; ares_process_fd(meta->chan, r, w); meta->got_events = true; } /* called by c-ares on new socket creation */ static int xares_new_socket_cb(ares_socket_t sock, int sock_type, void *arg) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; struct XaresFD *xfd; int pos; /* find free slot in array */ for (pos = 0; pos < meta->max_fds; pos++) { if (!meta->fds[pos].in_use) break; } if (pos >= MAX_CARES_FDS) { log_warning("c-ares fd overflow"); return ARES_ENOMEM; } if (pos == meta->max_fds) meta->max_fds++; /* fill it */ xfd = &meta->fds[pos]; xfd->meta = meta; xfd->sock = sock; xfd->wait = 0; xfd->in_use = true; return ARES_SUCCESS; } /* called by c-ares on socket state change (r=w=0 means socket close) */ static void xares_state_cb(void *arg, ares_socket_t sock, int r, int w) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; struct XaresFD *xfd; int pos; short new_wait = 0; if (r) new_wait |= EV_READ; if (w) new_wait |= EV_WRITE; /* find socket */ for (pos = 0; pos < meta->max_fds; pos++) { xfd = &meta->fds[pos]; if (!xfd->in_use) continue; if (xfd->sock != sock) continue; /* no change? */ if (xfd->wait == new_wait) return; goto re_set; } log_warning("adns: c-ares state change for unknown fd: %u", (unsigned)sock); return; re_set: if (xfd->wait) event_del(&xfd->ev); xfd->wait = new_wait; if (new_wait) { event_assign(&xfd->ev, pgb_event_base, sock, new_wait | EV_PERSIST, xares_fd_cb, xfd); if (event_add(&xfd->ev, NULL) < 0) log_warning("adns: event_add failed: %s", strerror(errno)); } else { xfd->in_use = false; } return; } /* called by c-ares on dns reply */ static void xares_host_cb(void *arg, int status, int timeouts, struct hostent *h) { struct DNSRequest *req = arg; struct addrinfo *res = NULL; log_noise("dns: xares_host_cb(%s)=%s", req->name, ares_strerror(status)); if (status == ARES_SUCCESS) { res = convert_hostent(h); got_result_gai(0, res, req); } else { log_debug("DNS lookup failed: %s - %s", req->name, ares_strerror(status)); got_result_gai(0, res, req); } } /* send hostname query */ static void impl_launch_query(struct DNSRequest *req) { struct XaresMeta *meta = req->ctx->edns; int af; /* * c-ares <= 1.10 cannot resolve CNAME with AF_UNSPEC. * * Force IPv4 there. * * Fixed in "host_callback: Fall back to AF_INET on searching with AF_UNSPEC" (c1fe47f) * in c-ares repo. */ #if ARES_VERSION <= 0x10A00 #warning c-ares <=1.10 has buggy IPv6 support; this PgBouncer build will use IPv4 only. af = AF_INET; #else af = AF_UNSPEC; #endif log_noise("dns: ares_gethostbyname(%s)", req->name); ares_gethostbyname(meta->chan, req->name, af, xares_host_cb, req); meta->got_events = true; } /* re-set timer if any dns event happened */ static void impl_per_loop(struct DNSContext *ctx) { struct timeval tv, *tvp; struct XaresMeta *meta = ctx->edns; if (!meta->got_events) return; if (meta->timer_active) { event_del(&meta->ev_timer); meta->timer_active = false; } tvp = ares_timeout(meta->chan, NULL, &tv); if (tvp != NULL) { if (event_add(&meta->ev_timer, tvp) < 0) log_warning("impl_per_loop: event_add failed: %s", strerror(errno)); meta->timer_active = true; } meta->got_events = false; } /* c-ares setup */ static bool impl_init(struct DNSContext *ctx) { struct XaresMeta *meta; int err; int mask; struct ares_options opts; err = ares_library_init(ARES_LIB_INIT_ALL); if (err) { log_error("ares_library_init: %s", ares_strerror(err)); return false; } meta = calloc(1, sizeof(*meta)); if (!meta) return false; memset(&opts, 0, sizeof(opts)); opts.sock_state_cb = xares_state_cb; opts.sock_state_cb_data = ctx; mask = ARES_OPT_SOCK_STATE_CB; if (cf_resolv_conf && cf_resolv_conf[0]) { #ifdef ARES_OPT_RESOLVCONF opts.resolvconf_path = strdup(cf_resolv_conf); if (!opts.resolvconf_path) { free(meta); return false; } mask |= ARES_OPT_RESOLVCONF; #else log_error("resolv_conf setting is not supported by this version of c-ares"); free(meta); return false; #endif } err = ares_init_options(&meta->chan, &opts, mask); if (err) { free(meta); log_error("ares_library_init: %s", ares_strerror(err)); return false; } ares_set_socket_callback(meta->chan, xares_new_socket_cb, ctx); evtimer_assign(&meta->ev_timer, pgb_event_base, xares_timer_cb, ctx); ctx->edns = meta; return true; } /* c-ares shutdown */ static void impl_release(struct DNSContext *ctx) { struct XaresMeta *meta = ctx->edns; ares_destroy(meta->chan); ares_library_cleanup(); if (meta->timer_active) event_del(&meta->ev_timer); free(meta); ctx->edns = NULL; } /* * query SOA with c-ares */ /* called by c-ares on SOA reply */ static void xares_soa_cb(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; struct ares_soa_reply *soa = NULL; meta->got_events = true; log_noise("ares SOA result: %s", ares_strerror(status)); if (status != ARES_SUCCESS) { got_zone_serial(ctx, NULL); return; } status = ares_parse_soa_reply(abuf, alen, &soa); if (status == ARES_SUCCESS) { got_zone_serial(ctx, &soa->serial); } else { log_warning("ares_parse_soa: %s", ares_strerror(status)); got_zone_serial(ctx, NULL); } ares_free_data(soa); } /* send SOA query */ static int impl_query_soa_serial(struct DNSContext *ctx, const char *zonename) { struct XaresMeta *meta = ctx->edns; log_debug("dns: ares query SOA(%s)", zonename); ares_search(meta->chan, zonename, ns_c_in, ns_t_soa, xares_soa_cb, ctx); meta->got_events = true; return 0; } #endif /* USE_CARES */ /* * Generic framework */ static void deliver_info(struct DNSRequest *req) { struct DNSContext *ctx = req->ctx; struct DNSToken *ucb; struct List *el; const struct addrinfo *ai = req->current; char sabuf[128]; ctx->active--; loop: /* get next req */ el = list_pop(&req->ucb_list); if (!el) return; ucb = container_of(el, struct DNSToken, node); /* launch callback */ log_noise("dns: deliver_info(%s) addr=%s", req->name, ai ? sa2str(ai->ai_addr, sabuf, sizeof(sabuf)) : "NULL"); ucb->cb_func(ucb->cb_arg, ai ? ai->ai_addr : NULL, ai ? ai->ai_addrlen : 0); free(ucb); /* scroll req list */ if (ai) { req->current = ai->ai_next; if (!req->current) req->current = req->result; } goto loop; } static int req_cmp(uintptr_t arg, struct AANode *node) { const char *s1 = (char *)arg; struct DNSRequest *req = container_of(node, struct DNSRequest, node); return strcmp(s1, req->name); } static void req_reset(struct DNSRequest *req) { req->done = false; if (req->result) { if (req->oldres) freeaddrinfo(req->oldres); req->oldres = req->result; } req->result = req->current = NULL; } static void req_free(struct AANode *node, void *arg) { struct DNSToken *ucb; struct DNSRequest *req; struct List *el; req = container_of(node, struct DNSRequest, node); while ((el = list_pop(&req->ucb_list)) != NULL) { ucb = container_of(el, struct DNSToken, node); free(ucb); } req_reset(req); if (req->oldres) { freeaddrinfo(req->oldres); req->oldres = NULL; } if (req->zone) statlist_remove(&req->zone->host_list, &req->znode); free(req->name); free(req); } struct DNSContext *adns_create_context(void) { struct DNSContext *ctx; log_debug("adns_create_context: %s", adns_get_backend()); ctx = calloc(1, sizeof(*ctx)); if (!ctx) return NULL; aatree_init(&ctx->req_tree, req_cmp, req_free); zone_init(ctx); if (!impl_init(ctx)) { adns_free_context(ctx); return NULL; } return ctx; } void adns_free_context(struct DNSContext *ctx) { if (ctx) { impl_release(ctx); aatree_destroy(&ctx->req_tree); zone_free(ctx); free(ctx); } } struct DNSToken *adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *cb_arg) { int namelen = strlen(name); struct DNSRequest *req; struct DNSToken *ucb; struct AANode *node; /* setup actual lookup */ node = aatree_search(&ctx->req_tree, (uintptr_t)name); if (node) { req = container_of(node, struct DNSRequest, node); } else { log_noise("dns: new req: %s", name); req = calloc(1, sizeof(*req)); if (!req) goto nomem; req->name = strdup(name); if (!req->name) { free(req); goto nomem; } req->ctx = ctx; req->namelen = namelen; list_init(&req->ucb_list); list_init(&req->znode); aatree_insert(&ctx->req_tree, (uintptr_t)req->name, &req->node); zone_register(ctx, req); ctx->active++; impl_launch_query(req); } /* remember user callback */ ucb = calloc(1, sizeof(*ucb)); if (!ucb) goto nomem; list_init(&ucb->node); ucb->cb_func = cb_func; ucb->cb_arg = cb_arg; list_append(&req->ucb_list, &ucb->node); /* if already have final result, report it */ if (req->done) { if (req->res_ttl < get_cached_time()) { log_noise("dns: ttl over: %s", req->name); req_reset(req); ctx->active++; impl_launch_query(req); } else { deliver_info(req); } } /* if ->done, then we have already reported */ return req->done ? NULL : ucb; nomem: log_warning("dns(%s): req failed, no mem", name); cb_func(cb_arg, NULL, 0); return NULL; } static int cmp_addrinfo(const struct addrinfo *a1, const struct addrinfo *a2) { if (a1->ai_family != a2->ai_family) return a1->ai_family - a2->ai_family; if (a1->ai_addrlen != a2->ai_addrlen) return a1->ai_addrlen - a2->ai_addrlen; return memcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen); } /* check if new dns reply is missing some IP compared to old one */ static void check_req_result_changes(struct DNSRequest *req) { struct addrinfo *ai, *aj; for (ai = req->oldres; ai; ai = ai->ai_next) { bool found = false; for (aj = req->result; aj; aj = aj->ai_next) { if (cmp_addrinfo(ai, aj) == 0) { found = true; break; } } /* missing IP (possible DNS failover) make connections to it dirty */ if (!found) tag_host_addr_dirty(req->name, ai->ai_addr); } } /* struct addrinfo -> deliver_info() */ static void got_result_gai(int result, struct addrinfo *res, void *arg) { struct DNSRequest *req = arg; req_reset(req); if (result == 0 && res) { req->result = res; req->current = res; if (req->oldres) check_req_result_changes(req); /* show all results */ if (cf_verbose > 1) { const struct addrinfo *ai = res; int n = 0; char buf[128]; while (ai) { log_noise("DNS: %s[%d] = %s [%s]", req->name, n++, sa2str(ai->ai_addr, buf, sizeof(buf)), ai->ai_socktype == SOCK_STREAM ? "STREAM" : "OTHER"); ai = ai->ai_next; } } req->res_ttl = get_cached_time() + cf_dns_max_ttl; } else { /* lookup failed */ log_warning("DNS lookup failed: %s: result=%d", req->name, result); req->res_ttl = get_cached_time() + cf_dns_nxdomain_ttl; } req->done = true; deliver_info(req); } void adns_cancel(struct DNSContext *ctx, struct DNSToken *tk) { list_del(&tk->node); memset(tk, 0, sizeof(*tk)); free(tk); } void adns_info(struct DNSContext *ctx, int *names, int *zones, int *queries, int *pending) { *names = ctx->req_tree.count; *zones = ctx->zone_tree.count; *queries = ctx->active; *pending = 0; } /* * zone code */ static void zone_item_free(struct AANode *n, void *arg) { struct DNSZone *z = container_of(n, struct DNSZone, tnode); list_del(&z->lnode); free(z->zonename); free(z); } static int zone_item_cmp(uintptr_t val1, struct AANode *n2) { const char *name1 = (const char *)val1; struct DNSZone *z2 = container_of(n2, struct DNSZone, tnode); return strcasecmp(name1, z2->zonename); } static void zone_init(struct DNSContext *ctx) { aatree_init(&ctx->zone_tree, zone_item_cmp, zone_item_free); list_init(&ctx->zone_list); } static void zone_free(struct DNSContext *ctx) { aatree_destroy(&ctx->zone_tree); } static void zone_register(struct DNSContext *ctx, struct DNSRequest *req) { struct DNSZone *z; struct AANode *n; const char *name; log_debug("zone_register(%s)", req->name); name = strchr(req->name, '.'); if (!name || name[1] == 0) return; name++; log_debug("zone_register(%s): name=%s", req->name, name); n = aatree_search(&ctx->zone_tree, (uintptr_t)name); if (n) { /* already exists */ z = container_of(n, struct DNSZone, tnode); req->zone = z; statlist_append(&z->host_list, &req->znode); return; } /* create struct */ z = calloc(1, sizeof(*z)); if (!z) return; z->zonename = strdup(name); if (!z->zonename) { free(z); return; } statlist_init(&z->host_list, "host_list"); list_init(&z->lnode); /* link */ aatree_insert(&ctx->zone_tree, (uintptr_t)z->zonename, &z->tnode); list_append(&ctx->zone_list, &z->lnode); statlist_append(&z->host_list, &req->znode); req->zone = z; } static void zone_timer(evutil_socket_t fd, short flg, void *arg) { struct DNSContext *ctx = arg; struct List *el; struct DNSZone *z; if (list_empty(&ctx->zone_list)) { ctx->zone_state = 0; return; } el = list_first(&ctx->zone_list); z = container_of(el, struct DNSZone, lnode); ctx->zone_state = 1; ctx->cur_zone = z; ctx->active++; impl_query_soa_serial(ctx, z->zonename); } static void launch_zone_timer(struct DNSContext *ctx) { struct timeval tv; tv.tv_sec = cf_dns_zone_check_period / USEC; tv.tv_usec = cf_dns_zone_check_period % USEC; evtimer_assign(&ctx->ev_zone_timer, pgb_event_base, zone_timer, ctx); safe_evtimer_add(&ctx->ev_zone_timer, &tv); ctx->zone_state = 2; } void adns_zone_cache_maint(struct DNSContext *ctx) { if (!cf_dns_zone_check_period) { if (ctx->zone_state == 2) { event_del(&ctx->ev_zone_timer); ctx->zone_state = 0; } ctx->cur_zone = NULL; return; } else if (ctx->zone_state == 0) { if (list_empty(&ctx->zone_list)) return; launch_zone_timer(ctx); } } static void zone_requeue(struct DNSContext *ctx, struct DNSZone *z) { struct List *el; struct DNSRequest *req; statlist_for_each(el, &z->host_list) { req = container_of(el, struct DNSRequest, znode); if (!req->done) continue; req->res_ttl = 0; ctx->active++; impl_launch_query(req); } } static void got_zone_serial(struct DNSContext *ctx, uint32_t *serial) { struct DNSZone *z = ctx->cur_zone; struct List *el; ctx->active--; if (!ctx->zone_state || !z) return; if (serial) { /* wraparound compare */ int32_t s1 = z->serial; int32_t s2 = *serial; int32_t ds = s2 - s1; if (ds > 0) { log_info("zone '%s' serial changed: old=%u new=%u", z->zonename, z->serial, *serial); z->serial = *serial; zone_requeue(ctx, z); } else { log_debug("zone '%s' unchanged: serial=%u", z->zonename, *serial); } } else { log_debug("failure to get zone '%s' serial", z->zonename); } el = z->lnode.next; if (el != &ctx->zone_list) { z = container_of(el, struct DNSZone, lnode); ctx->cur_zone = z; ctx->active++; impl_query_soa_serial(ctx, z->zonename); } else { launch_zone_timer(ctx); } } /* * Cache walkers */ struct WalkInfo { adns_walk_name_f name_cb; adns_walk_zone_f zone_cb; void *arg; }; static void walk_name(struct AANode *n, void *arg) { struct WalkInfo *w = arg; struct DNSRequest *req = container_of(n, struct DNSRequest, node); w->name_cb(w->arg, req->name, req->result, req->res_ttl); } static void walk_zone(struct AANode *n, void *arg) { struct WalkInfo *w = arg; struct DNSZone *z = container_of(n, struct DNSZone, tnode); w->zone_cb(w->arg, z->zonename, z->serial, statlist_count(&z->host_list)); } void adns_walk_names(struct DNSContext *ctx, adns_walk_name_f cb, void *arg) { struct WalkInfo w; w.name_cb = cb; w.arg = arg; aatree_walk(&ctx->req_tree, AA_WALK_IN_ORDER, walk_name, &w); } void adns_walk_zones(struct DNSContext *ctx, adns_walk_zone_f cb, void *arg) { struct WalkInfo w; w.zone_cb = cb; w.arg = arg; aatree_walk(&ctx->zone_tree, AA_WALK_IN_ORDER, walk_zone, &w); } void adns_per_loop(struct DNSContext *ctx) { impl_per_loop(ctx); } pgbouncer-1.24.1/src/takeover.c0000644000175000000000000002333714777762222013272 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Connect to running bouncer process, load fds from it, shut it down * and continue with them. * * Each row from SHOW FDS will have corresponding fd in ancillary message. * * Manpages: unix, sendmsg, recvmsg, cmsg, readv */ #include "bouncer.h" #include /* * Takeover done, old process shut down, * kick this one running. */ static PgSocket *old_bouncer = NULL; void takeover_finish(void) { uint8_t buf[512]; int fd = sbuf_socket(&old_bouncer->sbuf); bool res; ssize_t got; log_info("sending SHUTDOWN;"); socket_set_nonblocking(fd, 0); SEND_generic(res, old_bouncer, PqMsg_Query, "s", "SHUTDOWN;"); if (!res) die("failed to send SHUTDOWN;"); while (1) { got = safe_recv(fd, buf, sizeof(buf), 0); if (got == 0) break; if (got < 0) die("sky is falling - error while waiting result from SHUTDOWN: %s", strerror(errno)); } disconnect_server(old_bouncer, false, "disko over"); old_bouncer = NULL; if (cf_pidfile && cf_pidfile[0]) { log_info("waiting for old pidfile to go away"); while (1) { struct stat st; if (stat(cf_pidfile, &st) < 0) { if (errno == ENOENT) break; } usleep(USEC/10); } } log_info("old process killed, resuming work"); resume_all(); } static void takeover_finish_part1(PgSocket *bouncer) { Assert(old_bouncer == NULL); /* unregister bouncer from libevent */ if (!sbuf_pause(&bouncer->sbuf)) fatal_perror("sbuf_pause failed"); old_bouncer = bouncer; cf_reboot = 0; log_info("disko over, going background"); } /* parse msg for fd and info */ static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg) { int fd; char *task, *saddr, *user, *db; char *client_enc, *std_string, *datestyle, *timezone, *password, *scram_client_key, *scram_server_key; int scram_client_key_len, scram_server_key_len; int oldfd, port, linkfd; int got; uint64_t ckey; PgAddr addr; bool res = false; memset(&addr, 0, sizeof(addr)); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len >= CMSG_LEN(sizeof(int))) { /* get the fd */ memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); log_debug("got fd: %d", fd); } else { fatal("broken fd packet"); } /* parse row contents */ got = scan_text_result(pkt, "issssiqisssssbb", &oldfd, &task, &user, &db, &saddr, &port, &ckey, &linkfd, &client_enc, &std_string, &datestyle, &timezone, &password, &scram_client_key_len, &scram_client_key, &scram_server_key_len, &scram_server_key); if (got < 0) die("invalid data from old process"); if (task == NULL || saddr == NULL) die("incomplete data from old process"); log_debug("FD row: fd=%d(%d) linkfd=%d task=%s user=%s db=%s enc=%s", oldfd, fd, linkfd, task, user ? user : "NULL", db ? db : "NULL", client_enc ? client_enc : "NULL"); if (!password) password = ""; /* fill address */ if (strcmp(saddr, "unix") == 0) { pga_set(&addr, AF_UNIX, cf_listen_port); } else { if (!pga_pton(&addr, saddr, port)) fatal("failed to convert address: %s", saddr); } /* decide what to do with it */ if (strcmp(task, "client") == 0) { res = use_client_socket(fd, &addr, db, user, ckey, oldfd, linkfd, client_enc, std_string, datestyle, timezone, password, scram_client_key, scram_client_key_len, scram_server_key, scram_server_key_len); } else if (strcmp(task, "server") == 0) { res = use_server_socket(fd, &addr, db, user, ckey, oldfd, linkfd, client_enc, std_string, datestyle, timezone, password, scram_client_key, scram_client_key_len, scram_server_key, scram_server_key_len); } else if (strcmp(task, "pooler") == 0) { res = use_pooler_socket(fd, pga_is_unix(&addr)); } else { fatal("unknown task: %s", task); } free(scram_client_key); free(scram_server_key); if (!res) fatal("socket takeover failed"); } static void takeover_create_link(PgPool *pool, PgSocket *client) { struct List *item; PgSocket *server; statlist_for_each(item, &pool->active_server_list) { server = container_of(item, PgSocket, head); if (server->tmp_sk_oldfd == client->tmp_sk_linkfd) { server->link = client; client->link = server; return; } } fatal("takeover_create_link: failed to find pair"); } /* clean the inappropriate places the old fds got stored in */ static void takeover_clean_socket_list(struct StatList *list) { struct List *item; PgSocket *sk; statlist_for_each(item, list) { sk = container_of(item, PgSocket, head); if (sk->suspended) { sk->tmp_sk_oldfd = get_cached_time(); sk->tmp_sk_linkfd = get_cached_time(); } } } /* all fds loaded, create links */ static void takeover_postprocess_fds(void) { struct List *item, *item2; PgSocket *client; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; statlist_for_each(item2, &pool->active_client_list) { client = container_of(item2, PgSocket, head); if (client->suspended && client->tmp_sk_linkfd) takeover_create_link(pool, client); } } statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); takeover_clean_socket_list(&pool->active_client_list); takeover_clean_socket_list(&pool->active_server_list); takeover_clean_socket_list(&pool->idle_server_list); } } static void next_command(PgSocket *bouncer, struct MBuf *pkt) { bool res = true; const char *cmd; if (!mbuf_get_string(pkt, &cmd)) fatal("bad result pkt"); log_debug("takeover_recv_fds: CommandComplete body: %s", cmd); if (strcmp(cmd, "SUSPEND") == 0) { log_info("SUSPEND finished, sending SHOW FDS"); SEND_generic(res, bouncer, PqMsg_Query, "s", "SHOW FDS;"); } else if (strncmp(cmd, "SHOW", 4) == 0) { /* all fds loaded, review them */ takeover_postprocess_fds(); log_info("SHOW FDS finished"); takeover_finish_part1(bouncer); } else { fatal("got bad CMD from old bouncer: %s", cmd); } if (!res) fatal("command send failed"); } static void takeover_parse_data(PgSocket *bouncer, struct msghdr *msg, struct MBuf *data) { struct cmsghdr *cmsg; PktHdr pkt; cmsg = msg->msg_controllen ? CMSG_FIRSTHDR(msg) : NULL; while (mbuf_avail_for_read(data) > 0) { if (!get_header(data, &pkt)) fatal("cannot parse packet"); /* * There should not be partial reads from UNIX socket. */ if (incomplete_pkt(&pkt)) fatal("unexpected partial packet"); switch (pkt.type) { case PqMsg_RowDescription: log_debug("takeover_parse_data: RowDescription"); break; case PqMsg_DataRow: log_debug("takeover_parse_data: DataRow"); if (cmsg) { takeover_load_fd(&pkt.data, cmsg); cmsg = CMSG_NXTHDR(msg, cmsg); } else { fatal("got row without fd info"); } break; case PqMsg_ReadyForQuery: log_debug("takeover_parse_data: ReadyForQuery"); break; case PqMsg_CommandComplete: log_debug("takeover_parse_data: CommandComplete"); next_command(bouncer, &pkt.data); break; case PqMsg_ErrorResponse: log_server_error("old bouncer sent", &pkt); fatal("something failed"); default: fatal("takeover_parse_data: unexpected pkt: '%c'", pkt_desc(&pkt)); } } } /* * listen for data from old bouncer. * * use always recvmsg, to keep code simpler */ static void takeover_recv_cb(evutil_socket_t sock, short flags, void *arg) { PgSocket *bouncer = container_of(arg, PgSocket, sbuf); uint8_t data_buf[STARTUP_BUF * 2]; uint8_t cnt_buf[128]; struct msghdr msg; struct iovec io; ssize_t res; struct MBuf data; memset(&msg, 0, sizeof(msg)); io.iov_base = data_buf; io.iov_len = sizeof(data_buf); msg.msg_iov = &io; msg.msg_iovlen = 1; msg.msg_control = cnt_buf; msg.msg_controllen = sizeof(cnt_buf); res = safe_recvmsg(sock, &msg, 0); if (res > 0) { mbuf_init_fixed_reader(&data, data_buf, res); takeover_parse_data(bouncer, &msg, &data); } else if (res == 0) { fatal("unexpected EOF"); } else { if (errno == EAGAIN) return; fatal_perror("safe_recvmsg"); } } /* * login finished, send first command, * replace recv callback with custom recvmsg() based one. */ bool takeover_login(PgSocket *bouncer) { bool res; slog_info(bouncer, "login OK, sending SUSPEND"); SEND_generic(res, bouncer, PqMsg_Query, "s", "SUSPEND;"); if (res) { /* use own callback */ if (!sbuf_pause(&bouncer->sbuf)) fatal("sbuf_pause failed"); res = sbuf_continue_with_callback(&bouncer->sbuf, takeover_recv_cb); if (!res) fatal("takeover_login: sbuf_continue_with_callback failed"); } else { fatal("takeover_login: failed to send command"); } return res; } /* launch connection to running process */ void takeover_init(void) { PgDatabase *db; PgPool *pool = NULL; db = find_database("pgbouncer"); if (db) pool = get_pool(db, db->forced_user_credentials); if (!pool) fatal("no admin pool?"); log_info("takeover_init: launching connection"); launch_new_connection(pool, /* evict_if_needed= */ true); } void takeover_login_failed(void) { fatal("login failed"); } pgbouncer-1.24.1/src/server.c0000644000175000000000000006436414777762222012765 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Handling of server connections */ #include "bouncer.h" #include "usual/time.h" #include #define ERRCODE_CANNOT_CONNECT_NOW "57P03" static bool load_parameter(PgSocket *server, PktHdr *pkt, bool startup) { const char *key, *val; PgSocket *client = server->link; /* * Want to see complete packet. That means SMALL_PKT * in sbuf.c must be larger than max param pkt. */ if (incomplete_pkt(pkt)) return false; if (!mbuf_get_string(&pkt->data, &key)) goto failed; if (!mbuf_get_string(&pkt->data, &val)) goto failed; slog_debug(server, "S: param: %s = %s", key, val); varcache_set(&server->vars, key, val); if (client) { slog_debug(client, "setting client var: %s='%s'", key, val); varcache_set(&client->vars, key, val); } if (startup) { if (!add_welcome_parameter(server->pool, key, val)) goto failed_store; } return true; failed: disconnect_server(server, true, "broken ParameterStatus packet"); return false; failed_store: disconnect_server(server, true, "failed to store ParameterStatus"); return false; } /* * We cannot log in to the server at all. If we don't already have any usable * server connections, we disconnect all other clients in the pool that are * waiting for a server. */ void kill_pool_logins(PgPool *pool, const char *sqlstate, const char *msg) { struct List *item, *tmp; PgSocket *client; /* * The check for welcome_msg_ready is necessary because that indicates * that the pool got tagged as dirty. It's possible that there's still * working server connections in that case, but as soon as they get * unassigned from their client they would be closed. So they don't * really count as usable anymore. */ if (pool_connected_server_count(pool) != 0 && pool->welcome_msg_ready) return; statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); disconnect_client_sqlstate(client, true, sqlstate, msg); } } /* * We cannot log in to the server at all. If we don't already have any usable * server connections, we disconnect all other clients in the pool that are * also waiting for a server. We disconnect them with exactly the same error * message and code as we received from the server. */ const char * kill_pool_logins_server_error(PgPool *pool, PktHdr *errpkt) { const char *level, *sqlstate, *msg; parse_server_error(errpkt, &level, &msg, &sqlstate); log_warning("server login failed: %s %s", level, msg); /* * Kill all waiting clients unless it's a temporary error, such as * "database system is starting up". */ if (strcmp(sqlstate, ERRCODE_CANNOT_CONNECT_NOW) != 0) { log_noise("kill_pool_logins_server_error: sqlstate: %s", sqlstate); kill_pool_logins(pool, sqlstate, msg); } return msg; } /* process packets on server auth phase */ static bool handle_server_startup(PgSocket *server, PktHdr *pkt) { SBuf *sbuf = &server->sbuf; const char *msg; bool res = false; const uint8_t *ckey; if (incomplete_pkt(pkt)) { disconnect_server(server, true, "partial pkt in login phase"); return false; } /* ignore most that happens during connect_query */ if (server->exec_on_connect) { switch (pkt->type) { case PqMsg_ReadyForQuery: case PqMsg_ParameterStatus: /* handle them below */ break; case PqMsg_ErrorResponse: /* log & ignore errors */ log_server_error("S: error while executing exec_on_query", pkt); /* fallthrough */ default: /* ignore rest */ sbuf_prepare_skip(sbuf, pkt->len); return true; } } switch (pkt->type) { default: slog_error(server, "unknown pkt from server: '%c'", pkt_desc(pkt)); disconnect_server(server, true, "unknown pkt from server"); break; case PqMsg_ErrorResponse: /* * If we cannot log into the server, then we drop all clients * that are currently trying to log in because they will almost * certainly hit the same error. * * However, we don't do this if it's a replication connection, * because those can fail due to a variety of missing * permissions specific to replication connections, such as * missing REPLICATION role or missing pg_hba.conf line for the * replication database. In such cases normal connections would * still be able connect and query the database just fine, so * we don't want to kill all of those just yet. If there really * is a problem impacting all connections, we can wait for a * normal connection to report this problem. */ if (!server->replication) { msg = kill_pool_logins_server_error(server->pool, pkt); disconnect_server(server, true, "%s", (char *)msg); } else { log_server_error("S: login failed", pkt); disconnect_server(server, true, "login failed"); } break; /* packets that need closer look */ case PqMsg_AuthenticationRequest: slog_debug(server, "calling login_answer"); res = answer_authreq(server, pkt); if (!res) disconnect_server(server, false, "failed to answer authreq"); break; case PqMsg_ParameterStatus: res = load_parameter(server, pkt, true); break; case PqMsg_ReadyForQuery: if (server->exec_on_connect) { server->exec_on_connect = false; /* deliberately ignore transaction status */ } else if (server->pool->db->connect_query) { server->exec_on_connect = true; slog_debug(server, "server connect ok, send exec_on_connect"); SEND_generic(res, server, PqMsg_Query, "s", server->pool->db->connect_query); if (!res) disconnect_server(server, false, "exec_on_connect query failed"); break; } /* login ok */ slog_debug(server, "server login ok, start accepting queries"); server->ready = true; /* got all params */ finish_welcome_msg(server); /* need to notify sbuf if server was closed */ res = release_server(server); /* let the takeover process handle it */ if (res && server->pool->db->admin) res = takeover_login(server); break; /* ignorable packets */ case PqMsg_BackendKeyData: if (!mbuf_get_bytes(&pkt->data, BACKENDKEY_LEN, &ckey)) { disconnect_server(server, true, "bad cancel key"); return false; } memcpy(server->cancel_key, ckey, BACKENDKEY_LEN); res = true; break; case PqMsg_NoticeResponse: slog_noise(server, "skipping pkt: %c", pkt_desc(pkt)); res = true; break; } if (res) sbuf_prepare_skip(sbuf, pkt->len); return res; } /* * connection_pool_mode returns the pool_mode for the server. It specifically * forces session pooling if the server is a replication connection, because * replication connections require session pooling to work correctly. */ int connection_pool_mode(PgSocket *connection) { if (connection->replication) return POOL_SESSION; return probably_wrong_pool_pool_mode(connection->pool); } /* * probably_wrong_pool_pool_mode returns the pool_mode for the pool. * * IMPORTANT: You should almost certainly not use this function directly, * because the pool_mode of a pool is not necessarily the same as the pool mode * of each of the clients and servers in the pool. Most importantly replication * connections in a transaction/statement pool will still use session pooling. * * Normally you should use connection_pool_mode() */ int probably_wrong_pool_pool_mode(PgPool *pool) { int pool_mode = pool->user_credentials->global_user->pool_mode; if (pool_mode == POOL_INHERIT) pool_mode = pool->db->pool_mode; if (pool_mode == POOL_INHERIT) pool_mode = cf_pool_mode; return pool_mode; } int pool_pool_size(PgPool *pool) { int user_pool_size = pool->user_credentials ? pool->user_credentials->global_user->pool_size : -1; if (user_pool_size >= 0) return user_pool_size; else if (pool->db->pool_size >= 0) return pool->db->pool_size; else return cf_default_pool_size; } /* min_pool_size of the pool's db */ int pool_min_pool_size(PgPool *pool) { return database_min_pool_size(pool->db); } /* server_lifetime of the pool's db */ usec_t pool_server_lifetime(PgPool *pool) { if (pool->db->server_lifetime == 0) return cf_server_lifetime; else return pool->db->server_lifetime; } /* min_pool_size of the db */ int database_min_pool_size(PgDatabase *db) { if (db->min_pool_size < 0) return cf_min_pool_size; else return db->min_pool_size; } int pool_res_pool_size(PgPool *pool) { int user_res_pool_size = pool->user_credentials ? pool->user_credentials->global_user->res_pool_size : -1; if (user_res_pool_size >= 0) return user_res_pool_size; else if (pool->db->res_pool_size >= 0) return pool->db->res_pool_size; else return cf_res_pool_size; } int database_max_client_connections(PgDatabase *db) { if (db->max_db_client_connections <= 0) return cf_max_db_client_connections; else return db->max_db_client_connections; } int database_max_connections(PgDatabase *db) { if (db->max_db_connections <= 0) return cf_max_db_connections; else return db->max_db_connections; } int user_max_connections(PgGlobalUser *user) { if (user->max_user_connections <= 0) return cf_max_user_connections; else return user->max_user_connections; } int user_client_max_connections(PgGlobalUser *user) { if (user->max_user_client_connections <= 0) return cf_max_user_client_connections; else return user->max_user_client_connections; } /* process packets on logged in connection */ static bool handle_server_work(PgSocket *server, PktHdr *pkt) { bool ready = false; bool idle_tx = false; char state; SBuf *sbuf = &server->sbuf; PgSocket *client = server->link; bool async_response = false; struct List *item, *tmp; bool ignore_packet = false; Assert(!server->pool->db->admin); switch (pkt->type) { default: slog_error(server, "unknown pkt: '%c'", pkt_desc(pkt)); disconnect_server(server, true, "unknown pkt"); return false; /* pooling decisions will be based on this packet */ case PqMsg_ReadyForQuery: /* if partial pkt, wait */ if (!mbuf_get_char(&pkt->data, &state)) return false; if (!pop_outstanding_request(server, (char[]) {PqMsg_Sync, PqMsg_Query, PqMsg_FunctionCall, '\0'}, &ignore_packet) && server->query_failed) { if (!clear_outstanding_requests_until(server, (char[]) {PqMsg_Sync, '\0'})) return false; } server->query_failed = false; /* set ready only if no tx */ if (state == 'I') { ready = true; } else if (connection_pool_mode(server) == POOL_STMT) { disconnect_server(server, true, "transaction blocks not allowed in statement pooling mode"); return false; } else if (state == 'T' || state == 'E') { idle_tx = true; } break; case PqMsg_ParameterStatus: if (!load_parameter(server, pkt, false)) return false; break; /* * ErrorResponse and NoticeResponse packets currently set ->ready to false. Correct would * be to leave ->ready as-is, because overall TX state stays same. * It matters for connections in IDLE or USED state which get dirty * suddenly but should not as they are still usable. * * But the ErrorResponse or NoticeResponse packet between transactions signifies probably * dying backend. It is better to tag server as dirty and drop * it later. */ case PqMsg_ErrorResponse: if (server->setting_vars) { /* * the SET and user query will be different TX * so we cannot report SET error to user. */ log_server_error("varcache_apply failed", pkt); /* * client probably gave invalid values in startup pkt. * * no reason to keep such guys. */ disconnect_server(server, true, "invalid server parameter"); return false; } /* ErrorResponse and CommandComplete show end of copy mode */ if (server->copy_mode) { slog_debug(server, "COPY failed"); server->copy_mode = false; /* * Clear until next CopyDone or CopyFail message in the * queue. This is needed to remove any Sync messages * from the outstanding requests queue, for which we * don't expect a response from the server. * * It isn't a problem if the CopyDone or CopyFail * message has not been received yet. This message will * be removed from the queue later when the server * sends a ReadyForQuery message and we clear the queue * until the next Sync. * * NOTE: CopyFail is the obvious error case, because * here the client triggers a failure of the COPY. * But CopyDone is also included in the search. The * reason for that being that the server might fail the * COPY for some reason unknown to the client (e.g. a * unique constraint violation). */ if (!clear_outstanding_requests_until(server, (char[]) {PqMsg_CopyDone, PqMsg_CopyFail, '\0'})) return false; } server->query_failed = true; break; case PqMsg_CommandComplete: /* ErrorResponse and CommandComplete show end of copy mode */ if (server->copy_mode) { slog_debug(server, "COPY finished"); server->copy_mode = false; /* * Clear until next CopyDone message in the queue. This * is needed to remove any Sync messages from the * outstanding requests queue, for which we don't * expect a response from the server. */ if (!clear_outstanding_requests_until(server, (char[]) {PqMsg_CopyDone, '\0'})) return false; } /* * Clean up prepared statements if needed if the client sent a * DEALLOCATE ALL or a DISCARD ALL query. Not doing so would * confuse our prepared statement handling, because we would * expect certain queries to be prepared at the server that are * not. */ if (is_prepared_statements_enabled(server) && (pkt->len == 1 + 4 + 15 || pkt->len == 1 + 4 + 12)) { /* size of complete DEALLOCATE/DISCARD ALL */ const char *tag; if (mbuf_get_string(&pkt->data, &tag)) { if (strcmp(tag, "DEALLOCATE ALL") == 0 || strcmp(tag, "DISCARD ALL") == 0) { free_server_prepared_statements(server); if (client) free_client_prepared_statements(client); } } else { return false; } } pop_outstanding_request(server, (char[]) {PqMsg_Execute, '\0'}, &ignore_packet); break; case PqMsg_NoticeResponse: break; /* reply to LISTEN, don't change connection state */ case PqMsg_NotificationResponse: idle_tx = server->idle_tx; ready = server->ready; async_response = true; break; /* copy mode */ case PqMsg_CopyInResponse: case PqMsg_CopyBothResponse: slog_debug(server, "COPY started"); server->copy_mode = true; break; case PqMsg_CopyOutResponse: break; /* chat packets */ case PqMsg_ParseComplete: pop_outstanding_request(server, (char[]) {PqMsg_Parse, '\0'}, &ignore_packet); break; case PqMsg_BindComplete: pop_outstanding_request(server, (char[]) {PqMsg_Bind, '\0'}, &ignore_packet); break; case PqMsg_CloseComplete: pop_outstanding_request(server, (char[]) {PqMsg_Close, '\0'}, &ignore_packet); break; case PqMsg_NoData: case PqMsg_RowDescription: pop_outstanding_request(server, (char[]) {PqMsg_Describe, '\0'}, &ignore_packet); break; case PqMsg_ParameterDescription: case PqMsg_CopyDone: case PqMsg_CopyFail: case PqMsg_FunctionCallResponse: break; case PqMsg_EmptyQueryResponse: /* EmptyQueryResponse is similar to CommandComplete, which is handled above */ case PqMsg_PortalSuspended: pop_outstanding_request(server, (char[]) {PqMsg_Execute, '\0'}, &ignore_packet); break; /* data packets, there will be more coming */ case PqMsg_CopyData: case PqMsg_DataRow: break; } server->idle_tx = idle_tx; server->ready = ready; server->pool->stats.server_bytes += pkt->len; if (server->setting_vars) { Assert(client); sbuf_prepare_skip(sbuf, pkt->len); } else if (client) { if (client->state == CL_LOGIN) { return handle_auth_query_response(client, pkt); } else if (ignore_packet) { slog_noise(server, "not forwarding packet with type '%c' from server", pkt->type); sbuf_prepare_skip(sbuf, pkt->len); } else { sbuf_prepare_send(sbuf, &client->sbuf, pkt->len); /* * Compute query and transaction times * * For pipelined overlapping commands, we wait until * the last command is done (outstanding_requests==0). * That means, we count the time that PgBouncer is * occupied in a series of pipelined commands, not the * total time that all commands/queries take * individually. For that, we would have to track the * start time of each command separately in queue or * similar, not only per client. (which would probably * be a good future improvement) */ if (statlist_count(&server->outstanding_requests) == 0) { /* every statement (independent or in a transaction) counts as a query */ if (ready || idle_tx) { if (client->query_start) { usec_t total; total = get_cached_time() - client->query_start; client->query_start = 0; server->pool->stats.query_time += total; slog_debug(client, "query time: %d us", (int)total); } else if (!async_response) { slog_warning(client, "FIXME: query end, but query_start == 0"); Assert(false); } } /* statement ending in "idle" ends a transaction */ if (ready) { if (client->xact_start) { usec_t total; total = get_cached_time() - client->xact_start; client->xact_start = 0; server->pool->stats.xact_time += total; slog_debug(client, "transaction time: %d us", (int)total); } else if (!async_response) { /* XXX This happens during takeover if the new process * continues a transaction. */ slog_warning(client, "FIXME: transaction end, but xact_start == 0"); } } } statlist_for_each_safe(item, &server->outstanding_requests, tmp) { OutstandingRequest *request = container_of(item, OutstandingRequest, node); if (request->action != RA_FAKE) break; statlist_pop(&server->outstanding_requests); sbuf->extra_packet_queue_after = true; if (!queue_fake_response(client, request->type)) { /* * The only reason the above could have failed is because * of allocation errors. To actually be able to retry after * these failures the next round we would need to restore * the outstanding_requests queue to how it was before. * Instead of doing that, we take the easy and known * correct way out: Simply disconnecting the involved * client and server. */ disconnect_client(client, true, "out of memory"); disconnect_server(client->link, true, "out of memory"); return false; } slab_free(outstanding_request_cache, request); } } } else { if (server->state != SV_TESTED) { slog_warning(server, "got packet '%c' from server when not linked", pkt_desc(pkt)); } sbuf_prepare_skip(sbuf, pkt->len); } return true; } /* got connection, decide what to do */ static bool handle_connect(PgSocket *server) { bool res = false; PgPool *pool = server->pool; char buf[PGADDR_BUF + 32]; bool is_unix = pga_is_unix(&server->remote_addr); fill_local_addr(server, sbuf_socket(&server->sbuf), is_unix); if (cf_log_connections) { if (pga_is_unix(&server->remote_addr)) { slog_info(server, "new connection to server"); } else { slog_info(server, "new connection to server (from %s)", pga_str(&server->local_addr, buf, sizeof(buf))); } } /* * If there are cancel requests waiting we first handle those. By handling * these first we reduce the load on the server and we a server connection * might actually become free to use for queries, because its query got * canceled. * * Only if there are no cancel requests we proceed with the login procedure * that's necessary to handle queries. Cancel requests need to be sent * before the login procedure starts. * * A special case is when this is a peer pool, instead of a regular pool. * Since only cancellation requests should be sent to peers. */ if (!statlist_empty(&pool->waiting_cancel_req_list)) { slog_debug(server, "use it for pending cancel req"); if (forward_cancel_request(server)) { change_server_state(server, SV_ACTIVE_CANCEL); sbuf_continue(&server->sbuf); } else { /* notify disconnect_server() that connect did not fail */ server->ready = true; disconnect_server(server, false, "failed to send cancel req"); } } else if (pool->db->peer_id) { /* notify disconnect_server() that connect did not fail */ server->ready = true; disconnect_server(server, false, "peer server was not necessary anymore, because client cancel connection was already closed"); } else { /* proceed with login */ if (server_connect_sslmode > SSLMODE_DISABLED && !is_unix) { slog_noise(server, "P: SSL request"); res = send_sslreq_packet(server); if (res) server->wait_sslchar = true; } else { slog_noise(server, "P: startup"); res = send_startup_packet(server); } if (!res) disconnect_server(server, false, "startup pkt failed"); } return res; } static bool handle_sslchar(PgSocket *server, struct MBuf *data) { uint8_t schar = '?'; bool ok; server->wait_sslchar = false; ok = mbuf_get_byte(data, &schar); if (!ok || (schar != 'S' && schar != 'N')) { disconnect_server(server, false, "bad sslreq answer"); return false; } /* * At this point we should have no data already buffered. If * we do, it was received before we performed the SSL * handshake, so it wasn't encrypted and indeed may have been * injected by a man-in-the-middle. */ if (mbuf_avail_for_read(data) != 0) { disconnect_server(server, false, "received unencrypted data after SSL response"); return false; } if (schar == 'S') { slog_noise(server, "launching tls"); ok = sbuf_tls_connect(&server->sbuf, server->pool->db->host); } else if (server_connect_sslmode >= SSLMODE_REQUIRE) { disconnect_server(server, false, "server refused SSL"); return false; } else { /* proceed with non-TLS connection */ ok = send_startup_packet(server); } if (ok) { sbuf_prepare_skip(&server->sbuf, 1); } else { disconnect_server(server, false, "sslreq processing failed"); } return ok; } /* callback from SBuf */ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) { bool res = false; PgSocket *server = container_of(sbuf, PgSocket, sbuf); PgPool *pool = server->pool; PktHdr pkt; char infobuf[96]; Assert(is_server_socket(server)); Assert(server->state != SV_FREE); /* may happen if close failed */ if (server->state == SV_JUSTFREE) return false; switch (evtype) { case SBUF_EV_RECV_FAILED: if (server->state == SV_ACTIVE_CANCEL) disconnect_server(server, false, "successfully sent cancel request"); else disconnect_server(server, false, "server conn crashed?"); break; case SBUF_EV_SEND_FAILED: disconnect_client(server->link, false, "unexpected eof"); break; case SBUF_EV_READ: if (server->wait_sslchar) { res = handle_sslchar(server, data); break; } if (incomplete_header(data)) { slog_noise(server, "S: got partial header, trying to wait a bit"); break; } /* parse pkt header */ if (!get_header(data, &pkt)) { disconnect_server(server, true, "bad pkt header"); break; } slog_noise(server, "read pkt='%c', len=%u", pkt_desc(&pkt), pkt.len); server->request_time = get_cached_time(); switch (server->state) { case SV_LOGIN: res = handle_server_startup(server, &pkt); break; case SV_TESTED: case SV_USED: case SV_ACTIVE: case SV_ACTIVE_CANCEL: case SV_BEING_CANCELED: case SV_IDLE: res = handle_server_work(server, &pkt); break; default: fatal("server_proto: server in bad state: %d", server->state); } break; case SBUF_EV_CONNECT_FAILED: Assert(server->state == SV_LOGIN); disconnect_server(server, false, "connect failed"); break; case SBUF_EV_CONNECT_OK: slog_debug(server, "S: connect ok"); Assert(server->state == SV_LOGIN); server->request_time = get_cached_time(); res = handle_connect(server); break; case SBUF_EV_FLUSH: res = true; if (!server->ready) break; if (server->setting_vars) { PgSocket *client = server->link; Assert(client); /* * It's possible that the client vars and server vars have * different string representations, but still Postgres did not * send a ParameterStatus packet. This happens when the server * variable is the canonical version of the client variable, i.e. * they mean the same just written slightly different. To make sure * that the canonical version is also stored in the client, we now * copy the server variables over to the client variables. * See issue #776 for an example of this. */ varcache_set_canonical(server, client); server->setting_vars = false; log_noise("done setting vars unpausing client"); sbuf_continue(&client->sbuf); break; } if (connection_pool_mode(server) != POOL_SESSION || server->state == SV_TESTED || server->resetting) { server->resetting = false; switch (server->state) { case SV_ACTIVE: case SV_ACTIVE_CANCEL: case SV_TESTED: /* keep link if client expects more responses */ if (server->link) { if (statlist_count(&server->outstanding_requests) > 0) break; } /* retval does not matter here */ release_server(server); break; default: slog_warning(server, "EV_FLUSH with state=%d", server->state); case SV_BEING_CANCELED: case SV_IDLE: break; } } break; case SBUF_EV_PKT_CALLBACK: slog_warning(server, "SBUF_EV_PKT_CALLBACK with state=%d", server->state); break; case SBUF_EV_TLS_READY: Assert(server->state == SV_LOGIN); tls_get_connection_info(server->sbuf.tls, infobuf, sizeof infobuf); if (cf_log_connections) { slog_info(server, "SSL established: %s", infobuf); } else { slog_noise(server, "SSL established: %s", infobuf); } server->request_time = get_cached_time(); res = send_startup_packet(server); if (res) sbuf_continue(&server->sbuf); else disconnect_server(server, false, "TLS startup failed"); break; } if (!res && pool->db->admin) takeover_login_failed(); return res; } pgbouncer-1.24.1/src/hba.c0000644000175000000000000005220014777762222012173 00000000000000/* * Host-Based-Access-control file support. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" #include #include #include #include #include /* * StrSet */ struct StrSetNode { unsigned int s_len; char s_val[FLEX_ARRAY]; }; struct StrSet { CxMem *pool; unsigned count; unsigned alloc; struct StrSetNode **nodes; struct CBTree *cbtree; }; struct StrSet *strset_new(CxMem *cx); void strset_free(struct StrSet *set); bool strset_add(struct StrSet *set, const char *str, unsigned int len); bool strset_contains(struct StrSet *set, const char *str, unsigned int len); struct StrSet *strset_new(CxMem *cx) { struct StrSet *set; CxMem *pool; pool = cx_new_pool(cx, 1024, 0); if (!pool) return NULL; set = cx_alloc(pool, sizeof *set); if (!set) return NULL; set->pool = pool; set->cbtree = NULL; set->count = 0; set->alloc = 10; set->nodes = cx_alloc0(pool, set->alloc * sizeof(struct StrSet *)); if (!set->nodes) { cx_destroy(pool); return NULL; } return set; } static size_t strset_node_key(void *ctx, void *obj, const void **ptr_p) { struct StrSetNode *node = obj; *ptr_p = node->s_val; return node->s_len; } bool strset_add(struct StrSet *set, const char *str, unsigned int len) { struct StrSetNode *node; unsigned int i; bool ok; if (strset_contains(set, str, len)) return true; node = cx_alloc(set->pool, offsetof(struct StrSetNode, s_val) + len + 1); if (!node) return false; node->s_len = len; memcpy(node->s_val, str, len); node->s_val[len] = 0; if (set->count < set->alloc) { set->nodes[set->count++] = node; return true; } if (!set->cbtree) { set->cbtree = cbtree_create(strset_node_key, NULL, set, set->pool); if (!set->cbtree) return false; for (i = 0; i < set->count; i++) { ok = cbtree_insert(set->cbtree, set->nodes[i]); if (!ok) return false; } } ok = cbtree_insert(set->cbtree, node); if (!ok) return false; set->count++; return true; } bool strset_contains(struct StrSet *set, const char *str, unsigned int len) { unsigned int i; struct StrSetNode *node; if (set->cbtree) return cbtree_lookup(set->cbtree, str, len) != NULL; for (i = 0; i < set->count; i++) { node = set->nodes[i]; if (node->s_len != len) continue; if (memcmp(node->s_val, str, len) == 0) return true; } return false; } void strset_free(struct StrSet *set) { if (set) cx_destroy(set->pool); } /* * Parse HBA tokens. */ enum TokType { TOK_STRING, TOK_IDENT, TOK_COMMA, TOK_FAIL, TOK_EOL }; struct TokParser { const char *pos; enum TokType cur_tok; char *cur_tok_str; char *buf; size_t buflen; }; static bool tok_buf_check(struct TokParser *p, size_t len) { size_t tmplen; char *tmp; if (p->buflen >= len) return true; tmplen = len*2; tmp = realloc(p->buf, tmplen); if (!tmp) return false; p->buf = tmp; p->buflen = tmplen; return true; } static enum TokType next_token(struct TokParser *p) { const char *s, *s2; char *dst; if (p->cur_tok == TOK_EOL) return TOK_EOL; p->cur_tok_str = NULL; p->cur_tok = TOK_FAIL; while (p->pos[0] && isspace((unsigned char)p->pos[0])) p->pos++; if (p->pos[0] == '#' || p->pos[0] == '\0') { p->cur_tok = TOK_EOL; p->pos = NULL; } else if (p->pos[0] == ',') { p->cur_tok = TOK_COMMA; p->pos++; } else if (p->pos[0] == '"') { for (s = p->pos + 1; s[0]; s++) { if (s[0] == '"') { if (s[1] == '"') s++; else break; } } if (s[0] != '"' || !tok_buf_check(p, s - p->pos)) return TOK_FAIL; dst = p->buf; for (s2 = p->pos + 1; s2 < s; s2++) { *dst++ = *s2; if (*s2 == '"') s2++; } *dst = 0; p->pos = s + 1; p->cur_tok = TOK_STRING; p->cur_tok_str = p->buf; } else { for (s = p->pos + 1; *s; s++) { if (*s == ',' || *s == '#' || *s == '"') break; if (isspace((unsigned char)*s)) break; } if (!tok_buf_check(p, s - p->pos + 1)) return TOK_FAIL; memcpy(p->buf, p->pos, s - p->pos); p->buf[s - p->pos] = 0; p->pos = s; p->cur_tok = TOK_IDENT; p->cur_tok_str = p->buf; } return p->cur_tok; } static bool eat(struct TokParser *p, enum TokType ttype) { if (p->cur_tok == ttype) { next_token(p); return true; } return false; } static bool eat_kw(struct TokParser *p, const char *kw) { if (p->cur_tok == TOK_IDENT && strcmp(kw, p->cur_tok_str) == 0) { next_token(p); return true; } return false; } static bool expect(struct TokParser *tp, enum TokType ttype, const char **str_p) { if (tp->cur_tok == ttype) { *str_p = tp->buf; return true; } return false; } static char *path_join(const char *p1, const char *p2) { size_t len1, len2; char *res = NULL, *pos; if (p2[0] == '/' || p1[0] == 0 || !memcmp(p1, ".", 2)) return strdup(p2); len1 = strlen(p1); len2 = strlen(p2); res = malloc(len1 + len2 + 2 + 1); if (res) { memcpy(res, p1, len1); pos = res + len1; if (pos[-1] != '/') *pos++ = '/'; memcpy(pos, p2, len2 + 1); } return res; } static char *path_join_dirname(const char *parent, const char *fn) { char *tmp, *res; const char *basedir; if (fn[0] == '/') return strdup(fn); tmp = strdup(parent); if (!tmp) return NULL; basedir = dirname(tmp); res = path_join(basedir, fn); free(tmp); return res; } static void init_parser(struct TokParser *p) { memset(p, 0, sizeof(*p)); } static void parse_from_string(struct TokParser *p, const char *str) { p->pos = str; p->cur_tok = TOK_COMMA; p->cur_tok_str = NULL; next_token(p); } static void free_parser(struct TokParser *p) { free(p->buf); p->buf = NULL; } static bool parse_names(struct HBAName *hname, struct TokParser *p, bool is_db, const char *parent_filename); static bool parse_ident_name(const char **ident_name, struct TokParser *tp, bool *is_name_all); static bool parse_namefile(struct HBAName *hname, const char *fn, bool is_db) { FILE *f; ssize_t len; char *ln = NULL; size_t buflen = 0; bool ok = false; struct TokParser tp; init_parser(&tp); f = fopen(fn, "r"); if (!f) { return false; } for (;;) { len = getline(&ln, &buflen, f); if (len < 0) { ok = true; break; } parse_from_string(&tp, ln); if (!parse_names(hname, &tp, is_db, fn)) break; } free_parser(&tp); free(ln); fclose(f); return ok; } static bool parse_ident_name(const char **ident_name, struct TokParser *tp, bool *is_name_all) { if (eat_kw(tp, "all")) { *is_name_all = true; return true; } if (!expect(tp, TOK_IDENT, ident_name)) { if (!expect(tp, TOK_STRING, ident_name)) { return false; } } return true; } static bool parse_names(struct HBAName *hname, struct TokParser *tp, bool is_db, const char *parent_filename) { const char *tok; while (1) { if (eat_kw(tp, "all")) { hname->flags |= NAME_ALL; goto eat_comma; } if (is_db) { if (eat_kw(tp, "sameuser")) { hname->flags |= NAME_SAMEUSER; goto eat_comma; } if (eat_kw(tp, "samerole")) { log_warning("samerole is not supported"); return false; } if (eat_kw(tp, "samegroup")) { log_warning("samegroup is not supported"); return false; } if (eat_kw(tp, "replication")) { hname->flags |= NAME_REPLICATION; goto eat_comma; } } if (expect(tp, TOK_IDENT, &tok)) { if (tok[0] == '+') { return false; } if (tok[0] == '@') { bool ok; char *fn; fn = path_join_dirname(parent_filename, tok + 1); if (!fn) return false; ok = parse_namefile(hname, fn, is_db); free(fn); if (!ok) return false; next_token(tp); goto eat_comma; } /* fallthrough */ } else if (expect(tp, TOK_STRING, &tok)) { /* fallthrough */ } else { return false; } /* * TOK_IDENT or TOK_STRING as plain name. */ if (!hname->name_set) { hname->name_set = strset_new(NULL); if (!hname->name_set) return false; } if (!strset_add(hname->name_set, tok, strlen(tok))) return false; next_token(tp); eat_comma: if (!eat(tp, TOK_COMMA)) break; } return true; } static void rule_free(struct HBARule *rule) { strset_free(rule->db_name.name_set); strset_free(rule->user_name.name_set); free(rule); } static bool parse_addr(struct HBAAddress *haddress, const char *addr) { if (inet_pton(AF_INET6, addr, haddress->addr)) { haddress->family = AF_INET6; } else if (inet_pton(AF_INET, addr, haddress->addr)) { haddress->family = AF_INET; } else { return false; } return true; } static bool parse_nmask(struct HBAAddress *haddress, const char *nmask) { char *end = NULL; unsigned long bits; unsigned int i; errno = 0; bits = strtoul(nmask, &end, 10); if (errno || *end) { return false; } if (haddress->family == AF_INET && bits > 32) { return false; } if (haddress->family == AF_INET6 && bits > 128) { return false; } for (i = 0; i < bits/8; i++) haddress->mask[i] = 255; if (bits % 8) haddress->mask[i] = 255 << (8 - (bits % 8)); return true; } static bool bad_mask(struct HBAAddress *haddress) { int i, bytes = haddress->family == AF_INET ? 4 : 16; uint8_t res = 0; for (i = 0; i < bytes; i++) res |= haddress->addr[i] & (255 ^ haddress->mask[i]); return !!res; } static bool match_map(struct HBARule *rule, struct Ident *ident, const char *mapname) { struct List *el; struct IdentMap *map; if (!ident) return false; list_for_each(el, &ident->maps) { map = container_of(el, struct IdentMap, node); if (strcmp(map->map_name, mapname) == 0) { rule->identmap = map; return true; } } return false; } static bool parse_map_definition(struct HBARule *rule, struct Ident *ident, struct TokParser *tp, int linenr) { const char *str; char *val; if (!expect(tp, TOK_IDENT, &str)) return true; val = strchr(str, '='); if (val == NULL || strncmp(str, "map=", 4) != 0) { log_warning("hba line %d: Ident map %s is malformed. It is not in map=value format.", linenr, str); return false; } val++; next_token(tp); if (!match_map(rule, ident, val)) { log_warning("hba line %d: Ident map %s is not found in ident config file", linenr, val); return false; } return true; } static void mapping_free(struct Mapping *mapping) { free(mapping->system_user_name); free(mapping->postgres_user_name); free(mapping); } static void ident_map_free(struct IdentMap *ident_map) { struct List *el, *tmp; struct Mapping *mapping; if (!ident_map) return; list_for_each_safe(el, &ident_map->mappings, tmp) { mapping = container_of(el, struct Mapping, node); list_del(&mapping->node); mapping_free(mapping); } free(ident_map->map_name); free(ident_map); } static bool find_ident_map(struct Ident *ident, const char *mapname, struct IdentMap **ident_map) { struct List *el; list_for_each(el, &ident->maps) { *ident_map = container_of(el, struct IdentMap, node); if (!strcmp((*ident_map)->map_name, mapname)) return true; } return false; } static bool parse_ident_line(struct Ident *ident, struct TokParser *tp, int linenr) { const char *map_name = NULL; char *map_name_copy = NULL; const char *system_user_name = NULL; const char *postgres_user_name = NULL; struct IdentMap *ident_map = NULL; struct Mapping *mapping = NULL; bool is_name_all = false; if (eat(tp, TOK_EOL)) { return true; } mapping = calloc(1, sizeof(*mapping)); if (!mapping) { log_warning("ident: no mem for parsing mapping"); return false; } if (!expect(tp, TOK_IDENT, &map_name)) { goto failed; } map_name_copy = strdup(map_name); if (!map_name_copy) { log_warning("ident: no mem for map_name"); goto failed; } next_token(tp); if (!expect(tp, TOK_IDENT, &system_user_name)) { if (!expect(tp, TOK_STRING, &system_user_name)) goto failed; } mapping->system_user_name = strdup(system_user_name); if (!mapping->system_user_name) { log_warning("ident: no mem for system_user_name"); goto failed; } next_token(tp); if (!parse_ident_name(&postgres_user_name, tp, &is_name_all)) { goto failed; } if (is_name_all) { mapping->name_flags |= NAME_ALL; } else { mapping->postgres_user_name = strdup(postgres_user_name); if (!mapping->postgres_user_name) { log_warning("ident: no mem for postgres_user_name"); goto failed; } } next_token(tp); if (!eat(tp, TOK_EOL)) { log_warning("ident line %d: unsupported parameters", linenr); goto failed; } if (find_ident_map(ident, map_name_copy, &ident_map)) { list_append(&ident_map->mappings, &mapping->node); free(map_name_copy); map_name_copy = NULL; } else { ident_map = calloc(1, sizeof(*ident_map)); if (!ident_map) { log_warning("ident: no mem for parsing ident_map"); goto failed; } ident_map->map_name = map_name_copy; map_name_copy = NULL; list_init(&ident_map->mappings); list_append(&ident_map->mappings, &mapping->node); list_append(&ident->maps, &ident_map->node); } return true; failed: mapping_free(mapping); ident_map_free(ident_map); free(map_name_copy); return false; } static bool parse_line(struct HBA *hba, struct Ident *ident, struct TokParser *tp, int linenr, const char *parent_filename) { const char *addr = NULL, *mask = NULL; enum RuleType rtype; char *nmask = NULL; struct HBARule *rule = NULL; if (eat_kw(tp, "local")) { rtype = RULE_LOCAL; } else if (eat_kw(tp, "host")) { rtype = RULE_HOST; } else if (eat_kw(tp, "hostssl")) { rtype = RULE_HOSTSSL; } else if (eat_kw(tp, "hostnossl")) { rtype = RULE_HOSTNOSSL; } else if (eat(tp, TOK_EOL)) { return true; } else { log_warning("hba line %d: unknown type", linenr); return false; } rule = calloc(1, sizeof(*rule)); if (!rule) { log_warning("hba: no mem for rule"); return false; } rule->rule_type = rtype; if (!parse_names(&rule->db_name, tp, true, parent_filename)) goto failed; if (!parse_names(&rule->user_name, tp, true, parent_filename)) goto failed; if (rtype == RULE_LOCAL) { rule->address.family = AF_UNIX; } else if (eat_kw(tp, "all")) { rule->address.flags |= ADDRESS_ALL; } else { if (!expect(tp, TOK_IDENT, &addr)) { log_warning("hba line %d: did not find address - %d - '%s'", linenr, tp->cur_tok, tp->buf); goto failed; } nmask = strchr(addr, '/'); if (nmask) { *nmask++ = 0; } if (!parse_addr(&rule->address, addr)) { log_warning("hba line %d: failed to parse address - %s", linenr, addr); goto failed; } if (nmask) { if (!parse_nmask(&rule->address, nmask)) { log_warning("hba line %d: invalid mask", linenr); goto failed; } next_token(tp); } else { next_token(tp); if (!expect(tp, TOK_IDENT, &mask)) { log_warning("hba line %d: did not find mask", linenr); goto failed; } if (!inet_pton(rule->address.family, mask, rule->address.mask)) { log_warning("hba line %d: failed to parse mask: %s", linenr, mask); goto failed; } next_token(tp); } if (bad_mask(&rule->address)) { char buf1[128], buf2[128]; log_warning("address does not match mask in %s line #%d: %s / %s", parent_filename, linenr, inet_ntop(rule->address.family, rule->address.addr, buf1, sizeof buf1), inet_ntop(rule->address.family, rule->address.mask, buf2, sizeof buf2)); } } if (eat_kw(tp, "trust")) { rule->rule_method = AUTH_TYPE_TRUST; } else if (eat_kw(tp, "reject")) { rule->rule_method = AUTH_TYPE_REJECT; } else if (eat_kw(tp, "md5")) { rule->rule_method = AUTH_TYPE_MD5; } else if (eat_kw(tp, "password")) { rule->rule_method = AUTH_TYPE_PLAIN; } else if (eat_kw(tp, "peer")) { rule->rule_method = AUTH_TYPE_PEER; } else if (eat_kw(tp, "cert")) { rule->rule_method = AUTH_TYPE_CERT; } else if (eat_kw(tp, "scram-sha-256")) { rule->rule_method = AUTH_TYPE_SCRAM_SHA_256; } else { log_warning("hba line %d: unsupported method: buf=%s", linenr, tp->buf); goto failed; } if (!parse_map_definition(rule, ident, tp, linenr)) { goto failed; } if (!eat(tp, TOK_EOL)) { log_warning("hba line %d: unsupported parameters", linenr); goto failed; } rule->hba_linenr = linenr; list_append(&hba->rules, &rule->node); return true; failed: rule_free(rule); return false; } struct Ident *ident_load_map(const char *fn) { struct Ident *ident = NULL; FILE *f = NULL; char *ln = NULL; size_t lnbuf = 0; ssize_t len; int linenr; struct TokParser tp; if (fn == NULL) return NULL; init_parser(&tp); ident = malloc(sizeof *ident); if (!ident) goto out; list_init(&ident->maps); f = fopen(fn, "r"); if (!f) { log_error("could not open ident config file %s: %s", fn, strerror(errno)); goto out; } for (linenr = 1; ; linenr++) { len = getline(&ln, &lnbuf, f); if (len < 0) break; parse_from_string(&tp, ln); if (!parse_ident_line(ident, &tp, linenr)) { /* Tell the admin where to look for the problem. */ log_warning("could not parse ident config line %d", linenr); /* Ignore line, but parse to the end. */ continue; } } out: free_parser(&tp); free(ln); if (f) fclose(f); return ident; } struct HBA *hba_load_rules(const char *fn, struct Ident *ident) { struct HBA *hba = NULL; FILE *f = NULL; char *ln = NULL; size_t lnbuf = 0; ssize_t len; int linenr; struct TokParser tp; init_parser(&tp); hba = malloc(sizeof *hba); if (!hba) goto out; list_init(&hba->rules); f = fopen(fn, "r"); if (!f) { log_error("could not open hba config file %s: %s", fn, strerror(errno)); goto out; } for (linenr = 1; ; linenr++) { len = getline(&ln, &lnbuf, f); if (len < 0) break; parse_from_string(&tp, ln); if (!parse_line(hba, ident, &tp, linenr, fn)) { /* Tell the admin where to look for the problem. */ log_warning("could not parse hba config line %d", linenr); /* Ignore line, but parse to the end. */ continue; } } out: free_parser(&tp); free(ln); if (f) fclose(f); return hba; } void ident_free(struct Ident *ident) { struct List *el, *tmp; struct IdentMap *map; if (!ident) return; list_for_each_safe(el, &ident->maps, tmp) { map = container_of(el, struct IdentMap, node); list_del(&map->node); ident_map_free(map); } free(ident); } void hba_free(struct HBA *hba) { struct List *el, *tmp; struct HBARule *rule; if (!hba) return; list_for_each_safe(el, &hba->rules, tmp) { rule = container_of(el, struct HBARule, node); list_del(&rule->node); rule_free(rule); } free(hba); } static bool name_match(struct HBAName *hname, const char *name, unsigned int namelen, const char *pair) { if (hname->flags & NAME_ALL) return true; if ((hname->flags & NAME_SAMEUSER) && strcmp(name, pair) == 0) return true; if (hname->name_set) return strset_contains(hname->name_set, name, namelen); return false; } static bool match_inet4(const struct HBAAddress *haddress, PgAddr *addr) { const uint32_t *src, *base, *mask; if (pga_family(addr) != AF_INET) return false; src = (uint32_t *)&addr->sin.sin_addr.s_addr; base = (uint32_t *)haddress->addr; mask = (uint32_t *)haddress->mask; return (src[0] & mask[0]) == base[0]; } static bool match_inet6(const struct HBAAddress *haddress, PgAddr *addr) { const uint32_t *src, *base, *mask; if (pga_family(addr) != AF_INET6) return false; src = (uint32_t *)addr->sin6.sin6_addr.s6_addr; base = (uint32_t *)haddress->addr; mask = (uint32_t *)haddress->mask; return (src[0] & mask[0]) == base[0] && (src[1] & mask[1]) == base[1] && (src[2] & mask[2]) == base[2] && (src[3] & mask[3]) == base[3]; } static bool address_match(const struct HBAAddress *haddress, PgAddr *addr) { if (haddress->flags & ADDRESS_ALL) return true; switch (haddress->family) { case AF_INET: return match_inet4(haddress, addr); case AF_INET6: return match_inet6(haddress, addr); default: return false; } } struct HBARule * hba_eval(struct HBA *hba, PgAddr *addr, bool is_tls, ReplicationType replication, const char *dbname, const char *username) { struct List *el; struct HBARule *rule; unsigned int dbnamelen = strlen(dbname); unsigned int unamelen = strlen(username); if (!hba) return NULL; list_for_each(el, &hba->rules) { rule = container_of(el, struct HBARule, node); /* match address */ if (pga_is_unix(addr)) { if (rule->rule_type != RULE_LOCAL) continue; } else if (rule->rule_type == RULE_LOCAL) { continue; } else if (rule->rule_type == RULE_HOSTSSL && !is_tls) { continue; } else if (rule->rule_type == RULE_HOSTNOSSL && is_tls) { continue; } else if (!address_match(&rule->address, addr)) { continue; } /* match db & user */ if (replication == REPLICATION_PHYSICAL) { if (!(rule->db_name.flags & NAME_REPLICATION)) { continue; } } else { if (!name_match(&rule->db_name, dbname, dbnamelen, username)) continue; } if (!name_match(&rule->user_name, username, unamelen, dbname)) continue; /* rule matches */ return rule; } return NULL; } pgbouncer-1.24.1/src/common/0000755000000000000000000000000014777762567012645 500000000000000pgbouncer-1.24.1/src/common/wchar.c0000644000175000000000000010262514777762222014044 00000000000000/*------------------------------------------------------------------------- * * wchar.c * Functions for working with multibyte characters in various encodings. * * Portions Copyright (c) 1998-2020, PostgreSQL Global Development Group * * IDENTIFICATION * src/common/wchar.c * *------------------------------------------------------------------------- */ //#include "c.h" #include "system.h" #include "common/postgres_compat.h" #include "common/pg_wchar.h" /* * Operations on multi-byte encodings are driven by a table of helper * functions. * * To add an encoding support, define mblen(), dsplen() and verifier() for * the encoding. For server-encodings, also define mb2wchar() and wchar2mb() * conversion functions. * * These functions generally assume that their input is validly formed. * The "verifier" functions, further down in the file, have to be more * paranoid. * * We expect that mblen() does not need to examine more than the first byte * of the character to discover the correct length. GB18030 is an exception * to that rule, though, as it also looks at second byte. But even that * behaves in a predictable way, if you only pass the first byte: it will * treat 4-byte encoded characters as two 2-byte encoded characters, which is * good enough for all current uses. * * Note: for the display output of psql to work properly, the return values * of the dsplen functions must conform to the Unicode standard. In particular * the NUL character is zero width and control characters are generally * width -1. It is recommended that non-ASCII encodings refer their ASCII * subset to the ASCII routines to ensure consistency. */ /* * SQL/ASCII */ static int pg_ascii2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; while (len > 0 && *from) { *to++ = *from++; len--; cnt++; } *to = 0; return cnt; } static int pg_ascii_mblen(const unsigned char *s) { return 1; } static int pg_ascii_dsplen(const unsigned char *s) { if (*s == '\0') return 0; if (*s < 0x20 || *s == 0x7f) return -1; return 1; } /* * EUC */ static int pg_euc2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; while (len > 0 && *from) { if (*from == SS2 && len >= 2) /* JIS X 0201 (so called "1 byte * KANA") */ { from++; *to = (SS2 << 8) | *from++; len -= 2; } else if (*from == SS3 && len >= 3) /* JIS X 0212 KANJI */ { from++; *to = (SS3 << 16) | (*from++ << 8); *to |= *from++; len -= 3; } else if (IS_HIGHBIT_SET(*from) && len >= 2) /* JIS X 0208 KANJI */ { *to = *from++ << 8; *to |= *from++; len -= 2; } else /* must be ASCII */ { *to = *from++; len--; } to++; cnt++; } *to = 0; return cnt; } static inline int pg_euc_mblen(const unsigned char *s) { int len; if (*s == SS2) len = 2; else if (*s == SS3) len = 3; else if (IS_HIGHBIT_SET(*s)) len = 2; else len = 1; return len; } static inline int pg_euc_dsplen(const unsigned char *s) { int len; if (*s == SS2) len = 2; else if (*s == SS3) len = 2; else if (IS_HIGHBIT_SET(*s)) len = 2; else len = pg_ascii_dsplen(s); return len; } /* * EUC_JP */ static int pg_eucjp2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { return pg_euc2wchar_with_len(from, to, len); } static int pg_eucjp_mblen(const unsigned char *s) { return pg_euc_mblen(s); } static int pg_eucjp_dsplen(const unsigned char *s) { int len; if (*s == SS2) len = 1; else if (*s == SS3) len = 2; else if (IS_HIGHBIT_SET(*s)) len = 2; else len = pg_ascii_dsplen(s); return len; } /* * EUC_KR */ static int pg_euckr2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { return pg_euc2wchar_with_len(from, to, len); } static int pg_euckr_mblen(const unsigned char *s) { return pg_euc_mblen(s); } static int pg_euckr_dsplen(const unsigned char *s) { return pg_euc_dsplen(s); } /* * EUC_CN * */ static int pg_euccn2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; while (len > 0 && *from) { if (*from == SS2 && len >= 3) /* code set 2 (unused?) */ { from++; *to = (SS2 << 16) | (*from++ << 8); *to |= *from++; len -= 3; } else if (*from == SS3 && len >= 3) /* code set 3 (unused ?) */ { from++; *to = (SS3 << 16) | (*from++ << 8); *to |= *from++; len -= 3; } else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 1 */ { *to = *from++ << 8; *to |= *from++; len -= 2; } else { *to = *from++; len--; } to++; cnt++; } *to = 0; return cnt; } static int pg_euccn_mblen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; else len = 1; return len; } static int pg_euccn_dsplen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; else len = pg_ascii_dsplen(s); return len; } /* * EUC_TW * */ static int pg_euctw2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; while (len > 0 && *from) { if (*from == SS2 && len >= 4) /* code set 2 */ { from++; *to = (((uint32) SS2) << 24) | (*from++ << 16); *to |= *from++ << 8; *to |= *from++; len -= 4; } else if (*from == SS3 && len >= 3) /* code set 3 (unused?) */ { from++; *to = (SS3 << 16) | (*from++ << 8); *to |= *from++; len -= 3; } else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 2 */ { *to = *from++ << 8; *to |= *from++; len -= 2; } else { *to = *from++; len--; } to++; cnt++; } *to = 0; return cnt; } static int pg_euctw_mblen(const unsigned char *s) { int len; if (*s == SS2) len = 4; else if (*s == SS3) len = 3; else if (IS_HIGHBIT_SET(*s)) len = 2; else len = 1; return len; } static int pg_euctw_dsplen(const unsigned char *s) { int len; if (*s == SS2) len = 2; else if (*s == SS3) len = 2; else if (IS_HIGHBIT_SET(*s)) len = 2; else len = pg_ascii_dsplen(s); return len; } /* * Convert pg_wchar to EUC_* encoding. * caller must allocate enough space for "to", including a trailing zero! * len: length of from. * "from" not necessarily null terminated. */ static int pg_wchar2euc_with_len(const pg_wchar *from, unsigned char *to, int len) { int cnt = 0; while (len > 0 && *from) { unsigned char c; if ((c = (*from >> 24))) { *to++ = c; *to++ = (*from >> 16) & 0xff; *to++ = (*from >> 8) & 0xff; *to++ = *from & 0xff; cnt += 4; } else if ((c = (*from >> 16))) { *to++ = c; *to++ = (*from >> 8) & 0xff; *to++ = *from & 0xff; cnt += 3; } else if ((c = (*from >> 8))) { *to++ = c; *to++ = *from & 0xff; cnt += 2; } else { *to++ = *from; cnt++; } from++; len--; } *to = 0; return cnt; } /* * JOHAB */ static int pg_johab_mblen(const unsigned char *s) { return pg_euc_mblen(s); } static int pg_johab_dsplen(const unsigned char *s) { return pg_euc_dsplen(s); } /* * convert UTF8 string to pg_wchar (UCS-4) * caller must allocate enough space for "to", including a trailing zero! * len: length of from. * "from" not necessarily null terminated. */ static int pg_utf2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; uint32 c1, c2, c3, c4; while (len > 0 && *from) { if ((*from & 0x80) == 0) { *to = *from++; len--; } else if ((*from & 0xe0) == 0xc0) { if (len < 2) break; /* drop trailing incomplete char */ c1 = *from++ & 0x1f; c2 = *from++ & 0x3f; *to = (c1 << 6) | c2; len -= 2; } else if ((*from & 0xf0) == 0xe0) { if (len < 3) break; /* drop trailing incomplete char */ c1 = *from++ & 0x0f; c2 = *from++ & 0x3f; c3 = *from++ & 0x3f; *to = (c1 << 12) | (c2 << 6) | c3; len -= 3; } else if ((*from & 0xf8) == 0xf0) { if (len < 4) break; /* drop trailing incomplete char */ c1 = *from++ & 0x07; c2 = *from++ & 0x3f; c3 = *from++ & 0x3f; c4 = *from++ & 0x3f; *to = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4; len -= 4; } else { /* treat a bogus char as length 1; not ours to raise error */ *to = *from++; len--; } to++; cnt++; } *to = 0; return cnt; } /* * Map a Unicode code point to UTF-8. utf8string must have 4 bytes of * space allocated. */ unsigned char * unicode_to_utf8(pg_wchar c, unsigned char *utf8string) { if (c <= 0x7F) { utf8string[0] = c; } else if (c <= 0x7FF) { utf8string[0] = 0xC0 | ((c >> 6) & 0x1F); utf8string[1] = 0x80 | (c & 0x3F); } else if (c <= 0xFFFF) { utf8string[0] = 0xE0 | ((c >> 12) & 0x0F); utf8string[1] = 0x80 | ((c >> 6) & 0x3F); utf8string[2] = 0x80 | (c & 0x3F); } else { utf8string[0] = 0xF0 | ((c >> 18) & 0x07); utf8string[1] = 0x80 | ((c >> 12) & 0x3F); utf8string[2] = 0x80 | ((c >> 6) & 0x3F); utf8string[3] = 0x80 | (c & 0x3F); } return utf8string; } /* * Trivial conversion from pg_wchar to UTF-8. * caller should allocate enough space for "to" * len: length of from. * "from" not necessarily null terminated. */ static int pg_wchar2utf_with_len(const pg_wchar *from, unsigned char *to, int len) { int cnt = 0; while (len > 0 && *from) { int char_len; unicode_to_utf8(*from, to); char_len = pg_utf_mblen(to); cnt += char_len; to += char_len; from++; len--; } *to = 0; return cnt; } /* * Return the byte length of a UTF8 character pointed to by s * * Note: in the current implementation we do not support UTF8 sequences * of more than 4 bytes; hence do NOT return a value larger than 4. * We return "1" for any leading byte that is either flat-out illegal or * indicates a length larger than we support. * * pg_utf2wchar_with_len(), utf8_to_unicode(), pg_utf8_islegal(), and perhaps * other places would need to be fixed to change this. */ int pg_utf_mblen(const unsigned char *s) { int len; if ((*s & 0x80) == 0) len = 1; else if ((*s & 0xe0) == 0xc0) len = 2; else if ((*s & 0xf0) == 0xe0) len = 3; else if ((*s & 0xf8) == 0xf0) len = 4; #ifdef NOT_USED else if ((*s & 0xfc) == 0xf8) len = 5; else if ((*s & 0xfe) == 0xfc) len = 6; #endif else len = 1; return len; } /* * This is an implementation of wcwidth() and wcswidth() as defined in * "The Single UNIX Specification, Version 2, The Open Group, 1997" * * * Markus Kuhn -- 2001-09-08 -- public domain * * customised for PostgreSQL * * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ struct mbinterval { unsigned short first; unsigned short last; }; /* auxiliary function for binary search in interval table */ static int mbbisearch(pg_wchar ucs, const struct mbinterval *table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } /* The following functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * FullWidth (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ static int ucs_wcwidth(pg_wchar ucs) { #include "common/unicode_combining_table.h" /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 0x20 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff) return -1; /* binary search in table of non-spacing characters */ if (mbbisearch(ucs, combining, sizeof(combining) / sizeof(struct mbinterval) - 1)) return 0; /* * if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility * Ideographs */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2ffff))); } /* * Convert a UTF-8 character to a Unicode code point. * This is a one-character version of pg_utf2wchar_with_len. * * No error checks here, c must point to a long-enough string. */ pg_wchar utf8_to_unicode(const unsigned char *c) { if ((*c & 0x80) == 0) return (pg_wchar) c[0]; else if ((*c & 0xe0) == 0xc0) return (pg_wchar) (((c[0] & 0x1f) << 6) | (c[1] & 0x3f)); else if ((*c & 0xf0) == 0xe0) return (pg_wchar) (((c[0] & 0x0f) << 12) | ((c[1] & 0x3f) << 6) | (c[2] & 0x3f)); else if ((*c & 0xf8) == 0xf0) return (pg_wchar) (((c[0] & 0x07) << 18) | ((c[1] & 0x3f) << 12) | ((c[2] & 0x3f) << 6) | (c[3] & 0x3f)); else /* that is an invalid code on purpose */ return 0xffffffff; } static int pg_utf_dsplen(const unsigned char *s) { return ucs_wcwidth(utf8_to_unicode(s)); } /* * convert mule internal code to pg_wchar * caller should allocate enough space for "to" * len: length of from. * "from" not necessarily null terminated. */ static int pg_mule2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; while (len > 0 && *from) { if (IS_LC1(*from) && len >= 2) { *to = *from++ << 16; *to |= *from++; len -= 2; } else if (IS_LCPRV1(*from) && len >= 3) { from++; *to = *from++ << 16; *to |= *from++; len -= 3; } else if (IS_LC2(*from) && len >= 3) { *to = *from++ << 16; *to |= *from++ << 8; *to |= *from++; len -= 3; } else if (IS_LCPRV2(*from) && len >= 4) { from++; *to = *from++ << 16; *to |= *from++ << 8; *to |= *from++; len -= 4; } else { /* assume ASCII */ *to = (unsigned char) *from++; len--; } to++; cnt++; } *to = 0; return cnt; } /* * convert pg_wchar to mule internal code * caller should allocate enough space for "to" * len: length of from. * "from" not necessarily null terminated. */ static int pg_wchar2mule_with_len(const pg_wchar *from, unsigned char *to, int len) { int cnt = 0; while (len > 0 && *from) { unsigned char lb; lb = (*from >> 16) & 0xff; if (IS_LC1(lb)) { *to++ = lb; *to++ = *from & 0xff; cnt += 2; } else if (IS_LC2(lb)) { *to++ = lb; *to++ = (*from >> 8) & 0xff; *to++ = *from & 0xff; cnt += 3; } else if (IS_LCPRV1_A_RANGE(lb)) { *to++ = LCPRV1_A; *to++ = lb; *to++ = *from & 0xff; cnt += 3; } else if (IS_LCPRV1_B_RANGE(lb)) { *to++ = LCPRV1_B; *to++ = lb; *to++ = *from & 0xff; cnt += 3; } else if (IS_LCPRV2_A_RANGE(lb)) { *to++ = LCPRV2_A; *to++ = lb; *to++ = (*from >> 8) & 0xff; *to++ = *from & 0xff; cnt += 4; } else if (IS_LCPRV2_B_RANGE(lb)) { *to++ = LCPRV2_B; *to++ = lb; *to++ = (*from >> 8) & 0xff; *to++ = *from & 0xff; cnt += 4; } else { *to++ = *from & 0xff; cnt += 1; } from++; len--; } *to = 0; return cnt; } /* exported for direct use by conv.c */ int pg_mule_mblen(const unsigned char *s) { int len; if (IS_LC1(*s)) len = 2; else if (IS_LCPRV1(*s)) len = 3; else if (IS_LC2(*s)) len = 3; else if (IS_LCPRV2(*s)) len = 4; else len = 1; /* assume ASCII */ return len; } static int pg_mule_dsplen(const unsigned char *s) { int len; /* * Note: it's not really appropriate to assume that all multibyte charsets * are double-wide on screen. But this seems an okay approximation for * the MULE charsets we currently support. */ if (IS_LC1(*s)) len = 1; else if (IS_LCPRV1(*s)) len = 1; else if (IS_LC2(*s)) len = 2; else if (IS_LCPRV2(*s)) len = 2; else len = 1; /* assume ASCII */ return len; } /* * ISO8859-1 */ static int pg_latin12wchar_with_len(const unsigned char *from, pg_wchar *to, int len) { int cnt = 0; while (len > 0 && *from) { *to++ = *from++; len--; cnt++; } *to = 0; return cnt; } /* * Trivial conversion from pg_wchar to single byte encoding. Just ignores * high bits. * caller should allocate enough space for "to" * len: length of from. * "from" not necessarily null terminated. */ static int pg_wchar2single_with_len(const pg_wchar *from, unsigned char *to, int len) { int cnt = 0; while (len > 0 && *from) { *to++ = *from++; len--; cnt++; } *to = 0; return cnt; } static int pg_latin1_mblen(const unsigned char *s) { return 1; } static int pg_latin1_dsplen(const unsigned char *s) { return pg_ascii_dsplen(s); } /* * SJIS */ static int pg_sjis_mblen(const unsigned char *s) { int len; if (*s >= 0xa1 && *s <= 0xdf) len = 1; /* 1 byte kana? */ else if (IS_HIGHBIT_SET(*s)) len = 2; /* kanji? */ else len = 1; /* should be ASCII */ return len; } static int pg_sjis_dsplen(const unsigned char *s) { int len; if (*s >= 0xa1 && *s <= 0xdf) len = 1; /* 1 byte kana? */ else if (IS_HIGHBIT_SET(*s)) len = 2; /* kanji? */ else len = pg_ascii_dsplen(s); /* should be ASCII */ return len; } /* * Big5 */ static int pg_big5_mblen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; /* kanji? */ else len = 1; /* should be ASCII */ return len; } static int pg_big5_dsplen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; /* kanji? */ else len = pg_ascii_dsplen(s); /* should be ASCII */ return len; } /* * GBK */ static int pg_gbk_mblen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; /* kanji? */ else len = 1; /* should be ASCII */ return len; } static int pg_gbk_dsplen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; /* kanji? */ else len = pg_ascii_dsplen(s); /* should be ASCII */ return len; } /* * UHC */ static int pg_uhc_mblen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; /* 2byte? */ else len = 1; /* should be ASCII */ return len; } static int pg_uhc_dsplen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; /* 2byte? */ else len = pg_ascii_dsplen(s); /* should be ASCII */ return len; } /* * GB18030 * Added by Bill Huang , */ /* * Unlike all other mblen() functions, this also looks at the second byte of * the input. However, if you only pass the first byte of a multi-byte * string, and \0 as the second byte, this still works in a predictable way: * a 4-byte character will be reported as two 2-byte characters. That's * enough for all current uses, as a client-only encoding. It works that * way, because in any valid 4-byte GB18030-encoded character, the third and * fourth byte look like a 2-byte encoded character, when looked at * separately. */ static int pg_gb18030_mblen(const unsigned char *s) { int len; if (!IS_HIGHBIT_SET(*s)) len = 1; /* ASCII */ else if (*(s + 1) >= 0x30 && *(s + 1) <= 0x39) len = 4; else len = 2; return len; } static int pg_gb18030_dsplen(const unsigned char *s) { int len; if (IS_HIGHBIT_SET(*s)) len = 2; else len = pg_ascii_dsplen(s); /* ASCII */ return len; } /* *------------------------------------------------------------------- * multibyte sequence validators * * These functions accept "s", a pointer to the first byte of a string, * and "len", the remaining length of the string. If there is a validly * encoded character beginning at *s, return its length in bytes; else * return -1. * * The functions can assume that len > 0 and that *s != '\0', but they must * test for and reject zeroes in any additional bytes of a multibyte character. * * Note that this definition allows the function for a single-byte * encoding to be just "return 1". *------------------------------------------------------------------- */ static int pg_ascii_verifier(const unsigned char *s, int len) { return 1; } #define IS_EUC_RANGE_VALID(c) ((c) >= 0xa1 && (c) <= 0xfe) static int pg_eucjp_verifier(const unsigned char *s, int len) { int l; unsigned char c1, c2; c1 = *s++; switch (c1) { case SS2: /* JIS X 0201 */ l = 2; if (l > len) return -1; c2 = *s++; if (c2 < 0xa1 || c2 > 0xdf) return -1; break; case SS3: /* JIS X 0212 */ l = 3; if (l > len) return -1; c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; break; default: if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */ { l = 2; if (l > len) return -1; if (!IS_EUC_RANGE_VALID(c1)) return -1; c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; } else /* must be ASCII */ { l = 1; } break; } return l; } static int pg_euckr_verifier(const unsigned char *s, int len) { int l; unsigned char c1, c2; c1 = *s++; if (IS_HIGHBIT_SET(c1)) { l = 2; if (l > len) return -1; if (!IS_EUC_RANGE_VALID(c1)) return -1; c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; } else /* must be ASCII */ { l = 1; } return l; } /* EUC-CN byte sequences are exactly same as EUC-KR */ #define pg_euccn_verifier pg_euckr_verifier static int pg_euctw_verifier(const unsigned char *s, int len) { int l; unsigned char c1, c2; c1 = *s++; switch (c1) { case SS2: /* CNS 11643 Plane 1-7 */ l = 4; if (l > len) return -1; c2 = *s++; if (c2 < 0xa1 || c2 > 0xa7) return -1; c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; break; case SS3: /* unused */ return -1; default: if (IS_HIGHBIT_SET(c1)) /* CNS 11643 Plane 1 */ { l = 2; if (l > len) return -1; /* no further range check on c1? */ c2 = *s++; if (!IS_EUC_RANGE_VALID(c2)) return -1; } else /* must be ASCII */ { l = 1; } break; } return l; } static int pg_johab_verifier(const unsigned char *s, int len) { int l, mbl; unsigned char c; l = mbl = pg_johab_mblen(s); if (len < l) return -1; if (!IS_HIGHBIT_SET(*s)) return mbl; while (--l > 0) { c = *++s; if (!IS_EUC_RANGE_VALID(c)) return -1; } return mbl; } static int pg_mule_verifier(const unsigned char *s, int len) { int l, mbl; unsigned char c; l = mbl = pg_mule_mblen(s); if (len < l) return -1; while (--l > 0) { c = *++s; if (!IS_HIGHBIT_SET(c)) return -1; } return mbl; } static int pg_latin1_verifier(const unsigned char *s, int len) { return 1; } static int pg_sjis_verifier(const unsigned char *s, int len) { int l, mbl; unsigned char c1, c2; l = mbl = pg_sjis_mblen(s); if (len < l) return -1; if (l == 1) /* pg_sjis_mblen already verified it */ return mbl; c1 = *s++; c2 = *s; if (!ISSJISHEAD(c1) || !ISSJISTAIL(c2)) return -1; return mbl; } static int pg_big5_verifier(const unsigned char *s, int len) { int l, mbl; l = mbl = pg_big5_mblen(s); if (len < l) return -1; while (--l > 0) { if (*++s == '\0') return -1; } return mbl; } static int pg_gbk_verifier(const unsigned char *s, int len) { int l, mbl; l = mbl = pg_gbk_mblen(s); if (len < l) return -1; while (--l > 0) { if (*++s == '\0') return -1; } return mbl; } static int pg_uhc_verifier(const unsigned char *s, int len) { int l, mbl; l = mbl = pg_uhc_mblen(s); if (len < l) return -1; while (--l > 0) { if (*++s == '\0') return -1; } return mbl; } static int pg_gb18030_verifier(const unsigned char *s, int len) { int l; if (!IS_HIGHBIT_SET(*s)) l = 1; /* ASCII */ else if (len >= 4 && *(s + 1) >= 0x30 && *(s + 1) <= 0x39) { /* Should be 4-byte, validate remaining bytes */ if (*s >= 0x81 && *s <= 0xfe && *(s + 2) >= 0x81 && *(s + 2) <= 0xfe && *(s + 3) >= 0x30 && *(s + 3) <= 0x39) l = 4; else l = -1; } else if (len >= 2 && *s >= 0x81 && *s <= 0xfe) { /* Should be 2-byte, validate */ if ((*(s + 1) >= 0x40 && *(s + 1) <= 0x7e) || (*(s + 1) >= 0x80 && *(s + 1) <= 0xfe)) l = 2; else l = -1; } else l = -1; return l; } static int pg_utf8_verifier(const unsigned char *s, int len) { int l = pg_utf_mblen(s); if (len < l) return -1; if (!pg_utf8_islegal(s, l)) return -1; return l; } /* * Check for validity of a single UTF-8 encoded character * * This directly implements the rules in RFC3629. The bizarre-looking * restrictions on the second byte are meant to ensure that there isn't * more than one encoding of a given Unicode character point; that is, * you may not use a longer-than-necessary byte sequence with high order * zero bits to represent a character that would fit in fewer bytes. * To do otherwise is to create security hazards (eg, create an apparent * non-ASCII character that decodes to plain ASCII). * * length is assumed to have been obtained by pg_utf_mblen(), and the * caller must have checked that that many bytes are present in the buffer. */ bool pg_utf8_islegal(const unsigned char *source, int length) { unsigned char a; switch (length) { default: /* reject lengths 5 and 6 for now */ return false; case 4: a = source[3]; if (a < 0x80 || a > 0xBF) return false; /* FALL THRU */ case 3: a = source[2]; if (a < 0x80 || a > 0xBF) return false; /* FALL THRU */ case 2: a = source[1]; switch (*source) { case 0xE0: if (a < 0xA0 || a > 0xBF) return false; break; case 0xED: if (a < 0x80 || a > 0x9F) return false; break; case 0xF0: if (a < 0x90 || a > 0xBF) return false; break; case 0xF4: if (a < 0x80 || a > 0x8F) return false; break; default: if (a < 0x80 || a > 0xBF) return false; break; } /* FALL THRU */ case 1: a = *source; if (a >= 0x80 && a < 0xC2) return false; if (a > 0xF4) return false; break; } return true; } /* *------------------------------------------------------------------- * encoding info table * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) *------------------------------------------------------------------- */ const pg_wchar_tbl pg_wchar_table[] = { {pg_ascii2wchar_with_len, pg_wchar2single_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifier, 1}, /* PG_SQL_ASCII */ {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JP */ {pg_euccn2wchar_with_len, pg_wchar2euc_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifier, 2}, /* PG_EUC_CN */ {pg_euckr2wchar_with_len, pg_wchar2euc_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifier, 3}, /* PG_EUC_KR */ {pg_euctw2wchar_with_len, pg_wchar2euc_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifier, 4}, /* PG_EUC_TW */ {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JIS_2004 */ {pg_utf2wchar_with_len, pg_wchar2utf_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifier, 4}, /* PG_UTF8 */ {pg_mule2wchar_with_len, pg_wchar2mule_with_len, pg_mule_mblen, pg_mule_dsplen, pg_mule_verifier, 4}, /* PG_MULE_INTERNAL */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN1 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN2 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN3 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN4 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN5 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN6 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN7 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN8 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN9 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN10 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1256 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1258 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN866 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN874 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8R */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1251 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1252 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-5 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-6 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-7 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-8 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1250 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1253 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1254 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1255 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1257 */ {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8U */ {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2}, /* PG_SJIS */ {0, 0, pg_big5_mblen, pg_big5_dsplen, pg_big5_verifier, 2}, /* PG_BIG5 */ {0, 0, pg_gbk_mblen, pg_gbk_dsplen, pg_gbk_verifier, 2}, /* PG_GBK */ {0, 0, pg_uhc_mblen, pg_uhc_dsplen, pg_uhc_verifier, 2}, /* PG_UHC */ {0, 0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifier, 4}, /* PG_GB18030 */ {0, 0, pg_johab_mblen, pg_johab_dsplen, pg_johab_verifier, 3}, /* PG_JOHAB */ {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2} /* PG_SHIFT_JIS_2004 */ }; /* * Returns the byte length of a multibyte character. * * Caution: when dealing with text that is not certainly valid in the * specified encoding, the result may exceed the actual remaining * string length. Callers that are not prepared to deal with that * should use pg_encoding_mblen_bounded() instead. */ int pg_encoding_mblen(int encoding, const char *mbstr) { return (PG_VALID_ENCODING(encoding) ? pg_wchar_table[encoding].mblen((const unsigned char *) mbstr) : pg_wchar_table[PG_SQL_ASCII].mblen((const unsigned char *) mbstr)); } /* * Returns the byte length of a multibyte character; but not more than * the distance to end of string. */ int pg_encoding_mblen_bounded(int encoding, const char *mbstr) { return strnlen(mbstr, pg_encoding_mblen(encoding, mbstr)); } /* * Returns the display length of a multibyte character. */ int pg_encoding_dsplen(int encoding, const char *mbstr) { return (PG_VALID_ENCODING(encoding) ? pg_wchar_table[encoding].dsplen((const unsigned char *) mbstr) : pg_wchar_table[PG_SQL_ASCII].dsplen((const unsigned char *) mbstr)); } /* * Verify the first multibyte character of the given string. * Return its byte length if good, -1 if bad. (See comments above for * full details of the mbverify API.) */ int pg_encoding_verifymb(int encoding, const char *mbstr, int len) { return (PG_VALID_ENCODING(encoding) ? pg_wchar_table[encoding].mbverify((const unsigned char *) mbstr, len) : pg_wchar_table[PG_SQL_ASCII].mbverify((const unsigned char *) mbstr, len)); } /* * fetch maximum length of a given encoding */ int pg_encoding_max_length(int encoding) { Assert(PG_VALID_ENCODING(encoding)); return pg_wchar_table[encoding].maxmblen; } pgbouncer-1.24.1/src/common/unicode_norm.c0000644000175000000000000003313014777762222015413 00000000000000/*------------------------------------------------------------------------- * unicode_norm.c * Normalize a Unicode string * * This implements Unicode normalization, per the documentation at * https://www.unicode.org/reports/tr15/. * * Portions Copyright (c) 2017-2020, PostgreSQL Global Development Group * * IDENTIFICATION * src/common/unicode_norm.c * *------------------------------------------------------------------------- */ //#ifndef FRONTEND //#include "postgres.h" //#else //#include "postgres_fe.h" //#endif #include "system.h" #include "common/postgres_compat.h" #include "common/unicode_norm.h" #include "common/unicode_norm_table.h" #ifndef FRONTEND #include "common/unicode_normprops_table.h" #endif #ifndef FRONTEND #define ALLOC(size) palloc(size) #define FREE(size) pfree(size) #else #define ALLOC(size) malloc(size) #define FREE(size) free(size) #endif /* Constants for calculations with Hangul characters */ #define SBASE 0xAC00 /* U+AC00 */ #define LBASE 0x1100 /* U+1100 */ #define VBASE 0x1161 /* U+1161 */ #define TBASE 0x11A7 /* U+11A7 */ #define LCOUNT 19 #define VCOUNT 21 #define TCOUNT 28 #define NCOUNT VCOUNT * TCOUNT #define SCOUNT LCOUNT * NCOUNT /* comparison routine for bsearch() of decomposition lookup table. */ static int conv_compare(const void *p1, const void *p2) { uint32 v1, v2; v1 = *(const uint32 *) p1; v2 = ((const pg_unicode_decomposition *) p2)->codepoint; return (v1 > v2) ? 1 : ((v1 == v2) ? 0 : -1); } /* * Get the entry corresponding to code in the decomposition lookup table. */ static pg_unicode_decomposition * get_code_entry(pg_wchar code) { return bsearch(&(code), UnicodeDecompMain, lengthof(UnicodeDecompMain), sizeof(pg_unicode_decomposition), conv_compare); } /* * Given a decomposition entry looked up earlier, get the decomposed * characters. * * Note: the returned pointer can point to statically allocated buffer, and * is only valid until next call to this function! */ static const pg_wchar * get_code_decomposition(pg_unicode_decomposition *entry, int *dec_size) { static pg_wchar x; if (DECOMPOSITION_IS_INLINE(entry)) { Assert(DECOMPOSITION_SIZE(entry) == 1); x = (pg_wchar) entry->dec_index; *dec_size = 1; return &x; } else { *dec_size = DECOMPOSITION_SIZE(entry); return &UnicodeDecomp_codepoints[entry->dec_index]; } } /* * Calculate how many characters a given character will decompose to. * * This needs to recurse, if the character decomposes into characters that * are, in turn, decomposable. */ static int get_decomposed_size(pg_wchar code, bool compat) { pg_unicode_decomposition *entry; int size = 0; int i; const uint32 *decomp; int dec_size; /* * Fast path for Hangul characters not stored in tables to save memory as * decomposition is algorithmic. See * https://www.unicode.org/reports/tr15/tr15-18.html, annex 10 for details * on the matter. */ if (code >= SBASE && code < SBASE + SCOUNT) { uint32 tindex, sindex; sindex = code - SBASE; tindex = sindex % TCOUNT; if (tindex != 0) return 3; return 2; } entry = get_code_entry(code); /* * Just count current code if no other decompositions. A NULL entry is * equivalent to a character with class 0 and no decompositions. */ if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 || (!compat && DECOMPOSITION_IS_COMPAT(entry))) return 1; /* * If this entry has other decomposition codes look at them as well. First * get its decomposition in the list of tables available. */ decomp = get_code_decomposition(entry, &dec_size); for (i = 0; i < dec_size; i++) { uint32 lcode = decomp[i]; size += get_decomposed_size(lcode, compat); } return size; } /* * Recompose a set of characters. For hangul characters, the calculation * is algorithmic. For others, an inverse lookup at the decomposition * table is necessary. Returns true if a recomposition can be done, and * false otherwise. */ static bool recompose_code(uint32 start, uint32 code, uint32 *result) { /* * Handle Hangul characters algorithmically, per the Unicode spec. * * Check if two current characters are L and V. */ if (start >= LBASE && start < LBASE + LCOUNT && code >= VBASE && code < VBASE + VCOUNT) { /* make syllable of form LV */ uint32 lindex = start - LBASE; uint32 vindex = code - VBASE; *result = SBASE + (lindex * VCOUNT + vindex) * TCOUNT; return true; } /* Check if two current characters are LV and T */ else if (start >= SBASE && start < (SBASE + SCOUNT) && ((start - SBASE) % TCOUNT) == 0 && code >= TBASE && code < (TBASE + TCOUNT)) { /* make syllable of form LVT */ uint32 tindex = code - TBASE; *result = start + tindex; return true; } else { unsigned int i; /* * Do an inverse lookup of the decomposition tables to see if anything * matches. The comparison just needs to be a perfect match on the * sub-table of size two, because the start character has already been * recomposed partially. */ for (i = 0; i < lengthof(UnicodeDecompMain); i++) { const pg_unicode_decomposition *entry = &UnicodeDecompMain[i]; if (DECOMPOSITION_SIZE(entry) != 2) continue; if (DECOMPOSITION_NO_COMPOSE(entry)) continue; if (start == UnicodeDecomp_codepoints[entry->dec_index] && code == UnicodeDecomp_codepoints[entry->dec_index + 1]) { *result = entry->codepoint; return true; } } } return false; } /* * Decompose the given code into the array given by caller. The * decomposition begins at the position given by caller, saving one * lookup on the decomposition table. The current position needs to be * updated here to let the caller know from where to continue filling * in the array result. */ static void decompose_code(pg_wchar code, bool compat, pg_wchar **result, int *current) { pg_unicode_decomposition *entry; int i; const uint32 *decomp; int dec_size; /* * Fast path for Hangul characters not stored in tables to save memory as * decomposition is algorithmic. See * https://www.unicode.org/reports/tr15/tr15-18.html, annex 10 for details * on the matter. */ if (code >= SBASE && code < SBASE + SCOUNT) { uint32 l, v, tindex, sindex; pg_wchar *res = *result; sindex = code - SBASE; l = LBASE + sindex / (VCOUNT * TCOUNT); v = VBASE + (sindex % (VCOUNT * TCOUNT)) / TCOUNT; tindex = sindex % TCOUNT; res[*current] = l; (*current)++; res[*current] = v; (*current)++; if (tindex != 0) { res[*current] = TBASE + tindex; (*current)++; } return; } entry = get_code_entry(code); /* * Just fill in with the current decomposition if there are no * decomposition codes to recurse to. A NULL entry is equivalent to a * character with class 0 and no decompositions, so just leave also in * this case. */ if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 || (!compat && DECOMPOSITION_IS_COMPAT(entry))) { pg_wchar *res = *result; res[*current] = code; (*current)++; return; } /* * If this entry has other decomposition codes look at them as well. */ decomp = get_code_decomposition(entry, &dec_size); for (i = 0; i < dec_size; i++) { pg_wchar lcode = (pg_wchar) decomp[i]; /* Leave if no more decompositions */ decompose_code(lcode, compat, result, current); } } /* * unicode_normalize - Normalize a Unicode string to the specified form. * * The input is a 0-terminated array of codepoints. * * In frontend, returns a 0-terminated array of codepoints, allocated with * malloc. Or NULL if we run out of memory. In backend, the returned * string is palloc'd instead, and OOM is reported with ereport(). */ pg_wchar * unicode_normalize(UnicodeNormalizationForm form, const pg_wchar *input) { bool compat = (form == UNICODE_NFKC || form == UNICODE_NFKD); bool recompose = (form == UNICODE_NFC || form == UNICODE_NFKC); pg_wchar *decomp_chars; pg_wchar *recomp_chars; int decomp_size, current_size; int count; const pg_wchar *p; /* variables for recomposition */ int last_class; int starter_pos; int target_pos; uint32 starter_ch; /* First, do character decomposition */ /* * Calculate how many characters long the decomposed version will be. */ decomp_size = 0; for (p = input; *p; p++) decomp_size += get_decomposed_size(*p, compat); decomp_chars = (pg_wchar *) ALLOC((decomp_size + 1) * sizeof(pg_wchar)); if (decomp_chars == NULL) return NULL; /* * Now fill in each entry recursively. This needs a second pass on the * decomposition table. */ current_size = 0; for (p = input; *p; p++) decompose_code(*p, compat, &decomp_chars, ¤t_size); decomp_chars[decomp_size] = '\0'; Assert(decomp_size == current_size); /* * Now apply canonical ordering. */ for (count = 1; count < decomp_size; count++) { pg_wchar prev = decomp_chars[count - 1]; pg_wchar next = decomp_chars[count]; pg_wchar tmp; pg_unicode_decomposition *prevEntry = get_code_entry(prev); pg_unicode_decomposition *nextEntry = get_code_entry(next); /* * If no entries are found, the character used is either an Hangul * character or a character with a class of 0 and no decompositions, * so move to next result. */ if (prevEntry == NULL || nextEntry == NULL) continue; /* * Per Unicode (https://www.unicode.org/reports/tr15/tr15-18.html) * annex 4, a sequence of two adjacent characters in a string is an * exchangeable pair if the combining class (from the Unicode * Character Database) for the first character is greater than the * combining class for the second, and the second is not a starter. A * character is a starter if its combining class is 0. */ if (nextEntry->comb_class == 0x0 || prevEntry->comb_class == 0x0) continue; if (prevEntry->comb_class <= nextEntry->comb_class) continue; /* exchange can happen */ tmp = decomp_chars[count - 1]; decomp_chars[count - 1] = decomp_chars[count]; decomp_chars[count] = tmp; /* backtrack to check again */ if (count > 1) count -= 2; } if (!recompose) return decomp_chars; /* * The last phase of NFC and NFKC is the recomposition of the reordered * Unicode string using combining classes. The recomposed string cannot be * longer than the decomposed one, so make the allocation of the output * string based on that assumption. */ recomp_chars = (pg_wchar *) ALLOC((decomp_size + 1) * sizeof(pg_wchar)); if (!recomp_chars) { FREE(decomp_chars); return NULL; } last_class = -1; /* this eliminates a special check */ starter_pos = 0; target_pos = 1; starter_ch = recomp_chars[0] = decomp_chars[0]; for (count = 1; count < decomp_size; count++) { pg_wchar ch = decomp_chars[count]; pg_unicode_decomposition *ch_entry = get_code_entry(ch); int ch_class = (ch_entry == NULL) ? 0 : ch_entry->comb_class; pg_wchar composite; if (last_class < ch_class && recompose_code(starter_ch, ch, &composite)) { recomp_chars[starter_pos] = composite; starter_ch = composite; } else if (ch_class == 0) { starter_pos = target_pos; starter_ch = ch; last_class = -1; recomp_chars[target_pos++] = ch; } else { last_class = ch_class; recomp_chars[target_pos++] = ch; } } recomp_chars[target_pos] = (pg_wchar) '\0'; FREE(decomp_chars); return recomp_chars; } /* * Normalization "quick check" algorithm; see * */ /* We only need this in the backend. */ #ifndef FRONTEND static uint8 get_canonical_class(pg_wchar ch) { pg_unicode_decomposition *entry = get_code_entry(ch); if (!entry) return 0; else return entry->comb_class; } static int qc_compare(const void *p1, const void *p2) { uint32 v1, v2; v1 = ((const pg_unicode_normprops *) p1)->codepoint; v2 = ((const pg_unicode_normprops *) p2)->codepoint; return (v1 - v2); } /* * Look up the normalization quick check character property */ static UnicodeNormalizationQC qc_is_allowed(UnicodeNormalizationForm form, pg_wchar ch) { pg_unicode_normprops key; pg_unicode_normprops *found = NULL; key.codepoint = ch; switch (form) { case UNICODE_NFC: found = bsearch(&key, UnicodeNormProps_NFC_QC, lengthof(UnicodeNormProps_NFC_QC), sizeof(pg_unicode_normprops), qc_compare); break; case UNICODE_NFKC: found = bsearch(&key, UnicodeNormProps_NFKC_QC, lengthof(UnicodeNormProps_NFKC_QC), sizeof(pg_unicode_normprops), qc_compare); break; default: Assert(false); break; } if (found) return found->quickcheck; else return UNICODE_NORM_QC_YES; } UnicodeNormalizationQC unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const pg_wchar *input) { uint8 lastCanonicalClass = 0; UnicodeNormalizationQC result = UNICODE_NORM_QC_YES; /* * For the "D" forms, we don't run the quickcheck. We don't include the * lookup tables for those because they are huge, checking for these * particular forms is less common, and running the slow path is faster * for the "D" forms than the "C" forms because you don't need to * recompose, which is slow. */ if (form == UNICODE_NFD || form == UNICODE_NFKD) return UNICODE_NORM_QC_MAYBE; for (const pg_wchar *p = input; *p; p++) { pg_wchar ch = *p; uint8 canonicalClass; UnicodeNormalizationQC check; canonicalClass = get_canonical_class(ch); if (lastCanonicalClass > canonicalClass && canonicalClass != 0) return UNICODE_NORM_QC_NO; check = qc_is_allowed(form, ch); if (check == UNICODE_NORM_QC_NO) return UNICODE_NORM_QC_NO; else if (check == UNICODE_NORM_QC_MAYBE) result = UNICODE_NORM_QC_MAYBE; lastCanonicalClass = canonicalClass; } return result; } #endif /* !FRONTEND */ pgbouncer-1.24.1/src/common/bool.c0000644000175000000000000000421214777762222013664 00000000000000/*------------------------------------------------------------------------- * * bool.c * Functions for the built-in type "bool". * * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/utils/adt/bool.c * *------------------------------------------------------------------------- */ #include "common/postgres_compat.h" #include "common/builtins.h" /* * Try to interpret value as boolean value. Valid values are: true, * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof. * If the string parses okay, return true, else false. * If okay and result is not NULL, return the value in *result. */ bool parse_bool(const char *value, bool *result) { return parse_bool_with_len(value, strlen(value), result); } bool parse_bool_with_len(const char *value, size_t len, bool *result) { switch (*value) { case 't': case 'T': if (pg_strncasecmp(value, "true", len) == 0) { if (result) *result = true; return true; } break; case 'f': case 'F': if (pg_strncasecmp(value, "false", len) == 0) { if (result) *result = false; return true; } break; case 'y': case 'Y': if (pg_strncasecmp(value, "yes", len) == 0) { if (result) *result = true; return true; } break; case 'n': case 'N': if (pg_strncasecmp(value, "no", len) == 0) { if (result) *result = false; return true; } break; case 'o': case 'O': /* 'o' is not unique enough */ if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) { if (result) *result = true; return true; } else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) { if (result) *result = false; return true; } break; case '1': if (len == 1) { if (result) *result = true; return true; } break; case '0': if (len == 1) { if (result) *result = false; return true; } break; default: break; } if (result) *result = false; /* suppress compiler warning */ return false; } pgbouncer-1.24.1/src/common/saslprep.c0000644000175000000000000006245414777762222014576 00000000000000/*------------------------------------------------------------------------- * saslprep.c * SASLprep normalization, for SCRAM authentication * * The SASLprep algorithm is used to process a user-supplied password into * canonical form. For more details, see: * * [RFC3454] Preparation of Internationalized Strings ("stringprep"), * http://www.ietf.org/rfc/rfc3454.txt * * [RFC4013] SASLprep: Stringprep Profile for User Names and Passwords * http://www.ietf.org/rfc/rfc4013.txt * * * Portions Copyright (c) 2017-2020, PostgreSQL Global Development Group * * IDENTIFICATION * src/common/saslprep.c * *------------------------------------------------------------------------- */ //#ifndef FRONTEND //#include "postgres.h" //#else //#include "postgres_fe.h" //#endif #include "system.h" #include "common/postgres_compat.h" #include "common/saslprep.h" #include "common/unicode_norm.h" #include "common/pg_wchar.h" /* * Limit on how large password's we will try to process. A password * larger than this will be treated the same as out-of-memory. */ #define MAX_PASSWORD_LENGTH 1024 /* * In backend, we will use palloc/pfree. In frontend, use malloc, and * return SASLPREP_OOM on out-of-memory. */ #ifndef FRONTEND #define STRDUP(s) pstrdup(s) #define ALLOC(size) palloc(size) #define FREE(size) pfree(size) #else #define STRDUP(s) strdup(s) #define ALLOC(size) malloc(size) #define FREE(size) free(size) #endif /* Prototypes for local functions */ static int codepoint_range_cmp(const void *a, const void *b); static bool is_code_in_table(pg_wchar code, const pg_wchar *map, int mapsize); static int pg_utf8_string_len(const char *source); static bool pg_is_ascii_string(const char *p); /* * Stringprep Mapping Tables. * * The stringprep specification includes a number of tables of Unicode * codepoints, used in different parts of the algorithm. They are below, * as arrays of codepoint ranges. Each range is a pair of codepoints, * for the first and last codepoint included the range (inclusive!). */ /* * C.1.2 Non-ASCII space characters * * These are all mapped to the ASCII space character (U+00A0). */ static const pg_wchar non_ascii_space_ranges[] = { 0x00A0, 0x00A0, 0x1680, 0x1680, 0x2000, 0x200B, 0x202F, 0x202F, 0x205F, 0x205F, 0x3000, 0x3000 }; /* * B.1 Commonly mapped to nothing * * If any of these appear in the input, they are removed. */ static const pg_wchar commonly_mapped_to_nothing_ranges[] = { 0x00AD, 0x00AD, 0x034F, 0x034F, 0x1806, 0x1806, 0x180B, 0x180D, 0x200B, 0x200D, 0x2060, 0x2060, 0xFE00, 0xFE0F, 0xFEFF, 0xFEFF }; /* * prohibited_output_ranges is a union of all the characters from * the following tables: * * C.1.2 Non-ASCII space characters * C.2.1 ASCII control characters * C.2.2 Non-ASCII control characters * C.3 Private Use characters * C.4 Non-character code points * C.5 Surrogate code points * C.6 Inappropriate for plain text characters * C.7 Inappropriate for canonical representation characters * C.7 Change display properties or deprecated characters * C.8 Tagging characters * * These are the tables that are listed as "prohibited output" * characters in the SASLprep profile. * * The comment after each code range indicates which source table * the code came from. Note that there is some overlap in the source * tables, so one code might originate from multiple source tables. * Adjacent ranges have also been merged together, to save space. */ static const pg_wchar prohibited_output_ranges[] = { 0x0000, 0x001F, /* C.2.1 */ 0x007F, 0x00A0, /* C.1.2, C.2.1, C.2.2 */ 0x0340, 0x0341, /* C.8 */ 0x06DD, 0x06DD, /* C.2.2 */ 0x070F, 0x070F, /* C.2.2 */ 0x1680, 0x1680, /* C.1.2 */ 0x180E, 0x180E, /* C.2.2 */ 0x2000, 0x200F, /* C.1.2, C.2.2, C.8 */ 0x2028, 0x202F, /* C.1.2, C.2.2, C.8 */ 0x205F, 0x2063, /* C.1.2, C.2.2 */ 0x206A, 0x206F, /* C.2.2, C.8 */ 0x2FF0, 0x2FFB, /* C.7 */ 0x3000, 0x3000, /* C.1.2 */ 0xD800, 0xF8FF, /* C.3, C.5 */ 0xFDD0, 0xFDEF, /* C.4 */ 0xFEFF, 0xFEFF, /* C.2.2 */ 0xFFF9, 0xFFFF, /* C.2.2, C.4, C.6 */ 0x1D173, 0x1D17A, /* C.2.2 */ 0x1FFFE, 0x1FFFF, /* C.4 */ 0x2FFFE, 0x2FFFF, /* C.4 */ 0x3FFFE, 0x3FFFF, /* C.4 */ 0x4FFFE, 0x4FFFF, /* C.4 */ 0x5FFFE, 0x5FFFF, /* C.4 */ 0x6FFFE, 0x6FFFF, /* C.4 */ 0x7FFFE, 0x7FFFF, /* C.4 */ 0x8FFFE, 0x8FFFF, /* C.4 */ 0x9FFFE, 0x9FFFF, /* C.4 */ 0xAFFFE, 0xAFFFF, /* C.4 */ 0xBFFFE, 0xBFFFF, /* C.4 */ 0xCFFFE, 0xCFFFF, /* C.4 */ 0xDFFFE, 0xDFFFF, /* C.4 */ 0xE0001, 0xE0001, /* C.9 */ 0xE0020, 0xE007F, /* C.9 */ 0xEFFFE, 0xEFFFF, /* C.4 */ 0xF0000, 0xFFFFF, /* C.3, C.4 */ 0x100000, 0x10FFFF /* C.3, C.4 */ }; /* A.1 Unassigned code points in Unicode 3.2 */ static const pg_wchar unassigned_codepoint_ranges[] = { 0x0221, 0x0221, 0x0234, 0x024F, 0x02AE, 0x02AF, 0x02EF, 0x02FF, 0x0350, 0x035F, 0x0370, 0x0373, 0x0376, 0x0379, 0x037B, 0x037D, 0x037F, 0x0383, 0x038B, 0x038B, 0x038D, 0x038D, 0x03A2, 0x03A2, 0x03CF, 0x03CF, 0x03F7, 0x03FF, 0x0487, 0x0487, 0x04CF, 0x04CF, 0x04F6, 0x04F7, 0x04FA, 0x04FF, 0x0510, 0x0530, 0x0557, 0x0558, 0x0560, 0x0560, 0x0588, 0x0588, 0x058B, 0x0590, 0x05A2, 0x05A2, 0x05BA, 0x05BA, 0x05C5, 0x05CF, 0x05EB, 0x05EF, 0x05F5, 0x060B, 0x060D, 0x061A, 0x061C, 0x061E, 0x0620, 0x0620, 0x063B, 0x063F, 0x0656, 0x065F, 0x06EE, 0x06EF, 0x06FF, 0x06FF, 0x070E, 0x070E, 0x072D, 0x072F, 0x074B, 0x077F, 0x07B2, 0x0900, 0x0904, 0x0904, 0x093A, 0x093B, 0x094E, 0x094F, 0x0955, 0x0957, 0x0971, 0x0980, 0x0984, 0x0984, 0x098D, 0x098E, 0x0991, 0x0992, 0x09A9, 0x09A9, 0x09B1, 0x09B1, 0x09B3, 0x09B5, 0x09BA, 0x09BB, 0x09BD, 0x09BD, 0x09C5, 0x09C6, 0x09C9, 0x09CA, 0x09CE, 0x09D6, 0x09D8, 0x09DB, 0x09DE, 0x09DE, 0x09E4, 0x09E5, 0x09FB, 0x0A01, 0x0A03, 0x0A04, 0x0A0B, 0x0A0E, 0x0A11, 0x0A12, 0x0A29, 0x0A29, 0x0A31, 0x0A31, 0x0A34, 0x0A34, 0x0A37, 0x0A37, 0x0A3A, 0x0A3B, 0x0A3D, 0x0A3D, 0x0A43, 0x0A46, 0x0A49, 0x0A4A, 0x0A4E, 0x0A58, 0x0A5D, 0x0A5D, 0x0A5F, 0x0A65, 0x0A75, 0x0A80, 0x0A84, 0x0A84, 0x0A8C, 0x0A8C, 0x0A8E, 0x0A8E, 0x0A92, 0x0A92, 0x0AA9, 0x0AA9, 0x0AB1, 0x0AB1, 0x0AB4, 0x0AB4, 0x0ABA, 0x0ABB, 0x0AC6, 0x0AC6, 0x0ACA, 0x0ACA, 0x0ACE, 0x0ACF, 0x0AD1, 0x0ADF, 0x0AE1, 0x0AE5, 0x0AF0, 0x0B00, 0x0B04, 0x0B04, 0x0B0D, 0x0B0E, 0x0B11, 0x0B12, 0x0B29, 0x0B29, 0x0B31, 0x0B31, 0x0B34, 0x0B35, 0x0B3A, 0x0B3B, 0x0B44, 0x0B46, 0x0B49, 0x0B4A, 0x0B4E, 0x0B55, 0x0B58, 0x0B5B, 0x0B5E, 0x0B5E, 0x0B62, 0x0B65, 0x0B71, 0x0B81, 0x0B84, 0x0B84, 0x0B8B, 0x0B8D, 0x0B91, 0x0B91, 0x0B96, 0x0B98, 0x0B9B, 0x0B9B, 0x0B9D, 0x0B9D, 0x0BA0, 0x0BA2, 0x0BA5, 0x0BA7, 0x0BAB, 0x0BAD, 0x0BB6, 0x0BB6, 0x0BBA, 0x0BBD, 0x0BC3, 0x0BC5, 0x0BC9, 0x0BC9, 0x0BCE, 0x0BD6, 0x0BD8, 0x0BE6, 0x0BF3, 0x0C00, 0x0C04, 0x0C04, 0x0C0D, 0x0C0D, 0x0C11, 0x0C11, 0x0C29, 0x0C29, 0x0C34, 0x0C34, 0x0C3A, 0x0C3D, 0x0C45, 0x0C45, 0x0C49, 0x0C49, 0x0C4E, 0x0C54, 0x0C57, 0x0C5F, 0x0C62, 0x0C65, 0x0C70, 0x0C81, 0x0C84, 0x0C84, 0x0C8D, 0x0C8D, 0x0C91, 0x0C91, 0x0CA9, 0x0CA9, 0x0CB4, 0x0CB4, 0x0CBA, 0x0CBD, 0x0CC5, 0x0CC5, 0x0CC9, 0x0CC9, 0x0CCE, 0x0CD4, 0x0CD7, 0x0CDD, 0x0CDF, 0x0CDF, 0x0CE2, 0x0CE5, 0x0CF0, 0x0D01, 0x0D04, 0x0D04, 0x0D0D, 0x0D0D, 0x0D11, 0x0D11, 0x0D29, 0x0D29, 0x0D3A, 0x0D3D, 0x0D44, 0x0D45, 0x0D49, 0x0D49, 0x0D4E, 0x0D56, 0x0D58, 0x0D5F, 0x0D62, 0x0D65, 0x0D70, 0x0D81, 0x0D84, 0x0D84, 0x0D97, 0x0D99, 0x0DB2, 0x0DB2, 0x0DBC, 0x0DBC, 0x0DBE, 0x0DBF, 0x0DC7, 0x0DC9, 0x0DCB, 0x0DCE, 0x0DD5, 0x0DD5, 0x0DD7, 0x0DD7, 0x0DE0, 0x0DF1, 0x0DF5, 0x0E00, 0x0E3B, 0x0E3E, 0x0E5C, 0x0E80, 0x0E83, 0x0E83, 0x0E85, 0x0E86, 0x0E89, 0x0E89, 0x0E8B, 0x0E8C, 0x0E8E, 0x0E93, 0x0E98, 0x0E98, 0x0EA0, 0x0EA0, 0x0EA4, 0x0EA4, 0x0EA6, 0x0EA6, 0x0EA8, 0x0EA9, 0x0EAC, 0x0EAC, 0x0EBA, 0x0EBA, 0x0EBE, 0x0EBF, 0x0EC5, 0x0EC5, 0x0EC7, 0x0EC7, 0x0ECE, 0x0ECF, 0x0EDA, 0x0EDB, 0x0EDE, 0x0EFF, 0x0F48, 0x0F48, 0x0F6B, 0x0F70, 0x0F8C, 0x0F8F, 0x0F98, 0x0F98, 0x0FBD, 0x0FBD, 0x0FCD, 0x0FCE, 0x0FD0, 0x0FFF, 0x1022, 0x1022, 0x1028, 0x1028, 0x102B, 0x102B, 0x1033, 0x1035, 0x103A, 0x103F, 0x105A, 0x109F, 0x10C6, 0x10CF, 0x10F9, 0x10FA, 0x10FC, 0x10FF, 0x115A, 0x115E, 0x11A3, 0x11A7, 0x11FA, 0x11FF, 0x1207, 0x1207, 0x1247, 0x1247, 0x1249, 0x1249, 0x124E, 0x124F, 0x1257, 0x1257, 0x1259, 0x1259, 0x125E, 0x125F, 0x1287, 0x1287, 0x1289, 0x1289, 0x128E, 0x128F, 0x12AF, 0x12AF, 0x12B1, 0x12B1, 0x12B6, 0x12B7, 0x12BF, 0x12BF, 0x12C1, 0x12C1, 0x12C6, 0x12C7, 0x12CF, 0x12CF, 0x12D7, 0x12D7, 0x12EF, 0x12EF, 0x130F, 0x130F, 0x1311, 0x1311, 0x1316, 0x1317, 0x131F, 0x131F, 0x1347, 0x1347, 0x135B, 0x1360, 0x137D, 0x139F, 0x13F5, 0x1400, 0x1677, 0x167F, 0x169D, 0x169F, 0x16F1, 0x16FF, 0x170D, 0x170D, 0x1715, 0x171F, 0x1737, 0x173F, 0x1754, 0x175F, 0x176D, 0x176D, 0x1771, 0x1771, 0x1774, 0x177F, 0x17DD, 0x17DF, 0x17EA, 0x17FF, 0x180F, 0x180F, 0x181A, 0x181F, 0x1878, 0x187F, 0x18AA, 0x1DFF, 0x1E9C, 0x1E9F, 0x1EFA, 0x1EFF, 0x1F16, 0x1F17, 0x1F1E, 0x1F1F, 0x1F46, 0x1F47, 0x1F4E, 0x1F4F, 0x1F58, 0x1F58, 0x1F5A, 0x1F5A, 0x1F5C, 0x1F5C, 0x1F5E, 0x1F5E, 0x1F7E, 0x1F7F, 0x1FB5, 0x1FB5, 0x1FC5, 0x1FC5, 0x1FD4, 0x1FD5, 0x1FDC, 0x1FDC, 0x1FF0, 0x1FF1, 0x1FF5, 0x1FF5, 0x1FFF, 0x1FFF, 0x2053, 0x2056, 0x2058, 0x205E, 0x2064, 0x2069, 0x2072, 0x2073, 0x208F, 0x209F, 0x20B2, 0x20CF, 0x20EB, 0x20FF, 0x213B, 0x213C, 0x214C, 0x2152, 0x2184, 0x218F, 0x23CF, 0x23FF, 0x2427, 0x243F, 0x244B, 0x245F, 0x24FF, 0x24FF, 0x2614, 0x2615, 0x2618, 0x2618, 0x267E, 0x267F, 0x268A, 0x2700, 0x2705, 0x2705, 0x270A, 0x270B, 0x2728, 0x2728, 0x274C, 0x274C, 0x274E, 0x274E, 0x2753, 0x2755, 0x2757, 0x2757, 0x275F, 0x2760, 0x2795, 0x2797, 0x27B0, 0x27B0, 0x27BF, 0x27CF, 0x27EC, 0x27EF, 0x2B00, 0x2E7F, 0x2E9A, 0x2E9A, 0x2EF4, 0x2EFF, 0x2FD6, 0x2FEF, 0x2FFC, 0x2FFF, 0x3040, 0x3040, 0x3097, 0x3098, 0x3100, 0x3104, 0x312D, 0x3130, 0x318F, 0x318F, 0x31B8, 0x31EF, 0x321D, 0x321F, 0x3244, 0x3250, 0x327C, 0x327E, 0x32CC, 0x32CF, 0x32FF, 0x32FF, 0x3377, 0x337A, 0x33DE, 0x33DF, 0x33FF, 0x33FF, 0x4DB6, 0x4DFF, 0x9FA6, 0x9FFF, 0xA48D, 0xA48F, 0xA4C7, 0xABFF, 0xD7A4, 0xD7FF, 0xFA2E, 0xFA2F, 0xFA6B, 0xFAFF, 0xFB07, 0xFB12, 0xFB18, 0xFB1C, 0xFB37, 0xFB37, 0xFB3D, 0xFB3D, 0xFB3F, 0xFB3F, 0xFB42, 0xFB42, 0xFB45, 0xFB45, 0xFBB2, 0xFBD2, 0xFD40, 0xFD4F, 0xFD90, 0xFD91, 0xFDC8, 0xFDCF, 0xFDFD, 0xFDFF, 0xFE10, 0xFE1F, 0xFE24, 0xFE2F, 0xFE47, 0xFE48, 0xFE53, 0xFE53, 0xFE67, 0xFE67, 0xFE6C, 0xFE6F, 0xFE75, 0xFE75, 0xFEFD, 0xFEFE, 0xFF00, 0xFF00, 0xFFBF, 0xFFC1, 0xFFC8, 0xFFC9, 0xFFD0, 0xFFD1, 0xFFD8, 0xFFD9, 0xFFDD, 0xFFDF, 0xFFE7, 0xFFE7, 0xFFEF, 0xFFF8, 0x10000, 0x102FF, 0x1031F, 0x1031F, 0x10324, 0x1032F, 0x1034B, 0x103FF, 0x10426, 0x10427, 0x1044E, 0x1CFFF, 0x1D0F6, 0x1D0FF, 0x1D127, 0x1D129, 0x1D1DE, 0x1D3FF, 0x1D455, 0x1D455, 0x1D49D, 0x1D49D, 0x1D4A0, 0x1D4A1, 0x1D4A3, 0x1D4A4, 0x1D4A7, 0x1D4A8, 0x1D4AD, 0x1D4AD, 0x1D4BA, 0x1D4BA, 0x1D4BC, 0x1D4BC, 0x1D4C1, 0x1D4C1, 0x1D4C4, 0x1D4C4, 0x1D506, 0x1D506, 0x1D50B, 0x1D50C, 0x1D515, 0x1D515, 0x1D51D, 0x1D51D, 0x1D53A, 0x1D53A, 0x1D53F, 0x1D53F, 0x1D545, 0x1D545, 0x1D547, 0x1D549, 0x1D551, 0x1D551, 0x1D6A4, 0x1D6A7, 0x1D7CA, 0x1D7CD, 0x1D800, 0x1FFFD, 0x2A6D7, 0x2F7FF, 0x2FA1E, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD, 0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD, 0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD, 0xD0000, 0xDFFFD, 0xE0000, 0xE0000, 0xE0002, 0xE001F, 0xE0080, 0xEFFFD }; /* D.1 Characters with bidirectional property "R" or "AL" */ static const pg_wchar RandALCat_codepoint_ranges[] = { 0x05BE, 0x05BE, 0x05C0, 0x05C0, 0x05C3, 0x05C3, 0x05D0, 0x05EA, 0x05F0, 0x05F4, 0x061B, 0x061B, 0x061F, 0x061F, 0x0621, 0x063A, 0x0640, 0x064A, 0x066D, 0x066F, 0x0671, 0x06D5, 0x06DD, 0x06DD, 0x06E5, 0x06E6, 0x06FA, 0x06FE, 0x0700, 0x070D, 0x0710, 0x0710, 0x0712, 0x072C, 0x0780, 0x07A5, 0x07B1, 0x07B1, 0x200F, 0x200F, 0xFB1D, 0xFB1D, 0xFB1F, 0xFB28, 0xFB2A, 0xFB36, 0xFB38, 0xFB3C, 0xFB3E, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44, 0xFB46, 0xFBB1, 0xFBD3, 0xFD3D, 0xFD50, 0xFD8F, 0xFD92, 0xFDC7, 0xFDF0, 0xFDFC, 0xFE70, 0xFE74, 0xFE76, 0xFEFC }; /* D.2 Characters with bidirectional property "L" */ static const pg_wchar LCat_codepoint_ranges[] = { 0x0041, 0x005A, 0x0061, 0x007A, 0x00AA, 0x00AA, 0x00B5, 0x00B5, 0x00BA, 0x00BA, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 0x00F8, 0x0220, 0x0222, 0x0233, 0x0250, 0x02AD, 0x02B0, 0x02B8, 0x02BB, 0x02C1, 0x02D0, 0x02D1, 0x02E0, 0x02E4, 0x02EE, 0x02EE, 0x037A, 0x037A, 0x0386, 0x0386, 0x0388, 0x038A, 0x038C, 0x038C, 0x038E, 0x03A1, 0x03A3, 0x03CE, 0x03D0, 0x03F5, 0x0400, 0x0482, 0x048A, 0x04CE, 0x04D0, 0x04F5, 0x04F8, 0x04F9, 0x0500, 0x050F, 0x0531, 0x0556, 0x0559, 0x055F, 0x0561, 0x0587, 0x0589, 0x0589, 0x0903, 0x0903, 0x0905, 0x0939, 0x093D, 0x0940, 0x0949, 0x094C, 0x0950, 0x0950, 0x0958, 0x0961, 0x0964, 0x0970, 0x0982, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BE, 0x09C0, 0x09C7, 0x09C8, 0x09CB, 0x09CC, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E1, 0x09E6, 0x09F1, 0x09F4, 0x09FA, 0x0A05, 0x0A0A, 0x0A0F, 0x0A10, 0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36, 0x0A38, 0x0A39, 0x0A3E, 0x0A40, 0x0A59, 0x0A5C, 0x0A5E, 0x0A5E, 0x0A66, 0x0A6F, 0x0A72, 0x0A74, 0x0A83, 0x0A83, 0x0A85, 0x0A8B, 0x0A8D, 0x0A8D, 0x0A8F, 0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0, 0x0AB2, 0x0AB3, 0x0AB5, 0x0AB9, 0x0ABD, 0x0AC0, 0x0AC9, 0x0AC9, 0x0ACB, 0x0ACC, 0x0AD0, 0x0AD0, 0x0AE0, 0x0AE0, 0x0AE6, 0x0AEF, 0x0B02, 0x0B03, 0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28, 0x0B2A, 0x0B30, 0x0B32, 0x0B33, 0x0B36, 0x0B39, 0x0B3D, 0x0B3E, 0x0B40, 0x0B40, 0x0B47, 0x0B48, 0x0B4B, 0x0B4C, 0x0B57, 0x0B57, 0x0B5C, 0x0B5D, 0x0B5F, 0x0B61, 0x0B66, 0x0B70, 0x0B83, 0x0B83, 0x0B85, 0x0B8A, 0x0B8E, 0x0B90, 0x0B92, 0x0B95, 0x0B99, 0x0B9A, 0x0B9C, 0x0B9C, 0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8, 0x0BAA, 0x0BAE, 0x0BB5, 0x0BB7, 0x0BB9, 0x0BBE, 0x0BBF, 0x0BC1, 0x0BC2, 0x0BC6, 0x0BC8, 0x0BCA, 0x0BCC, 0x0BD7, 0x0BD7, 0x0BE7, 0x0BF2, 0x0C01, 0x0C03, 0x0C05, 0x0C0C, 0x0C0E, 0x0C10, 0x0C12, 0x0C28, 0x0C2A, 0x0C33, 0x0C35, 0x0C39, 0x0C41, 0x0C44, 0x0C60, 0x0C61, 0x0C66, 0x0C6F, 0x0C82, 0x0C83, 0x0C85, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8, 0x0CAA, 0x0CB3, 0x0CB5, 0x0CB9, 0x0CBE, 0x0CBE, 0x0CC0, 0x0CC4, 0x0CC7, 0x0CC8, 0x0CCA, 0x0CCB, 0x0CD5, 0x0CD6, 0x0CDE, 0x0CDE, 0x0CE0, 0x0CE1, 0x0CE6, 0x0CEF, 0x0D02, 0x0D03, 0x0D05, 0x0D0C, 0x0D0E, 0x0D10, 0x0D12, 0x0D28, 0x0D2A, 0x0D39, 0x0D3E, 0x0D40, 0x0D46, 0x0D48, 0x0D4A, 0x0D4C, 0x0D57, 0x0D57, 0x0D60, 0x0D61, 0x0D66, 0x0D6F, 0x0D82, 0x0D83, 0x0D85, 0x0D96, 0x0D9A, 0x0DB1, 0x0DB3, 0x0DBB, 0x0DBD, 0x0DBD, 0x0DC0, 0x0DC6, 0x0DCF, 0x0DD1, 0x0DD8, 0x0DDF, 0x0DF2, 0x0DF4, 0x0E01, 0x0E30, 0x0E32, 0x0E33, 0x0E40, 0x0E46, 0x0E4F, 0x0E5B, 0x0E81, 0x0E82, 0x0E84, 0x0E84, 0x0E87, 0x0E88, 0x0E8A, 0x0E8A, 0x0E8D, 0x0E8D, 0x0E94, 0x0E97, 0x0E99, 0x0E9F, 0x0EA1, 0x0EA3, 0x0EA5, 0x0EA5, 0x0EA7, 0x0EA7, 0x0EAA, 0x0EAB, 0x0EAD, 0x0EB0, 0x0EB2, 0x0EB3, 0x0EBD, 0x0EBD, 0x0EC0, 0x0EC4, 0x0EC6, 0x0EC6, 0x0ED0, 0x0ED9, 0x0EDC, 0x0EDD, 0x0F00, 0x0F17, 0x0F1A, 0x0F34, 0x0F36, 0x0F36, 0x0F38, 0x0F38, 0x0F3E, 0x0F47, 0x0F49, 0x0F6A, 0x0F7F, 0x0F7F, 0x0F85, 0x0F85, 0x0F88, 0x0F8B, 0x0FBE, 0x0FC5, 0x0FC7, 0x0FCC, 0x0FCF, 0x0FCF, 0x1000, 0x1021, 0x1023, 0x1027, 0x1029, 0x102A, 0x102C, 0x102C, 0x1031, 0x1031, 0x1038, 0x1038, 0x1040, 0x1057, 0x10A0, 0x10C5, 0x10D0, 0x10F8, 0x10FB, 0x10FB, 0x1100, 0x1159, 0x115F, 0x11A2, 0x11A8, 0x11F9, 0x1200, 0x1206, 0x1208, 0x1246, 0x1248, 0x1248, 0x124A, 0x124D, 0x1250, 0x1256, 0x1258, 0x1258, 0x125A, 0x125D, 0x1260, 0x1286, 0x1288, 0x1288, 0x128A, 0x128D, 0x1290, 0x12AE, 0x12B0, 0x12B0, 0x12B2, 0x12B5, 0x12B8, 0x12BE, 0x12C0, 0x12C0, 0x12C2, 0x12C5, 0x12C8, 0x12CE, 0x12D0, 0x12D6, 0x12D8, 0x12EE, 0x12F0, 0x130E, 0x1310, 0x1310, 0x1312, 0x1315, 0x1318, 0x131E, 0x1320, 0x1346, 0x1348, 0x135A, 0x1361, 0x137C, 0x13A0, 0x13F4, 0x1401, 0x1676, 0x1681, 0x169A, 0x16A0, 0x16F0, 0x1700, 0x170C, 0x170E, 0x1711, 0x1720, 0x1731, 0x1735, 0x1736, 0x1740, 0x1751, 0x1760, 0x176C, 0x176E, 0x1770, 0x1780, 0x17B6, 0x17BE, 0x17C5, 0x17C7, 0x17C8, 0x17D4, 0x17DA, 0x17DC, 0x17DC, 0x17E0, 0x17E9, 0x1810, 0x1819, 0x1820, 0x1877, 0x1880, 0x18A8, 0x1E00, 0x1E9B, 0x1EA0, 0x1EF9, 0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D, 0x1F50, 0x1F57, 0x1F59, 0x1F59, 0x1F5B, 0x1F5B, 0x1F5D, 0x1F5D, 0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FBC, 0x1FBE, 0x1FBE, 0x1FC2, 0x1FC4, 0x1FC6, 0x1FCC, 0x1FD0, 0x1FD3, 0x1FD6, 0x1FDB, 0x1FE0, 0x1FEC, 0x1FF2, 0x1FF4, 0x1FF6, 0x1FFC, 0x200E, 0x200E, 0x2071, 0x2071, 0x207F, 0x207F, 0x2102, 0x2102, 0x2107, 0x2107, 0x210A, 0x2113, 0x2115, 0x2115, 0x2119, 0x211D, 0x2124, 0x2124, 0x2126, 0x2126, 0x2128, 0x2128, 0x212A, 0x212D, 0x212F, 0x2131, 0x2133, 0x2139, 0x213D, 0x213F, 0x2145, 0x2149, 0x2160, 0x2183, 0x2336, 0x237A, 0x2395, 0x2395, 0x249C, 0x24E9, 0x3005, 0x3007, 0x3021, 0x3029, 0x3031, 0x3035, 0x3038, 0x303C, 0x3041, 0x3096, 0x309D, 0x309F, 0x30A1, 0x30FA, 0x30FC, 0x30FF, 0x3105, 0x312C, 0x3131, 0x318E, 0x3190, 0x31B7, 0x31F0, 0x321C, 0x3220, 0x3243, 0x3260, 0x327B, 0x327F, 0x32B0, 0x32C0, 0x32CB, 0x32D0, 0x32FE, 0x3300, 0x3376, 0x337B, 0x33DD, 0x33E0, 0x33FE, 0x3400, 0x4DB5, 0x4E00, 0x9FA5, 0xA000, 0xA48C, 0xAC00, 0xD7A3, 0xD800, 0xFA2D, 0xFA30, 0xFA6A, 0xFB00, 0xFB06, 0xFB13, 0xFB17, 0xFF21, 0xFF3A, 0xFF41, 0xFF5A, 0xFF66, 0xFFBE, 0xFFC2, 0xFFC7, 0xFFCA, 0xFFCF, 0xFFD2, 0xFFD7, 0xFFDA, 0xFFDC, 0x10300, 0x1031E, 0x10320, 0x10323, 0x10330, 0x1034A, 0x10400, 0x10425, 0x10428, 0x1044D, 0x1D000, 0x1D0F5, 0x1D100, 0x1D126, 0x1D12A, 0x1D166, 0x1D16A, 0x1D172, 0x1D183, 0x1D184, 0x1D18C, 0x1D1A9, 0x1D1AE, 0x1D1DD, 0x1D400, 0x1D454, 0x1D456, 0x1D49C, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4A9, 0x1D4AC, 0x1D4AE, 0x1D4B9, 0x1D4BB, 0x1D4BB, 0x1D4BD, 0x1D4C0, 0x1D4C2, 0x1D4C3, 0x1D4C5, 0x1D505, 0x1D507, 0x1D50A, 0x1D50D, 0x1D514, 0x1D516, 0x1D51C, 0x1D51E, 0x1D539, 0x1D53B, 0x1D53E, 0x1D540, 0x1D544, 0x1D546, 0x1D546, 0x1D54A, 0x1D550, 0x1D552, 0x1D6A3, 0x1D6A8, 0x1D7C9, 0x20000, 0x2A6D6, 0x2F800, 0x2FA1D, 0xF0000, 0xFFFFD, 0x100000, 0x10FFFD }; /* End of stringprep tables */ /* Is the given Unicode codepoint in the given table of ranges? */ #define IS_CODE_IN_TABLE(code, map) is_code_in_table(code, map, lengthof(map)) static int codepoint_range_cmp(const void *a, const void *b) { const pg_wchar *key = (const pg_wchar *) a; const pg_wchar *range = (const pg_wchar *) b; if (*key < range[0]) return -1; /* less than lower bound */ if (*key > range[1]) return 1; /* greater than upper bound */ return 0; /* within range */ } static bool is_code_in_table(pg_wchar code, const pg_wchar *map, int mapsize) { Assert(mapsize % 2 == 0); if (code < map[0] || code > map[mapsize - 1]) return false; if (bsearch(&code, map, mapsize / 2, sizeof(pg_wchar) * 2, codepoint_range_cmp)) return true; else return false; } /* * Calculate the length in characters of a null-terminated UTF-8 string. * * Returns -1 if the input is not valid UTF-8. */ static int pg_utf8_string_len(const char *source) { const unsigned char *p = (const unsigned char *) source; int l; int num_chars = 0; while (*p) { l = pg_utf_mblen(p); if (!pg_utf8_islegal(p, l)) return -1; p += l; num_chars++; } return num_chars; } /* * Returns true if the input string is pure ASCII. */ static bool pg_is_ascii_string(const char *p) { while (*p) { if (IS_HIGHBIT_SET(*p)) return false; p++; } return true; } /* * pg_saslprep - Normalize a password with SASLprep. * * SASLprep requires the input to be in UTF-8 encoding, but PostgreSQL * supports many encodings, so we don't blindly assume that. pg_saslprep * will check if the input looks like valid UTF-8, and returns * SASLPREP_INVALID_UTF8 if not. * * If the string contains prohibited characters (or more precisely, if the * output string would contain prohibited characters after normalization), * returns SASLPREP_PROHIBITED. * * On success, returns SASLPREP_SUCCESS, and the normalized string in * *output. * * In frontend, the normalized string is malloc'd, and the caller is * responsible for freeing it. If an allocation fails, returns * SASLPREP_OOM. In backend, the normalized string is palloc'd instead, * and a failed allocation leads to ereport(ERROR). */ pg_saslprep_rc pg_saslprep(const char *input, char **output) { pg_wchar *input_chars = NULL; pg_wchar *output_chars = NULL; int input_size; char *result; int result_size; int count; int i; bool contains_RandALCat; unsigned char *p; pg_wchar *wp; /* Ensure we return *output as NULL on failure */ *output = NULL; /* Check that the password isn't stupendously long */ if (strlen(input) > MAX_PASSWORD_LENGTH) { #ifndef FRONTEND ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("password too long"))); #else return SASLPREP_OOM; #endif } /* * Quick check if the input is pure ASCII. An ASCII string requires no * further processing. */ if (pg_is_ascii_string(input)) { *output = STRDUP(input); if (!(*output)) goto oom; return SASLPREP_SUCCESS; } /* * Convert the input from UTF-8 to an array of Unicode codepoints. * * This also checks that the input is a legal UTF-8 string. */ input_size = pg_utf8_string_len(input); if (input_size < 0) return SASLPREP_INVALID_UTF8; input_chars = ALLOC((input_size + 1) * sizeof(pg_wchar)); if (!input_chars) goto oom; p = (unsigned char *) input; for (i = 0; i < input_size; i++) { input_chars[i] = utf8_to_unicode(p); p += pg_utf_mblen(p); } input_chars[i] = (pg_wchar) '\0'; /* * The steps below correspond to the steps listed in [RFC3454], Section * "2. Preparation Overview" */ /* * 1) Map -- For each character in the input, check if it has a mapping * and, if so, replace it with its mapping. */ count = 0; for (i = 0; i < input_size; i++) { pg_wchar code = input_chars[i]; if (IS_CODE_IN_TABLE(code, non_ascii_space_ranges)) input_chars[count++] = 0x0020; else if (IS_CODE_IN_TABLE(code, commonly_mapped_to_nothing_ranges)) { /* map to nothing */ } else input_chars[count++] = code; } input_chars[count] = (pg_wchar) '\0'; input_size = count; if (input_size == 0) goto prohibited; /* don't allow empty password */ /* * 2) Normalize -- Normalize the result of step 1 using Unicode * normalization. */ output_chars = unicode_normalize(UNICODE_NFKC, input_chars); if (!output_chars) goto oom; /* * 3) Prohibit -- Check for any characters that are not allowed in the * output. If any are found, return an error. */ for (i = 0; i < input_size; i++) { pg_wchar code = input_chars[i]; if (IS_CODE_IN_TABLE(code, prohibited_output_ranges)) goto prohibited; if (IS_CODE_IN_TABLE(code, unassigned_codepoint_ranges)) goto prohibited; } /* * 4) Check bidi -- Possibly check for right-to-left characters, and if * any are found, make sure that the whole string satisfies the * requirements for bidirectional strings. If the string does not satisfy * the requirements for bidirectional strings, return an error. * * [RFC3454], Section "6. Bidirectional Characters" explains in more * detail what that means: * * "In any profile that specifies bidirectional character handling, all * three of the following requirements MUST be met: * * 1) The characters in section 5.8 MUST be prohibited. * * 2) If a string contains any RandALCat character, the string MUST NOT * contain any LCat character. * * 3) If a string contains any RandALCat character, a RandALCat character * MUST be the first character of the string, and a RandALCat character * MUST be the last character of the string." */ contains_RandALCat = false; for (i = 0; i < input_size; i++) { pg_wchar code = input_chars[i]; if (IS_CODE_IN_TABLE(code, RandALCat_codepoint_ranges)) { contains_RandALCat = true; break; } } if (contains_RandALCat) { pg_wchar first = input_chars[0]; pg_wchar last = input_chars[input_size - 1]; for (i = 0; i < input_size; i++) { pg_wchar code = input_chars[i]; if (IS_CODE_IN_TABLE(code, LCat_codepoint_ranges)) goto prohibited; } if (!IS_CODE_IN_TABLE(first, RandALCat_codepoint_ranges) || !IS_CODE_IN_TABLE(last, RandALCat_codepoint_ranges)) goto prohibited; } /* * Finally, convert the result back to UTF-8. */ result_size = 0; for (wp = output_chars; *wp; wp++) { unsigned char buf[4]; unicode_to_utf8(*wp, buf); result_size += pg_utf_mblen(buf); } result = ALLOC(result_size + 1); if (!result) goto oom; /* * There are no error exits below here, so the error exit paths don't need * to worry about possibly freeing "result". */ p = (unsigned char *) result; for (wp = output_chars; *wp; wp++) { unicode_to_utf8(*wp, p); p += pg_utf_mblen(p); } Assert((char *) p == result + result_size); *p = '\0'; FREE(input_chars); FREE(output_chars); *output = result; return SASLPREP_SUCCESS; prohibited: if (input_chars) FREE(input_chars); if (output_chars) FREE(output_chars); return SASLPREP_PROHIBITED; oom: if (input_chars) FREE(input_chars); if (output_chars) FREE(output_chars); return SASLPREP_OOM; } pgbouncer-1.24.1/src/common/scram-common.c0000644000175000000000000001552214777762222015332 00000000000000/*------------------------------------------------------------------------- * scram-common.c * Shared frontend/backend code for SCRAM authentication * * This contains the common low-level functions needed in both frontend and * backend, for implement the Salted Challenge Response Authentication * Mechanism (SCRAM), per IETF's RFC 5802. * * Portions Copyright (c) 2017-2020, PostgreSQL Global Development Group * * IDENTIFICATION * src/common/scram-common.c * *------------------------------------------------------------------------- */ //#ifndef FRONTEND //#include "postgres.h" //#else //#include "postgres_fe.h" //#endif #include "system.h" #include "common/postgres_compat.h" #include "usual/crypto/sha256.h" #include "usual/endian.h" #include "common/base64.h" #include "common/scram-common.h" #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5C /* * Calculate HMAC per RFC2104. * * The hash function used is SHA-256. */ void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen) { uint8 k_ipad[SHA256_HMAC_B]; int i; uint8 keybuf[SCRAM_KEY_LEN]; /* * If the key is longer than the block size (64 bytes for SHA-256), pass * it through SHA-256 once to shrink it down. */ if (keylen > SHA256_HMAC_B) { pg_sha256_ctx sha256_ctx; pg_sha256_init(&sha256_ctx); pg_sha256_update(&sha256_ctx, key, keylen); pg_sha256_final(&sha256_ctx, keybuf); key = keybuf; keylen = SCRAM_KEY_LEN; } memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B); memset(ctx->k_opad, HMAC_OPAD, SHA256_HMAC_B); for (i = 0; i < keylen; i++) { k_ipad[i] ^= key[i]; ctx->k_opad[i] ^= key[i]; } /* tmp = H(K XOR ipad, text) */ pg_sha256_init(&ctx->sha256ctx); pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B); } /* * Update HMAC calculation * The hash function used is SHA-256. */ void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen) { pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen); } /* * Finalize HMAC calculation. * The hash function used is SHA-256. */ void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx) { uint8 h[SCRAM_KEY_LEN]; pg_sha256_final(&ctx->sha256ctx, h); /* H(K XOR opad, tmp) */ pg_sha256_init(&ctx->sha256ctx); pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B); pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN); pg_sha256_final(&ctx->sha256ctx, result); } /* * Calculate SaltedPassword. * * The password should already be normalized by SASLprep. */ void scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result) { int password_len = strlen(password); uint32 one = pg_hton32(1); int i, j; uint8 Ui[SCRAM_KEY_LEN]; uint8 Ui_prev[SCRAM_KEY_LEN]; scram_HMAC_ctx hmac_ctx; /* * Iterate hash calculation of HMAC entry using given salt. This is * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom * function. */ /* First iteration */ scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); scram_HMAC_update(&hmac_ctx, salt, saltlen); scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)); scram_HMAC_final(Ui_prev, &hmac_ctx); memcpy(result, Ui_prev, SCRAM_KEY_LEN); /* Subsequent iterations */ for (i = 2; i <= iterations; i++) { scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN); scram_HMAC_final(Ui, &hmac_ctx); for (j = 0; j < SCRAM_KEY_LEN; j++) result[j] ^= Ui[j]; memcpy(Ui_prev, Ui, SCRAM_KEY_LEN); } } /* * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is * not included in the hash). */ void scram_H(const uint8 *input, int len, uint8 *result) { pg_sha256_ctx ctx; pg_sha256_init(&ctx); pg_sha256_update(&ctx, input, len); pg_sha256_final(&ctx, result); } /* * Calculate ClientKey. */ void scram_ClientKey(const uint8 *salted_password, uint8 *result) { scram_HMAC_ctx ctx; scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")); scram_HMAC_final(result, &ctx); } /* * Calculate ServerKey. */ void scram_ServerKey(const uint8 *salted_password, uint8 *result) { scram_HMAC_ctx ctx; scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")); scram_HMAC_final(result, &ctx); } /* * Construct a SCRAM secret, for storing in pg_authid.rolpassword. * * The password should already have been processed with SASLprep, if necessary! * * If iterations is 0, default number of iterations is used. The result is * palloc'd or malloc'd, so caller is responsible for freeing it. */ char * scram_build_secret(const char *salt, int saltlen, int iterations, const char *password) { uint8 salted_password[SCRAM_KEY_LEN]; uint8 stored_key[SCRAM_KEY_LEN]; uint8 server_key[SCRAM_KEY_LEN]; char *result; char *p; int maxlen; int encoded_salt_len; int encoded_stored_len; int encoded_server_len; int encoded_result; if (iterations <= 0) iterations = SCRAM_DEFAULT_ITERATIONS; /* Calculate StoredKey and ServerKey */ scram_SaltedPassword(password, salt, saltlen, iterations, salted_password); scram_ClientKey(salted_password, stored_key); scram_H(stored_key, SCRAM_KEY_LEN, stored_key); scram_ServerKey(salted_password, server_key); /*---------- * The format is: * SCRAM-SHA-256$:$: *---------- */ encoded_salt_len = pg_b64_enc_len(saltlen); encoded_stored_len = pg_b64_enc_len(SCRAM_KEY_LEN); encoded_server_len = pg_b64_enc_len(SCRAM_KEY_LEN); maxlen = strlen("SCRAM-SHA-256") + 1 + 10 + 1 /* iteration count */ + encoded_salt_len + 1 /* Base64-encoded salt */ + encoded_stored_len + 1 /* Base64-encoded StoredKey */ + encoded_server_len + 1; /* Base64-encoded ServerKey */ #ifdef FRONTEND result = malloc(maxlen); if (!result) return NULL; #else result = palloc(maxlen); #endif p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations); /* salt */ encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len); if (encoded_result < 0) { #ifdef FRONTEND free(result); return NULL; #else elog(ERROR, "could not encode salt"); #endif } p += encoded_result; *(p++) = '$'; /* stored key */ encoded_result = pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p, encoded_stored_len); if (encoded_result < 0) { #ifdef FRONTEND free(result); return NULL; #else elog(ERROR, "could not encode stored key"); #endif } p += encoded_result; *(p++) = ':'; /* server key */ encoded_result = pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p, encoded_server_len); if (encoded_result < 0) { #ifdef FRONTEND free(result); return NULL; #else elog(ERROR, "could not encode server key"); #endif } p += encoded_result; *(p++) = '\0'; Assert(p - result <= maxlen); return result; } pgbouncer-1.24.1/src/common/pgstrcasecmp.c0000644000175000000000000000344314777762222015431 00000000000000/*------------------------------------------------------------------------- * * pgstrcasecmp.c * Portable SQL-like case-independent comparisons and conversions. * * SQL99 specifies Unicode-aware case normalization, which we don't yet * have the infrastructure for. Instead we use tolower() to provide a * locale-aware translation. However, there are some locales where this * is not right either (eg, Turkish may do strange things with 'i' and * 'I'). Our current compromise is to use tolower() for characters with * the high bit set, and use an ASCII-only downcasing for 7-bit * characters. * * NB: this code should match downcase_truncate_identifier() in scansup.c. * * We also provide strict ASCII-only case conversion functions, which can * be used to implement C/POSIX case folding semantics no matter what the * C library thinks the locale is. * * * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group * * src/port/pgstrcasecmp.c * *------------------------------------------------------------------------- */ #include "common/postgres_compat.h" #include "common/builtins.h" /* * Case-independent comparison of two not-necessarily-null-terminated strings. * At most n bytes will be examined from each string. */ int pg_strncasecmp(const char *s1, const char *s2, size_t n) { while (n-- > 0) { unsigned char ch1 = (unsigned char) *s1++; unsigned char ch2 = (unsigned char) *s2++; if (ch1 != ch2) { if (ch1 >= 'A' && ch1 <= 'Z') ch1 += 'a' - 'A'; else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) ch1 = tolower(ch1); if (ch2 >= 'A' && ch2 <= 'Z') ch2 += 'a' - 'A'; else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) ch2 = tolower(ch2); if (ch1 != ch2) return (int) ch1 - (int) ch2; } if (ch1 == 0) break; } return 0; } pgbouncer-1.24.1/src/common/base64.c0000644000175000000000000001150014777762222014013 00000000000000/*------------------------------------------------------------------------- * * base64.c * Encoding and decoding routines for base64 without whitespace. * * Copyright (c) 2001-2020, PostgreSQL Global Development Group * * * IDENTIFICATION * src/common/base64.c * *------------------------------------------------------------------------- */ //#ifndef FRONTEND //#include "postgres.h" //#else //#include "postgres_fe.h" //#endif #include "system.h" #include "common/postgres_compat.h" #include "common/base64.h" /* * BASE64 */ static const char _base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const int8 b64lookup[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, }; /* * pg_b64_encode * * Encode into base64 the given string. Returns the length of the encoded * string, and -1 in the event of an error with the result buffer zeroed * for safety. */ int pg_b64_encode(const char *src, int len, char *dst, int dstlen) { char *p; const char *s, *end = src + len; int pos = 2; uint32 buf = 0; s = src; p = dst; while (s < end) { buf |= (unsigned char) *s << (pos << 3); pos--; s++; /* write it out */ if (pos < 0) { /* * Leave if there is an overflow in the area allocated for the * encoded string. */ if ((p - dst + 4) > dstlen) goto error; *p++ = _base64[(buf >> 18) & 0x3f]; *p++ = _base64[(buf >> 12) & 0x3f]; *p++ = _base64[(buf >> 6) & 0x3f]; *p++ = _base64[buf & 0x3f]; pos = 2; buf = 0; } } if (pos != 2) { /* * Leave if there is an overflow in the area allocated for the encoded * string. */ if ((p - dst + 4) > dstlen) goto error; *p++ = _base64[(buf >> 18) & 0x3f]; *p++ = _base64[(buf >> 12) & 0x3f]; *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '='; *p++ = '='; } Assert((p - dst) <= dstlen); return p - dst; error: memset(dst, 0, dstlen); return -1; } /* * pg_b64_decode * * Decode the given base64 string. Returns the length of the decoded * string on success, and -1 in the event of an error with the result * buffer zeroed for safety. */ int pg_b64_decode(const char *src, int len, char *dst, int dstlen) { const char *srcend = src + len, *s = src; char *p = dst; char c; int b = 0; uint32 buf = 0; int pos = 0, end = 0; while (s < srcend) { c = *s++; /* Leave if a whitespace is found */ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') goto error; if (c == '=') { /* end sequence */ if (!end) { if (pos == 2) end = 1; else if (pos == 3) end = 2; else { /* * Unexpected "=" character found while decoding base64 * sequence. */ goto error; } } b = 0; } else { b = -1; if (c > 0 && c < 127) b = b64lookup[(unsigned char) c]; if (b < 0) { /* invalid symbol found */ goto error; } } /* add it to buffer */ buf = (buf << 6) + b; pos++; if (pos == 4) { /* * Leave if there is an overflow in the area allocated for the * decoded string. */ if ((p - dst + 1) > dstlen) goto error; *p++ = (buf >> 16) & 255; if (end == 0 || end > 1) { /* overflow check */ if ((p - dst + 1) > dstlen) goto error; *p++ = (buf >> 8) & 255; } if (end == 0 || end > 2) { /* overflow check */ if ((p - dst + 1) > dstlen) goto error; *p++ = buf & 255; } buf = 0; pos = 0; } } if (pos != 0) { /* * base64 end sequence is invalid. Input data is missing padding, is * truncated or is otherwise corrupted. */ goto error; } Assert((p - dst) <= dstlen); return p - dst; error: memset(dst, 0, dstlen); return -1; } /* * pg_b64_enc_len * * Returns to caller the length of the string if it were encoded with * base64 based on the length provided by caller. This is useful to * estimate how large a buffer allocation needs to be done before doing * the actual encoding. */ int pg_b64_enc_len(int srclen) { /* 3 bytes will be converted to 4 */ return (srclen + 2) * 4 / 3; } /* * pg_b64_dec_len * * Returns to caller the length of the string if it were to be decoded * with base64, based on the length given by caller. This is useful to * estimate how large a buffer allocation needs to be done before doing * the actual decoding. */ int pg_b64_dec_len(int srclen) { return (srclen * 3) >> 2; } pgbouncer-1.24.1/src/janitor.c0000644000175000000000000006301514777762222013115 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Periodic maintenance. */ #include "bouncer.h" #include /* do full maintenance 3x per second */ static struct timeval full_maint_period = {0, USEC / 3}; static struct event full_maint_ev; extern bool any_user_level_server_timeout_set; extern bool any_user_level_client_timeout_set; /* close all sockets in server list */ static void close_server_list(struct StatList *sk_list, const char *reason) { struct List *item, *tmp; PgSocket *server; statlist_for_each_safe(item, sk_list, tmp) { server = container_of(item, PgSocket, head); disconnect_server(server, true, "%s", reason); } } static void close_client_list(struct StatList *sk_list, const char *reason) { struct List *item, *tmp; PgSocket *client; statlist_for_each_safe(item, sk_list, tmp) { client = container_of(item, PgSocket, head); disconnect_client(client, true, "%s", reason); } } bool suspend_socket(PgSocket *sk, bool force_suspend) { if (sk->suspended) return true; if (sbuf_is_empty(&sk->sbuf)) { if (sbuf_pause(&sk->sbuf)) sk->suspended = true; } if (sk->suspended || !force_suspend) return sk->suspended; if (is_server_socket(sk)) disconnect_server(sk, true, "suspend_timeout"); else disconnect_client(sk, true, "suspend_timeout"); return true; } /* suspend all sockets in socket list */ static int suspend_socket_list(struct StatList *list, bool force_suspend) { struct List *item, *tmp; PgSocket *sk; int active = 0; statlist_for_each_safe(item, list, tmp) { sk = container_of(item, PgSocket, head); if (!suspend_socket(sk, force_suspend)) active++; } return active; } /* resume all suspended sockets in socket list */ static void resume_socket_list(struct StatList *list) { struct List *item, *tmp; PgSocket *sk; statlist_for_each_safe(item, list, tmp) { sk = container_of(item, PgSocket, head); if (sk->suspended) { sk->suspended = false; sbuf_continue(&sk->sbuf); } } } /* resume all suspended sockets in all pools */ static void resume_sockets(void) { struct List *item; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; resume_socket_list(&pool->active_client_list); resume_socket_list(&pool->active_server_list); resume_socket_list(&pool->idle_server_list); resume_socket_list(&pool->used_server_list); } } /* resume pools and listen sockets */ void resume_all(void) { resume_sockets(); resume_pooler(); } /* * send test/reset query to server if needed */ static void launch_recheck(PgPool *pool) { const char *q = cf_server_check_query; bool need_check = true; PgSocket *server; bool res = true; /* find clean server */ while (1) { server = first_socket(&pool->used_server_list); if (!server) return; if (server->ready) break; disconnect_server(server, true, "idle server got dirty"); } /* is the check needed? */ if (q == NULL || q[0] == 0) { need_check = false; } else if (cf_server_check_delay > 0) { usec_t now = get_cached_time(); if (now - server->request_time < cf_server_check_delay) need_check = false; } if (need_check) { /* send test query, wait for result */ slog_debug(server, "P: checking: %s", q); change_server_state(server, SV_TESTED); SEND_generic(res, server, PqMsg_Query, "s", q); if (!res) disconnect_server(server, false, "test query failed"); } else { /* make immediately available */ release_server(server); } } /* * make servers available */ static void per_loop_activate(PgPool *pool) { struct List *item, *tmp; PgSocket *client; int sv_tested, sv_used; /* if there is a cancel request waiting, open a new connection */ if (!statlist_empty(&pool->waiting_cancel_req_list)) { launch_new_connection(pool, /* evict_if_needed= */ true); return; } /* see if any server have been freed */ sv_tested = statlist_count(&pool->tested_server_list); sv_used = statlist_count(&pool->used_server_list); statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); if (client->replication) { /* * For replication connections we always launch * a new connection, but we continue with the loop, * because there might be normal clients waiting too. */ launch_new_connection(pool, /* evict_if_needed= */ true); } else if (!statlist_empty(&pool->idle_server_list)) { /* db not fully initialized after reboot */ if (client->wait_for_welcome && !pool->welcome_msg_ready) { launch_new_connection(pool, /* evict_if_needed= */ true); continue; } /* there is a ready server already */ activate_client(client); } else if (sv_tested > 0) { /* some connections are in testing process */ --sv_tested; } else if (sv_used > 0) { /* ask for more connections to be tested */ launch_recheck(pool); --sv_used; } else { /* not enough connections */ launch_new_connection(pool, /* evict_if_needed= */ true); break; } } } /* * pause active clients */ static int per_loop_pause(PgPool *pool) { int active = 0; if (pool->db->admin) return 0; close_server_list(&pool->idle_server_list, "pause mode"); close_server_list(&pool->used_server_list, "pause mode"); close_server_list(&pool->new_server_list, "pause mode"); active += statlist_count(&pool->active_server_list); active += statlist_count(&pool->tested_server_list); return active; } /* * suspend active clients and servers */ static int per_loop_suspend(PgPool *pool, bool force_suspend) { int active = 0; if (pool->db->admin) return 0; active += suspend_socket_list(&pool->active_client_list, force_suspend); /* this list is not suspendable, but still need force_suspend and counting */ active += suspend_socket_list(&pool->waiting_client_list, force_suspend); if (active) per_loop_activate(pool); if (!active) { active += suspend_socket_list(&pool->active_server_list, force_suspend); active += suspend_socket_list(&pool->idle_server_list, force_suspend); /* as all clients are done, no need for them */ close_server_list(&pool->tested_server_list, "close unsafe file descriptors on suspend"); close_server_list(&pool->used_server_list, "close unsafe file descriptors on suspend"); } return active; } /* * Count the servers in server_list that have close_needed set. */ static int count_close_needed(struct StatList *server_list) { struct List *item; PgSocket *server; int count = 0; statlist_for_each(item, server_list) { server = container_of(item, PgSocket, head); if (server->close_needed) count++; } return count; } /* * Per-loop tasks for WAIT_CLOSE */ static int per_loop_wait_close(PgPool *pool) { int count = 0; if (pool->db->admin) return 0; count += count_close_needed(&pool->active_server_list); count += count_close_needed(&pool->idle_server_list); count += count_close_needed(&pool->new_server_list); count += count_close_needed(&pool->tested_server_list); count += count_close_needed(&pool->used_server_list); return count; } /* * this function is called for each event loop. */ void per_loop_maint(void) { struct List *item; PgPool *pool; int active_count = 0; int waiting_count = 0; bool partial_pause = false; bool partial_wait = false; bool force_suspend = false; if (cf_pause_mode == P_SUSPEND && cf_suspend_timeout > 0) { usec_t stime = get_cached_time() - g_suspend_start; if (stime >= cf_suspend_timeout) force_suspend = true; } statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; switch (cf_pause_mode) { case P_NONE: if (pool->db->db_paused) { partial_pause = true; active_count += per_loop_pause(pool); } else { per_loop_activate(pool); } break; case P_PAUSE: active_count += per_loop_pause(pool); break; case P_SUSPEND: active_count += per_loop_suspend(pool, force_suspend); break; } if (pool->db->db_wait_close) { partial_wait = true; waiting_count += per_loop_wait_close(pool); } } switch (cf_pause_mode) { case P_SUSPEND: if (force_suspend) { close_client_list(&login_client_list, "suspend_timeout"); } else { active_count += statlist_count(&login_client_list); } /* fallthrough */ case P_PAUSE: if (!active_count) admin_pause_done(); break; case P_NONE: if (partial_pause && !active_count) admin_pause_done(); break; } if (partial_wait && !waiting_count) admin_wait_close_done(); } /* maintaining clients in pool */ static void pool_client_maint(PgPool *pool) { struct List *item, *tmp; usec_t now = get_cached_time(); PgSocket *client; PgGlobalUser *user; usec_t age; usec_t effective_client_idle_timeout; /* force client_idle_timeout */ if (cf_client_idle_timeout > 0 || any_user_level_client_timeout_set) { statlist_for_each_safe(item, &pool->active_client_list, tmp) { client = container_of(item, PgSocket, head); Assert(client->state == CL_ACTIVE); if (client->link) continue; user = client->login_user_credentials->global_user; effective_client_idle_timeout = cf_client_idle_timeout; if (user->client_idle_timeout > 0) effective_client_idle_timeout = user->client_idle_timeout; if (now - client->request_time > effective_client_idle_timeout) disconnect_client(client, true, "client_idle_timeout"); } } /* force timeouts for waiting queries */ if (cf_query_timeout > 0 || cf_query_wait_timeout > 0) { statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); Assert(client->state == CL_WAITING || client->state == CL_WAITING_LOGIN); if (client->query_start == 0) { age = now - client->request_time; /* log_warning("query_start==0"); */ } else { age = now - client->query_start; } if (cf_shutdown == SHUTDOWN_WAIT_FOR_SERVERS) { disconnect_client(client, true, "server shutting down"); } else if (cf_query_timeout > 0 && age > cf_query_timeout) { disconnect_client(client, true, "query_timeout"); } else if (cf_query_wait_timeout > 0 && age > cf_query_wait_timeout) { disconnect_client(client, true, "query_wait_timeout"); } } } /* apply cancel_wait_timeout for cancel connections */ if (cf_cancel_wait_timeout > 0) { statlist_for_each_safe(item, &pool->waiting_cancel_req_list, tmp) { client = container_of(item, PgSocket, head); Assert(client->state == CL_WAITING_CANCEL); age = now - client->request_time; if (age > cf_cancel_wait_timeout) disconnect_client(client, false, "cancel_wait_timeout"); } } /* apply client_login_timeout to clients waiting for welcome pkt */ if (cf_client_login_timeout > 0 && !pool->welcome_msg_ready) { statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); if (!client->wait_for_welcome) continue; age = now - client->connect_time; if (age > cf_client_login_timeout) disconnect_client(client, true, "client_login_timeout (server down)"); } } } /* maintaining clients in peer pool */ static void peer_pool_client_maint(PgPool *pool) { struct List *item, *tmp; usec_t now = get_cached_time(); PgSocket *client; usec_t age; if (cf_cancel_wait_timeout > 0) { statlist_for_each_safe(item, &pool->waiting_cancel_req_list, tmp) { client = container_of(item, PgSocket, head); Assert(client->state == CL_WAITING_CANCEL); age = now - client->request_time; if (age > cf_cancel_wait_timeout) disconnect_client(client, false, "cancel_wait_timeout"); } } } static void check_unused_servers(PgPool *pool, struct StatList *slist, bool idle_test) { usec_t now = get_cached_time(); usec_t server_lifetime = pool_server_lifetime(pool); struct List *item, *tmp; usec_t idle, age; PgSocket *server; /* disconnect idle servers if needed */ statlist_for_each_safe(item, slist, tmp) { server = container_of(item, PgSocket, head); age = now - server->connect_time; idle = now - server->request_time; if (server->close_needed) { disconnect_server(server, true, "database configuration changed"); } else if (server->state == SV_IDLE && !server->ready) { disconnect_server(server, true, "SV_IDLE server got dirty"); } else if (server->state == SV_USED && !server->ready) { disconnect_server(server, true, "SV_USED server got dirty"); } else if (cf_server_idle_timeout > 0 && idle > cf_server_idle_timeout && (pool_min_pool_size(pool) == 0 || pool_connected_server_count(pool) > pool_min_pool_size(pool))) { disconnect_server(server, true, "server idle timeout"); } else if (age >= server_lifetime) { if (life_over(server)) { disconnect_server(server, true, "server lifetime over"); pool->last_lifetime_disconnect = now; } } else if (cf_pause_mode == P_PAUSE) { disconnect_server(server, true, "pause mode"); } else if (idle_test && *cf_server_check_query) { if (idle > cf_server_check_delay) change_server_state(server, SV_USED); } } } /* * Check pool size, close conns if too many. Makes pooler * react faster to the case when admin decreased pool size. */ static void check_pool_size(PgPool *pool) { PgSocket *server; int cur = pool_connected_server_count(pool); int many = cur - (pool_pool_size(pool) + pool_res_pool_size(pool)); Assert(pool_pool_size(pool) >= 0); if (pool_pool_size(pool) > 0) { while (many > 0) { server = first_socket(&pool->used_server_list); if (!server) server = first_socket(&pool->idle_server_list); if (!server) break; disconnect_server(server, true, "too many servers in the pool"); many--; cur--; } } /* launch extra connections to satisfy min_pool_size */ if (cur < pool_min_pool_size(pool) && cur < pool_pool_size(pool) && cf_pause_mode == P_NONE && cf_reboot == 0 && (pool_client_count(pool) > 0 || pool->db->forced_user_credentials != NULL)) { log_debug("launching new connection to satisfy min_pool_size"); launch_new_connection(pool, /* evict_if_needed= */ false); } } /* maintain servers in a pool */ static void pool_server_maint(PgPool *pool) { struct List *item, *tmp; usec_t now = get_cached_time(); PgSocket *server; /* find and disconnect idle servers */ check_unused_servers(pool, &pool->used_server_list, 0); check_unused_servers(pool, &pool->tested_server_list, 0); check_unused_servers(pool, &pool->idle_server_list, 1); statlist_for_each_safe(item, &pool->active_server_list, tmp) { server = container_of(item, PgSocket, head); Assert(server->state == SV_ACTIVE); /* * Disconnect active servers without outstanding requests if * server_fast_close is set. This only applies to session * pooling. */ if (cf_server_fast_close && server->ready && server->close_needed) disconnect_server(server, true, "database configuration changed"); /* * Always disconnect close_needed replication servers. These * connections are expected to be very long lived (possibly * indefinitely), so waiting until the session/transaction is * over is not an option. */ if (server->replication && server->close_needed) disconnect_server(server, true, "database configuration changed"); } /* handle query_timeout and idle_transaction_timeout */ if (cf_query_timeout > 0 || cf_idle_transaction_timeout > 0 || any_user_level_timeout_set) { statlist_for_each_safe(item, &pool->active_server_list, tmp) { usec_t age_client, age_server; usec_t effective_query_timeout; usec_t effective_idle_transaction_timeout; usec_t user_query_timeout; usec_t user_idle_transaction_timeout; server = container_of(item, PgSocket, head); Assert(server->state == SV_ACTIVE); if (server->ready) continue; /* * Note the different age calculations: * query_timeout counts from the last request * of the client (the client started the * query), idle_transaction_timeout counts * from the last request of the server (the * server sent the idle information). */ age_client = now - server->link->request_time; age_server = now - server->request_time; user_idle_transaction_timeout = server->login_user_credentials->global_user->idle_transaction_timeout; user_query_timeout = server->login_user_credentials->global_user->query_timeout; effective_idle_transaction_timeout = cf_idle_transaction_timeout; effective_query_timeout = cf_query_timeout; if (user_idle_transaction_timeout > 0) effective_idle_transaction_timeout = user_idle_transaction_timeout; if (user_query_timeout > 0) effective_query_timeout = user_query_timeout; if (effective_query_timeout > 0 && age_client > effective_query_timeout) { disconnect_server(server, true, "query timeout"); } else if (effective_idle_transaction_timeout > 0 && server->idle_tx && age_server > effective_idle_transaction_timeout) { disconnect_server(server, true, "idle transaction timeout"); } } } /* find connections that got connect, but could not log in */ if (cf_server_connect_timeout > 0) { statlist_for_each_safe(item, &pool->new_server_list, tmp) { usec_t age; server = container_of(item, PgSocket, head); Assert(server->state == SV_LOGIN); age = now - server->connect_time; if (age > cf_server_connect_timeout) disconnect_server(server, true, "connect timeout"); } } check_pool_size(pool); } /* maintain servers in a peer pool */ static void peer_pool_server_maint(PgPool *pool) { struct List *item, *tmp; usec_t now = get_cached_time(); PgSocket *server; /* * find connections that got connected, but could not log in. For normal * pools we only compare against server_connect_timeout for these servers, * but since peer pools are only for sending cancellations we also compare * against cancel_wait_timeout here. */ if (cf_server_connect_timeout > 0 || cf_cancel_wait_timeout > 0) { statlist_for_each_safe(item, &pool->new_server_list, tmp) { usec_t age; server = container_of(item, PgSocket, head); Assert(server->state == SV_LOGIN); age = now - server->connect_time; if (cf_server_connect_timeout > 0 && age > cf_server_connect_timeout) { disconnect_server(server, true, "connect timeout"); } else if (cf_cancel_wait_timeout > 0 && age > cf_cancel_wait_timeout) { disconnect_server(server, true, "cancel_wait_timeout"); } } } } static void cleanup_client_logins(void) { struct List *item, *tmp; PgSocket *client; usec_t age; usec_t now = get_cached_time(); if (cf_client_login_timeout <= 0) return; statlist_for_each_safe(item, &login_client_list, tmp) { client = container_of(item, PgSocket, head); age = now - client->connect_time; if (age > cf_client_login_timeout) disconnect_client(client, true, "client_login_timeout"); } } static void cleanup_inactive_autodatabases(void) { struct List *item, *tmp; PgDatabase *db; usec_t age; usec_t now = get_cached_time(); if (cf_autodb_idle_timeout <= 0) return; /* now kill the old ones */ statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_paused) continue; age = now - db->inactive_time; if (age > cf_autodb_idle_timeout) { kill_database(db); } else { break; } } } /* full-scale maintenance, done only occasionally */ static void do_full_maint(evutil_socket_t sock, short flags, void *arg) { struct List *item, *tmp; PgPool *pool; PgDatabase *db; static unsigned int seq; seq++; /* * Avoid doing anything that may surprise other pgbouncer. */ if (cf_pause_mode == P_SUSPEND) return; /* * Creating new pools to enable `min_pool_size` enforcement even if * there are no active clients. * * If clients never connect there won't be a pool to maintain the * min_pool_size on, which means we have to proactively create a pool, * so that it can be maintained. * * We are doing this for databases with forced users only, to reduce * the risk of creating connections in unexpected ways, where there are * many users. _ */ statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); if (database_min_pool_size(db) > 0 && db->forced_user_credentials != NULL) { get_pool(db, db->forced_user_credentials); } } statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; pool_server_maint(pool); pool_client_maint(pool); /* is autodb active? */ if (pool->db->db_auto && pool->db->inactive_time == 0) { if (pool_client_count(pool) > 0 || pool_server_count(pool) > 0) pool->db->active_stamp = seq; } } statlist_for_each_safe(item, &peer_pool_list, tmp) { pool = container_of(item, PgPool, head); peer_pool_server_maint(pool); peer_pool_client_maint(pool); } /* find inactive autodbs */ statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_auto && db->inactive_time == 0) { if (db->active_stamp == seq) continue; db->inactive_time = get_cached_time(); statlist_remove(&database_list, &db->head); statlist_append(&autodatabase_idle_list, &db->head); } } cleanup_inactive_autodatabases(); cleanup_client_logins(); if (cf_shutdown == SHUTDOWN_WAIT_FOR_SERVERS && get_active_server_count() == 0) { log_info("server connections dropped, exiting"); cf_shutdown = SHUTDOWN_IMMEDIATE; event_base_loopbreak(pgb_event_base); return; } if (cf_shutdown == SHUTDOWN_WAIT_FOR_CLIENTS && get_active_client_count() == 0) { log_info("client connections dropped, exiting"); cf_shutdown = SHUTDOWN_IMMEDIATE; event_base_loopbreak(pgb_event_base); return; } adns_zone_cache_maint(adns); } /* first-time initialization */ void janitor_setup(void) { /* launch maintenance */ event_assign(&full_maint_ev, pgb_event_base, -1, EV_PERSIST, do_full_maint, NULL); if (event_add(&full_maint_ev, &full_maint_period) < 0) log_warning("event_add failed: %s", strerror(errno)); } void kill_pool(PgPool *pool) { const char *reason = "database removed"; close_client_list(&pool->active_client_list, reason); close_client_list(&pool->waiting_client_list, reason); close_client_list(&pool->active_cancel_req_list, reason); close_client_list(&pool->waiting_cancel_req_list, reason); close_server_list(&pool->active_server_list, reason); close_server_list(&pool->active_cancel_server_list, reason); close_server_list(&pool->being_canceled_server_list, reason); close_server_list(&pool->idle_server_list, reason); close_server_list(&pool->used_server_list, reason); close_server_list(&pool->tested_server_list, reason); close_server_list(&pool->new_server_list, reason); pktbuf_free(pool->welcome_msg); list_del(&pool->map_head); statlist_remove(&pool_list, &pool->head); varcache_clean(&pool->orig_vars); slab_free(var_list_cache, pool->orig_vars.var_list); slab_free(pool_cache, pool); } void kill_peer_pool(PgPool *pool) { const char *reason = "peer removed"; close_client_list(&pool->active_cancel_req_list, reason); close_client_list(&pool->waiting_cancel_req_list, reason); close_server_list(&pool->active_cancel_server_list, reason); close_server_list(&pool->new_server_list, reason); pktbuf_free(pool->welcome_msg); list_del(&pool->map_head); statlist_remove(&peer_pool_list, &pool->head); varcache_clean(&pool->orig_vars); slab_free(var_list_cache, pool->orig_vars.var_list); slab_free(peer_pool_cache, pool); } void kill_database(PgDatabase *db) { PgPool *pool; struct List *item, *tmp; log_warning("dropping database '%s' as it does not exist anymore or inactive auto-database", db->name); statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db == db) kill_pool(pool); } pktbuf_free(db->startup_params); free(db->host); if (db->forced_user_credentials) slab_free(credentials_cache, db->forced_user_credentials); free(db->connect_query); if (db->inactive_time) { statlist_remove(&autodatabase_idle_list, &db->head); } else { statlist_remove(&database_list, &db->head); } if (db->auth_dbname) free((void *)db->auth_dbname); if (db->auth_query) free((void *)db->auth_query); aatree_destroy(&db->user_tree); slab_free(db_cache, db); } void kill_peer(PgDatabase *db) { PgPool *pool; struct List *item, *tmp; log_warning("dropping peer %s as it does not exist anymore", db->name); statlist_for_each_safe(item, &peer_pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db == db) kill_peer_pool(pool); } free(db->host); statlist_remove(&peer_list, &db->head); slab_free(peer_cache, db); } /* as [pgbouncer] section can be loaded after databases, there's need for review */ void config_postprocess(void) { struct List *item, *tmp; PgDatabase *db; statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_dead) { kill_database(db); continue; } } statlist_for_each_safe(item, &peer_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_dead) { kill_peer(db); continue; } } } pgbouncer-1.24.1/src/client.c0000644000175000000000000014675414777762222012741 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Client connection handling */ #include "bouncer.h" #include "pam.h" #include "scram.h" #include "common/builtins.h" #include #include static const char *hdr2hex(const struct MBuf *data, char *buf, unsigned buflen) { const uint8_t *bin = data->data + data->read_pos; unsigned int dlen; dlen = mbuf_avail_for_read(data); return bin2hex(bin, dlen, buf, buflen); } /* * Get authentication database for the current client. The order of preference is: * client->db->auth_dbname: per client authentication database * cf_auth_dbname: global authentication database * client->db: client database * * NOTE: if the authentication database is not found or it is disabled, client * will be disconnected. */ PgDatabase *prepare_auth_database(PgSocket *client) { PgDatabase *auth_db = NULL; const char *auth_dbname = client->db->auth_dbname ? client->db->auth_dbname : cf_auth_dbname; if (!auth_dbname) { auth_db = client->db; } else { auth_db = find_or_register_database(client, auth_dbname); } if (!auth_db) { slog_error(client, "authentication database \"%s\" is not configured.", auth_dbname); disconnect_client(client, true, "bouncer config error"); return NULL; } if (auth_db->db_disabled) { disconnect_client( client, true, "authentication database \"%s\" is disabled", auth_dbname); return NULL; } if (auth_db->admin) { slog_error(client, "cannot use the reserved \"%s\" database as an auth_dbname", auth_db->dbname); disconnect_client(client, true, "bouncer config error"); return NULL; } return auth_db; } static bool check_client_passwd(PgSocket *client, const char *passwd) { PgCredentials *user = client->login_user_credentials; int auth_type = client->client_auth_type; if (user->mock_auth) return false; /* disallow empty passwords */ if (!*user->passwd) return false; switch (auth_type) { case AUTH_TYPE_PLAIN: switch (get_password_type(user->passwd)) { case PASSWORD_TYPE_PLAINTEXT: return strcmp(user->passwd, passwd) == 0; case PASSWORD_TYPE_MD5: { char md5[MD5_PASSWD_LEN + 1]; if (!pg_md5_encrypt(passwd, user->name, strlen(user->name), md5)) return false; return strcmp(user->passwd, md5) == 0; } case PASSWORD_TYPE_SCRAM_SHA_256: return scram_verify_plain_password(client, user->name, passwd, user->passwd); default: return false; } case AUTH_TYPE_MD5: { char *stored_passwd; char md5[MD5_PASSWD_LEN + 1]; if (strlen(passwd) != MD5_PASSWD_LEN) return false; /* * The client sends * 'md5'+md5(md5(password+username)+salt). The stored * password is either 'md5'+md5(password+username) or * plain text. If the latter, we compute the inner * md5() call first. */ if (get_password_type(user->passwd) == PASSWORD_TYPE_PLAINTEXT) { if (!pg_md5_encrypt(user->passwd, user->name, strlen(user->name), md5)) return false; stored_passwd = md5; } else { stored_passwd = user->passwd; } if (!pg_md5_encrypt(stored_passwd + 3, (char *)client->tmp_login_salt, 4, md5)) return false; return strcmp(md5, passwd) == 0; } } return false; } static bool send_client_authreq(PgSocket *client) { int res; int auth_type = client->client_auth_type; if (auth_type == AUTH_TYPE_MD5) { uint8_t saltlen = 4; get_random_bytes((void *)client->tmp_login_salt, saltlen); SEND_generic(res, client, PqMsg_AuthenticationRequest, "ib", AUTH_REQ_MD5, client->tmp_login_salt, saltlen); } else if (auth_type == AUTH_TYPE_PLAIN || auth_type == AUTH_TYPE_PAM) { SEND_generic(res, client, PqMsg_AuthenticationRequest, "i", AUTH_REQ_PASSWORD); } else if (auth_type == AUTH_TYPE_SCRAM_SHA_256) { SEND_generic(res, client, PqMsg_AuthenticationRequest, "iss", AUTH_REQ_SASL, "SCRAM-SHA-256", ""); } else { return false; } if (!res) { slog_noise(client, "No authentication response received"); disconnect_client(client, false, "failed to send auth req"); } else { slog_noise(client, "Auth request sent successfully"); } return res; } /* * Returns true if the client is currently trying to send an auth query to the * server. */ bool sending_auth_query(PgSocket *client) { return client->wait_for_user_conn || client->wait_for_user; } static void start_auth_query(PgSocket *client, const char *username) { int res; PktBuf *buf; const char *auth_query = client->db->auth_query ? client->db->auth_query : cf_auth_query; /* have to fetch user info from db */ PgDatabase *auth_db = prepare_auth_database(client); if (!auth_db) return; client->pool = get_pool(auth_db, client->db->auth_user_credentials); if (!client->pool) { disconnect_client(client, true, "no memory for authentication pool"); return; } client->wait_for_user_conn = true; if (!find_server(client)) { return; } slog_noise(client, "doing auth_conn query: %s", auth_query); client->wait_for_user_conn = false; client->wait_for_user = true; if (!sbuf_pause(&client->sbuf)) { release_server(client->link); disconnect_client(client, true, "pause failed"); return; } client->link->ready = false; /* * Add outstanding request, so that the server is closed if the client * disconnects before the auth_query completes. */ if (!add_outstanding_request(client, PqMsg_Sync, RA_SKIP)) { disconnect_server(client->link, true, "out of memory"); return; } res = 0; buf = pktbuf_dynamic(512); if (buf) { pktbuf_write_ExtQuery(buf, auth_query, 1, username); res = pktbuf_send_immediate(buf, client->link); pktbuf_free(buf); /* * Should do instead: * res = pktbuf_send_queued(buf, client->link); * but that needs better integration with SBuf. */ } if (!res) disconnect_server(client->link, false, "unable to send auth_query"); } static bool login_via_cert(PgSocket *client, struct HBARule *rule) { struct tls *tls = client->sbuf.tls; if (!tls) { slog_error(client, "TLS connection required"); goto fail; } if (!tls_peer_cert_provided(client->sbuf.tls)) { slog_error(client, "TLS client certificate required"); goto fail; } if (client->login_user_credentials->mock_auth) goto fail; log_debug("TLS cert login: %s", tls_peer_cert_subject(client->sbuf.tls)); if (rule && rule->identmap) { struct List *el; struct Mapping *mapping; bool mapped = false; list_for_each(el, &rule->identmap->mappings) { mapping = container_of(el, struct Mapping, node); if (!tls_peer_cert_contains_name(client->sbuf.tls, mapping->system_user_name)) { continue; } if (!(mapping->name_flags & NAME_ALL)) { if (strcmp(client->login_user_credentials->name, mapping->postgres_user_name)) { continue; } } slog_noise(client, "ident map: %s %s %s", rule->identmap->map_name, mapping->system_user_name, mapping->postgres_user_name); mapped = true; break; } if (!mapped) { slog_error(client, "ident map: %s does not have a match", rule->identmap->map_name); goto fail; } } else if (!tls_peer_cert_contains_name(client->sbuf.tls, client->login_user_credentials->name)) { slog_error(client, "TLS certificate name mismatch"); goto fail; } /* login successful */ return finish_client_login(client); fail: disconnect_client(client, true, "certificate authentication failed"); return false; } static bool login_as_unix_peer(PgSocket *client, struct HBARule *rule) { if (!pga_is_unix(&client->remote_addr)) goto fail; if (client->login_user_credentials->mock_auth) goto fail; if (rule && rule->identmap) { struct List *el; struct Mapping *mapping; bool mapped = false; list_for_each(el, &rule->identmap->mappings) { mapping = container_of(el, struct Mapping, node); if (check_unix_peer_name(sbuf_socket(&client->sbuf), mapping->system_user_name)) { if ((mapping->name_flags & NAME_ALL) || strcmp(mapping->postgres_user_name, client->login_user_credentials->name) == 0) { slog_noise(client, "ident map '%s' is applied", rule->identmap->map_name); mapped = true; break; } } } if (!mapped) { slog_error(client, "ident map %s cannot be matched", rule->identmap->map_name); goto fail; } } else { if (!check_unix_peer_name(sbuf_socket(&client->sbuf), client->login_user_credentials->name)) goto fail; } return finish_client_login(client); fail: disconnect_client(client, true, "unix socket login rejected"); return false; } static bool finish_set_pool(PgSocket *client, bool takeover) { bool ok = false; int auth; struct HBARule *rule = NULL; if (!client->login_user_credentials->mock_auth && !client->db->fake) { PgCredentials *pool_user_credentials; if (client->db->forced_user_credentials) pool_user_credentials = client->db->forced_user_credentials; else pool_user_credentials = client->login_user_credentials; client->pool = get_pool(client->db, pool_user_credentials); if (!client->pool) { disconnect_client(client, true, "no memory for pool"); return false; } } if (cf_log_connections) { if (client->sbuf.tls) { char infobuf[96] = ""; tls_get_connection_info(client->sbuf.tls, infobuf, sizeof infobuf); slog_info(client, "login attempt: db=%s user=%s tls=%s replication=%s", client->db->name, client->login_user_credentials->name, infobuf, replication_type_parameters[client->replication]); } else { slog_info(client, "login attempt: db=%s user=%s tls=no replication=%s", client->db->name, client->login_user_credentials->name, replication_type_parameters[client->replication]); } } if (takeover) return true; if (client->pool && client->pool->db->admin) { if (!admin_post_login(client)) return false; } if (client->own_user) return finish_client_login(client); auth = cf_auth_type; if (auth == AUTH_TYPE_HBA) { rule = hba_eval( parsed_hba, &client->remote_addr, !!client->sbuf.tls, client->replication, client->db->name, client->login_user_credentials->name); if (!rule) { disconnect_client(client, true, "no authentication method is found"); return false; } slog_noise(client, "HBA Line %d is matched", rule->hba_linenr); auth = rule->rule_method; } if (auth == AUTH_TYPE_MD5) { if (get_password_type(client->login_user_credentials->passwd) == PASSWORD_TYPE_SCRAM_SHA_256) auth = AUTH_TYPE_SCRAM_SHA_256; } /* remember method */ client->client_auth_type = auth; switch (auth) { case AUTH_TYPE_ANY: ok = finish_client_login(client); break; case AUTH_TYPE_TRUST: if (client->login_user_credentials->mock_auth) disconnect_client(client, true, "\"trust\" authentication failed"); else ok = finish_client_login(client); break; case AUTH_TYPE_PLAIN: case AUTH_TYPE_MD5: case AUTH_TYPE_PAM: case AUTH_TYPE_SCRAM_SHA_256: ok = send_client_authreq(client); break; case AUTH_TYPE_CERT: ok = login_via_cert(client, rule); break; case AUTH_TYPE_PEER: ok = login_as_unix_peer(client, rule); break; default: disconnect_client(client, true, "login rejected"); ok = false; } return ok; } bool check_db_connection_count(PgSocket *client) { if (!client->contributes_db_client_count) { client->contributes_db_client_count = true; client->db->client_connection_count++; } if (database_max_client_connections(client->db) <= 0) return true; if (client->db->client_connection_count <= database_max_client_connections(client->db)) return true; if (client->db->admin && strlist_contains(cf_admin_users, client->login_user_credentials->name)) return true; log_debug("set_pool: db '%s' full (%d >= %d)", client->db->name, client->db->client_connection_count, client->db->max_db_client_connections); disconnect_client(client, true, "client connections exceeded (max_db_client_connections)"); return false; } bool check_user_connection_count(PgSocket *client) { int client_connection_count; int max_user_client_connections; /* Check client_connection count limit */ if (!client->login_user_credentials) return true; if (!client->login_user_credentials->global_user) return true; if (!client->user_connection_counted) { client->login_user_credentials->global_user->client_connection_count++; client->user_connection_counted = 1; } if (client->db->admin && strlist_contains(cf_admin_users, client->login_user_credentials->name)) { return true; } max_user_client_connections = user_client_max_connections(client->login_user_credentials->global_user); if (max_user_client_connections == 0) return true; client_connection_count = client->login_user_credentials->global_user->client_connection_count; if (client_connection_count <= max_user_client_connections) return true; log_debug("set_pool: user '%s' full (%d >= %d)", client->login_user_credentials->name, client_connection_count, max_user_client_connections); disconnect_client(client, true, "client connections exceeded (max_user_client_connections)"); return false; } bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) { Assert((password && takeover) || (!password && !takeover)); /* find database */ client->db = find_or_register_database(client, dbname); if (!client->db) { client->db = calloc(1, sizeof(*client->db)); client->db->fake = true; strlcpy(client->db->name, dbname, sizeof(client->db->name)); } if (client->db->admin) { if (admin_pre_login(client, username)) return finish_set_pool(client, takeover); } /* avoid dealing with invalid data below, and give an * appropriate error message */ if (strlen(username) >= MAX_USERNAME) { disconnect_client(client, true, "username too long"); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } if (password && strlen(password) >= MAX_PASSWORD) { disconnect_client(client, true, "password too long"); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } /* find user */ if (cf_auth_type == AUTH_TYPE_ANY) { /* ignore requested user */ if (client->db->forced_user_credentials == NULL) { slog_error(client, "auth_type=any requires forced user"); disconnect_client(client, true, "bouncer config error"); return false; } client->login_user_credentials = client->db->forced_user_credentials; if (!check_db_connection_count(client)) return false; if (!check_user_connection_count(client)) return false; } else if (cf_auth_type == AUTH_TYPE_PAM) { if (client->db->auth_user_credentials) { slog_error(client, "PAM can't be used together with database authentication"); disconnect_client(client, true, "bouncer config error"); return false; } /* Password will be set after successful authentication when not in takeover mode */ client->login_user_credentials = add_pam_credentials(username, password); if (!check_db_connection_count(client)) return false; if (!client->login_user_credentials) { slog_error(client, "set_pool(): failed to allocate new PAM user"); disconnect_client(client, true, "bouncer resources exhaustion"); return false; } if (!check_user_connection_count(client)) { return false; } } else { client->login_user_credentials = find_global_credentials(username); if (!check_db_connection_count(client)) return false; if (!check_user_connection_count(client)) return false; if (!client->login_user_credentials || client->login_user_credentials->dynamic_passwd) { PgGlobalUser *global_user; /* * If the login user specified by the client * does not exist or if it has no entry in auth_file, * check if an auth_user is set and if so, send off * an auth_query. If no auth_user is set for the db, * see if the global auth_user is set and use that. */ if (!client->db->auth_user_credentials && cf_auth_user) { client->db->auth_user_credentials = find_or_add_new_global_credentials(cf_auth_user, ""); if (client->db->auth_user_credentials == NULL) { slog_error(client, "set_pool(): failed to allocate a new global credentials"); disconnect_client(client, true, "bouncer resources exhaustion"); return false; } } if (client->db->auth_user_credentials) { if (client->db->fake) { slog_debug(client, "not running auth_query because database is fake"); } else { if (takeover) { client->login_user_credentials = add_dynamic_credentials(client->db, username, password); if (!check_db_connection_count(client)) return false; if (!check_user_connection_count(client)) return false; return finish_set_pool(client, takeover); } start_auth_query(client, username); return false; } } slog_info(client, "no such user: %s", username); client->login_user_credentials = calloc(1, sizeof(*client->login_user_credentials)); /* * For users that we are already tracking, we want to * track this correctly as a connection count. But for * users that we don't know about at all, we don't want * to create a new global user. That's why we use * find_global_user instead of * find_or_add_new_global_user. */ global_user = find_global_user(username); if (global_user) client->login_user_credentials->global_user = global_user; if (!check_db_connection_count(client)) return false; client->login_user_credentials->mock_auth = true; safe_strcpy(client->login_user_credentials->name, username, sizeof(client->login_user_credentials->name)); if (!check_user_connection_count(client)) { return false; } } } return finish_set_pool(client, takeover); } bool handle_auth_query_response(PgSocket *client, PktHdr *pkt) { uint16_t columns; uint32_t length; const char *username, *password; PgCredentials credentials; PgSocket *server = client->link; switch (pkt->type) { case PqMsg_RowDescription: if (!mbuf_get_uint16be(&pkt->data, &columns)) { disconnect_server(server, false, "bad packet"); return false; } if (columns != 2u) { disconnect_server(server, false, "expected 2 columns from auth_query, not %hu", columns); return false; } break; case PqMsg_DataRow: memset(&credentials, 0, sizeof(credentials)); if (!mbuf_get_uint16be(&pkt->data, &columns)) { disconnect_server(server, false, "bad packet"); return false; } if (columns != 2u) { disconnect_server(server, false, "expected 2 columns from auth_query, not %hu", columns); return false; } if (!mbuf_get_uint32be(&pkt->data, &length)) { disconnect_server(server, false, "bad packet"); return false; } if (length == (uint32_t)-1) { disconnect_server(server, false, "auth_query response contained null user name"); return false; } if (!mbuf_get_chars(&pkt->data, length, &username)) { disconnect_server(server, false, "bad packet"); return false; } if (sizeof(credentials.name) - 1 < length) length = sizeof(credentials.name) - 1; memcpy(credentials.name, username, length); if (!mbuf_get_uint32be(&pkt->data, &length)) { disconnect_server(server, false, "bad packet"); return false; } if (length == (uint32_t)-1) { /* * NULL - set an md5 password with an impossible value, * so that nothing will ever match */ password = "md5"; length = 3; } else { if (!mbuf_get_chars(&pkt->data, length, &password)) { disconnect_server(server, false, "bad packet"); return false; } } if (sizeof(credentials.passwd) - 1 < length) length = sizeof(credentials.passwd) - 1; memcpy(credentials.passwd, password, length); slog_debug(client, "successfully parsed auth_query response for user %s", credentials.name); client->login_user_credentials = add_dynamic_credentials(client->db, credentials.name, credentials.passwd); if (!check_user_connection_count(client)) { return false; } if (!client->login_user_credentials) { disconnect_server(server, false, "unable to allocate new user for auth"); return false; } break; case PqMsg_NoticeResponse: break; case PqMsg_CommandComplete: break; case PqMsg_ParseComplete: break; case PqMsg_BindComplete: break; case PqMsg_ParameterStatus: break; case PqMsg_ReadyForQuery: sbuf_prepare_skip(&client->link->sbuf, pkt->len); if (!client->login_user_credentials) { if (cf_log_connections) slog_info(client, "login failed: db=%s", client->db->name); /* * TODO: Currently no mock authentication when * using auth_query/auth_user; we just abort * with a revealing message to the client. * The main problem is that at this point we * don't know the original user name anymore * to do that. As a workaround, the * auth_query could be written in a way that * it returns a fake user and password if the * requested user doesn't exist. */ disconnect_client(client, true, "no such user"); } else { slog_noise(client, "auth query complete"); client->link->resetting = true; sbuf_continue(&client->sbuf); } /* * either sbuf_continue or disconnect_client could disconnect the server * way down in their bowels of other callbacks. so check that, and * return appropriately (similar to reuse_on_release) */ if (server->state == SV_FREE || server->state == SV_JUSTFREE) return false; return true; case PqMsg_ErrorResponse: log_server_error("S: error in auth_query", pkt); disconnect_server(server, false, "error response from auth_query"); return false; default: disconnect_server(server, false, "unexpected response from auth_query"); return false; } sbuf_prepare_skip(&server->sbuf, pkt->len); return true; } /* * read_escaped_token reads a token that might be escaped using backslashes * from the escaped_string_ptr. The token is written in unescaped form to the * unescaped_token buffer. escape_string_ptr is set to the character right * after the token. */ static bool read_escaped_token(const char **escaped_string_ptr, struct MBuf *unescaped_token) { const char *position = *escaped_string_ptr; const char *unwritten_start = position; while (*position) { if (*position == '\\') { if (!mbuf_write(unescaped_token, unwritten_start, position - unwritten_start)) return false; position++; unwritten_start = position; if (!*position) break; } else if (isspace(*position)) { break; } position++; } if (!mbuf_write(unescaped_token, unwritten_start, position - unwritten_start)) return false; if (!mbuf_write_byte(unescaped_token, '\0')) return false; *escaped_string_ptr = position; return true; } /* * set_startup_options takes the value of the "options" startup parameter * and uses it to set the parameters that are embedded in this value. * * It only supports the following type of PostgreSQL command line argument: * -c config=value * * The reason that we don't support all arguments is to keep the parsing simple * an this is by far the argument that's most commonly used in practice in the * options startup parameter. Also all other postgres command line arguments * can be rewritten to this form. * * NOTE: it's possible to supply "options" in ignore_startup_parameters, which * results in all unknown options being ignored. This is for historical reasons, * because it was supported like that in the past. */ static bool set_startup_options(PgSocket *client, const char *options) { char arg_buf[400]; struct MBuf arg; const char *position = options; if (client->replication) { /* * Since replication clients will be bound 1-to-1 to a server * connection, we can support any configuration flags and * fields in the options startup parameter. Because we can * simply send the exact same value for the options parameter * when opening the replication connection to the server. This * allows us to also support GUCs that don't have the * GUC_REPORT flag, specifically extra_float_digits which is a * configuration that is set by CREATE SUBSCRIPTION in the * options parameter. * * First free it, because set_startup_options might be called * multiple times in some cases. One of these being when * auth_user is enabled. */ free(client->startup_options); client->startup_options = strdup(options); if (!client->startup_options) disconnect_client(client, true, "out of memory"); return true; } mbuf_init_fixed_writer(&arg, arg_buf, sizeof(arg_buf)); slog_debug(client, "received options: %s", options); while (*position) { const char *start_position = position; const char *key_string, *value_string; char *equals; mbuf_rewind_writer(&arg); position = cstr_skip_ws((char *) position); if (strncmp("-c", position, 2) == 0) { position += 2; position = cstr_skip_ws((char *) position); } else if (strncmp("--", position, 2) == 0) { position += 2; } else { goto fail; } if (!read_escaped_token(&position, &arg)) { if (arg.fixed) { mbuf_init_dynamic(&arg); position = start_position; continue; } disconnect_client(client, true, "out of memory"); mbuf_free(&arg); return false; } equals = strchr((char *) arg.data, '='); if (!equals) goto fail; *equals = '\0'; key_string = (const char *) arg.data; value_string = (const char *) equals + 1; if (varcache_set(&client->vars, key_string, value_string)) { slog_debug(client, "got var from options: %s=%s", key_string, value_string); } else if (strlist_contains(cf_ignore_startup_params, key_string) || strlist_contains(cf_ignore_startup_params, "options")) { slog_debug(client, "ignoring startup parameter from options: %s=%s", key_string, value_string); } else { slog_warning(client, "unsupported startup parameter in options: %s=%s", key_string, value_string); disconnect_client(client, true, "unsupported startup parameter in options: %s", key_string); mbuf_free(&arg); return false; } } mbuf_free(&arg); return true; fail: disconnect_client(client, true, "unsupported options startup parameter: only '-c config=val' and '--config=val' are allowed"); mbuf_free(&arg); return false; } static void set_appname(PgSocket *client, const char *app_name) { char buf[400], abuf[300]; const char *details; if (cf_application_name_add_host) { /* give app a name */ if (!app_name) app_name = "app"; /* add details */ details = pga_details(&client->remote_addr, abuf, sizeof(abuf)); snprintf(buf, sizeof(buf), "%s - %s", app_name, details); app_name = buf; } if (app_name) { slog_debug(client, "using application_name: %s", app_name); varcache_set(&client->vars, "application_name", app_name); } } /* * set_replication sets the replication field on the client according the given * replicationString. */ static bool set_replication(PgSocket *client, const char *replicationString) { bool replicationBool = false; if (strcmp(replicationString, "database") == 0) { client->replication = REPLICATION_LOGICAL; return true; } if (!parse_bool(replicationString, &replicationBool)) { return false; } client->replication = replicationBool ? REPLICATION_PHYSICAL : REPLICATION_NONE; return true; } static bool decide_startup_pool(PgSocket *client, PktHdr *pkt) { const char *username = NULL, *dbname = NULL; const char *key, *val; bool ok; bool appname_found = false; struct MBuf unsupported_protocol_extensions; int unsupported_protocol_extensions_count = 0; unsigned original_read_pos = pkt->data.read_pos; mbuf_init_dynamic(&unsupported_protocol_extensions); /* * First check if we're dealing with a replication connection. Because for * those we support some additional things when parsing the startup * parameters, specifically we support any arguments in the options startup * packet. */ while (1) { ok = mbuf_get_string(&pkt->data, &key); if (!ok || *key == 0) break; ok = mbuf_get_string(&pkt->data, &val); if (!ok) break; if (strcmp(key, "replication") == 0) { slog_debug(client, "got var: %s=%s", key, val); set_replication(client, val); } } pkt->data.read_pos = original_read_pos; while (1) { ok = mbuf_get_string(&pkt->data, &key); if (!ok || *key == 0) break; ok = mbuf_get_string(&pkt->data, &val); if (!ok) break; if (strcmp(key, "database") == 0) { slog_debug(client, "got var: %s=%s", key, val); dbname = val; } else if (strcmp(key, "user") == 0) { slog_debug(client, "got var: %s=%s", key, val); username = val; } else if (strcmp(key, "options") == 0) { if (!set_startup_options(client, val)) return false; } else if (strcmp(key, "application_name") == 0) { set_appname(client, val); appname_found = true; } else if (strcmp(key, "replication") == 0) { /* do nothing, already checked in the previous loop */ } else if (strncmp("_pq_.", key, 5) == 0) { slog_debug(client, "ignoring protocol extension parameter: %s=%s", key, val); unsupported_protocol_extensions_count++; if (!mbuf_write(&unsupported_protocol_extensions, key, strlen(key) + 1)) return false; } else if (varcache_set(&client->vars, key, val)) { slog_debug(client, "got var: %s=%s", key, val); } else if (strlist_contains(cf_ignore_startup_params, key)) { slog_debug(client, "ignoring startup parameter: %s=%s", key, val); } else { slog_warning(client, "unsupported startup parameter: %s=%s", key, val); disconnect_client(client, true, "unsupported startup parameter: %s", key); return false; } } if (!username || !username[0]) { disconnect_client(client, true, "no username supplied"); return false; } /* if missing dbname, default to username */ if (!dbname || !dbname[0]) dbname = username; /* create application_name if requested */ if (!appname_found) set_appname(client, NULL); /* check if limit allows, don't limit admin db nb: new incoming conn will be attached to PgSocket, thus get_active_client_count() counts it */ if (get_active_client_count() > cf_max_client_conn) { if (strcmp(dbname, "pgbouncer") != 0) { disconnect_client(client, true, "no more connections allowed (max_client_conn)"); return false; } } if (pkt->type == PKT_STARTUP_V3_UNSUPPORTED || unsupported_protocol_extensions_count > 0) { PktBuf *buf = pktbuf_dynamic(512); int res; pktbuf_write_NegotiateProtocolVersion( buf, unsupported_protocol_extensions_count, unsupported_protocol_extensions.data, unsupported_protocol_extensions.write_pos ); res = pktbuf_send_immediate(buf, client); if (!res) { pktbuf_free(buf); disconnect_client(client, false, "unable to send protocol negotiation packet"); return false; } } /* find pool */ return set_pool(client, dbname, username, NULL, false); } static bool scram_client_first(PgSocket *client, uint32_t datalen, const uint8_t *data) { char *ibuf; char *input; int res; PgCredentials *user = client->login_user_credentials; ibuf = malloc(datalen + 1); if (ibuf == NULL) return false; memcpy(ibuf, data, datalen); ibuf[datalen] = '\0'; input = ibuf; slog_debug(client, "SCRAM client-first-message = \"%s\"", input); if (!read_client_first_message(client, input, &client->scram_state.cbind_flag, &client->scram_state.client_first_message_bare, &client->scram_state.client_nonce)) goto failed; if (!user->mock_auth) { slog_debug(client, "stored secret = \"%s\"", user->passwd); switch (get_password_type(user->passwd)) { case PASSWORD_TYPE_MD5: slog_error(client, "SCRAM authentication failed: user has MD5 secret"); goto failed; case PASSWORD_TYPE_PLAINTEXT: case PASSWORD_TYPE_SCRAM_SHA_256: break; } } if (!build_server_first_message(&client->scram_state, user->name, user->mock_auth ? NULL : user->passwd)) goto failed; slog_debug(client, "SCRAM server-first-message = \"%s\"", client->scram_state.server_first_message); SEND_generic(res, client, PqMsg_AuthenticationRequest, "ib", AUTH_REQ_SASL_CONT, client->scram_state.server_first_message, strlen(client->scram_state.server_first_message)); free(ibuf); return res; failed: free(ibuf); return false; } static bool scram_client_final(PgSocket *client, uint32_t datalen, const uint8_t *data) { char *ibuf; char *input; const char *client_final_nonce = NULL; char *proof = NULL; char *server_final_message; int res; ibuf = malloc(datalen + 1); if (ibuf == NULL) return false; memcpy(ibuf, data, datalen); ibuf[datalen] = '\0'; input = ibuf; slog_debug(client, "SCRAM client-final-message = \"%s\"", input); if (!read_client_final_message(client, data, input, &client_final_nonce, &proof)) goto failed; slog_debug(client, "SCRAM client-final-message-without-proof = \"%s\"", client->scram_state.client_final_message_without_proof); if (!verify_final_nonce(&client->scram_state, client_final_nonce)) { slog_error(client, "invalid SCRAM response (nonce does not match)"); goto failed; } if (!verify_client_proof(&client->scram_state, proof) || !client->login_user_credentials) { slog_error(client, "password authentication failed"); goto failed; } server_final_message = build_server_final_message(&client->scram_state); if (!server_final_message) goto failed; slog_debug(client, "SCRAM server-final-message = \"%s\"", server_final_message); SEND_generic(res, client, PqMsg_AuthenticationRequest, "ib", AUTH_REQ_SASL_FIN, server_final_message, strlen(server_final_message)); free(server_final_message); free(proof); free(ibuf); return res; failed: free(proof); free(ibuf); return false; } /* decide on packets of client in login phase */ static bool handle_client_startup(PgSocket *client, PktHdr *pkt) { const char *passwd; const uint8_t *key; bool ok; bool is_unix = pga_is_unix(&client->remote_addr); SBuf *sbuf = &client->sbuf; /* don't tolerate partial packets */ if (incomplete_pkt(pkt)) { if (pkt->len > (unsigned) cf_sbuf_len) { /* * We need to handle the complete packet, but it is too * large to fit into our sbuf buffer size (determined * by the pkt_buf config). So now we need to fetch the * whole packet using our dynamically sized packet * buffering logic. */ client->packet_cb_state.flag = CB_WANT_COMPLETE_PACKET; sbuf_prepare_fetch(sbuf, pkt->len); return true; } else { /* * We need to handle the complete packet, but it fits * in our sbuf buffer, so we can simply return false to * indicate to sbuf to retry once it has received more * data */ return false; } } if (client->wait_for_welcome || client->wait_for_auth) { if (finish_client_login(client)) { if (client->packet_cb_state.flag != CB_HANDLE_COMPLETE_PACKET) { /* the packet was already parsed */ sbuf_prepare_skip(sbuf, pkt->len); } return true; } else { return false; } } switch (pkt->type) { case PKT_SSLREQ: slog_noise(client, "C: req SSL"); if (client->sbuf.tls) { disconnect_client(client, false, "SSL req inside SSL"); return false; } if (client_accept_sslmode != SSLMODE_DISABLED && !is_unix) { slog_noise(client, "P: SSL ack"); if (!sbuf_answer(&client->sbuf, "S", 1)) { disconnect_client(client, false, "failed to ack SSL"); return false; } if (!sbuf_tls_accept(&client->sbuf)) { disconnect_client(client, false, "failed to accept SSL"); return false; } break; } /* reject SSL attempt */ slog_noise(client, "P: nak"); if (!sbuf_answer(&client->sbuf, "N", 1)) { disconnect_client(client, false, "failed to nak SSL"); return false; } break; case PKT_GSSENCREQ: /* reject GSS encryption attempt */ slog_noise(client, "C: req GSS enc"); if (!sbuf_answer(&client->sbuf, "N", 1)) { disconnect_client(client, false, "failed to nak GSS enc"); return false; } break; case PKT_STARTUP_V2: disconnect_client(client, true, "old V2 protocol not supported"); return false; case PKT_STARTUP_V3_UNSUPPORTED: case PKT_STARTUP_V3: /* require SSL except on unix socket */ if (client_accept_sslmode >= SSLMODE_REQUIRE && !client->sbuf.tls && !is_unix) { disconnect_client(client, true, "SSL required"); return false; } if (client->pool && !sending_auth_query(client)) { disconnect_client(client, true, "client re-sent startup pkt"); return false; } if (client->wait_for_user) { client->wait_for_user = false; if (!finish_set_pool(client, false)) return false; } else if (!decide_startup_pool(client, pkt)) { return false; } break; case PqMsg_PasswordMessage: /* or SASLInitialResponse, or SASLResponse */ /* too early */ if (!client->login_user_credentials) { disconnect_client(client, true, "client password pkt before startup packet"); return false; } if (client->client_auth_type == AUTH_TYPE_SCRAM_SHA_256) { const char *mech; uint32_t length; const uint8_t *data; if (!client->scram_state.server_nonce) { /* process as SASLInitialResponse */ if (!mbuf_get_string(&pkt->data, &mech)) return false; slog_debug(client, "C: selected SASL mechanism: %s", mech); if (strcmp(mech, "SCRAM-SHA-256") != 0) { disconnect_client(client, true, "client selected an invalid SASL authentication mechanism"); return false; } if (!mbuf_get_uint32be(&pkt->data, &length)) return false; if (!mbuf_get_bytes(&pkt->data, length, &data)) return false; if (!scram_client_first(client, length, data)) { disconnect_client(client, true, "SASL authentication failed"); return false; } } else { /* process as SASLResponse */ length = mbuf_avail_for_read(&pkt->data); if (!mbuf_get_bytes(&pkt->data, length, &data)) return false; if (scram_client_final(client, length, data)) { /* save SCRAM keys for user */ if (!client->scram_state.adhoc && !client->db->fake) { memcpy(client->pool->user_credentials->scram_ClientKey, client->scram_state.ClientKey, sizeof(client->scram_state.ClientKey)); memcpy(client->pool->user_credentials->scram_ServerKey, client->scram_state.ServerKey, sizeof(client->scram_state.ServerKey)); client->pool->user_credentials->has_scram_keys = true; } free_scram_state(&client->scram_state); if (!finish_client_login(client)) return false; } else { disconnect_client(client, true, "SASL authentication failed"); return false; } } } else { /* process as PasswordMessage */ ok = mbuf_get_string(&pkt->data, &passwd); if (ok) { /* * Don't allow an empty password; see * PostgreSQL recv_password_packet(). */ if (!*passwd) { disconnect_client(client, true, "empty password returned by client"); return false; } if (client->client_auth_type == AUTH_TYPE_PAM) { if (!sbuf_pause(&client->sbuf)) { disconnect_client(client, true, "pause failed"); return false; } pam_auth_begin(client, passwd); return false; } if (check_client_passwd(client, passwd)) { if (!finish_client_login(client)) return false; } else { disconnect_client(client, true, "password authentication failed"); return false; } } } break; case PKT_CANCEL: if (mbuf_avail_for_read(&pkt->data) == BACKENDKEY_LEN && mbuf_get_bytes(&pkt->data, BACKENDKEY_LEN, &key)) { memcpy(client->cancel_key, key, BACKENDKEY_LEN); accept_cancel_request(client); } else { disconnect_client(client, false, "bad cancel request"); } return false; default: disconnect_client(client, false, "bad packet"); return false; } if (client->packet_cb_state.flag != CB_HANDLE_COMPLETE_PACKET) { sbuf_prepare_skip(sbuf, pkt->len); } client->request_time = get_cached_time(); return true; } /* decide on packets of logged-in client */ static bool handle_client_work(PgSocket *client, PktHdr *pkt) { SBuf *sbuf = &client->sbuf; int track_outstanding = false; PreparedStatementAction ps_action = PS_IGNORE; PgClosePacket close_packet; switch (pkt->type) { /* one-packet queries */ case PqMsg_Query: if (cf_disable_pqexec) { slog_error(client, "client used \"Query\" packet type"); disconnect_client(client, true, "PQexec disallowed"); return false; } track_outstanding = true; break; case PqMsg_FunctionCall: track_outstanding = true; break; /* request immediate response from server */ case PqMsg_Sync: track_outstanding = true; break; case PqMsg_Flush: break; /* copy end markers */ case PqMsg_CopyDone: case PqMsg_CopyFail: track_outstanding = true; break; /* * extended protocol allows server (and thus pooler) * to buffer packets until sync or flush is sent by client */ case PqMsg_Parse: track_outstanding = true; if (is_prepared_statements_enabled(client)) { ps_action = inspect_parse_packet(client, pkt); pkt_rewind_v3(pkt); } break; case PqMsg_Execute: track_outstanding = true; break; case PqMsg_Close: track_outstanding = true; if (is_prepared_statements_enabled(client)) { ps_action = inspect_describe_or_close_packet(client, pkt); pkt_rewind_v3(pkt); } break; case PqMsg_Bind: track_outstanding = true; if (is_prepared_statements_enabled(client)) { ps_action = inspect_bind_packet(client, pkt); pkt_rewind_v3(pkt); } break; case PqMsg_Describe: track_outstanding = true; if (is_prepared_statements_enabled(client)) { ps_action = inspect_describe_or_close_packet(client, pkt); pkt_rewind_v3(pkt); } break; case PqMsg_CopyData: break; /* client wants to go away */ default: slog_error(client, "unknown pkt from client: %u/0x%x", pkt->type, pkt->type); disconnect_client(client, true, "unknown pkt"); return false; case PqMsg_Terminate: disconnect_client(client, false, "client close request"); return false; } if (ps_action == PS_HANDLE_FULL_PACKET && incomplete_pkt(pkt)) { if (pkt->len > (unsigned) cf_sbuf_len) { /* * We need to handle the complete packet, but it is too * large to fit into our sbuf buffer size (determined * by the pkt_buf config). So now we need to fetch the * whole packet using our dynamically sized packet * buffering logic. */ client->packet_cb_state.flag = CB_WANT_COMPLETE_PACKET; sbuf_prepare_fetch(sbuf, pkt->len); return true; } else { /* * We need to handle the complete packet, but it fits * in our sbuf buffer, so we can simply return false to * indicate to sbuf to retry once it has received more * data */ return false; } } if (ps_action == PS_INSPECT_FAILED) { if (!incomplete_pkt(pkt)) { /* * We have the full packet, but still inspection * failed. That means the packet is plain wrong. */ slog_error(client, "failed to parse prepared statement packet type '%c'", pkt->type); disconnect_client(client, true, "failed to parse packet"); return false; } /* * We don't have the full packet yet, so probably inspection * failed because the required part of the packet was not * received yet. */ if (pkt->data.write_pos >= (unsigned) cf_sbuf_len) { /* * We've filled up our complete sbuf buffer with this * packet, but we still haven't been able to determine * if we should handle this packet or not. This is * quite unexpected, and probably means that the * name of the prepared statement is larger than * pkt_buf. */ client->packet_cb_state.flag = CB_WANT_COMPLETE_PACKET; sbuf_prepare_fetch(sbuf, pkt->len); return true; } /* * In all other cases we simply return false to indicate to * sbuf to retry after receiving more data. */ return false; } if (ps_action != PS_IGNORE && pkt->type == PqMsg_Close) { if (!unmarshall_close_packet(client, pkt, &close_packet)) return false; if (is_close_named_statement_packet(&close_packet)) { if (!handle_close_statement_command(client, pkt, &close_packet)) return false; client->pool->stats.client_bytes += pkt->len; /* No further processing required */ return true; } } /* update stats */ if (!client->query_start) { client->pool->stats.query_count++; client->query_start = get_cached_time(); } /* remember timestamp of the first query in a transaction */ if (!client->xact_start) { client->pool->stats.xact_count++; client->xact_start = client->query_start; } if (client->pool->db->admin) return admin_handle_client(client, pkt); /* acquire server */ if (!find_server(client)) return false; client->pool->stats.client_bytes += pkt->len; /* tag the server as dirty */ client->link->ready = false; client->link->idle_tx = false; if (ps_action != PS_IGNORE) { /* * All the following handle_xxx_packet functions below insert packets * into the packet queue through the extra_packets field of SBuf. This * requires that all previous data in the iobuf is flushed. So lets * just do that now, so that these functions don't have to worry about * doing that. */ if (!sbuf_flush(sbuf)) return false; switch (pkt->type) { case PqMsg_Parse: return handle_parse_command(client, pkt); case PqMsg_Bind: return handle_bind_command(client, pkt); case PqMsg_Describe: return handle_describe_command(client, pkt); } return true; } /* forward the packet */ if (track_outstanding) { if (!add_outstanding_request(client, pkt->type, RA_FORWARD)) { /* TODO disconnect oom */ return false; } } if (client->packet_cb_state.flag == CB_HANDLE_COMPLETE_PACKET) { /* * It's possible that the prepared statement logic required fully * buffering the packet for inspection purposes using our callback * packet buffering logic. But once it was fully buffered and the * inspection caused us to determine that we should simply forward the * packet (e.g. it was a Describe for a Portal). In those cases we * cannot simply call sbuf_prepare_send, because we already consumed * the packet using our callback logic. So now we need to first flush * the queue, and then re-queue the fully buffered packet using our * packet queueing logic. */ if (!sbuf_flush(sbuf)) return false; if (!sbuf_queue_full_packet(&client->sbuf, &client->link->sbuf, pkt)) { disconnect_client(client, true, "out of memory"); disconnect_server(client->link, true, "out of memory"); return false; } return true; } sbuf_prepare_send(sbuf, &client->link->sbuf, pkt->len); return true; } /* * expect_startup_packet chooses returns true if we expect a startup packet and * false if we expect a regular packet. */ static bool expect_startup_packet(PgSocket *client) { switch (client->state) { case CL_LOGIN: return true; break; case CL_ACTIVE: if (client->wait_for_welcome) return true; else return false; break; case CL_WAITING: fatal("why waiting client in client_proto()"); case CL_WAITING_CANCEL: case CL_ACTIVE_CANCEL: fatal("why canceling client in client_proto()"); default: fatal("bad client state: %d", client->state); } } /* callback from SBuf */ bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) { bool res = false; PgSocket *client = container_of(sbuf, PgSocket, sbuf); PktHdr pkt; Assert(!is_server_socket(client)); Assert(client->sbuf.sock); Assert(client->state != CL_FREE); /* may happen if close failed */ if (client->state == CL_JUSTFREE) return false; switch (evtype) { case SBUF_EV_CONNECT_OK: case SBUF_EV_CONNECT_FAILED: /* ^ those should not happen */ case SBUF_EV_RECV_FAILED: /* * Don't log error if client disconnects right away, * could be monitoring probe. */ if (client->state == CL_LOGIN && mbuf_avail_for_read(data) == 0) disconnect_client(client, false, NULL); else disconnect_client(client, false, "client unexpected eof"); break; case SBUF_EV_SEND_FAILED: disconnect_server(client->link, false, "server connection closed"); break; case SBUF_EV_READ: /* Wait until full packet headers is available. */ if (incomplete_header(data)) { slog_noise(client, "C: got partial header, trying to wait a bit"); return false; } if (!get_header(data, &pkt)) { char hex[8*2 + 1]; disconnect_client(client, true, "bad packet header: '%s'", hdr2hex(data, hex, sizeof(hex))); return false; } slog_noise(client, "read pkt='%c' len=%u", pkt_desc(&pkt), pkt.len); /* * If we are reading an SSL request or GSSAPI * encryption request, we should have no data already * buffered at this point. If we do, it was received * before we performed the SSL or GSSAPI handshake, so * it wasn't encrypted and indeed may have been * injected by a man-in-the-middle. We report this * case to the client. */ if (pkt.type == PKT_SSLREQ && mbuf_avail_for_read(data) > 0) { disconnect_client(client, true, "received unencrypted data after SSL request"); return false; } if (pkt.type == PKT_GSSENCREQ && mbuf_avail_for_read(data) > 0) { disconnect_client(client, true, "received unencrypted data after GSSAPI encryption request"); return false; } client->request_time = get_cached_time(); if (expect_startup_packet(client)) { res = handle_client_startup(client, &pkt); } else { res = handle_client_work(client, &pkt); } break; case SBUF_EV_FLUSH: /* client is not interested in it */ break; case SBUF_EV_PKT_CALLBACK: { bool first = false; if (client->packet_cb_state.pkt.type == 0) { first = true; if (!get_header(data, &client->packet_cb_state.pkt)) { char hex[8*2 + 1]; disconnect_client(client, true, "bad packet header: '%s'", hdr2hex(data, hex, sizeof(hex))); return false; } mbuf_rewind_reader(data); } switch (client->packet_cb_state.flag) { case CB_WANT_COMPLETE_PACKET: if (first) { slog_debug(client, "buffering complete packet, pkt='%c' len=%d incomplete=%s available=%d", pkt_desc(&client->packet_cb_state.pkt), client->packet_cb_state.pkt.len, incomplete_pkt(&client->packet_cb_state.pkt) ? "true" : "false", mbuf_avail_for_read(data)); mbuf_init_dynamic(&client->packet_cb_state.pkt.data); if (!mbuf_make_room(&client->packet_cb_state.pkt.data, client->packet_cb_state.pkt.len)) return false; } if (!mbuf_write_raw_mbuf(&client->packet_cb_state.pkt.data, data)) return false; if (sbuf->pkt_remain != mbuf_avail_for_read(data)) { /* * We wrote the partial packet to our temporary buffer. So * we "handled" it and want to receive more data. */ res = true; break; } /* * We wrote the full packet into memory. Change the callback state * to indicate that. If anything fails while handling this packet * we'll continue from the current state in the callback state * machine. */ client->packet_cb_state.flag = CB_HANDLE_COMPLETE_PACKET; /* fallthrough */ case CB_HANDLE_COMPLETE_PACKET: /* Make sure we start reading after the header. */ if (expect_startup_packet(client)) { pkt_rewind_v2(&client->packet_cb_state.pkt); res = handle_client_startup(client, &client->packet_cb_state.pkt); } else { pkt_rewind_v3(&client->packet_cb_state.pkt); res = handle_client_work(client, &client->packet_cb_state.pkt); } if (!res) { return false; } client->packet_cb_state.flag = CB_NONE; free_header(&client->packet_cb_state.pkt); break; default: disconnect_client(client, true, "BUG: unknown packet callback flag"); break; } break; } case SBUF_EV_TLS_READY: sbuf_continue(&client->sbuf); res = true; break; } return res; } pgbouncer-1.24.1/src/pktbuf.c0000644000175000000000000002570714777762222012750 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Packet writing and sending. */ #include "bouncer.h" /* * PostgreSQL type OIDs for result sets */ #define BYTEAOID 17 #define INT8OID 20 #define INT4OID 23 #define TEXTOID 25 #define NUMERICOID 1700 static void pktbuf_free_internal(PktBuf *buf) { if (!buf || buf->fixed_buf) return; log_debug("pktbuf_free(%p)", buf); free(buf->buf); free(buf->ev); free(buf); } static PktBuf *temp_pktbuf; /* * free the given buffer, if it's a dynamic buffer and not the global temp * buffer */ void pktbuf_free(PktBuf *buf) { if (buf == temp_pktbuf) return; pktbuf_free_internal(buf); } PktBuf *pktbuf_dynamic(int start_len) { PktBuf *buf = zmalloc(sizeof(PktBuf)); log_debug("pktbuf_dynamic(%d): %p", start_len, buf); if (!buf) return NULL; buf->ev = zmalloc(sizeof(*buf->ev)); if (!buf->ev) { pktbuf_free(buf); return NULL; } buf->buf = malloc(start_len); if (!buf->buf) { pktbuf_free(buf); return NULL; } buf->buf_len = start_len; return buf; } void pktbuf_reset(struct PktBuf *pkt) { pkt->failed = false; pkt->write_pos = 0; pkt->pktlen_pos = 0; pkt->send_pos = 0; pkt->sending = false; } void pktbuf_static(PktBuf *buf, uint8_t *data, int len) { memset(buf, 0, sizeof(*buf)); buf->buf = data; buf->buf_len = len; buf->fixed_buf = true; } struct PktBuf *pktbuf_temp(void) { if (!temp_pktbuf) temp_pktbuf = pktbuf_dynamic(512); if (!temp_pktbuf) die("out of memory"); pktbuf_reset(temp_pktbuf); return temp_pktbuf; } void pktbuf_cleanup(void) { pktbuf_free_internal(temp_pktbuf); temp_pktbuf = NULL; } bool pktbuf_send_immediate(PktBuf *buf, PgSocket *sk) { uint8_t *pos = buf->buf + buf->send_pos; int amount = buf->write_pos - buf->send_pos; ssize_t res; if (buf->failed) return false; res = sbuf_op_send(&sk->sbuf, pos, amount); if (res < 0) { log_debug("pktbuf_send_immediate: %s", strerror(errno)); } return res == amount; } static void pktbuf_send_func(evutil_socket_t fd, short flags, void *arg) { PktBuf *buf = arg; SBuf *sbuf = &buf->queued_dst->sbuf; int amount, res; log_debug("pktbuf_send_func(%" PRId64 ", %d, %p)", (int64_t)fd, (int)flags, buf); if (buf->failed) return; amount = buf->write_pos - buf->send_pos; res = sbuf_op_send(sbuf, buf->buf + buf->send_pos, amount); if (res < 0) { if (errno == EAGAIN) { res = 0; } else { log_error("pktbuf_send_func: %s", strerror(errno)); pktbuf_free(buf); return; } } buf->send_pos += res; if (buf->send_pos < buf->write_pos) { event_assign(buf->ev, pgb_event_base, fd, EV_WRITE, pktbuf_send_func, buf); res = event_add(buf->ev, NULL); if (res < 0) { log_error("pktbuf_send_func: %s", strerror(errno)); pktbuf_free(buf); } } else { pktbuf_free(buf); } } bool pktbuf_send_queued(PktBuf *buf, PgSocket *sk) { Assert(!buf->sending); Assert(!buf->fixed_buf); if (buf->failed) { pktbuf_free(buf); return send_pooler_error(sk, true, NULL, false, "result prepare failed"); } else { buf->sending = true; buf->queued_dst = sk; pktbuf_send_func(sk->sbuf.sock, EV_WRITE, buf); return true; } } static void make_room(PktBuf *buf, int len) { int newlen = buf->buf_len; int need = buf->write_pos + len; void *ptr; if (newlen >= need) return; if (buf->failed) return; if (buf->fixed_buf) { buf->failed = true; return; } while (newlen < need) newlen = newlen * 2; log_debug("make_room(%p, %d): realloc newlen=%d", buf, len, newlen); ptr = realloc(buf->buf, newlen); if (!ptr) { buf->failed = true; } else { buf->buf = ptr; buf->buf_len = newlen; } } void pktbuf_put_char(PktBuf *buf, char val) { make_room(buf, 1); if (buf->failed) return; buf->buf[buf->write_pos++] = val; } void pktbuf_put_uint16(PktBuf *buf, uint16_t val) { make_room(buf, 4); if (buf->failed) return; buf->buf[buf->write_pos++] = (val >> 8) & 255; buf->buf[buf->write_pos++] = val & 255; } void pktbuf_put_uint32(PktBuf *buf, uint32_t val) { uint8_t *pos; make_room(buf, 4); if (buf->failed) return; pos = buf->buf + buf->write_pos; pos[0] = (val >> 24) & 255; pos[1] = (val >> 16) & 255; pos[2] = (val >> 8) & 255; pos[3] = val & 255; buf->write_pos += 4; } void pktbuf_put_uint64(PktBuf *buf, uint64_t val) { pktbuf_put_uint32(buf, val >> 32); pktbuf_put_uint32(buf, (uint32_t)val); } void pktbuf_put_bytes(PktBuf *buf, const void *data, int len) { if (len == 0) return; make_room(buf, len); if (buf->failed) return; memcpy(buf->buf + buf->write_pos, data, len); buf->write_pos += len; } void pktbuf_put_string(PktBuf *buf, const char *str) { int len = strlen(str); pktbuf_put_bytes(buf, str, len + 1); } /* * write header, remember pos to write length later. */ void pktbuf_start_packet(PktBuf *buf, int type) { if (buf->failed) return; if (type < 256) { /* new-style packet */ pktbuf_put_char(buf, type); buf->pktlen_pos = buf->write_pos; pktbuf_put_uint32(buf, 0); } else { /* old-style packet */ buf->pktlen_pos = buf->write_pos; pktbuf_put_uint32(buf, 0); pktbuf_put_uint32(buf, type); } } void pktbuf_finish_packet(PktBuf *buf) { uint8_t *pos; unsigned len; if (buf->failed) return; len = buf->write_pos - buf->pktlen_pos; pos = buf->buf + buf->pktlen_pos; buf->pktlen_pos = 0; *pos++ = (len >> 24) & 255; *pos++ = (len >> 16) & 255; *pos++ = (len >> 8) & 255; *pos++ = len & 255; } /* types: * c - char/byte * h - uint16 * i - uint32 * q - uint64 * s - Cstring * b - bytes */ void pktbuf_write_generic(PktBuf *buf, int type, const char *pktdesc, ...) { va_list ap; int len; const char *adesc = pktdesc; uint8_t *bin; pktbuf_start_packet(buf, type); va_start(ap, pktdesc); while (*adesc) { switch (*adesc) { case 'c': pktbuf_put_char(buf, va_arg(ap, int)); break; case 'h': pktbuf_put_uint16(buf, va_arg(ap, int)); break; case 'i': pktbuf_put_uint32(buf, va_arg(ap, int)); break; case 'q': pktbuf_put_uint64(buf, va_arg(ap, uint64_t)); break; case 's': pktbuf_put_string(buf, va_arg(ap, char *)); break; case 'b': bin = va_arg(ap, uint8_t *); len = va_arg(ap, int); pktbuf_put_bytes(buf, bin, len); break; default: fatal("bad pktdesc: %s", pktdesc); } adesc++; } va_end(ap); /* set correct length */ pktbuf_finish_packet(buf); } /* send resultset column info * tupdesc keys: * 'i' - int4 * 'q' - int8 * 's' - string to text * 'b' - bytes to bytea * 'N' - uint64_t to numeric * 'T' - usec_t to date */ void pktbuf_write_RowDescription(PktBuf *buf, const char *tupdesc, ...) { va_list ap; char *name; int i, ncol = strlen(tupdesc); log_noise("write RowDescription"); pktbuf_start_packet(buf, PqMsg_RowDescription); pktbuf_put_uint16(buf, ncol); va_start(ap, tupdesc); for (i = 0; i < ncol; i++) { name = va_arg(ap, char *); /* Fields: name, reloid, colnr, oid, typsize, typmod, fmt */ pktbuf_put_string(buf, name); pktbuf_put_uint32(buf, 0); pktbuf_put_uint16(buf, 0); if (tupdesc[i] == 's') { pktbuf_put_uint32(buf, TEXTOID); pktbuf_put_uint16(buf, -1); } else if (tupdesc[i] == 'b') { pktbuf_put_uint32(buf, BYTEAOID); pktbuf_put_uint16(buf, -1); } else if (tupdesc[i] == 'i') { pktbuf_put_uint32(buf, INT4OID); pktbuf_put_uint16(buf, 4); } else if (tupdesc[i] == 'q') { pktbuf_put_uint32(buf, INT8OID); pktbuf_put_uint16(buf, 8); } else if (tupdesc[i] == 'N') { pktbuf_put_uint32(buf, NUMERICOID); pktbuf_put_uint16(buf, -1); } else if (tupdesc[i] == 'T') { pktbuf_put_uint32(buf, TEXTOID); pktbuf_put_uint16(buf, -1); } else { fatal("bad tupdesc"); } pktbuf_put_uint32(buf, -1); pktbuf_put_uint16(buf, 0); } va_end(ap); /* set correct length */ pktbuf_finish_packet(buf); } /* * send DataRow. * * tupdesc keys: * 'i' - int4 * 'q' - int8 * 's' - string to text * 'b' - bytes to bytea * 'N' - uint64_t to numeric * 'T' - usec_t to date */ void pktbuf_write_DataRow(PktBuf *buf, const char *tupdesc, ...) { int ncol = strlen(tupdesc); va_list ap; pktbuf_start_packet(buf, PqMsg_DataRow); pktbuf_put_uint16(buf, ncol); va_start(ap, tupdesc); for (int i = 0; i < ncol; i++) { char tmp[100]; /* XXX good enough in practice */ const char *val = NULL; if (tupdesc[i] == 'i') { snprintf(tmp, sizeof(tmp), "%d", va_arg(ap, int)); val = tmp; } else if (tupdesc[i] == 'q' || tupdesc[i] == 'N') { snprintf(tmp, sizeof(tmp), "%" PRIu64, va_arg(ap, uint64_t)); val = tmp; } else if (tupdesc[i] == 's') { val = va_arg(ap, char *); } else if (tupdesc[i] == 'b') { int blen = va_arg(ap, int); if (blen >= 0) { uint8_t *bval = va_arg(ap, uint8_t *); size_t required = 2 + blen * 2 + 1; if (required > sizeof(tmp)) fatal("byte array too long (%zu > %zu)", required, sizeof(tmp)); strcpy(tmp, "\\x"); for (int j = 0; j < blen; j++) sprintf(tmp + (2 + j * 2), "%02x", bval[j]); val = tmp; } else { (void) va_arg(ap, uint8_t *); val = NULL; } } else if (tupdesc[i] == 'T') { usec_t time = va_arg(ap, usec_t); val = format_time_s(time, tmp, sizeof(tmp)); } else { fatal("bad tupdesc: %s", tupdesc); } if (val) { int len = strlen(val); pktbuf_put_uint32(buf, len); pktbuf_put_bytes(buf, val, len); } else { /* NULL */ pktbuf_put_uint32(buf, -1); } } va_end(ap); pktbuf_finish_packet(buf); } /* * Send Parse+Bind+Execute with string parameters. */ void pktbuf_write_ExtQuery(PktBuf *buf, const char *query, int nargs, ...) { va_list ap; const char *val; int len, i; pktbuf_write_generic(buf, PqMsg_Parse, "csh", 0, query, 0); pktbuf_start_packet(buf, PqMsg_Bind); pktbuf_put_char(buf, 0); /* portal name */ pktbuf_put_char(buf, 0); /* query name */ pktbuf_put_uint16(buf, 0); /* number of parameter format codes */ pktbuf_put_uint16(buf, nargs); /* number of parameter values */ va_start(ap, nargs); for (i = 0; i < nargs; i++) { val = va_arg(ap, char *); len = strlen(val); pktbuf_put_uint32(buf, len); pktbuf_put_bytes(buf, val, len); } va_end(ap); pktbuf_put_uint16(buf, 0); /* number of result-column format codes */ pktbuf_finish_packet(buf); pktbuf_write_generic(buf, PqMsg_Describe, "cc", 'P', 0); pktbuf_write_generic(buf, PqMsg_Execute, "ci", 0, 0); pktbuf_write_generic(buf, PqMsg_Sync, ""); } pgbouncer-1.24.1/src/util.c0000644000175000000000000003007314777762222012422 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Random small utility functions */ #include "bouncer.h" #include #include #include #include #ifdef USUAL_LIBSSL_FOR_TLS #include #include #endif int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen) { const struct PgSocket *sock = ctx; const char *user, *db, *host; char host6[PGADDR_BUF]; int peer_id; int port; char stype; /* no prefix */ if (!sock) return 0; /* format prefix */ stype = is_server_socket(sock) ? 'S' : 'C'; port = pga_port(&sock->remote_addr); peer_id = sock->pool ? sock->pool->db->peer_id : 0; db = sock->pool ? sock->pool->db->name : "(nodb)"; user = sock->login_user_credentials ? sock->login_user_credentials->name : "(nouser)"; if (pga_is_unix(&sock->remote_addr)) { unsigned long pid = sock->remote_addr.scred.pid; if (pid) { snprintf(host6, sizeof(host6), "unix(%lu)", pid); host = host6; } else { host = "unix"; } } else { host = pga_ntop(&sock->remote_addr, host6, sizeof(host6)); } if (pga_family(&sock->remote_addr) == AF_INET6) { if (peer_id) { return snprintf(dst, dstlen, "%c-%p: peer-%d@[%s]:%d ", stype, sock, peer_id, host, port); } return snprintf(dst, dstlen, "%c-%p: %s/%s@[%s]:%d ", stype, sock, db, user, host, port); } else { if (peer_id) { return snprintf(dst, dstlen, "%c-%p: peer-%d@%s:%d ", stype, sock, peer_id, host, port); } return snprintf(dst, dstlen, "%c-%p: %s/%s@%s:%d ", stype, sock, db, user, host, port); } } const char *bin2hex(const uint8_t *src, unsigned srclen, char *dst, unsigned dstlen) { unsigned int i, j; static const char hextbl [] = "0123456789abcdef"; if (!dstlen) return ""; if (srclen*2 + 1 > dstlen) srclen = (dstlen - 1) / 2; for (i = j = 0; i < srclen; i++) { dst[j++] = hextbl[src[i] >> 4]; dst[j++] = hextbl[src[i] & 15]; } dst[j] = 0; return dst; } /* * PostgreSQL MD5 hashing. */ static void hash2hex(const uint8_t *hash, char *dst) { bin2hex(hash, MD5_DIGEST_LENGTH, dst, 16*2 + 1); } bool pg_md5_encrypt(const char *part1, const char *part2, size_t part2len, char *dest) { #ifdef USUAL_LIBSSL_FOR_TLS EVP_MD_CTX *mdctx; uint8_t hash[MD5_DIGEST_LENGTH]; ERR_clear_error(); mdctx = EVP_MD_CTX_create(); if (mdctx == NULL) { log_error("MD5 authentication failed: out-of-memory"); return false; } if (!EVP_DigestInit(mdctx, EVP_md5())) goto failed; if (!EVP_DigestUpdate(mdctx, part1, strlen(part1))) goto failed; if (!EVP_DigestUpdate(mdctx, part2, part2len)) goto failed; if (!EVP_DigestFinal_ex(mdctx, hash, 0)) goto failed; EVP_MD_CTX_destroy(mdctx); memcpy(dest, "md5", 3); hash2hex(hash, dest + 3); return true; failed: log_error("MD5 authentication failed: %s", ERR_reason_error_string(ERR_get_error())); EVP_MD_CTX_destroy(mdctx); return false; #else struct md5_ctx ctx; uint8_t hash[MD5_DIGEST_LENGTH]; md5_reset(&ctx); md5_update(&ctx, part1, strlen(part1)); md5_update(&ctx, part2, part2len); md5_final(&ctx, hash); memcpy(dest, "md5", 3); hash2hex(hash, dest + 3); return true; #endif } /* wrapped for getting random bytes */ void get_random_bytes(uint8_t *dest, int len) { csrandom_bytes(dest, len); } /* set needed socket options */ bool tune_socket(int sock, bool is_unix) { int res; int val; const char *errpos; bool ok; /* * Generic stuff + nonblock. */ errpos = "socket_setup"; ok = socket_setup(sock, true); if (!ok) goto fail; /* * Following options are for network sockets */ if (is_unix) return true; /* * TCP Keepalive */ errpos = "socket_set_keepalive"; ok = socket_set_keepalive(sock, cf_tcp_keepalive, cf_tcp_keepidle, cf_tcp_keepintvl, cf_tcp_keepcnt); if (!ok) goto fail; /* * TCP user timeout */ if (cf_tcp_user_timeout) { errpos = "setsockopt/TCP_USER_TIMEOUT"; #ifdef TCP_USER_TIMEOUT val = cf_tcp_user_timeout; res = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &val, sizeof(val)); if (res < 0) goto fail; #else errno = EINVAL; goto fail; #endif } /* * set in-kernel socket buffer size */ if (cf_tcp_socket_buffer) { val = cf_tcp_socket_buffer; errpos = "setsockopt/SO_SNDBUF"; res = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); if (res < 0) goto fail; val = cf_tcp_socket_buffer; errpos = "setsockopt/SO_RCVBUF"; res = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)); if (res < 0) goto fail; } /* * Turn off kernel buffering, each send() will be one packet. */ val = 1; errpos = "setsockopt/TCP_NODELAY"; res = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); if (res < 0) goto fail; return true; fail: log_warning("%s(%d) failed: %s", errpos, sock, strerror(errno)); return false; } /* * Find a string in comma-separated list. * * It does not support space inside tokens. */ bool strlist_contains(const char *liststr, const char *str) { int c, len = strlen(str); const char *p, *listpos = liststr; loop: /* find string fragment, later check if actual token */ p = strstr(listpos, str); if (p == NULL) return false; /* move listpos further */ listpos = p + len; /* survive len=0 and avoid unnecessary compare */ if (*listpos) listpos++; /* check previous symbol */ if (p > liststr) { c = *(p - 1); if (!isspace(c) && c != ',') goto loop; } /* check following symbol */ c = p[len]; if (c != 0 && !isspace(c) && c != ',') goto loop; return true; } void fill_remote_addr(PgSocket *sk, int fd, bool is_unix) { PgAddr *dst = &sk->remote_addr; socklen_t len = sizeof(PgAddr); int err; if (is_unix) { uid_t uid = 0; gid_t gid = 0; pid_t pid = 0; pga_set(dst, AF_UNIX, cf_listen_port); if (getpeercreds(fd, &uid, &gid, &pid) >= 0) { log_noise("unix peer uid: %d", (int)uid); } else if (errno != ENOSYS) { /* * Check for ENOSYS, so we don't write a * warning every time if the OS doesn't * support this call. */ log_warning("unix peer uid failed: %s", strerror(errno)); } dst->scred.uid = uid; dst->scred.pid = pid; } else { err = getpeername(fd, (struct sockaddr *)dst, &len); if (err < 0) { log_error("fill_remote_addr: getpeername(%d) = %s", fd, strerror(errno)); } } } void fill_local_addr(PgSocket *sk, int fd, bool is_unix) { PgAddr *dst = &sk->local_addr; socklen_t len = sizeof(PgAddr); int err; if (is_unix) { pga_set(dst, AF_UNIX, cf_listen_port); dst->scred.uid = geteuid(); dst->scred.pid = getpid(); } else { err = getsockname(fd, (struct sockaddr *)dst, &len); if (err < 0) { log_error("fill_local_addr: getsockname(%d) = %s", fd, strerror(errno)); } } } /* * Error handling around evtimer_add() is nasty as the code * may not be called again. As there is fixed number of timers * in pgbouncer, provider safe_evtimer_add() that stores args of * failed calls in static array and retries later. */ #define TIMER_BACKUP_SLOTS 10 struct timer_slot { struct event *ev; struct timeval tv; }; static struct timer_slot timer_backup_list[TIMER_BACKUP_SLOTS]; static int timer_backup_used = 0; void safe_evtimer_add(struct event *ev, struct timeval *tv) { int res; struct timer_slot *ts; res = evtimer_add(ev, tv); if (res >= 0) return; if (timer_backup_used >= TIMER_BACKUP_SLOTS) fatal("TIMER_BACKUP_SLOTS full"); ts = &timer_backup_list[timer_backup_used++]; ts->ev = ev; ts->tv = *tv; } void rescue_timers(void) { struct timer_slot *ts; while (timer_backup_used) { ts = &timer_backup_list[timer_backup_used - 1]; if (evtimer_add(ts->ev, &ts->tv) < 0) break; timer_backup_used--; } } /* * PgAddr operations */ int pga_port(const PgAddr *a) { if (a->sa.sa_family == AF_INET6) { return ntohs(a->sin6.sin6_port); } else { return ntohs(a->sin.sin_port); } } /* set family and port */ void pga_set(PgAddr *a, int af, int port) { memset(a, 0, sizeof(*a)); if (af == AF_INET6) { a->sin6.sin6_family = af; a->sin6.sin6_port = htons(port); } else { a->sin.sin_family = af; a->sin.sin_port = htons(port); } } /* copy sockaddr_in/in6 to PgAddr */ void pga_copy(PgAddr *a, const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: memcpy(&a->sin, sa, sizeof(a->sin)); break; case AF_INET6: memcpy(&a->sin6, sa, sizeof(a->sin6)); break; case AF_UNIX: log_error("pga_copy: AF_UNIX copy not supported"); } } int pga_cmp_addr(const PgAddr *a, const PgAddr *b) { if (pga_family(a) != pga_family(b)) return pga_family(a) - pga_family(b); switch (pga_family(a)) { case AF_INET: return memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(a->sin.sin_addr)); break; case AF_INET6: return memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(a->sin6.sin6_addr)); break; default: log_error("pga_cmp_addr: unsupported family"); return 0; } } /* convert pgaddr to string */ const char *pga_ntop(const PgAddr *a, char *dst, int dstlen) { const char *res = NULL; char buf[PGADDR_BUF]; memset(buf, 0, sizeof(buf)); switch (pga_family(a)) { case AF_UNIX: res = "unix"; break; case AF_INET: res = inet_ntop(AF_INET, &a->sin.sin_addr, buf, sizeof(buf)); break; case AF_INET6: res = inet_ntop(AF_INET6, &a->sin6.sin6_addr, buf, sizeof(buf)); break; default: res = "(bad-af)"; } if (res == NULL) res = "(err-ntop)"; strlcpy(dst, res, dstlen); return dst; } /* parse address from string */ bool pga_pton(PgAddr *a, const char *s, int port) { int res = 1; if (strcmp(s, "unix") == 0) { pga_set(a, AF_UNIX, port); } else if (strcmp(s, "*") == 0) { pga_set(a, AF_INET, port); a->sin.sin_addr.s_addr = htonl(INADDR_ANY); } else if (strchr(s, ':')) { pga_set(a, AF_INET6, port); res = inet_pton(AF_INET6, s, &a->sin6.sin6_addr); } else { pga_set(a, AF_INET, port); res = inet_pton(AF_INET, s, &a->sin.sin_addr); } if (res == 0) errno = EINVAL; return res > 0; } const char *pga_str(const PgAddr *a, char *dst, int dstlen) { char buf[PGADDR_BUF]; pga_ntop(a, buf, sizeof(buf)); if (pga_family(a) == AF_INET6) { snprintf(dst, dstlen, "[%s]:%d", buf, pga_port(a)); } else if (pga_family(a) == AF_UNIX && a->scred.pid) { snprintf(dst, dstlen, "%s:%d$%lu", buf, pga_port(a), (unsigned long)a->scred.pid); } else { snprintf(dst, dstlen, "%s:%d", buf, pga_port(a)); } return dst; } static const char *cached_hostname(void) { static char cache[256]; int err; if (cache[0] == 0) { err = gethostname(cache, sizeof(cache)); if (err != 0) strlcpy(cache, "somehost", sizeof(cache)); } return cache; } const char *pga_details(const PgAddr *a, char *dst, int dstlen) { char buf[PGADDR_BUF]; pga_ntop(a, buf, sizeof(buf)); if (pga_family(a) == AF_INET6) { snprintf(dst, dstlen, "[%s]:%d", buf, pga_port(a)); } else if (pga_family(a) == AF_UNIX && a->scred.pid) { snprintf(dst, dstlen, "%s(%lu@%s):%d", buf, (unsigned long)a->scred.pid, cached_hostname(), pga_port(a)); } else { snprintf(dst, dstlen, "%s:%d", buf, pga_port(a)); } return dst; } bool cf_set_authdb(struct CfValue *cv, const char *value) { if (!check_reserved_database(value)) { log_error("cannot use the reserved \"%s\" database as an auth_dbname", value); return false; } return cf_set_str(cv, value); } bool check_reserved_database(const char *value) { if (value && strcmp(value, "pgbouncer") == 0) { return false; } return true; } pgbouncer-1.24.1/src/stats.c0000644000175000000000000002743014777762222012606 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" static struct event ev_stats; static usec_t old_stamp, new_stamp; static void reset_stats(PgStats *stat) { stat->server_bytes = 0; stat->client_bytes = 0; stat->server_assignment_count = 0; stat->query_count = 0; stat->query_time = 0; stat->xact_count = 0; stat->xact_time = 0; stat->wait_time = 0; stat->ps_client_parse_count = 0; stat->ps_server_parse_count = 0; stat->ps_bind_count = 0; } static void stat_add(PgStats *total, PgStats *stat) { total->server_bytes += stat->server_bytes; total->client_bytes += stat->client_bytes; total->server_assignment_count += stat->server_assignment_count; total->query_count += stat->query_count; total->query_time += stat->query_time; total->xact_count += stat->xact_count; total->xact_time += stat->xact_time; total->wait_time += stat->wait_time; total->ps_client_parse_count += stat->ps_client_parse_count; total->ps_server_parse_count += stat->ps_server_parse_count; total->ps_bind_count += stat->ps_bind_count; } static void calc_average(PgStats *avg, PgStats *cur, PgStats *old) { uint64_t server_assignment_count; uint64_t query_count; uint64_t xact_count; uint64_t ps_client_parse_count; uint64_t ps_server_parse_count; uint64_t ps_bind_count; usec_t dur = get_cached_time() - old_stamp; reset_stats(avg); if (dur <= 0) return; query_count = cur->query_count - old->query_count; xact_count = cur->xact_count - old->xact_count; server_assignment_count = cur->server_assignment_count - old->server_assignment_count; avg->query_count = USEC * query_count / dur; avg->xact_count = USEC * xact_count / dur; avg->server_assignment_count = USEC * server_assignment_count / dur; avg->client_bytes = USEC * (cur->client_bytes - old->client_bytes) / dur; avg->server_bytes = USEC * (cur->server_bytes - old->server_bytes) / dur; if (query_count > 0) avg->query_time = (cur->query_time - old->query_time) / query_count; if (xact_count > 0) avg->xact_time = (cur->xact_time - old->xact_time) / xact_count; if (server_assignment_count > 0) avg->wait_time = (cur->wait_time - old->wait_time) / server_assignment_count; ps_client_parse_count = cur->ps_client_parse_count - old->ps_client_parse_count; ps_server_parse_count = cur->ps_server_parse_count - old->ps_server_parse_count; ps_bind_count = cur->ps_bind_count - old->ps_bind_count; avg->ps_client_parse_count = USEC * ps_client_parse_count / dur; avg->ps_server_parse_count = USEC * ps_server_parse_count / dur; avg->ps_bind_count = USEC * ps_bind_count / dur; } static void write_stats(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) { PgStats avg; calc_average(&avg, stat, old); pktbuf_write_DataRow(buf, "sNNNNNNNNNNNNNNNNNNNNNN", dbname, stat->server_assignment_count, stat->xact_count, stat->query_count, stat->client_bytes, stat->server_bytes, stat->xact_time, stat->query_time, stat->wait_time, stat->ps_client_parse_count, stat->ps_server_parse_count, stat->ps_bind_count, avg.server_assignment_count, avg.xact_count, avg.query_count, avg.client_bytes, avg.server_bytes, avg.xact_time, avg.query_time, avg.wait_time, avg.ps_client_parse_count, avg.ps_server_parse_count, avg.ps_bind_count); } bool admin_database_stats(PgSocket *client, struct StatList *pool_list) { PgPool *pool; struct List *item; PgDatabase *cur_db = NULL; PgStats st_db, old_db; PktBuf *buf; reset_stats(&st_db); reset_stats(&old_db); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sNNNNNNNNNNNNNNNNNNNNNN", "database", "total_server_assignment_count", "total_xact_count", "total_query_count", "total_received", "total_sent", "total_xact_time", "total_query_time", "total_wait_time", "total_client_parse_count", "total_server_parse_count", "total_bind_count", "avg_server_assignment_count", "avg_xact_count", "avg_query_count", "avg_recv", "avg_sent", "avg_xact_time", "avg_query_time", "avg_wait_time", "avg_client_parse_count", "avg_server_parse_count", "avg_bind_count"); statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); if (!cur_db) cur_db = pool->db; if (pool->db != cur_db) { write_stats(buf, &st_db, &old_db, cur_db->name); cur_db = pool->db; reset_stats(&st_db); reset_stats(&old_db); } stat_add(&st_db, &pool->stats); stat_add(&old_db, &pool->older_stats); } if (cur_db) { write_stats(buf, &st_db, &old_db, cur_db->name); } admin_flush(client, buf, "SHOW"); return true; } static void write_stats_totals(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) { pktbuf_write_DataRow(buf, "sNNNNNNNNNNN", dbname, stat->server_assignment_count, stat->xact_count, stat->query_count, stat->client_bytes, stat->server_bytes, stat->xact_time, stat->query_time, stat->wait_time, stat->ps_client_parse_count, stat->ps_server_parse_count, stat->ps_bind_count); } bool admin_database_stats_totals(PgSocket *client, struct StatList *pool_list) { PgPool *pool; struct List *item; PgDatabase *cur_db = NULL; PgStats st_db, old_db; PktBuf *buf; reset_stats(&st_db); reset_stats(&old_db); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sNNNNNNNNNNN", "database", "server_assignment_count", "xact_count", "query_count", "bytes_received", "bytes_sent", "xact_time", "query_time", "wait_time", "client_parse_count", "server_parse_count", "bind_count"); statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); if (!cur_db) cur_db = pool->db; if (pool->db != cur_db) { write_stats_totals(buf, &st_db, &old_db, cur_db->name); cur_db = pool->db; reset_stats(&st_db); reset_stats(&old_db); } stat_add(&st_db, &pool->stats); stat_add(&old_db, &pool->older_stats); } if (cur_db) { write_stats_totals(buf, &st_db, &old_db, cur_db->name); } admin_flush(client, buf, "SHOW"); return true; } static void write_stats_averages(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) { PgStats avg; calc_average(&avg, stat, old); pktbuf_write_DataRow(buf, "sNNNNNNNNNNN", dbname, avg.server_assignment_count, avg.xact_count, avg.query_count, avg.client_bytes, avg.server_bytes, avg.xact_time, avg.query_time, avg.wait_time, avg.ps_client_parse_count, avg.ps_server_parse_count, avg.ps_bind_count); } bool admin_database_stats_averages(PgSocket *client, struct StatList *pool_list) { PgPool *pool; struct List *item; PgDatabase *cur_db = NULL; PgStats st_db, old_db; PktBuf *buf; reset_stats(&st_db); reset_stats(&old_db); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sNNNNNNNNNNN", "database", "server_assignment_count", "xact_count", "query_count", "bytes_received", "bytes_sent", "xact_time", "query_time", "wait_time", "avg_client_parse_count", "avg_server_parse_count", "avg_bind_count"); statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); if (!cur_db) cur_db = pool->db; if (pool->db != cur_db) { write_stats_averages(buf, &st_db, &old_db, cur_db->name); cur_db = pool->db; reset_stats(&st_db); reset_stats(&old_db); } stat_add(&st_db, &pool->stats); stat_add(&old_db, &pool->older_stats); } if (cur_db) { write_stats_averages(buf, &st_db, &old_db, cur_db->name); } admin_flush(client, buf, "SHOW"); return true; } bool show_stat_totals(PgSocket *client, struct StatList *pool_list) { PgPool *pool; struct List *item; PgStats st_total, old_total, avg; PktBuf *buf; reset_stats(&st_total); reset_stats(&old_total); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); stat_add(&st_total, &pool->stats); stat_add(&old_total, &pool->older_stats); } calc_average(&avg, &st_total, &old_total); pktbuf_write_RowDescription(buf, "sN", "name", "value"); #define WTOTAL(name) pktbuf_write_DataRow(buf, "sN", "total_" #name, st_total.name) #define WAVG(name) pktbuf_write_DataRow(buf, "sN", "avg_" #name, avg.name) WTOTAL(server_assignment_count); WTOTAL(xact_count); WTOTAL(query_count); WTOTAL(client_bytes); WTOTAL(server_bytes); WTOTAL(xact_time); WTOTAL(query_time); WTOTAL(wait_time); WTOTAL(ps_client_parse_count); WTOTAL(ps_server_parse_count); WTOTAL(ps_bind_count); WAVG(server_assignment_count); WAVG(xact_count); WAVG(query_count); WAVG(client_bytes); WAVG(server_bytes); WAVG(xact_time); WAVG(query_time); WAVG(wait_time); WAVG(ps_client_parse_count); WAVG(ps_server_parse_count); WAVG(ps_bind_count); admin_flush(client, buf, "SHOW"); return true; } static void refresh_stats(evutil_socket_t s, short flags, void *arg) { struct List *item; PgPool *pool; PgStats old_total, cur_total; PgStats avg; reset_stats(&old_total); reset_stats(&cur_total); old_stamp = new_stamp; new_stamp = get_cached_time(); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); pool->older_stats = pool->newer_stats; pool->newer_stats = pool->stats; if (cf_log_stats) { stat_add(&cur_total, &pool->stats); stat_add(&old_total, &pool->older_stats); } } calc_average(&avg, &cur_total, &old_total); if (cf_log_stats) { log_info("stats: %" PRIu64 " xacts/s," " %" PRIu64 " queries/s," " %" PRIu64 " client parses/s," " %" PRIu64 " server parses/s," " %" PRIu64 " binds/s," " in %" PRIu64 " B/s," " out %" PRIu64 " B/s," " xact %" PRIu64 " us," " query %" PRIu64 " us," " wait %" PRIu64 " us", avg.xact_count, avg.query_count, avg.ps_client_parse_count, avg.ps_server_parse_count, avg.ps_bind_count, avg.client_bytes, avg.server_bytes, avg.xact_time, avg.query_time, avg.wait_time); } sd_notifyf(0, "STATUS=stats: %" PRIu64 " xacts/s," " %" PRIu64 " queries/s," " %" PRIu64 " client parses/s," " %" PRIu64 " server parses/s," " %" PRIu64 " binds/s," " in %" PRIu64 " B/s," " out %" PRIu64 " B/s," " xact %" PRIu64 " μs," " query %" PRIu64 " μs," " wait %" PRIu64 " μs", avg.xact_count, avg.query_count, avg.ps_client_parse_count, avg.ps_server_parse_count, avg.ps_bind_count, avg.client_bytes, avg.server_bytes, avg.xact_time, avg.query_time, avg.wait_time); } void stats_setup(void) { struct timeval period = { cf_stats_period, 0 }; new_stamp = get_cached_time(); old_stamp = new_stamp - USEC; /* launch stats */ event_assign(&ev_stats, pgb_event_base, -1, EV_PERSIST, refresh_stats, NULL); if (event_add(&ev_stats, &period) < 0) log_warning("event_add failed: %s", strerror(errno)); } pgbouncer-1.24.1/src/objects.c0000644000175000000000000022751214777762222013104 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Herding objects between lists happens here. */ #include "bouncer.h" #include "scram.h" #include #include #include /* those items will be allocated as needed, never freed */ STATLIST(user_list); STATLIST(database_list); STATLIST(pool_list); STATLIST(peer_list); STATLIST(peer_pool_list); /* All locally defined users (in auth_file) are kept here. */ struct AATree user_tree; /* * All PAM users are kept here. We need to differentiate two user * lists to avoid user clashing for different authentication types, * and because pam_user_tree is closer to PgDatabase.user_tree in * logic. */ struct AATree pam_user_tree; /* * The global prepared statement cache, which deduplicates prepared statements * sent by the clients statements by storing every unique prepared statement * only once. */ PgPreparedStatement *prepared_statements = NULL; /* * client and server objects will be pre-allocated * they are always in either active or free lists * in addition to others. */ STATLIST(login_client_list); struct Slab *server_cache; struct Slab *client_cache; struct Slab *db_cache; struct Slab *peer_cache; struct Slab *peer_pool_cache; struct Slab *pool_cache; struct Slab *user_cache; struct Slab *credentials_cache; struct Slab *iobuf_cache; struct Slab *outstanding_request_cache; struct Slab *var_list_cache; struct Slab *server_prepared_statement_cache; unsigned long long int last_pgsocket_id; /* * libevent may still report events when event_del() * is called from somewhere else. So hide just freed * PgSockets for one loop. */ static STATLIST(justfree_client_list); static STATLIST(justfree_server_list); /* init autodb idle list */ STATLIST(autodatabase_idle_list); const char *replication_type_parameters[] = { [REPLICATION_NONE] = "no", [REPLICATION_LOGICAL] = "database", [REPLICATION_PHYSICAL] = "yes", }; /* fast way to get number of active clients */ int get_active_client_count(void) { return slab_active_count(client_cache); } /* fast way to get number of active servers */ int get_active_server_count(void) { return slab_active_count(server_cache); } static void construct_client(void *obj) { PgSocket *client = obj; memset(client, 0, sizeof(PgSocket)); list_init(&client->head); sbuf_init(&client->sbuf, client_proto); client->vars.var_list = slab_alloc(var_list_cache); client->state = CL_FREE; client->client_prepared_statements = NULL; client->id = ++last_pgsocket_id; } static void construct_server(void *obj) { PgSocket *server = obj; memset(server, 0, sizeof(PgSocket)); list_init(&server->head); sbuf_init(&server->sbuf, server_proto); server->vars.var_list = slab_alloc(var_list_cache); server->state = SV_FREE; server->server_prepared_statements = NULL; statlist_init(&server->outstanding_requests, "outstanding_requests"); server->id = ++last_pgsocket_id; } /* compare string with PgGlobalUser->credentials.name, for usage with btree */ static int global_user_node_cmp(uintptr_t userptr, struct AANode *node) { const char *name = (const char *)userptr; PgGlobalUser *global_user = container_of(node, PgGlobalUser, credentials.tree_node); return strcmp(name, global_user->credentials.name); } /* compare string with PgCredentials->name, for usage with btree */ static int credentials_node_cmp(uintptr_t userptr, struct AANode *node) { const char *name = (const char *)userptr; PgCredentials *credentials = container_of(node, PgCredentials, tree_node); return strcmp(name, credentials->name); } /* destroy PgCredentials, for usage with btree */ static void credentials_node_release(struct AANode *node, void *arg) { PgCredentials *user = container_of(node, PgCredentials, tree_node); slab_free(credentials_cache, user); } /* initialization before config loading */ void init_objects(void) { aatree_init(&user_tree, global_user_node_cmp, NULL); aatree_init(&pam_user_tree, credentials_node_cmp, NULL); user_cache = slab_create("user_cache", sizeof(PgGlobalUser), 0, NULL, USUAL_ALLOC); credentials_cache = slab_create("credentials_cache", sizeof(PgCredentials), 0, NULL, USUAL_ALLOC); db_cache = slab_create("db_cache", sizeof(PgDatabase), 0, NULL, USUAL_ALLOC); peer_cache = slab_create("peer_cache", sizeof(PgDatabase), 0, NULL, USUAL_ALLOC); peer_pool_cache = slab_create("peer_pool_cache", sizeof(PgPool), 0, NULL, USUAL_ALLOC); pool_cache = slab_create("pool_cache", sizeof(PgPool), 0, NULL, USUAL_ALLOC); outstanding_request_cache = slab_create("outstanding_request_cache", sizeof(OutstandingRequest), 0, NULL, USUAL_ALLOC); if (!user_cache || !db_cache || !peer_cache || !peer_pool_cache || !pool_cache) fatal("cannot create initial caches"); } static void do_iobuf_reset(void *arg) { IOBuf *io = arg; iobuf_reset(io); } /* initialization after config loading */ void init_caches(void) { server_cache = slab_create("server_cache", sizeof(PgSocket), 0, construct_server, USUAL_ALLOC); client_cache = slab_create("client_cache", sizeof(PgSocket), 0, construct_client, USUAL_ALLOC); iobuf_cache = slab_create("iobuf_cache", IOBUF_SIZE, 0, do_iobuf_reset, USUAL_ALLOC); var_list_cache = slab_create("var_list_cache", sizeof(struct PStr *) * get_num_var_cached(), 0, NULL, USUAL_ALLOC); server_prepared_statement_cache = slab_create("server_prepared_statement_cache", sizeof(PgServerPreparedStatement), 0, NULL, USUAL_ALLOC); } /* free all memory related to the given client */ static void client_free(PgSocket *client) { free_client_prepared_statements(client); varcache_clean(&client->vars); slab_free(var_list_cache, client->vars.var_list); slab_free(client_cache, client); } /* free all memory related to the given server */ static void server_free(PgSocket *server) { struct List *el, *tmp_l; OutstandingRequest *request; statlist_for_each_safe(el, &server->outstanding_requests, tmp_l) { request = container_of(el, OutstandingRequest, node); statlist_remove(&server->canceling_clients, el); if (request->server_ps) free_server_prepared_statement(request->server_ps); slab_free(outstanding_request_cache, request); } free_server_prepared_statements(server); varcache_clean(&server->vars); slab_free(var_list_cache, server->vars.var_list); slab_free(server_cache, server); } /* state change means moving between lists */ void change_client_state(PgSocket *client, SocketState newstate) { PgPool *pool = client->pool; /* remove from old location */ switch (client->state) { case CL_FREE: break; case CL_JUSTFREE: statlist_remove(&justfree_client_list, &client->head); break; case CL_LOGIN: if (newstate == CL_WAITING) newstate = CL_WAITING_LOGIN; statlist_remove(&login_client_list, &client->head); break; case CL_WAITING_LOGIN: if (newstate == CL_ACTIVE) newstate = CL_LOGIN; /* fallthrough */ case CL_WAITING: statlist_remove(&pool->waiting_client_list, &client->head); break; case CL_ACTIVE: statlist_remove(&pool->active_client_list, &client->head); break; case CL_ACTIVE_CANCEL: statlist_remove(&pool->active_cancel_req_list, &client->head); break; case CL_WAITING_CANCEL: statlist_remove(&pool->waiting_cancel_req_list, &client->head); break; default: fatal("bad cur client state: %d", client->state); } client->state = newstate; /* put to new location */ switch (client->state) { case CL_FREE: client_free(client); break; case CL_JUSTFREE: statlist_append(&justfree_client_list, &client->head); break; case CL_LOGIN: statlist_append(&login_client_list, &client->head); break; case CL_WAITING: case CL_WAITING_LOGIN: client->wait_start = get_cached_time(); statlist_append(&pool->waiting_client_list, &client->head); break; case CL_ACTIVE: statlist_append(&pool->active_client_list, &client->head); break; case CL_ACTIVE_CANCEL: statlist_append(&pool->active_cancel_req_list, &client->head); break; case CL_WAITING_CANCEL: statlist_append(&pool->waiting_cancel_req_list, &client->head); break; default: fatal("bad new client state: %d", client->state); } } /* state change means moving between lists */ void change_server_state(PgSocket *server, SocketState newstate) { PgPool *pool = server->pool; /* remove from old location */ switch (server->state) { case SV_FREE: break; case SV_JUSTFREE: statlist_remove(&justfree_server_list, &server->head); break; case SV_LOGIN: statlist_remove(&pool->new_server_list, &server->head); break; case SV_USED: statlist_remove(&pool->used_server_list, &server->head); break; case SV_TESTED: statlist_remove(&pool->tested_server_list, &server->head); break; case SV_BEING_CANCELED: statlist_remove(&pool->being_canceled_server_list, &server->head); break; case SV_IDLE: statlist_remove(&pool->idle_server_list, &server->head); break; case SV_ACTIVE: statlist_remove(&pool->active_server_list, &server->head); break; case SV_ACTIVE_CANCEL: statlist_remove(&pool->active_cancel_server_list, &server->head); break; default: fatal("bad old server state: %d", server->state); } server->state = newstate; /* put to new location */ switch (server->state) { case SV_FREE: server_free(server); break; case SV_JUSTFREE: statlist_append(&justfree_server_list, &server->head); break; case SV_LOGIN: statlist_append(&pool->new_server_list, &server->head); break; case SV_USED: /* use LIFO */ statlist_prepend(&pool->used_server_list, &server->head); break; case SV_TESTED: statlist_append(&pool->tested_server_list, &server->head); break; case SV_BEING_CANCELED: statlist_append(&pool->being_canceled_server_list, &server->head); break; case SV_IDLE: if (server->close_needed || cf_server_round_robin) { /* try to avoid immediate usage then */ statlist_append(&pool->idle_server_list, &server->head); } else { /* otherwise use LIFO */ statlist_prepend(&pool->idle_server_list, &server->head); } break; case SV_ACTIVE: statlist_append(&pool->active_server_list, &server->head); break; case SV_ACTIVE_CANCEL: statlist_append(&pool->active_cancel_server_list, &server->head); break; default: fatal("bad server state: %d", server->state); } } /* compare pool names, for use with put_in_order */ static int cmp_pool(struct List *i1, struct List *i2) { PgPool *p1 = container_of(i1, PgPool, head); PgPool *p2 = container_of(i2, PgPool, head); if (p1->db != p2->db) return strcmp(p1->db->name, p2->db->name); if (p1->user_credentials != p2->user_credentials) { if (p1->user_credentials == NULL) { return 1; } if (p2->user_credentials == NULL) { return -1; } return strcmp(p1->user_credentials->name, p2->user_credentials->name); } return 0; } /* compare pool names, for use with put_in_order */ static int cmp_peer_pool(struct List *i1, struct List *i2) { PgPool *p1 = container_of(i1, PgPool, head); PgPool *p2 = container_of(i2, PgPool, head); if (p1->db != p2->db) return p1->db->peer_id - p2->db->peer_id; return 0; } /* compare user names, for use with put_in_order */ static int cmp_user(struct List *i1, struct List *i2) { PgGlobalUser *u1 = container_of(i1, PgGlobalUser, head); PgGlobalUser *u2 = container_of(i2, PgGlobalUser, head); return strcmp(u1->credentials.name, u2->credentials.name); } /* compare db names, for use with put_in_order */ static int cmp_peer(struct List *i1, struct List *i2) { PgDatabase *db1 = container_of(i1, PgDatabase, head); PgDatabase *db2 = container_of(i2, PgDatabase, head); return db1->peer_id - db2->peer_id; } /* compare db names, for use with put_in_order */ static int cmp_database(struct List *i1, struct List *i2) { PgDatabase *db1 = container_of(i1, PgDatabase, head); PgDatabase *db2 = container_of(i2, PgDatabase, head); return strcmp(db1->name, db2->name); } /* put elem into list in correct pos */ static void put_in_order(struct List *newitem, struct StatList *list, int (*cmpfn)(struct List *, struct List *)) { int res; struct List *item; statlist_for_each(item, list) { res = cmpfn(item, newitem); if (res == 0) { fatal("put_in_order: found existing elem"); } else if (res > 0) { statlist_put_before(list, newitem, item); return; } } statlist_append(list, newitem); } /* create new object if new, then return it */ PgDatabase *add_peer(const char *name, int peer_id) { PgDatabase *peer = find_peer(peer_id); /* create new object if needed */ if (peer == NULL) { peer = slab_alloc(peer_cache); if (!peer) return NULL; list_init(&peer->head); peer->peer_id = peer_id; put_in_order(&peer->head, &peer_list, cmp_peer); } return peer; } /* create new object if new, then return it */ PgDatabase *add_database(const char *name) { PgDatabase *db = find_database(name); /* create new object if needed */ if (db == NULL) { db = slab_alloc(db_cache); if (!db) return NULL; list_init(&db->head); if (strlcpy(db->name, name, sizeof(db->name)) >= sizeof(db->name)) { log_warning("too long db name: %s", name); slab_free(db_cache, db); return NULL; } aatree_init(&db->user_tree, credentials_node_cmp, credentials_node_release); put_in_order(&db->head, &database_list, cmp_database); } return db; } /* register new auto database */ PgDatabase *register_auto_database(const char *name) { PgDatabase *db; if (!cf_autodb_connstr) return NULL; if (!parse_database(NULL, name, cf_autodb_connstr)) return NULL; db = find_database(name); if (db) { db->db_auto = true; } return db; } PgGlobalUser *update_global_user_passwd(PgGlobalUser *user, const char *passwd) { Assert(user); passwd = passwd ? passwd : ""; safe_strcpy(user->credentials.passwd, passwd, sizeof(user->credentials.passwd)); user->credentials.dynamic_passwd = strlen(passwd) == 0; return user; } static PgGlobalUser *add_new_global_user(const char *name, const char *passwd) { PgGlobalUser *user = slab_alloc(user_cache); if (!user) return NULL; user->credentials.global_user = user; list_init(&user->head); list_init(&user->pool_list); safe_strcpy(user->credentials.name, name, sizeof(user->credentials.name)); put_in_order(&user->head, &user_list, cmp_user); aatree_insert(&user_tree, (uintptr_t)user->credentials.name, &user->credentials.tree_node); user->pool_mode = POOL_INHERIT; user->pool_size = -1; user->res_pool_size = -1; return update_global_user_passwd(user, passwd); } /* * Add dynamic credentials to this database. This should be used for dynamic * credentials, that were retrieved using the auth_query. */ PgCredentials *add_dynamic_credentials(PgDatabase *db, const char *name, const char *passwd) { PgCredentials *credentials = NULL; struct AANode *node; /* * Dynamic credentials are stored in an aatree that's specific to the * database. So we cannot use find_global_user() here. */ node = aatree_search(&db->user_tree, (uintptr_t)name); credentials = node ? container_of(node, PgCredentials, tree_node) : NULL; if (credentials == NULL) { credentials = slab_alloc(credentials_cache); if (!credentials) return NULL; safe_strcpy(credentials->name, name, sizeof(credentials->name)); credentials->global_user = find_or_add_new_global_user(name, NULL); if (!credentials->global_user) { slab_free(credentials_cache, credentials); return NULL; } aatree_insert(&db->user_tree, (uintptr_t)credentials->name, &credentials->tree_node); } safe_strcpy(credentials->passwd, passwd, sizeof(credentials->passwd)); credentials->dynamic_passwd = true; return credentials; } /* Add PAM user. The logic is same as in add_dynamic_credentials */ PgCredentials *add_pam_credentials(const char *name, const char *passwd) { PgCredentials *credentials = NULL; struct AANode *node; node = aatree_search(&pam_user_tree, (uintptr_t)name); credentials = node ? container_of(node, PgCredentials, tree_node) : NULL; if (credentials == NULL) { credentials = slab_alloc(credentials_cache); if (!credentials) return NULL; safe_strcpy(credentials->name, name, sizeof(credentials->name)); credentials->global_user = find_or_add_new_global_user(name, NULL); if (!credentials->global_user) { slab_free(credentials_cache, credentials); return NULL; } aatree_insert(&pam_user_tree, (uintptr_t)credentials->name, &credentials->tree_node); } if (passwd) safe_strcpy(credentials->passwd, passwd, sizeof(credentials->passwd)); return credentials; } /* create separate PgCredentials object for this database */ PgCredentials *force_user_credentials(PgDatabase *db, const char *name, const char *passwd) { PgCredentials *credentials = db->forced_user_credentials; if (!credentials) { credentials = slab_alloc(credentials_cache); if (!credentials) return NULL; credentials->global_user = find_or_add_new_global_user(name, NULL); if (!credentials->global_user) { slab_free(credentials_cache, credentials); return NULL; } } safe_strcpy(credentials->name, name, sizeof(credentials->name)); safe_strcpy(credentials->passwd, passwd, sizeof(credentials->passwd)); db->forced_user_credentials = credentials; return credentials; } /* find an existing database */ PgDatabase *find_peer(int peer_id) { struct List *item; PgDatabase *peer; statlist_for_each(item, &peer_list) { peer = container_of(item, PgDatabase, head); if (peer->peer_id == peer_id) return peer; } return NULL; } /* find an existing database */ PgDatabase *find_database(const char *name) { struct List *item, *tmp; PgDatabase *db; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (strcmp(db->name, name) == 0) return db; } /* also trying to find in idle autodatabases list */ statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); if (strcmp(db->name, name) == 0) { db->inactive_time = 0; statlist_remove(&autodatabase_idle_list, &db->head); put_in_order(&db->head, &database_list, cmp_database); return db; } } return NULL; } /* * Similar to find_database. In case database is not found, it will try to register * it if auto-database ('*') is configured. */ PgDatabase *find_or_register_database(PgSocket *connection, const char *name) { PgDatabase *db = find_database(name); if (db == NULL) { db = register_auto_database(name); if (db != NULL) { slog_info(connection, "registered new auto-database: %s", name); } } return db; } /* find existing user */ PgGlobalUser *find_global_user(const char *name) { PgGlobalUser *user = NULL; struct AANode *node; node = aatree_search(&user_tree, (uintptr_t)name); /* we use the tree_node in the embedded PgCredentials struct */ user = node ? (PgGlobalUser *) container_of(node, PgCredentials, tree_node) : NULL; return user; } PgCredentials *find_global_credentials(const char *name) { PgGlobalUser *user = find_global_user(name); if (!user) return NULL; return &user->credentials; } /* create new pool object */ static PgPool *new_pool(PgDatabase *db, PgCredentials *user_credentials) { PgPool *pool; pool = slab_alloc(pool_cache); if (!pool) return NULL; list_init(&pool->head); list_init(&pool->map_head); pool->orig_vars.var_list = slab_alloc(var_list_cache); pool->user_credentials = user_credentials; pool->db = db; statlist_init(&pool->active_client_list, "active_client_list"); statlist_init(&pool->waiting_client_list, "waiting_client_list"); statlist_init(&pool->active_server_list, "active_server_list"); statlist_init(&pool->idle_server_list, "idle_server_list"); statlist_init(&pool->tested_server_list, "tested_server_list"); statlist_init(&pool->used_server_list, "used_server_list"); statlist_init(&pool->new_server_list, "new_server_list"); statlist_init(&pool->waiting_cancel_req_list, "waiting_cancel_req_list"); statlist_init(&pool->active_cancel_req_list, "active_cancel_req_list"); statlist_init(&pool->active_cancel_server_list, "active_cancel_server_list"); statlist_init(&pool->being_canceled_server_list, "being_canceled_server_list"); list_append(&user_credentials->global_user->pool_list, &pool->map_head); /* keep pools in db/user order to make stats faster */ put_in_order(&pool->head, &pool_list, cmp_pool); return pool; } /* * create new peer pool object * * This pool should only be used to forward cancellations to other pgbouncers * behind the same load balancer. The user field of this pool is NULL, because * cancellations don't need a user. */ static PgPool *new_peer_pool(PgDatabase *db) { PgPool *pool; pool = slab_alloc(peer_pool_cache); if (!pool) return NULL; list_init(&pool->head); list_init(&pool->map_head); pool->orig_vars.var_list = slab_alloc(var_list_cache); pool->db = db; statlist_init(&pool->new_server_list, "new_server_list"); statlist_init(&pool->waiting_cancel_req_list, "waiting_cancel_req_list"); statlist_init(&pool->active_cancel_req_list, "active_cancel_req_list"); statlist_init(&pool->active_cancel_server_list, "active_cancel_server_list"); /* keep pools in peer_id order to make stats faster */ put_in_order(&pool->head, &peer_pool_list, cmp_peer_pool); return pool; } /* find pool object, create if needed */ PgPool *get_pool(PgDatabase *db, PgCredentials *user_credentials) { struct List *item; PgPool *pool; if (!db || !user_credentials) return NULL; list_for_each(item, &user_credentials->global_user->pool_list) { pool = container_of(item, PgPool, map_head); if (pool->db == db) return pool; } return new_pool(db, user_credentials); } /* find pool object for the peer */ PgPool *get_peer_pool(PgDatabase *db) { if (!db) return NULL; if (!db->pool) { db->pool = new_peer_pool(db); } return db->pool; } /* deactivate socket and put into wait queue */ static void pause_client(PgSocket *client) { Assert(client->state == CL_ACTIVE || client->state == CL_LOGIN); slog_debug(client, "pause_client"); if (cf_shutdown == SHUTDOWN_WAIT_FOR_SERVERS) { disconnect_client(client, true, "server shutting down"); return; } change_client_state(client, CL_WAITING); if (!sbuf_pause(&client->sbuf)) disconnect_client(client, true, "pause failed"); } /* * Deactivate the client socket and put it into the cancel request wait queue. * We're not expecting any data from the client anymore at this point at all. * But some clients might send some anyway (specifically the Go client). Since * we don't care about any of that extra data we just stop reading from the * socket. */ static void pause_cancel_request(PgSocket *client) { Assert(client->state == CL_LOGIN); slog_debug(client, "pause_cancel_request"); change_client_state(client, CL_WAITING_CANCEL); if (!sbuf_pause(&client->sbuf)) disconnect_client(client, true, "pause cancel request failed"); } /* wake client from wait */ void activate_client(PgSocket *client) { Assert(client->state == CL_WAITING || client->state == CL_WAITING_LOGIN); Assert(client->wait_start > 0); /* account for time client spent waiting for server */ client->pool->stats.wait_time += (get_cached_time() - client->wait_start); slog_debug(client, "activate_client"); change_client_state(client, CL_ACTIVE); sbuf_continue(&client->sbuf); } /* * Don't let clients queue at all if there is no working server connection. * * It must still allow following cases: * - empty pool on startup * - idle pool where all servers are removed * * Current assumptions: * - old server connections will be dropped by query_timeout * - new server connections fail due to server_connect_timeout, or other failure * * So here we drop client if all server connections have been dropped * and new ones fail. * * Return true if the client connection should be allowed, false if it * should be rejected. */ bool check_fast_fail(PgSocket *client) { int cnt; PgPool *pool = client->pool; /* Could be mock authentication, proceed normally */ if (!pool) return true; /* If last login succeeded, client can go ahead. */ if (!pool->last_login_failed) return true; /* If there are servers available, client can go ahead. */ cnt = pool_server_count(pool) - statlist_count(&pool->new_server_list); if (cnt) return true; /* Else we fail the client. */ disconnect_client(client, true, "server login has been failing, cached error: %s (server_login_retry)", pool->last_connect_failed_message); /* * Try to launch a new connection. (launch_new_connection() * will check for server_login_retry etc.) The usual relaunch * from janitor.c won't do anything, as there are no waiting * clients, so we need to do it here to get any new servers * eventually. */ launch_new_connection(pool, /* evict_if_needed= */ true); return false; } /* link if found, otherwise put into wait queue */ bool find_server(PgSocket *client) { PgPool *pool = client->pool; PgSocket *server; bool res; bool varchange = false; Assert(client->state == CL_ACTIVE || client->state == CL_LOGIN); /* no wait by default */ client->wait_start = 0; if (client->link) return true; slog_noise(client, "find_server: client had no linked server yet"); /* try to get idle server, if allowed */ if (cf_pause_mode == P_PAUSE || pool->db->db_paused) { server = NULL; } else if (client->replication && !sending_auth_query(client)) { /* * For replication clients we open dedicated server connections. These * connections are linked to a client as soon as the server is ready, * instead of lazily being assigned to a client only when the client * sends a query. So if we reach this point we know that that has not * happened yet, and we need to create a new replication connection for * this client. */ launch_new_connection(pool, /*evict_if_needed= */ true); server = NULL; } else { while (1) { server = first_socket(&pool->idle_server_list); if (!server) { break; } else if (server->close_needed) { disconnect_server(server, true, "obsolete connection"); } else if (!server->ready) { disconnect_server(server, true, "idle server got dirty"); } else { break; } } if (!server && !check_fast_fail(client)) return false; } Assert(!server || server->state == SV_IDLE); /* send var changes */ if (server) { res = varcache_apply(server, client, &varchange); if (!res) { disconnect_server(server, true, "var change failed"); server = NULL; } } /* link or send to waiters list */ if (server) { slog_noise(client, "linking client to S-%p", server); client->link = server; server->link = client; server->pool->stats.server_assignment_count++; change_server_state(server, SV_ACTIVE); if (varchange) { server->setting_vars = true; server->ready = false; res = false; /* don't process client data yet */ slog_noise(client, "pausing client while applying vars"); if (!sbuf_pause(&client->sbuf)) disconnect_client(client, true, "pause failed"); } else { res = true; } } else { pause_client(client); res = false; } return res; } /* pick waiting client */ static bool reuse_on_release(PgSocket *server) { bool res = true; PgPool *pool = server->pool; PgSocket *client; Assert(!server->replication); slog_debug(server, "reuse_on_release: replication %d", server->replication); client = first_socket(&pool->waiting_client_list); if (client && (!client->replication || sending_auth_query(client))) { activate_client(client); /* * As the activate_client() does full read loop, * then it may happen that linked client closing * causes server closing. Report it. */ if (server->state == SV_FREE || server->state == SV_JUSTFREE) res = false; } return res; } bool queue_fake_response(PgSocket *client, char request_type) { bool res = true; PgSocket *server = client->link; Assert(server); if (request_type == PqMsg_Parse) { slog_debug(client, "Queuing fake ParseComplete packet"); QUEUE_ParseComplete(res, server, client); } else if (request_type == PqMsg_Close) { slog_debug(client, "Queuing fake CloseComplete packet"); QUEUE_CloseComplete(res, server, client); } else { fatal("Unknown fake request type %c", request_type); } return res; } /* Find an existing global user or add a new global user */ PgGlobalUser *find_or_add_new_global_user(const char *name, const char *passwd) { PgGlobalUser *user = find_global_user(name); if (!user) user = add_new_global_user(name, passwd); return user; } /* Find an existing global credentials or add a new global credentials */ PgCredentials *find_or_add_new_global_credentials(const char *name, const char *passwd) { PgGlobalUser *user = find_or_add_new_global_user(name, passwd); if (!user) return NULL; return &user->credentials; } /* * Adds a request to the outstanding requests queue, and schedule the given * action (see comments on ResponseAction for details). * * returns false if the required allocations failed */ bool add_outstanding_request(PgSocket *client, char type, ResponseAction action) { OutstandingRequest *request = NULL; PgSocket *server = client->link; Assert(server); if (action == RA_FAKE && statlist_empty(&server->outstanding_requests)) { /* * If there's no outstanding requests, we can send the response * right away. And we're actually required to do that to make * sure the client receives it, because we normally only send * responses to fake requests right after we handle a response * to a real request. So if none are outstanding, we won't send * such a response. */ slog_noise(client, "add_outstanding_request: queueing fake response right away %c", type); return queue_fake_response(client, type); } request = slab_alloc(outstanding_request_cache); if (request == NULL) return false; request->type = type; request->action = action; statlist_append(&server->outstanding_requests, &request->node); slog_noise(client, "add_outstanding_request: added %c, still outstanding %d", type, statlist_count(&client->link->outstanding_requests)); return true; } /* * If the next outstanding request is of one of the given types, pop it off the * queue. If it is of a different type, don't do anything. * * returns true if one of the given types was popped of off the queue. */ bool pop_outstanding_request(PgSocket *server, const char types[], bool *skip) { OutstandingRequest *request; struct List *item = statlist_first(&server->outstanding_requests); if (!item) return false; request = container_of(item, OutstandingRequest, node); if (request->action == RA_FAKE) { /* * This is weird, normally we should have already processed all fake * requests at the end of the previous packet. */ slog_warning(server, "pop_outstanding_request: unexpected fake request of type %c", request->type); return false; } if (strchr(types, request->type) == NULL) return false; statlist_pop(&server->outstanding_requests); if (skip) *skip = request->action == RA_SKIP; slog_noise(server, "pop_outstanding_request: popped %c, still outstanding %d, skip %d", request->type, statlist_count(&server->outstanding_requests), request->action == RA_SKIP); if (request->server_ps != NULL) { free_server_prepared_statement(request->server_ps); } slab_free(outstanding_request_cache, request); return true; } /* * Clear all outstanding requests until we reach response of any of the message * types in "types". Any Parse or Close statement requests that were still * outstanding will be unregistered or re-registered from the server its cache. */ bool clear_outstanding_requests_until(PgSocket *server, const char types[]) { struct List *item, *tmp; statlist_for_each_safe(item, &server->outstanding_requests, tmp) { OutstandingRequest *request = container_of(item, OutstandingRequest, node); char type = request->type; if (type == PqMsg_Parse && request->server_ps_query_id > 0) { unregister_prepared_statement(server, request->server_ps_query_id); slog_noise(server, "failed prepared statement '" PREPARED_STMT_NAME_FORMAT "' removed from server cache, %d cached items", request->server_ps_query_id, HASH_COUNT(server->server_prepared_statements)); } else if (type == PqMsg_Close && request->server_ps != NULL) { if (!add_prepared_statement(server, request->server_ps)) { if (server->link) disconnect_client(server->link, true, "out of memory"); disconnect_server(server, true, "out of memory"); return false; } slog_noise(server, "prepared statement '%s' added back to server cache, %d cached items", request->server_ps->ps->stmt_name, HASH_COUNT(server->server_prepared_statements)); } statlist_remove(&server->outstanding_requests, item); slab_free(outstanding_request_cache, request); if (strchr(types, type)) break; } slog_noise(server, "clear_outstanding_requests_until_sync: still outstanding %d", statlist_count(&server->outstanding_requests)); return true; } /* send reset query */ static bool reset_on_release(PgSocket *server) { bool res; Assert(server->state == SV_TESTED); slog_debug(server, "resetting: %s", cf_server_reset_query); SEND_generic(res, server, PqMsg_Query, "s", cf_server_reset_query); if (!res) disconnect_server(server, false, "reset query failed"); return res; } bool life_over(PgSocket *server) { PgPool *pool = server->pool; usec_t lifetime_kill_gap = 0; usec_t now = get_cached_time(); usec_t age = now - server->connect_time; usec_t last_kill = now - pool->last_lifetime_disconnect; usec_t server_lifetime = pool_server_lifetime(pool); if (age < server_lifetime) return false; /* * Calculate the time that disconnects because of server_lifetime * must be separated. This avoids the need to re-launch lot * of connections together. */ if (pool_pool_size(pool) > 0) lifetime_kill_gap = server_lifetime / pool_pool_size(pool); if (last_kill >= lifetime_kill_gap) return true; return false; } /* connecting/active -> idle, unlink if needed */ bool release_server(PgSocket *server) { PgPool *pool = server->pool; SocketState newstate = SV_IDLE; struct List *cancel_item, *tmp; Assert(server->ready); /* remove from old list */ switch (server->state) { case SV_BEING_CANCELED: case SV_ACTIVE: if (server->link) { server->link->link = NULL; server->link = NULL; } if (*cf_server_reset_query && (cf_server_reset_query_always || connection_pool_mode(server) == POOL_SESSION)) { /* notify reset is required */ newstate = SV_TESTED; } else if (cf_server_check_delay == 0 && *cf_server_check_query) { /* * deprecated: before reset_query, the check_delay = 0 * was used to get same effect. This if() can be removed * after couple of releases. */ newstate = SV_USED; } case SV_USED: case SV_TESTED: break; case SV_LOGIN: pool->last_login_failed = false; pool->last_connect_failed = false; break; default: fatal("bad server state: %d", server->state); } statlist_for_each_safe(cancel_item, &server->canceling_clients, tmp) { PgSocket *cancel_client = container_of(cancel_item, PgSocket, cancel_head); /* * If the cancel request is not in flight yet we can simply unlink * the cancel_client. When a cancel request doesn't have a * canceled_server linked to it forward_cancel_request will simply drop * the cancel request without forwarding it anywhere. */ if (cancel_client->state == CL_WAITING_CANCEL) { cancel_client->canceled_server = NULL; statlist_remove(&server->canceling_clients, cancel_item); } } /* enforce lifetime immediately on release */ if (server->state != SV_LOGIN && life_over(server)) { disconnect_server(server, true, "server lifetime over"); pool->last_lifetime_disconnect = get_cached_time(); return false; } if (statlist_count(&server->outstanding_requests) > 0) { /* * We can't release the server if there are outstanding requests * that haven't been responded to yet, otherwise the server * might get linked to another client and it will get those * responses when it does not expect them. To be on the safe * side we simply close this connection. */ disconnect_server(server, true, "client disconnected with queries in progress"); return true; } /* enforce close request */ if (server->close_needed) { disconnect_server(server, true, "close_needed"); return false; } if (statlist_count(&server->canceling_clients) > 0) { change_server_state(server, SV_BEING_CANCELED); return true; } if (server->replication) { if (server->link) { slog_debug(server, "release_server: new replication connection ready"); change_server_state(server, SV_ACTIVE); activate_client(server->link); return true; } else { disconnect_server(server, true, "replication client was closed"); return false; } } Assert(server->link == NULL); slog_noise(server, "release_server: new state=%d", newstate); change_server_state(server, newstate); if (newstate == SV_IDLE) { /* immediately process waiters, to give fair chance */ return reuse_on_release(server); } else if (newstate == SV_TESTED) { return reset_on_release(server); } return true; } static void unlink_server(PgSocket *server, const char *reason) { PgSocket *client; if (!server->link) return; client = server->link; client->link = NULL; server->link = NULL; /* * Send reason to client if it is already * logged in, otherwise send generic message. */ if (client->state == CL_ACTIVE || client->state == CL_WAITING) disconnect_client(client, true, "%s", reason); else if (client->state == CL_ACTIVE_CANCEL) disconnect_client(client, false, "successfully sent cancel request"); else disconnect_client(client, true, "bouncer config error"); } /* * close server connection * * send_term=true means to send a Terminate message to the server * before disconnecting, send_term=false means to disconnect without. * The latter is for protocol and communication errors where a normal * protocol termination is not possible. */ void disconnect_server(PgSocket *server, bool send_term, const char *reason, ...) { usec_t now = get_cached_time(); char buf[128]; va_list ap; struct List *cancel_item, *tmp; if (server == NULL) { return; } va_start(ap, reason); vsnprintf(buf, sizeof(buf), reason, ap); va_end(ap); reason = buf; if (cf_log_disconnections) { slog_info(server, "closing because: %s (age=%" PRIu64 "s)", reason, (now - server->connect_time) / USEC); } switch (server->state) { case SV_ACTIVE_CANCEL: case SV_ACTIVE: unlink_server(server, reason); break; case SV_TESTED: case SV_USED: case SV_IDLE: case SV_BEING_CANCELED: break; case SV_LOGIN: /* * usually disconnect means problems in startup phase, * except when sending cancel packet */ if (!server->ready) { server->pool->last_login_failed = true; server->pool->last_connect_failed = true; safe_strcpy(server->pool->last_connect_failed_message, reason, sizeof(server->pool->last_connect_failed_message)); } else { /* * We did manage to connect and used the connection for query * cancellation, so to the best of our knowledge we can connect to * the server, reset last_connect_failed accordingly. */ server->pool->last_connect_failed = false; send_term = false; } if (server->replication) unlink_server(server, reason); break; default: fatal("bad server state: %d", server->state); } Assert(server->link == NULL); statlist_for_each_safe(cancel_item, &server->canceling_clients, tmp) { PgSocket *cancel_client = container_of(cancel_item, PgSocket, cancel_head); cancel_client->canceled_server = NULL; statlist_remove(&server->canceling_clients, cancel_item); } /* notify server and close connection */ if (send_term) { static const uint8_t pkt_term[] = {PqMsg_Terminate, 0, 0, 0, 4}; bool _ignore = sbuf_answer(&server->sbuf, pkt_term, sizeof(pkt_term)); (void) _ignore; } if (server->dns_token) { adns_cancel(adns, server->dns_token); server->dns_token = NULL; } free_scram_state(&server->scram_state); server->pool->db->connection_count--; if (server->pool->user_credentials) server->pool->user_credentials->global_user->connection_count--; change_server_state(server, SV_JUSTFREE); if (!sbuf_close(&server->sbuf)) log_noise("sbuf_close failed, retry later"); } /* * A wrapper around disconnect_client_sqlstate() * * The function disconnect_client_sqlstate() inherits the disconnect_client() * content and add a new option that provides a specific SQLSTATE that is * forwarded to client. PgBouncer used to report SQLSTATE 08P01 * (protocol_violation) for all cases but it diverges from what Postgres * reports in some cases. */ void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) { if (reason) { char buf[128]; va_list ap; va_start(ap, reason); vsnprintf(buf, sizeof(buf), reason, ap); va_end(ap); disconnect_client_sqlstate(client, notify, NULL, buf); } else { disconnect_client_sqlstate(client, notify, NULL, reason); } } /* * close client connection * * notify=true means to send the reason message as an error to the * client, notify=false means no message is sent. The latter is for * protocol and communication errors where sending a regular error * message is not possible. */ void disconnect_client_sqlstate(PgSocket *client, bool notify, const char *sqlstate, const char *reason) { usec_t now = get_cached_time(); if (client->db && client->contributes_db_client_count) client->db->client_connection_count--; if (client->login_user_credentials) { if (client->login_user_credentials->global_user && client->user_connection_counted) { client->login_user_credentials->global_user->client_connection_count--; } } if (cf_log_disconnections && reason) { slog_info(client, "closing because: %s (age=%" PRIu64 "s)", reason, (now - client->connect_time) / USEC); } switch (client->state) { case CL_ACTIVE: case CL_LOGIN: if (client->link) { PgSocket *server = client->link; if (!server->ready) { server->link = NULL; client->link = NULL; /* * This can happen if the client * connection is normally closed while * the server has a transaction block * open. Then there is no way for us * to reset the server other than by * closing it. Perhaps it would be * worth tracking this separately to * make the error message more * precise and less scary. */ disconnect_server(server, true, "client disconnect while server was not ready"); } else if (statlist_count(&server->outstanding_requests) > 0) { server->link = NULL; client->link = NULL; /* * If there are outstanding requests we can't * release the server, because the responses * might be received by a different client. So * we need to close the client connection * immediately. */ disconnect_server(server, true, "client disconnected with query in progress"); } else if (!sbuf_is_empty(&server->sbuf)) { /* ->ready may be set before all is sent */ server->link = NULL; client->link = NULL; disconnect_server(server, true, "client disconnect before everything was sent to the server"); } else { /* retval does not matter here */ release_server(server); } } break; case CL_ACTIVE_CANCEL: case CL_WAITING_CANCEL: /* * During normal operation, cancel clients get closed because their * linked server finished sending the cancel request. But this is not * always the case. It's possible for the client to disconnect * itself. To avoid a reference to freed client object from the linked * server in such cases, we now unlink any still linked server. */ if (client->link) { PgSocket *server = client->link; server->link = NULL; client->link = NULL; disconnect_server(server, false, "client gave up on cancel request, so we also give up forwarding to server"); } /* * If the cancel request is still linked to the server that it * cancelled (or wanted to cancel) a query from, this is the time to * unlink them. The cancel request has finished at this point and we're * going to free its memory soon, so we don't want references to it * left behind. */ if (client->canceled_server) { PgSocket *canceled_server = client->canceled_server; statlist_remove(&canceled_server->canceling_clients, &client->cancel_head); client->canceled_server = NULL; /* * If the linked server was waiting until all cancel requests * targeting it were finished, and we were the last cancel request, * then we can now safely move the server to the idle state. We * trigger this by calling release_server again. */ if (canceled_server->state == SV_BEING_CANCELED && statlist_count(&canceled_server->canceling_clients) == 0) { release_server(canceled_server); } } break; case CL_WAITING: case CL_WAITING_LOGIN: /* * replication connections might already be linked to a server * while they are still in a waiting state. */ if (client->replication && client->link) { PgSocket *server = client->link; server->link = NULL; client->link = NULL; disconnect_server(server, false, "replication client disconnected"); } break; default: fatal("bad client state: %d", client->state); } /* send reason to client */ if (notify && reason && client->state != CL_WAITING_CANCEL) { /* * don't send Ready pkt here, or client won't notice * closed connection */ send_pooler_error(client, false, sqlstate, true, reason); } free_header(&client->packet_cb_state.pkt); free_scram_state(&client->scram_state); if (client->login_user_credentials && client->login_user_credentials->mock_auth) { free(client->login_user_credentials); client->login_user_credentials = NULL; } if (client->db && client->db->fake) { free(client->db); client->db = NULL; } free(client->startup_options); client->startup_options = NULL; change_client_state(client, CL_JUSTFREE); if (!sbuf_close(&client->sbuf)) log_noise("sbuf_close failed, retry later"); } /* * Connection creation utilities */ static void connect_server(struct PgSocket *server, const struct sockaddr *sa, int salen) { bool res; /* fill remote_addr */ memset(&server->remote_addr, 0, sizeof(server->remote_addr)); if (sa->sa_family == AF_UNIX) { pga_set(&server->remote_addr, AF_UNIX, server->pool->db->port); } else { pga_copy(&server->remote_addr, sa); } slog_debug(server, "launching new connection to server"); /* start connecting */ res = sbuf_connect(&server->sbuf, sa, salen, cf_server_connect_timeout / USEC); if (!res) log_noise("failed to launch new connection"); } static void dns_callback(void *arg, const struct sockaddr *sa, int salen) { struct PgSocket *server = arg; struct PgDatabase *db = server->pool->db; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; server->dns_token = NULL; if (!sa) { disconnect_server(server, true, "server DNS lookup failed"); return; } else if (sa->sa_family == AF_INET) { char buf[64]; memcpy(&sa_in, sa, sizeof(sa_in)); sa_in.sin_port = htons(db->port); sa = (struct sockaddr *)&sa_in; salen = sizeof(sa_in); slog_debug(server, "dns_callback: inet4: %s", sa2str(sa, buf, sizeof(buf))); } else if (sa->sa_family == AF_INET6) { char buf[64]; memcpy(&sa_in6, sa, sizeof(sa_in6)); sa_in6.sin6_port = htons(db->port); sa = (struct sockaddr *)&sa_in6; salen = sizeof(sa_in6); slog_debug(server, "dns_callback: inet6: %s", sa2str(sa, buf, sizeof(buf))); } else { disconnect_server(server, true, "unknown address family: %d", sa->sa_family); return; } connect_server(server, sa, salen); } static void dns_connect(struct PgSocket *server) { struct sockaddr_un sa_un; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; struct sockaddr *sa; struct PgDatabase *db = server->pool->db; const char *host; int sa_len; int res; char *host_copy = NULL; /* host list? */ if (db->host && strchr(db->host, ',')) { int count = 1; int n; if (server->pool->db->load_balance_hosts == LOAD_BALANCE_HOSTS_DISABLE && server->pool->last_connect_failed) server->pool->rrcounter++; for (const char *p = db->host; *p; p++) if (*p == ',') count++; host_copy = xstrdup(db->host); for (host = strtok(host_copy, ","), n = 0; host; host = strtok(NULL, ","), n++) if (server->pool->rrcounter % count == n) break; Assert(host); if (server->pool->db->load_balance_hosts == LOAD_BALANCE_HOSTS_ROUND_ROBIN) server->pool->rrcounter++; } else { host = db->host; } if (!host || host[0] == '/' || host[0] == '@') { const char *unix_dir; memset(&sa_un, 0, sizeof(sa_un)); sa_un.sun_family = AF_UNIX; unix_dir = host ? host : cf_unix_socket_dir; if (!unix_dir || !*unix_dir) { log_error("unix socket dir not configured: %s", db->name); disconnect_server(server, false, "cannot connect"); goto cleanup; } snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s/.s.PGSQL.%d", unix_dir, db->port); slog_noise(server, "unix socket: %s", sa_un.sun_path); if (unix_dir[0] == '@') { /* * By convention, for abstract Unix sockets, * only the length of the string is the * sockaddr length. */ sa_len = offsetof(struct sockaddr_un, sun_path) + strlen(sa_un.sun_path); sa_un.sun_path[0] = '\0'; } else { sa_len = sizeof(sa_un); } sa = (struct sockaddr *)&sa_un; res = 1; } else if (strchr(host, ':')) { /* assume IPv6 address on any : in addr */ slog_noise(server, "inet6 socket: %s", host); memset(&sa_in6, 0, sizeof(sa_in6)); sa_in6.sin6_family = AF_INET6; res = inet_pton(AF_INET6, host, &sa_in6.sin6_addr); sa_in6.sin6_port = htons(db->port); sa = (struct sockaddr *)&sa_in6; sa_len = sizeof(sa_in6); } else {/* else try IPv4 */ slog_noise(server, "inet socket: %s", host); memset(&sa_in, 0, sizeof(sa_in)); sa_in.sin_family = AF_INET; res = inet_pton(AF_INET, host, &sa_in.sin_addr); sa_in.sin_port = htons(db->port); sa = (struct sockaddr *)&sa_in; sa_len = sizeof(sa_in); } /* if simple parse failed, use DNS */ if (res != 1) { struct DNSToken *tk; slog_noise(server, "dns socket: %s", host); /* launch dns lookup */ tk = adns_resolve(adns, host, dns_callback, server); if (tk) server->dns_token = tk; goto cleanup; } connect_server(server, sa, sa_len); cleanup: free(host_copy); } PgSocket *compare_connections_by_time(PgSocket *lhs, PgSocket *rhs) { if (!lhs) return rhs; if (!rhs) return lhs; return lhs->request_time < rhs->request_time ? lhs : rhs; } /* evict the single most idle connection from among all pools to make room in the db */ bool evict_connection(PgDatabase *db) { struct List *item; PgPool *pool; PgSocket *oldest_connection = NULL; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db != db) continue; oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->idle_server_list)); /* only evict testing connections if nobody's waiting */ if (statlist_empty(&pool->waiting_client_list)) { oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->used_server_list)); oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->tested_server_list)); } } if (oldest_connection) { disconnect_server(oldest_connection, true, "evicted"); return true; } return false; } /* evict the oldest idle connection from the pool */ bool evict_pool_connection(PgPool *pool) { PgSocket *oldest_connection = NULL; oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->idle_server_list)); if (oldest_connection) { disconnect_server(oldest_connection, true, "evicted"); return true; } return false; } /* evict the single most idle connection from among all pools to make room in the user */ bool evict_user_connection(PgCredentials *user_credentials) { struct List *item; PgPool *pool; PgSocket *oldest_connection = NULL; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->user_credentials != user_credentials) continue; oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->idle_server_list)); /* only evict testing connections if nobody's waiting */ if (statlist_empty(&pool->waiting_client_list)) { oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->used_server_list)); oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->tested_server_list)); } } if (oldest_connection) { disconnect_server(oldest_connection, true, "evicted"); return true; } return false; } /* * Launches a new connection if possible. * * Called when the pool needs new connection. * * If `evict_if_needed` is true and the db or user has reached their * connection limits, this method will attempt to evict existing connections * from other users/dbs to make room for the new connection. */ void launch_new_connection(PgPool *pool, bool evict_if_needed) { PgSocket *server; int max; log_debug("launch_new_connection: start"); /* * Allow only a single connection attempt at a time. * * NOTE: If this is ever changed to allow more than a single connection * attempt at once (which would probably be a good thing), some code needs * to change that depends on the fact that there's only ever one connection * attempt at once. At least a little bit below in this function, where * connections are opened for cancel requests. */ if (!statlist_empty(&pool->new_server_list)) { log_debug("launch_new_connection: already progress"); return; } /* if server bounces, don't retry too fast */ if (pool->last_connect_failed) { usec_t now = get_cached_time(); if (now - pool->last_connect_time < cf_server_login_retry) { log_debug("launch_new_connection: last failed, not launching new connection yet, still waiting %" PRIu64 " s", (cf_server_login_retry - (now - pool->last_connect_time)) / USEC); return; } } max = pool_server_count(pool); /* * Peer pools only have a single pool_size. */ if (pool->db->peer_id) { if (max < pool_pool_size(pool)) goto force_new; log_debug("launch_new_connection: peer pool full (%d >= %d)", max, pool_pool_size(pool)); return; } /* * When a cancel request is queued allow connections up to twice the pool * size. * * NOTE: This logic might seem a bit confusing, because it seems like we'll * open many connections even if there's only a single cancel request. But * this works just fine, because we only ever open a single connection at * once (see top of this function). */ if (!statlist_empty(&pool->waiting_cancel_req_list) && max < (2 * pool_pool_size(pool))) { log_debug("launch_new_connection: bypass pool limitations for cancel request"); goto force_new; } /* is it allowed to add servers? */ if (pool_pool_size(pool) > 0) { if (max >= pool_pool_size(pool) && pool->welcome_msg_ready) { /* should we use reserve pool? */ PgSocket *c = first_socket(&pool->waiting_client_list); if (cf_res_pool_timeout && pool_res_pool_size(pool)) { usec_t now = get_cached_time(); if (c && (now - c->request_time) >= cf_res_pool_timeout) { if (max < pool_pool_size(pool) + pool_res_pool_size(pool)) { slog_warning(c, "taking connection from reserve_pool"); goto allow_new; } } } if (c && c->replication && !sending_auth_query(c)) { while (evict_if_needed && pool_pool_size(pool) >= max) { if (!evict_pool_connection(pool)) break; } if (pool_pool_size(pool) < max) goto allow_new; } log_debug("launch_new_connection: pool full (%d >= %d)", max, pool_pool_size(pool)); return; } } allow_new: max = database_max_connections(pool->db); if (max > 0) { /* try to evict unused connections first */ while (evict_if_needed && pool->db->connection_count >= max) { if (!evict_connection(pool->db)) { break; } } if (pool->db->connection_count >= max) { log_debug("launch_new_connection: database '%s' full (%d >= %d)", pool->db->name, pool->db->connection_count, max); return; } } max = user_max_connections(pool->user_credentials->global_user); if (max > 0) { /* try to evict unused connection first */ while (evict_if_needed && pool->user_credentials->global_user->connection_count >= max) { if (!evict_user_connection(pool->user_credentials)) { break; } } if (pool->user_credentials->global_user->connection_count >= max) { log_debug("launch_new_connection: user '%s' full (%d >= %d)", pool->user_credentials->name, pool->user_credentials->global_user->connection_count, max); return; } } force_new: /* get free conn object */ server = slab_alloc(server_cache); if (!server) { log_debug("launch_new_connection: no memory"); return; } /* initialize it */ server->pool = pool; server->login_user_credentials = server->pool->user_credentials; server->connect_time = get_cached_time(); statlist_init(&server->canceling_clients, "canceling_clients"); pool->last_connect_time = get_cached_time(); change_server_state(server, SV_LOGIN); pool->db->connection_count++; if (pool->user_credentials) pool->user_credentials->global_user->connection_count++; dns_connect(server); } /* new client connection attempt */ PgSocket *accept_client(int sock, bool is_unix) { bool res; PgSocket *client; /* get free PgSocket */ client = slab_alloc(client_cache); if (!client) { log_warning("cannot allocate client struct"); safe_close(sock); return NULL; } client->connect_time = client->request_time = get_cached_time(); client->query_start = 0; /* FIXME: take local and remote address from pool_accept() */ fill_remote_addr(client, sock, is_unix); fill_local_addr(client, sock, is_unix); change_client_state(client, CL_LOGIN); res = sbuf_accept(&client->sbuf, sock, is_unix); if (!res) { if (cf_log_connections) slog_debug(client, "failed connection attempt"); return NULL; } return client; } /* send cached parameters to client to pretend being server */ /* client managed to authenticate, send welcome msg and accept queries */ bool finish_client_login(PgSocket *client) { if (client->db->fake) { if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", client->db->name, client->login_user_credentials->name); disconnect_client(client, true, "no such database: %s", client->db->name); return false; } if (client->db->db_disabled) { disconnect_client(client, true, "database \"%s\" is disabled", client->db->name); return false; } switch (client->state) { case CL_LOGIN: change_client_state(client, CL_ACTIVE); case CL_ACTIVE: break; default: fatal("bad client state: %d", client->state); } client->wait_for_auth = false; /* check if we know server signature */ if (!client->pool->welcome_msg_ready) { log_debug("finish_client_login: no welcome message, pause"); client->wait_for_welcome = true; pause_client(client); if (cf_pause_mode == P_NONE) launch_new_connection(client->pool, /* evict_if_needed= */ true); return false; } client->wait_for_welcome = false; /* send the message */ if (!welcome_client(client)) return false; slog_debug(client, "logged in"); return true; } static void accept_cancel_request_for_peer(int peer_id, PgSocket *req) { PgDatabase *peer = NULL; PgPool *pool = NULL; int ttl = req->cancel_key[7] & CANCELLATION_TTL_MASK; if (ttl == 0) { disconnect_client(req, false, "failed to forward cancel request because its TTL was exhausted"); return; } /* * Before forwarding the cancel key, we need to decrement the TTL. Now is * as a good a time as any to do so. We simply subtract 1 from the last * byte, since the TTL is stored in the least significant bits. */ req->cancel_key[7]--; peer = find_peer(peer_id); if (!peer) { disconnect_client(req, false, "could not find peer to forward request to"); return; } log_debug("forwarding cancellation request to peer %d", peer_id); /* * When using peering (multiple pgbouncers behind the same load * balancer), we may receive cancellation messages that were intended * for another peer via the load balancer. We propagate the * cancellation via the peer's pool, instead of the server pool. */ pool = get_peer_pool(peer); if (!pool) { disconnect_client(req, false, "out of memory"); return; } /* * Attach to the target pool and change state to waiting_cancel. This way * once a new connection is opened, it's used to forward the cancel * request. */ req->pool = pool; pause_cancel_request(req); /* * Open a new connection over which the cancel request is forwarded to the * server. */ launch_new_connection(pool, /* evict_if_needed= */ true); } /* * Accepts a cancellation request, which will eventual cancel the query running * on the client that matches req->client_key */ void accept_cancel_request(PgSocket *req) { struct List *pitem, *citem; PgPool *pool = NULL; PgSocket *server = NULL, *client, *main_client = NULL; bool peering_enabled = false; Assert(req->state == CL_LOGIN); /* * PgBouncer peering */ peering_enabled = cf_peer_id > 0; if (peering_enabled) { /* * Extract the peer id from the cancel key. The peer id is * stored in the 2nd and 3rd byte. */ int peer_id = req->cancel_key[1] + (req->cancel_key[2] << 8); bool needs_forwarding_to_peer = cf_peer_id != peer_id; if (needs_forwarding_to_peer) { accept_cancel_request_for_peer(peer_id, req); return; } /* * Set the last two bits of the cancel key to 1. This is necessary to * compare the key from the request to our stored cancel keys, because * the stored cancel keys always have these TTL bits set to 1. */ req->cancel_key[7] |= CANCELLATION_TTL_MASK; } /* find the client that has the same cancel_key as this request */ statlist_for_each(pitem, &pool_list) { pool = container_of(pitem, PgPool, head); statlist_for_each(citem, &pool->active_client_list) { client = container_of(citem, PgSocket, head); if (memcmp(client->cancel_key, req->cancel_key, 8) == 0) { main_client = client; goto found; } } statlist_for_each(citem, &pool->waiting_client_list) { client = container_of(citem, PgSocket, head); if (memcmp(client->cancel_key, req->cancel_key, 8) == 0) { main_client = client; goto found; } } } found: /* wrong key */ if (!main_client) { disconnect_client(req, false, "failed cancel request"); return; } /* * cancel requests for administrative databases should be handled * differently from cancel request for normal servers. We should handle * these directly instead of forwarding them. */ if (main_client->pool->db->admin) { disconnect_client(req, false, "cancel request for console client"); admin_handle_cancel(main_client); return; } /* * The client is not linked to a server, which means that no query is * running that can be cancelled. This likely means the query finished by * itself before the cancel request arived to pgbouncer. */ if (!main_client->link) { disconnect_client(req, false, "cancel request for idle client"); return; } server = main_client->link; if (server->setting_vars) { disconnect_client(req, false, "ignoring cancel request for server that is setting vars"); return; } /* * Link the cancel request and the server on which the query is being * cancelled in a many-to-one way. */ req->canceled_server = server; statlist_append(&server->canceling_clients, &req->cancel_head); /* * Attach to the target pool and change state to waiting_cancel. This way * once a new connection is opened, it's used to forward the cancel * request. */ req->pool = pool; pause_cancel_request(req); /* * Open a new connection over which the cancel request is forwarded to the * server. */ launch_new_connection(pool, /* evict_if_needed= */ true); } bool forward_cancel_request(PgSocket *server) { bool res; PgSocket *req = first_socket(&server->pool->waiting_cancel_req_list); bool forwarding_to_peer = server->pool->db->peer_id != 0; Assert(req != NULL && req->state == CL_WAITING_CANCEL); Assert(server->state == SV_LOGIN); if (!forwarding_to_peer) { /* * In between accepting the cancel request and receiving an open connection * the query that was supposed to be cancelled has now completed. This * becomes a problem when the server is then reused for some other client. * Because this will mean that the cancel that is forwarded will cancel a * query from a completely different client than the client it was intended * for. */ if (!req->canceled_server) { disconnect_client(req, false, "not sending cancel request for client that is now idle"); return false; } } server->link = req; req->link = server; if (forwarding_to_peer) { SEND_CancelRequest(res, server, req->cancel_key); } else { SEND_CancelRequest(res, server, req->canceled_server->cancel_key); } if (!res) { slog_warning(req, "sending cancel request failed: %s", strerror(errno)); disconnect_client(req, false, "failed to send cancel request"); return false; } slog_debug(req, "started sending cancel request"); change_client_state(req, CL_ACTIVE_CANCEL); return true; } bool use_client_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_enc, const char *std_string, const char *datestyle, const char *timezone, const char *password, const char *scram_client_key, int scram_client_key_len, const char *scram_server_key, int scram_server_key_len) { PgDatabase *db = find_database(dbname); PgSocket *client; PktBuf tmp; /* if the database not found, it's an auto database -> registering... */ if (!db) { db = register_auto_database(dbname); if (!db) return true; } if (scram_client_key || scram_server_key) { PgCredentials *credentials; if (!scram_client_key || !scram_server_key) { log_error("incomplete SCRAM key data"); return false; } if (sizeof(credentials->scram_ClientKey) != scram_client_key_len || sizeof(credentials->scram_ServerKey) != scram_server_key_len) { log_error("incompatible SCRAM key data"); return false; } if (db->forced_user_credentials) { log_error("SCRAM key data received for forced user"); return false; } if (cf_auth_type == AUTH_TYPE_PAM) { log_error("SCRAM key data received for PAM user"); return false; } credentials = find_global_credentials(username); if (!credentials && db->auth_user_credentials) credentials = add_dynamic_credentials(db, username, password); if (!credentials) return false; memcpy(credentials->scram_ClientKey, scram_client_key, sizeof(credentials->scram_ClientKey)); memcpy(credentials->scram_ServerKey, scram_server_key, sizeof(credentials->scram_ServerKey)); credentials->has_scram_keys = true; } client = accept_client(fd, pga_is_unix(addr)); if (client == NULL) return false; client->suspended = true; if (!set_pool(client, dbname, username, password, true)) return false; change_client_state(client, CL_ACTIVE); /* store old cancel key */ pktbuf_static(&tmp, client->cancel_key, 8); pktbuf_put_uint64(&tmp, ckey); /* store old fds */ client->tmp_sk_oldfd = oldfd; client->tmp_sk_linkfd = linkfd; varcache_set(&client->vars, "client_encoding", client_enc); varcache_set(&client->vars, "standard_conforming_strings", std_string); varcache_set(&client->vars, "datestyle", datestyle); varcache_set(&client->vars, "timezone", timezone); return true; } bool use_server_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_enc, const char *std_string, const char *datestyle, const char *timezone, const char *password, const char *scram_client_key, int scram_client_key_len, const char *scram_server_key, int scram_server_key_len) { PgDatabase *db = find_database(dbname); PgCredentials *credentials; PgPool *pool; PgSocket *server; PktBuf tmp; bool res; /* if the database not found, it's an auto database -> registering... */ if (!db) { db = register_auto_database(dbname); if (!db) return true; } if (db->forced_user_credentials) { credentials = db->forced_user_credentials; } else if (cf_auth_type == AUTH_TYPE_PAM) { credentials = add_pam_credentials(username, password); } else { credentials = find_global_credentials(username); } if (!credentials && db->auth_user_credentials) credentials = add_dynamic_credentials(db, username, password); pool = get_pool(db, credentials); if (!pool) return false; server = slab_alloc(server_cache); if (!server) return false; res = sbuf_accept(&server->sbuf, fd, pga_is_unix(addr)); if (!res) return false; db->connection_count++; server->suspended = true; server->pool = pool; server->login_user_credentials = credentials; server->connect_time = server->request_time = get_cached_time(); server->query_start = 0; statlist_init(&server->canceling_clients, "canceling_clients"); fill_remote_addr(server, fd, pga_is_unix(addr)); fill_local_addr(server, fd, pga_is_unix(addr)); if (linkfd) { server->ready = false; change_server_state(server, SV_ACTIVE); } else { server->ready = true; change_server_state(server, SV_IDLE); } /* store old cancel key */ pktbuf_static(&tmp, server->cancel_key, 8); pktbuf_put_uint64(&tmp, ckey); /* store old fds */ server->tmp_sk_oldfd = oldfd; server->tmp_sk_linkfd = linkfd; varcache_set(&server->vars, "client_encoding", client_enc); varcache_set(&server->vars, "standard_conforming_strings", std_string); varcache_set(&server->vars, "datestyle", datestyle); varcache_set(&server->vars, "timezone", timezone); return true; } void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)) { struct List *item; statlist_for_each(item, &pool->idle_server_list) { func(container_of(item, PgSocket, head)); } statlist_for_each(item, &pool->used_server_list) { func(container_of(item, PgSocket, head)); } statlist_for_each(item, &pool->tested_server_list) { func(container_of(item, PgSocket, head)); } statlist_for_each(item, &pool->active_server_list) { func(container_of(item, PgSocket, head)); } statlist_for_each(item, &pool->new_server_list) { func(container_of(item, PgSocket, head)); } } static void for_each_server_filtered(PgPool *pool, void (*func)(PgSocket *sk), bool (*filter)(PgSocket *sk, void *arg), void *filter_arg) { struct List *item; PgSocket *sk; statlist_for_each(item, &pool->idle_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->used_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->tested_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->active_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->new_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } } static void tag_dirty(PgSocket *sk) { sk->close_needed = true; } void tag_pool_dirty(PgPool *pool) { struct List *item, *tmp; struct PgSocket *server; /* * Don't tag the admin pool as dirty, since this is not an actual postgres * server. Marking it as dirty breaks connecting to the pgbouncer admin * database on future connections. */ if (pool->db->admin) return; /* reset welcome msg */ if (pool->welcome_msg) { pktbuf_free(pool->welcome_msg); pool->welcome_msg = NULL; } pool->welcome_msg_ready = false; /* drop all existing servers ASAP */ for_each_server(pool, tag_dirty); /* drop servers login phase immediately */ statlist_for_each_safe(item, &pool->new_server_list, tmp) { server = container_of(item, PgSocket, head); disconnect_server(server, true, "connect string changed"); } } void tag_database_dirty(PgDatabase *db) { struct List *item; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db == db) tag_pool_dirty(pool); } } void tag_autodb_dirty(void) { struct List *item, *tmp; PgDatabase *db; PgPool *pool; /* * reload databases. */ statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (db->db_auto) register_auto_database(db->name); } statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_auto) register_auto_database(db->name); } /* * reload pools */ statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->db_auto) tag_pool_dirty(pool); } } static bool server_remote_addr_filter(PgSocket *sk, void *arg) { PgAddr *addr = arg; return (pga_cmp_addr(&sk->remote_addr, addr) == 0); } void tag_host_addr_dirty(const char *host, const struct sockaddr *sa) { struct List *item; PgPool *pool; PgAddr addr; memset(&addr, 0, sizeof(addr)); pga_copy(&addr, sa); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->host && strcmp(host, pool->db->host) == 0) { for_each_server_filtered(pool, tag_dirty, server_remote_addr_filter, &addr); } } } /* move objects from justfree_* to free_* lists */ void reuse_just_freed_objects(void) { struct List *tmp, *item; PgSocket *sk; bool close_works = true; /* * event_del() may fail because of ENOMEM for event handlers * that need only changes sent to kernel on each loop. * * Keep open sbufs in justfree lists until successful. */ statlist_for_each_safe(item, &justfree_client_list, tmp) { sk = container_of(item, PgSocket, head); if (sbuf_is_closed(&sk->sbuf)) { change_client_state(sk, CL_FREE); } else if (close_works) { close_works = sbuf_close(&sk->sbuf); } } statlist_for_each_safe(item, &justfree_server_list, tmp) { sk = container_of(item, PgSocket, head); if (sbuf_is_closed(&sk->sbuf)) { change_server_state(sk, SV_FREE); } else if (close_works) { close_works = sbuf_close(&sk->sbuf); } } } void objects_cleanup(void) { struct List *item, *tmp; PgDatabase *db; /* close can be postpones, just in case call twice */ reuse_just_freed_objects(); reuse_just_freed_objects(); statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); kill_database(db); } statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); kill_database(db); } statlist_for_each_safe(item, &peer_list, tmp) { PgDatabase *peer = container_of(item, PgDatabase, head); kill_peer(peer); } statlist_for_each_safe(item, &justfree_server_list, tmp) { PgSocket *server = container_of(item, PgSocket, head); server_free(server); } statlist_for_each_safe(item, &justfree_client_list, tmp) { PgSocket *client = container_of(item, PgSocket, head); client_free(client); } memset(&login_client_list, 0, sizeof login_client_list); memset(&user_list, 0, sizeof user_list); memset(&database_list, 0, sizeof database_list); memset(&pool_list, 0, sizeof pool_list); memset(&pam_user_tree, 0, sizeof pam_user_tree); memset(&user_tree, 0, sizeof user_tree); memset(&autodatabase_idle_list, 0, sizeof autodatabase_idle_list); slab_destroy(server_cache); server_cache = NULL; slab_destroy(client_cache); client_cache = NULL; slab_destroy(db_cache); db_cache = NULL; slab_destroy(peer_cache); peer_cache = NULL; slab_destroy(peer_pool_cache); peer_pool_cache = NULL; slab_destroy(pool_cache); pool_cache = NULL; slab_destroy(user_cache); user_cache = NULL; slab_destroy(credentials_cache); credentials_cache = NULL; slab_destroy(iobuf_cache); iobuf_cache = NULL; slab_destroy(outstanding_request_cache); outstanding_request_cache = NULL; slab_destroy(var_list_cache); var_list_cache = NULL; slab_destroy(server_prepared_statement_cache); server_prepared_statement_cache = NULL; } pgbouncer-1.24.1/src/loader.c0000644000175000000000000004241014777762222012711 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Config and auth file reading. */ #include "bouncer.h" #include "usual/time.h" #include #include /* * ConnString parsing */ bool any_user_level_timeout_set; bool any_user_level_client_timeout_set; /* parse parameter name before '=' */ static char *cstr_get_key(char *p, char **dst_p) { char *end; p = cstr_skip_ws(p); *dst_p = p; while (*p && *p != '=' && *p != ' ') p++; end = p; p = cstr_skip_ws(p); /* fail if no '=' or empty name */ if (*p != '=' || *dst_p == end) return NULL; *end = 0; return p + 1; } /* unquote the quoted value after first quote */ static char *cstr_unquote_value(char *p) { char *s = p; while (1) { if (!*p) return NULL; if (p[0] == '\'') { if (p[1] == '\'') p++; else break; } *s++ = *p++; } /* terminate actual value */ *s = 0; /* return position after quote */ return p + 1; } /* parse value, possibly quoted */ static char *cstr_get_value(char *p, char **dst_p) { p = cstr_skip_ws(p); if (*p == '\'') { *dst_p = ++p; p = cstr_unquote_value(p); if (!p) return NULL; } else { *dst_p = p; while (*p && *p != ' ') p++; } if (*p) { /* if not EOL, cut value */ *p = 0; p++; } /* disallow empty values */ if (*dst_p[0] == 0) return NULL; return p; } /* * Get key=val pair from connstring. Returns position it stopped * or NULL on error. EOF is signaled by *key = 0. */ static char * cstr_get_pair(char *p, char **key_p, char **val_p) { p = cstr_skip_ws(p); *key_p = *val_p = p; if (*p == 0) return p; /* read key */ p = cstr_get_key(p, key_p); if (!p) return NULL; /* read value */ p = cstr_get_value(p, val_p); if (!p) return NULL; log_noise("cstr_get_pair: \"%s\"=\"%s\"", *key_p, *val_p); return cstr_skip_ws(p); } /* * Free the old value and set the new value */ static bool set_param_value(char **old_value, const char *new_value) { if (strcmpeq(*old_value, new_value)) return true; if (*old_value) free(*old_value); if (new_value) { *old_value = strdup(new_value); if (!(*old_value)) { log_error("out of memory"); return false; } } else { *old_value = NULL; } return true; } static bool set_autodb(const char *connstr) { char *tmp = strdup(connstr); char *old = cf_autodb_connstr; if (!tmp) { log_error("no mem to change autodb_connstr"); return false; } cf_autodb_connstr = tmp; if (old) { if (strcmp(connstr, old) != 0) tag_autodb_dirty(); free(old); } return true; } /* fill PgDatabase from connstr */ bool parse_peer(void *base, const char *name, const char *connstr) { char *p, *key, *val; PgDatabase *peer; char *tmp_connstr; char *host = NULL; int port = 6432; int pool_size = -1; int peer_id = strtonum(name, 1, 0xFFFF, NULL); if (peer_id == 0) { log_error("ids of peers must be a number larger than 0 and at most 65536"); return false; } tmp_connstr = strdup(connstr); if (!tmp_connstr) { log_error("out of memory"); return false; } p = tmp_connstr; while (*p) { p = cstr_get_pair(p, &key, &val); if (p == NULL) { log_error("syntax error in connection string"); goto fail; } else if (!key[0]) { break; } if (strcmp("host", key) == 0) { if (!set_param_value(&host, val)) goto fail; } else if (strcmp("port", key) == 0) { port = atoi(val); if (port == 0) { log_error("invalid port: %s", val); goto fail; } } else if (strcmp("pool_size", key) == 0) { pool_size = atoi(val); } else { log_error("unrecognized connection parameter: %s", key); goto fail; } } if (!host) { log_error("host was not provided for peer %d", peer_id); goto fail; } peer = add_peer(name, peer_id); if (!peer) { log_error("cannot create peer, no memory?"); goto fail; } /* tag the peer as alive */ peer->db_dead = false; free(peer->host); peer->host = host; peer->port = port; peer->pool_size = pool_size; free(tmp_connstr); return true; fail: free(tmp_connstr); free(host); return false; } /* fill PgDatabase from connstr */ bool parse_database(void *base, const char *name, const char *connstr) { char *p, *key, *val; PktBuf *msg; PgDatabase *db; struct CfValue cv; struct CfValue load_balance_hosts_lookup; int pool_size = -1; int min_pool_size = -1; int res_pool_size = -1; int max_db_client_connections = -1; int max_db_connections = -1; usec_t server_lifetime = 0; int dbname_ofs; int pool_mode = POOL_INHERIT; enum LoadBalanceHosts load_balance_hosts = LOAD_BALANCE_HOSTS_ROUND_ROBIN; char *tmp_connstr; const char *dbname = name; char *host = NULL; int port = 5432; char *username = NULL; char *password = ""; char *auth_username = NULL; char *auth_dbname = NULL; char *client_encoding = NULL; char *datestyle = NULL; char *timezone = NULL; char *connect_query = NULL; char *appname = NULL; char *auth_query = NULL; cv.value_p = &pool_mode; cv.extra = (const void *)pool_mode_map; load_balance_hosts_lookup.value_p = &load_balance_hosts; load_balance_hosts_lookup.extra = (const void *)load_balance_hosts_map; if (!check_reserved_database(name)) { log_error("database name \"%s\" is reserved", name); return false; } if (strcmp(name, "*") == 0) { return set_autodb(connstr); } tmp_connstr = strdup(connstr); if (!tmp_connstr) { log_error("out of memory"); return false; } p = tmp_connstr; while (*p) { p = cstr_get_pair(p, &key, &val); if (p == NULL) { log_error("syntax error in connection string"); goto fail; } else if (!key[0]) { break; } if (strcmp("dbname", key) == 0) { dbname = val; } else if (strcmp("host", key) == 0) { if (!set_param_value(&host, val)) goto fail; } else if (strcmp("port", key) == 0) { port = atoi(val); if (port == 0) { log_error("invalid port: %s", val); goto fail; } } else if (strcmp("user", key) == 0) { username = val; } else if (strcmp("password", key) == 0) { password = val; } else if (strcmp("auth_user", key) == 0) { auth_username = val; } else if (strcmp("auth_dbname", key) == 0) { auth_dbname = val; } else if (strcmp("client_encoding", key) == 0) { client_encoding = val; } else if (strcmp("datestyle", key) == 0) { datestyle = val; } else if (strcmp("timezone", key) == 0) { timezone = val; } else if (strcmp("pool_size", key) == 0) { pool_size = atoi(val); } else if (strcmp("min_pool_size", key) == 0) { min_pool_size = atoi(val); } else if (strcmp("reserve_pool", key) == 0) { /* We continue supporting this option for backwards compatibility */ res_pool_size = atoi(val); } else if (strcmp("reserve_pool_size", key) == 0) { res_pool_size = atoi(val); } else if (strcmp("max_db_connections", key) == 0) { max_db_connections = atoi(val); } else if (strcmp("max_db_client_connections", key) == 0) { max_db_client_connections = atoi(val); } else if (strcmp("server_lifetime", key) == 0) { server_lifetime = atoi(val) * USEC; } else if (strcmp("load_balance_hosts", key) == 0) { if (!cf_set_lookup(&load_balance_hosts_lookup, val)) { log_error("invalid load_balance_hosts: %s", val); goto fail; } } else if (strcmp("pool_mode", key) == 0) { if (!cf_set_lookup(&cv, val)) { log_error("invalid pool mode: %s", val); goto fail; } } else if (strcmp("connect_query", key) == 0) { if (!set_param_value(&connect_query, val)) goto fail; } else if (strcmp("application_name", key) == 0) { appname = val; } else if (strcmp("auth_query", key) == 0) { auth_query = val; } else { log_error("unrecognized connection parameter: %s", key); goto fail; } } db = add_database(name); if (!db) { log_error("cannot create database, no memory?"); goto fail; } /* tag the db as alive */ db->db_dead = false; /* assuming not an autodb */ db->db_auto = false; db->inactive_time = 0; /* if updating old db, check if anything changed */ if (db->dbname) { bool changed = false; if (strcmp(db->dbname, dbname) != 0) { changed = true; } else if (!strcmpeq(host, db->host)) { changed = true; } else if (port != db->port) { changed = true; } else if (username && !db->forced_user_credentials) { changed = true; } else if (username && strcmp(username, db->forced_user_credentials->name) != 0) { changed = true; } else if (!username && db->forced_user_credentials) { changed = true; } else if (!strcmpeq(connect_query, db->connect_query)) { changed = true; } else if (!strcmpeq(db->auth_dbname, auth_dbname)) { changed = true; } else if (!strcmpeq(db->auth_query, auth_query)) { changed = true; } else if (load_balance_hosts != db->load_balance_hosts) { changed = true; } if (changed) tag_database_dirty(db); } free(db->host); db->host = host; host = NULL; db->port = port; db->pool_size = pool_size; db->min_pool_size = min_pool_size; db->res_pool_size = res_pool_size; db->pool_mode = pool_mode; db->max_db_client_connections = max_db_client_connections; db->max_db_connections = max_db_connections; db->server_lifetime = server_lifetime; db->load_balance_hosts = load_balance_hosts; free(db->connect_query); db->connect_query = connect_query; connect_query = NULL; if (!set_param_value(&db->auth_dbname, auth_dbname)) goto fail; if (!set_param_value(&db->auth_query, auth_query)) goto fail; if (db->startup_params) { msg = db->startup_params; pktbuf_reset(msg); } else { msg = pktbuf_dynamic(128); if (!msg) die("out of memory"); db->startup_params = msg; } pktbuf_put_string(msg, "database"); dbname_ofs = msg->write_pos; pktbuf_put_string(msg, dbname); if (client_encoding) { pktbuf_put_string(msg, "client_encoding"); pktbuf_put_string(msg, client_encoding); } if (datestyle) { pktbuf_put_string(msg, "datestyle"); pktbuf_put_string(msg, datestyle); } if (timezone) { pktbuf_put_string(msg, "timezone"); pktbuf_put_string(msg, timezone); } if (appname) { pktbuf_put_string(msg, "application_name"); pktbuf_put_string(msg, appname); } if (auth_username != NULL) { db->auth_user_credentials = find_or_add_new_global_credentials(auth_username, ""); if (!db->auth_user_credentials) goto fail; } else if (db->auth_user_credentials) { db->auth_user_credentials = NULL; } /* if user is forced, create fake object for it */ if (username != NULL) { if (!force_user_credentials(db, username, password)) log_warning("db setup failed, trying to continue"); } else if (db->forced_user_credentials) { log_warning("losing forced user not supported," " keeping old setting"); } /* remember dbname */ db->dbname = (char *)msg->buf + dbname_ofs; free(tmp_connstr); return true; fail: free(tmp_connstr); free(host); free(connect_query); return false; } bool parse_user(void *base, const char *name, const char *connstr) { char *p, *key, *val, *tmp_connstr; PgGlobalUser *user; struct CfValue cv; int pool_mode = POOL_INHERIT; int pool_size = -1; int res_pool_size = -1; int max_user_connections = -1; usec_t idle_transaction_timeout = 0; usec_t query_timeout = 0; usec_t client_idle_timeout = 0; int max_user_client_connections = -1; cv.value_p = &pool_mode; cv.extra = (const void *)pool_mode_map; tmp_connstr = strdup(connstr); if (!tmp_connstr) { log_error("out of memory"); return false; } p = tmp_connstr; while (*p) { p = cstr_get_pair(p, &key, &val); if (p == NULL) { log_error("syntax error in user settings"); goto fail; } else if (!key[0]) { break; } if (strcmp("pool_mode", key) == 0) { if (!cf_set_lookup(&cv, val)) { log_error("invalid pool mode: %s", val); goto fail; } } else if (strcmp("pool_size", key) == 0) { pool_size = atoi(val); } else if (strcmp("reserve_pool_size", key) == 0) { res_pool_size = atoi(val); } else if (strcmp("max_user_connections", key) == 0) { max_user_connections = atoi(val); } else if (strcmp("idle_transaction_timeout", key) == 0) { any_user_level_timeout_set = true; idle_transaction_timeout = atoi(val) * USEC; } else if (strcmp("query_timeout", key) == 0) { any_user_level_timeout_set = true; query_timeout = atoi(val) * USEC; } else if (strcmp("client_idle_timeout", key) == 0) { any_user_level_client_timeout_set = true; client_idle_timeout = atoi(val) * USEC; } else if (strcmp("max_user_client_connections", key) == 0) { max_user_client_connections = atoi(val); } else { log_error("unrecognized user parameter: %s", key); goto fail; } } user = find_or_add_new_global_user(name, ""); if (!user) { log_error("cannot create user, no memory?"); goto fail; } user->pool_mode = pool_mode; user->pool_size = pool_size; user->res_pool_size = res_pool_size; user->max_user_connections = max_user_connections; user->idle_transaction_timeout = idle_transaction_timeout; user->query_timeout = query_timeout; user->client_idle_timeout = client_idle_timeout; user->max_user_client_connections = max_user_client_connections; free(tmp_connstr); return true; fail: free(tmp_connstr); return false; } /* * User file parsing */ /* find next " in string, skipping escaped ones */ static char *find_quote(char *p, bool start) { loop: while (*p && *p != '"') p++; if (p[0] == '"' && p[1] == '"' && !start) { p += 2; goto loop; } return p; } /* string is unquoted while copying */ static void copy_quoted(char *dst, const char *src, int len) { char *end = dst + len - 1; while (*src && dst < end) { if (*src == '"') src++; *dst++ = *src++; } *dst = 0; } /* This function is only called when parsing the auth file, so all users added by this function do not have a dynamic password, by definition. If the password is empty, so be it. */ static void unquote_add_authfile_user(const char *username, const char *password) { char real_user[MAX_USERNAME]; char real_passwd[MAX_PASSWORD]; PgGlobalUser *user; copy_quoted(real_user, username, sizeof(real_user)); copy_quoted(real_passwd, password, sizeof(real_passwd)); user = find_or_add_new_global_user(real_user, real_passwd); if (!user) { log_warning("cannot create user, no memory"); return; } if (strcmp(user->credentials.passwd, real_passwd) != 0) { user = update_global_user_passwd(user, real_passwd); } user->credentials.dynamic_passwd = false; } static bool auth_loaded(const char *fn) { static bool cache_set = false; static struct stat cache; struct stat cur; /* no file specified */ if (fn == NULL) { memset(&cache, 0, sizeof(cache)); cache_set = true; return false; } if (stat(fn, &cur) < 0) memset(&cur, 0, sizeof(cur)); if (cache_set && cache.st_dev == cur.st_dev && cache.st_ino == cur.st_ino && cache.st_mode == cur.st_mode && cache.st_uid == cur.st_uid && cache.st_gid == cur.st_gid && cache.st_mtime == cur.st_mtime && cache.st_size == cur.st_size) return true; cache = cur; cache_set = true; return false; } bool loader_users_check(void) { if (auth_loaded(cf_auth_file)) return true; return load_auth_file(cf_auth_file); } static void disable_users(void) { struct List *item; statlist_for_each(item, &user_list) { PgGlobalUser *user = container_of(item, PgGlobalUser, head); user->credentials.passwd[0] = 0; } } /* load list of users from auth_file */ bool load_auth_file(const char *fn) { char *user, *password, *buf, *p; /* No file to load? */ if (fn == NULL) return false; buf = load_file(fn, NULL); if (buf == NULL) { log_error("could not open auth_file %s: %s", fn, strerror(errno)); return false; } log_debug("loading auth_file: \"%s\"", fn); disable_users(); p = buf; while (*p) { /* skip whitespace and empty lines */ while (*p && isspace(*p)) p++; if (!*p) break; /* skip commented-out lines */ if (*p == ';') { while (*p && *p != '\n') p++; continue; } /* start of line */ if (*p != '"') { log_error("broken auth file"); break; } user = ++p; p = find_quote(p, false); if (*p != '"') { log_error("broken auth file"); break; } if (p - user >= MAX_USERNAME) { log_error("username too long in auth file"); break; } *p++ = 0; /* tag username end */ /* get password */ p = find_quote(p, true); if (*p != '"') { log_error("broken auth file"); break; } password = ++p; p = find_quote(p, false); if (*p != '"') { log_error("broken auth file"); break; } if (p - password >= MAX_PASSWORD) { log_error("password too long in auth file"); break; } *p++ = 0; /* tag password end */ /* send them away */ unquote_add_authfile_user(user, password); /* skip rest of the line */ while (*p && *p != '\n') p++; } free(buf); return true; } pgbouncer-1.24.1/src/scram.c0000644000175000000000000007215514777762222012561 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * SCRAM support */ #include "bouncer.h" #include "scram.h" #include "common/base64.h" #include "common/saslprep.h" #include "common/scram-common.h" static bool calculate_client_proof(ScramState *scram_state, const PgCredentials *credentials, const char *salt, int saltlen, int iterations, const char *client_final_message_without_proof, uint8_t *result); /* * free SCRAM state info after auth is done */ void free_scram_state(ScramState *scram_state) { free(scram_state->client_nonce); free(scram_state->client_first_message_bare); free(scram_state->client_final_message_without_proof); free(scram_state->server_nonce); free(scram_state->server_first_message); free(scram_state->SaltedPassword); free(scram_state->salt); memset(scram_state, 0, sizeof(*scram_state)); } static bool is_scram_printable(char *p) { /*------ * Printable characters, as defined by SCRAM spec: (RFC 5802) * * printable = %x21-2B / %x2D-7E * ;; Printable ASCII except ",". * ;; Note that any "printable" is also * ;; a valid "value". *------ */ for (; *p; p++) if (*p < 0x21 || *p > 0x7E || *p == 0x2C /* comma */) return false; return true; } static char *sanitize_char(char c) { static char buf[5]; if (c >= 0x21 && c <= 0x7E) snprintf(buf, sizeof(buf), "'%c'", c); else snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c); return buf; } /* * Read value for an attribute part of a SCRAM message. */ static char *read_attr_value(PgSocket *sk, char **input, char attr) { char *begin = *input; char *end; if (*begin != attr) { slog_error(sk, "malformed SCRAM message (attribute \"%c\" expected)", attr); return NULL; } begin++; if (*begin != '=') { slog_error(sk, "malformed SCRAM message (expected \"=\" after attribute \"%c\")", attr); return NULL; } begin++; end = begin; while (*end && *end != ',') end++; if (*end) { *end = '\0'; *input = end + 1; } else { *input = end; } return begin; } /* * Read the next attribute and value in a SCRAM exchange message. * * Returns NULL if there is no attribute. */ static char *read_any_attr(PgSocket *sk, char **input, char *attr_p) { char *begin = *input; char *end; char attr = *begin; if (!((attr >= 'A' && attr <= 'Z') || (attr >= 'a' && attr <= 'z'))) { slog_error(sk, "malformed SCRAM message (attribute expected, but found invalid character \"%s\")", sanitize_char(attr)); return NULL; } if (attr_p) *attr_p = attr; begin++; if (*begin != '=') { slog_error(sk, "malformed SCRAM message (expected character \"=\" after attribute \"%c\")", attr); return NULL; } begin++; end = begin; while (*end && *end != ',') end++; if (*end) { *end = '\0'; *input = end + 1; } else { *input = end; } return begin; } /* * Parse and validate format of given SCRAM secret. * * Returns true if the SCRAM secret has been parsed, and false otherwise. */ static bool parse_scram_secret(const char *secret, int *iterations, char **salt, uint8_t *stored_key, uint8_t *server_key) { char *s; char *p; char *scheme_str; char *salt_str; char *iterations_str; char *storedkey_str; char *serverkey_str; int decoded_len; char *decoded_salt_buf; char *decoded_stored_buf = NULL; char *decoded_server_buf = NULL; /* * The secret is of form: * * SCRAM-SHA-256$:$: */ s = strdup(secret); if (!s) goto invalid_secret; if ((scheme_str = strtok(s, "$")) == NULL) goto invalid_secret; if ((iterations_str = strtok(NULL, ":")) == NULL) goto invalid_secret; if ((salt_str = strtok(NULL, "$")) == NULL) goto invalid_secret; if ((storedkey_str = strtok(NULL, ":")) == NULL) goto invalid_secret; if ((serverkey_str = strtok(NULL, "")) == NULL) goto invalid_secret; /* Parse the fields */ if (strcmp(scheme_str, "SCRAM-SHA-256") != 0) goto invalid_secret; errno = 0; *iterations = strtol(iterations_str, &p, 10); if (*p || errno != 0) goto invalid_secret; /* * Verify that the salt is in Base64-encoded format, by decoding it, * although we return the encoded version to the caller. */ decoded_len = pg_b64_dec_len(strlen(salt_str)); decoded_salt_buf = malloc(decoded_len); if (!decoded_salt_buf) goto invalid_secret; decoded_len = pg_b64_decode(salt_str, strlen(salt_str), decoded_salt_buf, decoded_len); free(decoded_salt_buf); if (decoded_len < 0) goto invalid_secret; *salt = strdup(salt_str); if (!*salt) goto invalid_secret; /* * Decode StoredKey and ServerKey. */ decoded_len = pg_b64_dec_len(strlen(storedkey_str)); decoded_stored_buf = malloc(decoded_len); if (!decoded_stored_buf) goto invalid_secret; decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str), decoded_stored_buf, decoded_len); if (decoded_len != SCRAM_KEY_LEN) goto invalid_secret; memcpy(stored_key, decoded_stored_buf, SCRAM_KEY_LEN); decoded_len = pg_b64_dec_len(strlen(serverkey_str)); decoded_server_buf = malloc(decoded_len); decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str), decoded_server_buf, decoded_len); if (decoded_len != SCRAM_KEY_LEN) goto invalid_secret; memcpy(server_key, decoded_server_buf, SCRAM_KEY_LEN); free(decoded_stored_buf); free(decoded_server_buf); free(s); return true; invalid_secret: free(decoded_stored_buf); free(decoded_server_buf); free(s); free(*salt); *salt = NULL; return false; } #define MD5_PASSWD_CHARSET "0123456789abcdef" /* * What kind of a password type is 'shadow_pass'? */ PasswordType get_password_type(const char *shadow_pass) { char *encoded_salt = NULL; int iterations; uint8_t stored_key[SCRAM_KEY_LEN]; uint8_t server_key[SCRAM_KEY_LEN]; if (strncmp(shadow_pass, "md5", 3) == 0 && strlen(shadow_pass) == MD5_PASSWD_LEN && strspn(shadow_pass + 3, MD5_PASSWD_CHARSET) == MD5_PASSWD_LEN - 3) return PASSWORD_TYPE_MD5; if (parse_scram_secret(shadow_pass, &iterations, &encoded_salt, stored_key, server_key)) { free(encoded_salt); return PASSWORD_TYPE_SCRAM_SHA_256; } free(encoded_salt); return PASSWORD_TYPE_PLAINTEXT; } /* * Functions for communicating as a client with the server */ char *build_client_first_message(ScramState *scram_state) { uint8_t raw_nonce[SCRAM_RAW_NONCE_LEN + 1]; int encoded_len; size_t len; char *result = NULL; get_random_bytes(raw_nonce, SCRAM_RAW_NONCE_LEN); encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN); scram_state->client_nonce = malloc(encoded_len + 1); if (scram_state->client_nonce == NULL) goto failed; encoded_len = pg_b64_encode((char *) raw_nonce, SCRAM_RAW_NONCE_LEN, scram_state->client_nonce, encoded_len); if (encoded_len < 0) goto failed; scram_state->client_nonce[encoded_len] = '\0'; len = 8 + strlen(scram_state->client_nonce) + 1; result = malloc(len); if (result == NULL) goto failed; snprintf(result, len, "n,,n=,r=%s", scram_state->client_nonce); scram_state->client_first_message_bare = strdup(result + 3); if (scram_state->client_first_message_bare == NULL) goto failed; return result; failed: free(result); free(scram_state->client_nonce); free(scram_state->client_first_message_bare); return NULL; } char *build_client_final_message(ScramState *scram_state, const PgCredentials *credentials, const char *server_nonce, const char *salt, int saltlen, int iterations) { char buf[512]; size_t len; uint8_t client_proof[SCRAM_KEY_LEN]; int enclen; snprintf(buf, sizeof(buf), "c=biws,r=%s", server_nonce); scram_state->client_final_message_without_proof = strdup(buf); if (scram_state->client_final_message_without_proof == NULL) goto failed; if (!calculate_client_proof(scram_state, credentials, salt, saltlen, iterations, buf, client_proof)) goto failed; len = strlcat(buf, ",p=", sizeof(buf)); enclen = pg_b64_enc_len(sizeof(client_proof)); enclen = pg_b64_encode((char *) client_proof, SCRAM_KEY_LEN, buf + len, enclen); if (enclen < 0) goto failed; len += enclen; buf[len] = '\0'; return strdup(buf); failed: return NULL; } bool read_server_first_message(PgSocket *server, char *input, char **server_nonce_p, char **salt_p, int *saltlen_p, int *iterations_p) { char *server_nonce; char *encoded_salt; char *salt = NULL; int saltlen; char *iterations_str; char *endptr; int iterations; server->scram_state.server_first_message = strdup(input); if (server->scram_state.server_first_message == NULL) goto failed; server_nonce = read_attr_value(server, &input, 'r'); if (server_nonce == NULL) goto failed; if (strlen(server_nonce) < strlen(server->scram_state.client_nonce) || memcmp(server_nonce, server->scram_state.client_nonce, strlen(server->scram_state.client_nonce)) != 0) { slog_error(server, "invalid SCRAM response (nonce mismatch)"); goto failed; } encoded_salt = read_attr_value(server, &input, 's'); if (encoded_salt == NULL) goto failed; saltlen = pg_b64_dec_len(strlen(encoded_salt)); salt = malloc(saltlen); if (salt == NULL) goto failed; saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt, saltlen); if (saltlen < 0) { slog_error(server, "malformed SCRAM message (invalid salt)"); goto failed; } iterations_str = read_attr_value(server, &input, 'i'); if (iterations_str == NULL) goto failed; iterations = strtol(iterations_str, &endptr, 10); if (*endptr != '\0' || iterations < 1) { slog_error(server, "malformed SCRAM message (invalid iteration count)"); goto failed; } if (*input != '\0') { slog_error(server, "malformed SCRAM message (garbage at end of server-first-message)"); goto failed; } *server_nonce_p = server_nonce; *salt_p = salt; *saltlen_p = saltlen; *iterations_p = iterations; return true; failed: free(salt); return false; } bool read_server_final_message(PgSocket *server, char *input, char *ServerSignature) { char *encoded_server_signature; char *decoded_server_signature = NULL; int server_signature_len; if (*input == 'e') { char *errmsg = read_attr_value(server, &input, 'e'); slog_error(server, "error received from server in SCRAM exchange: %s", errmsg); goto failed; } encoded_server_signature = read_attr_value(server, &input, 'v'); if (encoded_server_signature == NULL) goto failed; if (*input != '\0') slog_error(server, "malformed SCRAM message (garbage at end of server-final-message)"); server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature)); decoded_server_signature = malloc(server_signature_len); if (!decoded_server_signature) goto failed; server_signature_len = pg_b64_decode(encoded_server_signature, strlen(encoded_server_signature), decoded_server_signature, server_signature_len); if (server_signature_len != SCRAM_KEY_LEN) { slog_error(server, "malformed SCRAM message (malformed server signature)"); goto failed; } memcpy(ServerSignature, decoded_server_signature, SCRAM_KEY_LEN); free(decoded_server_signature); return true; failed: free(decoded_server_signature); return false; } static bool calculate_client_proof(ScramState *scram_state, const PgCredentials *credentials, const char *salt, int saltlen, int iterations, const char *client_final_message_without_proof, uint8_t *result) { pg_saslprep_rc rc; char *prep_password = NULL; uint8_t StoredKey[SCRAM_KEY_LEN]; uint8_t ClientKey[SCRAM_KEY_LEN]; uint8_t ClientSignature[SCRAM_KEY_LEN]; scram_HMAC_ctx ctx; if (credentials->has_scram_keys) { memcpy(ClientKey, credentials->scram_ClientKey, SCRAM_KEY_LEN); } else { rc = pg_saslprep(credentials->passwd, &prep_password); if (rc == SASLPREP_OOM) return false; if (rc != SASLPREP_SUCCESS) { prep_password = strdup(credentials->passwd); if (!prep_password) return false; } scram_state->SaltedPassword = malloc(SCRAM_KEY_LEN); if (scram_state->SaltedPassword == NULL) goto failed; scram_SaltedPassword(prep_password, salt, saltlen, iterations, scram_state->SaltedPassword); scram_ClientKey(scram_state->SaltedPassword, ClientKey); } scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey); scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, scram_state->client_first_message_bare, strlen(scram_state->client_first_message_bare)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, scram_state->server_first_message, strlen(scram_state->server_first_message)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, client_final_message_without_proof, strlen(client_final_message_without_proof)); scram_HMAC_final(ClientSignature, &ctx); for (int i = 0; i < SCRAM_KEY_LEN; i++) result[i] = ClientKey[i] ^ ClientSignature[i]; free(prep_password); return true; failed: free(prep_password); return false; } bool verify_server_signature(ScramState *scram_state, const PgCredentials *credentials, const char *ServerSignature) { uint8_t expected_ServerSignature[SCRAM_KEY_LEN]; uint8_t ServerKey[SCRAM_KEY_LEN]; scram_HMAC_ctx ctx; if (credentials->has_scram_keys) memcpy(ServerKey, credentials->scram_ServerKey, SCRAM_KEY_LEN); else scram_ServerKey(scram_state->SaltedPassword, ServerKey); scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, scram_state->client_first_message_bare, strlen(scram_state->client_first_message_bare)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, scram_state->server_first_message, strlen(scram_state->server_first_message)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, scram_state->client_final_message_without_proof, strlen(scram_state->client_final_message_without_proof)); scram_HMAC_final(expected_ServerSignature, &ctx); if (memcmp(expected_ServerSignature, ServerSignature, SCRAM_KEY_LEN) != 0) return false; return true; } /* * Functions for communicating as a server to the client */ bool read_client_first_message(PgSocket *client, char *input, char *cbind_flag_p, char **client_first_message_bare_p, char **client_nonce_p) { char *client_first_message_bare = NULL; char *client_nonce = NULL; char *client_nonce_copy = NULL; *cbind_flag_p = *input; switch (*input) { case 'n': /* Client does not support channel binding */ input++; break; case 'y': /* Client supports channel binding, but we're not doing it today */ input++; break; case 'p': /* Client requires channel binding. We don't support it. */ slog_error(client, "client requires SCRAM channel binding, but it is not supported"); goto failed; default: slog_error(client, "malformed SCRAM message (unexpected channel-binding flag \"%s\")", sanitize_char(*input)); goto failed; } if (*input != ',') { slog_error(client, "malformed SCRAM message (comma expected, but found character \"%s\")", sanitize_char(*input)); goto failed; } input++; if (*input == 'a') { slog_error(client, "client uses authorization identity, but it is not supported"); goto failed; } if (*input != ',') { slog_error(client, "malformed SCRAM message (unexpected attribute \"%s\" in client-first-message)", sanitize_char(*input)); goto failed; } input++; client_first_message_bare = strdup(input); if (client_first_message_bare == NULL) goto failed; if (*input == 'm') { slog_error(client, "client requires an unsupported SCRAM extension"); goto failed; } /* read and ignore user name */ read_attr_value(client, &input, 'n'); client_nonce = read_attr_value(client, &input, 'r'); if (client_nonce == NULL) goto failed; if (!is_scram_printable(client_nonce)) { slog_error(client, "non-printable characters in SCRAM nonce"); goto failed; } client_nonce_copy = strdup(client_nonce); if (client_nonce_copy == NULL) goto failed; /* * There can be any number of optional extensions after this. We don't * support any extensions, so ignore them. */ while (*input != '\0') { if (!read_any_attr(client, &input, NULL)) goto failed; } *client_first_message_bare_p = client_first_message_bare; *client_nonce_p = client_nonce_copy; return true; failed: free(client_first_message_bare); free(client_nonce_copy); return false; } bool read_client_final_message(PgSocket *client, const uint8_t *raw_input, char *input, const char **client_final_nonce_p, char **proof_p) { const char *input_start = input; char attr; char *channel_binding; char *client_final_nonce; char *proof_start; char *value; char *encoded_proof; char *proof = NULL; int prooflen; /* * Read channel-binding. We don't support channel binding, so * it's expected to always be "biws", which is "n,,", * base64-encoded, or "eSws", which is "y,,". We also have to * check whether the flag is the same one that the client * originally sent. */ channel_binding = read_attr_value(client, &input, 'c'); if (channel_binding == NULL) goto failed; if (!(strcmp(channel_binding, "biws") == 0 && client->scram_state.cbind_flag == 'n') && !(strcmp(channel_binding, "eSws") == 0 && client->scram_state.cbind_flag == 'y')) { slog_error(client, "unexpected SCRAM channel-binding attribute in client-final-message"); goto failed; } client_final_nonce = read_attr_value(client, &input, 'r'); /* ignore optional extensions */ do { proof_start = input - 1; value = read_any_attr(client, &input, &attr); } while (value && attr != 'p'); if (!value) { slog_error(client, "could not read proof"); goto failed; } encoded_proof = value; prooflen = pg_b64_dec_len(strlen(encoded_proof)); proof = malloc(prooflen); if (proof == NULL) { slog_error(client, "could not decode proof"); goto failed; } prooflen = pg_b64_decode(encoded_proof, strlen(encoded_proof), proof, prooflen); if (prooflen != SCRAM_KEY_LEN) { slog_error(client, "malformed SCRAM message (malformed proof in client-final-message)"); goto failed; } if (*input != '\0') { slog_error(client, "malformed SCRAM message (garbage at the end of client-final-message)"); goto failed; } client->scram_state.client_final_message_without_proof = malloc(proof_start - input_start + 1); if (!client->scram_state.client_final_message_without_proof) goto failed; memcpy(client->scram_state.client_final_message_without_proof, raw_input, proof_start - input_start); client->scram_state.client_final_message_without_proof[proof_start - input_start] = '\0'; *client_final_nonce_p = client_final_nonce; *proof_p = proof; return true; failed: free(proof); return false; } /* * For doing SCRAM with a password stored in plain text, build a SCRAM * secret on the fly. */ static bool build_adhoc_scram_secret(const char *plain_password, ScramState *scram_state) { const char *password; char *prep_password; pg_saslprep_rc rc; char saltbuf[SCRAM_DEFAULT_SALT_LEN]; int encoded_len; uint8_t salted_password[SCRAM_KEY_LEN]; rc = pg_saslprep(plain_password, &prep_password); if (rc == SASLPREP_OOM) goto failed; else if (rc == SASLPREP_SUCCESS) password = prep_password; else password = plain_password; get_random_bytes((uint8_t *) saltbuf, sizeof(saltbuf)); scram_state->adhoc = true; scram_state->iterations = SCRAM_DEFAULT_ITERATIONS; encoded_len = pg_b64_enc_len(sizeof(saltbuf)); scram_state->salt = malloc(encoded_len + 1); if (!scram_state->salt) goto failed; encoded_len = pg_b64_encode(saltbuf, sizeof(saltbuf), scram_state->salt, encoded_len); if (encoded_len < 0) goto failed; scram_state->salt[encoded_len] = '\0'; /* Calculate StoredKey and ServerKey */ scram_SaltedPassword(password, saltbuf, sizeof(saltbuf), scram_state->iterations, salted_password); scram_ClientKey(salted_password, scram_state->StoredKey); scram_H(scram_state->StoredKey, SCRAM_KEY_LEN, scram_state->StoredKey); scram_ServerKey(salted_password, scram_state->ServerKey); free(prep_password); return true; failed: free(prep_password); return false; } /* * Deterministically generate salt for mock authentication, using a * SHA256 hash based on the username and an instance-level secret key. * Target buffer needs to be of size SCRAM_DEFAULT_SALT_LEN. */ static void scram_mock_salt(const char *username, uint8_t *saltbuf) { static uint8_t mock_auth_nonce[32]; static bool mock_auth_nonce_initialized = false; struct sha256_ctx ctx; uint8_t sha_digest[PG_SHA256_DIGEST_LENGTH]; /* * Generating salt using a SHA256 hash works as long as the * required salt length is not larger than the SHA256 digest * length. */ static_assert(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN, "salt length greater than SHA256 digest length"); if (!mock_auth_nonce_initialized) { get_random_bytes(mock_auth_nonce, sizeof(mock_auth_nonce)); mock_auth_nonce_initialized = true; } sha256_reset(&ctx); sha256_update(&ctx, (uint8_t *) username, strlen(username)); sha256_update(&ctx, mock_auth_nonce, sizeof(mock_auth_nonce)); sha256_final(&ctx, sha_digest); memcpy(saltbuf, sha_digest, SCRAM_DEFAULT_SALT_LEN); } static bool build_mock_scram_secret(const char *username, ScramState *scram_state) { uint8_t saltbuf[SCRAM_DEFAULT_SALT_LEN]; int encoded_len; scram_state->iterations = SCRAM_DEFAULT_ITERATIONS; scram_mock_salt(username, saltbuf); encoded_len = pg_b64_enc_len(sizeof(saltbuf)); scram_state->salt = malloc(encoded_len + 1); if (!scram_state->salt) goto failed; encoded_len = pg_b64_encode((char *) saltbuf, sizeof(saltbuf), scram_state->salt, encoded_len); if (encoded_len < 0) goto failed; scram_state->salt[encoded_len] = '\0'; return true; failed: return false; } char *build_server_first_message(ScramState *scram_state, const char *username, const char *stored_secret) { uint8_t raw_nonce[SCRAM_RAW_NONCE_LEN + 1]; int encoded_len; size_t len; char *result; if (!stored_secret) { if (!build_mock_scram_secret(username, scram_state)) goto failed; } else { switch (get_password_type(stored_secret)) { case PASSWORD_TYPE_SCRAM_SHA_256: if (!parse_scram_secret(stored_secret, &scram_state->iterations, &scram_state->salt, scram_state->StoredKey, scram_state->ServerKey)) goto failed; break; case PASSWORD_TYPE_PLAINTEXT: if (!build_adhoc_scram_secret(stored_secret, scram_state)) goto failed; break; default: /* shouldn't get here */ goto failed; } } get_random_bytes(raw_nonce, SCRAM_RAW_NONCE_LEN); encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN); scram_state->server_nonce = malloc(encoded_len + 1); if (scram_state->server_nonce == NULL) goto failed; encoded_len = pg_b64_encode((char *) raw_nonce, SCRAM_RAW_NONCE_LEN, scram_state->server_nonce, encoded_len); if (encoded_len < 0) goto failed; scram_state->server_nonce[encoded_len] = '\0'; len = (2 + strlen(scram_state->client_nonce) + strlen(scram_state->server_nonce) + 3 + strlen(scram_state->salt) + 3 + 10 + 1); result = malloc(len); if (!result) goto failed; snprintf(result, len, "r=%s%s,s=%s,i=%u", scram_state->client_nonce, scram_state->server_nonce, scram_state->salt, scram_state->iterations); scram_state->server_first_message = result; return result; failed: free(scram_state->server_nonce); free(scram_state->server_first_message); return NULL; } static char *compute_server_signature(ScramState *state) { uint8_t ServerSignature[SCRAM_KEY_LEN]; char *server_signature_base64; int siglen; scram_HMAC_ctx ctx; /* calculate ServerSignature */ scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, state->client_first_message_bare, strlen(state->client_first_message_bare)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->server_first_message, strlen(state->server_first_message)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->client_final_message_without_proof, strlen(state->client_final_message_without_proof)); scram_HMAC_final(ServerSignature, &ctx); siglen = pg_b64_enc_len(SCRAM_KEY_LEN); server_signature_base64 = malloc(siglen + 1); if (!server_signature_base64) return NULL; siglen = pg_b64_encode((const char *) ServerSignature, SCRAM_KEY_LEN, server_signature_base64, siglen); if (siglen < 0) { free(server_signature_base64); return NULL; } server_signature_base64[siglen] = '\0'; return server_signature_base64; } char *build_server_final_message(ScramState *scram_state) { char *server_signature = NULL; size_t len; char *result; server_signature = compute_server_signature(scram_state); if (!server_signature) goto failed; len = 2 + strlen(server_signature) + 1; /* * Avoid compiler warning at snprintf() below because len * could in theory overflow snprintf() result. If this * happened in practice, it would surely be some crazy * corruption, so treat it as an error. */ if (len >= INT_MAX) goto failed; result = malloc(len); if (!result) goto failed; snprintf(result, len, "v=%s", server_signature); free(server_signature); return result; failed: free(server_signature); return NULL; } bool verify_final_nonce(const ScramState *scram_state, const char *client_final_nonce) { size_t client_nonce_len = strlen(scram_state->client_nonce); size_t server_nonce_len = strlen(scram_state->server_nonce); size_t final_nonce_len = strlen(client_final_nonce); if (final_nonce_len != client_nonce_len + server_nonce_len) return false; if (memcmp(client_final_nonce, scram_state->client_nonce, client_nonce_len) != 0) return false; if (memcmp(client_final_nonce + client_nonce_len, scram_state->server_nonce, server_nonce_len) != 0) return false; return true; } bool verify_client_proof(ScramState *state, const char *ClientProof) { uint8_t ClientSignature[SCRAM_KEY_LEN]; uint8_t client_StoredKey[SCRAM_KEY_LEN]; scram_HMAC_ctx ctx; int i; /* calculate ClientSignature */ scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, state->client_first_message_bare, strlen(state->client_first_message_bare)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->server_first_message, strlen(state->server_first_message)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->client_final_message_without_proof, strlen(state->client_final_message_without_proof)); scram_HMAC_final(ClientSignature, &ctx); /* Extract the ClientKey that the client calculated from the proof */ for (i = 0; i < SCRAM_KEY_LEN; i++) state->ClientKey[i] = ClientProof[i] ^ ClientSignature[i]; /* Hash it one more time, and compare with StoredKey */ scram_H(state->ClientKey, SCRAM_KEY_LEN, client_StoredKey); if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0) return false; return true; } /* * Verify a plaintext password against a SCRAM secret. This is used when * performing plaintext password authentication for a user that has a SCRAM * secret stored in pg_authid. */ bool scram_verify_plain_password(PgSocket *client, const char *username, const char *password, const char *secret) { char *encoded_salt = NULL; char *salt = NULL; int saltlen; int iterations; uint8_t salted_password[SCRAM_KEY_LEN]; uint8_t stored_key[SCRAM_KEY_LEN]; uint8_t server_key[SCRAM_KEY_LEN]; uint8_t computed_key[SCRAM_KEY_LEN]; char *prep_password = NULL; pg_saslprep_rc rc; bool result = false; if (!parse_scram_secret(secret, &iterations, &encoded_salt, stored_key, server_key)) { /* The password looked like a SCRAM secret, but could not be parsed. */ slog_warning(client, "invalid SCRAM secret for user \"%s\"", username); goto failed; } saltlen = pg_b64_dec_len(strlen(encoded_salt)); salt = malloc(saltlen); if (!salt) goto failed; saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt, saltlen); if (saltlen < 0) { slog_warning(client, "invalid SCRAM secret for user \"%s\"", username); goto failed; } /* Normalize the password */ rc = pg_saslprep(password, &prep_password); if (rc == SASLPREP_SUCCESS) password = prep_password; /* Compute Server Key based on the user-supplied plaintext password */ scram_SaltedPassword(password, salt, saltlen, iterations, salted_password); scram_ServerKey(salted_password, computed_key); /* * Compare the secret's Server Key with the one computed from the * user-supplied password. */ result = memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0; failed: free(encoded_salt); free(salt); free(prep_password); return result; } pgbouncer-1.24.1/src/main.c0000644000175000000000000007550414777762222012401 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Launcher for all the rest. */ #include "bouncer.h" #include #include #include #include #include #include #include #include #ifdef WIN32 #include "win32support.h" #endif #ifdef HAVE_SYS_RESOURCE_H #include #endif static void usage(const char *exe) { printf("%s is a connection pooler for PostgreSQL.\n\n", exe); printf("Usage:\n"); printf(" %s [OPTION]... CONFIG_FILE\n", exe); printf("\nOptions:\n"); printf(" -d, --daemon run in background (as a daemon)\n"); printf(" -q, --quiet run quietly\n"); printf(" -R, --reboot do an online reboot\n"); printf(" -u, --user=USERNAME assume identity of USERNAME\n"); printf(" -v, --verbose increase verbosity\n"); printf(" -V, --version show version, then exit\n"); printf(" -h, --help show this help, then exit\n"); printf("\n"); #ifdef WIN32 printf("Windows service registration:\n"); printf(" --regservice CONFIG_FILE [-U USERNAME [-P PASSWORD]]\n"); printf(" --unregservice CONFIG_FILE\n"); printf("\n"); #endif printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT); printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL); exit(0); } /* global libevent handle */ struct event_base *pgb_event_base; /* async dns handler */ struct DNSContext *adns; struct HBA *parsed_hba; struct Ident *parsed_ident; /* * configuration storage */ int cf_daemon; int cf_pause_mode = P_NONE; int cf_shutdown = SHUTDOWN_NONE; int cf_reboot; static char *global_username; char *cf_config_file; char *cf_listen_addr; int cf_listen_port; int cf_listen_backlog; char *cf_unix_socket_dir; int cf_unix_socket_mode; char *cf_unix_socket_group; int cf_peer_id; int cf_pool_mode = POOL_SESSION; /* sbuf config */ int cf_sbuf_len; int cf_sbuf_loopcnt; int cf_so_reuseport; int cf_tcp_socket_buffer; int cf_tcp_defer_accept; #if defined(TCP_DEFER_ACCEPT) #define DEFAULT_TCP_DEFER_ACCEPT "1" #else #define DEFAULT_TCP_DEFER_ACCEPT "0" #endif int cf_tcp_keepalive; int cf_tcp_keepcnt; int cf_tcp_keepidle; int cf_tcp_keepintvl; int cf_tcp_user_timeout; int cf_auth_type = AUTH_TYPE_MD5; char *cf_auth_file; char *cf_auth_hba_file; char *cf_auth_ident_file; char *cf_auth_user; char *cf_auth_query; char *cf_auth_dbname; char *cf_track_extra_parameters; int cf_max_client_conn; int cf_default_pool_size; int cf_min_pool_size; int cf_res_pool_size; usec_t cf_res_pool_timeout; int cf_max_db_connections; int cf_max_db_client_connections; int cf_max_user_connections; int cf_max_user_client_connections; char *cf_server_reset_query; int cf_server_reset_query_always; char *cf_server_check_query; usec_t cf_server_check_delay; int cf_server_fast_close; int cf_server_round_robin; int cf_disable_pqexec; usec_t cf_dns_max_ttl; usec_t cf_dns_nxdomain_ttl; usec_t cf_dns_zone_check_period; char *cf_resolv_conf; unsigned int cf_max_packet_size; char *cf_ignore_startup_params; char *cf_autodb_connstr; /* here is "" different from NULL */ usec_t cf_autodb_idle_timeout; usec_t cf_server_lifetime; usec_t cf_server_idle_timeout; usec_t cf_server_connect_timeout; usec_t cf_server_login_retry; usec_t cf_query_timeout; usec_t cf_query_wait_timeout; usec_t cf_cancel_wait_timeout; usec_t cf_client_idle_timeout; usec_t cf_client_login_timeout; usec_t cf_idle_transaction_timeout; usec_t cf_suspend_timeout; usec_t g_suspend_start; char *cf_pidfile; char *cf_jobname; char *cf_admin_users; char *cf_stats_users; int cf_stats_period; int cf_log_stats; int cf_log_connections; int cf_log_disconnections; int cf_log_pooler_errors; int cf_application_name_add_host; int cf_client_tls_sslmode; char *cf_client_tls_protocols; char *cf_client_tls_ca_file; char *cf_client_tls_cert_file; char *cf_client_tls_key_file; char *cf_client_tls_ciphers; char *cf_client_tls_dheparams; char *cf_client_tls_ecdhecurve; int cf_server_tls_sslmode; char *cf_server_tls_protocols; char *cf_server_tls_ca_file; char *cf_server_tls_cert_file; char *cf_server_tls_key_file; char *cf_server_tls_ciphers; int cf_max_prepared_statements; /* * config file description */ static bool set_defer_accept(struct CfValue *cv, const char *val); #define DEFER_OPS {set_defer_accept, cf_get_int} static const struct CfLookup auth_type_map[] = { { "any", AUTH_TYPE_ANY }, { "trust", AUTH_TYPE_TRUST }, { "plain", AUTH_TYPE_PLAIN }, { "md5", AUTH_TYPE_MD5 }, { "cert", AUTH_TYPE_CERT }, { "hba", AUTH_TYPE_HBA }, #ifdef HAVE_PAM { "pam", AUTH_TYPE_PAM }, #endif { "scram-sha-256", AUTH_TYPE_SCRAM_SHA_256 }, { NULL } }; const struct CfLookup pool_mode_map[] = { { "session", POOL_SESSION }, { "transaction", POOL_TX }, { "statement", POOL_STMT }, { NULL } }; const struct CfLookup sslmode_map[] = { { "disable", SSLMODE_DISABLED }, { "allow", SSLMODE_ALLOW }, { "prefer", SSLMODE_PREFER }, { "require", SSLMODE_REQUIRE }, { "verify-ca", SSLMODE_VERIFY_CA }, { "verify-full", SSLMODE_VERIFY_FULL }, { NULL } }; const struct CfLookup load_balance_hosts_map[] = { { "disable", LOAD_BALANCE_HOSTS_DISABLE }, { "round-robin", LOAD_BALANCE_HOSTS_ROUND_ROBIN }, { NULL } }; /* * Add new parameters in alphabetical order. This order is used by SHOW CONFIG. */ static const struct CfKey bouncer_params [] = { CF_ABS("admin_users", CF_STR, cf_admin_users, 0, ""), CF_ABS("application_name_add_host", CF_INT, cf_application_name_add_host, 0, "0"), CF_ABS("auth_dbname", CF_AUTHDB, cf_auth_dbname, 0, NULL), CF_ABS("auth_file", CF_STR, cf_auth_file, 0, NULL), CF_ABS("auth_hba_file", CF_STR, cf_auth_hba_file, 0, ""), CF_ABS("auth_ident_file", CF_STR, cf_auth_ident_file, 0, NULL), CF_ABS("auth_query", CF_STR, cf_auth_query, 0, "SELECT rolname, CASE WHEN rolvaliduntil < now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolname=$1 AND rolcanlogin"), CF_ABS("auth_type", CF_LOOKUP(auth_type_map), cf_auth_type, 0, "md5"), CF_ABS("auth_user", CF_STR, cf_auth_user, 0, NULL), CF_ABS("autodb_idle_timeout", CF_TIME_USEC, cf_autodb_idle_timeout, 0, "3600"), CF_ABS("client_idle_timeout", CF_TIME_USEC, cf_client_idle_timeout, 0, "0"), CF_ABS("client_login_timeout", CF_TIME_USEC, cf_client_login_timeout, 0, "60"), CF_ABS("client_tls_ca_file", CF_STR, cf_client_tls_ca_file, 0, ""), CF_ABS("client_tls_cert_file", CF_STR, cf_client_tls_cert_file, 0, ""), CF_ABS("client_tls_ciphers", CF_STR, cf_client_tls_ciphers, 0, "default"), CF_ABS("client_tls_dheparams", CF_STR, cf_client_tls_dheparams, 0, "auto"), CF_ABS("client_tls_ecdhcurve", CF_STR, cf_client_tls_ecdhecurve, 0, "auto"), CF_ABS("client_tls_key_file", CF_STR, cf_client_tls_key_file, 0, ""), CF_ABS("client_tls_protocols", CF_STR, cf_client_tls_protocols, 0, "secure"), CF_ABS("client_tls_sslmode", CF_LOOKUP(sslmode_map), cf_client_tls_sslmode, 0, "disable"), CF_ABS("conffile", CF_STR, cf_config_file, 0, NULL), CF_ABS("default_pool_size", CF_INT, cf_default_pool_size, 0, "20"), CF_ABS("disable_pqexec", CF_INT, cf_disable_pqexec, CF_NO_RELOAD, "0"), CF_ABS("dns_max_ttl", CF_TIME_USEC, cf_dns_max_ttl, 0, "15"), CF_ABS("dns_nxdomain_ttl", CF_TIME_USEC, cf_dns_nxdomain_ttl, 0, "15"), CF_ABS("dns_zone_check_period", CF_TIME_USEC, cf_dns_zone_check_period, 0, "0"), CF_ABS("idle_transaction_timeout", CF_TIME_USEC, cf_idle_transaction_timeout, 0, "0"), CF_ABS("ignore_startup_parameters", CF_STR, cf_ignore_startup_params, 0, ""), CF_ABS("job_name", CF_STR, cf_jobname, CF_NO_RELOAD, "pgbouncer"), CF_ABS("listen_addr", CF_STR, cf_listen_addr, CF_NO_RELOAD, ""), CF_ABS("listen_backlog", CF_INT, cf_listen_backlog, CF_NO_RELOAD, "128"), CF_ABS("listen_port", CF_INT, cf_listen_port, CF_NO_RELOAD, "6432"), CF_ABS("log_connections", CF_INT, cf_log_connections, 0, "1"), CF_ABS("log_disconnections", CF_INT, cf_log_disconnections, 0, "1"), CF_ABS("log_pooler_errors", CF_INT, cf_log_pooler_errors, 0, "1"), CF_ABS("log_stats", CF_INT, cf_log_stats, 0, "1"), CF_ABS("logfile", CF_STR, cf_logfile, 0, ""), CF_ABS("max_client_conn", CF_INT, cf_max_client_conn, 0, "100"), CF_ABS("max_db_connections", CF_INT, cf_max_db_connections, 0, "0"), CF_ABS("max_db_client_connections", CF_INT, cf_max_db_client_connections, 0, "0"), CF_ABS("max_packet_size", CF_UINT, cf_max_packet_size, 0, "2147483647"), CF_ABS("max_prepared_statements", CF_INT, cf_max_prepared_statements, 0, "200"), CF_ABS("max_user_connections", CF_INT, cf_max_user_connections, 0, "0"), CF_ABS("max_user_client_connections", CF_INT, cf_max_user_client_connections, 0, "0"), CF_ABS("min_pool_size", CF_INT, cf_min_pool_size, 0, "0"), CF_ABS("peer_id", CF_INT, cf_peer_id, 0, "0"), CF_ABS("pidfile", CF_STR, cf_pidfile, CF_NO_RELOAD, ""), CF_ABS("pkt_buf", CF_INT, cf_sbuf_len, CF_NO_RELOAD, "4096"), CF_ABS("pool_mode", CF_LOOKUP(pool_mode_map), cf_pool_mode, 0, "session"), CF_ABS("query_timeout", CF_TIME_USEC, cf_query_timeout, 0, "0"), CF_ABS("query_wait_timeout", CF_TIME_USEC, cf_query_wait_timeout, 0, "120"), CF_ABS("cancel_wait_timeout", CF_TIME_USEC, cf_cancel_wait_timeout, 0, "10"), CF_ABS("reserve_pool_size", CF_INT, cf_res_pool_size, 0, "0"), CF_ABS("reserve_pool_timeout", CF_TIME_USEC, cf_res_pool_timeout, 0, "5"), CF_ABS("resolv_conf", CF_STR, cf_resolv_conf, CF_NO_RELOAD, ""), CF_ABS("sbuf_loopcnt", CF_INT, cf_sbuf_loopcnt, 0, "5"), CF_ABS("server_check_delay", CF_TIME_USEC, cf_server_check_delay, 0, "30"), CF_ABS("server_check_query", CF_STR, cf_server_check_query, 0, "select 1"), CF_ABS("server_connect_timeout", CF_TIME_USEC, cf_server_connect_timeout, 0, "15"), CF_ABS("server_fast_close", CF_INT, cf_server_fast_close, 0, "0"), CF_ABS("server_idle_timeout", CF_TIME_USEC, cf_server_idle_timeout, 0, "600"), CF_ABS("server_lifetime", CF_TIME_USEC, cf_server_lifetime, 0, "3600"), CF_ABS("server_login_retry", CF_TIME_USEC, cf_server_login_retry, 0, "15"), CF_ABS("server_reset_query", CF_STR, cf_server_reset_query, 0, "DISCARD ALL"), CF_ABS("server_reset_query_always", CF_INT, cf_server_reset_query_always, 0, "0"), CF_ABS("server_round_robin", CF_INT, cf_server_round_robin, 0, "0"), CF_ABS("server_tls_ca_file", CF_STR, cf_server_tls_ca_file, 0, ""), CF_ABS("server_tls_cert_file", CF_STR, cf_server_tls_cert_file, 0, ""), CF_ABS("server_tls_ciphers", CF_STR, cf_server_tls_ciphers, 0, "default"), CF_ABS("server_tls_key_file", CF_STR, cf_server_tls_key_file, 0, ""), CF_ABS("server_tls_protocols", CF_STR, cf_server_tls_protocols, 0, "secure"), CF_ABS("server_tls_sslmode", CF_LOOKUP(sslmode_map), cf_server_tls_sslmode, 0, "prefer"), #ifdef WIN32 CF_ABS("service_name", CF_STR, cf_jobname, CF_NO_RELOAD, NULL), /* alias for job_name */ #endif CF_ABS("so_reuseport", CF_INT, cf_so_reuseport, CF_NO_RELOAD, "0"), CF_ABS("stats_period", CF_INT, cf_stats_period, 0, "60"), CF_ABS("stats_users", CF_STR, cf_stats_users, 0, ""), CF_ABS("suspend_timeout", CF_TIME_USEC, cf_suspend_timeout, 0, "10"), CF_ABS("syslog", CF_INT, cf_syslog, 0, "0"), CF_ABS("syslog_facility", CF_STR, cf_syslog_facility, 0, "daemon"), CF_ABS("syslog_ident", CF_STR, cf_syslog_ident, 0, "pgbouncer"), CF_ABS("tcp_defer_accept", DEFER_OPS, cf_tcp_defer_accept, 0, DEFAULT_TCP_DEFER_ACCEPT), CF_ABS("tcp_keepalive", CF_INT, cf_tcp_keepalive, 0, "1"), CF_ABS("tcp_keepcnt", CF_INT, cf_tcp_keepcnt, 0, "0"), CF_ABS("tcp_keepidle", CF_INT, cf_tcp_keepidle, 0, "0"), CF_ABS("tcp_keepintvl", CF_INT, cf_tcp_keepintvl, 0, "0"), CF_ABS("tcp_socket_buffer", CF_INT, cf_tcp_socket_buffer, 0, "0"), CF_ABS("tcp_user_timeout", CF_INT, cf_tcp_user_timeout, 0, "0"), CF_ABS("track_extra_parameters", CF_STR, cf_track_extra_parameters, CF_NO_RELOAD, "IntervalStyle"), CF_ABS("unix_socket_dir", CF_STR, cf_unix_socket_dir, CF_NO_RELOAD, DEFAULT_UNIX_SOCKET_DIR), #ifndef WIN32 CF_ABS("unix_socket_group", CF_STR, cf_unix_socket_group, CF_NO_RELOAD, ""), CF_ABS("unix_socket_mode", CF_INT, cf_unix_socket_mode, CF_NO_RELOAD, "0777"), #endif #ifndef WIN32 CF_ABS("user", CF_STR, global_username, CF_NO_RELOAD, NULL), #endif CF_ABS("verbose", CF_INT, cf_verbose, 0, NULL), {NULL} }; static const struct CfSect config_sects [] = { { .sect_name = "pgbouncer", .key_list = bouncer_params, }, { .sect_name = "databases", .set_key = parse_database, }, { .sect_name = "users", .set_key = parse_user, }, { .sect_name = "peers", .set_key = parse_peer, }, { .sect_name = NULL, } }; static struct CfContext main_config = { config_sects, }; bool set_config_param(const char *key, const char *val) { return cf_set(&main_config, "pgbouncer", key, val); } void config_for_each(void (*param_cb)(void *arg, const char *name, const char *val, const char *defval, bool reloadable), void *arg) { const struct CfKey *k = bouncer_params; char buf[256]; bool reloadable; const char *val; int ro = CF_NO_RELOAD | CF_READONLY; for (; k->key_name; k++) { val = cf_get(&main_config, "pgbouncer", k->key_name, buf, sizeof(buf)); reloadable = (k->flags & ro) == 0; param_cb(arg, k->key_name, val, k->def_value, reloadable); } } static bool set_defer_accept(struct CfValue *cv, const char *val) { int *p = cv->value_p; bool ok; int oldval = *p; ok = cf_set_int(cv, val); if (ok && !!oldval != !!*p) pooler_tune_accept(*p); return ok; } static void set_dbs_dead(bool flag) { struct List *item; PgDatabase *db; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (db->admin) continue; if (db->db_auto) continue; db->db_dead = flag; } } static void set_peers_dead(bool flag) { struct List *item; PgDatabase *db; statlist_for_each(item, &peer_list) { db = container_of(item, PgDatabase, head); db->db_dead = flag; } } /* Tells if the specified auth type requires data from the auth file. */ static bool requires_auth_file(int auth_type) { /* For PAM authentication auth file is not used */ if (auth_type == AUTH_TYPE_PAM) return false; return auth_type >= AUTH_TYPE_TRUST; } /* config loading, tries to be tolerant to errors */ void load_config(void) { static bool loaded = false; bool ok; any_user_level_timeout_set = false; any_user_level_client_timeout_set = false; set_dbs_dead(true); set_peers_dead(true); /* actual loading */ ok = cf_load_file(&main_config, cf_config_file); if (ok) { /* load users if needed */ if (requires_auth_file(cf_auth_type)) loader_users_check(); loaded = true; } else if (!loaded) { die("cannot load config file"); } else { log_warning("config file loading failed"); /* if ini file missing, don't kill anybody */ set_dbs_dead(false); } if (cf_auth_type == AUTH_TYPE_HBA) { struct Ident *ident; struct HBA *hba; ident = ident_load_map(cf_auth_ident_file); if (ident) { ident_free(parsed_ident); parsed_ident = ident; } hba = hba_load_rules(cf_auth_hba_file, parsed_ident); if (hba) { hba_free(parsed_hba); parsed_hba = hba; } } else { hba_free(parsed_hba); parsed_hba = NULL; } /* kill dbs */ config_postprocess(); /* reopen logfile */ if (main_config.loaded) reset_logging(); } /* * signal handling. * * handle_* functions are not actual signal handlers but called from * event_loop() so they have no restrictions what they can do. */ static struct event ev_sigterm; static struct event ev_sigint; static void handle_sigterm(evutil_socket_t sock, short flags, void *arg) { if (cf_shutdown) { log_info("got SIGTERM while shutting down, fast exit"); /* pidfile cleanup happens via atexit() */ exit(0); } log_info("got SIGTERM, shutting down, waiting for all clients disconnect"); sd_notify(0, "STOPPING=1"); if (cf_reboot) die("takeover was in progress, going down immediately"); if (cf_pause_mode == P_SUSPEND) die("suspend was in progress, going down immediately"); cf_shutdown = SHUTDOWN_WAIT_FOR_CLIENTS; cleanup_sockets(); } static void handle_sigint(evutil_socket_t sock, short flags, void *arg) { if (cf_shutdown) { log_info("got SIGINT while shutting down, fast exit"); /* pidfile cleanup happens via atexit() */ exit(0); } log_info("got SIGINT, shutting down, waiting for all servers connections to be released"); sd_notify(0, "STOPPING=1"); if (cf_reboot) die("takeover was in progress, going down immediately"); if (cf_pause_mode == P_SUSPEND) die("suspend was in progress, going down immediately"); cf_pause_mode = P_PAUSE; cf_shutdown = SHUTDOWN_WAIT_FOR_SERVERS; cleanup_sockets(); } #ifndef WIN32 static struct event ev_sigquit; static struct event ev_sigusr1; static struct event ev_sigusr2; static struct event ev_sighup; static void handle_sigquit(evutil_socket_t sock, short flags, void *arg) { log_info("got SIGQUIT, fast exit"); /* pidfile cleanup happens via atexit() */ exit(0); } static void handle_sigusr1(int sock, short flags, void *arg) { if (cf_pause_mode == P_NONE) { log_info("got SIGUSR1, pausing all activity"); cf_pause_mode = P_PAUSE; } else { log_info("got SIGUSR1, but already paused/suspended"); } } static void handle_sigusr2(int sock, short flags, void *arg) { if (cf_shutdown) { log_info("got SIGUSR2 while shutting down, ignoring"); return; } switch (cf_pause_mode) { case P_SUSPEND: log_info("got SIGUSR2, continuing from SUSPEND"); resume_all(); cf_pause_mode = P_NONE; break; case P_PAUSE: log_info("got SIGUSR2, continuing from PAUSE"); cf_pause_mode = P_NONE; break; case P_NONE: log_info("got SIGUSR2, but not paused/suspended"); } } /* * Notify systemd that we are reloading, including a CLOCK_MONOTONIC timestamp * in usec so that the program is compatible with a Type=notify-reload service. * * See https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html */ static void notify_reloading(void) { #ifdef USE_SYSTEMD struct timespec ts; usec_t usec; clock_gettime(CLOCK_MONOTONIC, &ts); usec = (usec_t)ts.tv_sec * USEC + (usec_t)ts.tv_nsec / (usec_t)1000; sd_notifyf(0, "RELOADING=1\nMONOTONIC_USEC=%" PRIu64, usec); #endif } static void handle_sighup(int sock, short flags, void *arg) { log_info("got SIGHUP, re-reading config"); notify_reloading(); load_config(); if (!sbuf_tls_setup()) log_error("TLS configuration could not be reloaded, keeping old configuration"); sd_notify(0, "READY=1"); } #endif static void signal_setup(void) { int err; #ifndef WIN32 sigset_t set; /* block SIGPIPE */ sigemptyset(&set); sigaddset(&set, SIGPIPE); err = sigprocmask(SIG_BLOCK, &set, NULL); if (err < 0) fatal_perror("sigprocmask"); /* install handlers */ evsignal_assign(&ev_sigusr1, pgb_event_base, SIGUSR1, handle_sigusr1, NULL); err = evsignal_add(&ev_sigusr1, NULL); if (err < 0) fatal_perror("evsignal_add"); evsignal_assign(&ev_sigusr2, pgb_event_base, SIGUSR2, handle_sigusr2, NULL); err = evsignal_add(&ev_sigusr2, NULL); if (err < 0) fatal_perror("evsignal_add"); evsignal_assign(&ev_sighup, pgb_event_base, SIGHUP, handle_sighup, NULL); err = evsignal_add(&ev_sighup, NULL); if (err < 0) fatal_perror("evsignal_add"); evsignal_assign(&ev_sigquit, pgb_event_base, SIGQUIT, handle_sigquit, NULL); err = evsignal_add(&ev_sigquit, NULL); if (err < 0) fatal_perror("evsignal_add"); #endif evsignal_assign(&ev_sigterm, pgb_event_base, SIGTERM, handle_sigterm, NULL); err = evsignal_add(&ev_sigterm, NULL); if (err < 0) fatal_perror("evsignal_add"); evsignal_assign(&ev_sigint, pgb_event_base, SIGINT, handle_sigint, NULL); err = evsignal_add(&ev_sigint, NULL); if (err < 0) fatal_perror("evsignal_add"); } /* * daemon mode */ static void go_daemon(void) { int pid, fd; #ifdef WIN32 die("option --daemon (-d) is not supported on this platform"); #endif if (!cf_pidfile || !cf_pidfile[0]) die("daemon needs pidfile configured"); /* don't log to stdout anymore */ cf_quiet = 1; /* send stdin, stdout, stderr to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) die("could not open /dev/null: %s", strerror(errno)); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); /* fork new process */ pid = fork(); if (pid < 0) die("fork failed: %s", strerror(errno)); if (pid > 0) _exit(0); /* create new session */ pid = setsid(); if (pid < 0) die("setsid failed: %s", strerror(errno)); /* fork again to avoid being session leader */ pid = fork(); if (pid < 0) die("fork failed: %s", strerror(errno)); if (pid > 0) _exit(0); } /* * pidfile management. */ static void remove_pidfile(void) { if (cf_pidfile) { if (cf_pidfile[0]) unlink(cf_pidfile); free(cf_pidfile); cf_pidfile = NULL; } } static void check_pidfile(void) { char buf[128 + 1]; pid_t pid = 0; int fd, res, err; if (!cf_pidfile || !cf_pidfile[0]) return; /* read old pid */ fd = open(cf_pidfile, O_RDONLY); if (fd < 0) { if (errno == ENOENT) return; die("could not open pidfile '%s': %s", cf_pidfile, strerror(errno)); } res = read(fd, buf, sizeof(buf) - 1); close(fd); if (res < 0) die("could not read pidfile '%s': %s", cf_pidfile, strerror(errno)); if (res == 0) goto locked_pidfile; /* parse pid */ buf[res] = 0; pid = atol(buf); if (pid <= 0) goto locked_pidfile; /* check if running */ if (kill(pid, 0) >= 0) goto locked_pidfile; if (errno != ESRCH) goto locked_pidfile; /* seems the pidfile is not in use */ log_info("stale pidfile, removing"); err = unlink(cf_pidfile); if (err != 0) die("could not remove stale pidfile: %s", strerror(errno)); return; locked_pidfile: die("pidfile '%s' exists, another instance running?", cf_pidfile); } static void write_pidfile(void) { char buf[64]; pid_t pid; int res, fd; if (!cf_pidfile || !cf_pidfile[0]) return; pid = getpid(); snprintf(buf, sizeof(buf), "%u\n", (unsigned)pid); fd = open(cf_pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) die("could not open pidfile '%s': %s", cf_pidfile, strerror(errno)); res = safe_write(fd, buf, strlen(buf)); if (res < 0) die("could not write pidfile '%s': %s", cf_pidfile, strerror(errno)); close(fd); /* only remove when we have it actually written */ atexit(remove_pidfile); } /* just print out max files, in the future may warn if something is off */ static void check_limits(void) { struct rlimit lim; int total_users = statlist_count(&user_list); int fd_count; int err; struct List *item; PgDatabase *db; log_noise("event: %d, SBuf: %d, PgSocket: %d, IOBuf: %d", (int)sizeof(struct event), (int)sizeof(SBuf), (int)sizeof(PgSocket), (int)IOBUF_SIZE); /* load limits */ err = getrlimit(RLIMIT_NOFILE, &lim); if (err < 0) { log_error("could not get RLIMIT_NOFILE: %s", strerror(errno)); return; } /* calculate theoretical max, +10 is just in case */ fd_count = cf_max_client_conn + 10; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (db->forced_user_credentials) fd_count += (db->pool_size >= 0 ? db->pool_size : cf_default_pool_size); else fd_count += (db->pool_size >= 0 ? db->pool_size : cf_default_pool_size) * total_users; } log_info("kernel file descriptor limit: %d (hard: %d); max_client_conn: %d, max expected fd use: %d", (int)lim.rlim_cur, (int)lim.rlim_max, cf_max_client_conn, fd_count); } static bool check_old_process_unix(void) { struct sockaddr_un sa_un; socklen_t len = sizeof(sa_un); int domain = AF_UNIX; int res, fd; if (!cf_unix_socket_dir || !*cf_unix_socket_dir || sd_listen_fds(0) > 0) return false; memset(&sa_un, 0, len); sa_un.sun_family = domain; snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s/.s.PGSQL.%d", cf_unix_socket_dir, cf_listen_port); fd = socket(domain, SOCK_STREAM, 0); if (fd < 0) die("could not create socket: %s", strerror(errno)); res = safe_connect(fd, (struct sockaddr *)&sa_un, len); safe_close(fd); if (res < 0) return false; return true; } static void main_loop_once(void) { int err; reset_time_cache(); err = event_base_loop(pgb_event_base, EVLOOP_ONCE); if (err < 0) { if (errno != EINTR) log_warning("event_loop failed: %s", strerror(errno)); } pam_poll(); per_loop_maint(); reuse_just_freed_objects(); rescue_timers(); per_loop_pooler_maint(); if (adns) adns_per_loop(adns); } static void takeover_part1(void) { /* use temporary libevent base */ struct event_base *evtmp; evtmp = pgb_event_base; pgb_event_base = event_base_new(); if (!cf_unix_socket_dir || !*cf_unix_socket_dir) die("cannot reboot if unix dir not configured"); /* * Takeover with abstract Unix socket doesn't work because the * new process can't unlink the socket used by the old process * and put its own in place (see create_unix_socket()). */ if (cf_unix_socket_dir[0] == '@') die("cannot reboot with abstract Unix socket"); if (sd_listen_fds(0) > 0) die("cannot reboot under service manager"); takeover_init(); while (cf_reboot) main_loop_once(); event_base_free(pgb_event_base); pgb_event_base = evtmp; } static void dns_setup(void) { if (adns) return; adns = adns_create_context(); if (!adns) die("dns setup failed"); } static void xfree(char **ptr_p) { if (*ptr_p) { free(*ptr_p); *ptr_p = NULL; } } _UNUSED static void cleanup(void) { /* * We don't want to cleanup when we're the target of a takeover, because * that would close the sockets that we hand over. There's no real clean * way to detect that we were the target, so the below check is rather * crude. But since this cleanup is only happening in builds with asserts * enabled anyway it seems fine. */ if (cf_pause_mode == P_SUSPEND && cf_shutdown == SHUTDOWN_IMMEDIATE) { return; } adns_free_context(adns); adns = NULL; hba_free(parsed_hba); parsed_hba = NULL; ident_free(parsed_ident); parsed_ident = NULL; admin_cleanup(); objects_cleanup(); sbuf_cleanup(); event_base_free(pgb_event_base); tls_deinit(); varcache_deinit(); pktbuf_cleanup(); reset_logging(); xfree(&global_username); xfree(&cf_config_file); xfree(&cf_listen_addr); xfree(&cf_unix_socket_dir); xfree(&cf_unix_socket_group); xfree(&cf_auth_file); xfree(&cf_auth_ident_file); xfree(&cf_auth_dbname); xfree(&cf_auth_hba_file); xfree(&cf_auth_query); xfree(&cf_auth_user); xfree(&cf_server_reset_query); xfree(&cf_server_check_query); xfree(&cf_ignore_startup_params); xfree(&cf_autodb_connstr); xfree(&cf_jobname); xfree(&cf_admin_users); xfree(&cf_stats_users); xfree(&cf_client_tls_protocols); xfree(&cf_client_tls_ca_file); xfree(&cf_client_tls_cert_file); xfree(&cf_client_tls_key_file); xfree(&cf_client_tls_ciphers); xfree(&cf_client_tls_dheparams); xfree(&cf_client_tls_ecdhecurve); xfree(&cf_server_tls_protocols); xfree(&cf_server_tls_ca_file); xfree(&cf_server_tls_cert_file); xfree(&cf_server_tls_key_file); xfree(&cf_server_tls_ciphers); xfree((char **)&cf_logfile); xfree((char **)&cf_syslog_ident); xfree((char **)&cf_syslog_facility); xfree(&cf_track_extra_parameters); } /* boot everything */ int main(int argc, char *argv[]) { int c; bool did_takeover = false; char *arg_username = NULL; int long_idx; static const struct option long_options[] = { {"quiet", no_argument, NULL, 'q'}, {"verbose", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {"daemon", no_argument, NULL, 'd'}, {"version", no_argument, NULL, 'V'}, {"reboot", no_argument, NULL, 'R'}, {"user", required_argument, NULL, 'u'}, {NULL, 0, NULL, 0} }; setprogname(basename(argv[0])); /* parse cmdline */ while ((c = getopt_long(argc, argv, "qvhdVRu:", long_options, &long_idx)) != -1) { switch (c) { case 'R': cf_reboot = 1; break; case 'v': cf_verbose++; break; case 'V': printf("%s\n", PACKAGE_STRING); printf("libevent %s\nadns: %s\ntls: %s\n", event_get_version(), adns_get_backend(), tls_backend_version()); #ifdef USE_SYSTEMD printf("systemd: yes\n"); #endif return 0; case 'd': cf_daemon = 1; break; case 'q': cf_quiet = 1; break; case 'u': arg_username = optarg; break; case 'h': usage(argv[0]); break; default: fprintf(stderr, "Try \"%s --help\" for more information.\n", argv[0]); exit(1); break; } } if (optind + 1 != argc) { fprintf(stderr, "%s: no configuration file specified\n", argv[0]); fprintf(stderr, "Try \"%s --help\" for more information.\n", argv[0]); exit(1); } cf_config_file = xstrdup(argv[optind]); #ifdef CASSERT /* * Clean up all objects at the end, only for testing the * cleanup code, not useful for production. This must be the * first atexit() call, since other atexit() handlers still * make use of things that will be cleaned up. */ atexit(cleanup); #endif init_objects(); load_config(); main_config.loaded = true; init_var_lookup(cf_track_extra_parameters); init_caches(); logging_prefix_cb = log_socket_prefix; if (!sbuf_tls_setup()) die("TLS setup failed"); /* prefer cmdline over config for username */ if (arg_username) { free(global_username); global_username = xstrdup(arg_username); } /* switch user is needed */ if (global_username && *global_username) change_user(global_username); /* disallow running as root */ if (getuid() == 0) die("PgBouncer should not run as root"); admin_setup(); if (cf_reboot) { log_warning("Online restart is deprecated, use so_reuseport instead"); if (check_old_process_unix()) { takeover_part1(); did_takeover = true; } else { log_info("old process not found, try to continue normally"); cf_reboot = 0; check_pidfile(); } } else { if (check_old_process_unix()) die("unix socket is in use, cannot continue"); check_pidfile(); } if (cf_daemon) go_daemon(); #ifndef USE_SYSTEMD if (getenv("NOTIFY_SOCKET")) log_warning("apparently running under systemd with notify socket, but systemd support was not built"); #endif /* need to do that after loading config; also do after * go_daemon() so that output goes to log file */ check_limits(); /* initialize subsystems, order important */ srandom(time(NULL) ^ getpid()); if (!(pgb_event_base = event_base_new())) die("event_base_new() failed"); dns_setup(); signal_setup(); janitor_setup(); stats_setup(); pam_init(); if (did_takeover) { takeover_finish(); } else { pooler_setup(); } write_pidfile(); log_info("process up: %s, libevent %s (%s), adns: %s, tls: %s", PACKAGE_STRING, event_get_version(), event_base_get_method(pgb_event_base), adns_get_backend(), tls_backend_version()); sd_notify(0, "READY=1"); /* main loop */ while (cf_shutdown != SHUTDOWN_IMMEDIATE) main_loop_once(); return 0; } pgbouncer-1.24.1/AUTHORS0000644000175000000000000000146014777762222011560 00000000000000Maintainers ----------- Peter Eisentraut Maintainers emeriti ------------------- Marko Kreen Petr Jelinek Contributors ------------ Alexander Schöcke Andrew Dunstan Bjoern Metzdorf Bob Poekert Christoph Berg Cody Cutrer Dan McGee David Fetter David Galoyan David Sommerseth Devrim GÜNDÜZ Dimitri Fontaine Dmitriy Olshevskiy Dominique Hermsdorff Emmanuel Courreges Eric Radman Euler Taveira Filip RembiaÅ‚kowski Giorgio Valoti Guillaume Aubert Guillaume Lelarge Greg Sabino Mullane Hannu Krosing Heikki Linnakangas Hiroshi Saito Hubert Depesz Lubaczewski Jacob Coby James Pye Jørgen Austvik Lou Picciano Magne Mæhre Magnus Hagander Martin Pihlak Mathieu Fenniak Michael Tharp MichaÅ‚ Trojnara Matan Ryngler Pavel Stehule Pierre-Emmanuel André Rich Schaaf Robert Gogolok Sam McLeod Teodor Sigaev William Grant pgbouncer-1.24.1/configure0000775000175000017500000105606314777762315012453 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for PgBouncer 1.24.1. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/pgbouncer/pgbouncer/issues about $0: your system, including any error possibly output before $0: this message. Then install a modern shell, or manually $0: run the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='PgBouncer' PACKAGE_TARNAME='pgbouncer' PACKAGE_VERSION='1.24.1' PACKAGE_STRING='PgBouncer 1.24.1' PACKAGE_BUGREPORT='https://github.com/pgbouncer/pgbouncer/issues' PACKAGE_URL='https://www.pgbouncer.org/' ac_unique_file="src/janitor.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS enable_debug TLS_LIBS TLS_LDFLAGS TLS_CPPFLAGS tls_support PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config CARES_LIBS CARES_CFLAGS with_systemd LIBEVENT_LIBS LIBEVENT_CFLAGS DLLTOOL DLLWRAP WINDRES PYTHON PANDOC PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG ARFLAGS AR RANLIB STRIP SED MKDIR_P AWK EGREP GREP LN_S INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM WFLAGS HAVE_CC_DEPFLAG CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC pkgconfigdir pkgdatadir PORTNAME host_os host_vendor host_cpu host build_os build_vendor build_cpu build target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_largefile with_pam with_systemd with_cares enable_evdns with_openssl with_root_ca_file enable_debug enable_cassert enable_werror ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR LIBEVENT_CFLAGS LIBEVENT_LIBS CARES_CFLAGS CARES_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures PgBouncer 1.24.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/pgbouncer] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of PgBouncer 1.24.1:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-largefile omit support for large files --disable-evdns do not use libevent for DNS lookups --disable-debug strip binary --enable-cassert turn on assert checking in code --enable-werror add -Werror to CFLAGS Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pam build with PAM support --with-systemd build with systemd support --with-cares[=PREFIX] build with c-ares support --without-openssl do not build with OpenSSL support --with-openssl[=PREFIX] specify where OpenSSL is installed --with-root-ca-file=FILE specify where the root CA certificates are Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path LIBEVENT_CFLAGS C compiler flags for LIBEVENT, overriding pkg-config LIBEVENT_LIBS linker flags for LIBEVENT, overriding pkg-config CARES_CFLAGS C compiler flags for CARES, overriding pkg-config CARES_LIBS linker flags for CARES, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . PgBouncer home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF PgBouncer configure 1.24.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that # executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 printf %s "checking for uint$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else $as_nop break fi done fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR # ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_check_decl ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by PgBouncer $as_me 1.24.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="install-sh config.guess config.sub" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers lib/usual/config.h" # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking target host type" >&5 printf %s "checking target host type... " >&6; } xhost="$host_alias" if test "x$xhost" = "x"; then xhost=`uname -s` fi case "$xhost" in *cygwin* | *mingw* | *pw32* | *MINGW*) LIBS="$LIBS -lws2_32" PORTNAME=win32;; *) PORTNAME=unix ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PORTNAME" >&5 printf "%s\n" "$PORTNAME" >&6; } if test "$PORTNAME" = "win32"; then printf "%s\n" "#define WIN32_LEAN_AND_MEAN 1" >>confdefs.h printf "%s\n" "#define WINVER 0x0600" >>confdefs.h fi printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h pkgdatadir='${datarootdir}'/${PACKAGE_TARNAME} pkgconfigdir='${libdir}/pkgconfig' ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in grep ggrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ 'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$MKDIR_P" >&6; } ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 printf %s "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test ${ac_cv_prog_CPP+y} then : printf %s "(cached) " >&6 else $as_nop # Double quotes because $CC needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 printf "%s\n" "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports __func__" >&5 printf %s "checking whether compiler supports __func__... " >&6; } if test ${pgac_cv_funcname_func+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { printf("%s\n", __func__); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : pgac_cv_funcname_func=yes else $as_nop pgac_cv_funcname_func=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_funcname_func" >&5 printf "%s\n" "$pgac_cv_funcname_func" >&6; } if test x"$pgac_cv_funcname_func" = xyes ; then printf "%s\n" "#define HAVE_FUNCNAME__FUNC 1" >>confdefs.h fi if test "$GCC" = "yes"; then old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--as-needed" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether linker supports --as-needed" >&5 printf %s "checking whether linker supports --as-needed... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$old_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports dependency generation" >&5 printf %s "checking whether compiler supports dependency generation... " >&6; } old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -MD -MP -MT conftest.o -MF conftest.o.d" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void foo(void){} _ACEOF if ac_fn_c_try_compile "$LINENO" then : HAVE_CC_DEPFLAG=yes else $as_nop HAVE_CC_DEPFLAG=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext rm -f conftest.d CFLAGS="$old_CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $HAVE_CC_DEPFLAG" >&5 printf "%s\n" "$HAVE_CC_DEPFLAG" >&6; } WFLAGS="" if test x"$GCC" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working warning switches" >&5 printf %s "checking for working warning switches... " >&6; } good_CFLAGS="$CFLAGS" flags="-Wall -Wextra" # turn off noise from Wextra flags="$flags -Wno-unused-parameter -Wno-missing-field-initializers" # Wextra does not turn those on? flags="$flags -Wmissing-prototypes -Wpointer-arith -Wendif-labels" flags="$flags -Wdeclaration-after-statement -Wold-style-definition" flags="$flags -Wstrict-prototypes -Wundef -Wformat=2" flags="$flags -Wuninitialized -Wmissing-format-attribute" for f in $flags; do CFLAGS="$good_CFLAGS $WFLAGS $f" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void foo(void){} _ACEOF if ac_fn_c_try_compile "$LINENO" then : WFLAGS="$WFLAGS $f" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done # avoid -Wextra if missing-field.initializers does not work echo "$WFLAGS" | grep missing-field-initializers > /dev/null \ || WFLAGS=`echo "$WFLAGS"|sed 's/ -Wextra//'` CFLAGS="$good_CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 printf "%s\n" "done" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 printf %s "checking for egrep... " >&6; } if test ${ac_cv_path_EGREP+y} then : printf %s "(cached) " >&6 else $as_nop if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in egrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 printf %s "checking for a sed that does not truncate output... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 else $as_nop ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in sed gsed do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 printf "%s\n" "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed case "$ac_install_sh" in ./*) ac_install_sh="`pwd`/${ac_install_sh}" ;; ../*) ac_install_sh="`pwd`/${ac_install_sh}" ;; esac case "$INSTALL" in ./*) INSTALL="`pwd`/${INSTALL}" ;; ../*) INSTALL="`pwd`/${INSTALL}" ;; esac case "$MKDIR_P" in ./*) MKDIR_P="`pwd`/${MKDIR_P}" ;; ../*) MKDIR_P="`pwd`/${MKDIR_P}" ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB="true" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AR="${ac_tool_prefix}ar" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 printf "%s\n" "$AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="ar" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 printf "%s\n" "$ac_ct_AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi ARFLAGS=rcu if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 printf "%s\n" "$PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 printf "%s\n" "$ac_pt_PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 printf %s "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } PKG_CONFIG="" fi fi for ac_prog in pandoc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PANDOC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$PANDOC"; then ac_cv_prog_PANDOC="$PANDOC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_PANDOC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PANDOC=$ac_cv_prog_PANDOC if test -n "$PANDOC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PANDOC" >&5 printf "%s\n" "$PANDOC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$PANDOC" && break done test -n "$PANDOC" || PANDOC="pandoc" for ac_prog in python3 python do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PYTHON+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$PYTHON"; then ac_cv_prog_PYTHON="$PYTHON" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_PYTHON="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PYTHON=$ac_cv_prog_PYTHON if test -n "$PYTHON"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 printf "%s\n" "$PYTHON" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$PYTHON" && break done test -n "$PYTHON" || PYTHON="python3" if test "$PORTNAME" = "win32"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. set dummy ${ac_tool_prefix}windres; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_WINDRES+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$WINDRES"; then ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_WINDRES="${ac_tool_prefix}windres" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi WINDRES=$ac_cv_prog_WINDRES if test -n "$WINDRES"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5 printf "%s\n" "$WINDRES" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_WINDRES"; then ac_ct_WINDRES=$WINDRES # Extract the first word of "windres", so it can be a program name with args. set dummy windres; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_WINDRES+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_WINDRES"; then ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_WINDRES="windres" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES if test -n "$ac_ct_WINDRES"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5 printf "%s\n" "$ac_ct_WINDRES" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_WINDRES" = x; then WINDRES="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac WINDRES=$ac_ct_WINDRES fi else WINDRES="$ac_cv_prog_WINDRES" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dllwrap", so it can be a program name with args. set dummy ${ac_tool_prefix}dllwrap; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DLLWRAP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$DLLWRAP"; then ac_cv_prog_DLLWRAP="$DLLWRAP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DLLWRAP="${ac_tool_prefix}dllwrap" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLWRAP=$ac_cv_prog_DLLWRAP if test -n "$DLLWRAP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLWRAP" >&5 printf "%s\n" "$DLLWRAP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLWRAP"; then ac_ct_DLLWRAP=$DLLWRAP # Extract the first word of "dllwrap", so it can be a program name with args. set dummy dllwrap; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DLLWRAP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_DLLWRAP"; then ac_cv_prog_ac_ct_DLLWRAP="$ac_ct_DLLWRAP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLWRAP="dllwrap" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLWRAP=$ac_cv_prog_ac_ct_DLLWRAP if test -n "$ac_ct_DLLWRAP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLWRAP" >&5 printf "%s\n" "$ac_ct_DLLWRAP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_DLLWRAP" = x; then DLLWRAP="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLWRAP=$ac_ct_DLLWRAP fi else DLLWRAP="$ac_cv_prog_DLLWRAP" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_DLLTOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 printf "%s\n" "$DLLTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_DLLTOOL+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 printf "%s\n" "$ac_ct_DLLTOOL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default" if test "x$ac_cv_header_inttypes_h" = xyes then : printf "%s\n" "#define HAVE_INTTYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdbool.h" "ac_cv_header_stdbool_h" "$ac_includes_default" if test "x$ac_cv_header_stdbool_h" = xyes then : printf "%s\n" "#define HAVE_STDBOOL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" if test "x$ac_cv_header_unistd_h" = xyes then : printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" if test "x$ac_cv_header_sys_time_h" = xyes then : printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" if test "x$ac_cv_header_sys_socket_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" if test "x$ac_cv_header_poll_h" = xyes then : printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/un.h" "ac_cv_header_sys_un_h" "$ac_includes_default" if test "x$ac_cv_header_sys_un_h" = xyes then : printf "%s\n" "#define HAVE_SYS_UN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default" if test "x$ac_cv_header_arpa_inet_h" = xyes then : printf "%s\n" "#define HAVE_ARPA_INET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_in_h" = xyes then : printf "%s\n" "#define HAVE_NETINET_IN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/tcp.h" "ac_cv_header_netinet_tcp_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_tcp_h" = xyes then : printf "%s\n" "#define HAVE_NETINET_TCP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" if test "x$ac_cv_header_sys_param_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/uio.h" "ac_cv_header_sys_uio_h" "$ac_includes_default" if test "x$ac_cv_header_sys_uio_h" = xyes then : printf "%s\n" "#define HAVE_SYS_UIO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pwd.h" "ac_cv_header_pwd_h" "$ac_includes_default" if test "x$ac_cv_header_pwd_h" = xyes then : printf "%s\n" "#define HAVE_PWD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "grp.h" "ac_cv_header_grp_h" "$ac_includes_default" if test "x$ac_cv_header_grp_h" = xyes then : printf "%s\n" "#define HAVE_GRP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/wait.h" "ac_cv_header_sys_wait_h" "$ac_includes_default" if test "x$ac_cv_header_sys_wait_h" = xyes then : printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default" if test "x$ac_cv_header_sys_mman_h" = xyes then : printf "%s\n" "#define HAVE_SYS_MMAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "syslog.h" "ac_cv_header_syslog_h" "$ac_includes_default" if test "x$ac_cv_header_syslog_h" = xyes then : printf "%s\n" "#define HAVE_SYSLOG_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default" if test "x$ac_cv_header_netdb_h" = xyes then : printf "%s\n" "#define HAVE_NETDB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" if test "x$ac_cv_header_dlfcn_h" = xyes then : printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "err.h" "ac_cv_header_err_h" "$ac_includes_default" if test "x$ac_cv_header_err_h" = xyes then : printf "%s\n" "#define HAVE_ERR_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_h" = xyes then : printf "%s\n" "#define HAVE_PTHREAD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "endian.h" "ac_cv_header_endian_h" "$ac_includes_default" if test "x$ac_cv_header_endian_h" = xyes then : printf "%s\n" "#define HAVE_ENDIAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/endian.h" "ac_cv_header_sys_endian_h" "$ac_includes_default" if test "x$ac_cv_header_sys_endian_h" = xyes then : printf "%s\n" "#define HAVE_SYS_ENDIAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "byteswap.h" "ac_cv_header_byteswap_h" "$ac_includes_default" if test "x$ac_cv_header_byteswap_h" = xyes then : printf "%s\n" "#define HAVE_BYTESWAP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default" if test "x$ac_cv_header_malloc_h" = xyes then : printf "%s\n" "#define HAVE_MALLOC_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default" if test "x$ac_cv_header_regex_h" = xyes then : printf "%s\n" "#define HAVE_REGEX_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "getopt.h" "ac_cv_header_getopt_h" "$ac_includes_default" if test "x$ac_cv_header_getopt_h" = xyes then : printf "%s\n" "#define HAVE_GETOPT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "fnmatch.h" "ac_cv_header_fnmatch_h" "$ac_includes_default" if test "x$ac_cv_header_fnmatch_h" = xyes then : printf "%s\n" "#define HAVE_FNMATCH_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "langinfo.h" "ac_cv_header_langinfo_h" "$ac_includes_default" if test "x$ac_cv_header_langinfo_h" = xyes then : printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "xlocale.h" "ac_cv_header_xlocale_h" "$ac_includes_default" if test "x$ac_cv_header_xlocale_h" = xyes then : printf "%s\n" "#define HAVE_XLOCALE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/random.h" "ac_cv_header_linux_random_h" "$ac_includes_default" if test "x$ac_cv_header_linux_random_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_RANDOM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "ucred.h" "ac_cv_header_ucred_h" " #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif " if test "x$ac_cv_header_ucred_h" = xyes then : printf "%s\n" "#define HAVE_UCRED_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/ucred.h" "ac_cv_header_sys_ucred_h" " #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif " if test "x$ac_cv_header_sys_ucred_h" = xyes then : printf "%s\n" "#define HAVE_SYS_UCRED_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/resource.h" "ac_cv_header_sys_resource_h" "$ac_includes_default" if test "x$ac_cv_header_sys_resource_h" = xyes then : printf "%s\n" "#define HAVE_SYS_RESOURCE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/wait.h" "ac_cv_header_sys_wait_h" "$ac_includes_default" if test "x$ac_cv_header_sys_wait_h" = xyes then : printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 printf %s "checking for inline... " >&6; } if test ${ac_cv_c_inline+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo (void) {return 0; } $ac_kw foo_t foo (void) {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 printf %s "checking for C/C++ restrict keyword... " >&6; } if test ${ac_cv_c_restrict+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_restrict=no # Put '__restrict__' first, to avoid problems with glibc and non-GCC; see: # https://lists.gnu.org/archive/html/bug-autoconf/2016-02/msg00006.html # Put 'restrict' last, because C++ lacks it. for ac_kw in __restrict__ __restrict _Restrict restrict; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ typedef int *int_ptr; int foo (int_ptr $ac_kw ip) { return ip[0]; } int bar (int [$ac_kw]); /* Catch GCC bug 14050. */ int bar (int ip[$ac_kw]) { return ip[0]; } int main (void) { int s[1]; int *$ac_kw t = s; t[0] = 0; return foo (t) + bar (t); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_restrict=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_restrict" != no && break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 printf "%s\n" "$ac_cv_c_restrict" >&6; } case $ac_cv_c_restrict in restrict) ;; no) printf "%s\n" "#define restrict /**/" >>confdefs.h ;; *) printf "%s\n" "#define restrict $ac_cv_c_restrict" >>confdefs.h ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 printf %s "checking whether byte ordering is bigendian... " >&6; } if test ${ac_cv_c_bigendian+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO" then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ unsigned short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; unsigned short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } unsigned short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; unsigned short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main (void) { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_bigendian=no else $as_nop ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 printf "%s\n" "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # Check whether --enable-largefile was given. if test ${enable_largefile+y} then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 printf %s "checking for special C compiler options needed for large files... " >&6; } if test ${ac_cv_sys_largefile_CC+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : break fi rm -f core conftest.err conftest.$ac_objext conftest.beam CC="$CC -n32" if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if test ${ac_cv_sys_file_offset_bits+y} then : printf %s "(cached) " >&6 else $as_nop while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 printf %s "checking for _LARGE_FILES value needed for large files... " >&6; } if test ${ac_cv_sys_large_files+y} then : printf %s "(cached) " >&6 else $as_nop while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 printf "%s\n" "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h ;; esac rm -rf conftest* fi fi ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default " if test "x$ac_cv_type_pid_t" = xyes then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined _WIN64 && !defined __CYGWIN__ LLP64 #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_pid_type='int' else $as_nop ac_pid_type='__int64' fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 printf %s "checking for uid_t in sys/types.h... " >&6; } if test ${ac_cv_type_uid_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "uid_t" >/dev/null 2>&1 then : ac_cv_type_uid_t=yes else $as_nop ac_cv_type_uid_t=no fi rm -rf conftest* fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 printf "%s\n" "$ac_cv_type_uid_t" >&6; } if test $ac_cv_type_uid_t = no; then printf "%s\n" "#define uid_t int" >>confdefs.h printf "%s\n" "#define gid_t int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : else $as_nop printf "%s\n" "#define size_t unsigned int" >>confdefs.h fi ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT8_T 1" >>confdefs.h printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT32_T 1" >>confdefs.h printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" case $ac_cv_c_uint64_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT64_T 1" >>confdefs.h printf "%s\n" "#define uint64_t $ac_cv_c_uint64_t" >>confdefs.h ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' else $as_nop ac_cv_c_undeclared_builtin_options=$ac_arg fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; esac ac_fn_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_strerror_r" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_STRERROR_R $ac_have_decl" >>confdefs.h ### Functions provided if missing ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes then : printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat" if test "x$ac_cv_func_strlcat" = xyes then : printf "%s\n" "#define HAVE_STRLCAT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strnlen" "ac_cv_func_strnlen" if test "x$ac_cv_func_strnlen" = xyes then : printf "%s\n" "#define HAVE_STRNLEN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep" if test "x$ac_cv_func_strsep" = xyes then : printf "%s\n" "#define HAVE_STRSEP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpeereid" "ac_cv_func_getpeereid" if test "x$ac_cv_func_getpeereid" = xyes then : printf "%s\n" "#define HAVE_GETPEEREID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction" if test "x$ac_cv_func_sigaction" = xyes then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sigqueue" "ac_cv_func_sigqueue" if test "x$ac_cv_func_sigqueue" = xyes then : printf "%s\n" "#define HAVE_SIGQUEUE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memmem" "ac_cv_func_memmem" if test "x$ac_cv_func_memmem" = xyes then : printf "%s\n" "#define HAVE_MEMMEM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memrchr" "ac_cv_func_memrchr" if test "x$ac_cv_func_memrchr" = xyes then : printf "%s\n" "#define HAVE_MEMRCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mempcpy" "ac_cv_func_mempcpy" if test "x$ac_cv_func_mempcpy" = xyes then : printf "%s\n" "#define HAVE_MEMPCPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_ntop" "ac_cv_func_inet_ntop" if test "x$ac_cv_func_inet_ntop" = xyes then : printf "%s\n" "#define HAVE_INET_NTOP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_pton" "ac_cv_func_inet_pton" if test "x$ac_cv_func_inet_pton" = xyes then : printf "%s\n" "#define HAVE_INET_PTON 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "poll" "ac_cv_func_poll" if test "x$ac_cv_func_poll" = xyes then : printf "%s\n" "#define HAVE_POLL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getline" "ac_cv_func_getline" if test "x$ac_cv_func_getline" = xyes then : printf "%s\n" "#define HAVE_GETLINE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "regcomp" "ac_cv_func_regcomp" if test "x$ac_cv_func_regcomp" = xyes then : printf "%s\n" "#define HAVE_REGCOMP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "err" "ac_cv_func_err" if test "x$ac_cv_func_err" = xyes then : printf "%s\n" "#define HAVE_ERR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "errx" "ac_cv_func_errx" if test "x$ac_cv_func_errx" = xyes then : printf "%s\n" "#define HAVE_ERRX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "warn" "ac_cv_func_warn" if test "x$ac_cv_func_warn" = xyes then : printf "%s\n" "#define HAVE_WARN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "warnx" "ac_cv_func_warnx" if test "x$ac_cv_func_warnx" = xyes then : printf "%s\n" "#define HAVE_WARNX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getprogname" "ac_cv_func_getprogname" if test "x$ac_cv_func_getprogname" = xyes then : printf "%s\n" "#define HAVE_GETPROGNAME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setprogname" "ac_cv_func_setprogname" if test "x$ac_cv_func_setprogname" = xyes then : printf "%s\n" "#define HAVE_SETPROGNAME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "posix_memalign" "ac_cv_func_posix_memalign" if test "x$ac_cv_func_posix_memalign" = xyes then : printf "%s\n" "#define HAVE_POSIX_MEMALIGN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memalign" "ac_cv_func_memalign" if test "x$ac_cv_func_memalign" = xyes then : printf "%s\n" "#define HAVE_MEMALIGN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "valloc" "ac_cv_func_valloc" if test "x$ac_cv_func_valloc" = xyes then : printf "%s\n" "#define HAVE_VALLOC 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes then : printf "%s\n" "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memset_s" "ac_cv_func_memset_s" if test "x$ac_cv_func_memset_s" = xyes then : printf "%s\n" "#define HAVE_MEMSET_S 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray" if test "x$ac_cv_func_reallocarray" = xyes then : printf "%s\n" "#define HAVE_REALLOCARRAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt" if test "x$ac_cv_func_getopt" = xyes then : printf "%s\n" "#define HAVE_GETOPT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long" if test "x$ac_cv_func_getopt_long" = xyes then : printf "%s\n" "#define HAVE_GETOPT_LONG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getopt_long_only" "ac_cv_func_getopt_long_only" if test "x$ac_cv_func_getopt_long_only" = xyes then : printf "%s\n" "#define HAVE_GETOPT_LONG_ONLY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fls" "ac_cv_func_fls" if test "x$ac_cv_func_fls" = xyes then : printf "%s\n" "#define HAVE_FLS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "flsl" "ac_cv_func_flsl" if test "x$ac_cv_func_flsl" = xyes then : printf "%s\n" "#define HAVE_FLSL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "flsll" "ac_cv_func_flsll" if test "x$ac_cv_func_flsll" = xyes then : printf "%s\n" "#define HAVE_FLSLL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "ffs" "ac_cv_func_ffs" if test "x$ac_cv_func_ffs" = xyes then : printf "%s\n" "#define HAVE_FFS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "ffsl" "ac_cv_func_ffsl" if test "x$ac_cv_func_ffsl" = xyes then : printf "%s\n" "#define HAVE_FFSL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "ffsll" "ac_cv_func_ffsll" if test "x$ac_cv_func_ffsll" = xyes then : printf "%s\n" "#define HAVE_FFSLL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fnmatch" "ac_cv_func_fnmatch" if test "x$ac_cv_func_fnmatch" = xyes then : printf "%s\n" "#define HAVE_FNMATCH 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mbsnrtowcs" "ac_cv_func_mbsnrtowcs" if test "x$ac_cv_func_mbsnrtowcs" = xyes then : printf "%s\n" "#define HAVE_MBSNRTOWCS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "nl_langinfo" "ac_cv_func_nl_langinfo" if test "x$ac_cv_func_nl_langinfo" = xyes then : printf "%s\n" "#define HAVE_NL_LANGINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtod_l" "ac_cv_func_strtod_l" if test "x$ac_cv_func_strtod_l" = xyes then : printf "%s\n" "#define HAVE_STRTOD_L 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtonum" "ac_cv_func_strtonum" if test "x$ac_cv_func_strtonum" = xyes then : printf "%s\n" "#define HAVE_STRTONUM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "asprintf" "ac_cv_func_asprintf" if test "x$ac_cv_func_asprintf" = xyes then : printf "%s\n" "#define HAVE_ASPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf" if test "x$ac_cv_func_vasprintf" = xyes then : printf "%s\n" "#define HAVE_VASPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm" if test "x$ac_cv_func_timegm" = xyes then : printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h fi ### Functions provided only on win32 ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r" if test "x$ac_cv_func_localtime_r" = xyes then : printf "%s\n" "#define HAVE_LOCALTIME_R 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" if test "x$ac_cv_func_gettimeofday" = xyes then : printf "%s\n" "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "recvmsg" "ac_cv_func_recvmsg" if test "x$ac_cv_func_recvmsg" = xyes then : printf "%s\n" "#define HAVE_RECVMSG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sendmsg" "ac_cv_func_sendmsg" if test "x$ac_cv_func_sendmsg" = xyes then : printf "%s\n" "#define HAVE_SENDMSG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "usleep" "ac_cv_func_usleep" if test "x$ac_cv_func_usleep" = xyes then : printf "%s\n" "#define HAVE_USLEEP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrusage" "ac_cv_func_getrusage" if test "x$ac_cv_func_getrusage" = xyes then : printf "%s\n" "#define HAVE_GETRUSAGE 1" >>confdefs.h fi ### Functions used by libusual itself ac_fn_c_check_func "$LINENO" "syslog" "ac_cv_func_syslog" if test "x$ac_cv_func_syslog" = xyes then : printf "%s\n" "#define HAVE_SYSLOG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mmap" "ac_cv_func_mmap" if test "x$ac_cv_func_mmap" = xyes then : printf "%s\n" "#define HAVE_MMAP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpeerucred" "ac_cv_func_getpeerucred" if test "x$ac_cv_func_getpeerucred" = xyes then : printf "%s\n" "#define HAVE_GETPEERUCRED 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "arc4random_buf" "ac_cv_func_arc4random_buf" if test "x$ac_cv_func_arc4random_buf" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM_BUF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : printf "%s\n" "#define HAVE_GETENTROPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrandom" "ac_cv_func_getrandom" if test "x$ac_cv_func_getrandom" = xyes then : printf "%s\n" "#define HAVE_GETRANDOM 1" >>confdefs.h fi ### win32: link with ws2_32 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing WSAGetLastError" >&5 printf %s "checking for library containing WSAGetLastError... " >&6; } if test ${ac_cv_search_WSAGetLastError+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char WSAGetLastError (); int main (void) { return WSAGetLastError (); ; return 0; } _ACEOF for ac_lib in '' ws2_32 do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_WSAGetLastError=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_WSAGetLastError+y} then : break fi done if test ${ac_cv_search_WSAGetLastError+y} then : else $as_nop ac_cv_search_WSAGetLastError=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_WSAGetLastError" >&5 printf "%s\n" "$ac_cv_search_WSAGetLastError" >&6; } ac_res=$ac_cv_search_WSAGetLastError if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi if test $ac_cv_have_decl_strerror_r = yes; then # For backward compatibility's sake, define HAVE_STRERROR_R. # (We used to run AC_CHECK_FUNCS_ONCE for strerror_r, as well # as AC_CHECK_DECLS_ONCE.) printf "%s\n" "#define HAVE_STRERROR_R 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 printf %s "checking whether strerror_r returns char *... " >&6; } if test ${ac_cv_func_strerror_r_char_p+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_func_strerror_r_char_p=no if test $ac_cv_have_decl_strerror_r = yes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char buf[100]; char x = *strerror_r (0, buf, sizeof buf); char *p = strerror_r (0, buf, sizeof buf); return !p || x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_strerror_r_char_p=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 printf "%s\n" "$ac_cv_func_strerror_r_char_p" >&6; } if test $ac_cv_func_strerror_r_char_p = yes; then printf "%s\n" "#define STRERROR_R_CHAR_P 1" >>confdefs.h fi ### { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for integer enc/dec functions" >&5 printf %s "checking for integer enc/dec functions... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_ENDIAN_H #include #endif char p[] = "01234567"; int main(void) { be16enc(p, 0); be32enc(p, 1); be64enc(p, 2); le16enc(p, 2); le32enc(p, 3); le64enc(p, 4); return (int)(be16dec(p) + be32dec(p) + be64dec(p)) + (int)(le16dec(p) + le32dec(p) + le64dec(p)); } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found" >&5 printf "%s\n" "found" >&6; } printf "%s\n" "#define HAVE_ENCDEC_FUNCS 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 printf %s "checking for library containing clock_gettime... " >&6; } if test ${ac_cv_search_clock_gettime+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char clock_gettime (); int main (void) { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_clock_gettime+y} then : break fi done if test ${ac_cv_search_clock_gettime+y} then : else $as_nop ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 printf "%s\n" "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing getsockname" >&5 printf %s "checking for library containing getsockname... " >&6; } if test ${ac_cv_search_getsockname+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getsockname (); int main (void) { return getsockname (); ; return 0; } _ACEOF for ac_lib in '' socket do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_getsockname=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_getsockname+y} then : break fi done if test ${ac_cv_search_getsockname+y} then : else $as_nop ac_cv_search_getsockname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getsockname" >&5 printf "%s\n" "$ac_cv_search_getsockname" >&6; } ac_res=$ac_cv_search_getsockname if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 printf %s "checking for library containing gethostbyname... " >&6; } if test ${ac_cv_search_gethostbyname+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gethostbyname (); int main (void) { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' nsl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_gethostbyname+y} then : break fi done if test ${ac_cv_search_gethostbyname+y} then : else $as_nop ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 printf "%s\n" "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing hstrerror" >&5 printf %s "checking for library containing hstrerror... " >&6; } if test ${ac_cv_search_hstrerror+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char hstrerror (); int main (void) { return hstrerror (); ; return 0; } _ACEOF for ac_lib in '' resolv do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_hstrerror=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_hstrerror+y} then : break fi done if test ${ac_cv_search_hstrerror+y} then : else $as_nop ac_cv_search_hstrerror=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_hstrerror" >&5 printf "%s\n" "$ac_cv_search_hstrerror" >&6; } ac_res=$ac_cv_search_hstrerror if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_fn_c_check_func "$LINENO" "lstat" "ac_cv_func_lstat" if test "x$ac_cv_func_lstat" = xyes then : printf "%s\n" "#define HAVE_LSTAT 1" >>confdefs.h fi pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libevent" >&5 printf %s "checking for libevent... " >&6; } if test -n "$LIBEVENT_CFLAGS"; then pkg_cv_LIBEVENT_CFLAGS="$LIBEVENT_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent\""; } >&5 ($PKG_CONFIG --exists --print-errors "libevent") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEVENT_CFLAGS=`$PKG_CONFIG --cflags "libevent" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBEVENT_LIBS"; then pkg_cv_LIBEVENT_LIBS="$LIBEVENT_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent\""; } >&5 ($PKG_CONFIG --exists --print-errors "libevent") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEVENT_LIBS=`$PKG_CONFIG --libs "libevent" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBEVENT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libevent" 2>&1` else LIBEVENT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libevent" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBEVENT_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libevent) were not met: $LIBEVENT_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LIBEVENT_CFLAGS and LIBEVENT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LIBEVENT_CFLAGS and LIBEVENT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else LIBEVENT_CFLAGS=$pkg_cv_LIBEVENT_CFLAGS LIBEVENT_LIBS=$pkg_cv_LIBEVENT_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi pam_support=no # Check whether --with-pam was given. if test ${with_pam+y} then : withval=$with_pam; PAM= if test "$withval" != no; then have_pthreads=no # Look for PAM header and lib for ac_header in security/pam_appl.h do : ac_fn_c_check_header_compile "$LINENO" "security/pam_appl.h" "ac_cv_header_security_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_security_pam_appl_h" = xyes then : printf "%s\n" "#define HAVE_SECURITY_PAM_APPL_H 1" >>confdefs.h have_pam_header=t fi done for ac_header in pthread.h do : ac_fn_c_check_header_compile "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_h" = xyes then : printf "%s\n" "#define HAVE_PTHREAD_H 1" >>confdefs.h have_pthreads=yes fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing pam_start" >&5 printf %s "checking for library containing pam_start... " >&6; } if test ${ac_cv_search_pam_start+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pam_start (); int main (void) { return pam_start (); ; return 0; } _ACEOF for ac_lib in '' pam do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_pam_start=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_pam_start+y} then : break fi done if test ${ac_cv_search_pam_start+y} then : else $as_nop ac_cv_search_pam_start=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pam_start" >&5 printf "%s\n" "$ac_cv_search_pam_start" >&6; } ac_res=$ac_cv_search_pam_start if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_libpam=t fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5 printf %s "checking for library containing pthread_create... " >&6; } if test ${ac_cv_search_pthread_create+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pthread_create (); int main (void) { return pthread_create (); ; return 0; } _ACEOF for ac_lib in '' pthread do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_pthread_create=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_pthread_create+y} then : break fi done if test ${ac_cv_search_pthread_create+y} then : else $as_nop ac_cv_search_pthread_create=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5 printf "%s\n" "$ac_cv_search_pthread_create" >&6; } ac_res=$ac_cv_search_pthread_create if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" else $as_nop have_pthreads=no fi if test x"${have_pthreads}" != xyes; then as_fn_error $? "pthread library should be available for PAM support" "$LINENO" 5 fi if test x"${have_pam_header}" != x -a x"${have_libpam}" != x -a x"${have_pthreads}" = xyes; then pam_support=yes printf "%s\n" "#define HAVE_PAM 1" >>confdefs.h fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build with systemd support" >&5 printf %s "checking whether to build with systemd support... " >&6; } # Check whether --with-systemd was given. if test ${with_systemd+y} then : withval=$with_systemd; if test "$withval" != no; then with_systemd=yes; else with_systemd=no; fi else $as_nop with_systemd=no fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_systemd" >&5 printf "%s\n" "$with_systemd" >&6; } if test "$with_systemd" = yes; then printf "%s\n" "#define USE_SYSTEMD 1" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "systemd/sd-daemon.h" "ac_cv_header_systemd_sd_daemon_h" "$ac_includes_default" if test "x$ac_cv_header_systemd_sd_daemon_h" = xyes then : else $as_nop as_fn_error $? "header file is required for systemd support" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing sd_notify" >&5 printf %s "checking for library containing sd_notify... " >&6; } if test ${ac_cv_search_sd_notify+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char sd_notify (); int main (void) { return sd_notify (); ; return 0; } _ACEOF for ac_lib in '' systemd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_sd_notify=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_sd_notify+y} then : break fi done if test ${ac_cv_search_sd_notify+y} then : else $as_nop ac_cv_search_sd_notify=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sd_notify" >&5 printf "%s\n" "$ac_cv_search_sd_notify" >&6; } ac_res=$ac_cv_search_sd_notify if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi fi ## ## DNS backend ## # make sure all vars are set use_cares=no use_evdns=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use c-ares for DNS lookups" >&5 printf %s "checking whether to use c-ares for DNS lookups... " >&6; } # Check whether --with-cares was given. if test ${with_cares+y} then : withval=$with_cares; if test "$withval" = "no"; then use_cares=no elif test "$withval" = "yes"; then use_cares="$withval" CARES_CFLAGS="" CARES_LIBS="-lcares" elif test "$withval" = "auto"; then use_cares="$withval" else use_cares=yes CARES_CFLAGS="-I$withval/include" CARES_LIBS="-L$withval/lib -lcares" fi else $as_nop use_cares=auto fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $use_cares" >&5 printf "%s\n" "$use_cares" >&6; } if test "$use_cares" = "auto"; then pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libcares >= 1.9.0" >&5 printf %s "checking for libcares >= 1.9.0... " >&6; } if test -n "$CARES_CFLAGS"; then pkg_cv_CARES_CFLAGS="$CARES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcares >= 1.9.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcares >= 1.9.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CARES_CFLAGS=`$PKG_CONFIG --cflags "libcares >= 1.9.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$CARES_LIBS"; then pkg_cv_CARES_LIBS="$CARES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcares >= 1.9.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcares >= 1.9.0") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CARES_LIBS=`$PKG_CONFIG --libs "libcares >= 1.9.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then CARES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcares >= 1.9.0" 2>&1` else CARES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcares >= 1.9.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$CARES_PKG_ERRORS" >&5 use_cares=no elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } use_cares=no else CARES_CFLAGS=$pkg_cv_CARES_CFLAGS CARES_LIBS=$pkg_cv_CARES_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } use_cares=yes fi fi if test "$use_cares" = "yes"; then printf "%s\n" "#define USE_CARES 1" >>confdefs.h tmp_CFLAGS="$CFLAGS" tmp_LIBS="$LIBS" CFLAGS="$CARES_CFLAGS $CFLAGS" LIBS="$CARES_LIBS $LIBS" # portability substitute for arpa/nameser.h, needed mainly for Windows ac_fn_c_check_header_compile "$LINENO" "ares_nameser.h" "ac_cv_header_ares_nameser_h" "$ac_includes_default" if test "x$ac_cv_header_ares_nameser_h" = xyes then : printf "%s\n" "#define HAVE_ARES_NAMESER_H 1" >>confdefs.h fi LIBS="$tmp_LIBS" CFLAGS="$tmp_CFLAGS" else # !cares # Check whether --enable-evdns was given. if test ${enable_evdns+y} then : enableval=$enable_evdns; use_evdns=$enableval else $as_nop use_evdns=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use libevent for DNS lookups" >&5 printf %s "checking whether to use libevent for DNS lookups... " >&6; } if test "$use_evdns" = "yes"; then printf "%s\n" "#define USE_EVDNS 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "$use_cares.$use_evdns" = "no.no"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing getaddrinfo_a" >&5 printf %s "checking for library containing getaddrinfo_a... " >&6; } if test ${ac_cv_search_getaddrinfo_a+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getaddrinfo_a (); int main (void) { return getaddrinfo_a (); ; return 0; } _ACEOF for ac_lib in '' anl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_getaddrinfo_a=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_getaddrinfo_a+y} then : break fi done if test ${ac_cv_search_getaddrinfo_a+y} then : else $as_nop ac_cv_search_getaddrinfo_a=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getaddrinfo_a" >&5 printf "%s\n" "$ac_cv_search_getaddrinfo_a" >&6; } ac_res=$ac_cv_search_getaddrinfo_a if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use native getaddinfo_a" >&5 printf %s "checking whether to use native getaddinfo_a... " >&6; } if test ${ac_cv_usual_glibc_gaia+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_NETDB_H #include #endif int main (void) { #if __GLIBC_PREREQ(2,9) getaddrinfo_a(0,NULL,0,NULL); #else none or broken #endif ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_usual_glibc_gaia=yes else $as_nop ac_cv_usual_glibc_gaia=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_usual_glibc_gaia" >&5 printf "%s\n" "$ac_cv_usual_glibc_gaia" >&6; } if test x"$ac_cv_usual_glibc_gaia" = xyes ; then printf "%s\n" "#define HAVE_GETADDRINFO_A 1" >>confdefs.h else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" if test "x$PTHREAD_CC" != "x" then : CC="$PTHREAD_CC" fi CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS" >&5 printf %s "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pthread_join (); int main (void) { return pthread_join (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 printf "%s\n" "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items with a "," contain both # C compiler flags (before ",") and linker flags (after ","). Other items # starting with a "-" are C compiler flags, and remaining items are # library names, except for "none" which indicates that we try without # any flags at all, and "pthread-config" which is a program returning # the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1 then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5 printf "%s\n" "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;} fi rm -rf conftest* ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" ;; esac # Are we compiling with Clang? { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC is Clang" >&5 printf %s "checking whether $CC is Clang... " >&6; } if test ${ax_cv_PTHREAD_CLANG+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1 then : ax_cv_PTHREAD_CLANG=yes fi rm -rf conftest* fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5 printf "%s\n" "$ax_cv_PTHREAD_CLANG" >&6; } ax_pthread_clang="$ax_cv_PTHREAD_CLANG" # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) # Note that for GCC and Clang -pthread generally implies -lpthread, # except when -nostdlib is passed. # This is problematic using libtool to build C++ shared libraries with pthread: # [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 # [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 # [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 # To solve this, first try -pthread together with -lpthread for GCC if test "x$GCC" = "xyes" then : ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags" fi # Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first if test "x$ax_pthread_clang" = "xyes" then : ax_pthread_flags="-pthread,-lpthread -pthread" fi # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac if test "x$ax_pthread_check_macro" = "x--" then : ax_pthread_check_cond=0 else $as_nop ax_pthread_check_cond="!defined($ax_pthread_check_macro)" fi if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 printf %s "checking whether pthreads work without any flags... " >&6; } ;; *,*) PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with \"$PTHREAD_CFLAGS\" and \"$PTHREAD_LIBS\"" >&5 printf %s "checking whether pthreads work with \"$PTHREAD_CFLAGS\" and \"$PTHREAD_LIBS\"... " >&6; } ;; -*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $ax_pthread_try_flag" >&5 printf %s "checking whether pthreads work with $ax_pthread_try_flag... " >&6; } PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) # Extract the first word of "pthread-config", so it can be a program name with args. set dummy pthread-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ax_pthread_config+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ax_pthread_config"; then ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ax_pthread_config="yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" fi fi ax_pthread_config=$ac_cv_prog_ax_pthread_config if test -n "$ax_pthread_config"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 printf "%s\n" "$ax_pthread_config" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ax_pthread_config" = "xno" then : continue fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$ax_pthread_try_flag" >&5 printf %s "checking for the pthreads library -l$ax_pthread_try_flag... " >&6; } PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void *some_global = NULL; static void routine(void *a) { /* To avoid any unused-parameter or unused-but-set-parameter warning. */ some_global = a; } static void *start_routine(void *a) { return a; } int main (void) { pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 printf "%s\n" "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = "xyes" then : break fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread" >&5 printf %s "checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread... " >&6; } if test ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`printf "%s\n" "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do if test "x$ax_pthread_try" = "xunknown" then : break fi CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void){return 0;} _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_link="$ax_pthread_2step_ac_link" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void){return 0;} _ACEOF if ac_fn_c_try_link "$LINENO" then : break fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" if test "x$ax_pthread_try" = "x" then : ax_pthread_try=no fi ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5 printf "%s\n" "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; } case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 printf %s "checking for joinable pthread attribute... " >&6; } if test ${ax_cv_PTHREAD_JOINABLE_ATTR+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int attr = $ax_pthread_attr; return attr /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5 printf "%s\n" "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; } if test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes" then : printf "%s\n" "#define PTHREAD_CREATE_JOINABLE $ax_cv_PTHREAD_JOINABLE_ATTR" >>confdefs.h ax_pthread_joinable_attr_defined=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether more special flags are required for pthreads" >&5 printf %s "checking whether more special flags are required for pthreads... " >&6; } if test ${ax_cv_PTHREAD_SPECIAL_FLAGS+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5 printf "%s\n" "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; } if test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes" then : PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 printf %s "checking for PTHREAD_PRIO_INHERIT... " >&6; } if test ${ax_cv_PTHREAD_PRIO_INHERIT+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int i = PTHREAD_PRIO_INHERIT; return i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_cv_PTHREAD_PRIO_INHERIT=yes else $as_nop ax_cv_PTHREAD_PRIO_INHERIT=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 printf "%s\n" "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes" then : printf "%s\n" "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h ax_pthread_prio_inherit_defined=yes fi CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) case "x/$CC" in #( x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : #handle absolute path differently from PATH based program lookup case "x$CC" in #( x/*) : if as_fn_executable_p ${CC}_r then : PTHREAD_CC="${CC}_r" fi ;; #( *) : for ac_prog in ${CC}_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PTHREAD_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$PTHREAD_CC"; then ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_PTHREAD_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PTHREAD_CC=$ac_cv_prog_PTHREAD_CC if test -n "$PTHREAD_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 printf "%s\n" "$PTHREAD_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$PTHREAD_CC" && break done test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" ;; esac ;; #( *) : ;; esac ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then printf "%s\n" "#define HAVE_PTHREAD 1" >>confdefs.h : else ax_pthread_ok=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Threads not available and fallback getaddrinfo_a() non-functional." >&5 printf "%s\n" "Threads not available and fallback getaddrinfo_a() non-functional." >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="$PTHREAD_CC" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" fi fi fi # !cares ## end of DNS tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for OpenSSL" >&5 printf %s "checking for OpenSSL... " >&6; } # Check whether --with-openssl was given. if test ${with_openssl+y} then : withval=$with_openssl; if test "$withval" = "no"; then tls_support=no elif test "$withval" = "yes"; then tls_support=libssl TLS_LIBS="-lssl -lcrypto" else tls_support=libssl TLS_CPPFLAGS="-I$withval/include" TLS_LDFLAGS="-L$withval/lib" TLS_LIBS="-lssl -lcrypto" fi else $as_nop tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="-lssl -lcrypto" fi if test "$tls_support" = "auto" -o "$tls_support" = "libssl"; then printf "%s\n" "#define USUAL_LIBSSL_FOR_TLS 1" >>confdefs.h printf "%s\n" "#define OPENSSL_API_COMPAT 0x00908000L" >>confdefs.h tmp_LIBS="$LIBS" tmp_LDFLAGS="$LDFLAGS" tmp_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$TLS_CPPFLAGS $CPPFLAGS" LDFLAGS="$TLS_LDFLAGS $LDFLAGS" LIBS="$TLS_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { SSL_CTX *ctx = SSL_CTX_new(SSLv23_method()); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : tls_support=yes; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found" >&5 printf "%s\n" "found" >&6; } else $as_nop as_fn_error $? "not found" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ac_fn_c_check_func "$LINENO" "SSL_CTX_use_certificate_chain_mem" "ac_cv_func_SSL_CTX_use_certificate_chain_mem" if test "x$ac_cv_func_SSL_CTX_use_certificate_chain_mem" = xyes then : printf "%s\n" "#define HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "SSL_CTX_load_verify_mem" "ac_cv_func_SSL_CTX_load_verify_mem" if test "x$ac_cv_func_SSL_CTX_load_verify_mem" = xyes then : printf "%s\n" "#define HAVE_SSL_CTX_LOAD_VERIFY_MEM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "asn1_time_parse" "ac_cv_func_asn1_time_parse" if test "x$ac_cv_func_asn1_time_parse" = xyes then : printf "%s\n" "#define HAVE_ASN1_TIME_PARSE 1" >>confdefs.h fi CPPFLAGS="$tmp_CPPFLAGS" LDFLAGS="$tmp_LDFLAGS" LIBS="$tmp_LIBS" cafile=auto { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for root CA certs" >&5 printf %s "checking for root CA certs... " >&6; } # Check whether --with-root-ca-file was given. if test ${with_root_ca_file+y} then : withval=$with_root_ca_file; if test "$withval" = "no"; then : elif test "$withval" = "yes"; then : else cafile="$withval" fi fi if test "$cafile" = "auto"; then for cafile in \ /etc/ssl/certs/ca-certificates.crt \ /etc/pki/tls/certs/ca-bundle.crt \ /etc/ssl/ca-bundle.pem \ /etc/pki/tls/cacert.pem \ /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \ /etc/ssl/cert.pem do if test -f "$cafile"; then break fi done fi printf "%s\n" "#define USUAL_TLS_CA_FILE \"$cafile\"" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cafile" >&5 printf "%s\n" "$cafile" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; else $as_nop enable_debug=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build debug binary" >&5 printf %s "checking whether to build debug binary... " >&6; } if test "$enable_debug" = "yes"; then LDFLAGS="-g $LDFLAGS" BININSTALL="$INSTALL" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else BININSTALL="$INSTALL -s" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Check whether --enable-cassert was given. if test ${enable_cassert+y} then : enableval=$enable_cassert; fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable asserts" >&5 printf %s "checking whether to enable asserts... " >&6; } if test "$enable_cassert" = "yes"; then printf "%s\n" "#define CASSERT 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Check whether --enable-werror was given. if test ${enable_werror+y} then : enableval=$enable_werror; fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to fail on warnings" >&5 printf %s "checking whether to fail on warnings... " >&6; } if test "$enable_werror" = "yes"; then CFLAGS="$CFLAGS -Werror" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi PACKAGE_VERSION_4B=`echo "${PACKAGE_VERSION}.0" | sed -e 's/\./,/g'` printf "%s\n" "#define PACKAGE_VERSION_4B $PACKAGE_VERSION_4B" >>confdefs.h ac_config_files="$ac_config_files config.mak" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by PgBouncer $as_me 1.24.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to . PgBouncer home page: ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ PgBouncer config.status 1.24.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "lib/usual/config.h") CONFIG_HEADERS="$CONFIG_HEADERS lib/usual/config.h" ;; "config.mak") CONFIG_FILES="$CONFIG_FILES config.mak" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi test -f Makefile || { echo "Linking Makefile" ln -s $srcdir/Makefile } echo "" echo "Results:" if test "$use_cares" = "yes"; then echo " adns = c-ares" elif test "$use_evdns" = "yes"; then echo " adns = evdns2" elif test "$ac_cv_usual_glibc_gaia" = "yes"; then echo " adns = libc" else echo " adns = compat" fi echo " pam = $pam_support" echo " systemd = $with_systemd" echo " tls = $tls_support" echo "" pgbouncer-1.24.1/configure.ac0000644000175000000000000001262714777762222013005 00000000000000dnl Process this file with autoconf to produce a configure script. AC_INIT([PgBouncer], [1.24.1], [https://github.com/pgbouncer/pgbouncer/issues], [], [https://www.pgbouncer.org/]) AC_CONFIG_SRCDIR(src/janitor.c) AC_CONFIG_HEADERS([lib/usual/config.h]) AC_PREREQ([2.59]) dnl basic init AC_USUAL_INIT dnl Checks for programs. AC_USUAL_PROGRAM_CHECK PKG_PROG_PKG_CONFIG AC_CHECK_PROGS(PANDOC, pandoc, pandoc) AC_CHECK_PROGS(PYTHON, [python3 python], python3) dnl check for windows tools if test "$PORTNAME" = "win32"; then AC_CHECK_TOOL([WINDRES], [windres]) AC_CHECK_TOOL([DLLWRAP], [dllwrap]) AC_CHECK_TOOL([DLLTOOL], [dlltool]) fi AC_CHECK_TOOL([STRIP], [strip]) dnl Checks for header files. AC_USUAL_HEADER_CHECK AC_CHECK_HEADERS([sys/resource.h sys/wait.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_USUAL_TYPE_CHECK dnl autoconf 2.59 does not have UINT macros nor docdir m4_ifdef([AC_TYPE_UINT8_T], [ AC_TYPE_UINT8_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T ], [ datarootdir='${prefix}/share' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' AC_SUBST(datarootdir) AC_SUBST(docdir) ]) dnl Checks for library functions. AC_USUAL_FUNCTION_CHECK AC_SEARCH_LIBS(clock_gettime, rt) AC_SEARCH_LIBS(getsockname, socket) AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(hstrerror, resolv) AC_CHECK_FUNCS(lstat) dnl Find libevent PKG_CHECK_MODULES(LIBEVENT, libevent) dnl Check for PAM authentication support pam_support=no AC_ARG_WITH(pam, AS_HELP_STRING([--with-pam], [build with PAM support]), [ PAM= if test "$withval" != no; then have_pthreads=no # Look for PAM header and lib AC_CHECK_HEADERS(security/pam_appl.h, [have_pam_header=t]) AC_CHECK_HEADERS(pthread.h, [have_pthreads=yes]) AC_SEARCH_LIBS(pam_start, pam, [have_libpam=t]) AC_SEARCH_LIBS(pthread_create, pthread, [], [have_pthreads=no]) if test x"${have_pthreads}" != xyes; then AC_MSG_ERROR([pthread library should be available for PAM support]) fi if test x"${have_pam_header}" != x -a x"${have_libpam}" != x -a x"${have_pthreads}" = xyes; then pam_support=yes AC_DEFINE(HAVE_PAM, 1, [PAM support]) fi fi ], []) dnl Check for systemd support AC_MSG_CHECKING([whether to build with systemd support]) AC_ARG_WITH(systemd, [AS_HELP_STRING([--with-systemd], [build with systemd support])], [if test "$withval" != no; then with_systemd=yes; else with_systemd=no; fi], [with_systemd=no]) AC_MSG_RESULT([$with_systemd]) AC_SUBST(with_systemd)[]dnl if test "$with_systemd" = yes; then AC_DEFINE([USE_SYSTEMD], 1, [Define to build with systemd support. (--with-systemd)]) AC_CHECK_HEADER(systemd/sd-daemon.h, [], [AC_MSG_ERROR([header file is required for systemd support])]) AC_SEARCH_LIBS(sd_notify, systemd) fi ## ## DNS backend ## # make sure all vars are set use_cares=no use_evdns=no dnl Find c-ares AC_MSG_CHECKING([whether to use c-ares for DNS lookups]) AC_ARG_WITH(cares, AS_HELP_STRING([--with-cares@<:@=PREFIX@:>@], [build with c-ares support]), [ if test "$withval" = "no"; then use_cares=no elif test "$withval" = "yes"; then use_cares="$withval" CARES_CFLAGS="" CARES_LIBS="-lcares" elif test "$withval" = "auto"; then use_cares="$withval" else use_cares=yes CARES_CFLAGS="-I$withval/include" CARES_LIBS="-L$withval/lib -lcares" fi ], [use_cares=auto]) AC_MSG_RESULT([$use_cares]) if test "$use_cares" = "auto"; then PKG_CHECK_MODULES(CARES, [libcares >= 1.9.0], [use_cares=yes], [use_cares=no]) fi if test "$use_cares" = "yes"; then AC_DEFINE(USE_CARES, 1, [Use c-ares for name resolution.]) tmp_CFLAGS="$CFLAGS" tmp_LIBS="$LIBS" CFLAGS="$CARES_CFLAGS $CFLAGS" LIBS="$CARES_LIBS $LIBS" # portability substitute for arpa/nameser.h, needed mainly for Windows AC_CHECK_HEADERS([ares_nameser.h]) LIBS="$tmp_LIBS" CFLAGS="$tmp_CFLAGS" else # !cares dnl Allow user to override the decision AC_ARG_ENABLE(evdns, AS_HELP_STRING([--disable-evdns], [do not use libevent for DNS lookups]), [use_evdns=$enableval], [use_evdns=yes]) AC_MSG_CHECKING([whether to use libevent for DNS lookups]) if test "$use_evdns" = "yes"; then AC_DEFINE(USE_EVDNS, 1, [Use libevent for DNS lookups.]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi dnl Check if need getaddinfo_a compat if test "$use_cares.$use_evdns" = "no.no"; then AC_USUAL_GETADDRINFO_A fi fi # !cares ## end of DNS AC_USUAL_TLS AC_USUAL_DEBUG AC_USUAL_CASSERT AC_USUAL_WERROR PACKAGE_VERSION_4B=`echo "${PACKAGE_VERSION}.0" | sed -e 's/\./,/g'` AC_DEFINE_UNQUOTED(PACKAGE_VERSION_4B, [$PACKAGE_VERSION_4B], [Define to the version of this package for Windows resource file (1,2,3,4).])dnl dnl Output findings AC_CONFIG_FILES([config.mak]) AC_OUTPUT dnl If separate build dir, link Makefile over test -f Makefile || { echo "Linking Makefile" ln -s $srcdir/Makefile } echo "" echo "Results:" dnl Note: Report here should match selection in src/dnslookup.c if test "$use_cares" = "yes"; then echo " adns = c-ares" elif test "$use_evdns" = "yes"; then echo " adns = evdns2" elif test "$ac_cv_usual_glibc_gaia" = "yes"; then echo " adns = libc" else echo " adns = compat" fi echo " pam = $pam_support" echo " systemd = $with_systemd" echo " tls = $tls_support" echo "" pgbouncer-1.24.1/doc/0000755000000000000000000000000014777762567011333 500000000000000pgbouncer-1.24.1/doc/usage.md0000644000175000000000000006537114777762222012716 00000000000000# pgbouncer ## Synopsis pgbouncer [-d][-R][-v][-u user] pgbouncer -V|-h On Windows, the options are: pgbouncer.exe [-v][-u user] pgbouncer.exe -V|-h Additional options for setting up a Windows service: pgbouncer.exe --regservice pgbouncer.exe --unregservice ## Description **pgbouncer** is a PostgreSQL connection pooler. Any target application can be connected to **pgbouncer** as if it were a PostgreSQL server, and **pgbouncer** will create a connection to the actual server, or it will reuse one of its existing connections. The aim of **pgbouncer** is to lower the performance impact of opening new connections to PostgreSQL. In order not to compromise transaction semantics for connection pooling, **pgbouncer** supports several types of pooling when rotating connections: Session pooling : Most polite method. When a client connects, a server connection will be assigned to it for the whole duration the client stays connected. When the client disconnects, the server connection will be put back into the pool. This is the default method. Transaction pooling : A server connection is assigned to a client only during a transaction. When PgBouncer notices that transaction is over, the server connection will be put back into the pool. Statement pooling : Most aggressive method. The server connection will be put back into the pool immediately after a query completes. Multi-statement transactions are disallowed in this mode as they would break. The administration interface of **pgbouncer** consists of some new `SHOW` commands available when connected to a special "virtual" database **pgbouncer**. ## Quick-start Basic setup and usage is as follows. 1. Create a pgbouncer.ini file. Details in **pgbouncer(5)**. Simple example: [databases] template1 = host=localhost port=5432 dbname=template1 [pgbouncer] listen_port = 6432 listen_addr = localhost auth_type = md5 auth_file = userlist.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser 2. Create a `userlist.txt` file that contains the users allowed in: "someuser" "same_password_as_in_server" 3. Launch **pgbouncer**: $ pgbouncer -d pgbouncer.ini 4. Have your application (or the **psql** client) connect to **pgbouncer** instead of directly to the PostgreSQL server: $ psql -p 6432 -U someuser template1 5. Manage **pgbouncer** by connecting to the special administration database **pgbouncer** and issuing `SHOW HELP;` to begin: $ psql -p 6432 -U someuser pgbouncer pgbouncer=# SHOW HELP; NOTICE: Console usage DETAIL: SHOW [HELP|CONFIG|DATABASES|FDS|POOLS|CLIENTS|SERVERS|SOCKETS|LISTS|VERSION|...] SET key = arg RELOAD PAUSE SUSPEND RESUME SHUTDOWN [...] 6. If you made changes to the pgbouncer.ini file, you can reload it with: pgbouncer=# RELOAD; ## Command line switches `-d`, `--daemon` : Run in the background. Without it, the process will run in the foreground. In daemon mode, setting `pidfile` as well as `logfile` or `syslog` is required. No log messages will be written to stderr after going into the background. Note: Does not work on Windows; **pgbouncer** need to run as service there. `-R`, `--reboot` : **DEPRECATED: Instead of this option use a rolling restart with multiple pgbouncer processes listening on the same port using so_reuseport instead** Do an online restart. That means connecting to the running process, loading the open sockets from it, and then using them. If there is no active process, boot normally. Note: Works only if OS supports Unix sockets and the `unix_socket_dir` is not disabled in configuration. Does not work on Windows. Does not work with TLS connections, they are dropped. `-u` _USERNAME_, `--user=`_USERNAME_ : Switch to the given user on startup. `-v`, `--verbose` : Increase verbosity. Can be used multiple times. `-q`, `--quiet` : Be quiet: do not log to stderr. This does not affect logging verbosity, only that stderr is not to be used. For use in init.d scripts. `-V`, `--version` : Show version. `-h`, `--help` : Show short help. `--regservice` : Win32: Register pgbouncer to run as Windows service. The **service_name** configuration parameter value is used as the name to register under. `--unregservice` : Win32: Unregister Windows service. ## Admin console The console is available by connecting as normal to the database **pgbouncer**: $ psql -p 6432 pgbouncer Only users listed in the configuration parameters **admin_users** or **stats_users** are allowed to log in to the console. (Except when `auth_type=any`, then any user is allowed in as a stats_user.) Additionally, the user name **pgbouncer** is allowed to log in without password, if the login comes via the Unix socket and the client has same Unix user UID as the running process. The admin console currently only supports the simple query protocol. Some drivers use the extended query protocol for all commands; these drivers will not work for this. ### Show commands The **SHOW** commands output information. Each command is described below. #### SHOW STATS Shows statistics. In this and related commands, the total figures are since process start, the averages are updated every `stats_period`. database : Statistics are presented per database. total_xact_count : Total number of SQL transactions pooled by **pgbouncer**. total_query_count : Total number of SQL commands pooled by **pgbouncer**. total_server_assignment_count : Total times a server was assigned to a client total_received : Total volume in bytes of network traffic received by **pgbouncer**. total_sent : Total volume in bytes of network traffic sent by **pgbouncer**. total_xact_time : Total number of microseconds spent by **pgbouncer** when connected to PostgreSQL in a transaction, either idle in transaction or executing queries. total_query_time : Total number of microseconds spent by **pgbouncer** when actively connected to PostgreSQL, executing queries. total_wait_time : Time spent by clients waiting for a server, in microseconds. Updated when a client connection is assigned a backend connection. total_client_parse_count : Total number of prepared statements created by clients. Only applicable in named prepared statement tracking mode, see `max_prepared_statements`. total_server_parse_count : Total number of prepared statements created by **pgbouncer** on a server. Only applicable in named prepared statement tracking mode, see `max_prepared_statements`. total_bind_count : Total number of prepared statements readied for execution by clients and forwarded to PostgreSQL by **pgbouncer**. Only applicable in named prepared statement tracking mode, see `max_prepared_statements`. avg_xact_count : Average transactions per second in last stat period. avg_query_count : Average queries per second in last stat period. avg_server_assignment_count : Average number of times a server as assigned to a client per second in the last stat period. avg_recv : Average received (from clients) bytes per second. avg_sent : Average sent (to clients) bytes per second. avg_xact_time : Average transaction duration, in microseconds. avg_query_time : Average query duration, in microseconds. avg_wait_time : Time spent by clients waiting for a server, in microseconds (average of the wait times for clients assigned a backend during the current `stats_period`). avg_client_parse_count : Average number of prepared statements created by clients. Only applicable in named prepared statement tracking mode, see `max_prepared_statements`. avg_server_parse_count : Average number of prepared statements created by **pgbouncer** on a server. Only applicable in named prepared statement tracking mode, see `max_prepared_statements`. avg_bind_count : Average number of prepared statements readied for execution by clients and forwarded to PostgreSQL by **pgbouncer**. Only applicable in named prepared statement tracking mode, see `max_prepared_statements`. #### SHOW STATS_TOTALS Subset of **SHOW STATS** showing the total values (**total_**). #### SHOW STATS_AVERAGES Subset of **SHOW STATS** showing the average values (**avg_**). #### SHOW TOTALS Like **SHOW STATS** but aggregated across all databases. #### SHOW SERVERS type : S, for server. user : User name **pgbouncer** uses to connect to server. database : Database name. replication : If server connection uses replication. Can be **none**, **logical** or **physical**. state : State of the pgbouncer server connection, one of **active**, **idle**, **used**, **tested**, **new**, **active_cancel**, **being_canceled**. addr : IP address of PostgreSQL server. port : Port of PostgreSQL server. local_addr : Connection start address on local machine. local_port : Connection start port on local machine. connect_time : When the connection was made. request_time : When last request was issued. wait : Not used for server connections. wait_us : Not used for server connections. close_needed : 1 if the connection will be closed as soon as possible, because a configuration file reload or DNS update changed the connection information or **RECONNECT** was issued. ptr : Address of internal object for this connection. link : Address of client connection the server is paired with. remote_pid : PID of backend server process. In case connection is made over Unix socket and OS supports getting process ID info, its OS PID. Otherwise it's extracted from cancel packet the server sent, which should be the PID in case the server is PostgreSQL, but it's a random number in case the server it is another PgBouncer. tls : A string with TLS connection information, or empty if not using TLS. application_name : A string containing the `application_name` set on the linked client connection, or empty if this is not set, or if there is no linked connection. prepared_statements : The amount of prepared statements that are prepared on the server. This number is limited by the `max_prepared_statements` setting. id : Unique ID for server. #### SHOW CLIENTS type : C, for client. user : Client connected user. database : Database name. replication : If client connection uses replication. Can be **none**, **logical** or **physical**. state : State of the client connection, one of **active**, **waiting**, **active_cancel_req**, or **waiting_cancel_req**. addr : IP address of client. port : Source port of client. local_addr : Connection end address on local machine. local_port : Connection end port on local machine. connect_time : Timestamp of connect time. request_time : Timestamp of latest client request. wait : Current waiting time in seconds. wait_us : Microsecond part of the current waiting time. close_needed : not used for clients ptr : Address of internal object for this connection. link : Address of server connection the client is paired with. remote_pid : Process ID, in case client connects over Unix socket and OS supports getting it. tls : A string with TLS connection information, or empty if not using TLS. application_name : A string containing the `application_name` set by the client for this connection, or empty if this was not set. prepared_statements : The amount of prepared statements that the client has prepared id : Unique ID for client. #### SHOW POOLS A new pool entry is made for each couple of (database, user). database : Database name. user : User name. cl_active : Client connections that are either linked to server connections or are idle with no queries waiting to be processed. cl_waiting : Client connections that have sent queries but have not yet got a server connection. cl_active_cancel_req : Client connections that have forwarded query cancellations to the server and are waiting for the server response. cl_waiting_cancel_req : Client connections that have not forwarded query cancellations to the server yet. sv_active : Server connections that are linked to a client. sv_active_cancel : Server connections that are currently forwarding a cancel request. sv_being_canceled : Servers that normally could become idle but are waiting to do so until all in-flight cancel requests have completed that were sent to cancel a query on this server. sv_idle : Server connections that are unused and immediately usable for client queries. sv_used : Server connections that have been idle for more than `server_check_delay`, so they need `server_check_query` to run on them before they can be used again. sv_tested : Server connections that are currently running either `server_reset_query` or `server_check_query`. sv_login : Server connections currently in the process of logging in. maxwait : How long the first (oldest) client in the queue has waited, in seconds. If this starts increasing, then the current pool of servers does not handle requests quickly enough. The reason may be either an overloaded server or just too small of a **pool_size** setting. maxwait_us : Microsecond part of the maximum waiting time. pool_mode : The pooling mode in use. load_balance_hosts : The load_balance_hosts in use if the pool's host contains a comma-separated list. #### SHOW PEER_POOLS A new peer_pool entry is made for each configured peer. database : ID of the configured peer entry. cl_active_cancel_req : Client connections that have forwarded query cancellations to the server and are waiting for the server response. cl_waiting_cancel_req : Client connections that have not forwarded query cancellations to the server yet. sv_active_cancel : Server connections that are currently forwarding a cancel request. sv_login : Server connections currently in the process of logging in. #### SHOW LISTS Show following internal information, in columns (not rows): databases : Count of databases. users : Count of users. pools : Count of pools. free_clients : Count of free clients. These are clients that are disconnected, but PgBouncer keeps the memory around that was allocated for them so it can be reused for a future clients to avoid allocations. used_clients : Count of used clients. login_clients : Count of clients in **login** state. free_servers : Count of free servers. These are servers that are disconnected, but PgBouncer keeps the memory around that was allocated for them so it can be reused for a future servers to avoid allocations. used_servers : Count of used servers. dns_names : Count of DNS names in the cache. dns_zones : Count of DNS zones in the cache. dns_queries : Count of in-flight DNS queries. dns_pending : not used #### SHOW USERS name : The user name pool_size : The user's override pool_size. or NULL if not set. reserve_pool_size : The user's override reserve_pool_size. or NULL if not set. pool_mode : The user's override pool_mode, or NULL if not set. max_user_connections : The user's max_user_connections setting. If this setting is not set for this specific user, then the default value will be displayed. current_connections : Current number of server connections that this user has open to all servers. max_user_client_connections : The user's max_user_client_connections setting. If this setting is not set for this specific user, then the default value will be displayed. current_client_connections : Current number of client connections that this user has open to pgbouncer. #### SHOW DATABASES name : Name of configured database entry. host : Host pgbouncer connects to. port : Port pgbouncer connects to. database : Actual database name pgbouncer connects to. force_user : When the user is part of the connection string, the connection between pgbouncer and PostgreSQL is forced to the given user, whatever the client user. pool_size : Maximum number of server connections. min_pool_size : Minimum number of server connections. reserve_pool_size : Maximum number of additional connections for this database. server_lifetime : The maximum lifetime of a server connection for this database pool_mode : The database's override pool_mode, or NULL if the default will be used instead. load_balance_hosts : The database's load_balance_hosts if the host contains a comma-separated list. max_connections : Maximum number of allowed server connections for this database, as set by **max_db_connections**, either globally or per database. current_connections : Current number of server connections for this database. max_client_connections : Maximum number of allowed client connections for this pgbouncer instance, as set by max_db_client_connections per database. current_client_connections : Current number of client connections for this database. paused : 1 if this database is currently paused, else 0. disabled : 1 if this database is currently disabled, else 0. #### SHOW PEERS peer_id : ID of the configured peer entry. host : Host pgbouncer connects to. port : Port pgbouncer connects to. pool_size : Maximum number of server connections that can be made to this peer #### SHOW FDS Internal command - shows list of file descriptors in use with internal state attached to them. When the connected user has the user name "pgbouncer", connects through the Unix socket and has same the UID as the running process, the actual FDs are passed over the connection. This mechanism is used to do an online restart. Note: This does not work on Windows. This command also blocks the internal event loop, so it should not be used while PgBouncer is in use. fd : File descriptor numeric value. task : One of **pooler**, **client** or **server**. user : User of the connection using the FD. database : Database of the connection using the FD. addr : IP address of the connection using the FD, **unix** if a Unix socket is used. port : Port used by the connection using the FD. cancel : Cancel key for this connection. link : fd for corresponding server/client. NULL if idle. #### SHOW SOCKETS, SHOW ACTIVE_SOCKETS Shows low-level information about sockets or only active sockets. This includes the information shown under **SHOW CLIENTS** and **SHOW SERVERS** as well as other more low-level information. #### SHOW CONFIG Show the current configuration settings, one per row, with the following columns: key : Configuration variable name value : Configuration value default : Configuration default value changeable : Either **yes** or **no**, shows if the variable can be changed while running. If **no**, the variable can be changed only at boot time. Use **SET** to change a variable at run time. #### SHOW MEM Shows low-level information about the current sizes of various internal memory allocations. The information presented is subject to change. #### SHOW DNS_HOSTS Show host names in DNS cache. hostname : Host name. ttl : How many seconds until next lookup. addrs : Comma separated list of addresses. #### SHOW DNS_ZONES Show DNS zones in cache. zonename : Zone name. serial : Current serial. count : Host names belonging to this zone. #### SHOW VERSION Show the PgBouncer version string. #### SHOW STATE Show the PgBouncer state settings. Current states are active, paused and suspended. ### Process controlling commands #### PAUSE [db] PgBouncer tries to disconnect from all servers. Disconnecting each server connection waits for that server connection to be released according to the server pool's pooling mode (in transaction pooling mode, the transaction must complete, in statement mode, the statement must complete, and in session pooling mode the client must disconnect). The command will not return before all server connections have been disconnected. To be used at the time of database restart. If database name is given, only that database will be paused. New client connections to a paused database will wait until **RESUME** is called. #### DISABLE db Reject all new client connections on the given database. #### ENABLE db Allow new client connections after a previous **DISABLE** command. #### RECONNECT [db] Close each open server connection for the given database, or all databases, after it is released (according to the pooling mode), even if its lifetime is not up yet. New server connections can be made immediately and will connect as necessary according to the pool size settings. This command is useful when the server connection setup has changed, for example to perform a gradual switchover to a new server. It is *not* necessary to run this command when the connection string in pgbouncer.ini has been changed and reloaded (see **RELOAD**) or when DNS resolution has changed, because then the equivalent of this command will be run automatically. This command is only necessary if something downstream of PgBouncer routes the connections. After this command is run, there could be an extended period where some server connections go to an old destination and some server connections go to a new destination. This is likely only sensible when switching read-only traffic between read-only replicas, or when switching between nodes of a multimaster replication setup. If all connections need to be switched at the same time, **PAUSE** is recommended instead. To close server connections without waiting (for example, in emergency failover rather than gradual switchover scenarios), also consider **KILL**. #### KILL db Immediately drop all client and server connections on given database. New client connections to a killed database will wait until **RESUME** is called. #### KILL_CLIENT id Immediately kill specificed client connection along with any server connections for the given client. The client to kill, is identified by the `id` value that can be found using the `SHOW CLIENTS` command. An example command will look something like `KILL_CLIENT 1234`. #### SUSPEND All socket buffers are flushed and PgBouncer stops listening for data on them. The command will not return before all buffers are empty. To be used at the time of PgBouncer online reboot. New client connections to a suspended database will wait until **RESUME** is called. #### RESUME [db] Resume work from previous **KILL**, **PAUSE**, or **SUSPEND** command. #### SHUTDOWN The PgBouncer process will exit. #### SHUTDOWN WAIT_FOR_SERVERS Stop accepting new connections and shutdown after all servers are released. This is basically the same as issuing **PAUSE** and **SHUTDOWN**, except that this also stops accepting new connections while waiting for the **PAUSE** as well as eagerly disconnecting clients that are waiting to receive a server connection. #### SHUTDOWN WAIT_FOR_CLIENTS Stop accepting new connections and shutdown the process once all existing clients have disconnected. This command can be used to do zero-downtime rolling restart of two PgBouncer processes using the following procedure: 1. Have two or more PgBouncer processes running on the same port using `so_reuseport` ([configuring peering](/config.html#section-peers) is recommended, but not required). To achieve zero downtime when restarting we'll restart these processes one-by-one, thus leaving the others running to accept connections while one is being restarted. 2. Pick a process to restart first, let's call it A. 3. Run `SHUTDOWN WAIT_FOR_CLIENTS` (or send `SIGTERM`) to process A. 4. Cause all clients to reconnect. Possibly by waiting some time until the client side pooler causes reconnects due to its `server_idle_timeout` (or similar config). Or if no client side pooler is used, possibly by restarting the clients. Once all clients have reconnected. Process A will exit automatically, because no clients are connected to it anymore. 5. Start process A again. 6. Repeat step 3, 4 and 5 for each of the remaining processes, one-by-one until you restarted all processes. #### RELOAD The PgBouncer process will reload its configuration files and update changeable settings. This includes the main configuration file as well as the files specified by the settings `auth_file` and `auth_hba_file`. PgBouncer notices when a configuration file reload changes the connection parameters of a database definition. An existing server connection to the old destination will be closed when the server connection is next released (according to the pooling mode), and new server connections will immediately use the updated connection parameters. #### WAIT_CLOSE [db] Wait until all server connections, either of the specified database or of all databases, have cleared the "close_needed" state (see **SHOW SERVERS**). This can be called after a **RECONNECT** or **RELOAD** to wait until the respective configuration change has been fully activated, for example in switchover scripts. ### Other commands #### SET key = arg Changes a configuration setting (see also **SHOW CONFIG**). For example: SET log_connections = 1; SET server_check_query = 'select 2'; (Note that this command is run on the PgBouncer admin console and sets PgBouncer settings. A **SET** command run on another database will be passed to the PostgreSQL backend like any other SQL command.) ### Signals SIGHUP : Reload config. Same as issuing the command **RELOAD** on the console. SIGTERM : Super safe shutdown. Wait for all existing clients to disconnect, but don't accept new connections. This is the same as issuing **SHUTDOWN WAIT_FOR_CLIENTS** on the console. If this signal is received while there is already a shutdown in progress, then an "immediate shutdown" is triggered instead of a "super safe shutdown". In PgBouncer versions earlier than 1.23.0, this signal would cause an "immediate shutdown". SIGINT : Safe shutdown. Same as issuing **SHUTDOWN WAIT_FOR_SERVERS** on the console. If this signal is received while there is already a shutdown in progress, then an "immediate shutdown" is triggered instead of a "safe shutdown". SIGQUIT : Immediate shutdown. Same as issuing **SHUTDOWN** on the console. SIGUSR1 : Same as issuing **PAUSE** on the console. SIGUSR2 : Same as issuing **RESUME** on the console. ### Libevent settings From the Libevent documentation: > It is possible to disable support for epoll, kqueue, devpoll, poll > or select by setting the environment variable EVENT_NOEPOLL, > EVENT_NOKQUEUE, EVENT_NODEVPOLL, EVENT_NOPOLL or EVENT_NOSELECT, > respectively. > > By setting the environment variable EVENT_SHOW_METHOD, libevent > displays the kernel notification method that it uses. ## See also pgbouncer(5) - man page of configuration settings descriptions pgbouncer-1.24.1/doc/frag-config-man.md0000644000175000000000000000015414777762222014531 00000000000000% PGBOUNCER.INI(5) @PACKAGE_VERSION@ | Databases ## Name pgbouncer.ini - configuration file for pgbouncer pgbouncer-1.24.1/doc/filter.py0000644000175000000000000000104514777762222013113 00000000000000#!/usr/bin/env python3 import fileinput import os import sys for line in fileinput.input(): # substitute package version if line.startswith("% "): line = line.replace("@PACKAGE_VERSION@", os.environ["PACKAGE_VERSION"]) # drop level-1 header if line.startswith("# "): continue # decrease level of all headers by 1 if line.startswith("##"): line = line.replace("#", "", 1) # convert level-1 headers to uppercase if line.startswith("# "): line = line.upper() sys.stdout.write(line) pgbouncer-1.24.1/doc/frag-usage-man.md0000644000175000000000000000016014777762222014365 00000000000000% PGBOUNCER(1) @PACKAGE_VERSION@ | Databases ## Name pgbouncer - lightweight connection pooler for PostgreSQL pgbouncer-1.24.1/doc/config.md0000644000175000000000000016106714777762222013056 00000000000000# pgbouncer.ini ## Description The configuration file is in "ini" format. Section names are between "[" and "]". Lines starting with ";" or "#" are taken as comments and ignored. The characters ";" and "#" are not recognized as special when they appear later in the line. ## Generic settings ### logfile Specifies the log file. For daemonization (`-d`), either this or `syslog` need to be set. The log file is kept open, so after rotation, `kill -HUP` or on console `RELOAD;` should be done. On Windows, the service must be stopped and started. Note that setting `logfile` does not by itself turn off logging to stderr. Use the command-line option `-q` or `-d` for that. Default: not set ### pidfile Specifies the PID file. Without `pidfile` set, daemonization (`-d`) is not allowed. Default: not set ### listen_addr Specifies a list (comma-separated) of addresses where to listen for TCP connections. You may also use `*` meaning "listen on all addresses". When not set, only Unix socket connections are accepted. Addresses can be specified numerically (IPv4/IPv6) or by name. Default: not set ### listen_port Which port to listen on. Applies to both TCP and Unix sockets. Default: 6432 ### unix_socket_dir Specifies the location for Unix sockets. Applies to both the listening socket and to server connections. If set to an empty string, Unix sockets are disabled. A value that starts with `@` specifies that a Unix socket in the abstract namespace should be created (currently supported on Linux and Windows). For online reboot (`-R`) to work, a Unix socket needs to be configured, and it needs to be in the file-system namespace. Default: `/tmp` (empty on Windows) ### unix_socket_mode File system mode for Unix socket. Ignored for sockets in the abstract namespace. Not supported on Windows. Default: 0777 ### unix_socket_group Group name to use for Unix socket. Ignored for sockets in the abstract namespace. Not supported on Windows. Default: not set ### user If set, specifies the Unix user to change to after startup. Works only if PgBouncer is started as root or if it's already running as the given user. Not supported on Windows. Default: not set ### pool_mode Specifies when a server connection can be reused by other clients. session : Server is released back to pool after client disconnects. Default. transaction : Server is released back to pool after transaction finishes. statement : Server is released back to pool after query finishes. Transactions spanning multiple statements are disallowed in this mode. ### max_client_conn Maximum number of client connections allowed. When this setting is increased, then the file descriptor limits in the operating system might also have to be increased. Note that the number of file descriptors potentially used is more than `max_client_conn`. If each user connects under its own user name to the server, the theoretical maximum used is: max_client_conn + (max pool_size * total databases * total users) If a database user is specified in the connection string (all users connect under the same user name), the theoretical maximum is: max_client_conn + (max pool_size * total databases) The theoretical maximum should never be reached, unless somebody deliberately crafts a special load for it. Still, it means you should set the number of file descriptors to a safely high number. Search for `ulimit` in your favorite shell man page. Note: `ulimit` does not apply in a Windows environment. Default: 100 ### default_pool_size How many server connections to allow per user/database pair. Can be overridden in the per-database configuration. Default: 20 ### min_pool_size Add more server connections to pool if below this number. Improves behavior when the normal load suddenly comes back after a period of total inactivity. The value is effectively capped at the pool size. Only enforced for pools where at least one of the following is true: * the entry in the `[database]` section for the pool has a value set for the `user` key (aka forced user) * there is at least one client connected to the pool Default: 0 (disabled) ### reserve_pool_size How many additional connections to allow to a pool (see `reserve_pool_timeout`). 0 disables. Default: 0 (disabled) ### reserve_pool_timeout If a client has not been serviced in this time, use additional connections from the reserve pool. 0 disables. [seconds] Default: 5.0 ### max_db_connections Do not allow more than this many server connections per database (regardless of user). This considers the PgBouncer database that the client has connected to, not the PostgreSQL database of the outgoing connection. This can also be set per database in the `[databases]` section. Note that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. Default: 0 (unlimited) ### max_db_client_connections Do not allow more than this many client connections to PgBouncer per database (regardless of user). This considers the PgBouncer database that the client has connected to, not the PostgreSQL database of the outgoing connection. This should be set at a number greater than or equal to max_db_connections. The difference between the two numbers can be thought of as how many connections to a given database can be in the queue while waiting for active connections to finish. This can also be set per database in the `[databases]` section. Default: 0 (unlimited) ### max_user_connections Do not allow more than this many server connections per user (regardless of database). This considers the PgBouncer user that is associated with a pool, which is either the user specified for the server connection or in absence of that the user the client has connected as. This can also be set per user in the `[users]` section. Note that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. Default: 0 (unlimited) ### max_user_client_connections Do not allow more than this many client connections per user (regardless of database). This value should be set to a number higher than max_user_connections. This difference between max_user_connections and max_user_client_connections can be conceptualized as the number the max size of the queue for the user. This can also be set per user in the `[users]` section. Default: 0 (unlimited) ### server_round_robin By default, PgBouncer reuses server connections in LIFO (last-in, first-out) manner, so that few connections get the most load. This gives best performance if you have a single server serving a database. But if there is a round-robin system behind a database address (TCP, DNS, or host list), then it is better if PgBouncer also uses connections in that manner, thus achieving uniform load. Default: 0 ### track_extra_parameters By default, PgBouncer tracks `client_encoding`, `datestyle`, `timezone`, `standard_conforming_strings` and `application_name` parameters per client. To allow other parameters to be tracked, they can be specified here, so that PgBouncer knows that they should be maintained in the client variable cache and restored in the server whenever the client becomes active. If you need to specify multiple values, use a comma-separated list (e.g. `default_transaction_read_only, IntervalStyle`) Note: Most parameters cannot be tracked this way. The only parameters that can be tracked are ones that Postgres reports to the client. Postgres has [an official list of parameters that it reports to the client](https://www.postgresql.org/docs/15/protocol-flow.html#PROTOCOL-ASYNC). Postgres extensions can change this list though, they can add parameters themselves that they also report, and they can start reporting already existing parameters that Postgres does not report. Notably Citus 12.0+ causes Postgres to also report `search_path`. The Postgres protocol allows specifying parameters settings, both directly as a parameter in the startup packet, or inside the [`options` startup packet][options-startup]. Parameters specified using both of these methods are supported by `track_extra_parameters`. However, it's not possible to include `options` itself in `track_extra_parameters`, only the parameters contained in `options`. Default: IntervalStyle ### ignore_startup_parameters By default, PgBouncer allows only parameters it can keep track of in startup packets: `client_encoding`, `datestyle`, `timezone` and `standard_conforming_strings`. All others parameters will raise an error. To allow others parameters, they can be specified here, so that PgBouncer knows that they are handled by the admin and it can ignore them. If you need to specify multiple values, use a comma-separated list (e.g. `options,extra_float_digits`) The Postgres protocol allows specifying parameters settings, both directly as a parameter in the startup packet, or inside the [`options` startup packet][options-startup]. Parameters specified using both of these methods are supported by `ignore_startup_parameters`. It's even possible to include `options` itself in `track_extra_parameters`, which results in any unknown parameters contained inside `options` to be ignored. [options-startup]: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS Default: empty ### peer_id The peer id used to identify this PgBouncer process in a group of PgBouncer processes that are peered together. The `peer_id` value should be unique within a group of peered PgBouncer processes. When set to 0 pgbouncer peering is disabled. See the docs for the `[peers]` section for more information. The maximum value that can be used for the `peer_id` is 16383. Default: 0 ### disable_pqexec Disable the Simple Query protocol (PQexec). Unlike the Extended Query protocol, Simple Query allows multiple queries in one packet, which allows some classes of SQL-injection attacks. Disabling it can improve security. Obviously, this means only clients that exclusively use the Extended Query protocol will stay working. Default: 0 ### application_name_add_host Add the client host address and port to the application name setting set on connection start. This helps in identifying the source of bad queries etc. This logic applies only at the start of a connection. If `application_name` is later changed with `SET`, PgBouncer does not change it again. Default: 0 ### conffile Show location of current config file. Changing it will make PgBouncer use another config file for next `RELOAD` / `SIGHUP`. Default: file from command line ### service_name Used on win32 service registration. Default: `pgbouncer` ### job_name Alias for `service_name`. ### stats_period Sets how often the averages shown in various `SHOW` commands are updated and how often aggregated statistics are written to the log (but see `log_stats`). [seconds] Default: 60 ### max_prepared_statements When this is set to a non-zero value PgBouncer tracks protocol-level named prepared statements related commands sent by the client in transaction and statement pooling mode. PgBouncer makes sure that any statement prepared by a client is available on the backing server connection. Even when the statement was originally prepared on another server connection. PgBouncer internally examines all the queries that are sent by clients as a prepared statement, and gives each unique query string an internal name with the format `PGBOUNCER_{unique_id}`. If the same query string is prepared multiple times (possibly by different clients), then these queries share the same internal name. PgBouncer only prepares the statement on the actual PostgreSQL server using the internal name (so not the name provided by the client). PgBouncer keeps track of the name that the client gave to each prepared statement. It then rewrites each command that uses a prepared statement to by replacing the client side name with the the internal name (e.g. replacing `my_prepared_statement` with `PGBOUNCER_123`) before forwarding that command to the server. More importantly, if the prepared statement that the client wants to execute is not yet prepared on the server (e.g. because a different server is now assigned to the client than when the client prepared the statement), then PgBouncer transparently prepares the statement before executing it. Note: This tracking and rewriting of prepared statement commands does not work for SQL-level prepared statement commands, so `PREPARE`, `EXECUTE` and `DEALLOCATE` are forwarded straight to Postgres. The exception to this rule are the `DEALLOCATE ALL` and `DISCARD ALL` commands, these do work as expected and will clear the prepared statements that PgBouncer tracked for the client that sends this command. The actual value of this setting controls the number of prepared statements kept active in an LRU cache on a single server connection. When the setting is set to 0 prepared statement support for transaction and statement pooling is disabled. To get the best performance you should try to make sure that this setting is larger than the amount of commonly used prepared statements in your application. Keep in mind that the higher this value, the larger the memory footprint of each PgBouncer connection will be on your PostgreSQL server, because it will keep more queries prepared on those connections. It also increases the memory footprint of PgBouncer itself, because it now needs to keep track of query strings. The impact on PgBouncer memory usage is not that big though: - Each unique query is stored once in a global query cache. - Each client connection keeps a buffer that it uses to rewrite packets. This is, at most, 4 times the size of `pkt_buf`. This limit is often not reached though, it only happens when the queries in your prepared statements are between 2 and 4 times the size of `pkt_buf`. So if you consider the following as an example scenario: - There are 1000 active clients - The clients prepare 200 unique queries - The average size of a query is 5kB - `pkt_buf` parameter is set to the default of 4096 (4kB) Then, PgBouncer needs at most the following amount of memory to handle these prepared statements: 200 x 5kB + 1000 x 4 x 4kB = ~17MB of memory. Tracking prepared statements does not only come with a memory cost, but also with increased CPU usage, because PgBouncer needs to inspect and rewrite the queries. Multiple PgBouncer instances can listen on the same port to use more than one core for processing, see [the documentation for the `so_reuseport` option](/config.html#so_reuseport) for details. But of course there are also performance benefits to prepared statements. Just as when connecting to PostgreSQL directly, by preparing a query that is executed many times, it reduces the total amount of parsing and planning that needs to be done. The way that PgBouncer tracks prepared statements is especially beneficial to performance when multiple clients prepare the same queries. Because client connections automatically reuse a prepared statement on a server connection, even if it was prepared by another client. As an example, if you have a `pool_size` of 20 and you have 100 clients that all prepare the exact same query, then the query is prepared (and thus parsed) only 20 times on the PostgreSQL server. The reuse of prepared statements has one downside. If the return or argument types of a prepared statement changes across executions then PostgreSQL currently throws an error such as: ``` ERROR: cached plan must not change result type ``` You can avoid such errors by not having multiple clients that use the exact same query string in a prepared statement, but expecting different argument or result types. One of the most common ways of running into this issue is during a DDL migration where you add a new column or change a column type on an existing table. In those cases you can run `RECONNECT` on the PgBouncer admin console after doing the migration to force a re-prepare of the query and make the error go away. Default: 200 ## Authentication settings PgBouncer handles its own client authentication and has its own database of users. These settings control this. ### auth_type How to authenticate users. cert : Client must connect over TLS connection with a valid client certificate. The user name is then taken from the CommonName field from the certificate. md5 : Use MD5-based password check. This is the default authentication method. `auth_file` may contain both MD5-encrypted and plain-text passwords. If `md5` is configured and a user has a SCRAM secret, then SCRAM authentication is used automatically instead. scram-sha-256 : Use password check with SCRAM-SHA-256. `auth_file` has to contain SCRAM secrets or plain-text passwords. plain : The clear-text password is sent over the wire. Deprecated. trust : No authentication is done. The user name must still exist in `auth_file`. any : Like the `trust` method, but the user name given is ignored. Requires that all databases are configured to log in as a specific user. Additionally, the console database allows any user to log in as admin. hba : The actual authentication type is loaded from `auth_hba_file`. This allows different authentication methods for different access paths, for example: connections over Unix socket use the `peer` auth method, connections over TCP must use TLS. pam : PAM is used to authenticate users, `auth_file` is ignored. This method is not compatible with databases using the `auth_user` option. The service name reported to PAM is "pgbouncer". `pam` is not supported in the HBA configuration file. ### auth_hba_file HBA configuration file to use when `auth_type` is `hba`. See section [HBA file format](#hba-file-format) below about details. Default: not set ### auth_ident_file Identity map file to use when `auth_type` is `hba` and a user map will be defined. See section [Ident map file format](#ident-map-file-format) below about details. Default: not set ### auth_file The name of the file to load user names and passwords from. See section [Authentication file format](#authentication-file-format) below about details. Most authentication types (see above) require that either `auth_file` or `auth_user` be set; otherwise there would be no users defined. Default: not set ### auth_user If `auth_user` is set, then any user not specified in `auth_file` will be queried through the `auth_query` query from `pg_authid` in the database, using `auth_user`. The password of `auth_user` will be taken from `auth_file`. (If the `auth_user` does not require a password then it does not need to be defined in `auth_file`.) Direct access to `pg_authid` requires admin rights. It's preferable to use a non-superuser that calls a SECURITY DEFINER function instead. Default: not set ### auth_query Query to load user's password from database. Direct access to `pg_authid` requires admin rights. It's preferable to use a non-superuser that calls a SECURITY DEFINER function instead. Note that the query is run inside the target database. So if a function is used, it needs to be installed into each database. Default: `SELECT rolname, CASE WHEN rolvaliduntil < now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolname=$1 AND rolcanlogin` ### auth_dbname Database name in the `[database]` section to be used for authentication purposes. This option can be either global or overridden in the connection string if this parameter is specified. ## Log settings ### syslog Toggles syslog on/off. On Windows, the event log is used instead. Default: 0 ### syslog_ident Under what name to send logs to syslog. Default: `pgbouncer` (program name) ### syslog_facility Under what facility to send logs to syslog. Possibilities: `auth`, `authpriv`, `daemon`, `user`, `local0-7`. Default: `daemon` ### log_connections Log successful logins. Default: 1 ### log_disconnections Log disconnections with reasons. Default: 1 ### log_pooler_errors Log error messages the pooler sends to clients. Default: 1 ### log_stats Write aggregated statistics into the log, every `stats_period`. This can be disabled if external monitoring tools are used to grab the same data from `SHOW` commands. Default: 1 ### verbose Increase verbosity. Mirrors the "-v" switch on the command line. For example, using "-v -v" on the command line is the same as `verbose=2`. 3 is the highest currently-supported verbosity. Default: 0 ## Console access control ### admin_users Comma-separated list of database users that are allowed to connect and run all commands on the console. Ignored when `auth_type` is `any`, in which case any user name is allowed in as admin. Default: empty ### stats_users Comma-separated list of database users that are allowed to connect and run read-only queries on the console. That means all `SHOW` commands except `SHOW FDS`. Default: empty ## Connection sanity checks, timeouts ### server_reset_query Query sent to server on connection release, before making it available to other clients. At that moment no transaction is in progress, so the value should not include `ABORT` or `ROLLBACK`. The query is supposed to clean any changes made to the database session so that the next client gets the connection in a well-defined state. The default is `DISCARD ALL`, which cleans everything, but that leaves the next client no pre-cached state. It can be made lighter, e.g. `DEALLOCATE ALL` to just drop prepared statements, if the application does not break when some state is kept around. When transaction pooling is used, the `server_reset_query` is not used, because in that mode, clients must not use any session-based features, since each transaction ends up in a different connection and thus gets a different session state. Default: `DISCARD ALL` ### server_reset_query_always Whether `server_reset_query` should be run in all pooling modes. When this setting is off (default), the `server_reset_query` will be run only in pools that are in sessions-pooling mode. Connections in transaction-pooling mode should not have any need for a reset query. This setting is for working around broken setups that run applications that use session features over a transaction-pooled PgBouncer. It changes non-deterministic breakage to deterministic breakage: Clients always lose their state after each transaction. Default: 0 ### server_check_delay How long to keep released connections available for immediate re-use, without running `server_check_query` on it. If 0 then the check is always run. Default: 30.0 ### server_check_query Simple do-nothing query to check if the server connection is alive. If an empty string, then sanity checking is disabled. Default: `select 1` ### server_fast_close Disconnect a server in session pooling mode immediately or after the end of the current transaction if it is in "close_needed" mode (set by `RECONNECT`, `RELOAD` that changes connection settings, or DNS change), rather than waiting for the session end. In statement or transaction pooling mode, this has no effect since that is the default behavior there. If because of this setting a server connection is closed before the end of the client session, the client connection is also closed. This ensures that the client notices that the session has been interrupted. This setting makes connection configuration changes take effect sooner if session pooling and long-running sessions are used. The downside is that client sessions are liable to be interrupted by a configuration change, so client applications will need logic to reconnect and reestablish session state. But note that no transactions will be lost, because running transactions are not interrupted, only idle sessions. Default: 0 ### server_lifetime The pooler will close an unused (not currently linked to any client connection) server connection that has been connected longer than this. Setting it to 0 means the connection is to be used only once, then closed. [seconds] This can also be set per database in the `[databases]` section. Default: 3600.0 ### server_idle_timeout If a server connection has been idle more than this many seconds it will be closed. If 0 then this timeout is disabled. [seconds] Default: 600.0 ### server_connect_timeout If connection and login don't finish in this amount of time, the connection will be closed. [seconds] Default: 15.0 ### server_login_retry If login to the server failed, because of failure to connect or from authentication, the pooler waits this much before retrying to connect. During the waiting interval, new clients trying to connect to the failing server will get an error immediately without another connection attempt. [seconds] The purpose of this behavior is that clients don't unnecessarily queue up waiting for a server connection to become available if the server is not working. However, it also means that if a server is momentarily failing, for example during a restart or if the configuration was erroneous, then it will take at least this long until the pooler will consider connecting to it again. Planned events such as restarts should normally be managed using the `PAUSE` command to avoid this. Default: 15.0 ### client_login_timeout If a client connects but does not manage to log in in this amount of time, it will be disconnected. Mainly needed to avoid dead connections stalling `SUSPEND` and thus online restart. [seconds] Default: 60.0 ### autodb_idle_timeout If the automatically created (via "*") database pools have been unused this many seconds, they are freed. The negative aspect of that is that their statistics are also forgotten. [seconds] Default: 3600.0 ### dns_max_ttl How long DNS lookups can be cached. The actual DNS TTL is ignored. [seconds] Default: 15.0 ### dns_nxdomain_ttl How long DNS errors and NXDOMAIN DNS lookups can be cached. [seconds] Default: 15.0 ### dns_zone_check_period Period to check if a zone serial has changed. PgBouncer can collect DNS zones from host names (everything after first dot) and then periodically check if the zone serial changes. If it notices changes, all host names under that zone are looked up again. If any host IP changes, its connections are invalidated. Works only with c-ares backend (`configure` option `--with-cares`). Default: 0.0 (disabled) ### resolv_conf The location of a custom `resolv.conf` file. This is to allow specifying custom DNS servers and perhaps other name resolution options, independent of the global operating system configuration. Requires evdns (>= 2.0.3) or c-ares (>= 1.15.0) backend. The parsing of the file is done by the DNS backend library, not PgBouncer, so see the library's documentation for details on allowed syntax and directives. Default: empty (use operating system defaults) ## TLS settings If the contents of any of the cert or key files are changed without changing the actual setting filename in the config, the new file contents will be used for new connections after a RELOAD. Existing connections won't be closed though. If it's necessary for security reasons that all connections start using the new files ASAP, it's advised to run RECONNECT after the RELOAD. Changing any TLS settings will trigger a RECONNECT automatically for security reasons. ### client_tls_sslmode TLS mode to use for connections from clients. TLS connections are disabled by default. When enabled, `client_tls_key_file` and `client_tls_cert_file` must be also configured to set up the key and certificate PgBouncer uses to accept client connections. The most common certificate file format usable by PgBouncer is pem. disable : Plain TCP. If client requests TLS, it's ignored. Default. allow : If client requests TLS, it is used. If not, plain TCP is used. If the client presents a client certificate, it is not validated. prefer : Same as `allow`. require : Client must use TLS. If not, the client connection is rejected. If the client presents a client certificate, it is not validated. verify-ca : Client must use TLS with valid client certificate. verify-full : Same as `verify-ca`. ### client_tls_key_file Private key for PgBouncer to accept client connections. Default: not set ### client_tls_cert_file Certificate for private key. Clients can validate it. Default: not set ### client_tls_ca_file Root certificate file to validate client certificates. Default: not set ### client_tls_protocols Which TLS protocol versions are allowed. Allowed values: `tlsv1.0`, `tlsv1.1`, `tlsv1.2`, `tlsv1.3`. Shortcuts: `all` (tlsv1.0,tlsv1.1,tlsv1.2,tlsv1.3), `secure` (tlsv1.2,tlsv1.3). Default: `secure` ### client_tls_ciphers Allowed TLS ciphers, in OpenSSL syntax. Shortcuts: - `default`/`secure`/`fast`/`normal` (these all use system wide OpenSSL defaults) - `all` (enables all ciphers, not recommended) Only connections using TLS version 1.2 and lower are affected. There is currently no setting that controls the cipher choices used by TLS version 1.3 connections. Default: `default` ### client_tls_ecdhcurve Elliptic Curve name to use for ECDH key exchanges. Allowed values: `none` (DH is disabled), `auto` (256-bit ECDH), curve name Default: `auto` ### client_tls_dheparams DHE key exchange type. Allowed values: `none` (DH is disabled), `auto` (2048-bit DH), `legacy` (1024-bit DH) Default: `auto` ### server_tls_sslmode TLS mode to use for connections to PostgreSQL servers. The default mode is `prefer`. disable : Plain TCP. TLS is not even requested from the server. allow : FIXME: if server rejects plain, try TLS? prefer : TLS connection is always requested first from PostgreSQL. If refused, the connection will be established over plain TCP. Server certificate is not validated. Default require : Connection must go over TLS. If server rejects it, plain TCP is not attempted. Server certificate is not validated. verify-ca : Connection must go over TLS and server certificate must be valid according to `server_tls_ca_file`. Server host name is not checked against certificate. verify-full : Connection must go over TLS and server certificate must be valid according to `server_tls_ca_file`. Server host name must match certificate information. ### server_tls_ca_file Root certificate file to validate PostgreSQL server certificates. Default: not set ### server_tls_key_file Private key for PgBouncer to authenticate against PostgreSQL server. Default: not set ### server_tls_cert_file Certificate for private key. PostgreSQL server can validate it. Default: not set ### server_tls_protocols Which TLS protocol versions are allowed. Allowed values: `tlsv1.0`, `tlsv1.1`, `tlsv1.2`, `tlsv1.3`. Shortcuts: `all` (tlsv1.0,tlsv1.1,tlsv1.2,tlsv1.3), `secure` (tlsv1.2,tlsv1.3), `legacy` (all). Default: `secure` ### server_tls_ciphers Allowed TLS ciphers, in OpenSSL syntax. Shortcuts: - `default`/`secure`/`fast`/`normal` (these all use system wide OpenSSL defaults) - `all` (enables all ciphers, not recommended) Only connections using TLS version 1.2 and lower are affected. There is currently no setting that controls the cipher choices used by TLS version 1.3 connections. Default: `default` ## Dangerous timeouts Setting the following timeouts can cause unexpected errors. ### query_timeout Queries running longer than that are canceled. This should be used only with a slightly smaller server-side `statement_timeout`, to apply only for network problems. [seconds] Default: 0.0 (disabled) ### query_wait_timeout Maximum time queries are allowed to spend waiting for execution. If the query is not assigned to a server during that time, the client is disconnected. 0 disables. If this is disabled, clients will be queued indefinitely. [seconds] This setting is used to prevent unresponsive servers from grabbing up connections. It also helps when the server is down or rejects connections for any reason. Default: 120.0 ### cancel_wait_timeout Maximum time cancellation requests are allowed to spend waiting for execution. If the cancel request is not assigned to a server during that time, the client is disconnected. 0 disables. If this is disabled, cancel requests will be queued indefinitely. [seconds] This setting is used to prevent a client locking up when a cancel cannot be forwarded due to the server being down. Default: 10.0 ### client_idle_timeout Client connections idling longer than this many seconds are closed. This should be larger than the client-side connection lifetime settings, and only used for network problems. [seconds] Default: 0.0 (disabled) ### idle_transaction_timeout If a client has been in "idle in transaction" state longer, it will be disconnected. [seconds] Default: 0.0 (disabled) ### suspend_timeout How long to wait for buffer flush during `SUSPEND` or reboot (`-R`). A connection is dropped if the flush does not succeed. [seconds] Default: 10 ## Low-level network settings ### pkt_buf Internal buffer size for packets. Affects size of TCP packets sent and general memory usage. Actual libpq packets can be larger than this, so no need to set it large. Default: 4096 ### max_packet_size Maximum size for PostgreSQL packets that PgBouncer allows through. One packet is either one query or one result set row. The full result set can be larger. Default: 2147483647 ### listen_backlog Backlog argument for listen(2). Determines how many new unanswered connection attempts are kept in the queue. When the queue is full, further new connections are dropped. Default: 128 ### sbuf_loopcnt How many times to process data on one connection, before proceeding. Without this limit, one connection with a big result set can stall PgBouncer for a long time. One loop processes one `pkt_buf` amount of data. 0 means no limit. Default: 5 ### so_reuseport Specifies whether to set the socket option `SO_REUSEPORT` on TCP listening sockets. On some operating systems, this allows running multiple PgBouncer instances on the same host listening on the same port and having the kernel distribute the connections automatically. This option is a way to get PgBouncer to use more CPU cores. (PgBouncer is single-threaded and uses one CPU core per instance.) The behavior in detail depends on the operating system kernel. As of this writing, this setting has the desired effect on (sufficiently recent versions of) Linux, DragonFlyBSD, and FreeBSD. (On FreeBSD, it applies the socket option `SO_REUSEPORT_LB` instead.) Some other operating systems support the socket option but it won't have the desired effect: It will allow multiple processes to bind to the same port but only one of them will get the connections. See your operating system's setsockopt() documentation for details. On systems that don't support the socket option at all, turning this setting on will result in an error. Each PgBouncer instance on the same host needs different settings for at least `unix_socket_dir` and `pidfile`, as well as `logfile` if that is used. Also note that if you make use of this option, you can no longer connect to a specific PgBouncer instance via TCP/IP, which might have implications for monitoring and metrics collection. To make sure query cancellations keep working, you should set up PgBouncer peering between the different PgBouncer processes. For details look at docs for the `peer_id` configuration option and the `peers` configuration section. There's also an example that uses peering and so_reuseport in the example section of these docs. Default: 0 ### tcp_defer_accept Sets the `TCP_DEFER_ACCEPT` socket option; see `man 7 tcp` for details. (This is a Boolean option: 1 means enabled. The actual value set if enabled is currently hardcoded to 45 seconds.) This is currently only supported on Linux. Default: 1 on Linux, otherwise 0 ### tcp_socket_buffer Default: not set ### tcp_keepalive Turns on basic keepalive with OS defaults. On Linux, the system defaults are tcp_keepidle=7200, tcp_keepintvl=75, tcp_keepcnt=9. They are probably similar on other operating systems. Default: 1 ### tcp_keepcnt Default: not set ### tcp_keepidle Default: not set ### tcp_keepintvl Default: not set ### tcp_user_timeout Sets the `TCP_USER_TIMEOUT` socket option. This specifies the maximum amount of time in milliseconds that transmitted data may remain unacknowledged before the TCP connection is forcibly closed. If set to 0, then operating system's default is used. This is currently only supported on Linux. Default: 0 ## Section [databases] The section `[databases]` defines the names of the databases that clients of PgBouncer can connect to and specifies where those connections will be routed. The section contains key=value lines like dbname = connection string where the key will be taken as a database name and the value as a connection string, consisting of key=value pairs of connection parameters, described below (similar to libpq, but the actual libpq is not used and the set of available features is different). Example: foodb = host=host1.example.com port=5432 bardb = host=localhost dbname=bazdb The database name can contain characters `_0-9A-Za-z` without quoting. Names that contain other characters need to be quoted with standard SQL identifier quoting: double quotes, with "" for a single instance of a double quote. The database name "pgbouncer" is reserved for the admin console and cannot be used as a key here. "*" acts as a fallback database: If the exact name does not exist, its value is taken as connection string for the requested database. For example, if there is an entry (and no other overriding entries) * = host=foo then a connection to PgBouncer specifying a database "bar" will effectively behave as if an entry bar = host=foo dbname=bar exists (taking advantage of the default for `dbname` being the client-side database name; see below). Such automatically created database entries are cleaned up if they stay idle longer than the time specified by the `autodb_idle_timeout` parameter. ### dbname Destination database name. Default: same as client-side database name ### host Host name or IP address to connect to. Host names are resolved at connection time, the result is cached per `dns_max_ttl` parameter. When a host name's resolution changes, existing server connections are automatically closed when they are released (according to the pooling mode), and new server connections immediately use the new resolution. If DNS returns several results, they are used in a round-robin manner. If the value begins with `/`, then a Unix socket in the file-system namespace is used. If the value begins with `@`, then a Unix socket in the abstract namespace is used. A comma-separated list of host names or addresses can be specified. In that case, connections are made in a round-robin manner. (If a host list contains host names that in turn resolve via DNS to multiple addresses, the round-robin systems operate independently. This is an implementation dependency that is subject to change.) Note that in a list, all hosts must be available at all times: There are no mechanisms to skip unreachable hosts or to select only available hosts from a list or similar. (This is different from what a host list in libpq means.) Also note that this only affects how the destinations of new connections are chosen. See also the setting `server_round_robin` for how clients are assigned to already established server connections. Examples: host=localhost host=127.0.0.1 host=2001:0db8:85a3:0000:0000:8a2e:0370:7334 host=/var/run/postgresql host=192.168.0.1,192.168.0.2,192.168.0.3 Default: not set, meaning to use a Unix socket ### port Default: 5432 ### user If `user=` is set, all connections to the destination database will be done with the specified user, meaning that there will be only one pool for this database. Otherwise, PgBouncer logs into the destination database with the client user name, meaning that there will be one pool per user. ### password If no password is specified here, the password from the `auth_file` will be used for the user specified above. Dynamic forms of password discovery such as `auth_query` are not currently supported. ### auth_user Override of the global `auth_user` setting, if specified. ### auth_query Override of the global `auth_query` setting, if specified. The entire SQL statement needs to be enclosed in single quotes. ### auth_dbname Override of the global `auth_dbname` setting, if specified. ### pool_size Set the maximum size of pools for this database. If not set, the `default_pool_size` is used. ### min_pool_size Set the minimum pool size for this database. If not set, the global `min_pool_size` is used. Only enforced if at least one of the following is true: * this entry in the `[database]` section has a value set for the `user` key (aka forced user) * there is at least one client connected to the pool ### reserve_pool_size Set additional connections for this database. If not set, the global `reserve_pool_size` is used. For backwards compatibilty reasons `reserve_pool` is an alias for this option. ### connect_query Query to be executed after a connection is established, but before allowing the connection to be used by any clients. If the query raises errors, they are logged but ignored otherwise. ### pool_mode Set the pool mode specific to this database. If not set, the default `pool_mode` is used. ### load_balance_hosts When a comma-separated list is specified in `host`, `load_balance_hosts` controls which entry is chosen for a new connection. Note: This setting currently only controls the load balancing behaviour when providing multiple hosts in the connection string, but not when a single host its DNS record references multiple IP addresses. This is a missing feature, so in a future release this setting might start to to control both methods of load balancing. round-robin : A new connection attempt chooses the next host entry in the list. disable : A new connection continues using the same host entry until a connection fails, after which the next host entry is chosen. It is recommended to set `server_login_retry` lower than the default to ensure fast retries when multiple hosts are available. Default: `round-robin` ### max_db_connections Configure a database-wide maximum of server connections (i.e. all pools within the database will not have more than this many server connections). ### max_db_client_connections Configure a database-wide client connection maximum. Should be used in conjunction with max_client_conn to limit the number of connections that PgBouncer is allowed to accept. ### server_lifetime Configure the server_lifetime per database. If not set the database will fall back to the instance wide configured value for `server_lifetime` ### client_encoding Ask specific `client_encoding` from server. ### datestyle Ask specific `datestyle` from server. ### timezone Ask specific `timezone` from server. ## Section [users] This section contains key=value lines like user1 = settings where the key will be taken as a user name and the value as a list of key=value pairs of configuration settings specific for this user. Example: user1 = pool_mode=session Only a few settings are available here. Note that when `auth_file` is configured, if a user is defined in this section but not listed in `auth_file`, pgBouncer will attempt to use `auth_query` to find a password for that user if `auth_user` is set. If `auth_user` is not set, pgBouncer will pretend the user exists and fail to return "no such user" messages to the client, but neither will it accept any provided password. ### pool_size Set the maximum size of pools for all connections from this user. If not set, the database or `default_pool_size` is used. ### reserve_pool_size Set the number of additional connections to allow to a pool for this user. If not set, the database configuration or the global `reserve_pool_size` is used. ### pool_mode Set the pool mode to be used for all connections from this user. If not set, the database or default `pool_mode` is used. ### max_user_connections Configure a maximum for the user of server connections (i.e. all pools with the user will not have more than this many server connections). ### query_timeout Set the maximum number of seconds that a user query can run for. If set this timeout overrides the server level query_timeout described above. ### idle_transaction_timeout Set the maximum number of seconds that a user can have an idle transaction open. If set this timeout overides the server level idle_transaction_timeout described above. ### client_idle_timeout Set the maximum amount of time in seconds that a client is allowed to idly connect to the pgbouncer instance. If set this timeout overrides the server level client_idle_timeout described above. Please note that this is a potentially dangeous timeout. ### max_user_client_connections Configure a maximum for the user of client connections. This is the user equivalent ofthe max_client_conn setting. ## Section [peers] The section `[peers]` defines the peers that PgBouncer can forward cancellation requests to and where those cancellation requests will be routed. PgBouncer processes can be peered together in a group by defining a `peer_id` value and a `[peers]` section in the configs of all the PgBouncer processes. These PgBouncer processes can then forward cancellations requests to the process that it originated from. This is needed to make cancellations work when multiple PgBouncer processes (possibly on different servers) are behind the same TCP load balancer. Cancellation requests are sent over different TCP connections than the query they are cancelling, so a TCP load balancer might send the cancellation request connection to a different process than the one that it was meant for. By peering them these cancellation requests eventually end up at the right process. A more in-depth explanation is provided in this [recording of a conference talk][cancel-problem-video]. [cancel-problem-video]: https://www.youtube.com/watch?v=X-nCHcZ6vQU The section contains key=value lines like peer_id = connection string Where the key will be taken as a `peer_id` and the value as a connection string, consisting of key=value pairs of connection parameters, described below (similar to libpq, but the actual libpq is not used and the set of available features is different). Example: 1 = host=host1.example.com 2 = host=/tmp/pgbouncer-2 port=5555 Note 1: For peering to work, the `peer_id` of each PgBouncer process in the group must be unique within the peered group. And the `[peers]` section should contain entries for each of those peer ids. An example can be found in the examples section of these docs. It **is** allowed, but not necessary, for the `[peers]` section to contain the `peer_id` of the PgBouncer that the config is for. Such an entry will be ignored, but it is allowed to config management easy. Because it allows using the exact same `[peers]` section for multiple configs. Note 2: Cross-version peering is supported as long as all peers are on the same side of the v1.21.0 version boundary. In v1.21.0 some breaking changes were made in how we encode the cancellation tokens that made them incompatible with the ones created by earlier versions. ### host Host name or IP address to connect to. Host names are resolved at connection time, the result is cached per `dns_max_ttl` parameter. If DNS returns several results, they are used in a round-robin manner. But in general it's not recommended to use a hostname that resolves to multiple IPs, because then the cancel request might still be forwarded to the wrong node and it would need to be forwarded again (which is only allowed up to three times). If the value begins with `/`, then a Unix socket in the file-system namespace is used. If the value begins with `@`, then a Unix socket in the abstract namespace is used. Examples: host=localhost host=127.0.0.1 host=2001:0db8:85a3:0000:0000:8a2e:0370:7334 host=/var/run/pgbouncer-1 ### port Default: 6432 ### pool_size Set the maximum number of cancel requests that can be in flight to the peer at the same time. It's quite normal for cancel requests to arrive in bursts, e.g. when the backing Postgres server slow or down. So it's important for `pool_size` to not be so low that it cannot handle these bursts. If not set, the `default_pool_size` is used. ## Include directive The PgBouncer configuration file can contain include directives, which specify another configuration file to read and process. This allows splitting the configuration file into physically separate parts. The include directives look like this: %include filename If the file name is not an absolute path, it is taken as relative to the current working directory. ## Authentication file format This section describes the format of the file specified by the `auth_file` setting. It is a text file in the following format: "username1" "password" ... "username2" "md5abcdef012342345" ... "username2" "SCRAM-SHA-256$:$:" There should be at least 2 fields, surrounded by double quotes. The first field is the user name and the second is either a plain-text, a MD5-hashed password, or a SCRAM secret. PgBouncer ignores the rest of the line. Double quotes in a field value can be escaped by writing two double quotes. PostgreSQL MD5-hashed password format: "md5" + md5(password + username) So user `admin` with password `1234` will have MD5-hashed password `md545f2603610af569b6155c45067268c6b`. PostgreSQL SCRAM secret format: SCRAM-SHA-256$:$: See the PostgreSQL documentation and RFC 5803 for details on this. The passwords or secrets stored in the authentication file serve two purposes. First, they are used to verify the passwords of incoming client connections, if a password-based authentication method is configured. Second, they are used as the passwords for outgoing connections to the backend server, if the backend server requires password-based authentication (unless the password is specified directly in the database's connection string). ### Limitations If the password is stored in plain text, it can be used for any password-based authentication used in the backend server; plain text, MD5 or SCRAM (see for details). MD5-hashed passwords can be used if backend server uses MD5 authentication (or specific users have MD5-hashed passwords). SCRAM secrets can only be used for logging into a server if the client authentication also uses SCRAM, the PgBouncer database definition does not specify a user name, and the SCRAM secrets are identical in PgBouncer and the PostgreSQL server (same salt and iterations, not merely the same password). This is due to an inherent security property of SCRAM: The stored SCRAM secret cannot by itself be used for deriving login credentials. The authentication file can be written by hand, but it's also useful to generate it from some other list of users and passwords. See `./etc/mkauth.py` for a sample script to generate the authentication file from the `pg_authid` system table. Alternatively, use `auth_query` instead of `auth_file` to avoid having to maintain a separate authentication file. ### Note on managed servers If the backend server is configured to use SCRAM password authentication PgBouncer cannot successfully authenticate if it does not know either a) user password in plain text or b) corresponding SCRAM secret. Some cloud providers (i.e. AWS RDS) prohibit access to PostgreSQL sensitive system tables for fetching passwords. Even for the most privileged user (i.e. member of rds_superuser) the `select * from pg_authid`; returns the `ERROR: permission denied for table pg_authid.` That is a known behaviour ([blog](https://aws.amazon.com/blogs/database/best-practices-for-migrating-postgresql-databases-to-amazon-rds-and-amazon-aurora/)). Therefore, fetching an existing SCRAM secret once it has been stored in a managed server is impossible which makes it hard to configure PgBouncer to use the same SCRAM secret. Nevertheless, SCRAM secret can still be configured and used on both sides using the following trick: Generate SCRAM secret for arbitrary password with a tool that is capable of printing out the secret. For example `psql --echo-hidden` and the command `\password` prints out the SCRAM secret to the console before sending it over to the server. ``` $ psql --echo-hidden postgres=# \password Enter new password for user "": Enter it again: ********* QUERY ********** ALTER USER PASSWORD 'SCRAM-SHA-256$:$:' ************************** ``` Note down the SCRAM secret from the QUERY and set it in PgBouncer's `userlist.txt`. If you used a tool other than `psql --echo-hidden` then you need to set the SCRAM secret also in the server (you can use `alter role password ''` for that). ## HBA file format The location of the HBA file is specified by the setting `auth_hba_file`. It is only used if `auth_type` is set to `hba`. The file follows the format of the PostgreSQL `pg_hba.conf` file (see ). * Supported record types: `local`, `host`, `hostssl`, `hostnossl`. * Database field: Supports `all`, `replication`, `sameuser`, `@file`, multiple names. Not supported: `samerole`, `samegroup`. * User name field: Supports `all`, `@file`, multiple names. Not supported: `+groupname`. * Address field: Supports `all`, IPv4, IPv6. Not supported: `samehost`, `samenet`, DNS names, domain prefixes. * Auth-method field: Only methods supported by PgBouncer's `auth_type` are supported, plus `peer` and `reject`, but except `any` and `pam`, which only work globally. * User name map (`map=`) parameter is supported when `auth_type` is `cert` or `peer`. ## Ident map file format The location of the ident map file is specified by the setting `auth_ident_file`. It is only loaded if `auth_type` is set to `hba`. The file format is a simplified variation of the PostgreSQL ident map file (see ). * Supported lines are only of the form `map-name system-username database-username`. * There is no support for including file/directory. * System-username field: Not supported: regular expressions. * Database-username field: Supports `all` or a single postgres user name. Not supported: `+groupname`, regular expressions. ## Examples Small example configuration: [databases] template1 = host=localhost dbname=template1 auth_user=someuser [pgbouncer] pool_mode = session listen_port = 6432 listen_addr = localhost auth_type = md5 auth_file = users.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser stats_users = stat_collector Database examples: [databases] ; foodb over Unix socket foodb = ; redirect bardb to bazdb on localhost bardb = host=localhost dbname=bazdb ; access to destination database will go with single user forcedb = host=localhost port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO Example of a secure function for `auth_query`: CREATE OR REPLACE FUNCTION pgbouncer.user_lookup(in i_username text, out uname text, out phash text) RETURNS record AS $$ BEGIN SELECT rolname, CASE WHEN rolvaliduntil < now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolname=i_username AND rolcanlogin INTO uname, phash; RETURN; END; $$ LANGUAGE plpgsql SECURITY DEFINER -- Set a secure search_path: trusted schema(s), then 'pg_temp'. SET search_path = pg_catalog, pg_temp; REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public, pgbouncer; GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer; Example configs for 2 peered PgBouncer processes to create a multi-core PgBouncer setup using `so_reuseport`. The config for the first process: [databases] postgres = host=localhost dbname=postgres [peers] 1 = host=/tmp/pgbouncer1 2 = host=/tmp/pgbouncer2 [pgbouncer] listen_addr=127.0.0.1 auth_file=auth_file.conf so_reuseport=1 unix_socket_dir=/tmp/pgbouncer1 peer_id=1 The config for the second process: [databases] postgres = host=localhost dbname=postgres [peers] 1 = host=/tmp/pgbouncer1 2 = host=/tmp/pgbouncer2 [pgbouncer] listen_addr=127.0.0.1 auth_file=auth_file.conf so_reuseport=1 ; only unix_socket_dir and peer_id are different unix_socket_dir=/tmp/pgbouncer2 peer_id=2 ## See also pgbouncer(1) - man page for general usage, console commands pgbouncer-1.24.1/doc/pgbouncer.50000664000175000017500000017025514777762332013361 00000000000000.\" Automatically generated by Pandoc 2.9.2.1 .\" .TH "PGBOUNCER.INI" "5" "" "1.24.1" "Databases" .hy .SH NAME .PP pgbouncer.ini - configuration file for pgbouncer .SH DESCRIPTION .PP The configuration file is in \[lq]ini\[rq] format. Section names are between \[lq][\[dq] and \[dq]]\[rq]. Lines starting with \[lq];\[rq] or \[lq]#\[rq] are taken as comments and ignored. The characters \[lq];\[rq] and \[lq]#\[rq] are not recognized as special when they appear later in the line. .SH GENERIC SETTINGS .SS logfile .PP Specifies the log file. For daemonization (\f[C]-d\f[R]), either this or \f[C]syslog\f[R] need to be set. .PP The log file is kept open, so after rotation, \f[C]kill -HUP\f[R] or on console \f[C]RELOAD;\f[R] should be done. On Windows, the service must be stopped and started. .PP Note that setting \f[C]logfile\f[R] does not by itself turn off logging to stderr. Use the command-line option \f[C]-q\f[R] or \f[C]-d\f[R] for that. .PP Default: not set .SS pidfile .PP Specifies the PID file. Without \f[C]pidfile\f[R] set, daemonization (\f[C]-d\f[R]) is not allowed. .PP Default: not set .SS listen_addr .PP Specifies a list (comma-separated) of addresses where to listen for TCP connections. You may also use \f[C]*\f[R] meaning \[lq]listen on all addresses\[rq]. When not set, only Unix socket connections are accepted. .PP Addresses can be specified numerically (IPv4/IPv6) or by name. .PP Default: not set .SS listen_port .PP Which port to listen on. Applies to both TCP and Unix sockets. .PP Default: 6432 .SS unix_socket_dir .PP Specifies the location for Unix sockets. Applies to both the listening socket and to server connections. If set to an empty string, Unix sockets are disabled. A value that starts with \f[C]\[at]\f[R] specifies that a Unix socket in the abstract namespace should be created (currently supported on Linux and Windows). .PP For online reboot (\f[C]-R\f[R]) to work, a Unix socket needs to be configured, and it needs to be in the file-system namespace. .PP Default: \f[C]/tmp\f[R] (empty on Windows) .SS unix_socket_mode .PP File system mode for Unix socket. Ignored for sockets in the abstract namespace. Not supported on Windows. .PP Default: 0777 .SS unix_socket_group .PP Group name to use for Unix socket. Ignored for sockets in the abstract namespace. Not supported on Windows. .PP Default: not set .SS user .PP If set, specifies the Unix user to change to after startup. Works only if PgBouncer is started as root or if it\[cq]s already running as the given user. Not supported on Windows. .PP Default: not set .SS pool_mode .PP Specifies when a server connection can be reused by other clients. .TP session Server is released back to pool after client disconnects. Default. .TP transaction Server is released back to pool after transaction finishes. .TP statement Server is released back to pool after query finishes. Transactions spanning multiple statements are disallowed in this mode. .SS max_client_conn .PP Maximum number of client connections allowed. .PP When this setting is increased, then the file descriptor limits in the operating system might also have to be increased. Note that the number of file descriptors potentially used is more than \f[C]max_client_conn\f[R]. If each user connects under its own user name to the server, the theoretical maximum used is: .IP .nf \f[C] max_client_conn + (max pool_size * total databases * total users) \f[R] .fi .PP If a database user is specified in the connection string (all users connect under the same user name), the theoretical maximum is: .IP .nf \f[C] max_client_conn + (max pool_size * total databases) \f[R] .fi .PP The theoretical maximum should never be reached, unless somebody deliberately crafts a special load for it. Still, it means you should set the number of file descriptors to a safely high number. .PP Search for \f[C]ulimit\f[R] in your favorite shell man page. Note: \f[C]ulimit\f[R] does not apply in a Windows environment. .PP Default: 100 .SS default_pool_size .PP How many server connections to allow per user/database pair. Can be overridden in the per-database configuration. .PP Default: 20 .SS min_pool_size .PP Add more server connections to pool if below this number. Improves behavior when the normal load suddenly comes back after a period of total inactivity. The value is effectively capped at the pool size. .PP Only enforced for pools where at least one of the following is true: .IP \[bu] 2 the entry in the \f[C][database]\f[R] section for the pool has a value set for the \f[C]user\f[R] key (aka forced user) .IP \[bu] 2 there is at least one client connected to the pool .PP Default: 0 (disabled) .SS reserve_pool_size .PP How many additional connections to allow to a pool (see \f[C]reserve_pool_timeout\f[R]). 0 disables. .PP Default: 0 (disabled) .SS reserve_pool_timeout .PP If a client has not been serviced in this time, use additional connections from the reserve pool. 0 disables. [seconds] .PP Default: 5.0 .SS max_db_connections .PP Do not allow more than this many server connections per database (regardless of user). This considers the PgBouncer database that the client has connected to, not the PostgreSQL database of the outgoing connection. .PP This can also be set per database in the \f[C][databases]\f[R] section. .PP Note that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. .PP Default: 0 (unlimited) .SS max_db_client_connections .PP Do not allow more than this many client connections to PgBouncer per database (regardless of user). This considers the PgBouncer database that the client has connected to, not the PostgreSQL database of the outgoing connection. .PP This should be set at a number greater than or equal to max_db_connections. The difference between the two numbers can be thought of as how many connections to a given database can be in the queue while waiting for active connections to finish. .PP This can also be set per database in the \f[C][databases]\f[R] section. .PP Default: 0 (unlimited) .SS max_user_connections .PP Do not allow more than this many server connections per user (regardless of database). This considers the PgBouncer user that is associated with a pool, which is either the user specified for the server connection or in absence of that the user the client has connected as. .PP This can also be set per user in the \f[C][users]\f[R] section. .PP Note that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. .PP Default: 0 (unlimited) .SS max_user_client_connections .PP Do not allow more than this many client connections per user (regardless of database). This value should be set to a number higher than max_user_connections. This difference between max_user_connections and max_user_client_connections can be conceptualized as the number the max size of the queue for the user. .PP This can also be set per user in the \f[C][users]\f[R] section. .PP Default: 0 (unlimited) .SS server_round_robin .PP By default, PgBouncer reuses server connections in LIFO (last-in, first-out) manner, so that few connections get the most load. This gives best performance if you have a single server serving a database. But if there is a round-robin system behind a database address (TCP, DNS, or host list), then it is better if PgBouncer also uses connections in that manner, thus achieving uniform load. .PP Default: 0 .SS track_extra_parameters .PP By default, PgBouncer tracks \f[C]client_encoding\f[R], \f[C]datestyle\f[R], \f[C]timezone\f[R], \f[C]standard_conforming_strings\f[R] and \f[C]application_name\f[R] parameters per client. To allow other parameters to be tracked, they can be specified here, so that PgBouncer knows that they should be maintained in the client variable cache and restored in the server whenever the client becomes active. .PP If you need to specify multiple values, use a comma-separated list (e.g. \f[C]default_transaction_read_only, IntervalStyle\f[R]) .PP Note: Most parameters cannot be tracked this way. The only parameters that can be tracked are ones that Postgres reports to the client. Postgres has an official list of parameters that it reports to the client (https://www.postgresql.org/docs/15/protocol-flow.html#PROTOCOL-ASYNC). Postgres extensions can change this list though, they can add parameters themselves that they also report, and they can start reporting already existing parameters that Postgres does not report. Notably Citus 12.0+ causes Postgres to also report \f[C]search_path\f[R]. .PP The Postgres protocol allows specifying parameters settings, both directly as a parameter in the startup packet, or inside the \f[C]options\f[R] startup packet (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS). Parameters specified using both of these methods are supported by \f[C]track_extra_parameters\f[R]. However, it\[cq]s not possible to include \f[C]options\f[R] itself in \f[C]track_extra_parameters\f[R], only the parameters contained in \f[C]options\f[R]. .PP Default: IntervalStyle .SS ignore_startup_parameters .PP By default, PgBouncer allows only parameters it can keep track of in startup packets: \f[C]client_encoding\f[R], \f[C]datestyle\f[R], \f[C]timezone\f[R] and \f[C]standard_conforming_strings\f[R]. All others parameters will raise an error. To allow others parameters, they can be specified here, so that PgBouncer knows that they are handled by the admin and it can ignore them. .PP If you need to specify multiple values, use a comma-separated list (e.g. \f[C]options,extra_float_digits\f[R]) .PP The Postgres protocol allows specifying parameters settings, both directly as a parameter in the startup packet, or inside the \f[C]options\f[R] startup packet (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS). Parameters specified using both of these methods are supported by \f[C]ignore_startup_parameters\f[R]. It\[cq]s even possible to include \f[C]options\f[R] itself in \f[C]track_extra_parameters\f[R], which results in any unknown parameters contained inside \f[C]options\f[R] to be ignored. .PP Default: empty .SS peer_id .PP The peer id used to identify this PgBouncer process in a group of PgBouncer processes that are peered together. The \f[C]peer_id\f[R] value should be unique within a group of peered PgBouncer processes. When set to 0 pgbouncer peering is disabled. See the docs for the \f[C][peers]\f[R] section for more information. The maximum value that can be used for the \f[C]peer_id\f[R] is 16383. .PP Default: 0 .SS disable_pqexec .PP Disable the Simple Query protocol (PQexec). Unlike the Extended Query protocol, Simple Query allows multiple queries in one packet, which allows some classes of SQL-injection attacks. Disabling it can improve security. Obviously, this means only clients that exclusively use the Extended Query protocol will stay working. .PP Default: 0 .SS application_name_add_host .PP Add the client host address and port to the application name setting set on connection start. This helps in identifying the source of bad queries etc. This logic applies only at the start of a connection. If \f[C]application_name\f[R] is later changed with \f[C]SET\f[R], PgBouncer does not change it again. .PP Default: 0 .SS conffile .PP Show location of current config file. Changing it will make PgBouncer use another config file for next \f[C]RELOAD\f[R] / \f[C]SIGHUP\f[R]. .PP Default: file from command line .SS service_name .PP Used on win32 service registration. .PP Default: \f[C]pgbouncer\f[R] .SS job_name .PP Alias for \f[C]service_name\f[R]. .SS stats_period .PP Sets how often the averages shown in various \f[C]SHOW\f[R] commands are updated and how often aggregated statistics are written to the log (but see \f[C]log_stats\f[R]). [seconds] .PP Default: 60 .SS max_prepared_statements .PP When this is set to a non-zero value PgBouncer tracks protocol-level named prepared statements related commands sent by the client in transaction and statement pooling mode. PgBouncer makes sure that any statement prepared by a client is available on the backing server connection. Even when the statement was originally prepared on another server connection. .PP PgBouncer internally examines all the queries that are sent by clients as a prepared statement, and gives each unique query string an internal name with the format \f[C]PGBOUNCER_{unique_id}\f[R]. If the same query string is prepared multiple times (possibly by different clients), then these queries share the same internal name. PgBouncer only prepares the statement on the actual PostgreSQL server using the internal name (so not the name provided by the client). PgBouncer keeps track of the name that the client gave to each prepared statement. It then rewrites each command that uses a prepared statement to by replacing the client side name with the the internal name (e.g. replacing \f[C]my_prepared_statement\f[R] with \f[C]PGBOUNCER_123\f[R]) before forwarding that command to the server. More importantly, if the prepared statement that the client wants to execute is not yet prepared on the server (e.g.\ because a different server is now assigned to the client than when the client prepared the statement), then PgBouncer transparently prepares the statement before executing it. .PP Note: This tracking and rewriting of prepared statement commands does not work for SQL-level prepared statement commands, so \f[C]PREPARE\f[R], \f[C]EXECUTE\f[R] and \f[C]DEALLOCATE\f[R] are forwarded straight to Postgres. The exception to this rule are the \f[C]DEALLOCATE ALL\f[R] and \f[C]DISCARD ALL\f[R] commands, these do work as expected and will clear the prepared statements that PgBouncer tracked for the client that sends this command. .PP The actual value of this setting controls the number of prepared statements kept active in an LRU cache on a single server connection. When the setting is set to 0 prepared statement support for transaction and statement pooling is disabled. To get the best performance you should try to make sure that this setting is larger than the amount of commonly used prepared statements in your application. Keep in mind that the higher this value, the larger the memory footprint of each PgBouncer connection will be on your PostgreSQL server, because it will keep more queries prepared on those connections. It also increases the memory footprint of PgBouncer itself, because it now needs to keep track of query strings. .PP The impact on PgBouncer memory usage is not that big though: - Each unique query is stored once in a global query cache. - Each client connection keeps a buffer that it uses to rewrite packets. This is, at most, 4 times the size of \f[C]pkt_buf\f[R]. This limit is often not reached though, it only happens when the queries in your prepared statements are between 2 and 4 times the size of \f[C]pkt_buf\f[R]. .PP So if you consider the following as an example scenario: - There are 1000 active clients - The clients prepare 200 unique queries - The average size of a query is 5kB - \f[C]pkt_buf\f[R] parameter is set to the default of 4096 (4kB) .PP Then, PgBouncer needs at most the following amount of memory to handle these prepared statements: .PP 200 x 5kB + 1000 x 4 x 4kB = \[ti]17MB of memory. .PP Tracking prepared statements does not only come with a memory cost, but also with increased CPU usage, because PgBouncer needs to inspect and rewrite the queries. Multiple PgBouncer instances can listen on the same port to use more than one core for processing, see the documentation for the \f[C]so_reuseport\f[R] option for details. .PP But of course there are also performance benefits to prepared statements. Just as when connecting to PostgreSQL directly, by preparing a query that is executed many times, it reduces the total amount of parsing and planning that needs to be done. The way that PgBouncer tracks prepared statements is especially beneficial to performance when multiple clients prepare the same queries. Because client connections automatically reuse a prepared statement on a server connection, even if it was prepared by another client. As an example, if you have a \f[C]pool_size\f[R] of 20 and you have 100 clients that all prepare the exact same query, then the query is prepared (and thus parsed) only 20 times on the PostgreSQL server. .PP The reuse of prepared statements has one downside. If the return or argument types of a prepared statement changes across executions then PostgreSQL currently throws an error such as: .IP .nf \f[C] ERROR: cached plan must not change result type \f[R] .fi .PP You can avoid such errors by not having multiple clients that use the exact same query string in a prepared statement, but expecting different argument or result types. One of the most common ways of running into this issue is during a DDL migration where you add a new column or change a column type on an existing table. In those cases you can run \f[C]RECONNECT\f[R] on the PgBouncer admin console after doing the migration to force a re-prepare of the query and make the error go away. .PP Default: 200 .SH AUTHENTICATION SETTINGS .PP PgBouncer handles its own client authentication and has its own database of users. These settings control this. .SS auth_type .PP How to authenticate users. .TP cert Client must connect over TLS connection with a valid client certificate. The user name is then taken from the CommonName field from the certificate. .TP md5 Use MD5-based password check. This is the default authentication method. \f[C]auth_file\f[R] may contain both MD5-encrypted and plain-text passwords. If \f[C]md5\f[R] is configured and a user has a SCRAM secret, then SCRAM authentication is used automatically instead. .TP scram-sha-256 Use password check with SCRAM-SHA-256. \f[C]auth_file\f[R] has to contain SCRAM secrets or plain-text passwords. .TP plain The clear-text password is sent over the wire. Deprecated. .TP trust No authentication is done. The user name must still exist in \f[C]auth_file\f[R]. .TP any Like the \f[C]trust\f[R] method, but the user name given is ignored. Requires that all databases are configured to log in as a specific user. Additionally, the console database allows any user to log in as admin. .TP hba The actual authentication type is loaded from \f[C]auth_hba_file\f[R]. This allows different authentication methods for different access paths, for example: connections over Unix socket use the \f[C]peer\f[R] auth method, connections over TCP must use TLS. .TP pam PAM is used to authenticate users, \f[C]auth_file\f[R] is ignored. This method is not compatible with databases using the \f[C]auth_user\f[R] option. The service name reported to PAM is \[lq]pgbouncer\[rq]. \f[C]pam\f[R] is not supported in the HBA configuration file. .SS auth_hba_file .PP HBA configuration file to use when \f[C]auth_type\f[R] is \f[C]hba\f[R]. See section HBA file format below about details. .PP Default: not set .SS auth_ident_file .PP Identity map file to use when \f[C]auth_type\f[R] is \f[C]hba\f[R] and a user map will be defined. See section Ident map file format below about details. .PP Default: not set .SS auth_file .PP The name of the file to load user names and passwords from. See section Authentication file format below about details. .PP Most authentication types (see above) require that either \f[C]auth_file\f[R] or \f[C]auth_user\f[R] be set; otherwise there would be no users defined. .PP Default: not set .SS auth_user .PP If \f[C]auth_user\f[R] is set, then any user not specified in \f[C]auth_file\f[R] will be queried through the \f[C]auth_query\f[R] query from \f[C]pg_authid\f[R] in the database, using \f[C]auth_user\f[R]. The password of \f[C]auth_user\f[R] will be taken from \f[C]auth_file\f[R]. (If the \f[C]auth_user\f[R] does not require a password then it does not need to be defined in \f[C]auth_file\f[R].) .PP Direct access to \f[C]pg_authid\f[R] requires admin rights. It\[cq]s preferable to use a non-superuser that calls a SECURITY DEFINER function instead. .PP Default: not set .SS auth_query .PP Query to load user\[cq]s password from database. .PP Direct access to \f[C]pg_authid\f[R] requires admin rights. It\[cq]s preferable to use a non-superuser that calls a SECURITY DEFINER function instead. .PP Note that the query is run inside the target database. So if a function is used, it needs to be installed into each database. .PP Default: \f[C]SELECT rolname, CASE WHEN rolvaliduntil < now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolname=$1 AND rolcanlogin\f[R] .SS auth_dbname .PP Database name in the \f[C][database]\f[R] section to be used for authentication purposes. This option can be either global or overridden in the connection string if this parameter is specified. .SH LOG SETTINGS .SS syslog .PP Toggles syslog on/off. On Windows, the event log is used instead. .PP Default: 0 .SS syslog_ident .PP Under what name to send logs to syslog. .PP Default: \f[C]pgbouncer\f[R] (program name) .SS syslog_facility .PP Under what facility to send logs to syslog. Possibilities: \f[C]auth\f[R], \f[C]authpriv\f[R], \f[C]daemon\f[R], \f[C]user\f[R], \f[C]local0-7\f[R]. .PP Default: \f[C]daemon\f[R] .SS log_connections .PP Log successful logins. .PP Default: 1 .SS log_disconnections .PP Log disconnections with reasons. .PP Default: 1 .SS log_pooler_errors .PP Log error messages the pooler sends to clients. .PP Default: 1 .SS log_stats .PP Write aggregated statistics into the log, every \f[C]stats_period\f[R]. This can be disabled if external monitoring tools are used to grab the same data from \f[C]SHOW\f[R] commands. .PP Default: 1 .SS verbose .PP Increase verbosity. Mirrors the \[lq]-v\[rq] switch on the command line. For example, using \[lq]-v -v\[rq] on the command line is the same as \f[C]verbose=2\f[R]. 3 is the highest currently-supported verbosity. .PP Default: 0 .SH CONSOLE ACCESS CONTROL .SS admin_users .PP Comma-separated list of database users that are allowed to connect and run all commands on the console. Ignored when \f[C]auth_type\f[R] is \f[C]any\f[R], in which case any user name is allowed in as admin. .PP Default: empty .SS stats_users .PP Comma-separated list of database users that are allowed to connect and run read-only queries on the console. That means all \f[C]SHOW\f[R] commands except \f[C]SHOW FDS\f[R]. .PP Default: empty .SH CONNECTION SANITY CHECKS, TIMEOUTS .SS server_reset_query .PP Query sent to server on connection release, before making it available to other clients. At that moment no transaction is in progress, so the value should not include \f[C]ABORT\f[R] or \f[C]ROLLBACK\f[R]. .PP The query is supposed to clean any changes made to the database session so that the next client gets the connection in a well-defined state. The default is \f[C]DISCARD ALL\f[R], which cleans everything, but that leaves the next client no pre-cached state. It can be made lighter, e.g.\ \f[C]DEALLOCATE ALL\f[R] to just drop prepared statements, if the application does not break when some state is kept around. .PP When transaction pooling is used, the \f[C]server_reset_query\f[R] is not used, because in that mode, clients must not use any session-based features, since each transaction ends up in a different connection and thus gets a different session state. .PP Default: \f[C]DISCARD ALL\f[R] .SS server_reset_query_always .PP Whether \f[C]server_reset_query\f[R] should be run in all pooling modes. When this setting is off (default), the \f[C]server_reset_query\f[R] will be run only in pools that are in sessions-pooling mode. Connections in transaction-pooling mode should not have any need for a reset query. .PP This setting is for working around broken setups that run applications that use session features over a transaction-pooled PgBouncer. It changes non-deterministic breakage to deterministic breakage: Clients always lose their state after each transaction. .PP Default: 0 .SS server_check_delay .PP How long to keep released connections available for immediate re-use, without running \f[C]server_check_query\f[R] on it. If 0 then the check is always run. .PP Default: 30.0 .SS server_check_query .PP Simple do-nothing query to check if the server connection is alive. .PP If an empty string, then sanity checking is disabled. .PP Default: \f[C]select 1\f[R] .SS server_fast_close .PP Disconnect a server in session pooling mode immediately or after the end of the current transaction if it is in \[lq]close_needed\[rq] mode (set by \f[C]RECONNECT\f[R], \f[C]RELOAD\f[R] that changes connection settings, or DNS change), rather than waiting for the session end. In statement or transaction pooling mode, this has no effect since that is the default behavior there. .PP If because of this setting a server connection is closed before the end of the client session, the client connection is also closed. This ensures that the client notices that the session has been interrupted. .PP This setting makes connection configuration changes take effect sooner if session pooling and long-running sessions are used. The downside is that client sessions are liable to be interrupted by a configuration change, so client applications will need logic to reconnect and reestablish session state. But note that no transactions will be lost, because running transactions are not interrupted, only idle sessions. .PP Default: 0 .SS server_lifetime .PP The pooler will close an unused (not currently linked to any client connection) server connection that has been connected longer than this. Setting it to 0 means the connection is to be used only once, then closed. [seconds] .PP This can also be set per database in the \f[C][databases]\f[R] section. .PP Default: 3600.0 .SS server_idle_timeout .PP If a server connection has been idle more than this many seconds it will be closed. If 0 then this timeout is disabled. [seconds] .PP Default: 600.0 .SS server_connect_timeout .PP If connection and login don\[cq]t finish in this amount of time, the connection will be closed. [seconds] .PP Default: 15.0 .SS server_login_retry .PP If login to the server failed, because of failure to connect or from authentication, the pooler waits this much before retrying to connect. During the waiting interval, new clients trying to connect to the failing server will get an error immediately without another connection attempt. [seconds] .PP The purpose of this behavior is that clients don\[cq]t unnecessarily queue up waiting for a server connection to become available if the server is not working. However, it also means that if a server is momentarily failing, for example during a restart or if the configuration was erroneous, then it will take at least this long until the pooler will consider connecting to it again. Planned events such as restarts should normally be managed using the \f[C]PAUSE\f[R] command to avoid this. .PP Default: 15.0 .SS client_login_timeout .PP If a client connects but does not manage to log in in this amount of time, it will be disconnected. Mainly needed to avoid dead connections stalling \f[C]SUSPEND\f[R] and thus online restart. [seconds] .PP Default: 60.0 .SS autodb_idle_timeout .PP If the automatically created (via \[dq]*\[dq]) database pools have been unused this many seconds, they are freed. The negative aspect of that is that their statistics are also forgotten. [seconds] .PP Default: 3600.0 .SS dns_max_ttl .PP How long DNS lookups can be cached. The actual DNS TTL is ignored. [seconds] .PP Default: 15.0 .SS dns_nxdomain_ttl .PP How long DNS errors and NXDOMAIN DNS lookups can be cached. [seconds] .PP Default: 15.0 .SS dns_zone_check_period .PP Period to check if a zone serial has changed. .PP PgBouncer can collect DNS zones from host names (everything after first dot) and then periodically check if the zone serial changes. If it notices changes, all host names under that zone are looked up again. If any host IP changes, its connections are invalidated. .PP Works only with c-ares backend (\f[C]configure\f[R] option \f[C]--with-cares\f[R]). .PP Default: 0.0 (disabled) .SS resolv_conf .PP The location of a custom \f[C]resolv.conf\f[R] file. This is to allow specifying custom DNS servers and perhaps other name resolution options, independent of the global operating system configuration. .PP Requires evdns (>= 2.0.3) or c-ares (>= 1.15.0) backend. .PP The parsing of the file is done by the DNS backend library, not PgBouncer, so see the library\[cq]s documentation for details on allowed syntax and directives. .PP Default: empty (use operating system defaults) .SH TLS SETTINGS .PP If the contents of any of the cert or key files are changed without changing the actual setting filename in the config, the new file contents will be used for new connections after a RELOAD. Existing connections won\[cq]t be closed though. If it\[cq]s necessary for security reasons that all connections start using the new files ASAP, it\[cq]s advised to run RECONNECT after the RELOAD. .PP Changing any TLS settings will trigger a RECONNECT automatically for security reasons. .SS client_tls_sslmode .PP TLS mode to use for connections from clients. TLS connections are disabled by default. When enabled, \f[C]client_tls_key_file\f[R] and \f[C]client_tls_cert_file\f[R] must be also configured to set up the key and certificate PgBouncer uses to accept client connections. The most common certificate file format usable by PgBouncer is pem. .TP disable Plain TCP. If client requests TLS, it\[cq]s ignored. Default. .TP allow If client requests TLS, it is used. If not, plain TCP is used. If the client presents a client certificate, it is not validated. .TP prefer Same as \f[C]allow\f[R]. .TP require Client must use TLS. If not, the client connection is rejected. If the client presents a client certificate, it is not validated. .TP verify-ca Client must use TLS with valid client certificate. .TP verify-full Same as \f[C]verify-ca\f[R]. .SS client_tls_key_file .PP Private key for PgBouncer to accept client connections. .PP Default: not set .SS client_tls_cert_file .PP Certificate for private key. Clients can validate it. .PP Default: not set .SS client_tls_ca_file .PP Root certificate file to validate client certificates. .PP Default: not set .SS client_tls_protocols .PP Which TLS protocol versions are allowed. Allowed values: \f[C]tlsv1.0\f[R], \f[C]tlsv1.1\f[R], \f[C]tlsv1.2\f[R], \f[C]tlsv1.3\f[R]. Shortcuts: \f[C]all\f[R] (tlsv1.0,tlsv1.1,tlsv1.2,tlsv1.3), \f[C]secure\f[R] (tlsv1.2,tlsv1.3). .PP Default: \f[C]secure\f[R] .SS client_tls_ciphers .PP Allowed TLS ciphers, in OpenSSL syntax. Shortcuts: .IP \[bu] 2 \f[C]default\f[R]/\f[C]secure\f[R]/\f[C]fast\f[R]/\f[C]normal\f[R] (these all use system wide OpenSSL defaults) .IP \[bu] 2 \f[C]all\f[R] (enables all ciphers, not recommended) .PP Only connections using TLS version 1.2 and lower are affected. There is currently no setting that controls the cipher choices used by TLS version 1.3 connections. .PP Default: \f[C]default\f[R] .SS client_tls_ecdhcurve .PP Elliptic Curve name to use for ECDH key exchanges. .PP Allowed values: \f[C]none\f[R] (DH is disabled), \f[C]auto\f[R] (256-bit ECDH), curve name .PP Default: \f[C]auto\f[R] .SS client_tls_dheparams .PP DHE key exchange type. .PP Allowed values: \f[C]none\f[R] (DH is disabled), \f[C]auto\f[R] (2048-bit DH), \f[C]legacy\f[R] (1024-bit DH) .PP Default: \f[C]auto\f[R] .SS server_tls_sslmode .PP TLS mode to use for connections to PostgreSQL servers. The default mode is \f[C]prefer\f[R]. .TP disable Plain TCP. TLS is not even requested from the server. .TP allow FIXME: if server rejects plain, try TLS? .TP prefer TLS connection is always requested first from PostgreSQL. If refused, the connection will be established over plain TCP. Server certificate is not validated. Default .TP require Connection must go over TLS. If server rejects it, plain TCP is not attempted. Server certificate is not validated. .TP verify-ca Connection must go over TLS and server certificate must be valid according to \f[C]server_tls_ca_file\f[R]. Server host name is not checked against certificate. .TP verify-full Connection must go over TLS and server certificate must be valid according to \f[C]server_tls_ca_file\f[R]. Server host name must match certificate information. .SS server_tls_ca_file .PP Root certificate file to validate PostgreSQL server certificates. .PP Default: not set .SS server_tls_key_file .PP Private key for PgBouncer to authenticate against PostgreSQL server. .PP Default: not set .SS server_tls_cert_file .PP Certificate for private key. PostgreSQL server can validate it. .PP Default: not set .SS server_tls_protocols .PP Which TLS protocol versions are allowed. Allowed values: \f[C]tlsv1.0\f[R], \f[C]tlsv1.1\f[R], \f[C]tlsv1.2\f[R], \f[C]tlsv1.3\f[R]. Shortcuts: \f[C]all\f[R] (tlsv1.0,tlsv1.1,tlsv1.2,tlsv1.3), \f[C]secure\f[R] (tlsv1.2,tlsv1.3), \f[C]legacy\f[R] (all). .PP Default: \f[C]secure\f[R] .SS server_tls_ciphers .PP Allowed TLS ciphers, in OpenSSL syntax. Shortcuts: .IP \[bu] 2 \f[C]default\f[R]/\f[C]secure\f[R]/\f[C]fast\f[R]/\f[C]normal\f[R] (these all use system wide OpenSSL defaults) .IP \[bu] 2 \f[C]all\f[R] (enables all ciphers, not recommended) .PP Only connections using TLS version 1.2 and lower are affected. There is currently no setting that controls the cipher choices used by TLS version 1.3 connections. .PP Default: \f[C]default\f[R] .SH DANGEROUS TIMEOUTS .PP Setting the following timeouts can cause unexpected errors. .SS query_timeout .PP Queries running longer than that are canceled. This should be used only with a slightly smaller server-side \f[C]statement_timeout\f[R], to apply only for network problems. [seconds] .PP Default: 0.0 (disabled) .SS query_wait_timeout .PP Maximum time queries are allowed to spend waiting for execution. If the query is not assigned to a server during that time, the client is disconnected. 0 disables. If this is disabled, clients will be queued indefinitely. [seconds] .PP This setting is used to prevent unresponsive servers from grabbing up connections. It also helps when the server is down or rejects connections for any reason. .PP Default: 120.0 .SS cancel_wait_timeout .PP Maximum time cancellation requests are allowed to spend waiting for execution. If the cancel request is not assigned to a server during that time, the client is disconnected. 0 disables. If this is disabled, cancel requests will be queued indefinitely. [seconds] .PP This setting is used to prevent a client locking up when a cancel cannot be forwarded due to the server being down. .PP Default: 10.0 .SS client_idle_timeout .PP Client connections idling longer than this many seconds are closed. This should be larger than the client-side connection lifetime settings, and only used for network problems. [seconds] .PP Default: 0.0 (disabled) .SS idle_transaction_timeout .PP If a client has been in \[lq]idle in transaction\[rq] state longer, it will be disconnected. [seconds] .PP Default: 0.0 (disabled) .SS suspend_timeout .PP How long to wait for buffer flush during \f[C]SUSPEND\f[R] or reboot (\f[C]-R\f[R]). A connection is dropped if the flush does not succeed. [seconds] .PP Default: 10 .SH LOW-LEVEL NETWORK SETTINGS .SS pkt_buf .PP Internal buffer size for packets. Affects size of TCP packets sent and general memory usage. Actual libpq packets can be larger than this, so no need to set it large. .PP Default: 4096 .SS max_packet_size .PP Maximum size for PostgreSQL packets that PgBouncer allows through. One packet is either one query or one result set row. The full result set can be larger. .PP Default: 2147483647 .SS listen_backlog .PP Backlog argument for listen(2). Determines how many new unanswered connection attempts are kept in the queue. When the queue is full, further new connections are dropped. .PP Default: 128 .SS sbuf_loopcnt .PP How many times to process data on one connection, before proceeding. Without this limit, one connection with a big result set can stall PgBouncer for a long time. One loop processes one \f[C]pkt_buf\f[R] amount of data. 0 means no limit. .PP Default: 5 .SS so_reuseport .PP Specifies whether to set the socket option \f[C]SO_REUSEPORT\f[R] on TCP listening sockets. On some operating systems, this allows running multiple PgBouncer instances on the same host listening on the same port and having the kernel distribute the connections automatically. This option is a way to get PgBouncer to use more CPU cores. (PgBouncer is single-threaded and uses one CPU core per instance.) .PP The behavior in detail depends on the operating system kernel. As of this writing, this setting has the desired effect on (sufficiently recent versions of) Linux, DragonFlyBSD, and FreeBSD. (On FreeBSD, it applies the socket option \f[C]SO_REUSEPORT_LB\f[R] instead.) Some other operating systems support the socket option but it won\[cq]t have the desired effect: It will allow multiple processes to bind to the same port but only one of them will get the connections. See your operating system\[cq]s setsockopt() documentation for details. .PP On systems that don\[cq]t support the socket option at all, turning this setting on will result in an error. .PP Each PgBouncer instance on the same host needs different settings for at least \f[C]unix_socket_dir\f[R] and \f[C]pidfile\f[R], as well as \f[C]logfile\f[R] if that is used. Also note that if you make use of this option, you can no longer connect to a specific PgBouncer instance via TCP/IP, which might have implications for monitoring and metrics collection. .PP To make sure query cancellations keep working, you should set up PgBouncer peering between the different PgBouncer processes. For details look at docs for the \f[C]peer_id\f[R] configuration option and the \f[C]peers\f[R] configuration section. There\[cq]s also an example that uses peering and so_reuseport in the example section of these docs. .PP Default: 0 .SS tcp_defer_accept .PP Sets the \f[C]TCP_DEFER_ACCEPT\f[R] socket option; see \f[C]man 7 tcp\f[R] for details. (This is a Boolean option: 1 means enabled. The actual value set if enabled is currently hardcoded to 45 seconds.) .PP This is currently only supported on Linux. .PP Default: 1 on Linux, otherwise 0 .SS tcp_socket_buffer .PP Default: not set .SS tcp_keepalive .PP Turns on basic keepalive with OS defaults. .PP On Linux, the system defaults are tcp_keepidle=7200, tcp_keepintvl=75, tcp_keepcnt=9. They are probably similar on other operating systems. .PP Default: 1 .SS tcp_keepcnt .PP Default: not set .SS tcp_keepidle .PP Default: not set .SS tcp_keepintvl .PP Default: not set .SS tcp_user_timeout .PP Sets the \f[C]TCP_USER_TIMEOUT\f[R] socket option. This specifies the maximum amount of time in milliseconds that transmitted data may remain unacknowledged before the TCP connection is forcibly closed. If set to 0, then operating system\[cq]s default is used. .PP This is currently only supported on Linux. .PP Default: 0 .SH SECTION [DATABASES] .PP The section \f[C][databases]\f[R] defines the names of the databases that clients of PgBouncer can connect to and specifies where those connections will be routed. The section contains key=value lines like .IP .nf \f[C] dbname = connection string \f[R] .fi .PP where the key will be taken as a database name and the value as a connection string, consisting of key=value pairs of connection parameters, described below (similar to libpq, but the actual libpq is not used and the set of available features is different). Example: .IP .nf \f[C] foodb = host=host1.example.com port=5432 bardb = host=localhost dbname=bazdb \f[R] .fi .PP The database name can contain characters \f[C]_0-9A-Za-z\f[R] without quoting. Names that contain other characters need to be quoted with standard SQL identifier quoting: double quotes, with \[dq]\[dq] for a single instance of a double quote. .PP The database name \[lq]pgbouncer\[rq] is reserved for the admin console and cannot be used as a key here. .PP \[dq]*\[dq] acts as a fallback database: If the exact name does not exist, its value is taken as connection string for the requested database. For example, if there is an entry (and no other overriding entries) .IP .nf \f[C] * = host=foo \f[R] .fi .PP then a connection to PgBouncer specifying a database \[lq]bar\[rq] will effectively behave as if an entry .IP .nf \f[C] bar = host=foo dbname=bar \f[R] .fi .PP exists (taking advantage of the default for \f[C]dbname\f[R] being the client-side database name; see below). .PP Such automatically created database entries are cleaned up if they stay idle longer than the time specified by the \f[C]autodb_idle_timeout\f[R] parameter. .SS dbname .PP Destination database name. .PP Default: same as client-side database name .SS host .PP Host name or IP address to connect to. Host names are resolved at connection time, the result is cached per \f[C]dns_max_ttl\f[R] parameter. When a host name\[cq]s resolution changes, existing server connections are automatically closed when they are released (according to the pooling mode), and new server connections immediately use the new resolution. If DNS returns several results, they are used in a round-robin manner. .PP If the value begins with \f[C]/\f[R], then a Unix socket in the file-system namespace is used. If the value begins with \f[C]\[at]\f[R], then a Unix socket in the abstract namespace is used. .PP A comma-separated list of host names or addresses can be specified. In that case, connections are made in a round-robin manner. (If a host list contains host names that in turn resolve via DNS to multiple addresses, the round-robin systems operate independently. This is an implementation dependency that is subject to change.) Note that in a list, all hosts must be available at all times: There are no mechanisms to skip unreachable hosts or to select only available hosts from a list or similar. (This is different from what a host list in libpq means.) Also note that this only affects how the destinations of new connections are chosen. See also the setting \f[C]server_round_robin\f[R] for how clients are assigned to already established server connections. .PP Examples: .IP .nf \f[C] host=localhost host=127.0.0.1 host=2001:0db8:85a3:0000:0000:8a2e:0370:7334 host=/var/run/postgresql host=192.168.0.1,192.168.0.2,192.168.0.3 \f[R] .fi .PP Default: not set, meaning to use a Unix socket .SS port .PP Default: 5432 .SS user .PP If \f[C]user=\f[R] is set, all connections to the destination database will be done with the specified user, meaning that there will be only one pool for this database. .PP Otherwise, PgBouncer logs into the destination database with the client user name, meaning that there will be one pool per user. .SS password .PP If no password is specified here, the password from the \f[C]auth_file\f[R] will be used for the user specified above. Dynamic forms of password discovery such as \f[C]auth_query\f[R] are not currently supported. .SS auth_user .PP Override of the global \f[C]auth_user\f[R] setting, if specified. .SS auth_query .PP Override of the global \f[C]auth_query\f[R] setting, if specified. The entire SQL statement needs to be enclosed in single quotes. .SS auth_dbname .PP Override of the global \f[C]auth_dbname\f[R] setting, if specified. .SS pool_size .PP Set the maximum size of pools for this database. If not set, the \f[C]default_pool_size\f[R] is used. .SS min_pool_size .PP Set the minimum pool size for this database. If not set, the global \f[C]min_pool_size\f[R] is used. .PP Only enforced if at least one of the following is true: .IP \[bu] 2 this entry in the \f[C][database]\f[R] section has a value set for the \f[C]user\f[R] key (aka forced user) .IP \[bu] 2 there is at least one client connected to the pool .SS reserve_pool_size .PP Set additional connections for this database. If not set, the global \f[C]reserve_pool_size\f[R] is used. For backwards compatibilty reasons \f[C]reserve_pool\f[R] is an alias for this option. .SS connect_query .PP Query to be executed after a connection is established, but before allowing the connection to be used by any clients. If the query raises errors, they are logged but ignored otherwise. .SS pool_mode .PP Set the pool mode specific to this database. If not set, the default \f[C]pool_mode\f[R] is used. .SS load_balance_hosts .PP When a comma-separated list is specified in \f[C]host\f[R], \f[C]load_balance_hosts\f[R] controls which entry is chosen for a new connection. .PP Note: This setting currently only controls the load balancing behaviour when providing multiple hosts in the connection string, but not when a single host its DNS record references multiple IP addresses. This is a missing feature, so in a future release this setting might start to to control both methods of load balancing. .TP round-robin A new connection attempt chooses the next host entry in the list. .TP disable A new connection continues using the same host entry until a connection fails, after which the next host entry is chosen. .PP It is recommended to set \f[C]server_login_retry\f[R] lower than the default to ensure fast retries when multiple hosts are available. .PP Default: \f[C]round-robin\f[R] .SS max_db_connections .PP Configure a database-wide maximum of server connections (i.e.\ all pools within the database will not have more than this many server connections). .SS max_db_client_connections .PP Configure a database-wide client connection maximum. Should be used in conjunction with max_client_conn to limit the number of connections that PgBouncer is allowed to accept. .SS server_lifetime .PP Configure the server_lifetime per database. If not set the database will fall back to the instance wide configured value for \f[C]server_lifetime\f[R] .SS client_encoding .PP Ask specific \f[C]client_encoding\f[R] from server. .SS datestyle .PP Ask specific \f[C]datestyle\f[R] from server. .SS timezone .PP Ask specific \f[C]timezone\f[R] from server. .SH SECTION [USERS] .PP This section contains key=value lines like .IP .nf \f[C] user1 = settings \f[R] .fi .PP where the key will be taken as a user name and the value as a list of key=value pairs of configuration settings specific for this user. Example: .IP .nf \f[C] user1 = pool_mode=session \f[R] .fi .PP Only a few settings are available here. .PP Note that when \f[C]auth_file\f[R] is configured, if a user is defined in this section but not listed in \f[C]auth_file\f[R], pgBouncer will attempt to use \f[C]auth_query\f[R] to find a password for that user if \f[C]auth_user\f[R] is set. If \f[C]auth_user\f[R] is not set, pgBouncer will pretend the user exists and fail to return \[lq]no such user\[rq] messages to the client, but neither will it accept any provided password. .SS pool_size .PP Set the maximum size of pools for all connections from this user. If not set, the database or \f[C]default_pool_size\f[R] is used. .SS reserve_pool_size .PP Set the number of additional connections to allow to a pool for this user. If not set, the database configuration or the global \f[C]reserve_pool_size\f[R] is used. .SS pool_mode .PP Set the pool mode to be used for all connections from this user. If not set, the database or default \f[C]pool_mode\f[R] is used. .SS max_user_connections .PP Configure a maximum for the user of server connections (i.e.\ all pools with the user will not have more than this many server connections). .SS query_timeout .PP Set the maximum number of seconds that a user query can run for. If set this timeout overrides the server level query_timeout described above. .SS idle_transaction_timeout .PP Set the maximum number of seconds that a user can have an idle transaction open. If set this timeout overides the server level idle_transaction_timeout described above. .SS client_idle_timeout .PP Set the maximum amount of time in seconds that a client is allowed to idly connect to the pgbouncer instance. If set this timeout overrides the server level client_idle_timeout described above. .PP Please note that this is a potentially dangeous timeout. .SS max_user_client_connections .PP Configure a maximum for the user of client connections. This is the user equivalent ofthe max_client_conn setting. .SH SECTION [PEERS] .PP The section \f[C][peers]\f[R] defines the peers that PgBouncer can forward cancellation requests to and where those cancellation requests will be routed. .PP PgBouncer processes can be peered together in a group by defining a \f[C]peer_id\f[R] value and a \f[C][peers]\f[R] section in the configs of all the PgBouncer processes. These PgBouncer processes can then forward cancellations requests to the process that it originated from. This is needed to make cancellations work when multiple PgBouncer processes (possibly on different servers) are behind the same TCP load balancer. Cancellation requests are sent over different TCP connections than the query they are cancelling, so a TCP load balancer might send the cancellation request connection to a different process than the one that it was meant for. By peering them these cancellation requests eventually end up at the right process. A more in-depth explanation is provided in this recording of a conference talk (https://www.youtube.com/watch?v=X-nCHcZ6vQU). .PP The section contains key=value lines like .IP .nf \f[C] peer_id = connection string \f[R] .fi .PP Where the key will be taken as a \f[C]peer_id\f[R] and the value as a connection string, consisting of key=value pairs of connection parameters, described below (similar to libpq, but the actual libpq is not used and the set of available features is different). Example: .IP .nf \f[C] 1 = host=host1.example.com 2 = host=/tmp/pgbouncer-2 port=5555 \f[R] .fi .PP Note 1: For peering to work, the \f[C]peer_id\f[R] of each PgBouncer process in the group must be unique within the peered group. And the \f[C][peers]\f[R] section should contain entries for each of those peer ids. An example can be found in the examples section of these docs. It \f[B]is\f[R] allowed, but not necessary, for the \f[C][peers]\f[R] section to contain the \f[C]peer_id\f[R] of the PgBouncer that the config is for. Such an entry will be ignored, but it is allowed to config management easy. Because it allows using the exact same \f[C][peers]\f[R] section for multiple configs. .PP Note 2: Cross-version peering is supported as long as all peers are on the same side of the v1.21.0 version boundary. In v1.21.0 some breaking changes were made in how we encode the cancellation tokens that made them incompatible with the ones created by earlier versions. .SS host .PP Host name or IP address to connect to. Host names are resolved at connection time, the result is cached per \f[C]dns_max_ttl\f[R] parameter. If DNS returns several results, they are used in a round-robin manner. But in general it\[cq]s not recommended to use a hostname that resolves to multiple IPs, because then the cancel request might still be forwarded to the wrong node and it would need to be forwarded again (which is only allowed up to three times). .PP If the value begins with \f[C]/\f[R], then a Unix socket in the file-system namespace is used. If the value begins with \f[C]\[at]\f[R], then a Unix socket in the abstract namespace is used. .PP Examples: .IP .nf \f[C] host=localhost host=127.0.0.1 host=2001:0db8:85a3:0000:0000:8a2e:0370:7334 host=/var/run/pgbouncer-1 \f[R] .fi .SS port .PP Default: 6432 .SS pool_size .PP Set the maximum number of cancel requests that can be in flight to the peer at the same time. It\[cq]s quite normal for cancel requests to arrive in bursts, e.g. when the backing Postgres server slow or down. So it\[cq]s important for \f[C]pool_size\f[R] to not be so low that it cannot handle these bursts. .PP If not set, the \f[C]default_pool_size\f[R] is used. .SH INCLUDE DIRECTIVE .PP The PgBouncer configuration file can contain include directives, which specify another configuration file to read and process. This allows splitting the configuration file into physically separate parts. The include directives look like this: .IP .nf \f[C] %include filename \f[R] .fi .PP If the file name is not an absolute path, it is taken as relative to the current working directory. .SH AUTHENTICATION FILE FORMAT .PP This section describes the format of the file specified by the \f[C]auth_file\f[R] setting. It is a text file in the following format: .IP .nf \f[C] \[dq]username1\[dq] \[dq]password\[dq] ... \[dq]username2\[dq] \[dq]md5abcdef012342345\[dq] ... \[dq]username2\[dq] \[dq]SCRAM-SHA-256$:$:\[dq] \f[R] .fi .PP There should be at least 2 fields, surrounded by double quotes. The first field is the user name and the second is either a plain-text, a MD5-hashed password, or a SCRAM secret. PgBouncer ignores the rest of the line. Double quotes in a field value can be escaped by writing two double quotes. .PP PostgreSQL MD5-hashed password format: .IP .nf \f[C] \[dq]md5\[dq] + md5(password + username) \f[R] .fi .PP So user \f[C]admin\f[R] with password \f[C]1234\f[R] will have MD5-hashed password \f[C]md545f2603610af569b6155c45067268c6b\f[R]. .PP PostgreSQL SCRAM secret format: .IP .nf \f[C] SCRAM-SHA-256$:$: \f[R] .fi .PP See the PostgreSQL documentation and RFC 5803 for details on this. .PP The passwords or secrets stored in the authentication file serve two purposes. First, they are used to verify the passwords of incoming client connections, if a password-based authentication method is configured. Second, they are used as the passwords for outgoing connections to the backend server, if the backend server requires password-based authentication (unless the password is specified directly in the database\[cq]s connection string). .SS Limitations .PP If the password is stored in plain text, it can be used for any password-based authentication used in the backend server; plain text, MD5 or SCRAM (see for details). .PP MD5-hashed passwords can be used if backend server uses MD5 authentication (or specific users have MD5-hashed passwords). .PP SCRAM secrets can only be used for logging into a server if the client authentication also uses SCRAM, the PgBouncer database definition does not specify a user name, and the SCRAM secrets are identical in PgBouncer and the PostgreSQL server (same salt and iterations, not merely the same password). This is due to an inherent security property of SCRAM: The stored SCRAM secret cannot by itself be used for deriving login credentials. .PP The authentication file can be written by hand, but it\[cq]s also useful to generate it from some other list of users and passwords. See \f[C]./etc/mkauth.py\f[R] for a sample script to generate the authentication file from the \f[C]pg_authid\f[R] system table. Alternatively, use \f[C]auth_query\f[R] instead of \f[C]auth_file\f[R] to avoid having to maintain a separate authentication file. .SS Note on managed servers .PP If the backend server is configured to use SCRAM password authentication PgBouncer cannot successfully authenticate if it does not know either a) user password in plain text or b) corresponding SCRAM secret. .PP Some cloud providers (i.e.\ AWS RDS) prohibit access to PostgreSQL sensitive system tables for fetching passwords. Even for the most privileged user (i.e.\ member of rds_superuser) the \f[C]select * from pg_authid\f[R]; returns the \f[C]ERROR: permission denied for table pg_authid.\f[R] That is a known behaviour (blog (https://aws.amazon.com/blogs/database/best-practices-for-migrating-postgresql-databases-to-amazon-rds-and-amazon-aurora/)). .PP Therefore, fetching an existing SCRAM secret once it has been stored in a managed server is impossible which makes it hard to configure PgBouncer to use the same SCRAM secret. Nevertheless, SCRAM secret can still be configured and used on both sides using the following trick: .PP Generate SCRAM secret for arbitrary password with a tool that is capable of printing out the secret. For example \f[C]psql --echo-hidden\f[R] and the command \f[C]\[rs]password\f[R] prints out the SCRAM secret to the console before sending it over to the server. .IP .nf \f[C] $ psql --echo-hidden postgres=# \[rs]password Enter new password for user \[dq]\[dq]: Enter it again: ********* QUERY ********** ALTER USER PASSWORD \[aq]SCRAM-SHA-256$:$:\[aq] ************************** \f[R] .fi .PP Note down the SCRAM secret from the QUERY and set it in PgBouncer\[cq]s \f[C]userlist.txt\f[R]. .PP If you used a tool other than \f[C]psql --echo-hidden\f[R] then you need to set the SCRAM secret also in the server (you can use \f[C]alter role password \[aq]\[aq]\f[R] for that). .SH HBA FILE FORMAT .PP The location of the HBA file is specified by the setting \f[C]auth_hba_file\f[R]. It is only used if \f[C]auth_type\f[R] is set to \f[C]hba\f[R]. .PP The file follows the format of the PostgreSQL \f[C]pg_hba.conf\f[R] file (see ). .IP \[bu] 2 Supported record types: \f[C]local\f[R], \f[C]host\f[R], \f[C]hostssl\f[R], \f[C]hostnossl\f[R]. .IP \[bu] 2 Database field: Supports \f[C]all\f[R], \f[C]replication\f[R], \f[C]sameuser\f[R], \f[C]\[at]file\f[R], multiple names. Not supported: \f[C]samerole\f[R], \f[C]samegroup\f[R]. .IP \[bu] 2 User name field: Supports \f[C]all\f[R], \f[C]\[at]file\f[R], multiple names. Not supported: \f[C]+groupname\f[R]. .IP \[bu] 2 Address field: Supports \f[C]all\f[R], IPv4, IPv6. Not supported: \f[C]samehost\f[R], \f[C]samenet\f[R], DNS names, domain prefixes. .IP \[bu] 2 Auth-method field: Only methods supported by PgBouncer\[cq]s \f[C]auth_type\f[R] are supported, plus \f[C]peer\f[R] and \f[C]reject\f[R], but except \f[C]any\f[R] and \f[C]pam\f[R], which only work globally. .IP \[bu] 2 User name map (\f[C]map=\f[R]) parameter is supported when \f[C]auth_type\f[R] is \f[C]cert\f[R] or \f[C]peer\f[R]. .SH IDENT MAP FILE FORMAT .PP The location of the ident map file is specified by the setting \f[C]auth_ident_file\f[R]. It is only loaded if \f[C]auth_type\f[R] is set to \f[C]hba\f[R]. .PP The file format is a simplified variation of the PostgreSQL ident map file (see ). .IP \[bu] 2 Supported lines are only of the form \f[C]map-name system-username database-username\f[R]. .IP \[bu] 2 There is no support for including file/directory. .IP \[bu] 2 System-username field: Not supported: regular expressions. .IP \[bu] 2 Database-username field: Supports \f[C]all\f[R] or a single postgres user name. Not supported: \f[C]+groupname\f[R], regular expressions. .SH EXAMPLES .PP Small example configuration: .IP .nf \f[C] [databases] template1 = host=localhost dbname=template1 auth_user=someuser [pgbouncer] pool_mode = session listen_port = 6432 listen_addr = localhost auth_type = md5 auth_file = users.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser stats_users = stat_collector \f[R] .fi .PP Database examples: .IP .nf \f[C] [databases] ; foodb over Unix socket foodb = ; redirect bardb to bazdb on localhost bardb = host=localhost dbname=bazdb ; access to destination database will go with single user forcedb = host=localhost port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO \f[R] .fi .PP Example of a secure function for \f[C]auth_query\f[R]: .IP .nf \f[C] CREATE OR REPLACE FUNCTION pgbouncer.user_lookup(in i_username text, out uname text, out phash text) RETURNS record AS $$ BEGIN SELECT rolname, CASE WHEN rolvaliduntil < now() THEN NULL ELSE rolpassword END FROM pg_authid WHERE rolname=i_username AND rolcanlogin INTO uname, phash; RETURN; END; $$ LANGUAGE plpgsql SECURITY DEFINER -- Set a secure search_path: trusted schema(s), then \[aq]pg_temp\[aq]. SET search_path = pg_catalog, pg_temp; REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public, pgbouncer; GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer; \f[R] .fi .PP Example configs for 2 peered PgBouncer processes to create a multi-core PgBouncer setup using \f[C]so_reuseport\f[R]. The config for the first process: .IP .nf \f[C] [databases] postgres = host=localhost dbname=postgres [peers] 1 = host=/tmp/pgbouncer1 2 = host=/tmp/pgbouncer2 [pgbouncer] listen_addr=127.0.0.1 auth_file=auth_file.conf so_reuseport=1 unix_socket_dir=/tmp/pgbouncer1 peer_id=1 \f[R] .fi .PP The config for the second process: .IP .nf \f[C] [databases] postgres = host=localhost dbname=postgres [peers] 1 = host=/tmp/pgbouncer1 2 = host=/tmp/pgbouncer2 [pgbouncer] listen_addr=127.0.0.1 auth_file=auth_file.conf so_reuseport=1 ; only unix_socket_dir and peer_id are different unix_socket_dir=/tmp/pgbouncer2 peer_id=2 \f[R] .fi .SH SEE ALSO .PP pgbouncer(1) - man page for general usage, console commands .PP pgbouncer-1.24.1/doc/Makefile0000644000175000000000000000125614777762222012720 00000000000000-include ../config.mak manpages = pgbouncer.1 pgbouncer.5 dist_man_MANS = $(manpages) EXTRA_DIST = config.md usage.md Makefile $(manpages) \ frag-config-man.md frag-usage-man.md \ filter.py CLEANFILES = pgbouncer_1.md pgbouncer_5.md # make maintainer-clean removes those MAINTAINERCLEANFILES = $(manpages) SUBLOC = doc abs_top_srcdir ?= $(CURDIR)/.. include $(abs_top_srcdir)/lib/mk/antimake.mk export PACKAGE_VERSION pgbouncer.%: pgbouncer_%.md $(PANDOC) -s -t man -o $@ $< pgbouncer_1.md: filter.py frag-usage-man.md usage.md $(PYTHON) $^ >$@ pgbouncer_5.md: filter.py frag-config-man.md config.md $(PYTHON) $^ >$@ web: $(MAKE) -C ../../pgbouncer.github.io pgbouncer-1.24.1/doc/pgbouncer.10000664000175000017500000006760014777762332013354 00000000000000.\" Automatically generated by Pandoc 2.9.2.1 .\" .TH "PGBOUNCER" "1" "" "1.24.1" "Databases" .hy .SH NAME .PP pgbouncer - lightweight connection pooler for PostgreSQL .SH SYNOPSIS .IP .nf \f[C] pgbouncer [-d][-R][-v][-u user] pgbouncer -V|-h \f[R] .fi .PP On Windows, the options are: .IP .nf \f[C] pgbouncer.exe [-v][-u user] pgbouncer.exe -V|-h \f[R] .fi .PP Additional options for setting up a Windows service: .IP .nf \f[C] pgbouncer.exe --regservice pgbouncer.exe --unregservice \f[R] .fi .SH DESCRIPTION .PP \f[B]pgbouncer\f[R] is a PostgreSQL connection pooler. Any target application can be connected to \f[B]pgbouncer\f[R] as if it were a PostgreSQL server, and \f[B]pgbouncer\f[R] will create a connection to the actual server, or it will reuse one of its existing connections. .PP The aim of \f[B]pgbouncer\f[R] is to lower the performance impact of opening new connections to PostgreSQL. .PP In order not to compromise transaction semantics for connection pooling, \f[B]pgbouncer\f[R] supports several types of pooling when rotating connections: .TP Session pooling Most polite method. When a client connects, a server connection will be assigned to it for the whole duration the client stays connected. When the client disconnects, the server connection will be put back into the pool. This is the default method. .TP Transaction pooling A server connection is assigned to a client only during a transaction. When PgBouncer notices that transaction is over, the server connection will be put back into the pool. .TP Statement pooling Most aggressive method. The server connection will be put back into the pool immediately after a query completes. Multi-statement transactions are disallowed in this mode as they would break. .PP The administration interface of \f[B]pgbouncer\f[R] consists of some new \f[C]SHOW\f[R] commands available when connected to a special \[lq]virtual\[rq] database \f[B]pgbouncer\f[R]. .SH QUICK-START .PP Basic setup and usage is as follows. .IP "1." 3 Create a pgbouncer.ini file. Details in \f[B]pgbouncer(5)\f[R]. Simple example: .RS 4 .IP .nf \f[C] [databases] template1 = host=localhost port=5432 dbname=template1 [pgbouncer] listen_port = 6432 listen_addr = localhost auth_type = md5 auth_file = userlist.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser \f[R] .fi .RE .IP "2." 3 Create a \f[C]userlist.txt\f[R] file that contains the users allowed in: .RS 4 .IP .nf \f[C] \[dq]someuser\[dq] \[dq]same_password_as_in_server\[dq] \f[R] .fi .RE .IP "3." 3 Launch \f[B]pgbouncer\f[R]: .RS 4 .IP .nf \f[C] $ pgbouncer -d pgbouncer.ini \f[R] .fi .RE .IP "4." 3 Have your application (or the \f[B]psql\f[R] client) connect to \f[B]pgbouncer\f[R] instead of directly to the PostgreSQL server: .RS 4 .IP .nf \f[C] $ psql -p 6432 -U someuser template1 \f[R] .fi .RE .IP "5." 3 Manage \f[B]pgbouncer\f[R] by connecting to the special administration database \f[B]pgbouncer\f[R] and issuing \f[C]SHOW HELP;\f[R] to begin: .RS 4 .IP .nf \f[C] $ psql -p 6432 -U someuser pgbouncer pgbouncer=# SHOW HELP; NOTICE: Console usage DETAIL: SHOW [HELP|CONFIG|DATABASES|FDS|POOLS|CLIENTS|SERVERS|SOCKETS|LISTS|VERSION|...] SET key = arg RELOAD PAUSE SUSPEND RESUME SHUTDOWN [...] \f[R] .fi .RE .IP "6." 3 If you made changes to the pgbouncer.ini file, you can reload it with: .RS 4 .IP .nf \f[C] pgbouncer=# RELOAD; \f[R] .fi .RE .SH COMMAND LINE SWITCHES .TP \f[B]\f[CB]-d\f[B]\f[R], \f[B]\f[CB]--daemon\f[B]\f[R] Run in the background. Without it, the process will run in the foreground. .RS .PP In daemon mode, setting \f[C]pidfile\f[R] as well as \f[C]logfile\f[R] or \f[C]syslog\f[R] is required. No log messages will be written to stderr after going into the background. .PP Note: Does not work on Windows; \f[B]pgbouncer\f[R] need to run as service there. .RE .TP \f[B]\f[CB]-R\f[B]\f[R], \f[B]\f[CB]--reboot\f[B]\f[R] \f[B]DEPRECATED: Instead of this option use a rolling restart with multiple pgbouncer processes listening on the same port using so_reuseport instead\f[R] Do an online restart. That means connecting to the running process, loading the open sockets from it, and then using them. If there is no active process, boot normally. Note: Works only if OS supports Unix sockets and the \f[C]unix_socket_dir\f[R] is not disabled in configuration. Does not work on Windows. Does not work with TLS connections, they are dropped. .TP \f[B]\f[CB]-u\f[B]\f[R] \f[I]USERNAME\f[R], \f[B]\f[CB]--user=\f[B]\f[R]\f[I]USERNAME\f[R] Switch to the given user on startup. .TP \f[B]\f[CB]-v\f[B]\f[R], \f[B]\f[CB]--verbose\f[B]\f[R] Increase verbosity. Can be used multiple times. .TP \f[B]\f[CB]-q\f[B]\f[R], \f[B]\f[CB]--quiet\f[B]\f[R] Be quiet: do not log to stderr. This does not affect logging verbosity, only that stderr is not to be used. For use in init.d scripts. .TP \f[B]\f[CB]-V\f[B]\f[R], \f[B]\f[CB]--version\f[B]\f[R] Show version. .TP \f[B]\f[CB]-h\f[B]\f[R], \f[B]\f[CB]--help\f[B]\f[R] Show short help. .TP \f[B]\f[CB]--regservice\f[B]\f[R] Win32: Register pgbouncer to run as Windows service. The \f[B]service_name\f[R] configuration parameter value is used as the name to register under. .TP \f[B]\f[CB]--unregservice\f[B]\f[R] Win32: Unregister Windows service. .SH ADMIN CONSOLE .PP The console is available by connecting as normal to the database \f[B]pgbouncer\f[R]: .IP .nf \f[C] $ psql -p 6432 pgbouncer \f[R] .fi .PP Only users listed in the configuration parameters \f[B]admin_users\f[R] or \f[B]stats_users\f[R] are allowed to log in to the console. (Except when \f[C]auth_type=any\f[R], then any user is allowed in as a stats_user.) .PP Additionally, the user name \f[B]pgbouncer\f[R] is allowed to log in without password, if the login comes via the Unix socket and the client has same Unix user UID as the running process. .PP The admin console currently only supports the simple query protocol. Some drivers use the extended query protocol for all commands; these drivers will not work for this. .SS Show commands .PP The \f[B]SHOW\f[R] commands output information. Each command is described below. .SS SHOW STATS .PP Shows statistics. In this and related commands, the total figures are since process start, the averages are updated every \f[C]stats_period\f[R]. .TP database Statistics are presented per database. .TP total_xact_count Total number of SQL transactions pooled by \f[B]pgbouncer\f[R]. .TP total_query_count Total number of SQL commands pooled by \f[B]pgbouncer\f[R]. .TP total_server_assignment_count Total times a server was assigned to a client .TP total_received Total volume in bytes of network traffic received by \f[B]pgbouncer\f[R]. .TP total_sent Total volume in bytes of network traffic sent by \f[B]pgbouncer\f[R]. .TP total_xact_time Total number of microseconds spent by \f[B]pgbouncer\f[R] when connected to PostgreSQL in a transaction, either idle in transaction or executing queries. .TP total_query_time Total number of microseconds spent by \f[B]pgbouncer\f[R] when actively connected to PostgreSQL, executing queries. .TP total_wait_time Time spent by clients waiting for a server, in microseconds. Updated when a client connection is assigned a backend connection. .TP total_client_parse_count Total number of prepared statements created by clients. Only applicable in named prepared statement tracking mode, see \f[C]max_prepared_statements\f[R]. .TP total_server_parse_count Total number of prepared statements created by \f[B]pgbouncer\f[R] on a server. Only applicable in named prepared statement tracking mode, see \f[C]max_prepared_statements\f[R]. .TP total_bind_count Total number of prepared statements readied for execution by clients and forwarded to PostgreSQL by \f[B]pgbouncer\f[R]. Only applicable in named prepared statement tracking mode, see \f[C]max_prepared_statements\f[R]. .TP avg_xact_count Average transactions per second in last stat period. .TP avg_query_count Average queries per second in last stat period. .TP avg_server_assignment_count Average number of times a server as assigned to a client per second in the last stat period. .TP avg_recv Average received (from clients) bytes per second. .TP avg_sent Average sent (to clients) bytes per second. .TP avg_xact_time Average transaction duration, in microseconds. .TP avg_query_time Average query duration, in microseconds. .TP avg_wait_time Time spent by clients waiting for a server, in microseconds (average of the wait times for clients assigned a backend during the current \f[C]stats_period\f[R]). .TP avg_client_parse_count Average number of prepared statements created by clients. Only applicable in named prepared statement tracking mode, see \f[C]max_prepared_statements\f[R]. .TP avg_server_parse_count Average number of prepared statements created by \f[B]pgbouncer\f[R] on a server. Only applicable in named prepared statement tracking mode, see \f[C]max_prepared_statements\f[R]. .TP avg_bind_count Average number of prepared statements readied for execution by clients and forwarded to PostgreSQL by \f[B]pgbouncer\f[R]. Only applicable in named prepared statement tracking mode, see \f[C]max_prepared_statements\f[R]. .SS SHOW STATS_TOTALS .PP Subset of \f[B]SHOW STATS\f[R] showing the total values (\f[B]total_\f[R]). .SS SHOW STATS_AVERAGES .PP Subset of \f[B]SHOW STATS\f[R] showing the average values (\f[B]avg_\f[R]). .SS SHOW TOTALS .PP Like \f[B]SHOW STATS\f[R] but aggregated across all databases. .SS SHOW SERVERS .TP type S, for server. .TP user User name \f[B]pgbouncer\f[R] uses to connect to server. .TP database Database name. .TP replication If server connection uses replication. Can be \f[B]none\f[R], \f[B]logical\f[R] or \f[B]physical\f[R]. .TP state State of the pgbouncer server connection, one of \f[B]active\f[R], \f[B]idle\f[R], \f[B]used\f[R], \f[B]tested\f[R], \f[B]new\f[R], \f[B]active_cancel\f[R], \f[B]being_canceled\f[R]. .TP addr IP address of PostgreSQL server. .TP port Port of PostgreSQL server. .TP local_addr Connection start address on local machine. .TP local_port Connection start port on local machine. .TP connect_time When the connection was made. .TP request_time When last request was issued. .TP wait Not used for server connections. .TP wait_us Not used for server connections. .TP close_needed 1 if the connection will be closed as soon as possible, because a configuration file reload or DNS update changed the connection information or \f[B]RECONNECT\f[R] was issued. .TP ptr Address of internal object for this connection. .TP link Address of client connection the server is paired with. .TP remote_pid PID of backend server process. In case connection is made over Unix socket and OS supports getting process ID info, its OS PID. Otherwise it\[cq]s extracted from cancel packet the server sent, which should be the PID in case the server is PostgreSQL, but it\[cq]s a random number in case the server it is another PgBouncer. .TP tls A string with TLS connection information, or empty if not using TLS. .TP application_name A string containing the \f[C]application_name\f[R] set on the linked client connection, or empty if this is not set, or if there is no linked connection. .TP prepared_statements The amount of prepared statements that are prepared on the server. This number is limited by the \f[C]max_prepared_statements\f[R] setting. .TP id Unique ID for server. .SS SHOW CLIENTS .TP type C, for client. .TP user Client connected user. .TP database Database name. .TP replication If client connection uses replication. Can be \f[B]none\f[R], \f[B]logical\f[R] or \f[B]physical\f[R]. .TP state State of the client connection, one of \f[B]active\f[R], \f[B]waiting\f[R], \f[B]active_cancel_req\f[R], or \f[B]waiting_cancel_req\f[R]. .TP addr IP address of client. .TP port Source port of client. .TP local_addr Connection end address on local machine. .TP local_port Connection end port on local machine. .TP connect_time Timestamp of connect time. .TP request_time Timestamp of latest client request. .TP wait Current waiting time in seconds. .TP wait_us Microsecond part of the current waiting time. .TP close_needed not used for clients .TP ptr Address of internal object for this connection. .TP link Address of server connection the client is paired with. .TP remote_pid Process ID, in case client connects over Unix socket and OS supports getting it. .TP tls A string with TLS connection information, or empty if not using TLS. .TP application_name A string containing the \f[C]application_name\f[R] set by the client for this connection, or empty if this was not set. .TP prepared_statements The amount of prepared statements that the client has prepared .TP id Unique ID for client. .SS SHOW POOLS .PP A new pool entry is made for each couple of (database, user). .TP database Database name. .TP user User name. .TP cl_active Client connections that are either linked to server connections or are idle with no queries waiting to be processed. .TP cl_waiting Client connections that have sent queries but have not yet got a server connection. .TP cl_active_cancel_req Client connections that have forwarded query cancellations to the server and are waiting for the server response. .TP cl_waiting_cancel_req Client connections that have not forwarded query cancellations to the server yet. .TP sv_active Server connections that are linked to a client. .TP sv_active_cancel Server connections that are currently forwarding a cancel request. .TP sv_being_canceled Servers that normally could become idle but are waiting to do so until all in-flight cancel requests have completed that were sent to cancel a query on this server. .TP sv_idle Server connections that are unused and immediately usable for client queries. .TP sv_used Server connections that have been idle for more than \f[C]server_check_delay\f[R], so they need \f[C]server_check_query\f[R] to run on them before they can be used again. .TP sv_tested Server connections that are currently running either \f[C]server_reset_query\f[R] or \f[C]server_check_query\f[R]. .TP sv_login Server connections currently in the process of logging in. .TP maxwait How long the first (oldest) client in the queue has waited, in seconds. If this starts increasing, then the current pool of servers does not handle requests quickly enough. The reason may be either an overloaded server or just too small of a \f[B]pool_size\f[R] setting. .TP maxwait_us Microsecond part of the maximum waiting time. .TP pool_mode The pooling mode in use. .TP load_balance_hosts The load_balance_hosts in use if the pool\[cq]s host contains a comma-separated list. .SS SHOW PEER_POOLS .PP A new peer_pool entry is made for each configured peer. .TP database ID of the configured peer entry. .TP cl_active_cancel_req Client connections that have forwarded query cancellations to the server and are waiting for the server response. .TP cl_waiting_cancel_req Client connections that have not forwarded query cancellations to the server yet. .TP sv_active_cancel Server connections that are currently forwarding a cancel request. .TP sv_login Server connections currently in the process of logging in. .SS SHOW LISTS .PP Show following internal information, in columns (not rows): .TP databases Count of databases. .TP users Count of users. .TP pools Count of pools. .TP free_clients Count of free clients. These are clients that are disconnected, but PgBouncer keeps the memory around that was allocated for them so it can be reused for a future clients to avoid allocations. .TP used_clients Count of used clients. .TP login_clients Count of clients in \f[B]login\f[R] state. .TP free_servers Count of free servers. These are servers that are disconnected, but PgBouncer keeps the memory around that was allocated for them so it can be reused for a future servers to avoid allocations. .TP used_servers Count of used servers. .TP dns_names Count of DNS names in the cache. .TP dns_zones Count of DNS zones in the cache. .TP dns_queries Count of in-flight DNS queries. .TP dns_pending not used .SS SHOW USERS .TP name The user name .TP pool_size The user\[cq]s override pool_size. or NULL if not set. .TP reserve_pool_size The user\[cq]s override reserve_pool_size. or NULL if not set. .TP pool_mode The user\[cq]s override pool_mode, or NULL if not set. .TP max_user_connections The user\[cq]s max_user_connections setting. If this setting is not set for this specific user, then the default value will be displayed. .TP current_connections Current number of server connections that this user has open to all servers. .TP max_user_client_connections The user\[cq]s max_user_client_connections setting. If this setting is not set for this specific user, then the default value will be displayed. .TP current_client_connections Current number of client connections that this user has open to pgbouncer. .SS SHOW DATABASES .TP name Name of configured database entry. .TP host Host pgbouncer connects to. .TP port Port pgbouncer connects to. .TP database Actual database name pgbouncer connects to. .TP force_user When the user is part of the connection string, the connection between pgbouncer and PostgreSQL is forced to the given user, whatever the client user. .TP pool_size Maximum number of server connections. .TP min_pool_size Minimum number of server connections. .TP reserve_pool_size Maximum number of additional connections for this database. .TP server_lifetime The maximum lifetime of a server connection for this database .TP pool_mode The database\[cq]s override pool_mode, or NULL if the default will be used instead. .TP load_balance_hosts The database\[cq]s load_balance_hosts if the host contains a comma-separated list. .TP max_connections Maximum number of allowed server connections for this database, as set by \f[B]max_db_connections\f[R], either globally or per database. .TP current_connections Current number of server connections for this database. .TP max_client_connections Maximum number of allowed client connections for this pgbouncer instance, as set by max_db_client_connections per database. .TP current_client_connections Current number of client connections for this database. .TP paused 1 if this database is currently paused, else 0. .TP disabled 1 if this database is currently disabled, else 0. .SS SHOW PEERS .TP peer_id ID of the configured peer entry. .TP host Host pgbouncer connects to. .TP port Port pgbouncer connects to. .TP pool_size Maximum number of server connections that can be made to this peer .SS SHOW FDS .PP Internal command - shows list of file descriptors in use with internal state attached to them. .PP When the connected user has the user name \[lq]pgbouncer\[rq], connects through the Unix socket and has same the UID as the running process, the actual FDs are passed over the connection. This mechanism is used to do an online restart. Note: This does not work on Windows. .PP This command also blocks the internal event loop, so it should not be used while PgBouncer is in use. .TP fd File descriptor numeric value. .TP task One of \f[B]pooler\f[R], \f[B]client\f[R] or \f[B]server\f[R]. .TP user User of the connection using the FD. .TP database Database of the connection using the FD. .TP addr IP address of the connection using the FD, \f[B]unix\f[R] if a Unix socket is used. .TP port Port used by the connection using the FD. .TP cancel Cancel key for this connection. .TP link fd for corresponding server/client. NULL if idle. .SS SHOW SOCKETS, SHOW ACTIVE_SOCKETS .PP Shows low-level information about sockets or only active sockets. This includes the information shown under \f[B]SHOW CLIENTS\f[R] and \f[B]SHOW SERVERS\f[R] as well as other more low-level information. .SS SHOW CONFIG .PP Show the current configuration settings, one per row, with the following columns: .TP key Configuration variable name .TP value Configuration value .TP default Configuration default value .TP changeable Either \f[B]yes\f[R] or \f[B]no\f[R], shows if the variable can be changed while running. If \f[B]no\f[R], the variable can be changed only at boot time. Use \f[B]SET\f[R] to change a variable at run time. .SS SHOW MEM .PP Shows low-level information about the current sizes of various internal memory allocations. The information presented is subject to change. .SS SHOW DNS_HOSTS .PP Show host names in DNS cache. .TP hostname Host name. .TP ttl How many seconds until next lookup. .TP addrs Comma separated list of addresses. .SS SHOW DNS_ZONES .PP Show DNS zones in cache. .TP zonename Zone name. .TP serial Current serial. .TP count Host names belonging to this zone. .SS SHOW VERSION .PP Show the PgBouncer version string. .SS SHOW STATE .PP Show the PgBouncer state settings. Current states are active, paused and suspended. .SS Process controlling commands .SS PAUSE [db] .PP PgBouncer tries to disconnect from all servers. Disconnecting each server connection waits for that server connection to be released according to the server pool\[cq]s pooling mode (in transaction pooling mode, the transaction must complete, in statement mode, the statement must complete, and in session pooling mode the client must disconnect). The command will not return before all server connections have been disconnected. To be used at the time of database restart. .PP If database name is given, only that database will be paused. .PP New client connections to a paused database will wait until \f[B]RESUME\f[R] is called. .SS DISABLE db .PP Reject all new client connections on the given database. .SS ENABLE db .PP Allow new client connections after a previous \f[B]DISABLE\f[R] command. .SS RECONNECT [db] .PP Close each open server connection for the given database, or all databases, after it is released (according to the pooling mode), even if its lifetime is not up yet. New server connections can be made immediately and will connect as necessary according to the pool size settings. .PP This command is useful when the server connection setup has changed, for example to perform a gradual switchover to a new server. It is \f[I]not\f[R] necessary to run this command when the connection string in pgbouncer.ini has been changed and reloaded (see \f[B]RELOAD\f[R]) or when DNS resolution has changed, because then the equivalent of this command will be run automatically. This command is only necessary if something downstream of PgBouncer routes the connections. .PP After this command is run, there could be an extended period where some server connections go to an old destination and some server connections go to a new destination. This is likely only sensible when switching read-only traffic between read-only replicas, or when switching between nodes of a multimaster replication setup. If all connections need to be switched at the same time, \f[B]PAUSE\f[R] is recommended instead. To close server connections without waiting (for example, in emergency failover rather than gradual switchover scenarios), also consider \f[B]KILL\f[R]. .SS KILL db .PP Immediately drop all client and server connections on given database. .PP New client connections to a killed database will wait until \f[B]RESUME\f[R] is called. .SS KILL_CLIENT id .PP Immediately kill specificed client connection along with any server connections for the given client. The client to kill, is identified by the \f[C]id\f[R] value that can be found using the \f[C]SHOW CLIENTS\f[R] command. .PP An example command will look something like \f[C]KILL_CLIENT 1234\f[R]. .SS SUSPEND .PP All socket buffers are flushed and PgBouncer stops listening for data on them. The command will not return before all buffers are empty. To be used at the time of PgBouncer online reboot. .PP New client connections to a suspended database will wait until \f[B]RESUME\f[R] is called. .SS RESUME [db] .PP Resume work from previous \f[B]KILL\f[R], \f[B]PAUSE\f[R], or \f[B]SUSPEND\f[R] command. .SS SHUTDOWN .PP The PgBouncer process will exit. .SS SHUTDOWN WAIT_FOR_SERVERS .PP Stop accepting new connections and shutdown after all servers are released. This is basically the same as issuing \f[B]PAUSE\f[R] and \f[B]SHUTDOWN\f[R], except that this also stops accepting new connections while waiting for the \f[B]PAUSE\f[R] as well as eagerly disconnecting clients that are waiting to receive a server connection. .SS SHUTDOWN WAIT_FOR_CLIENTS .PP Stop accepting new connections and shutdown the process once all existing clients have disconnected. This command can be used to do zero-downtime rolling restart of two PgBouncer processes using the following procedure: .IP "1." 3 Have two or more PgBouncer processes running on the same port using \f[C]so_reuseport\f[R] (configuring peering is recommended, but not required). To achieve zero downtime when restarting we\[cq]ll restart these processes one-by-one, thus leaving the others running to accept connections while one is being restarted. .IP "2." 3 Pick a process to restart first, let\[cq]s call it A. .IP "3." 3 Run \f[C]SHUTDOWN WAIT_FOR_CLIENTS\f[R] (or send \f[C]SIGTERM\f[R]) to process A. .IP "4." 3 Cause all clients to reconnect. Possibly by waiting some time until the client side pooler causes reconnects due to its \f[C]server_idle_timeout\f[R] (or similar config). Or if no client side pooler is used, possibly by restarting the clients. Once all clients have reconnected. Process A will exit automatically, because no clients are connected to it anymore. .IP "5." 3 Start process A again. .IP "6." 3 Repeat step 3, 4 and 5 for each of the remaining processes, one-by-one until you restarted all processes. .SS RELOAD .PP The PgBouncer process will reload its configuration files and update changeable settings. This includes the main configuration file as well as the files specified by the settings \f[C]auth_file\f[R] and \f[C]auth_hba_file\f[R]. .PP PgBouncer notices when a configuration file reload changes the connection parameters of a database definition. An existing server connection to the old destination will be closed when the server connection is next released (according to the pooling mode), and new server connections will immediately use the updated connection parameters. .SS WAIT_CLOSE [db] .PP Wait until all server connections, either of the specified database or of all databases, have cleared the \[lq]close_needed\[rq] state (see \f[B]SHOW SERVERS\f[R]). This can be called after a \f[B]RECONNECT\f[R] or \f[B]RELOAD\f[R] to wait until the respective configuration change has been fully activated, for example in switchover scripts. .SS Other commands .SS SET key = arg .PP Changes a configuration setting (see also \f[B]SHOW CONFIG\f[R]). For example: .IP .nf \f[C] SET log_connections = 1; SET server_check_query = \[aq]select 2\[aq]; \f[R] .fi .PP (Note that this command is run on the PgBouncer admin console and sets PgBouncer settings. A \f[B]SET\f[R] command run on another database will be passed to the PostgreSQL backend like any other SQL command.) .SS Signals .TP SIGHUP Reload config. Same as issuing the command \f[B]RELOAD\f[R] on the console. .TP SIGTERM Super safe shutdown. Wait for all existing clients to disconnect, but don\[cq]t accept new connections. This is the same as issuing \f[B]SHUTDOWN WAIT_FOR_CLIENTS\f[R] on the console. If this signal is received while there is already a shutdown in progress, then an \[lq]immediate shutdown\[rq] is triggered instead of a \[lq]super safe shutdown\[rq]. In PgBouncer versions earlier than 1.23.0, this signal would cause an \[lq]immediate shutdown\[rq]. .TP SIGINT Safe shutdown. Same as issuing \f[B]SHUTDOWN WAIT_FOR_SERVERS\f[R] on the console. If this signal is received while there is already a shutdown in progress, then an \[lq]immediate shutdown\[rq] is triggered instead of a \[lq]safe shutdown\[rq]. .TP SIGQUIT Immediate shutdown. Same as issuing \f[B]SHUTDOWN\f[R] on the console. .TP SIGUSR1 Same as issuing \f[B]PAUSE\f[R] on the console. .TP SIGUSR2 Same as issuing \f[B]RESUME\f[R] on the console. .SS Libevent settings .PP From the Libevent documentation: .RS .PP It is possible to disable support for epoll, kqueue, devpoll, poll or select by setting the environment variable EVENT_NOEPOLL, EVENT_NOKQUEUE, EVENT_NODEVPOLL, EVENT_NOPOLL or EVENT_NOSELECT, respectively. .PP By setting the environment variable EVENT_SHOW_METHOD, libevent displays the kernel notification method that it uses. .RE .SH SEE ALSO .PP pgbouncer(5) - man page of configuration settings descriptions .PP pgbouncer-1.24.1/include/0000755000000000000000000000000014777762567012211 500000000000000pgbouncer-1.24.1/include/client.h0000644000175000000000000000247714777762222013573 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) _MUSTCHECK; bool handle_auth_query_response(PgSocket *client, PktHdr *pkt); bool check_db_connection_count(PgSocket *client); bool check_user_connection_count(PgSocket *client); bool sending_auth_query(PgSocket *client); PgDatabase *prepare_auth_database(PgSocket *client) _MUSTCHECK; pgbouncer-1.24.1/include/pktbuf.h0000644000175000000000000001663614777762222013612 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Safe & easy creation of PostgreSQL packets. */ #include "common/protocol.h" typedef struct PktBuf PktBuf; struct PktBuf { uint8_t *buf; int buf_len; int write_pos; int pktlen_pos; int send_pos; struct event *ev; PgSocket *queued_dst; bool failed : 1; bool sending : 1; bool fixed_buf : 1; }; /* * pktbuf creation */ PktBuf *pktbuf_dynamic(int start_len) _MUSTCHECK; void pktbuf_static(PktBuf *buf, uint8_t *data, int len); void pktbuf_free(PktBuf *buf); void pktbuf_reset(struct PktBuf *pkt); struct PktBuf *pktbuf_temp(void); /* * sending */ bool pktbuf_send_immediate(PktBuf *buf, PgSocket *sk) _MUSTCHECK; bool pktbuf_send_queued(PktBuf *buf, PgSocket *sk) _MUSTCHECK; /* * low-level ops */ void pktbuf_start_packet(PktBuf *buf, int type); void pktbuf_put_char(PktBuf *buf, char val); void pktbuf_put_uint16(PktBuf *buf, uint16_t val); void pktbuf_put_uint32(PktBuf *buf, uint32_t val); void pktbuf_put_uint64(PktBuf *buf, uint64_t val); void pktbuf_put_string(PktBuf *buf, const char *str); void pktbuf_put_bytes(PktBuf *buf, const void *data, int len); void pktbuf_finish_packet(PktBuf *buf); #define pktbuf_written(buf) ((buf)->write_pos) /* * Packet writing */ void pktbuf_write_generic(PktBuf *buf, int type, const char *fmt, ...); void pktbuf_write_RowDescription(PktBuf *buf, const char *tupdesc, ...); void pktbuf_write_DataRow(PktBuf *buf, const char *tupdesc, ...); void pktbuf_write_ExtQuery(PktBuf *buf, const char *query, int nargs, ...); /* * Shortcuts for actual packets. */ #define pktbuf_write_ParameterStatus(buf, key, val) \ pktbuf_write_generic(buf, PqMsg_ParameterStatus, "ss", key, val) #define pktbuf_write_AuthenticationOk(buf) \ pktbuf_write_generic(buf, PqMsg_AuthenticationRequest, "i", 0) #define pktbuf_write_ReadyForQuery(buf) \ pktbuf_write_generic(buf, PqMsg_ReadyForQuery, "c", 'I') #define pktbuf_write_CommandComplete(buf, desc) \ pktbuf_write_generic(buf, PqMsg_CommandComplete, "s", desc) #define pktbuf_write_BackendKeyData(buf, key) \ pktbuf_write_generic(buf, PqMsg_BackendKeyData, "b", key, 8) #define pktbuf_write_CancelRequest(buf, key) \ pktbuf_write_generic(buf, PKT_CANCEL, "b", key, 8) #define pktbuf_write_NegotiateProtocolVersion( \ buf, \ unsupported_protocol_extensions_count, \ unsupported_protocol_extensions_bytes, \ unsupported_protocol_extensions_bytes_length) \ pktbuf_write_generic(buf, PqMsg_NegotiateProtocolVersion, "iib", PKT_STARTUP_V3, unsupported_protocol_extensions_count, unsupported_protocol_extensions_bytes, unsupported_protocol_extensions_bytes_length) #define pktbuf_write_PasswordMessage(buf, psw) \ pktbuf_write_generic(buf, PqMsg_PasswordMessage, "s", psw) #define pkgbuf_write_SASLInitialResponseMessage(buf, mech, cir) \ pktbuf_write_generic(buf, PqMsg_SASLInitialResponse, "sib", mech, strlen(cir), cir, strlen(cir)) #define pkgbuf_write_SASLResponseMessage(buf, cr) \ pktbuf_write_generic(buf, PqMsg_SASLResponse, "b", cr, strlen(cr)) #define pktbuf_write_Notice(buf, msg) \ pktbuf_write_generic(buf, PqMsg_NoticeResponse, "sscss", "SNOTICE", "C00000", 'M', msg, ""); #define pktbuf_write_SSLRequest(buf) \ pktbuf_write_generic(buf, PKT_SSLREQ, "") #define pktbuf_write_Parse(buf, stmt, query_and_parameters, query_and_parameters_len) \ pktbuf_write_generic(buf, PqMsg_Parse, "sb", stmt, query_and_parameters, query_and_parameters_len) #define pktbuf_write_ParseComplete(buf) \ pktbuf_write_generic(buf, PqMsg_ParseComplete, "") #define pktbuf_write_DescribeStmt(buf, stmt) \ pktbuf_write_generic(buf, PqMsg_Describe, "cs", 'S', stmt) #define pktbuf_write_CloseStmt(buf, stmt) \ pktbuf_write_generic(buf, PqMsg_Close, "cs", 'S', stmt) #define pktbuf_write_CloseComplete(buf) \ pktbuf_write_generic(buf, PqMsg_CloseComplete, "") /* * Shortcut for creating DataRow in memory. */ #define BUILD_DataRow(reslen, dst, dstlen, args...) do { \ PktBuf _buf; \ pktbuf_static(&_buf, dst, dstlen); \ pktbuf_write_DataRow(&_buf, ## args); \ reslen = _buf.failed ? -1 : _buf.write_pos; \ } while (0) /* * Shortcuts for immediate send of one packet. These should only be used when * server and client are not linked yet. Otherwise the data sent by these * functions might get sent right in the middle of a message send by the other * side of the link. * * NOTE: If the OS socket buffer is full then these functions return failure. So * only use these functions when that is so unlikely that we don't expect that * to ever happen in practice. */ #define SEND_wrap(buflen, pktfn, res, sk, args...) do { \ uint8_t _data[buflen]; PktBuf _buf; \ pktbuf_static(&_buf, _data, sizeof(_data)); \ pktfn(&_buf, ## args); \ res = pktbuf_send_immediate(&_buf, sk); \ } while (0) #define SEND_RowDescription(res, sk, args...) \ SEND_wrap(512, pktbuf_write_RowDescription, res, sk, ## args) #define SEND_generic(res, sk, args...) \ SEND_wrap(512, pktbuf_write_generic, res, sk, ## args) #define SEND_ReadyForQuery(res, sk) \ SEND_wrap(8, pktbuf_write_ReadyForQuery, res, sk) #define SEND_CancelRequest(res, sk, key) \ SEND_wrap(16, pktbuf_write_CancelRequest, res, sk, key) #define SEND_PasswordMessage(res, sk, psw) \ SEND_wrap(MAX_PASSWORD + 8, pktbuf_write_PasswordMessage, res, sk, psw) #define SEND_SASLInitialResponseMessage(res, sk, mech, cir) \ SEND_wrap(512, pkgbuf_write_SASLInitialResponseMessage, res, sk, mech, cir) #define SEND_SASLResponseMessage(res, sk, cr) \ SEND_wrap(512, pkgbuf_write_SASLResponseMessage, res, sk, cr) #define SEND_CloseComplete(res, sk) \ SEND_wrap(5, pktbuf_write_CloseComplete, res, sk) /* * Shortcuts for queueing one packet. These should only be used when the server * and client are linked. They wait with actually sending the data until a * any partially sent packet from the other side of the link has been fully * sent. */ #define QUEUE_wrap(buflen, pktfn, res, source, target, args...) do { \ uint8_t _data[buflen]; PktBuf _buf; \ pktbuf_static(&_buf, _data, sizeof(_data)); \ pktfn(&_buf, ## args); \ res = sbuf_queue_packet(&source->sbuf, &target->sbuf, &_buf); \ } while (0) #define QUEUE_ParseComplete(res, source, target) \ QUEUE_wrap(5, pktbuf_write_ParseComplete, res, source, target) #define QUEUE_DescribeStmt(res, source, target, statement) \ QUEUE_wrap(6 + MAX_SERVER_PREPARED_STMT_NAME, pktbuf_write_DescribeStmt, res, source, target, statement) #define QUEUE_CloseStmt(res, source, target, statement) \ QUEUE_wrap(6 + MAX_SERVER_PREPARED_STMT_NAME, pktbuf_write_CloseStmt, res, source, target, statement) #define QUEUE_CloseComplete(res, source, target) \ QUEUE_wrap(5, pktbuf_write_CloseComplete, res, source, target) void pktbuf_cleanup(void); pgbouncer-1.24.1/include/pam.h0000644000175000000000000000243514777762222013064 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * PAM support. */ /* Name of the service to be passed to PAM */ #define PGBOUNCER_PAM_SERVICE "pgbouncer" /* * Defines how many authentication requests can be placed to the waiting queue. * When the queue is full calls to pam_auth_begin() will block until there is * free space in the queue. */ #define PAM_REQUEST_QUEUE_SIZE 20 void pam_init(void); void pam_auth_begin(PgSocket *client, const char *passwd); int pam_poll(void); pgbouncer-1.24.1/include/messages.h0000644000175000000000000000445214777762222014117 00000000000000/* The parsed contents of a Parse ('P') packet. */ typedef struct PgParsePacket { unsigned int len; const char *name; size_t query_and_parameters_len; const char *query_and_parameters; } PgParsePacket; /* The parsed contents of a Bind ('B') packet. */ typedef struct PgBindPacket { unsigned int len; const char *portal; const char *name; } PgBindPacket; /* The parsed contents of a Describe ('D') packet. */ typedef struct PgDescribePacket { char type; const char *name; } PgDescribePacket; /* The parsed contents of a Close ('C') packet. */ typedef struct PgClosePacket { char type; const char *name; } PgClosePacket; typedef enum PreparedStatementAction { PS_IGNORE = 0, /* not related to prepared statements */ /* * It's a prepared statement related packet that we need to handle and * we have received enough data to handle it. */ PS_HANDLE, /* * It's a prepared statement related packet that we need to handle and * but it needs to be completely buffered into memory before we can * handle it. */ PS_HANDLE_FULL_PACKET, /* * We could not determine if the packet is related to prepared * statements that we need to handle. If we have not received all data, * then we should wait for more. If we already received all data, then * it's a broken packet. */ PS_INSPECT_FAILED, } PreparedStatementAction; PreparedStatementAction inspect_parse_packet(PgSocket *client, PktHdr *pkt); PreparedStatementAction inspect_bind_packet(PgSocket *client, PktHdr *pkt); PreparedStatementAction inspect_describe_or_close_packet(PgSocket *client, PktHdr *pkt); bool unmarshall_parse_packet(PgSocket *client, PktHdr *pkt, PgParsePacket *parse_packet_p); bool unmarshall_bind_packet(PgSocket *client, PktHdr *pkt, PgBindPacket *bind_packet_p); bool unmarshall_describe_packet(PgSocket *client, PktHdr *pkt, PgDescribePacket *describe_packet_p); bool unmarshall_close_packet(PgSocket *client, PktHdr *pkt, PgClosePacket *close_packet_p); bool is_close_named_statement_packet(PgClosePacket *close_packet); PktBuf *create_parse_packet(char *statement, PgPreparedStatement *parse_packet); PktBuf *create_parse_complete_packet(void); PktBuf *create_describe_packet(char *statement); PktBuf *create_close_packet(char *statement); PktBuf *create_close_complete_packet(void); void parse_packet_free(PgParsePacket *pkt); pgbouncer-1.24.1/include/util.h0000644000175000000000000000514714777762222013267 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * logging about specific socket */ int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen); #define slog_error(sk, args...) log_generic(LG_ERROR, sk, ## args) #define slog_warning(sk, args...) log_generic(LG_WARNING, sk, ## args) #define slog_info(sk, args...) log_generic(LG_INFO, sk, ## args) #define slog_debug(sk, args...) do { \ if (unlikely(cf_verbose > 0)) \ log_generic(LG_DEBUG, sk, ## args); \ } while (0) #define slog_noise(sk, args...) do { \ if (unlikely(cf_verbose > 1)) \ log_generic(LG_NOISE, sk, ## args); \ } while (0) /* * password tools */ #define MD5_PASSWD_LEN 35 bool pg_md5_encrypt(const char *part1, const char *part2, size_t p2len, char *dest) _MUSTCHECK; void get_random_bytes(uint8_t *dest, int len); const char *bin2hex(const uint8_t *src, unsigned srclen, char *dst, unsigned dstlen); bool tune_socket(int sock, bool is_unix) _MUSTCHECK; bool strlist_contains(const char *liststr, const char *str); void fill_remote_addr(PgSocket *sk, int fd, bool is_unix); void fill_local_addr(PgSocket *sk, int fd, bool is_unix); void rescue_timers(void); void safe_evtimer_add(struct event *ev, struct timeval *tv); /* log truncated strings */ #define safe_strcpy(dst, src, dstlen) do { \ size_t needed = strlcpy(dst, src, dstlen); \ if (unlikely(needed >= (dstlen))) \ log_warning("bug in %s:%d - string truncated", __FILE__, __LINE__); \ } while (0) /* parser setter and getter for parsing auth_dbname parameters using "cfparser" */ #define CF_AUTHDB { cf_set_authdb, cf_get_str } bool cf_set_authdb(struct CfValue *cv, const char *value); /* reserved database name checking */ bool check_reserved_database(const char *value); pgbouncer-1.24.1/include/server.h0000644000175000000000000000325214777762222013613 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; void kill_pool_logins(PgPool *pool, const char *sqlstate, const char *msg); const char * kill_pool_logins_server_error(PgPool *pool, PktHdr *errpkt); int connection_pool_mode(PgSocket *server) _MUSTCHECK; int probably_wrong_pool_pool_mode(PgPool *pool) _MUSTCHECK; int pool_pool_size(PgPool *pool) _MUSTCHECK; int pool_min_pool_size(PgPool *pool) _MUSTCHECK; usec_t pool_server_lifetime(PgPool *pool) _MUSTCHECK; int database_min_pool_size(PgDatabase *db) _MUSTCHECK; int pool_res_pool_size(PgPool *pool) _MUSTCHECK; int database_max_connections(PgDatabase *db) _MUSTCHECK; int database_max_client_connections(PgDatabase *db) _MUSTCHECK; int user_max_connections(PgGlobalUser *user) _MUSTCHECK; int user_client_max_connections(PgGlobalUser *user) _MUSTCHECK; pgbouncer-1.24.1/include/varcache.h0000644000175000000000000000141114777762222014054 00000000000000#include enum VarCacheIdx { VDateStyle = 0, VClientEncoding, VTimeZone, VStdStr, VAppName, NumVars }; typedef struct VarCache VarCache; struct VarCache { struct PStr **var_list; }; void init_var_lookup(const char *cf_track_extra_parameters); int get_num_var_cached(void); bool varcache_set(VarCache *cache, const char *key, const char *value) /* _MUSTCHECK */; bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p) _MUSTCHECK; void varcache_apply_startup(PktBuf *pkt, PgSocket *client); void varcache_fill_unset(VarCache *src, PgSocket *dst); void varcache_clean(VarCache *cache); void varcache_add_params(PktBuf *pkt, VarCache *vars); void varcache_deinit(void); void varcache_set_canonical(PgSocket *server, PgSocket *client); pgbouncer-1.24.1/include/objects.h0000644000175000000000000001300114777762222013727 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ extern struct StatList user_list; extern struct AATree user_tree; extern struct StatList pool_list; extern struct StatList peer_pool_list; extern struct StatList database_list; extern struct StatList peer_list; extern struct StatList autodatabase_idle_list; extern struct StatList login_client_list; extern struct Slab *client_cache; extern struct Slab *server_cache; extern struct Slab *db_cache; extern struct Slab *peer_cache; extern struct Slab *peer_pool_cache; extern struct Slab *pool_cache; extern struct Slab *user_cache; extern struct Slab *credentials_cache; extern struct Slab *iobuf_cache; extern struct Slab *outstanding_request_cache; extern struct Slab *var_list_cache; extern struct Slab *server_prepared_statement_cache; extern PgPreparedStatement *prepared_statements; extern unsigned long long int last_pgsocket_id; PgDatabase *find_peer(int peer_id); PgDatabase *find_database(const char *name); PgDatabase *find_or_register_database(PgSocket *connection, const char *name); PgGlobalUser *find_global_user(const char *name); PgCredentials *find_global_credentials(const char *name); PgPool *get_pool(PgDatabase *db, PgCredentials *user_credentials); PgPool *get_peer_pool(PgDatabase *); PgSocket *compare_connections_by_time(PgSocket *lhs, PgSocket *rhs); bool evict_connection(PgDatabase *db) _MUSTCHECK; bool evict_pool_connection(PgPool *pool) _MUSTCHECK; bool evict_user_connection(PgCredentials *user_credentials) _MUSTCHECK; bool find_server(PgSocket *client) _MUSTCHECK; bool life_over(PgSocket *server); bool release_server(PgSocket *server) /* _MUSTCHECK */; bool finish_client_login(PgSocket *client) _MUSTCHECK; bool check_fast_fail(PgSocket *client) _MUSTCHECK; PgSocket *accept_client(int sock, bool is_unix) _MUSTCHECK; void disconnect_server(PgSocket *server, bool notify, const char *reason, ...) _PRINTF(3, 4); void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) _PRINTF(3, 4); void disconnect_client_sqlstate(PgSocket *client, bool notify, const char *sqlstate, const char *reason); PgDatabase * add_peer(const char *name, int peer_id) _MUSTCHECK; PgDatabase * add_database(const char *name) _MUSTCHECK; PgDatabase *register_auto_database(const char *name); PgCredentials * add_dynamic_credentials(PgDatabase *db, const char *name, const char *passwd) _MUSTCHECK; PgCredentials * force_user_credentials(PgDatabase *db, const char *username, const char *passwd) _MUSTCHECK; bool add_outstanding_request(PgSocket *client, char type, ResponseAction action) _MUSTCHECK; bool pop_outstanding_request(PgSocket *client, const char types[], bool *skip); bool clear_outstanding_requests_until(PgSocket *server, const char types[]) _MUSTCHECK; bool queue_fake_response(PgSocket *client, char request_type) _MUSTCHECK; PgGlobalUser * update_global_user_passwd(PgGlobalUser *user, const char *passwd) _MUSTCHECK; PgGlobalUser * find_or_add_new_global_user(const char *name, const char *passwd) _MUSTCHECK; PgCredentials * find_or_add_new_global_credentials(const char *name, const char *passwd) _MUSTCHECK; PgCredentials * add_pam_credentials(const char *name, const char *passwd) _MUSTCHECK; void accept_cancel_request(PgSocket *req); bool forward_cancel_request(PgSocket *server); void launch_new_connection(PgPool *pool, bool evict_if_needed); bool use_client_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_end, const char *std_string, const char *datestyle, const char *timezone, const char *password, const char *scram_client_key, int scram_client_key_len, const char *scram_server_key, int scram_server_key_len) _MUSTCHECK; bool use_server_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_end, const char *std_string, const char *datestyle, const char *timezone, const char *password, const char *scram_client_key, int scram_client_key_len, const char *scram_server_key, int scram_server_key_len) _MUSTCHECK; void activate_client(PgSocket *client); void change_client_state(PgSocket *client, SocketState newstate); void change_server_state(PgSocket *server, SocketState newstate); int get_active_client_count(void); int get_active_server_count(void); void tag_pool_dirty(PgPool *pool); void tag_database_dirty(PgDatabase *db); void tag_autodb_dirty(void); void tag_host_addr_dirty(const char *host, const struct sockaddr *sa); void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)); void reuse_just_freed_objects(void); void init_objects(void); void init_caches(void); void objects_cleanup(void); pgbouncer-1.24.1/include/dnslookup.h0000644000175000000000000000354714777762222014332 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct DNSContext; struct DNSToken; struct addrinfo; typedef void (*adns_callback_f)(void *arg, const struct sockaddr *sa, int salen); struct DNSContext *adns_create_context(void); void adns_reload(struct DNSContext *ctx); void adns_free_context(struct DNSContext *ctx); struct DNSToken *adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *arg); void adns_cancel(struct DNSContext *ctx, struct DNSToken *tk); const char *adns_get_backend(void); void adns_zone_cache_maint(struct DNSContext *ctx); void adns_info(struct DNSContext *ctx, int *names, int *zones, int *queries, int *pending); typedef void (*adns_walk_name_f)(void *arg, const char *name, const struct addrinfo *ai, usec_t ttl); typedef void (*adns_walk_zone_f)(void *arg, const char *name, uint32_t serial, int nhosts); void adns_walk_names(struct DNSContext *ctx, adns_walk_name_f cb, void *arg); void adns_walk_zones(struct DNSContext *ctx, adns_walk_zone_f cb, void *arg); void adns_per_loop(struct DNSContext *ctx); pgbouncer-1.24.1/include/takeover.h0000644000175000000000000000175714777762222014135 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void takeover_init(void); bool takeover_login(PgSocket *bouncer) _MUSTCHECK; void takeover_login_failed(void); void takeover_finish(void); pgbouncer-1.24.1/include/system.h0000644000175000000000000000270414777762222013632 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Required system headers */ #include #include #include #include #include #include #ifdef HAVE_LIBGEN_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif /* * libc compat functions. */ #ifndef HAVE_LSTAT static inline int lstat(const char *path, struct stat *st) { return stat(path, st); } #endif bool check_unix_peer_name(int fd, const char *username); void change_user(const char *user); void change_file_mode(const char *fn, mode_t mode, const char *user, const char *group); pgbouncer-1.24.1/include/sbuf.h0000644000175000000000000001317714777762222013253 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * event types for protocol handler */ typedef enum { SBUF_EV_READ, /* got new packet */ SBUF_EV_RECV_FAILED, /* error */ SBUF_EV_SEND_FAILED, /* error */ SBUF_EV_CONNECT_FAILED, /* error */ SBUF_EV_CONNECT_OK, /* got connection */ SBUF_EV_FLUSH, /* data is sent, buffer empty */ SBUF_EV_PKT_CALLBACK, /* next part of pkt data */ SBUF_EV_TLS_READY /* TLS was established */ } SBufEvent; /* * If less than this amount of data is pending, then * prefer to merge it with next recv(). * * It needs to be larger than data handler wants * to see completely. Generally just header, * but currently also ServerParam pkt. */ #define SBUF_SMALL_PKT 64 struct tls; /* fwd def */ typedef struct SBuf SBuf; typedef struct SBufIO SBufIO; /* callback should return true if it used one of sbuf_prepare_* on sbuf, false if it used sbuf_pause(), sbuf_close() or simply wants to wait for next event loop (eg. too few data available). */ typedef bool (*sbuf_cb_t)(SBuf *sbuf, SBufEvent evtype, struct MBuf *mbuf); struct SBufIO { ssize_t (*sbufio_recv)(SBuf *sbuf, void *buf, size_t len); ssize_t (*sbufio_send)(SBuf *sbuf, const void *data, size_t len); int (*sbufio_close)(SBuf *sbuf); }; /* * Stream Buffer. * * Stream is divided to packets. On each packet start * protocol handler is called that decides what to do. */ struct SBuf { struct event ev; /* libevent handle */ uint8_t wait_type; /* track wait state */ uint8_t pkt_action; /* method for handling current pkt */ uint8_t tls_state; /* progress of tls */ int sock; /* fd for this socket */ unsigned pkt_remain; /* total packet length remaining */ unsigned skip_remain; /* the amount of data that still needs to be skipped before doing the pkt_action */ struct MBuf extra_packets; /* extra packets that pgbouncer inserts into the packet stream */ bool extra_packet_queue_after; /* if packets should be queued after the current packet that's being put on the queue */ sbuf_cb_t proto_cb; /* protocol callback */ SBuf *dst; /* target SBuf for current packet */ IOBuf *io; /* data buffer, lazily allocated */ const SBufIO *ops; /* normal vs. TLS */ struct tls *tls; /* TLS context */ const char *tls_host; /* target hostname */ }; #define sbuf_socket(sbuf) ((sbuf)->sock) void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn); bool sbuf_accept(SBuf *sbuf, int read_sock, bool is_unix) _MUSTCHECK; bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, socklen_t sa_len, time_t timeout_sec) _MUSTCHECK; /* * client_accept_sslmode is the currently applied sslmode that is used to * accept client connections. This is usually the same as * cf_client_tls_sslmode, except when changing the TLS configuration failed for * some reason (e.g. cert file not found). In this exceptional case, * cf_client_tls_sslmode will be the new sslmode, which is not actually * applied. And client_accept_sslmode is the still applied previous version. So * usually you should use this variable over cf_client_tls_sslmode. */ extern int client_accept_sslmode; /* * Same as client_accept_sslmode, but for server connections. */ extern int server_connect_sslmode; bool sbuf_tls_setup(void); bool sbuf_tls_accept(SBuf *sbuf) _MUSTCHECK; bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) _MUSTCHECK; bool sbuf_pause(SBuf *sbuf) _MUSTCHECK; void sbuf_continue(SBuf *sbuf); bool sbuf_close(SBuf *sbuf) _MUSTCHECK; bool sbuf_flush(SBuf *sbuf) _MUSTCHECK; /* proto_fn can use those functions to order behaviour */ void sbuf_prepare_send(SBuf *sbuf, SBuf *dst, unsigned amount); void sbuf_prepare_skip(SBuf *sbuf, unsigned amount); void sbuf_prepare_skip_then_send_leftover(SBuf *sbuf, SBuf *dst, unsigned skip_amount, unsigned total_amount); void sbuf_prepare_fetch(SBuf *sbuf, unsigned amount); bool sbuf_queue_packet(SBuf *sbuf, SBuf *dst, PktBuf *pkt) _MUSTCHECK; bool sbuf_queue_full_packet(SBuf *sbuf, SBuf *dst, PktHdr *pkt) _MUSTCHECK; bool sbuf_answer(SBuf *sbuf, const void *buf, size_t len) _MUSTCHECK; bool sbuf_continue_with_callback(SBuf *sbuf, event_callback_fn cb) _MUSTCHECK; bool sbuf_use_callback_once(SBuf *sbuf, short ev, event_callback_fn user_cb) _MUSTCHECK; /* * Returns true if SBuf is has no data buffered * and is not in a middle of a packet. */ static inline bool sbuf_is_empty(SBuf *sbuf) { return iobuf_empty(sbuf->io) && sbuf->pkt_remain == 0; } static inline bool sbuf_is_closed(SBuf *sbuf) { return sbuf->sock == 0; } /* * Lowlevel operations. */ static inline ssize_t sbuf_op_recv(SBuf *sbuf, void *buf, size_t len) { return sbuf->ops->sbufio_recv(sbuf, buf, len); } static inline ssize_t sbuf_op_send(SBuf *sbuf, const void *buf, size_t len) { return sbuf->ops->sbufio_send(sbuf, buf, len); } static inline int sbuf_op_close(SBuf *sbuf) { return sbuf->ops->sbufio_close(sbuf); } void sbuf_cleanup(void); pgbouncer-1.24.1/include/janitor.h0000644000175000000000000000221714777762222013753 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void janitor_setup(void); void config_postprocess(void); void resume_all(void); void per_loop_maint(void); bool suspend_socket(PgSocket *sk, bool force) _MUSTCHECK; void kill_pool(PgPool *pool); void kill_peer_pool(PgPool *pool); void kill_database(PgDatabase *db); void kill_peer(PgDatabase *db); pgbouncer-1.24.1/include/prepare.h0000644000175000000000000000333414777762222013744 00000000000000#include /* The format that our prepared statements are called on the Postgres server */ #define PREPARED_STMT_NAME_FORMAT "PGBOUNCER_%" PRIu64 /* "PGBOUNCER_" + 20 digits for unsigned 64 bit int + \0 */ #define MAX_SERVER_PREPARED_STMT_NAME 31 /* Structure to store text of prepared query */ typedef struct PgPreparedStatement { UT_hash_handle hh; uint64_t query_id; uint32_t use_count; size_t query_and_parameters_len; uint8_t stmt_name_len; char stmt_name[MAX_SERVER_PREPARED_STMT_NAME]; char query_and_parameters[]; /* varying length */ } PgPreparedStatement; /* Client session prepared statements */ typedef struct PgClientPreparedStatement { UT_hash_handle hh; PgPreparedStatement *ps; char stmt_name[]; /* varying size */ } PgClientPreparedStatement; /* Prepared statements in Postgres backends */ typedef struct PgServerPreparedStatement { uint64_t query_id; UT_hash_handle hh; PgPreparedStatement *ps; } PgServerPreparedStatement; #define is_prepared_statements_enabled(client_or_server) \ (connection_pool_mode(client_or_server) != POOL_SESSION && cf_max_prepared_statements != 0) bool handle_parse_command(PgSocket *client, PktHdr *pkt); bool handle_bind_command(PgSocket *client, PktHdr *pkt); bool handle_describe_command(PgSocket *client, PktHdr *pkt); bool handle_close_statement_command(PgSocket *client, PktHdr *pkt, PgClosePacket *close_packet); void free_server_prepared_statement(PgServerPreparedStatement *server_ps); void unregister_prepared_statement(PgSocket *server, uint64_t query_id); bool add_prepared_statement(PgSocket *server, PgServerPreparedStatement *server_ps) _MUSTCHECK; void free_client_prepared_statements(PgSocket *client); void free_server_prepared_statements(PgSocket *server); pgbouncer-1.24.1/include/bouncer.h0000644000175000000000000007063314777762222013751 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * core structures */ #include "system.h" #include #include #include #include #include #include #include #include /* * By default uthash exits the program when an allocation fails. But for some * of our hashmap usecases we don't want that. Luckily you can install your own * OOM handler. But to do so you need to define HASH_NON_FATAL before the * header is loaded. Then later you can actually install your own handler. For * now we simply install a fatal handler, which can be overridden again later * in C files where we want to handle allocation failures differently. */ #define HASH_NONFATAL_OOM 1 #include "uthash.h" #undef uthash_nonfatal_oom #define uthash_nonfatal_oom(elt) fatal("out of memory") #ifdef USE_SYSTEMD #include #else #define SD_LISTEN_FDS_START 3 #define sd_is_socket(fd, f, t, l) (0) #define sd_listen_fds(ue) (0) #define sd_notify(ue, s) #define sd_notifyf(ue, f, ...) #endif /* global libevent handle */ extern struct event_base *pgb_event_base; /* each state corresponds to a list */ enum SocketState { CL_FREE, /* free_client_list */ CL_JUSTFREE, /* justfree_client_list */ CL_LOGIN, /* login_client_list */ CL_WAITING, /* pool->waiting_client_list */ CL_WAITING_LOGIN, /* - but return to CL_LOGIN instead of CL_ACTIVE */ CL_ACTIVE, /* pool->active_client_list */ CL_WAITING_CANCEL, /* pool->waiting_cancel_req_list */ CL_ACTIVE_CANCEL, /* pool->active_cancel_req_list */ SV_FREE, /* free_server_list */ SV_JUSTFREE, /* justfree_server_list */ SV_LOGIN, /* pool->new_server_list */ SV_BEING_CANCELED, /* pool->being_canceled_server_list */ SV_IDLE, /* pool->idle_server_list */ SV_ACTIVE, /* pool->active_server_list */ SV_ACTIVE_CANCEL, /* pool->active_cancel_server_list */ SV_USED, /* pool->used_server_list */ SV_TESTED /* pool->tested_server_list */ }; enum PauseMode { P_NONE = 0, /* active pooling */ P_PAUSE = 1, /* wait for client to finish work */ P_SUSPEND = 2 /* wait for buffers to be empty */ }; enum ShutDownMode { /* just stay running */ SHUTDOWN_NONE = 0, /* * Wait for all servers to become idle before stopping the process. New * client connection attempts are denied while waiting for the servers * to be released. Already connected clients that go to CL_WAITING * state are disconnected eagerly. */ SHUTDOWN_WAIT_FOR_SERVERS, /* * Wait for all clients to disconnect before stopping the process. * While waiting for this we stop listening on the socket so no new * clients can connect. Still connected clients will continue to be * handed connections from the pool until they disconnect. * * This allows for a rolling restart in combination with so_reuseport. * * This is an even more graceful shutdown than SHUTDOWN_WAIT_FOR_SERVERS. */ SHUTDOWN_WAIT_FOR_CLIENTS, /* close all connections immediately and stop the process */ SHUTDOWN_IMMEDIATE, }; enum SSLMode { SSLMODE_DISABLED, SSLMODE_ALLOW, SSLMODE_PREFER, SSLMODE_REQUIRE, SSLMODE_VERIFY_CA, SSLMODE_VERIFY_FULL }; enum PacketCallbackFlag { /* no callback */ CB_NONE = 0, /* * Buffer the full packet into client->packet_cb_state.pkt and once it is * done switch to CB_HANDLE_COMPLETE_PACKET. This is used to handle * prepared statements in transaction pooling mode. */ CB_WANT_COMPLETE_PACKET, /* * The state after CB_WANT_COMPLETE_PACKET. The packet is fully buffered * and can now be processed by client_proto(). */ CB_HANDLE_COMPLETE_PACKET, }; enum LoadBalanceHosts { LOAD_BALANCE_HOSTS_DISABLE, LOAD_BALANCE_HOSTS_ROUND_ROBIN }; #define is_server_socket(sk) ((sk)->state >= SV_FREE) typedef struct PgSocket PgSocket; typedef struct PgCredentials PgCredentials; typedef struct PgGlobalUser PgGlobalUser; typedef struct PgDatabase PgDatabase; typedef struct PgPool PgPool; typedef struct PgStats PgStats; typedef union PgAddr PgAddr; typedef enum SocketState SocketState; typedef enum PacketCallbackFlag PacketCallbackFlag; typedef struct PktHdr PktHdr; typedef struct PktBuf PktBuf; typedef struct ScramState ScramState; typedef struct PgPreparedStatement PgPreparedStatement; typedef enum ResponseAction ResponseAction; typedef enum ReplicationType ReplicationType; extern int cf_sbuf_len; #include "util.h" #include "iobuf.h" #include "sbuf.h" #include "pktbuf.h" #include "varcache.h" #include "dnslookup.h" #include "admin.h" #include "loader.h" #include "client.h" #include "server.h" #include "pooler.h" #include "proto.h" #include "objects.h" #include "stats.h" #include "takeover.h" #include "janitor.h" #include "hba.h" #include "messages.h" #include "pam.h" #include "prepare.h" #ifndef WIN32 #define DEFAULT_UNIX_SOCKET_DIR "/tmp" #else #define DEFAULT_UNIX_SOCKET_DIR "" #endif /* * To avoid allocations, we use static buffers. * * Note that a trailing zero byte is used in each case, so the actual * usable length is one less. */ /* matching NAMEDATALEN */ #define MAX_DBNAME 64 /* * Ought to match NAMEDATALEN. Some cloud services use longer user * names, so give it some extra room. */ #define MAX_USERNAME 128 /* * Some cloud services use very long generated passwords, so give it * plenty of room. */ #define MAX_PASSWORD 2048 /* * Symbols for authentication type settings (auth_type, hba). */ enum auth_type { AUTH_TYPE_ANY, AUTH_TYPE_TRUST, AUTH_TYPE_PLAIN, AUTH_TYPE_MD5, AUTH_TYPE_CERT, AUTH_TYPE_HBA, AUTH_TYPE_PAM, AUTH_TYPE_SCRAM_SHA_256, AUTH_TYPE_PEER, AUTH_TYPE_REJECT, }; /* type codes for weird pkts */ #define PKT_STARTUP_V2 0x20000 #define PKT_STARTUP_V3 0x30000 #define PKT_STARTUP_V3_UNSUPPORTED 0x30001 #define PKT_STARTUP_V4 0x40000 #define PKT_CANCEL 80877102 #define PKT_SSLREQ 80877103 #define PKT_GSSENCREQ 80877104 #define POOL_SESSION 0 #define POOL_TX 1 #define POOL_STMT 2 #define POOL_INHERIT 3 #define BACKENDKEY_LEN 8 /* buffer size for startup noise */ #define STARTUP_BUF 1024 /* * When peering is enabled we always put a 1 in the last two bits of the cancel * key when sending it to the client. These bits indicate the TTL and thus * allow forwarding the the cancel key 3 times before it is dropped. Triple * forwarding seems enough for any reasonable multi layered load balancing * setup. */ #define CANCELLATION_TTL_MASK 0x03 /* * Remote/local address */ /* buffer for pgaddr string conversions (with port) */ #define PGADDR_BUF (INET6_ADDRSTRLEN + 10) struct sockaddr_ucreds { struct sockaddr_in sin; uid_t uid; pid_t pid; }; /* * AF_INET,AF_INET6 are stored as-is, * AF_UNIX uses sockaddr_in port + uid/pid. */ union PgAddr { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr_ucreds scred; }; static inline unsigned int pga_family(const PgAddr *a) { return a->sa.sa_family; } static inline bool pga_is_unix(const PgAddr *a) { return a->sa.sa_family == AF_UNIX; } int pga_port(const PgAddr *a); void pga_set(PgAddr *a, int fam, int port); void pga_copy(PgAddr *a, const struct sockaddr *sa); bool pga_pton(PgAddr *a, const char *s, int port); const char *pga_ntop(const PgAddr *a, char *dst, int dstlen); const char *pga_str(const PgAddr *a, char *dst, int dstlen); const char *pga_details(const PgAddr *a, char *dst, int dstlen); int pga_cmp_addr(const PgAddr *a, const PgAddr *b); /* * Stats, kept per-pool. */ struct PgStats { uint64_t server_assignment_count; uint64_t xact_count; uint64_t query_count; uint64_t server_bytes; uint64_t client_bytes; usec_t xact_time; /* total transaction time in us */ usec_t query_time; /* total query time in us */ usec_t wait_time; /* total time clients had to wait */ /* stats for prepared statements */ uint64_t ps_server_parse_count; uint64_t ps_client_parse_count; uint64_t ps_bind_count; }; /* * Contains connections for one db+user pair. * * Stats: * ->stats is updated online. * for each stats_period: * ->older_stats = ->newer_stats * ->newer_stats = ->stats */ struct PgPool { struct List head; /* entry in global pool_list */ struct List map_head; /* entry in user->pool_list */ PgDatabase *db; /* corresponding database */ /* * credentials for the user logged in user, this field is NULL for peer * pools. */ PgCredentials *user_credentials; /* * Clients that are both logged in and where pgbouncer is actively * listening for messages on the client socket. */ struct StatList active_client_list; /* * Clients that are waiting for a server to be available to which their * query/queries can be sent. These clients were originally in the * active_client_list. But were placed in this list when a query was * received on the client socket when no server connection was available to * handle it. */ struct StatList waiting_client_list; /* * Clients that sent cancel request, to cancel another client its query. * These requests are waiting for a new server connection to be opened, * before the request can be forwarded. * * This is a separate list from waiting_client_list, because we want to * give cancel requests priority over regular clients. The main reason * for this is, because a cancel request might free up a connection, * which can be used for one of the waiting clients. */ struct StatList waiting_cancel_req_list; /* * Clients that sent a cancel request, to cancel another client its query. * This request was already forwarded to a server. They are waiting for a * response from the server. */ struct StatList active_cancel_req_list; /* * Server connections that are linked with a client. These clients cannot * be used for other clients until they are back in the idle_server_list, * which is done by calling release_server. */ struct StatList active_server_list; /* * Server connections that are only used to forward a cancel request. These * servers have a cancel request in-flight */ struct StatList active_cancel_server_list; /* * Servers that normally could become idle, to be linked with with a new * server. But active_cancel_server_list still contains servers that have a * cancel request in flight which cancels queries on this server. To avoid * race conditions this server will not be placed in the idle list (and * thus not be reused) until all in-flight cancel requests for it have * completed. */ struct StatList being_canceled_server_list; /* * Servers connections that are ready to be linked with clients. These will * be automatically used whenever a client needs a new connection to the * server. */ struct StatList idle_server_list; /* * Server connections that were just unlinked from their previous client. * Some work is needed to make sure these server connections can be reused * for another client. After all that that work is done the server is * placed into idle_server_list. */ struct StatList used_server_list; /* * Server connections in testing process. This is only applicable when the * server_reset_query option is set in the pgbouncer.ini config. The server * connection is in this state when it needs to run this reset query. */ struct StatList tested_server_list; /* * Servers connections that are in the login phase. This is the initial * state that every server connection is in. Once the whole login process * has completed the server is moved to the idle list. * * A special case is when there are cancel requests waiting to be forwarded * to servers in waiting_cancel_req_list. In that case the server bails out * of the login flow, because a cancel request needs to be sent before * logging in. * * NOTE: This list can at most contain a single server due to the way * launch_new_connection spawns them. */ struct StatList new_server_list; PgStats stats; PgStats newer_stats; PgStats older_stats; /* database info to be sent to client */ struct PktBuf *welcome_msg; /* ServerParams without VarCache ones */ VarCache orig_vars; /* default params from server */ usec_t last_lifetime_disconnect;/* last time when server_lifetime was applied */ /* if last connect to server failed, there should be delay before next */ usec_t last_connect_time; bool last_connect_failed : 1; char last_connect_failed_message[100]; bool last_login_failed : 1; bool welcome_msg_ready : 1; uint16_t rrcounter; /* round-robin counter */ }; /* * pool_connected_server_count returns the number of servers that are fully * connected. This is used by the janitor to make the number of connected * servers satisfy the pool_size and min_pool_size config values. This * explicitly doesn't contain server connections used to send cancellation * requests, since those connections are untracked by Postgres and they cannot * be reused for purposes other than sending a single cancellation. */ #define pool_connected_server_count(pool) ( \ statlist_count(&(pool)->active_server_list) + \ statlist_count(&(pool)->being_canceled_server_list) + \ statlist_count(&(pool)->idle_server_list) + \ statlist_count(&(pool)->tested_server_list) + \ statlist_count(&(pool)->used_server_list)) /* * pool_server_count returns how many connections to the server are open. This * includes connections for cancellations, because we also want to limit those * to some extent. */ #define pool_server_count(pool) ( \ pool_connected_server_count(pool) + \ statlist_count(&(pool)->new_server_list) + \ statlist_count(&(pool)->active_cancel_server_list)) /* * pool_client_count returns the number of clients that have completed the * login phase. This doesn't include clients that are sending a cancellation * request. */ #define pool_client_count(pool) ( \ statlist_count(&(pool)->active_client_list) + \ statlist_count(&(pool)->waiting_client_list)) /* * Credentials for a user in login db. */ struct PgCredentials { struct AANode tree_node; /* used to attach user to tree */ char name[MAX_USERNAME]; char passwd[MAX_PASSWORD]; uint8_t scram_ClientKey[32]; uint8_t scram_ServerKey[32]; bool has_scram_keys; /* true if the above two are valid */ bool mock_auth; /* not a real user, only for mock auth */ bool dynamic_passwd; /* does the password need to be refreshed every use */ /* * global_user points at the global user which is used for configuration * settings and connection count tracking. */ PgGlobalUser *global_user; }; /* * The global user is used for configuration settings and connection count. It * includes credentials, but these are empty if the user is not configured in * the auth_file. * * global_user->pool_list contains all the pools that this user is used for * each PgDatabases that uses this global user. * * FIXME: remove ->head as ->tree_node should be enough. */ struct PgGlobalUser { PgCredentials credentials; /* needs to be first for AAtree */ struct List head; /* used to attach user to list */ struct List pool_list; /* list of pools where pool->user == this user */ int pool_mode; int pool_size; /* max server connections in one pool */ int res_pool_size; /* max additional server connections in one pool */ usec_t idle_transaction_timeout; /* how long a user is allowed to stay idle in transaction before being killed */ usec_t query_timeout; /* how long a users query is allowed to run before beign killed */ usec_t client_idle_timeout; /* how long is user allowed to idly connect to pgbouncer */ int max_user_connections; /* how many server connections are allowed */ int max_user_client_connections; /* how many client connections are allowed */ int connection_count; /* how many server connections are used by user now */ int client_connection_count; /* how many client connections are used by user now */ }; /* * A database entry from config. */ struct PgDatabase { struct List head; char name[MAX_DBNAME]; /* db name for clients */ /* * Pgbouncer peer database related settings */ int peer_id; /* the peer_id of this peer */ struct PgPool *pool; /* the pool of this peer database */ /* * configuration */ char *host; /* host or unix socket name */ int port; int pool_size; /* max server connections in one pool */ int min_pool_size; /* min server connections in one pool */ int res_pool_size; /* additional server connections in case of trouble */ int pool_mode; /* pool mode for this database */ int max_db_client_connections; /* max connections that pgbouncer will accept from client to this database */ int max_db_connections; /* max server connections between all pools */ usec_t server_lifetime; /* max lifetime of server connection */ char *connect_query; /* startup commands to send to server after connect */ enum LoadBalanceHosts load_balance_hosts; /* strategy for host selection in a comma-separated host list */ struct PktBuf *startup_params; /* partial StartupMessage (without user) be sent to server */ const char *dbname; /* server-side name, pointer to inside startup_msg */ char *auth_dbname; /* if not NULL, auth_query will be run on the specified database */ PgCredentials *forced_user_credentials; /* if not NULL, the user/psw is forced */ PgCredentials *auth_user_credentials; /* if not NULL, users not in userlist.txt will be looked up on the server */ char *auth_query; /* if not NULL, will be used to fetch password from database. */ /* * run-time state */ bool db_paused; /* PAUSE ; was issued */ bool db_wait_close; /* WAIT_CLOSE was issued for this database */ bool db_dead; /* used on RELOAD/SIGHUP to later detect removed dbs */ bool db_auto; /* is the database auto-created by autodb_connstr */ bool db_disabled; /* is the database accepting new connections? */ bool admin; /* internal console db */ bool fake; /* not a real database, only for mock auth */ usec_t inactive_time; /* when auto-database became inactive (to kill it after timeout) */ unsigned active_stamp; /* set if autodb has connections */ int connection_count; /* total connections for this database in all pools */ int client_connection_count; /* total client connections for this database */ struct AATree user_tree; /* users that have been queried on this database */ }; enum ResponseAction { /* Forward the response that is received from the server */ RA_FORWARD, /* * drop the response received from the server (the client did not initiate * the request) */ RA_SKIP, /* * Generate a response to this type of request at this spot in the * pipeline. The request from the client was not actually sent to the * server, but the client expects a response to it. */ RA_FAKE, }; typedef struct OutstandingRequest { struct List node; char type; /* The single character type of the request */ ResponseAction action; /* What action to take (see comments on ResponseAction) */ /* * A pointer to the server-side prepared statement that is being closed * by this Close message. If the request fails we should add the * prepared statement back to the server its cache. */ PgServerPreparedStatement *server_ps; uint64_t server_ps_query_id; } OutstandingRequest; enum ReplicationType { REPLICATION_NONE = 0, REPLICATION_LOGICAL, REPLICATION_PHYSICAL, }; extern const char *replication_type_parameters[3]; /* * A client or server connection. * * ->state corresponds to various lists the struct can be at. */ struct PgSocket { struct List head; /* list header for pool list */ struct List cancel_head; /* list header for server->canceling_clients */ PgSocket *link; /* the dest of packets */ PgPool *pool; /* parent pool, if NULL not yet assigned */ PgCredentials *login_user_credentials; /* presented login, for client it may differ from pool->user */ unsigned long long int id; /* unique numeric ID used to identify PgSocket instance */ int client_auth_type; /* auth method decided by hba */ /* the queue of requests that we still expect a server response for */ struct StatList outstanding_requests; SocketState state : 8; /* this also specifies socket location */ bool contributes_db_client_count : 1; bool user_connection_counted : 1; bool ready : 1; /* server: accepts new query */ bool idle_tx : 1; /* server: idling in tx */ bool close_needed : 1; /* server: this socket must be closed ASAP */ bool setting_vars : 1; /* server: setting client vars */ bool exec_on_connect : 1; /* server: executing connect_query */ bool resetting : 1; /* server: executing reset query from auth login; don't release on flush */ bool copy_mode : 1; /* server: in copy stream, ignores any Sync packets until CopyDone or CopyFail */ bool wait_for_welcome : 1; /* client: no server yet in pool, cannot send welcome msg */ bool wait_for_user_conn : 1; /* client: waiting for auth_conn server connection */ bool wait_for_user : 1; /* client: waiting for auth_conn query results */ bool wait_for_auth : 1; /* client: waiting for external auth (PAM) to be completed */ bool suspended : 1; /* client/server: if the socket is suspended */ bool admin_user : 1; /* console client: has admin rights */ bool own_user : 1; /* console client: client with same uid on unix socket */ bool wait_for_response : 1; /* console client: waits for completion of PAUSE/SUSPEND cmd */ bool wait_sslchar : 1; /* server: waiting for ssl response: S/N */ /* server: received an ErrorResponse, waiting for ReadyForQuery to clear * the outstanding requests until the next Sync */ bool query_failed : 1; ReplicationType replication; /* If this is a replication connection */ char *startup_options; /* only tracked for replication connections */ usec_t connect_time; /* when connection was made */ usec_t request_time; /* last activity time */ usec_t query_start; /* client: query start moment */ usec_t xact_start; /* client: xact start moment */ usec_t wait_start; /* client: waiting start moment */ uint8_t cancel_key[BACKENDKEY_LEN]; /* client: generated, server: remote */ struct StatList canceling_clients; /* clients trying to cancel the query on this connection */ PgSocket *canceled_server; /* server that is being canceled by this request */ PgAddr remote_addr; /* ip:port for remote endpoint */ PgAddr local_addr; /* ip:port for local endpoint */ union { struct DNSToken *dns_token; /* ongoing request */ PgDatabase *db; /* cache db while doing auth query */ }; struct ScramState { char *client_nonce; char *client_first_message_bare; char *client_final_message_without_proof; char *server_nonce; char *server_first_message; uint8_t *SaltedPassword; char cbind_flag; bool adhoc; /* SCRAM data made up from plain-text password */ int iterations; char *salt; /* base64-encoded */ uint8_t ClientKey[32]; /* SHA256_DIGEST_LENGTH */ uint8_t StoredKey[32]; uint8_t ServerKey[32]; } scram_state; VarCache vars; /* state of interesting server parameters */ /* client: prepared statements prepared by this client */ PgClientPreparedStatement *client_prepared_statements; /* server: prepared statements prepared on this server */ PgServerPreparedStatement *server_prepared_statements; /* cb state during SBUF_EV_PKT_CALLBACK processing */ struct CallbackState { /* * Which callback should be executed. * See comments on PacketCallbackFlag for details */ PacketCallbackFlag flag : 8; /* * A temporary buffer into which we load the complete * packet (if desired by the callback). */ PktHdr pkt; } packet_cb_state; SBuf sbuf; /* stream buffer, must be last */ }; #define RAW_IOBUF_SIZE offsetof(IOBuf, buf) #define IOBUF_SIZE (RAW_IOBUF_SIZE + cf_sbuf_len) /* where to store old fd info during SHOW FDS result processing */ #define tmp_sk_oldfd request_time #define tmp_sk_linkfd query_start /* takeover_clean_socket() needs to clean those up */ /* where the salt is temporarily stored */ #define tmp_login_salt cancel_key /* main.c */ extern int cf_daemon; extern char *cf_config_file; extern char *cf_jobname; extern char *cf_unix_socket_dir; extern int cf_unix_socket_mode; extern char *cf_unix_socket_group; extern char *cf_listen_addr; extern int cf_listen_port; extern int cf_listen_backlog; extern int cf_peer_id; extern int cf_pool_mode; extern int cf_max_client_conn; extern int cf_default_pool_size; extern int cf_min_pool_size; extern int cf_res_pool_size; extern usec_t cf_res_pool_timeout; extern int cf_max_db_connections; extern int cf_max_db_client_connections; extern int cf_max_user_connections; extern int cf_max_user_client_connections; extern char *cf_autodb_connstr; extern usec_t cf_autodb_idle_timeout; extern usec_t cf_suspend_timeout; extern usec_t cf_server_lifetime; extern usec_t cf_server_idle_timeout; extern char *cf_server_reset_query; extern int cf_server_reset_query_always; extern char *cf_server_check_query; extern usec_t cf_server_check_delay; extern int cf_server_fast_close; extern usec_t cf_server_connect_timeout; extern usec_t cf_server_login_retry; extern usec_t cf_query_timeout; extern usec_t cf_query_wait_timeout; extern usec_t cf_cancel_wait_timeout; extern usec_t cf_client_idle_timeout; extern usec_t cf_client_login_timeout; extern usec_t cf_idle_transaction_timeout; extern bool any_user_level_timeout_set; extern bool any_user_level_client_timeout_set; extern int cf_server_round_robin; extern int cf_disable_pqexec; extern usec_t cf_dns_max_ttl; extern usec_t cf_dns_nxdomain_ttl; extern usec_t cf_dns_zone_check_period; extern char *cf_resolv_conf; extern int cf_auth_type; extern char *cf_auth_file; extern char *cf_auth_query; extern char *cf_auth_user; extern char *cf_auth_hba_file; extern char *cf_auth_dbname; extern char *cf_pidfile; extern char *cf_ignore_startup_params; extern char *cf_admin_users; extern char *cf_stats_users; extern int cf_stats_period; extern int cf_log_stats; extern int cf_pause_mode; extern int cf_shutdown; extern int cf_reboot; extern unsigned int cf_max_packet_size; extern int cf_sbuf_loopcnt; extern int cf_so_reuseport; extern int cf_tcp_keepalive; extern int cf_tcp_keepcnt; extern int cf_tcp_keepidle; extern int cf_tcp_keepintvl; extern int cf_tcp_socket_buffer; extern int cf_tcp_defer_accept; extern int cf_tcp_user_timeout; extern int cf_log_connections; extern int cf_log_disconnections; extern int cf_log_pooler_errors; extern int cf_application_name_add_host; extern int cf_client_tls_sslmode; extern char *cf_client_tls_protocols; extern char *cf_client_tls_ca_file; extern char *cf_client_tls_cert_file; extern char *cf_client_tls_key_file; extern char *cf_client_tls_ciphers; extern char *cf_client_tls_dheparams; extern char *cf_client_tls_ecdhecurve; extern int cf_server_tls_sslmode; extern char *cf_server_tls_protocols; extern char *cf_server_tls_ca_file; extern char *cf_server_tls_cert_file; extern char *cf_server_tls_key_file; extern char *cf_server_tls_ciphers; extern int cf_max_prepared_statements; extern const struct CfLookup pool_mode_map[]; extern const struct CfLookup load_balance_hosts_map[]; extern usec_t g_suspend_start; extern struct DNSContext *adns; extern struct HBA *parsed_hba; static inline PgSocket * _MUSTCHECK pop_socket(struct StatList *slist) { struct List *item = statlist_pop(slist); if (item == NULL) return NULL; return container_of(item, PgSocket, head); } static inline PgSocket *first_socket(struct StatList *slist) { if (statlist_empty(slist)) return NULL; return container_of(slist->head.next, PgSocket, head); } static inline PgSocket *last_socket(struct StatList *slist) { if (statlist_empty(slist)) return NULL; return container_of(slist->head.prev, PgSocket, head); } /* * cstr_skip_ws returns a pointer to the first non whitespace character * in the given string. */ static inline char *cstr_skip_ws(char *p) { while (*p && *p == ' ') p++; return p; } void load_config(void); bool set_config_param(const char *key, const char *val); void config_for_each(void (*param_cb)(void *arg, const char *name, const char *val, const char *defval, bool reloadable), void *arg); pgbouncer-1.24.1/include/pooler.h0000644000175000000000000000230014777762222013576 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void pooler_setup(void); bool use_pooler_socket(int fd, bool is_unix) _MUSTCHECK; void resume_pooler(void); void suspend_pooler(void); void per_loop_pooler_maint(void); void pooler_tune_accept(bool on); void cleanup_sockets(void); typedef bool (*pooler_cb)(void *arg, int fd, const PgAddr *addr); bool for_each_pooler_fd(pooler_cb cb, void *arg); pgbouncer-1.24.1/include/common/0000755000000000000000000000000014777762567013501 500000000000000pgbouncer-1.24.1/include/common/pg_wchar.h0000644000175000000000000006172514777762222015400 00000000000000/*------------------------------------------------------------------------- * * pg_wchar.h * multibyte-character support * * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/include/mb/pg_wchar.h * * NOTES * This is used both by the backend and by frontends, but should not be * included by libpq client programs. In particular, a libpq client * should not assume that the encoding IDs used by the version of libpq * it's linked to match up with the IDs declared here. * *------------------------------------------------------------------------- */ #ifndef PG_WCHAR_H #define PG_WCHAR_H /* * The pg_wchar type */ typedef unsigned int pg_wchar; /* * Maximum byte length of multibyte characters in any backend encoding */ #define MAX_MULTIBYTE_CHAR_LEN 4 /* * various definitions for EUC */ #define SS2 0x8e /* single shift 2 (JIS0201) */ #define SS3 0x8f /* single shift 3 (JIS0212) */ /* * SJIS validation macros */ #define ISSJISHEAD(c) (((c) >= 0x81 && (c) <= 0x9f) || ((c) >= 0xe0 && (c) <= 0xfc)) #define ISSJISTAIL(c) (((c) >= 0x40 && (c) <= 0x7e) || ((c) >= 0x80 && (c) <= 0xfc)) /*---------------------------------------------------- * MULE Internal Encoding (MIC) * * This encoding follows the design used within XEmacs; it is meant to * subsume many externally-defined character sets. Each character includes * identification of the character set it belongs to, so the encoding is * general but somewhat bulky. * * Currently PostgreSQL supports 5 types of MULE character sets: * * 1) 1-byte ASCII characters. Each byte is below 0x80. * * 2) "Official" single byte charsets such as ISO-8859-1 (Latin1). * Each MULE character consists of 2 bytes: LC1 + C1, where LC1 is * an identifier for the charset (in the range 0x81 to 0x8d) and C1 * is the character code (in the range 0xa0 to 0xff). * * 3) "Private" single byte charsets such as SISHENG. Each MULE * character consists of 3 bytes: LCPRV1 + LC12 + C1, where LCPRV1 * is a private-charset flag, LC12 is an identifier for the charset, * and C1 is the character code (in the range 0xa0 to 0xff). * LCPRV1 is either 0x9a (if LC12 is in the range 0xa0 to 0xdf) * or 0x9b (if LC12 is in the range 0xe0 to 0xef). * * 4) "Official" multibyte charsets such as JIS X0208. Each MULE * character consists of 3 bytes: LC2 + C1 + C2, where LC2 is * an identifier for the charset (in the range 0x90 to 0x99) and C1 * and C2 form the character code (each in the range 0xa0 to 0xff). * * 5) "Private" multibyte charsets such as CNS 11643-1992 Plane 3. * Each MULE character consists of 4 bytes: LCPRV2 + LC22 + C1 + C2, * where LCPRV2 is a private-charset flag, LC22 is an identifier for * the charset, and C1 and C2 form the character code (each in the range * 0xa0 to 0xff). LCPRV2 is either 0x9c (if LC22 is in the range 0xf0 * to 0xf4) or 0x9d (if LC22 is in the range 0xf5 to 0xfe). * * "Official" encodings are those that have been assigned code numbers by * the XEmacs project; "private" encodings have Postgres-specific charset * identifiers. * * See the "XEmacs Internals Manual", available at http://www.xemacs.org, * for more details. Note that for historical reasons, Postgres' * private-charset flag values do not match what XEmacs says they should be, * so this isn't really exactly MULE (not that private charsets would be * interoperable anyway). * * Note that XEmacs's implementation is different from what emacs does. * We follow emacs's implementation, rather than XEmacs's. *---------------------------------------------------- */ /* * Charset identifiers (also called "leading bytes" in the MULE documentation) */ /* * Charset IDs for official single byte encodings (0x81-0x8e) */ #define LC_ISO8859_1 0x81 /* ISO8859 Latin 1 */ #define LC_ISO8859_2 0x82 /* ISO8859 Latin 2 */ #define LC_ISO8859_3 0x83 /* ISO8859 Latin 3 */ #define LC_ISO8859_4 0x84 /* ISO8859 Latin 4 */ #define LC_TIS620 0x85 /* Thai (not supported yet) */ #define LC_ISO8859_7 0x86 /* Greek (not supported yet) */ #define LC_ISO8859_6 0x87 /* Arabic (not supported yet) */ #define LC_ISO8859_8 0x88 /* Hebrew (not supported yet) */ #define LC_JISX0201K 0x89 /* Japanese 1 byte kana */ #define LC_JISX0201R 0x8a /* Japanese 1 byte Roman */ /* Note that 0x8b seems to be unused as of Emacs 20.7. * However, there might be a chance that 0x8b could be used * in later versions of Emacs. */ #define LC_KOI8_R 0x8b /* Cyrillic KOI8-R */ #define LC_ISO8859_5 0x8c /* ISO8859 Cyrillic */ #define LC_ISO8859_9 0x8d /* ISO8859 Latin 5 (not supported yet) */ #define LC_ISO8859_15 0x8e /* ISO8859 Latin 15 (not supported yet) */ /* #define CONTROL_1 0x8f control characters (unused) */ /* Is a leading byte for "official" single byte encodings? */ #define IS_LC1(c) ((unsigned char)(c) >= 0x81 && (unsigned char)(c) <= 0x8d) /* * Charset IDs for official multibyte encodings (0x90-0x99) * 0x9a-0x9d are free. 0x9e and 0x9f are reserved. */ #define LC_JISX0208_1978 0x90 /* Japanese Kanji, old JIS (not supported) */ #define LC_GB2312_80 0x91 /* Chinese */ #define LC_JISX0208 0x92 /* Japanese Kanji (JIS X 0208) */ #define LC_KS5601 0x93 /* Korean */ #define LC_JISX0212 0x94 /* Japanese Kanji (JIS X 0212) */ #define LC_CNS11643_1 0x95 /* CNS 11643-1992 Plane 1 */ #define LC_CNS11643_2 0x96 /* CNS 11643-1992 Plane 2 */ #define LC_JISX0213_1 0x97 /* Japanese Kanji (JIS X 0213 Plane 1) * (not supported) */ #define LC_BIG5_1 0x98 /* Plane 1 Chinese traditional (not * supported) */ #define LC_BIG5_2 0x99 /* Plane 1 Chinese traditional (not * supported) */ /* Is a leading byte for "official" multibyte encodings? */ #define IS_LC2(c) ((unsigned char)(c) >= 0x90 && (unsigned char)(c) <= 0x99) /* * Postgres-specific prefix bytes for "private" single byte encodings * (According to the MULE docs, we should be using 0x9e for this) */ #define LCPRV1_A 0x9a #define LCPRV1_B 0x9b #define IS_LCPRV1(c) ((unsigned char)(c) == LCPRV1_A || (unsigned char)(c) == LCPRV1_B) #define IS_LCPRV1_A_RANGE(c) \ ((unsigned char)(c) >= 0xa0 && (unsigned char)(c) <= 0xdf) #define IS_LCPRV1_B_RANGE(c) \ ((unsigned char)(c) >= 0xe0 && (unsigned char)(c) <= 0xef) /* * Postgres-specific prefix bytes for "private" multibyte encodings * (According to the MULE docs, we should be using 0x9f for this) */ #define LCPRV2_A 0x9c #define LCPRV2_B 0x9d #define IS_LCPRV2(c) ((unsigned char)(c) == LCPRV2_A || (unsigned char)(c) == LCPRV2_B) #define IS_LCPRV2_A_RANGE(c) \ ((unsigned char)(c) >= 0xf0 && (unsigned char)(c) <= 0xf4) #define IS_LCPRV2_B_RANGE(c) \ ((unsigned char)(c) >= 0xf5 && (unsigned char)(c) <= 0xfe) /* * Charset IDs for private single byte encodings (0xa0-0xef) */ #define LC_SISHENG 0xa0 /* Chinese SiSheng characters for * PinYin/ZhuYin (not supported) */ #define LC_IPA 0xa1 /* IPA (International Phonetic * Association) (not supported) */ #define LC_VISCII_LOWER 0xa2 /* Vietnamese VISCII1.1 lower-case (not * supported) */ #define LC_VISCII_UPPER 0xa3 /* Vietnamese VISCII1.1 upper-case (not * supported) */ #define LC_ARABIC_DIGIT 0xa4 /* Arabic digit (not supported) */ #define LC_ARABIC_1_COLUMN 0xa5 /* Arabic 1-column (not supported) */ #define LC_ASCII_RIGHT_TO_LEFT 0xa6 /* ASCII (left half of ISO8859-1) with * right-to-left direction (not * supported) */ #define LC_LAO 0xa7 /* Lao characters (ISO10646 0E80..0EDF) * (not supported) */ #define LC_ARABIC_2_COLUMN 0xa8 /* Arabic 1-column (not supported) */ /* * Charset IDs for private multibyte encodings (0xf0-0xff) */ #define LC_INDIAN_1_COLUMN 0xf0 /* Indian charset for 1-column width * glyphs (not supported) */ #define LC_TIBETAN_1_COLUMN 0xf1 /* Tibetan 1-column width glyphs (not * supported) */ #define LC_UNICODE_SUBSET_2 0xf2 /* Unicode characters of the range * U+2500..U+33FF. (not supported) */ #define LC_UNICODE_SUBSET_3 0xf3 /* Unicode characters of the range * U+E000..U+FFFF. (not supported) */ #define LC_UNICODE_SUBSET 0xf4 /* Unicode characters of the range * U+0100..U+24FF. (not supported) */ #define LC_ETHIOPIC 0xf5 /* Ethiopic characters (not supported) */ #define LC_CNS11643_3 0xf6 /* CNS 11643-1992 Plane 3 */ #define LC_CNS11643_4 0xf7 /* CNS 11643-1992 Plane 4 */ #define LC_CNS11643_5 0xf8 /* CNS 11643-1992 Plane 5 */ #define LC_CNS11643_6 0xf9 /* CNS 11643-1992 Plane 6 */ #define LC_CNS11643_7 0xfa /* CNS 11643-1992 Plane 7 */ #define LC_INDIAN_2_COLUMN 0xfb /* Indian charset for 2-column width * glyphs (not supported) */ #define LC_TIBETAN 0xfc /* Tibetan (not supported) */ /* #define FREE 0xfd free (unused) */ /* #define FREE 0xfe free (unused) */ /* #define FREE 0xff free (unused) */ /*---------------------------------------------------- * end of MULE stuff *---------------------------------------------------- */ /* * PostgreSQL encoding identifiers * * WARNING: the order of this enum must be same as order of entries * in the pg_enc2name_tbl[] array (in src/common/encnames.c), and * in the pg_wchar_table[] array (in src/common/wchar.c)! * * If you add some encoding don't forget to check * PG_ENCODING_BE_LAST macro. * * PG_SQL_ASCII is default encoding and must be = 0. * * XXX We must avoid renumbering any backend encoding until libpq's major * version number is increased beyond 5; it turns out that the backend * encoding IDs are effectively part of libpq's ABI as far as 8.2 initdb and * psql are concerned. */ typedef enum pg_enc { PG_SQL_ASCII = 0, /* SQL/ASCII */ PG_EUC_JP, /* EUC for Japanese */ PG_EUC_CN, /* EUC for Chinese */ PG_EUC_KR, /* EUC for Korean */ PG_EUC_TW, /* EUC for Taiwan */ PG_EUC_JIS_2004, /* EUC-JIS-2004 */ PG_UTF8, /* Unicode UTF8 */ PG_MULE_INTERNAL, /* Mule internal code */ PG_LATIN1, /* ISO-8859-1 Latin 1 */ PG_LATIN2, /* ISO-8859-2 Latin 2 */ PG_LATIN3, /* ISO-8859-3 Latin 3 */ PG_LATIN4, /* ISO-8859-4 Latin 4 */ PG_LATIN5, /* ISO-8859-9 Latin 5 */ PG_LATIN6, /* ISO-8859-10 Latin6 */ PG_LATIN7, /* ISO-8859-13 Latin7 */ PG_LATIN8, /* ISO-8859-14 Latin8 */ PG_LATIN9, /* ISO-8859-15 Latin9 */ PG_LATIN10, /* ISO-8859-16 Latin10 */ PG_WIN1256, /* windows-1256 */ PG_WIN1258, /* Windows-1258 */ PG_WIN866, /* (MS-DOS CP866) */ PG_WIN874, /* windows-874 */ PG_KOI8R, /* KOI8-R */ PG_WIN1251, /* windows-1251 */ PG_WIN1252, /* windows-1252 */ PG_ISO_8859_5, /* ISO-8859-5 */ PG_ISO_8859_6, /* ISO-8859-6 */ PG_ISO_8859_7, /* ISO-8859-7 */ PG_ISO_8859_8, /* ISO-8859-8 */ PG_WIN1250, /* windows-1250 */ PG_WIN1253, /* windows-1253 */ PG_WIN1254, /* windows-1254 */ PG_WIN1255, /* windows-1255 */ PG_WIN1257, /* windows-1257 */ PG_KOI8U, /* KOI8-U */ /* PG_ENCODING_BE_LAST points to the above entry */ /* following are for client encoding only */ PG_SJIS, /* Shift JIS (Windows-932) */ PG_BIG5, /* Big5 (Windows-950) */ PG_GBK, /* GBK (Windows-936) */ PG_UHC, /* UHC (Windows-949) */ PG_GB18030, /* GB18030 */ PG_JOHAB, /* EUC for Korean JOHAB */ PG_SHIFT_JIS_2004, /* Shift-JIS-2004 */ _PG_LAST_ENCODING_ /* mark only */ } pg_enc; #define PG_ENCODING_BE_LAST PG_KOI8U /* * Please use these tests before access to pg_enc2name_tbl[] * or to other places... */ #define PG_VALID_BE_ENCODING(_enc) \ ((_enc) >= 0 && (_enc) <= PG_ENCODING_BE_LAST) #define PG_ENCODING_IS_CLIENT_ONLY(_enc) \ ((_enc) > PG_ENCODING_BE_LAST && (_enc) < _PG_LAST_ENCODING_) #define PG_VALID_ENCODING(_enc) \ ((_enc) >= 0 && (_enc) < _PG_LAST_ENCODING_) /* On FE are possible all encodings */ #define PG_VALID_FE_ENCODING(_enc) PG_VALID_ENCODING(_enc) /* * When converting strings between different encodings, we assume that space * for converted result is 4-to-1 growth in the worst case. The rate for * currently supported encoding pairs are within 3 (SJIS JIS X0201 half width * kanna -> UTF8 is the worst case). So "4" should be enough for the moment. * * Note that this is not the same as the maximum character width in any * particular encoding. */ #define MAX_CONVERSION_GROWTH 4 /* * Maximum byte length of the string equivalent to any one Unicode code point, * in any backend encoding. The current value assumes that a 4-byte UTF-8 * character might expand by MAX_CONVERSION_GROWTH, which is a huge * overestimate. But in current usage we don't allocate large multiples of * this, so there's little point in being stingy. */ #define MAX_UNICODE_EQUIVALENT_STRING 16 /* * Table for mapping an encoding number to official encoding name and * possibly other subsidiary data. Be careful to check encoding number * before accessing a table entry! * * if (PG_VALID_ENCODING(encoding)) * pg_enc2name_tbl[ encoding ]; */ typedef struct pg_enc2name { const char *name; pg_enc encoding; #ifdef WIN32 unsigned codepage; /* codepage for WIN32 */ #endif } pg_enc2name; extern const pg_enc2name pg_enc2name_tbl[]; /* * Encoding names for gettext */ typedef struct pg_enc2gettext { pg_enc encoding; const char *name; } pg_enc2gettext; extern const pg_enc2gettext pg_enc2gettext_tbl[]; /* * pg_wchar stuff */ typedef int (*mb2wchar_with_len_converter) (const unsigned char *from, pg_wchar *to, int len); typedef int (*wchar2mb_with_len_converter) (const pg_wchar *from, unsigned char *to, int len); typedef int (*mblen_converter) (const unsigned char *mbstr); typedef int (*mbdisplaylen_converter) (const unsigned char *mbstr); typedef bool (*mbcharacter_incrementer) (unsigned char *mbstr, int len); typedef int (*mbverifier) (const unsigned char *mbstr, int len); typedef struct { mb2wchar_with_len_converter mb2wchar_with_len; /* convert a multibyte * string to a wchar */ wchar2mb_with_len_converter wchar2mb_with_len; /* convert a wchar string * to a multibyte */ mblen_converter mblen; /* get byte length of a char */ mbdisplaylen_converter dsplen; /* get display width of a char */ mbverifier mbverify; /* verify multibyte sequence */ int maxmblen; /* max bytes for a char in this encoding */ } pg_wchar_tbl; extern const pg_wchar_tbl pg_wchar_table[]; /* * Data structures for conversions between UTF-8 and other encodings * (UtfToLocal() and LocalToUtf()). In these data structures, characters of * either encoding are represented by uint32 words; hence we can only support * characters up to 4 bytes long. For example, the byte sequence 0xC2 0x89 * would be represented by 0x0000C289, and 0xE8 0xA2 0xB4 by 0x00E8A2B4. * * There are three possible ways a character can be mapped: * * 1. Using a radix tree, from source to destination code. * 2. Using a sorted array of source -> destination code pairs. This * method is used for "combining" characters. There are so few of * them that building a radix tree would be wasteful. * 3. Using a conversion function. */ /* * Radix tree for character conversion. * * Logically, this is actually four different radix trees, for 1-byte, * 2-byte, 3-byte and 4-byte inputs. The 1-byte tree is a simple lookup * table from source to target code. The 2-byte tree consists of two levels: * one lookup table for the first byte, where the value in the lookup table * points to a lookup table for the second byte. And so on. * * Physically, all the trees are stored in one big array, in 'chars16' or * 'chars32', depending on the maximum value that needs to be represented. For * each level in each tree, we also store lower and upper bound of allowed * values - values outside those bounds are considered invalid, and are left * out of the tables. * * In the intermediate levels of the trees, the values stored are offsets * into the chars[16|32] array. * * In the beginning of the chars[16|32] array, there is always a number of * zeros, so that you safely follow an index from an intermediate table * without explicitly checking for a zero. Following a zero any number of * times will always bring you to the dummy, all-zeros table in the * beginning. This helps to shave some cycles when looking up values. */ typedef struct { /* * Array containing all the values. Only one of chars16 or chars32 is * used, depending on how wide the values we need to represent are. */ const uint16 *chars16; const uint32 *chars32; /* Radix tree for 1-byte inputs */ uint32 b1root; /* offset of table in the chars[16|32] array */ uint8 b1_lower; /* min allowed value for a single byte input */ uint8 b1_upper; /* max allowed value for a single byte input */ /* Radix tree for 2-byte inputs */ uint32 b2root; /* offset of 1st byte's table */ uint8 b2_1_lower; /* min/max allowed value for 1st input byte */ uint8 b2_1_upper; uint8 b2_2_lower; /* min/max allowed value for 2nd input byte */ uint8 b2_2_upper; /* Radix tree for 3-byte inputs */ uint32 b3root; /* offset of 1st byte's table */ uint8 b3_1_lower; /* min/max allowed value for 1st input byte */ uint8 b3_1_upper; uint8 b3_2_lower; /* min/max allowed value for 2nd input byte */ uint8 b3_2_upper; uint8 b3_3_lower; /* min/max allowed value for 3rd input byte */ uint8 b3_3_upper; /* Radix tree for 4-byte inputs */ uint32 b4root; /* offset of 1st byte's table */ uint8 b4_1_lower; /* min/max allowed value for 1st input byte */ uint8 b4_1_upper; uint8 b4_2_lower; /* min/max allowed value for 2nd input byte */ uint8 b4_2_upper; uint8 b4_3_lower; /* min/max allowed value for 3rd input byte */ uint8 b4_3_upper; uint8 b4_4_lower; /* min/max allowed value for 4th input byte */ uint8 b4_4_upper; } pg_mb_radix_tree; /* * UTF-8 to local code conversion map (for combined characters) */ typedef struct { uint32 utf1; /* UTF-8 code 1 */ uint32 utf2; /* UTF-8 code 2 */ uint32 code; /* local code */ } pg_utf_to_local_combined; /* * local code to UTF-8 conversion map (for combined characters) */ typedef struct { uint32 code; /* local code */ uint32 utf1; /* UTF-8 code 1 */ uint32 utf2; /* UTF-8 code 2 */ } pg_local_to_utf_combined; /* * callback function for algorithmic encoding conversions (in either direction) * * if function returns zero, it does not know how to convert the code */ typedef uint32 (*utf_local_conversion_func) (uint32 code); /* * Support macro for encoding conversion functions to validate their * arguments. (This could be made more compact if we included fmgr.h * here, but we don't want to do that because this header file is also * used by frontends.) */ #define CHECK_ENCODING_CONVERSION_ARGS(srcencoding,destencoding) \ check_encoding_conversion_args(PG_GETARG_INT32(0), \ PG_GETARG_INT32(1), \ PG_GETARG_INT32(4), \ (srcencoding), \ (destencoding)) /* * Some handy functions for Unicode-specific tests. */ static inline bool is_valid_unicode_codepoint(pg_wchar c) { return (c > 0 && c <= 0x10FFFF); } static inline bool is_utf16_surrogate_first(pg_wchar c) { return (c >= 0xD800 && c <= 0xDBFF); } static inline bool is_utf16_surrogate_second(pg_wchar c) { return (c >= 0xDC00 && c <= 0xDFFF); } static inline pg_wchar surrogate_pair_to_codepoint(pg_wchar first, pg_wchar second) { return ((first & 0x3FF) << 10) + 0x10000 + (second & 0x3FF); } /* * These functions are considered part of libpq's exported API and * are also declared in libpq-fe.h. */ extern int pg_char_to_encoding(const char *name); extern const char *pg_encoding_to_char(int encoding); extern int pg_valid_server_encoding_id(int encoding); /* * These functions are available to frontend code that links with libpgcommon * (in addition to the ones just above). The constant tables declared * earlier in this file are also available from libpgcommon. */ extern int pg_encoding_mblen(int encoding, const char *mbstr); extern int pg_encoding_mblen_bounded(int encoding, const char *mbstr); extern int pg_encoding_dsplen(int encoding, const char *mbstr); extern int pg_encoding_verifymb(int encoding, const char *mbstr, int len); extern int pg_encoding_max_length(int encoding); extern int pg_valid_client_encoding(const char *name); extern int pg_valid_server_encoding(const char *name); extern bool is_encoding_supported_by_icu(int encoding); extern const char *get_encoding_name_for_icu(int encoding); extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string); extern pg_wchar utf8_to_unicode(const unsigned char *c); extern bool pg_utf8_islegal(const unsigned char *source, int length); extern int pg_utf_mblen(const unsigned char *s); extern int pg_mule_mblen(const unsigned char *s); /* * The remaining functions are backend-only. */ extern int pg_mb2wchar(const char *from, pg_wchar *to); extern int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len); extern int pg_encoding_mb2wchar_with_len(int encoding, const char *from, pg_wchar *to, int len); extern int pg_wchar2mb(const pg_wchar *from, char *to); extern int pg_wchar2mb_with_len(const pg_wchar *from, char *to, int len); extern int pg_encoding_wchar2mb_with_len(int encoding, const pg_wchar *from, char *to, int len); extern int pg_char_and_wchar_strcmp(const char *s1, const pg_wchar *s2); extern int pg_wchar_strncmp(const pg_wchar *s1, const pg_wchar *s2, size_t n); extern int pg_char_and_wchar_strncmp(const char *s1, const pg_wchar *s2, size_t n); extern size_t pg_wchar_strlen(const pg_wchar *wstr); extern int pg_mblen(const char *mbstr); extern int pg_dsplen(const char *mbstr); extern int pg_mbstrlen(const char *mbstr); extern int pg_mbstrlen_with_len(const char *mbstr, int len); extern int pg_mbcliplen(const char *mbstr, int len, int limit); extern int pg_encoding_mbcliplen(int encoding, const char *mbstr, int len, int limit); extern int pg_mbcharcliplen(const char *mbstr, int len, int limit); extern int pg_database_encoding_max_length(void); extern mbcharacter_incrementer pg_database_encoding_character_incrementer(void); extern int PrepareClientEncoding(int encoding); extern int SetClientEncoding(int encoding); extern void InitializeClientEncoding(void); extern int pg_get_client_encoding(void); extern const char *pg_get_client_encoding_name(void); extern void SetDatabaseEncoding(int encoding); extern int GetDatabaseEncoding(void); extern const char *GetDatabaseEncodingName(void); extern void SetMessageEncoding(int encoding); extern int GetMessageEncoding(void); #ifdef ENABLE_NLS extern int pg_bind_textdomain_codeset(const char *domainname); #endif extern unsigned char *pg_do_encoding_conversion(unsigned char *src, int len, int src_encoding, int dest_encoding); extern char *pg_client_to_server(const char *s, int len); extern char *pg_server_to_client(const char *s, int len); extern char *pg_any_to_server(const char *s, int len, int encoding); extern char *pg_server_to_any(const char *s, int len, int encoding); extern void pg_unicode_to_server(pg_wchar c, unsigned char *s); extern unsigned short BIG5toCNS(unsigned short big5, unsigned char *lc); extern unsigned short CNStoBIG5(unsigned short cns, unsigned char lc); extern void UtfToLocal(const unsigned char *utf, int len, unsigned char *iso, const pg_mb_radix_tree *map, const pg_utf_to_local_combined *cmap, int cmapsize, utf_local_conversion_func conv_func, int encoding); extern void LocalToUtf(const unsigned char *iso, int len, unsigned char *utf, const pg_mb_radix_tree *map, const pg_local_to_utf_combined *cmap, int cmapsize, utf_local_conversion_func conv_func, int encoding); extern bool pg_verifymbstr(const char *mbstr, int len, bool noError); extern bool pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError); extern int pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError); extern void check_encoding_conversion_args(int src_encoding, int dest_encoding, int len, int expected_src_encoding, int expected_dest_encoding); extern void report_invalid_encoding(int encoding, const char *mbstr, int len) pg_attribute_noreturn(); extern void report_untranslatable_char(int src_encoding, int dest_encoding, const char *mbstr, int len) pg_attribute_noreturn(); extern void local2local(const unsigned char *l, unsigned char *p, int len, int src_encoding, int dest_encoding, const unsigned char *tab); extern void latin2mic(const unsigned char *l, unsigned char *p, int len, int lc, int encoding); extern void mic2latin(const unsigned char *mic, unsigned char *p, int len, int lc, int encoding); extern void latin2mic_with_table(const unsigned char *l, unsigned char *p, int len, int lc, int encoding, const unsigned char *tab); extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p, int len, int lc, int encoding, const unsigned char *tab); #ifdef WIN32 extern WCHAR *pgwin32_message_to_UTF16(const char *str, int len, int *utf16len); #endif #endif /* PG_WCHAR_H */ pgbouncer-1.24.1/include/common/builtins.h0000644000175000000000000000116514777762222015427 00000000000000/*------------------------------------------------------------------------- * * builtins.h * Declarations for operations on built-in types. * * * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * include/common/builtins.h * *------------------------------------------------------------------------- */ /* bool.c */ extern bool parse_bool(const char *value, bool *result); extern bool parse_bool_with_len(const char *value, size_t len, bool *result); extern int pg_strncasecmp(const char *s1, const char *s2, size_t n); pgbouncer-1.24.1/include/common/unicode_norm.h0000644000175000000000000000177014777762222016261 00000000000000/*------------------------------------------------------------------------- * * unicode_norm.h * Routines for normalizing Unicode strings * * These definitions are used by both frontend and backend code. * * Copyright (c) 2017-2020, PostgreSQL Global Development Group * * src/include/common/unicode_norm.h * *------------------------------------------------------------------------- */ #ifndef UNICODE_NORM_H #define UNICODE_NORM_H #include "common/postgres_compat.h" #include "common/pg_wchar.h" typedef enum { UNICODE_NFC = 0, UNICODE_NFD = 1, UNICODE_NFKC = 2, UNICODE_NFKD = 3, } UnicodeNormalizationForm; /* see UAX #15 */ typedef enum { UNICODE_NORM_QC_NO = 0, UNICODE_NORM_QC_YES = 1, UNICODE_NORM_QC_MAYBE = -1, } UnicodeNormalizationQC; extern pg_wchar *unicode_normalize(UnicodeNormalizationForm form, const pg_wchar *input); extern UnicodeNormalizationQC unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const pg_wchar *input); #endif /* UNICODE_NORM_H */ pgbouncer-1.24.1/include/common/saslprep.h0000644000175000000000000000152514777762222015427 00000000000000/*------------------------------------------------------------------------- * * saslprep.h * SASLprep normalization, for SCRAM authentication * * These definitions are used by both frontend and backend code. * * Copyright (c) 2017-2020, PostgreSQL Global Development Group * * src/include/common/saslprep.h * *------------------------------------------------------------------------- */ #ifndef SASLPREP_H #define SASLPREP_H /* * Return codes for pg_saslprep() function. */ typedef enum { SASLPREP_SUCCESS = 0, SASLPREP_OOM = -1, /* out of memory (only in frontend) */ SASLPREP_INVALID_UTF8 = -2, /* input is not a valid UTF-8 string */ SASLPREP_PROHIBITED = -3 /* output would contain prohibited characters */ } pg_saslprep_rc; extern pg_saslprep_rc pg_saslprep(const char *input, char **output); #endif /* SASLPREP_H */ pgbouncer-1.24.1/include/common/uthash_lowercase.h0000644000175000000000000001130314777762222017131 00000000000000/* Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This header provides the HASH_FUNCTION and the HASH_CMP overrides for a case * insensitive uthash with key type of string. */ #undef HASH_FUNCTION #define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN_LOWERCASE(keyptr, keylen, hashv) /* This is a modified version of HASH_JEN provided by uthash. * It is the same algorithm except that every char is turned to lower case. */ #define HASH_JEN_LOWERCASE(key,keylen,hashv) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ unsigned const char *_hj_key=(unsigned const char*)(key); \ hashv = 0xfeedbeefu; \ _hj_i = _hj_j = 0x9e3779b9u; \ _hj_k = (unsigned)(keylen); \ while (_hj_k >= 12U) { \ _hj_i += (tolower(_hj_key[0]) + ( (unsigned)tolower(_hj_key[1]) << 8 ) \ + ( (unsigned)tolower(_hj_key[2]) << 16 ) \ + ( (unsigned)tolower(_hj_key[3]) << 24 ) ); \ _hj_j += (tolower(_hj_key[4]) + ( (unsigned)tolower(_hj_key[5]) << 8 ) \ + ( (unsigned)tolower(_hj_key[6]) << 16 ) \ + ( (unsigned)tolower(_hj_key[7]) << 24 ) ); \ hashv += (tolower(_hj_key[8]) + ( (unsigned)tolower(_hj_key[9]) << 8 ) \ + ( (unsigned)tolower(_hj_key[10]) << 16 ) \ + ( (unsigned)tolower(_hj_key[11]) << 24 ) ); \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ _hj_key += 12; \ _hj_k -= 12U; \ } \ hashv += (unsigned)(keylen); \ switch ( _hj_k ) { \ case 11: hashv += ( (unsigned)tolower(_hj_key[10]) << 24 ); /* FALLTHROUGH */ \ case 10: hashv += ( (unsigned)tolower(_hj_key[9]) << 16 ); /* FALLTHROUGH */ \ case 9: hashv += ( (unsigned)tolower(_hj_key[8]) << 8 ); /* FALLTHROUGH */ \ case 8: _hj_j += ( (unsigned)tolower(_hj_key[7]) << 24 ); /* FALLTHROUGH */ \ case 7: _hj_j += ( (unsigned)tolower(_hj_key[6]) << 16 ); /* FALLTHROUGH */ \ case 6: _hj_j += ( (unsigned)tolower(_hj_key[5]) << 8 ); /* FALLTHROUGH */ \ case 5: _hj_j += tolower(_hj_key[4]); /* FALLTHROUGH */ \ case 4: _hj_i += ( (unsigned)tolower(_hj_key[3]) << 24 ); /* FALLTHROUGH */ \ case 3: _hj_i += ( (unsigned)tolower(_hj_key[2]) << 16 ); /* FALLTHROUGH */ \ case 2: _hj_i += ( (unsigned)tolower(_hj_key[1]) << 8 ); /* FALLTHROUGH */ \ case 1: _hj_i += tolower(_hj_key[0]); /* FALLTHROUGH */ \ default: ; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ } while (0) #undef HASH_KEYCMP #define HASH_KEYCMP(a,b,len) (strcasecmp(a,b)) pgbouncer-1.24.1/include/common/unicode_norm_table.h0000644000175000000000000125100214777762222017424 00000000000000/*------------------------------------------------------------------------- * * unicode_norm_table.h * Composition table used for Unicode normalization * * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/include/common/unicode_norm_table.h * *------------------------------------------------------------------------- */ /* * File auto-generated by src/common/unicode/generate-unicode_norm_table.pl, * do not edit. There is deliberately not an #ifndef PG_UNICODE_NORM_TABLE_H * here. */ typedef struct { uint32 codepoint; /* Unicode codepoint */ uint8 comb_class; /* combining class of character */ uint8 dec_size_flags; /* size and flags of decomposition code list */ uint16 dec_index; /* index into UnicodeDecomp_codepoints, or the * decomposition itself if DECOMP_INLINE */ } pg_unicode_decomposition; #define DECOMP_NO_COMPOSE 0x80 /* don't use for re-composition */ #define DECOMP_INLINE 0x40 /* decomposition is stored inline in * dec_index */ #define DECOMP_COMPAT 0x20 /* compatibility mapping */ #define DECOMPOSITION_SIZE(x) ((x)->dec_size_flags & 0x1F) #define DECOMPOSITION_NO_COMPOSE(x) (((x)->dec_size_flags & (DECOMP_NO_COMPOSE | DECOMP_COMPAT)) != 0) #define DECOMPOSITION_IS_INLINE(x) (((x)->dec_size_flags & DECOMP_INLINE) != 0) #define DECOMPOSITION_IS_COMPAT(x) (((x)->dec_size_flags & DECOMP_COMPAT) != 0) /* Table of Unicode codepoints and their decompositions */ static const pg_unicode_decomposition UnicodeDecompMain[6604] = { {0x00A0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x00A8, 0, 2 | DECOMP_COMPAT, 0}, {0x00AA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x00AF, 0, 2 | DECOMP_COMPAT, 2}, {0x00B2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x00B3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x00B4, 0, 2 | DECOMP_COMPAT, 4}, {0x00B5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BC}, {0x00B8, 0, 2 | DECOMP_COMPAT, 6}, {0x00B9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x00BA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x00BC, 0, 3 | DECOMP_COMPAT, 8}, {0x00BD, 0, 3 | DECOMP_COMPAT, 11}, {0x00BE, 0, 3 | DECOMP_COMPAT, 14}, {0x00C0, 0, 2, 17}, {0x00C1, 0, 2, 19}, {0x00C2, 0, 2, 21}, {0x00C3, 0, 2, 23}, {0x00C4, 0, 2, 25}, {0x00C5, 0, 2, 27}, {0x00C7, 0, 2, 29}, {0x00C8, 0, 2, 31}, {0x00C9, 0, 2, 33}, {0x00CA, 0, 2, 35}, {0x00CB, 0, 2, 37}, {0x00CC, 0, 2, 39}, {0x00CD, 0, 2, 41}, {0x00CE, 0, 2, 43}, {0x00CF, 0, 2, 45}, {0x00D1, 0, 2, 47}, {0x00D2, 0, 2, 49}, {0x00D3, 0, 2, 51}, {0x00D4, 0, 2, 53}, {0x00D5, 0, 2, 55}, {0x00D6, 0, 2, 57}, {0x00D9, 0, 2, 59}, {0x00DA, 0, 2, 61}, {0x00DB, 0, 2, 63}, {0x00DC, 0, 2, 65}, {0x00DD, 0, 2, 67}, {0x00E0, 0, 2, 69}, {0x00E1, 0, 2, 71}, {0x00E2, 0, 2, 73}, {0x00E3, 0, 2, 75}, {0x00E4, 0, 2, 77}, {0x00E5, 0, 2, 79}, {0x00E7, 0, 2, 81}, {0x00E8, 0, 2, 83}, {0x00E9, 0, 2, 85}, {0x00EA, 0, 2, 87}, {0x00EB, 0, 2, 89}, {0x00EC, 0, 2, 91}, {0x00ED, 0, 2, 93}, {0x00EE, 0, 2, 95}, {0x00EF, 0, 2, 97}, {0x00F1, 0, 2, 99}, {0x00F2, 0, 2, 101}, {0x00F3, 0, 2, 103}, {0x00F4, 0, 2, 105}, {0x00F5, 0, 2, 107}, {0x00F6, 0, 2, 109}, {0x00F9, 0, 2, 111}, {0x00FA, 0, 2, 113}, {0x00FB, 0, 2, 115}, {0x00FC, 0, 2, 117}, {0x00FD, 0, 2, 119}, {0x00FF, 0, 2, 121}, {0x0100, 0, 2, 123}, {0x0101, 0, 2, 125}, {0x0102, 0, 2, 127}, {0x0103, 0, 2, 129}, {0x0104, 0, 2, 131}, {0x0105, 0, 2, 133}, {0x0106, 0, 2, 135}, {0x0107, 0, 2, 137}, {0x0108, 0, 2, 139}, {0x0109, 0, 2, 141}, {0x010A, 0, 2, 143}, {0x010B, 0, 2, 145}, {0x010C, 0, 2, 147}, {0x010D, 0, 2, 149}, {0x010E, 0, 2, 151}, {0x010F, 0, 2, 153}, {0x0112, 0, 2, 155}, {0x0113, 0, 2, 157}, {0x0114, 0, 2, 159}, {0x0115, 0, 2, 161}, {0x0116, 0, 2, 163}, {0x0117, 0, 2, 165}, {0x0118, 0, 2, 167}, {0x0119, 0, 2, 169}, {0x011A, 0, 2, 171}, {0x011B, 0, 2, 173}, {0x011C, 0, 2, 175}, {0x011D, 0, 2, 177}, {0x011E, 0, 2, 179}, {0x011F, 0, 2, 181}, {0x0120, 0, 2, 183}, {0x0121, 0, 2, 185}, {0x0122, 0, 2, 187}, {0x0123, 0, 2, 189}, {0x0124, 0, 2, 191}, {0x0125, 0, 2, 193}, {0x0128, 0, 2, 195}, {0x0129, 0, 2, 197}, {0x012A, 0, 2, 199}, {0x012B, 0, 2, 201}, {0x012C, 0, 2, 203}, {0x012D, 0, 2, 205}, {0x012E, 0, 2, 207}, {0x012F, 0, 2, 209}, {0x0130, 0, 2, 211}, {0x0132, 0, 2 | DECOMP_COMPAT, 213}, {0x0133, 0, 2 | DECOMP_COMPAT, 215}, {0x0134, 0, 2, 217}, {0x0135, 0, 2, 219}, {0x0136, 0, 2, 221}, {0x0137, 0, 2, 223}, {0x0139, 0, 2, 225}, {0x013A, 0, 2, 227}, {0x013B, 0, 2, 229}, {0x013C, 0, 2, 231}, {0x013D, 0, 2, 233}, {0x013E, 0, 2, 235}, {0x013F, 0, 2 | DECOMP_COMPAT, 237}, {0x0140, 0, 2 | DECOMP_COMPAT, 239}, {0x0143, 0, 2, 241}, {0x0144, 0, 2, 243}, {0x0145, 0, 2, 245}, {0x0146, 0, 2, 247}, {0x0147, 0, 2, 249}, {0x0148, 0, 2, 251}, {0x0149, 0, 2 | DECOMP_COMPAT, 253}, {0x014C, 0, 2, 255}, {0x014D, 0, 2, 257}, {0x014E, 0, 2, 259}, {0x014F, 0, 2, 261}, {0x0150, 0, 2, 263}, {0x0151, 0, 2, 265}, {0x0154, 0, 2, 267}, {0x0155, 0, 2, 269}, {0x0156, 0, 2, 271}, {0x0157, 0, 2, 273}, {0x0158, 0, 2, 275}, {0x0159, 0, 2, 277}, {0x015A, 0, 2, 279}, {0x015B, 0, 2, 281}, {0x015C, 0, 2, 283}, {0x015D, 0, 2, 285}, {0x015E, 0, 2, 287}, {0x015F, 0, 2, 289}, {0x0160, 0, 2, 291}, {0x0161, 0, 2, 293}, {0x0162, 0, 2, 295}, {0x0163, 0, 2, 297}, {0x0164, 0, 2, 299}, {0x0165, 0, 2, 301}, {0x0168, 0, 2, 303}, {0x0169, 0, 2, 305}, {0x016A, 0, 2, 307}, {0x016B, 0, 2, 309}, {0x016C, 0, 2, 311}, {0x016D, 0, 2, 313}, {0x016E, 0, 2, 315}, {0x016F, 0, 2, 317}, {0x0170, 0, 2, 319}, {0x0171, 0, 2, 321}, {0x0172, 0, 2, 323}, {0x0173, 0, 2, 325}, {0x0174, 0, 2, 327}, {0x0175, 0, 2, 329}, {0x0176, 0, 2, 331}, {0x0177, 0, 2, 333}, {0x0178, 0, 2, 335}, {0x0179, 0, 2, 337}, {0x017A, 0, 2, 339}, {0x017B, 0, 2, 341}, {0x017C, 0, 2, 343}, {0x017D, 0, 2, 345}, {0x017E, 0, 2, 347}, {0x017F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x01A0, 0, 2, 349}, {0x01A1, 0, 2, 351}, {0x01AF, 0, 2, 353}, {0x01B0, 0, 2, 355}, {0x01C4, 0, 2 | DECOMP_COMPAT, 357}, {0x01C5, 0, 2 | DECOMP_COMPAT, 359}, {0x01C6, 0, 2 | DECOMP_COMPAT, 361}, {0x01C7, 0, 2 | DECOMP_COMPAT, 363}, {0x01C8, 0, 2 | DECOMP_COMPAT, 365}, {0x01C9, 0, 2 | DECOMP_COMPAT, 367}, {0x01CA, 0, 2 | DECOMP_COMPAT, 369}, {0x01CB, 0, 2 | DECOMP_COMPAT, 371}, {0x01CC, 0, 2 | DECOMP_COMPAT, 373}, {0x01CD, 0, 2, 375}, {0x01CE, 0, 2, 377}, {0x01CF, 0, 2, 379}, {0x01D0, 0, 2, 381}, {0x01D1, 0, 2, 383}, {0x01D2, 0, 2, 385}, {0x01D3, 0, 2, 387}, {0x01D4, 0, 2, 389}, {0x01D5, 0, 2, 391}, {0x01D6, 0, 2, 393}, {0x01D7, 0, 2, 395}, {0x01D8, 0, 2, 397}, {0x01D9, 0, 2, 399}, {0x01DA, 0, 2, 401}, {0x01DB, 0, 2, 403}, {0x01DC, 0, 2, 405}, {0x01DE, 0, 2, 407}, {0x01DF, 0, 2, 409}, {0x01E0, 0, 2, 411}, {0x01E1, 0, 2, 413}, {0x01E2, 0, 2, 415}, {0x01E3, 0, 2, 417}, {0x01E6, 0, 2, 419}, {0x01E7, 0, 2, 421}, {0x01E8, 0, 2, 423}, {0x01E9, 0, 2, 425}, {0x01EA, 0, 2, 427}, {0x01EB, 0, 2, 429}, {0x01EC, 0, 2, 431}, {0x01ED, 0, 2, 433}, {0x01EE, 0, 2, 435}, {0x01EF, 0, 2, 437}, {0x01F0, 0, 2, 439}, {0x01F1, 0, 2 | DECOMP_COMPAT, 441}, {0x01F2, 0, 2 | DECOMP_COMPAT, 443}, {0x01F3, 0, 2 | DECOMP_COMPAT, 445}, {0x01F4, 0, 2, 447}, {0x01F5, 0, 2, 449}, {0x01F8, 0, 2, 451}, {0x01F9, 0, 2, 453}, {0x01FA, 0, 2, 455}, {0x01FB, 0, 2, 457}, {0x01FC, 0, 2, 459}, {0x01FD, 0, 2, 461}, {0x01FE, 0, 2, 463}, {0x01FF, 0, 2, 465}, {0x0200, 0, 2, 467}, {0x0201, 0, 2, 469}, {0x0202, 0, 2, 471}, {0x0203, 0, 2, 473}, {0x0204, 0, 2, 475}, {0x0205, 0, 2, 477}, {0x0206, 0, 2, 479}, {0x0207, 0, 2, 481}, {0x0208, 0, 2, 483}, {0x0209, 0, 2, 485}, {0x020A, 0, 2, 487}, {0x020B, 0, 2, 489}, {0x020C, 0, 2, 491}, {0x020D, 0, 2, 493}, {0x020E, 0, 2, 495}, {0x020F, 0, 2, 497}, {0x0210, 0, 2, 499}, {0x0211, 0, 2, 501}, {0x0212, 0, 2, 503}, {0x0213, 0, 2, 505}, {0x0214, 0, 2, 507}, {0x0215, 0, 2, 509}, {0x0216, 0, 2, 511}, {0x0217, 0, 2, 513}, {0x0218, 0, 2, 515}, {0x0219, 0, 2, 517}, {0x021A, 0, 2, 519}, {0x021B, 0, 2, 521}, {0x021E, 0, 2, 523}, {0x021F, 0, 2, 525}, {0x0226, 0, 2, 527}, {0x0227, 0, 2, 529}, {0x0228, 0, 2, 531}, {0x0229, 0, 2, 533}, {0x022A, 0, 2, 535}, {0x022B, 0, 2, 537}, {0x022C, 0, 2, 539}, {0x022D, 0, 2, 541}, {0x022E, 0, 2, 543}, {0x022F, 0, 2, 545}, {0x0230, 0, 2, 547}, {0x0231, 0, 2, 549}, {0x0232, 0, 2, 551}, {0x0233, 0, 2, 553}, {0x02B0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x02B1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0266}, {0x02B2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x02B3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x02B4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0279}, {0x02B5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x027B}, {0x02B6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0281}, {0x02B7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x02B8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x02D8, 0, 2 | DECOMP_COMPAT, 555}, {0x02D9, 0, 2 | DECOMP_COMPAT, 557}, {0x02DA, 0, 2 | DECOMP_COMPAT, 559}, {0x02DB, 0, 2 | DECOMP_COMPAT, 561}, {0x02DC, 0, 2 | DECOMP_COMPAT, 563}, {0x02DD, 0, 2 | DECOMP_COMPAT, 565}, {0x02E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0263}, {0x02E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x02E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x02E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x02E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0295}, {0x0300, 230, 0, 0}, {0x0301, 230, 0, 0}, {0x0302, 230, 0, 0}, {0x0303, 230, 0, 0}, {0x0304, 230, 0, 0}, {0x0305, 230, 0, 0}, {0x0306, 230, 0, 0}, {0x0307, 230, 0, 0}, {0x0308, 230, 0, 0}, {0x0309, 230, 0, 0}, {0x030A, 230, 0, 0}, {0x030B, 230, 0, 0}, {0x030C, 230, 0, 0}, {0x030D, 230, 0, 0}, {0x030E, 230, 0, 0}, {0x030F, 230, 0, 0}, {0x0310, 230, 0, 0}, {0x0311, 230, 0, 0}, {0x0312, 230, 0, 0}, {0x0313, 230, 0, 0}, {0x0314, 230, 0, 0}, {0x0315, 232, 0, 0}, {0x0316, 220, 0, 0}, {0x0317, 220, 0, 0}, {0x0318, 220, 0, 0}, {0x0319, 220, 0, 0}, {0x031A, 232, 0, 0}, {0x031B, 216, 0, 0}, {0x031C, 220, 0, 0}, {0x031D, 220, 0, 0}, {0x031E, 220, 0, 0}, {0x031F, 220, 0, 0}, {0x0320, 220, 0, 0}, {0x0321, 202, 0, 0}, {0x0322, 202, 0, 0}, {0x0323, 220, 0, 0}, {0x0324, 220, 0, 0}, {0x0325, 220, 0, 0}, {0x0326, 220, 0, 0}, {0x0327, 202, 0, 0}, {0x0328, 202, 0, 0}, {0x0329, 220, 0, 0}, {0x032A, 220, 0, 0}, {0x032B, 220, 0, 0}, {0x032C, 220, 0, 0}, {0x032D, 220, 0, 0}, {0x032E, 220, 0, 0}, {0x032F, 220, 0, 0}, {0x0330, 220, 0, 0}, {0x0331, 220, 0, 0}, {0x0332, 220, 0, 0}, {0x0333, 220, 0, 0}, {0x0334, 1, 0, 0}, {0x0335, 1, 0, 0}, {0x0336, 1, 0, 0}, {0x0337, 1, 0, 0}, {0x0338, 1, 0, 0}, {0x0339, 220, 0, 0}, {0x033A, 220, 0, 0}, {0x033B, 220, 0, 0}, {0x033C, 220, 0, 0}, {0x033D, 230, 0, 0}, {0x033E, 230, 0, 0}, {0x033F, 230, 0, 0}, {0x0340, 230, 1 | DECOMP_INLINE, 0x0300}, {0x0341, 230, 1 | DECOMP_INLINE, 0x0301}, {0x0342, 230, 0, 0}, {0x0343, 230, 1 | DECOMP_INLINE, 0x0313}, {0x0344, 230, 2 | DECOMP_NO_COMPOSE, 567}, /* non-starter decomposition */ {0x0345, 240, 0, 0}, {0x0346, 230, 0, 0}, {0x0347, 220, 0, 0}, {0x0348, 220, 0, 0}, {0x0349, 220, 0, 0}, {0x034A, 230, 0, 0}, {0x034B, 230, 0, 0}, {0x034C, 230, 0, 0}, {0x034D, 220, 0, 0}, {0x034E, 220, 0, 0}, {0x0350, 230, 0, 0}, {0x0351, 230, 0, 0}, {0x0352, 230, 0, 0}, {0x0353, 220, 0, 0}, {0x0354, 220, 0, 0}, {0x0355, 220, 0, 0}, {0x0356, 220, 0, 0}, {0x0357, 230, 0, 0}, {0x0358, 232, 0, 0}, {0x0359, 220, 0, 0}, {0x035A, 220, 0, 0}, {0x035B, 230, 0, 0}, {0x035C, 233, 0, 0}, {0x035D, 234, 0, 0}, {0x035E, 234, 0, 0}, {0x035F, 233, 0, 0}, {0x0360, 234, 0, 0}, {0x0361, 234, 0, 0}, {0x0362, 233, 0, 0}, {0x0363, 230, 0, 0}, {0x0364, 230, 0, 0}, {0x0365, 230, 0, 0}, {0x0366, 230, 0, 0}, {0x0367, 230, 0, 0}, {0x0368, 230, 0, 0}, {0x0369, 230, 0, 0}, {0x036A, 230, 0, 0}, {0x036B, 230, 0, 0}, {0x036C, 230, 0, 0}, {0x036D, 230, 0, 0}, {0x036E, 230, 0, 0}, {0x036F, 230, 0, 0}, {0x0374, 0, 1 | DECOMP_INLINE, 0x02B9}, {0x037A, 0, 2 | DECOMP_COMPAT, 569}, {0x037E, 0, 1 | DECOMP_INLINE, 0x003B}, {0x0384, 0, 2 | DECOMP_COMPAT, 571}, {0x0385, 0, 2, 573}, {0x0386, 0, 2, 575}, {0x0387, 0, 1 | DECOMP_INLINE, 0x00B7}, {0x0388, 0, 2, 577}, {0x0389, 0, 2, 579}, {0x038A, 0, 2, 581}, {0x038C, 0, 2, 583}, {0x038E, 0, 2, 585}, {0x038F, 0, 2, 587}, {0x0390, 0, 2, 589}, {0x03AA, 0, 2, 591}, {0x03AB, 0, 2, 593}, {0x03AC, 0, 2, 595}, {0x03AD, 0, 2, 597}, {0x03AE, 0, 2, 599}, {0x03AF, 0, 2, 601}, {0x03B0, 0, 2, 603}, {0x03CA, 0, 2, 605}, {0x03CB, 0, 2, 607}, {0x03CC, 0, 2, 609}, {0x03CD, 0, 2, 611}, {0x03CE, 0, 2, 613}, {0x03D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x03D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x03D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A5}, {0x03D3, 0, 2, 615}, {0x03D4, 0, 2, 617}, {0x03D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x03D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x03F0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BA}, {0x03F1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x03F2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C2}, {0x03F4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0398}, {0x03F5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B5}, {0x03F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A3}, {0x0400, 0, 2, 619}, {0x0401, 0, 2, 621}, {0x0403, 0, 2, 623}, {0x0407, 0, 2, 625}, {0x040C, 0, 2, 627}, {0x040D, 0, 2, 629}, {0x040E, 0, 2, 631}, {0x0419, 0, 2, 633}, {0x0439, 0, 2, 635}, {0x0450, 0, 2, 637}, {0x0451, 0, 2, 639}, {0x0453, 0, 2, 641}, {0x0457, 0, 2, 643}, {0x045C, 0, 2, 645}, {0x045D, 0, 2, 647}, {0x045E, 0, 2, 649}, {0x0476, 0, 2, 651}, {0x0477, 0, 2, 653}, {0x0483, 230, 0, 0}, {0x0484, 230, 0, 0}, {0x0485, 230, 0, 0}, {0x0486, 230, 0, 0}, {0x0487, 230, 0, 0}, {0x04C1, 0, 2, 655}, {0x04C2, 0, 2, 657}, {0x04D0, 0, 2, 659}, {0x04D1, 0, 2, 661}, {0x04D2, 0, 2, 663}, {0x04D3, 0, 2, 665}, {0x04D6, 0, 2, 667}, {0x04D7, 0, 2, 669}, {0x04DA, 0, 2, 671}, {0x04DB, 0, 2, 673}, {0x04DC, 0, 2, 675}, {0x04DD, 0, 2, 677}, {0x04DE, 0, 2, 679}, {0x04DF, 0, 2, 681}, {0x04E2, 0, 2, 683}, {0x04E3, 0, 2, 685}, {0x04E4, 0, 2, 687}, {0x04E5, 0, 2, 689}, {0x04E6, 0, 2, 691}, {0x04E7, 0, 2, 693}, {0x04EA, 0, 2, 695}, {0x04EB, 0, 2, 697}, {0x04EC, 0, 2, 699}, {0x04ED, 0, 2, 701}, {0x04EE, 0, 2, 703}, {0x04EF, 0, 2, 705}, {0x04F0, 0, 2, 707}, {0x04F1, 0, 2, 709}, {0x04F2, 0, 2, 711}, {0x04F3, 0, 2, 713}, {0x04F4, 0, 2, 715}, {0x04F5, 0, 2, 717}, {0x04F8, 0, 2, 719}, {0x04F9, 0, 2, 721}, {0x0587, 0, 2 | DECOMP_COMPAT, 723}, {0x0591, 220, 0, 0}, {0x0592, 230, 0, 0}, {0x0593, 230, 0, 0}, {0x0594, 230, 0, 0}, {0x0595, 230, 0, 0}, {0x0596, 220, 0, 0}, {0x0597, 230, 0, 0}, {0x0598, 230, 0, 0}, {0x0599, 230, 0, 0}, {0x059A, 222, 0, 0}, {0x059B, 220, 0, 0}, {0x059C, 230, 0, 0}, {0x059D, 230, 0, 0}, {0x059E, 230, 0, 0}, {0x059F, 230, 0, 0}, {0x05A0, 230, 0, 0}, {0x05A1, 230, 0, 0}, {0x05A2, 220, 0, 0}, {0x05A3, 220, 0, 0}, {0x05A4, 220, 0, 0}, {0x05A5, 220, 0, 0}, {0x05A6, 220, 0, 0}, {0x05A7, 220, 0, 0}, {0x05A8, 230, 0, 0}, {0x05A9, 230, 0, 0}, {0x05AA, 220, 0, 0}, {0x05AB, 230, 0, 0}, {0x05AC, 230, 0, 0}, {0x05AD, 222, 0, 0}, {0x05AE, 228, 0, 0}, {0x05AF, 230, 0, 0}, {0x05B0, 10, 0, 0}, {0x05B1, 11, 0, 0}, {0x05B2, 12, 0, 0}, {0x05B3, 13, 0, 0}, {0x05B4, 14, 0, 0}, {0x05B5, 15, 0, 0}, {0x05B6, 16, 0, 0}, {0x05B7, 17, 0, 0}, {0x05B8, 18, 0, 0}, {0x05B9, 19, 0, 0}, {0x05BA, 19, 0, 0}, {0x05BB, 20, 0, 0}, {0x05BC, 21, 0, 0}, {0x05BD, 22, 0, 0}, {0x05BF, 23, 0, 0}, {0x05C1, 24, 0, 0}, {0x05C2, 25, 0, 0}, {0x05C4, 230, 0, 0}, {0x05C5, 220, 0, 0}, {0x05C7, 18, 0, 0}, {0x0610, 230, 0, 0}, {0x0611, 230, 0, 0}, {0x0612, 230, 0, 0}, {0x0613, 230, 0, 0}, {0x0614, 230, 0, 0}, {0x0615, 230, 0, 0}, {0x0616, 230, 0, 0}, {0x0617, 230, 0, 0}, {0x0618, 30, 0, 0}, {0x0619, 31, 0, 0}, {0x061A, 32, 0, 0}, {0x0622, 0, 2, 725}, {0x0623, 0, 2, 727}, {0x0624, 0, 2, 729}, {0x0625, 0, 2, 731}, {0x0626, 0, 2, 733}, {0x064B, 27, 0, 0}, {0x064C, 28, 0, 0}, {0x064D, 29, 0, 0}, {0x064E, 30, 0, 0}, {0x064F, 31, 0, 0}, {0x0650, 32, 0, 0}, {0x0651, 33, 0, 0}, {0x0652, 34, 0, 0}, {0x0653, 230, 0, 0}, {0x0654, 230, 0, 0}, {0x0655, 220, 0, 0}, {0x0656, 220, 0, 0}, {0x0657, 230, 0, 0}, {0x0658, 230, 0, 0}, {0x0659, 230, 0, 0}, {0x065A, 230, 0, 0}, {0x065B, 230, 0, 0}, {0x065C, 220, 0, 0}, {0x065D, 230, 0, 0}, {0x065E, 230, 0, 0}, {0x065F, 220, 0, 0}, {0x0670, 35, 0, 0}, {0x0675, 0, 2 | DECOMP_COMPAT, 735}, {0x0676, 0, 2 | DECOMP_COMPAT, 737}, {0x0677, 0, 2 | DECOMP_COMPAT, 739}, {0x0678, 0, 2 | DECOMP_COMPAT, 741}, {0x06C0, 0, 2, 743}, {0x06C2, 0, 2, 745}, {0x06D3, 0, 2, 747}, {0x06D6, 230, 0, 0}, {0x06D7, 230, 0, 0}, {0x06D8, 230, 0, 0}, {0x06D9, 230, 0, 0}, {0x06DA, 230, 0, 0}, {0x06DB, 230, 0, 0}, {0x06DC, 230, 0, 0}, {0x06DF, 230, 0, 0}, {0x06E0, 230, 0, 0}, {0x06E1, 230, 0, 0}, {0x06E2, 230, 0, 0}, {0x06E3, 220, 0, 0}, {0x06E4, 230, 0, 0}, {0x06E7, 230, 0, 0}, {0x06E8, 230, 0, 0}, {0x06EA, 220, 0, 0}, {0x06EB, 230, 0, 0}, {0x06EC, 230, 0, 0}, {0x06ED, 220, 0, 0}, {0x0711, 36, 0, 0}, {0x0730, 230, 0, 0}, {0x0731, 220, 0, 0}, {0x0732, 230, 0, 0}, {0x0733, 230, 0, 0}, {0x0734, 220, 0, 0}, {0x0735, 230, 0, 0}, {0x0736, 230, 0, 0}, {0x0737, 220, 0, 0}, {0x0738, 220, 0, 0}, {0x0739, 220, 0, 0}, {0x073A, 230, 0, 0}, {0x073B, 220, 0, 0}, {0x073C, 220, 0, 0}, {0x073D, 230, 0, 0}, {0x073E, 220, 0, 0}, {0x073F, 230, 0, 0}, {0x0740, 230, 0, 0}, {0x0741, 230, 0, 0}, {0x0742, 220, 0, 0}, {0x0743, 230, 0, 0}, {0x0744, 220, 0, 0}, {0x0745, 230, 0, 0}, {0x0746, 220, 0, 0}, {0x0747, 230, 0, 0}, {0x0748, 220, 0, 0}, {0x0749, 230, 0, 0}, {0x074A, 230, 0, 0}, {0x07EB, 230, 0, 0}, {0x07EC, 230, 0, 0}, {0x07ED, 230, 0, 0}, {0x07EE, 230, 0, 0}, {0x07EF, 230, 0, 0}, {0x07F0, 230, 0, 0}, {0x07F1, 230, 0, 0}, {0x07F2, 220, 0, 0}, {0x07F3, 230, 0, 0}, {0x07FD, 220, 0, 0}, {0x0816, 230, 0, 0}, {0x0817, 230, 0, 0}, {0x0818, 230, 0, 0}, {0x0819, 230, 0, 0}, {0x081B, 230, 0, 0}, {0x081C, 230, 0, 0}, {0x081D, 230, 0, 0}, {0x081E, 230, 0, 0}, {0x081F, 230, 0, 0}, {0x0820, 230, 0, 0}, {0x0821, 230, 0, 0}, {0x0822, 230, 0, 0}, {0x0823, 230, 0, 0}, {0x0825, 230, 0, 0}, {0x0826, 230, 0, 0}, {0x0827, 230, 0, 0}, {0x0829, 230, 0, 0}, {0x082A, 230, 0, 0}, {0x082B, 230, 0, 0}, {0x082C, 230, 0, 0}, {0x082D, 230, 0, 0}, {0x0859, 220, 0, 0}, {0x085A, 220, 0, 0}, {0x085B, 220, 0, 0}, {0x08D3, 220, 0, 0}, {0x08D4, 230, 0, 0}, {0x08D5, 230, 0, 0}, {0x08D6, 230, 0, 0}, {0x08D7, 230, 0, 0}, {0x08D8, 230, 0, 0}, {0x08D9, 230, 0, 0}, {0x08DA, 230, 0, 0}, {0x08DB, 230, 0, 0}, {0x08DC, 230, 0, 0}, {0x08DD, 230, 0, 0}, {0x08DE, 230, 0, 0}, {0x08DF, 230, 0, 0}, {0x08E0, 230, 0, 0}, {0x08E1, 230, 0, 0}, {0x08E3, 220, 0, 0}, {0x08E4, 230, 0, 0}, {0x08E5, 230, 0, 0}, {0x08E6, 220, 0, 0}, {0x08E7, 230, 0, 0}, {0x08E8, 230, 0, 0}, {0x08E9, 220, 0, 0}, {0x08EA, 230, 0, 0}, {0x08EB, 230, 0, 0}, {0x08EC, 230, 0, 0}, {0x08ED, 220, 0, 0}, {0x08EE, 220, 0, 0}, {0x08EF, 220, 0, 0}, {0x08F0, 27, 0, 0}, {0x08F1, 28, 0, 0}, {0x08F2, 29, 0, 0}, {0x08F3, 230, 0, 0}, {0x08F4, 230, 0, 0}, {0x08F5, 230, 0, 0}, {0x08F6, 220, 0, 0}, {0x08F7, 230, 0, 0}, {0x08F8, 230, 0, 0}, {0x08F9, 220, 0, 0}, {0x08FA, 220, 0, 0}, {0x08FB, 230, 0, 0}, {0x08FC, 230, 0, 0}, {0x08FD, 230, 0, 0}, {0x08FE, 230, 0, 0}, {0x08FF, 230, 0, 0}, {0x0929, 0, 2, 749}, {0x0931, 0, 2, 751}, {0x0934, 0, 2, 753}, {0x093C, 7, 0, 0}, {0x094D, 9, 0, 0}, {0x0951, 230, 0, 0}, {0x0952, 220, 0, 0}, {0x0953, 230, 0, 0}, {0x0954, 230, 0, 0}, {0x0958, 0, 2 | DECOMP_NO_COMPOSE, 755}, /* in exclusion list */ {0x0959, 0, 2 | DECOMP_NO_COMPOSE, 757}, /* in exclusion list */ {0x095A, 0, 2 | DECOMP_NO_COMPOSE, 759}, /* in exclusion list */ {0x095B, 0, 2 | DECOMP_NO_COMPOSE, 761}, /* in exclusion list */ {0x095C, 0, 2 | DECOMP_NO_COMPOSE, 763}, /* in exclusion list */ {0x095D, 0, 2 | DECOMP_NO_COMPOSE, 765}, /* in exclusion list */ {0x095E, 0, 2 | DECOMP_NO_COMPOSE, 767}, /* in exclusion list */ {0x095F, 0, 2 | DECOMP_NO_COMPOSE, 769}, /* in exclusion list */ {0x09BC, 7, 0, 0}, {0x09CB, 0, 2, 771}, {0x09CC, 0, 2, 773}, {0x09CD, 9, 0, 0}, {0x09DC, 0, 2 | DECOMP_NO_COMPOSE, 775}, /* in exclusion list */ {0x09DD, 0, 2 | DECOMP_NO_COMPOSE, 777}, /* in exclusion list */ {0x09DF, 0, 2 | DECOMP_NO_COMPOSE, 779}, /* in exclusion list */ {0x09FE, 230, 0, 0}, {0x0A33, 0, 2 | DECOMP_NO_COMPOSE, 781}, /* in exclusion list */ {0x0A36, 0, 2 | DECOMP_NO_COMPOSE, 783}, /* in exclusion list */ {0x0A3C, 7, 0, 0}, {0x0A4D, 9, 0, 0}, {0x0A59, 0, 2 | DECOMP_NO_COMPOSE, 785}, /* in exclusion list */ {0x0A5A, 0, 2 | DECOMP_NO_COMPOSE, 787}, /* in exclusion list */ {0x0A5B, 0, 2 | DECOMP_NO_COMPOSE, 789}, /* in exclusion list */ {0x0A5E, 0, 2 | DECOMP_NO_COMPOSE, 791}, /* in exclusion list */ {0x0ABC, 7, 0, 0}, {0x0ACD, 9, 0, 0}, {0x0B3C, 7, 0, 0}, {0x0B48, 0, 2, 793}, {0x0B4B, 0, 2, 795}, {0x0B4C, 0, 2, 797}, {0x0B4D, 9, 0, 0}, {0x0B5C, 0, 2 | DECOMP_NO_COMPOSE, 799}, /* in exclusion list */ {0x0B5D, 0, 2 | DECOMP_NO_COMPOSE, 801}, /* in exclusion list */ {0x0B94, 0, 2, 803}, {0x0BCA, 0, 2, 805}, {0x0BCB, 0, 2, 807}, {0x0BCC, 0, 2, 809}, {0x0BCD, 9, 0, 0}, {0x0C48, 0, 2, 811}, {0x0C4D, 9, 0, 0}, {0x0C55, 84, 0, 0}, {0x0C56, 91, 0, 0}, {0x0CBC, 7, 0, 0}, {0x0CC0, 0, 2, 813}, {0x0CC7, 0, 2, 815}, {0x0CC8, 0, 2, 817}, {0x0CCA, 0, 2, 819}, {0x0CCB, 0, 2, 821}, {0x0CCD, 9, 0, 0}, {0x0D3B, 9, 0, 0}, {0x0D3C, 9, 0, 0}, {0x0D4A, 0, 2, 823}, {0x0D4B, 0, 2, 825}, {0x0D4C, 0, 2, 827}, {0x0D4D, 9, 0, 0}, {0x0DCA, 9, 0, 0}, {0x0DDA, 0, 2, 829}, {0x0DDC, 0, 2, 831}, {0x0DDD, 0, 2, 833}, {0x0DDE, 0, 2, 835}, {0x0E33, 0, 2 | DECOMP_COMPAT, 837}, {0x0E38, 103, 0, 0}, {0x0E39, 103, 0, 0}, {0x0E3A, 9, 0, 0}, {0x0E48, 107, 0, 0}, {0x0E49, 107, 0, 0}, {0x0E4A, 107, 0, 0}, {0x0E4B, 107, 0, 0}, {0x0EB3, 0, 2 | DECOMP_COMPAT, 839}, {0x0EB8, 118, 0, 0}, {0x0EB9, 118, 0, 0}, {0x0EBA, 9, 0, 0}, {0x0EC8, 122, 0, 0}, {0x0EC9, 122, 0, 0}, {0x0ECA, 122, 0, 0}, {0x0ECB, 122, 0, 0}, {0x0EDC, 0, 2 | DECOMP_COMPAT, 841}, {0x0EDD, 0, 2 | DECOMP_COMPAT, 843}, {0x0F0C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0F0B}, {0x0F18, 220, 0, 0}, {0x0F19, 220, 0, 0}, {0x0F35, 220, 0, 0}, {0x0F37, 220, 0, 0}, {0x0F39, 216, 0, 0}, {0x0F43, 0, 2 | DECOMP_NO_COMPOSE, 845}, /* in exclusion list */ {0x0F4D, 0, 2 | DECOMP_NO_COMPOSE, 847}, /* in exclusion list */ {0x0F52, 0, 2 | DECOMP_NO_COMPOSE, 849}, /* in exclusion list */ {0x0F57, 0, 2 | DECOMP_NO_COMPOSE, 851}, /* in exclusion list */ {0x0F5C, 0, 2 | DECOMP_NO_COMPOSE, 853}, /* in exclusion list */ {0x0F69, 0, 2 | DECOMP_NO_COMPOSE, 855}, /* in exclusion list */ {0x0F71, 129, 0, 0}, {0x0F72, 130, 0, 0}, {0x0F73, 0, 2 | DECOMP_NO_COMPOSE, 857}, /* non-starter decomposition */ {0x0F74, 132, 0, 0}, {0x0F75, 0, 2 | DECOMP_NO_COMPOSE, 859}, /* non-starter decomposition */ {0x0F76, 0, 2 | DECOMP_NO_COMPOSE, 861}, /* in exclusion list */ {0x0F77, 0, 2 | DECOMP_COMPAT, 863}, {0x0F78, 0, 2 | DECOMP_NO_COMPOSE, 865}, /* in exclusion list */ {0x0F79, 0, 2 | DECOMP_COMPAT, 867}, {0x0F7A, 130, 0, 0}, {0x0F7B, 130, 0, 0}, {0x0F7C, 130, 0, 0}, {0x0F7D, 130, 0, 0}, {0x0F80, 130, 0, 0}, {0x0F81, 0, 2 | DECOMP_NO_COMPOSE, 869}, /* non-starter decomposition */ {0x0F82, 230, 0, 0}, {0x0F83, 230, 0, 0}, {0x0F84, 9, 0, 0}, {0x0F86, 230, 0, 0}, {0x0F87, 230, 0, 0}, {0x0F93, 0, 2 | DECOMP_NO_COMPOSE, 871}, /* in exclusion list */ {0x0F9D, 0, 2 | DECOMP_NO_COMPOSE, 873}, /* in exclusion list */ {0x0FA2, 0, 2 | DECOMP_NO_COMPOSE, 875}, /* in exclusion list */ {0x0FA7, 0, 2 | DECOMP_NO_COMPOSE, 877}, /* in exclusion list */ {0x0FAC, 0, 2 | DECOMP_NO_COMPOSE, 879}, /* in exclusion list */ {0x0FB9, 0, 2 | DECOMP_NO_COMPOSE, 881}, /* in exclusion list */ {0x0FC6, 220, 0, 0}, {0x1026, 0, 2, 883}, {0x1037, 7, 0, 0}, {0x1039, 9, 0, 0}, {0x103A, 9, 0, 0}, {0x108D, 220, 0, 0}, {0x10FC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x10DC}, {0x135D, 230, 0, 0}, {0x135E, 230, 0, 0}, {0x135F, 230, 0, 0}, {0x1714, 9, 0, 0}, {0x1734, 9, 0, 0}, {0x17D2, 9, 0, 0}, {0x17DD, 230, 0, 0}, {0x18A9, 228, 0, 0}, {0x1939, 222, 0, 0}, {0x193A, 230, 0, 0}, {0x193B, 220, 0, 0}, {0x1A17, 230, 0, 0}, {0x1A18, 220, 0, 0}, {0x1A60, 9, 0, 0}, {0x1A75, 230, 0, 0}, {0x1A76, 230, 0, 0}, {0x1A77, 230, 0, 0}, {0x1A78, 230, 0, 0}, {0x1A79, 230, 0, 0}, {0x1A7A, 230, 0, 0}, {0x1A7B, 230, 0, 0}, {0x1A7C, 230, 0, 0}, {0x1A7F, 220, 0, 0}, {0x1AB0, 230, 0, 0}, {0x1AB1, 230, 0, 0}, {0x1AB2, 230, 0, 0}, {0x1AB3, 230, 0, 0}, {0x1AB4, 230, 0, 0}, {0x1AB5, 220, 0, 0}, {0x1AB6, 220, 0, 0}, {0x1AB7, 220, 0, 0}, {0x1AB8, 220, 0, 0}, {0x1AB9, 220, 0, 0}, {0x1ABA, 220, 0, 0}, {0x1ABB, 230, 0, 0}, {0x1ABC, 230, 0, 0}, {0x1ABD, 220, 0, 0}, {0x1ABF, 220, 0, 0}, {0x1AC0, 220, 0, 0}, {0x1B06, 0, 2, 885}, {0x1B08, 0, 2, 887}, {0x1B0A, 0, 2, 889}, {0x1B0C, 0, 2, 891}, {0x1B0E, 0, 2, 893}, {0x1B12, 0, 2, 895}, {0x1B34, 7, 0, 0}, {0x1B3B, 0, 2, 897}, {0x1B3D, 0, 2, 899}, {0x1B40, 0, 2, 901}, {0x1B41, 0, 2, 903}, {0x1B43, 0, 2, 905}, {0x1B44, 9, 0, 0}, {0x1B6B, 230, 0, 0}, {0x1B6C, 220, 0, 0}, {0x1B6D, 230, 0, 0}, {0x1B6E, 230, 0, 0}, {0x1B6F, 230, 0, 0}, {0x1B70, 230, 0, 0}, {0x1B71, 230, 0, 0}, {0x1B72, 230, 0, 0}, {0x1B73, 230, 0, 0}, {0x1BAA, 9, 0, 0}, {0x1BAB, 9, 0, 0}, {0x1BE6, 7, 0, 0}, {0x1BF2, 9, 0, 0}, {0x1BF3, 9, 0, 0}, {0x1C37, 7, 0, 0}, {0x1CD0, 230, 0, 0}, {0x1CD1, 230, 0, 0}, {0x1CD2, 230, 0, 0}, {0x1CD4, 1, 0, 0}, {0x1CD5, 220, 0, 0}, {0x1CD6, 220, 0, 0}, {0x1CD7, 220, 0, 0}, {0x1CD8, 220, 0, 0}, {0x1CD9, 220, 0, 0}, {0x1CDA, 230, 0, 0}, {0x1CDB, 230, 0, 0}, {0x1CDC, 220, 0, 0}, {0x1CDD, 220, 0, 0}, {0x1CDE, 220, 0, 0}, {0x1CDF, 220, 0, 0}, {0x1CE0, 230, 0, 0}, {0x1CE2, 1, 0, 0}, {0x1CE3, 1, 0, 0}, {0x1CE4, 1, 0, 0}, {0x1CE5, 1, 0, 0}, {0x1CE6, 1, 0, 0}, {0x1CE7, 1, 0, 0}, {0x1CE8, 1, 0, 0}, {0x1CED, 220, 0, 0}, {0x1CF4, 230, 0, 0}, {0x1CF8, 230, 0, 0}, {0x1CF9, 230, 0, 0}, {0x1D2C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D2D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00C6}, {0x1D2E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D30, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D31, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D32, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x018E}, {0x1D33, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D34, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D35, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D36, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D37, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D38, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D39, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D3A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D3C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D3D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0222}, {0x1D3E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D3F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D40, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D41, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D42, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D43, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D44, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0250}, {0x1D45, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0251}, {0x1D46, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D02}, {0x1D47, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D48, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D49, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D4A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0259}, {0x1D4B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x025B}, {0x1D4C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x025C}, {0x1D4D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D4F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D50, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D51, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x014B}, {0x1D52, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D53, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0254}, {0x1D54, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D16}, {0x1D55, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D17}, {0x1D56, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D57, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D58, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D59, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D1D}, {0x1D5A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x026F}, {0x1D5B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D5C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D25}, {0x1D5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D5E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B4}, {0x1D60, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D61, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D62, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D63, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D64, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D65, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D66, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D67, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D68, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x1D69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D6A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D78, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x043D}, {0x1D9B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0252}, {0x1D9C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D9D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0255}, {0x1D9E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00F0}, {0x1D9F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x025C}, {0x1DA0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1DA1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x025F}, {0x1DA2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0261}, {0x1DA3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0265}, {0x1DA4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0268}, {0x1DA5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0269}, {0x1DA6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x026A}, {0x1DA7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D7B}, {0x1DA8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x029D}, {0x1DA9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x026D}, {0x1DAA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D85}, {0x1DAB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x029F}, {0x1DAC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0271}, {0x1DAD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0270}, {0x1DAE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0272}, {0x1DAF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0273}, {0x1DB0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0274}, {0x1DB1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0275}, {0x1DB2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0278}, {0x1DB3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0282}, {0x1DB4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0283}, {0x1DB5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x01AB}, {0x1DB6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0289}, {0x1DB7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x028A}, {0x1DB8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1D1C}, {0x1DB9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x028B}, {0x1DBA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x028C}, {0x1DBB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1DBC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0290}, {0x1DBD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0291}, {0x1DBE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0292}, {0x1DBF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x1DC0, 230, 0, 0}, {0x1DC1, 230, 0, 0}, {0x1DC2, 220, 0, 0}, {0x1DC3, 230, 0, 0}, {0x1DC4, 230, 0, 0}, {0x1DC5, 230, 0, 0}, {0x1DC6, 230, 0, 0}, {0x1DC7, 230, 0, 0}, {0x1DC8, 230, 0, 0}, {0x1DC9, 230, 0, 0}, {0x1DCA, 220, 0, 0}, {0x1DCB, 230, 0, 0}, {0x1DCC, 230, 0, 0}, {0x1DCD, 234, 0, 0}, {0x1DCE, 214, 0, 0}, {0x1DCF, 220, 0, 0}, {0x1DD0, 202, 0, 0}, {0x1DD1, 230, 0, 0}, {0x1DD2, 230, 0, 0}, {0x1DD3, 230, 0, 0}, {0x1DD4, 230, 0, 0}, {0x1DD5, 230, 0, 0}, {0x1DD6, 230, 0, 0}, {0x1DD7, 230, 0, 0}, {0x1DD8, 230, 0, 0}, {0x1DD9, 230, 0, 0}, {0x1DDA, 230, 0, 0}, {0x1DDB, 230, 0, 0}, {0x1DDC, 230, 0, 0}, {0x1DDD, 230, 0, 0}, {0x1DDE, 230, 0, 0}, {0x1DDF, 230, 0, 0}, {0x1DE0, 230, 0, 0}, {0x1DE1, 230, 0, 0}, {0x1DE2, 230, 0, 0}, {0x1DE3, 230, 0, 0}, {0x1DE4, 230, 0, 0}, {0x1DE5, 230, 0, 0}, {0x1DE6, 230, 0, 0}, {0x1DE7, 230, 0, 0}, {0x1DE8, 230, 0, 0}, {0x1DE9, 230, 0, 0}, {0x1DEA, 230, 0, 0}, {0x1DEB, 230, 0, 0}, {0x1DEC, 230, 0, 0}, {0x1DED, 230, 0, 0}, {0x1DEE, 230, 0, 0}, {0x1DEF, 230, 0, 0}, {0x1DF0, 230, 0, 0}, {0x1DF1, 230, 0, 0}, {0x1DF2, 230, 0, 0}, {0x1DF3, 230, 0, 0}, {0x1DF4, 230, 0, 0}, {0x1DF5, 230, 0, 0}, {0x1DF6, 232, 0, 0}, {0x1DF7, 228, 0, 0}, {0x1DF8, 228, 0, 0}, {0x1DF9, 220, 0, 0}, {0x1DFB, 230, 0, 0}, {0x1DFC, 233, 0, 0}, {0x1DFD, 220, 0, 0}, {0x1DFE, 230, 0, 0}, {0x1DFF, 220, 0, 0}, {0x1E00, 0, 2, 907}, {0x1E01, 0, 2, 909}, {0x1E02, 0, 2, 911}, {0x1E03, 0, 2, 913}, {0x1E04, 0, 2, 915}, {0x1E05, 0, 2, 917}, {0x1E06, 0, 2, 919}, {0x1E07, 0, 2, 921}, {0x1E08, 0, 2, 923}, {0x1E09, 0, 2, 925}, {0x1E0A, 0, 2, 927}, {0x1E0B, 0, 2, 929}, {0x1E0C, 0, 2, 931}, {0x1E0D, 0, 2, 933}, {0x1E0E, 0, 2, 935}, {0x1E0F, 0, 2, 937}, {0x1E10, 0, 2, 939}, {0x1E11, 0, 2, 941}, {0x1E12, 0, 2, 943}, {0x1E13, 0, 2, 945}, {0x1E14, 0, 2, 947}, {0x1E15, 0, 2, 949}, {0x1E16, 0, 2, 951}, {0x1E17, 0, 2, 953}, {0x1E18, 0, 2, 955}, {0x1E19, 0, 2, 957}, {0x1E1A, 0, 2, 959}, {0x1E1B, 0, 2, 961}, {0x1E1C, 0, 2, 963}, {0x1E1D, 0, 2, 965}, {0x1E1E, 0, 2, 967}, {0x1E1F, 0, 2, 969}, {0x1E20, 0, 2, 971}, {0x1E21, 0, 2, 973}, {0x1E22, 0, 2, 975}, {0x1E23, 0, 2, 977}, {0x1E24, 0, 2, 979}, {0x1E25, 0, 2, 981}, {0x1E26, 0, 2, 983}, {0x1E27, 0, 2, 985}, {0x1E28, 0, 2, 987}, {0x1E29, 0, 2, 989}, {0x1E2A, 0, 2, 991}, {0x1E2B, 0, 2, 993}, {0x1E2C, 0, 2, 995}, {0x1E2D, 0, 2, 997}, {0x1E2E, 0, 2, 999}, {0x1E2F, 0, 2, 1001}, {0x1E30, 0, 2, 1003}, {0x1E31, 0, 2, 1005}, {0x1E32, 0, 2, 1007}, {0x1E33, 0, 2, 1009}, {0x1E34, 0, 2, 1011}, {0x1E35, 0, 2, 1013}, {0x1E36, 0, 2, 1015}, {0x1E37, 0, 2, 1017}, {0x1E38, 0, 2, 1019}, {0x1E39, 0, 2, 1021}, {0x1E3A, 0, 2, 1023}, {0x1E3B, 0, 2, 1025}, {0x1E3C, 0, 2, 1027}, {0x1E3D, 0, 2, 1029}, {0x1E3E, 0, 2, 1031}, {0x1E3F, 0, 2, 1033}, {0x1E40, 0, 2, 1035}, {0x1E41, 0, 2, 1037}, {0x1E42, 0, 2, 1039}, {0x1E43, 0, 2, 1041}, {0x1E44, 0, 2, 1043}, {0x1E45, 0, 2, 1045}, {0x1E46, 0, 2, 1047}, {0x1E47, 0, 2, 1049}, {0x1E48, 0, 2, 1051}, {0x1E49, 0, 2, 1053}, {0x1E4A, 0, 2, 1055}, {0x1E4B, 0, 2, 1057}, {0x1E4C, 0, 2, 1059}, {0x1E4D, 0, 2, 1061}, {0x1E4E, 0, 2, 1063}, {0x1E4F, 0, 2, 1065}, {0x1E50, 0, 2, 1067}, {0x1E51, 0, 2, 1069}, {0x1E52, 0, 2, 1071}, {0x1E53, 0, 2, 1073}, {0x1E54, 0, 2, 1075}, {0x1E55, 0, 2, 1077}, {0x1E56, 0, 2, 1079}, {0x1E57, 0, 2, 1081}, {0x1E58, 0, 2, 1083}, {0x1E59, 0, 2, 1085}, {0x1E5A, 0, 2, 1087}, {0x1E5B, 0, 2, 1089}, {0x1E5C, 0, 2, 1091}, {0x1E5D, 0, 2, 1093}, {0x1E5E, 0, 2, 1095}, {0x1E5F, 0, 2, 1097}, {0x1E60, 0, 2, 1099}, {0x1E61, 0, 2, 1101}, {0x1E62, 0, 2, 1103}, {0x1E63, 0, 2, 1105}, {0x1E64, 0, 2, 1107}, {0x1E65, 0, 2, 1109}, {0x1E66, 0, 2, 1111}, {0x1E67, 0, 2, 1113}, {0x1E68, 0, 2, 1115}, {0x1E69, 0, 2, 1117}, {0x1E6A, 0, 2, 1119}, {0x1E6B, 0, 2, 1121}, {0x1E6C, 0, 2, 1123}, {0x1E6D, 0, 2, 1125}, {0x1E6E, 0, 2, 1127}, {0x1E6F, 0, 2, 1129}, {0x1E70, 0, 2, 1131}, {0x1E71, 0, 2, 1133}, {0x1E72, 0, 2, 1135}, {0x1E73, 0, 2, 1137}, {0x1E74, 0, 2, 1139}, {0x1E75, 0, 2, 1141}, {0x1E76, 0, 2, 1143}, {0x1E77, 0, 2, 1145}, {0x1E78, 0, 2, 1147}, {0x1E79, 0, 2, 1149}, {0x1E7A, 0, 2, 1151}, {0x1E7B, 0, 2, 1153}, {0x1E7C, 0, 2, 1155}, {0x1E7D, 0, 2, 1157}, {0x1E7E, 0, 2, 1159}, {0x1E7F, 0, 2, 1161}, {0x1E80, 0, 2, 1163}, {0x1E81, 0, 2, 1165}, {0x1E82, 0, 2, 1167}, {0x1E83, 0, 2, 1169}, {0x1E84, 0, 2, 1171}, {0x1E85, 0, 2, 1173}, {0x1E86, 0, 2, 1175}, {0x1E87, 0, 2, 1177}, {0x1E88, 0, 2, 1179}, {0x1E89, 0, 2, 1181}, {0x1E8A, 0, 2, 1183}, {0x1E8B, 0, 2, 1185}, {0x1E8C, 0, 2, 1187}, {0x1E8D, 0, 2, 1189}, {0x1E8E, 0, 2, 1191}, {0x1E8F, 0, 2, 1193}, {0x1E90, 0, 2, 1195}, {0x1E91, 0, 2, 1197}, {0x1E92, 0, 2, 1199}, {0x1E93, 0, 2, 1201}, {0x1E94, 0, 2, 1203}, {0x1E95, 0, 2, 1205}, {0x1E96, 0, 2, 1207}, {0x1E97, 0, 2, 1209}, {0x1E98, 0, 2, 1211}, {0x1E99, 0, 2, 1213}, {0x1E9A, 0, 2 | DECOMP_COMPAT, 1215}, {0x1E9B, 0, 2, 1217}, {0x1EA0, 0, 2, 1219}, {0x1EA1, 0, 2, 1221}, {0x1EA2, 0, 2, 1223}, {0x1EA3, 0, 2, 1225}, {0x1EA4, 0, 2, 1227}, {0x1EA5, 0, 2, 1229}, {0x1EA6, 0, 2, 1231}, {0x1EA7, 0, 2, 1233}, {0x1EA8, 0, 2, 1235}, {0x1EA9, 0, 2, 1237}, {0x1EAA, 0, 2, 1239}, {0x1EAB, 0, 2, 1241}, {0x1EAC, 0, 2, 1243}, {0x1EAD, 0, 2, 1245}, {0x1EAE, 0, 2, 1247}, {0x1EAF, 0, 2, 1249}, {0x1EB0, 0, 2, 1251}, {0x1EB1, 0, 2, 1253}, {0x1EB2, 0, 2, 1255}, {0x1EB3, 0, 2, 1257}, {0x1EB4, 0, 2, 1259}, {0x1EB5, 0, 2, 1261}, {0x1EB6, 0, 2, 1263}, {0x1EB7, 0, 2, 1265}, {0x1EB8, 0, 2, 1267}, {0x1EB9, 0, 2, 1269}, {0x1EBA, 0, 2, 1271}, {0x1EBB, 0, 2, 1273}, {0x1EBC, 0, 2, 1275}, {0x1EBD, 0, 2, 1277}, {0x1EBE, 0, 2, 1279}, {0x1EBF, 0, 2, 1281}, {0x1EC0, 0, 2, 1283}, {0x1EC1, 0, 2, 1285}, {0x1EC2, 0, 2, 1287}, {0x1EC3, 0, 2, 1289}, {0x1EC4, 0, 2, 1291}, {0x1EC5, 0, 2, 1293}, {0x1EC6, 0, 2, 1295}, {0x1EC7, 0, 2, 1297}, {0x1EC8, 0, 2, 1299}, {0x1EC9, 0, 2, 1301}, {0x1ECA, 0, 2, 1303}, {0x1ECB, 0, 2, 1305}, {0x1ECC, 0, 2, 1307}, {0x1ECD, 0, 2, 1309}, {0x1ECE, 0, 2, 1311}, {0x1ECF, 0, 2, 1313}, {0x1ED0, 0, 2, 1315}, {0x1ED1, 0, 2, 1317}, {0x1ED2, 0, 2, 1319}, {0x1ED3, 0, 2, 1321}, {0x1ED4, 0, 2, 1323}, {0x1ED5, 0, 2, 1325}, {0x1ED6, 0, 2, 1327}, {0x1ED7, 0, 2, 1329}, {0x1ED8, 0, 2, 1331}, {0x1ED9, 0, 2, 1333}, {0x1EDA, 0, 2, 1335}, {0x1EDB, 0, 2, 1337}, {0x1EDC, 0, 2, 1339}, {0x1EDD, 0, 2, 1341}, {0x1EDE, 0, 2, 1343}, {0x1EDF, 0, 2, 1345}, {0x1EE0, 0, 2, 1347}, {0x1EE1, 0, 2, 1349}, {0x1EE2, 0, 2, 1351}, {0x1EE3, 0, 2, 1353}, {0x1EE4, 0, 2, 1355}, {0x1EE5, 0, 2, 1357}, {0x1EE6, 0, 2, 1359}, {0x1EE7, 0, 2, 1361}, {0x1EE8, 0, 2, 1363}, {0x1EE9, 0, 2, 1365}, {0x1EEA, 0, 2, 1367}, {0x1EEB, 0, 2, 1369}, {0x1EEC, 0, 2, 1371}, {0x1EED, 0, 2, 1373}, {0x1EEE, 0, 2, 1375}, {0x1EEF, 0, 2, 1377}, {0x1EF0, 0, 2, 1379}, {0x1EF1, 0, 2, 1381}, {0x1EF2, 0, 2, 1383}, {0x1EF3, 0, 2, 1385}, {0x1EF4, 0, 2, 1387}, {0x1EF5, 0, 2, 1389}, {0x1EF6, 0, 2, 1391}, {0x1EF7, 0, 2, 1393}, {0x1EF8, 0, 2, 1395}, {0x1EF9, 0, 2, 1397}, {0x1F00, 0, 2, 1399}, {0x1F01, 0, 2, 1401}, {0x1F02, 0, 2, 1403}, {0x1F03, 0, 2, 1405}, {0x1F04, 0, 2, 1407}, {0x1F05, 0, 2, 1409}, {0x1F06, 0, 2, 1411}, {0x1F07, 0, 2, 1413}, {0x1F08, 0, 2, 1415}, {0x1F09, 0, 2, 1417}, {0x1F0A, 0, 2, 1419}, {0x1F0B, 0, 2, 1421}, {0x1F0C, 0, 2, 1423}, {0x1F0D, 0, 2, 1425}, {0x1F0E, 0, 2, 1427}, {0x1F0F, 0, 2, 1429}, {0x1F10, 0, 2, 1431}, {0x1F11, 0, 2, 1433}, {0x1F12, 0, 2, 1435}, {0x1F13, 0, 2, 1437}, {0x1F14, 0, 2, 1439}, {0x1F15, 0, 2, 1441}, {0x1F18, 0, 2, 1443}, {0x1F19, 0, 2, 1445}, {0x1F1A, 0, 2, 1447}, {0x1F1B, 0, 2, 1449}, {0x1F1C, 0, 2, 1451}, {0x1F1D, 0, 2, 1453}, {0x1F20, 0, 2, 1455}, {0x1F21, 0, 2, 1457}, {0x1F22, 0, 2, 1459}, {0x1F23, 0, 2, 1461}, {0x1F24, 0, 2, 1463}, {0x1F25, 0, 2, 1465}, {0x1F26, 0, 2, 1467}, {0x1F27, 0, 2, 1469}, {0x1F28, 0, 2, 1471}, {0x1F29, 0, 2, 1473}, {0x1F2A, 0, 2, 1475}, {0x1F2B, 0, 2, 1477}, {0x1F2C, 0, 2, 1479}, {0x1F2D, 0, 2, 1481}, {0x1F2E, 0, 2, 1483}, {0x1F2F, 0, 2, 1485}, {0x1F30, 0, 2, 1487}, {0x1F31, 0, 2, 1489}, {0x1F32, 0, 2, 1491}, {0x1F33, 0, 2, 1493}, {0x1F34, 0, 2, 1495}, {0x1F35, 0, 2, 1497}, {0x1F36, 0, 2, 1499}, {0x1F37, 0, 2, 1501}, {0x1F38, 0, 2, 1503}, {0x1F39, 0, 2, 1505}, {0x1F3A, 0, 2, 1507}, {0x1F3B, 0, 2, 1509}, {0x1F3C, 0, 2, 1511}, {0x1F3D, 0, 2, 1513}, {0x1F3E, 0, 2, 1515}, {0x1F3F, 0, 2, 1517}, {0x1F40, 0, 2, 1519}, {0x1F41, 0, 2, 1521}, {0x1F42, 0, 2, 1523}, {0x1F43, 0, 2, 1525}, {0x1F44, 0, 2, 1527}, {0x1F45, 0, 2, 1529}, {0x1F48, 0, 2, 1531}, {0x1F49, 0, 2, 1533}, {0x1F4A, 0, 2, 1535}, {0x1F4B, 0, 2, 1537}, {0x1F4C, 0, 2, 1539}, {0x1F4D, 0, 2, 1541}, {0x1F50, 0, 2, 1543}, {0x1F51, 0, 2, 1545}, {0x1F52, 0, 2, 1547}, {0x1F53, 0, 2, 1549}, {0x1F54, 0, 2, 1551}, {0x1F55, 0, 2, 1553}, {0x1F56, 0, 2, 1555}, {0x1F57, 0, 2, 1557}, {0x1F59, 0, 2, 1559}, {0x1F5B, 0, 2, 1561}, {0x1F5D, 0, 2, 1563}, {0x1F5F, 0, 2, 1565}, {0x1F60, 0, 2, 1567}, {0x1F61, 0, 2, 1569}, {0x1F62, 0, 2, 1571}, {0x1F63, 0, 2, 1573}, {0x1F64, 0, 2, 1575}, {0x1F65, 0, 2, 1577}, {0x1F66, 0, 2, 1579}, {0x1F67, 0, 2, 1581}, {0x1F68, 0, 2, 1583}, {0x1F69, 0, 2, 1585}, {0x1F6A, 0, 2, 1587}, {0x1F6B, 0, 2, 1589}, {0x1F6C, 0, 2, 1591}, {0x1F6D, 0, 2, 1593}, {0x1F6E, 0, 2, 1595}, {0x1F6F, 0, 2, 1597}, {0x1F70, 0, 2, 1599}, {0x1F71, 0, 1 | DECOMP_INLINE, 0x03AC}, {0x1F72, 0, 2, 1601}, {0x1F73, 0, 1 | DECOMP_INLINE, 0x03AD}, {0x1F74, 0, 2, 1603}, {0x1F75, 0, 1 | DECOMP_INLINE, 0x03AE}, {0x1F76, 0, 2, 1605}, {0x1F77, 0, 1 | DECOMP_INLINE, 0x03AF}, {0x1F78, 0, 2, 1607}, {0x1F79, 0, 1 | DECOMP_INLINE, 0x03CC}, {0x1F7A, 0, 2, 1609}, {0x1F7B, 0, 1 | DECOMP_INLINE, 0x03CD}, {0x1F7C, 0, 2, 1611}, {0x1F7D, 0, 1 | DECOMP_INLINE, 0x03CE}, {0x1F80, 0, 2, 1613}, {0x1F81, 0, 2, 1615}, {0x1F82, 0, 2, 1617}, {0x1F83, 0, 2, 1619}, {0x1F84, 0, 2, 1621}, {0x1F85, 0, 2, 1623}, {0x1F86, 0, 2, 1625}, {0x1F87, 0, 2, 1627}, {0x1F88, 0, 2, 1629}, {0x1F89, 0, 2, 1631}, {0x1F8A, 0, 2, 1633}, {0x1F8B, 0, 2, 1635}, {0x1F8C, 0, 2, 1637}, {0x1F8D, 0, 2, 1639}, {0x1F8E, 0, 2, 1641}, {0x1F8F, 0, 2, 1643}, {0x1F90, 0, 2, 1645}, {0x1F91, 0, 2, 1647}, {0x1F92, 0, 2, 1649}, {0x1F93, 0, 2, 1651}, {0x1F94, 0, 2, 1653}, {0x1F95, 0, 2, 1655}, {0x1F96, 0, 2, 1657}, {0x1F97, 0, 2, 1659}, {0x1F98, 0, 2, 1661}, {0x1F99, 0, 2, 1663}, {0x1F9A, 0, 2, 1665}, {0x1F9B, 0, 2, 1667}, {0x1F9C, 0, 2, 1669}, {0x1F9D, 0, 2, 1671}, {0x1F9E, 0, 2, 1673}, {0x1F9F, 0, 2, 1675}, {0x1FA0, 0, 2, 1677}, {0x1FA1, 0, 2, 1679}, {0x1FA2, 0, 2, 1681}, {0x1FA3, 0, 2, 1683}, {0x1FA4, 0, 2, 1685}, {0x1FA5, 0, 2, 1687}, {0x1FA6, 0, 2, 1689}, {0x1FA7, 0, 2, 1691}, {0x1FA8, 0, 2, 1693}, {0x1FA9, 0, 2, 1695}, {0x1FAA, 0, 2, 1697}, {0x1FAB, 0, 2, 1699}, {0x1FAC, 0, 2, 1701}, {0x1FAD, 0, 2, 1703}, {0x1FAE, 0, 2, 1705}, {0x1FAF, 0, 2, 1707}, {0x1FB0, 0, 2, 1709}, {0x1FB1, 0, 2, 1711}, {0x1FB2, 0, 2, 1713}, {0x1FB3, 0, 2, 1715}, {0x1FB4, 0, 2, 1717}, {0x1FB6, 0, 2, 1719}, {0x1FB7, 0, 2, 1721}, {0x1FB8, 0, 2, 1723}, {0x1FB9, 0, 2, 1725}, {0x1FBA, 0, 2, 1727}, {0x1FBB, 0, 1 | DECOMP_INLINE, 0x0386}, {0x1FBC, 0, 2, 1729}, {0x1FBD, 0, 2 | DECOMP_COMPAT, 1731}, {0x1FBE, 0, 1 | DECOMP_INLINE, 0x03B9}, {0x1FBF, 0, 2 | DECOMP_COMPAT, 1733}, {0x1FC0, 0, 2 | DECOMP_COMPAT, 1735}, {0x1FC1, 0, 2, 1737}, {0x1FC2, 0, 2, 1739}, {0x1FC3, 0, 2, 1741}, {0x1FC4, 0, 2, 1743}, {0x1FC6, 0, 2, 1745}, {0x1FC7, 0, 2, 1747}, {0x1FC8, 0, 2, 1749}, {0x1FC9, 0, 1 | DECOMP_INLINE, 0x0388}, {0x1FCA, 0, 2, 1751}, {0x1FCB, 0, 1 | DECOMP_INLINE, 0x0389}, {0x1FCC, 0, 2, 1753}, {0x1FCD, 0, 2, 1755}, {0x1FCE, 0, 2, 1757}, {0x1FCF, 0, 2, 1759}, {0x1FD0, 0, 2, 1761}, {0x1FD1, 0, 2, 1763}, {0x1FD2, 0, 2, 1765}, {0x1FD3, 0, 1 | DECOMP_INLINE, 0x0390}, {0x1FD6, 0, 2, 1767}, {0x1FD7, 0, 2, 1769}, {0x1FD8, 0, 2, 1771}, {0x1FD9, 0, 2, 1773}, {0x1FDA, 0, 2, 1775}, {0x1FDB, 0, 1 | DECOMP_INLINE, 0x038A}, {0x1FDD, 0, 2, 1777}, {0x1FDE, 0, 2, 1779}, {0x1FDF, 0, 2, 1781}, {0x1FE0, 0, 2, 1783}, {0x1FE1, 0, 2, 1785}, {0x1FE2, 0, 2, 1787}, {0x1FE3, 0, 1 | DECOMP_INLINE, 0x03B0}, {0x1FE4, 0, 2, 1789}, {0x1FE5, 0, 2, 1791}, {0x1FE6, 0, 2, 1793}, {0x1FE7, 0, 2, 1795}, {0x1FE8, 0, 2, 1797}, {0x1FE9, 0, 2, 1799}, {0x1FEA, 0, 2, 1801}, {0x1FEB, 0, 1 | DECOMP_INLINE, 0x038E}, {0x1FEC, 0, 2, 1803}, {0x1FED, 0, 2, 1805}, {0x1FEE, 0, 1 | DECOMP_INLINE, 0x0385}, {0x1FEF, 0, 1 | DECOMP_INLINE, 0x0060}, {0x1FF2, 0, 2, 1807}, {0x1FF3, 0, 2, 1809}, {0x1FF4, 0, 2, 1811}, {0x1FF6, 0, 2, 1813}, {0x1FF7, 0, 2, 1815}, {0x1FF8, 0, 2, 1817}, {0x1FF9, 0, 1 | DECOMP_INLINE, 0x038C}, {0x1FFA, 0, 2, 1819}, {0x1FFB, 0, 1 | DECOMP_INLINE, 0x038F}, {0x1FFC, 0, 2, 1821}, {0x1FFD, 0, 1 | DECOMP_INLINE, 0x00B4}, {0x1FFE, 0, 2 | DECOMP_COMPAT, 1823}, {0x2000, 0, 1 | DECOMP_INLINE, 0x2002}, {0x2001, 0, 1 | DECOMP_INLINE, 0x2003}, {0x2002, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2003, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2004, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2005, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2006, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2007, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2008, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2009, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x200A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2011, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2010}, {0x2017, 0, 2 | DECOMP_COMPAT, 1825}, {0x2024, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002E}, {0x2025, 0, 2 | DECOMP_COMPAT, 1827}, {0x2026, 0, 3 | DECOMP_COMPAT, 1829}, {0x202F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2033, 0, 2 | DECOMP_COMPAT, 1832}, {0x2034, 0, 3 | DECOMP_COMPAT, 1834}, {0x2036, 0, 2 | DECOMP_COMPAT, 1837}, {0x2037, 0, 3 | DECOMP_COMPAT, 1839}, {0x203C, 0, 2 | DECOMP_COMPAT, 1842}, {0x203E, 0, 2 | DECOMP_COMPAT, 1844}, {0x2047, 0, 2 | DECOMP_COMPAT, 1846}, {0x2048, 0, 2 | DECOMP_COMPAT, 1848}, {0x2049, 0, 2 | DECOMP_COMPAT, 1850}, {0x2057, 0, 4 | DECOMP_COMPAT, 1852}, {0x205F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x2070, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x2071, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x2074, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x2075, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x2076, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x2077, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x2078, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x2079, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x207A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002B}, {0x207B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2212}, {0x207C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003D}, {0x207D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0028}, {0x207E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0029}, {0x207F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x2080, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x2081, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x2082, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x2083, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x2084, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x2085, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x2086, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x2087, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x2088, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x2089, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x208A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002B}, {0x208B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2212}, {0x208C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003D}, {0x208D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0028}, {0x208E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0029}, {0x2090, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x2091, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x2092, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x2093, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x2094, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0259}, {0x2095, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x2096, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x2097, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x2098, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x2099, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x209A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x209B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x209C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x20A8, 0, 2 | DECOMP_COMPAT, 1856}, {0x20D0, 230, 0, 0}, {0x20D1, 230, 0, 0}, {0x20D2, 1, 0, 0}, {0x20D3, 1, 0, 0}, {0x20D4, 230, 0, 0}, {0x20D5, 230, 0, 0}, {0x20D6, 230, 0, 0}, {0x20D7, 230, 0, 0}, {0x20D8, 1, 0, 0}, {0x20D9, 1, 0, 0}, {0x20DA, 1, 0, 0}, {0x20DB, 230, 0, 0}, {0x20DC, 230, 0, 0}, {0x20E1, 230, 0, 0}, {0x20E5, 1, 0, 0}, {0x20E6, 1, 0, 0}, {0x20E7, 230, 0, 0}, {0x20E8, 220, 0, 0}, {0x20E9, 230, 0, 0}, {0x20EA, 1, 0, 0}, {0x20EB, 1, 0, 0}, {0x20EC, 220, 0, 0}, {0x20ED, 220, 0, 0}, {0x20EE, 220, 0, 0}, {0x20EF, 220, 0, 0}, {0x20F0, 230, 0, 0}, {0x2100, 0, 3 | DECOMP_COMPAT, 1858}, {0x2101, 0, 3 | DECOMP_COMPAT, 1861}, {0x2102, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x2103, 0, 2 | DECOMP_COMPAT, 1864}, {0x2105, 0, 3 | DECOMP_COMPAT, 1866}, {0x2106, 0, 3 | DECOMP_COMPAT, 1869}, {0x2107, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0190}, {0x2109, 0, 2 | DECOMP_COMPAT, 1872}, {0x210A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x210B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x210C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x210D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x210E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x210F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0127}, {0x2110, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x2111, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x2112, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x2113, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x2115, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x2116, 0, 2 | DECOMP_COMPAT, 1874}, {0x2119, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x211A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x211B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x211C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x211D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x2120, 0, 2 | DECOMP_COMPAT, 1876}, {0x2121, 0, 3 | DECOMP_COMPAT, 1878}, {0x2122, 0, 2 | DECOMP_COMPAT, 1881}, {0x2124, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x2126, 0, 1 | DECOMP_INLINE, 0x03A9}, {0x2128, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x212A, 0, 1 | DECOMP_INLINE, 0x004B}, {0x212B, 0, 1 | DECOMP_INLINE, 0x00C5}, {0x212C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x212D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x212F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x2130, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x2131, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x2133, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x2134, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x2135, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D0}, {0x2136, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D1}, {0x2137, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D2}, {0x2138, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D3}, {0x2139, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x213B, 0, 3 | DECOMP_COMPAT, 1883}, {0x213C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x213D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x213E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0393}, {0x213F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A0}, {0x2140, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2211}, {0x2145, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x2146, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x2147, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x2148, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x2149, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x2150, 0, 3 | DECOMP_COMPAT, 1886}, {0x2151, 0, 3 | DECOMP_COMPAT, 1889}, {0x2152, 0, 4 | DECOMP_COMPAT, 1892}, {0x2153, 0, 3 | DECOMP_COMPAT, 1896}, {0x2154, 0, 3 | DECOMP_COMPAT, 1899}, {0x2155, 0, 3 | DECOMP_COMPAT, 1902}, {0x2156, 0, 3 | DECOMP_COMPAT, 1905}, {0x2157, 0, 3 | DECOMP_COMPAT, 1908}, {0x2158, 0, 3 | DECOMP_COMPAT, 1911}, {0x2159, 0, 3 | DECOMP_COMPAT, 1914}, {0x215A, 0, 3 | DECOMP_COMPAT, 1917}, {0x215B, 0, 3 | DECOMP_COMPAT, 1920}, {0x215C, 0, 3 | DECOMP_COMPAT, 1923}, {0x215D, 0, 3 | DECOMP_COMPAT, 1926}, {0x215E, 0, 3 | DECOMP_COMPAT, 1929}, {0x215F, 0, 2 | DECOMP_COMPAT, 1932}, {0x2160, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x2161, 0, 2 | DECOMP_COMPAT, 1934}, {0x2162, 0, 3 | DECOMP_COMPAT, 1936}, {0x2163, 0, 2 | DECOMP_COMPAT, 1939}, {0x2164, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x2165, 0, 2 | DECOMP_COMPAT, 1941}, {0x2166, 0, 3 | DECOMP_COMPAT, 1943}, {0x2167, 0, 4 | DECOMP_COMPAT, 1946}, {0x2168, 0, 2 | DECOMP_COMPAT, 1950}, {0x2169, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x216A, 0, 2 | DECOMP_COMPAT, 1952}, {0x216B, 0, 3 | DECOMP_COMPAT, 1954}, {0x216C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x216D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x216E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x216F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x2170, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x2171, 0, 2 | DECOMP_COMPAT, 1957}, {0x2172, 0, 3 | DECOMP_COMPAT, 1959}, {0x2173, 0, 2 | DECOMP_COMPAT, 1962}, {0x2174, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x2175, 0, 2 | DECOMP_COMPAT, 1964}, {0x2176, 0, 3 | DECOMP_COMPAT, 1966}, {0x2177, 0, 4 | DECOMP_COMPAT, 1969}, {0x2178, 0, 2 | DECOMP_COMPAT, 1973}, {0x2179, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x217A, 0, 2 | DECOMP_COMPAT, 1975}, {0x217B, 0, 3 | DECOMP_COMPAT, 1977}, {0x217C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x217D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x217E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x217F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x2189, 0, 3 | DECOMP_COMPAT, 1980}, {0x219A, 0, 2, 1983}, {0x219B, 0, 2, 1985}, {0x21AE, 0, 2, 1987}, {0x21CD, 0, 2, 1989}, {0x21CE, 0, 2, 1991}, {0x21CF, 0, 2, 1993}, {0x2204, 0, 2, 1995}, {0x2209, 0, 2, 1997}, {0x220C, 0, 2, 1999}, {0x2224, 0, 2, 2001}, {0x2226, 0, 2, 2003}, {0x222C, 0, 2 | DECOMP_COMPAT, 2005}, {0x222D, 0, 3 | DECOMP_COMPAT, 2007}, {0x222F, 0, 2 | DECOMP_COMPAT, 2010}, {0x2230, 0, 3 | DECOMP_COMPAT, 2012}, {0x2241, 0, 2, 2015}, {0x2244, 0, 2, 2017}, {0x2247, 0, 2, 2019}, {0x2249, 0, 2, 2021}, {0x2260, 0, 2, 2023}, {0x2262, 0, 2, 2025}, {0x226D, 0, 2, 2027}, {0x226E, 0, 2, 2029}, {0x226F, 0, 2, 2031}, {0x2270, 0, 2, 2033}, {0x2271, 0, 2, 2035}, {0x2274, 0, 2, 2037}, {0x2275, 0, 2, 2039}, {0x2278, 0, 2, 2041}, {0x2279, 0, 2, 2043}, {0x2280, 0, 2, 2045}, {0x2281, 0, 2, 2047}, {0x2284, 0, 2, 2049}, {0x2285, 0, 2, 2051}, {0x2288, 0, 2, 2053}, {0x2289, 0, 2, 2055}, {0x22AC, 0, 2, 2057}, {0x22AD, 0, 2, 2059}, {0x22AE, 0, 2, 2061}, {0x22AF, 0, 2, 2063}, {0x22E0, 0, 2, 2065}, {0x22E1, 0, 2, 2067}, {0x22E2, 0, 2, 2069}, {0x22E3, 0, 2, 2071}, {0x22EA, 0, 2, 2073}, {0x22EB, 0, 2, 2075}, {0x22EC, 0, 2, 2077}, {0x22ED, 0, 2, 2079}, {0x2329, 0, 1 | DECOMP_INLINE, 0x3008}, {0x232A, 0, 1 | DECOMP_INLINE, 0x3009}, {0x2460, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x2461, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x2462, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x2463, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x2464, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x2465, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x2466, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x2467, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x2468, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x2469, 0, 2 | DECOMP_COMPAT, 2081}, {0x246A, 0, 2 | DECOMP_COMPAT, 2083}, {0x246B, 0, 2 | DECOMP_COMPAT, 2085}, {0x246C, 0, 2 | DECOMP_COMPAT, 2087}, {0x246D, 0, 2 | DECOMP_COMPAT, 2089}, {0x246E, 0, 2 | DECOMP_COMPAT, 2091}, {0x246F, 0, 2 | DECOMP_COMPAT, 2093}, {0x2470, 0, 2 | DECOMP_COMPAT, 2095}, {0x2471, 0, 2 | DECOMP_COMPAT, 2097}, {0x2472, 0, 2 | DECOMP_COMPAT, 2099}, {0x2473, 0, 2 | DECOMP_COMPAT, 2101}, {0x2474, 0, 3 | DECOMP_COMPAT, 2103}, {0x2475, 0, 3 | DECOMP_COMPAT, 2106}, {0x2476, 0, 3 | DECOMP_COMPAT, 2109}, {0x2477, 0, 3 | DECOMP_COMPAT, 2112}, {0x2478, 0, 3 | DECOMP_COMPAT, 2115}, {0x2479, 0, 3 | DECOMP_COMPAT, 2118}, {0x247A, 0, 3 | DECOMP_COMPAT, 2121}, {0x247B, 0, 3 | DECOMP_COMPAT, 2124}, {0x247C, 0, 3 | DECOMP_COMPAT, 2127}, {0x247D, 0, 4 | DECOMP_COMPAT, 2130}, {0x247E, 0, 4 | DECOMP_COMPAT, 2134}, {0x247F, 0, 4 | DECOMP_COMPAT, 2138}, {0x2480, 0, 4 | DECOMP_COMPAT, 2142}, {0x2481, 0, 4 | DECOMP_COMPAT, 2146}, {0x2482, 0, 4 | DECOMP_COMPAT, 2150}, {0x2483, 0, 4 | DECOMP_COMPAT, 2154}, {0x2484, 0, 4 | DECOMP_COMPAT, 2158}, {0x2485, 0, 4 | DECOMP_COMPAT, 2162}, {0x2486, 0, 4 | DECOMP_COMPAT, 2166}, {0x2487, 0, 4 | DECOMP_COMPAT, 2170}, {0x2488, 0, 2 | DECOMP_COMPAT, 2174}, {0x2489, 0, 2 | DECOMP_COMPAT, 2176}, {0x248A, 0, 2 | DECOMP_COMPAT, 2178}, {0x248B, 0, 2 | DECOMP_COMPAT, 2180}, {0x248C, 0, 2 | DECOMP_COMPAT, 2182}, {0x248D, 0, 2 | DECOMP_COMPAT, 2184}, {0x248E, 0, 2 | DECOMP_COMPAT, 2186}, {0x248F, 0, 2 | DECOMP_COMPAT, 2188}, {0x2490, 0, 2 | DECOMP_COMPAT, 2190}, {0x2491, 0, 3 | DECOMP_COMPAT, 2192}, {0x2492, 0, 3 | DECOMP_COMPAT, 2195}, {0x2493, 0, 3 | DECOMP_COMPAT, 2198}, {0x2494, 0, 3 | DECOMP_COMPAT, 2201}, {0x2495, 0, 3 | DECOMP_COMPAT, 2204}, {0x2496, 0, 3 | DECOMP_COMPAT, 2207}, {0x2497, 0, 3 | DECOMP_COMPAT, 2210}, {0x2498, 0, 3 | DECOMP_COMPAT, 2213}, {0x2499, 0, 3 | DECOMP_COMPAT, 2216}, {0x249A, 0, 3 | DECOMP_COMPAT, 2219}, {0x249B, 0, 3 | DECOMP_COMPAT, 2222}, {0x249C, 0, 3 | DECOMP_COMPAT, 2225}, {0x249D, 0, 3 | DECOMP_COMPAT, 2228}, {0x249E, 0, 3 | DECOMP_COMPAT, 2231}, {0x249F, 0, 3 | DECOMP_COMPAT, 2234}, {0x24A0, 0, 3 | DECOMP_COMPAT, 2237}, {0x24A1, 0, 3 | DECOMP_COMPAT, 2240}, {0x24A2, 0, 3 | DECOMP_COMPAT, 2243}, {0x24A3, 0, 3 | DECOMP_COMPAT, 2246}, {0x24A4, 0, 3 | DECOMP_COMPAT, 2249}, {0x24A5, 0, 3 | DECOMP_COMPAT, 2252}, {0x24A6, 0, 3 | DECOMP_COMPAT, 2255}, {0x24A7, 0, 3 | DECOMP_COMPAT, 2258}, {0x24A8, 0, 3 | DECOMP_COMPAT, 2261}, {0x24A9, 0, 3 | DECOMP_COMPAT, 2264}, {0x24AA, 0, 3 | DECOMP_COMPAT, 2267}, {0x24AB, 0, 3 | DECOMP_COMPAT, 2270}, {0x24AC, 0, 3 | DECOMP_COMPAT, 2273}, {0x24AD, 0, 3 | DECOMP_COMPAT, 2276}, {0x24AE, 0, 3 | DECOMP_COMPAT, 2279}, {0x24AF, 0, 3 | DECOMP_COMPAT, 2282}, {0x24B0, 0, 3 | DECOMP_COMPAT, 2285}, {0x24B1, 0, 3 | DECOMP_COMPAT, 2288}, {0x24B2, 0, 3 | DECOMP_COMPAT, 2291}, {0x24B3, 0, 3 | DECOMP_COMPAT, 2294}, {0x24B4, 0, 3 | DECOMP_COMPAT, 2297}, {0x24B5, 0, 3 | DECOMP_COMPAT, 2300}, {0x24B6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x24B7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x24B8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x24B9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x24BA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x24BB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x24BC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x24BD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x24BE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x24BF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x24C0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x24C1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x24C2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x24C3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x24C4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x24C5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x24C6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x24C7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x24C8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x24C9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x24CA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x24CB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x24CC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x24CD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x24CE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x24CF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x24D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x24D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x24D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x24D3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x24D4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x24D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x24D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x24D7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x24D8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x24D9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x24DA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x24DB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x24DC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x24DD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x24DE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x24DF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x24E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x24E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x24E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x24E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x24E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x24E5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x24E6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x24E7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x24E8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x24E9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x24EA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x2A0C, 0, 4 | DECOMP_COMPAT, 2303}, {0x2A74, 0, 3 | DECOMP_COMPAT, 2307}, {0x2A75, 0, 2 | DECOMP_COMPAT, 2310}, {0x2A76, 0, 3 | DECOMP_COMPAT, 2312}, {0x2ADC, 0, 2 | DECOMP_NO_COMPOSE, 2315}, /* in exclusion list */ {0x2C7C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x2C7D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x2CEF, 230, 0, 0}, {0x2CF0, 230, 0, 0}, {0x2CF1, 230, 0, 0}, {0x2D6F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2D61}, {0x2D7F, 9, 0, 0}, {0x2DE0, 230, 0, 0}, {0x2DE1, 230, 0, 0}, {0x2DE2, 230, 0, 0}, {0x2DE3, 230, 0, 0}, {0x2DE4, 230, 0, 0}, {0x2DE5, 230, 0, 0}, {0x2DE6, 230, 0, 0}, {0x2DE7, 230, 0, 0}, {0x2DE8, 230, 0, 0}, {0x2DE9, 230, 0, 0}, {0x2DEA, 230, 0, 0}, {0x2DEB, 230, 0, 0}, {0x2DEC, 230, 0, 0}, {0x2DED, 230, 0, 0}, {0x2DEE, 230, 0, 0}, {0x2DEF, 230, 0, 0}, {0x2DF0, 230, 0, 0}, {0x2DF1, 230, 0, 0}, {0x2DF2, 230, 0, 0}, {0x2DF3, 230, 0, 0}, {0x2DF4, 230, 0, 0}, {0x2DF5, 230, 0, 0}, {0x2DF6, 230, 0, 0}, {0x2DF7, 230, 0, 0}, {0x2DF8, 230, 0, 0}, {0x2DF9, 230, 0, 0}, {0x2DFA, 230, 0, 0}, {0x2DFB, 230, 0, 0}, {0x2DFC, 230, 0, 0}, {0x2DFD, 230, 0, 0}, {0x2DFE, 230, 0, 0}, {0x2DFF, 230, 0, 0}, {0x2E9F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6BCD}, {0x2EF3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F9F}, {0x2F00, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E00}, {0x2F01, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E28}, {0x2F02, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E36}, {0x2F03, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E3F}, {0x2F04, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E59}, {0x2F05, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E85}, {0x2F06, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E8C}, {0x2F07, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4EA0}, {0x2F08, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4EBA}, {0x2F09, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x513F}, {0x2F0A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5165}, {0x2F0B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x516B}, {0x2F0C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5182}, {0x2F0D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5196}, {0x2F0E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x51AB}, {0x2F0F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x51E0}, {0x2F10, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x51F5}, {0x2F11, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5200}, {0x2F12, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x529B}, {0x2F13, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x52F9}, {0x2F14, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5315}, {0x2F15, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x531A}, {0x2F16, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5338}, {0x2F17, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5341}, {0x2F18, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x535C}, {0x2F19, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5369}, {0x2F1A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5382}, {0x2F1B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53B6}, {0x2F1C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53C8}, {0x2F1D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53E3}, {0x2F1E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x56D7}, {0x2F1F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x571F}, {0x2F20, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x58EB}, {0x2F21, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5902}, {0x2F22, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x590A}, {0x2F23, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5915}, {0x2F24, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5927}, {0x2F25, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5973}, {0x2F26, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5B50}, {0x2F27, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5B80}, {0x2F28, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5BF8}, {0x2F29, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5C0F}, {0x2F2A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5C22}, {0x2F2B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5C38}, {0x2F2C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5C6E}, {0x2F2D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5C71}, {0x2F2E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5DDB}, {0x2F2F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5DE5}, {0x2F30, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5DF1}, {0x2F31, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5DFE}, {0x2F32, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5E72}, {0x2F33, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5E7A}, {0x2F34, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5E7F}, {0x2F35, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5EF4}, {0x2F36, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5EFE}, {0x2F37, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F0B}, {0x2F38, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F13}, {0x2F39, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F50}, {0x2F3A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F61}, {0x2F3B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F73}, {0x2F3C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5FC3}, {0x2F3D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6208}, {0x2F3E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6236}, {0x2F3F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x624B}, {0x2F40, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x652F}, {0x2F41, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6534}, {0x2F42, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6587}, {0x2F43, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6597}, {0x2F44, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x65A4}, {0x2F45, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x65B9}, {0x2F46, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x65E0}, {0x2F47, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x65E5}, {0x2F48, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x66F0}, {0x2F49, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6708}, {0x2F4A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6728}, {0x2F4B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6B20}, {0x2F4C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6B62}, {0x2F4D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6B79}, {0x2F4E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6BB3}, {0x2F4F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6BCB}, {0x2F50, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6BD4}, {0x2F51, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6BDB}, {0x2F52, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6C0F}, {0x2F53, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6C14}, {0x2F54, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6C34}, {0x2F55, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x706B}, {0x2F56, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x722A}, {0x2F57, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7236}, {0x2F58, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x723B}, {0x2F59, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x723F}, {0x2F5A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7247}, {0x2F5B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7259}, {0x2F5C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x725B}, {0x2F5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x72AC}, {0x2F5E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7384}, {0x2F5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7389}, {0x2F60, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x74DC}, {0x2F61, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x74E6}, {0x2F62, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7518}, {0x2F63, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x751F}, {0x2F64, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7528}, {0x2F65, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7530}, {0x2F66, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x758B}, {0x2F67, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7592}, {0x2F68, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7676}, {0x2F69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x767D}, {0x2F6A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x76AE}, {0x2F6B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x76BF}, {0x2F6C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x76EE}, {0x2F6D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x77DB}, {0x2F6E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x77E2}, {0x2F6F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x77F3}, {0x2F70, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x793A}, {0x2F71, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x79B8}, {0x2F72, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x79BE}, {0x2F73, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7A74}, {0x2F74, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7ACB}, {0x2F75, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7AF9}, {0x2F76, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7C73}, {0x2F77, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7CF8}, {0x2F78, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7F36}, {0x2F79, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7F51}, {0x2F7A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7F8A}, {0x2F7B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7FBD}, {0x2F7C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8001}, {0x2F7D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x800C}, {0x2F7E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8012}, {0x2F7F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8033}, {0x2F80, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x807F}, {0x2F81, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8089}, {0x2F82, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x81E3}, {0x2F83, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x81EA}, {0x2F84, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x81F3}, {0x2F85, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x81FC}, {0x2F86, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x820C}, {0x2F87, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x821B}, {0x2F88, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x821F}, {0x2F89, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x826E}, {0x2F8A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8272}, {0x2F8B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8278}, {0x2F8C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x864D}, {0x2F8D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x866B}, {0x2F8E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8840}, {0x2F8F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x884C}, {0x2F90, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8863}, {0x2F91, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x897E}, {0x2F92, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x898B}, {0x2F93, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x89D2}, {0x2F94, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8A00}, {0x2F95, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8C37}, {0x2F96, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8C46}, {0x2F97, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8C55}, {0x2F98, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8C78}, {0x2F99, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8C9D}, {0x2F9A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8D64}, {0x2F9B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8D70}, {0x2F9C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8DB3}, {0x2F9D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8EAB}, {0x2F9E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8ECA}, {0x2F9F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8F9B}, {0x2FA0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8FB0}, {0x2FA1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8FB5}, {0x2FA2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9091}, {0x2FA3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9149}, {0x2FA4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x91C6}, {0x2FA5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x91CC}, {0x2FA6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x91D1}, {0x2FA7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9577}, {0x2FA8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9580}, {0x2FA9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x961C}, {0x2FAA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x96B6}, {0x2FAB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x96B9}, {0x2FAC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x96E8}, {0x2FAD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9751}, {0x2FAE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x975E}, {0x2FAF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9762}, {0x2FB0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9769}, {0x2FB1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x97CB}, {0x2FB2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x97ED}, {0x2FB3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x97F3}, {0x2FB4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9801}, {0x2FB5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x98A8}, {0x2FB6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x98DB}, {0x2FB7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x98DF}, {0x2FB8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9996}, {0x2FB9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9999}, {0x2FBA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x99AC}, {0x2FBB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9AA8}, {0x2FBC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9AD8}, {0x2FBD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9ADF}, {0x2FBE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9B25}, {0x2FBF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9B2F}, {0x2FC0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9B32}, {0x2FC1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9B3C}, {0x2FC2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9B5A}, {0x2FC3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9CE5}, {0x2FC4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9E75}, {0x2FC5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9E7F}, {0x2FC6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9EA5}, {0x2FC7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9EBB}, {0x2FC8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9EC3}, {0x2FC9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9ECD}, {0x2FCA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9ED1}, {0x2FCB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9EF9}, {0x2FCC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9EFD}, {0x2FCD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F0E}, {0x2FCE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F13}, {0x2FCF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F20}, {0x2FD0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F3B}, {0x2FD1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F4A}, {0x2FD2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F52}, {0x2FD3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F8D}, {0x2FD4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9F9C}, {0x2FD5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9FA0}, {0x3000, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0020}, {0x302A, 218, 0, 0}, {0x302B, 228, 0, 0}, {0x302C, 232, 0, 0}, {0x302D, 222, 0, 0}, {0x302E, 224, 0, 0}, {0x302F, 224, 0, 0}, {0x3036, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3012}, {0x3038, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5341}, {0x3039, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5344}, {0x303A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5345}, {0x304C, 0, 2, 2317}, {0x304E, 0, 2, 2319}, {0x3050, 0, 2, 2321}, {0x3052, 0, 2, 2323}, {0x3054, 0, 2, 2325}, {0x3056, 0, 2, 2327}, {0x3058, 0, 2, 2329}, {0x305A, 0, 2, 2331}, {0x305C, 0, 2, 2333}, {0x305E, 0, 2, 2335}, {0x3060, 0, 2, 2337}, {0x3062, 0, 2, 2339}, {0x3065, 0, 2, 2341}, {0x3067, 0, 2, 2343}, {0x3069, 0, 2, 2345}, {0x3070, 0, 2, 2347}, {0x3071, 0, 2, 2349}, {0x3073, 0, 2, 2351}, {0x3074, 0, 2, 2353}, {0x3076, 0, 2, 2355}, {0x3077, 0, 2, 2357}, {0x3079, 0, 2, 2359}, {0x307A, 0, 2, 2361}, {0x307C, 0, 2, 2363}, {0x307D, 0, 2, 2365}, {0x3094, 0, 2, 2367}, {0x3099, 8, 0, 0}, {0x309A, 8, 0, 0}, {0x309B, 0, 2 | DECOMP_COMPAT, 2369}, {0x309C, 0, 2 | DECOMP_COMPAT, 2371}, {0x309E, 0, 2, 2373}, {0x309F, 0, 2 | DECOMP_COMPAT, 2375}, {0x30AC, 0, 2, 2377}, {0x30AE, 0, 2, 2379}, {0x30B0, 0, 2, 2381}, {0x30B2, 0, 2, 2383}, {0x30B4, 0, 2, 2385}, {0x30B6, 0, 2, 2387}, {0x30B8, 0, 2, 2389}, {0x30BA, 0, 2, 2391}, {0x30BC, 0, 2, 2393}, {0x30BE, 0, 2, 2395}, {0x30C0, 0, 2, 2397}, {0x30C2, 0, 2, 2399}, {0x30C5, 0, 2, 2401}, {0x30C7, 0, 2, 2403}, {0x30C9, 0, 2, 2405}, {0x30D0, 0, 2, 2407}, {0x30D1, 0, 2, 2409}, {0x30D3, 0, 2, 2411}, {0x30D4, 0, 2, 2413}, {0x30D6, 0, 2, 2415}, {0x30D7, 0, 2, 2417}, {0x30D9, 0, 2, 2419}, {0x30DA, 0, 2, 2421}, {0x30DC, 0, 2, 2423}, {0x30DD, 0, 2, 2425}, {0x30F4, 0, 2, 2427}, {0x30F7, 0, 2, 2429}, {0x30F8, 0, 2, 2431}, {0x30F9, 0, 2, 2433}, {0x30FA, 0, 2, 2435}, {0x30FE, 0, 2, 2437}, {0x30FF, 0, 2 | DECOMP_COMPAT, 2439}, {0x3131, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1100}, {0x3132, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1101}, {0x3133, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11AA}, {0x3134, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1102}, {0x3135, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11AC}, {0x3136, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11AD}, {0x3137, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1103}, {0x3138, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1104}, {0x3139, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1105}, {0x313A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11B0}, {0x313B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11B1}, {0x313C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11B2}, {0x313D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11B3}, {0x313E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11B4}, {0x313F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11B5}, {0x3140, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x111A}, {0x3141, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1106}, {0x3142, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1107}, {0x3143, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1108}, {0x3144, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1121}, {0x3145, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1109}, {0x3146, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110A}, {0x3147, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110B}, {0x3148, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110C}, {0x3149, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110D}, {0x314A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110E}, {0x314B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110F}, {0x314C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1110}, {0x314D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1111}, {0x314E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1112}, {0x314F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1161}, {0x3150, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1162}, {0x3151, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1163}, {0x3152, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1164}, {0x3153, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1165}, {0x3154, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1166}, {0x3155, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1167}, {0x3156, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1168}, {0x3157, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1169}, {0x3158, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x116A}, {0x3159, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x116B}, {0x315A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x116C}, {0x315B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x116D}, {0x315C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x116E}, {0x315D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x116F}, {0x315E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1170}, {0x315F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1171}, {0x3160, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1172}, {0x3161, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1173}, {0x3162, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1174}, {0x3163, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1175}, {0x3164, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1160}, {0x3165, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1114}, {0x3166, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1115}, {0x3167, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11C7}, {0x3168, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11C8}, {0x3169, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11CC}, {0x316A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11CE}, {0x316B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11D3}, {0x316C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11D7}, {0x316D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11D9}, {0x316E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x111C}, {0x316F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11DD}, {0x3170, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11DF}, {0x3171, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x111D}, {0x3172, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x111E}, {0x3173, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1120}, {0x3174, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1122}, {0x3175, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1123}, {0x3176, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1127}, {0x3177, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1129}, {0x3178, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x112B}, {0x3179, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x112C}, {0x317A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x112D}, {0x317B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x112E}, {0x317C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x112F}, {0x317D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1132}, {0x317E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1136}, {0x317F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1140}, {0x3180, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1147}, {0x3181, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x114C}, {0x3182, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11F1}, {0x3183, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11F2}, {0x3184, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1157}, {0x3185, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1158}, {0x3186, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1159}, {0x3187, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1184}, {0x3188, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1185}, {0x3189, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1188}, {0x318A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1191}, {0x318B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1192}, {0x318C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1194}, {0x318D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x119E}, {0x318E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x11A1}, {0x3192, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E00}, {0x3193, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E8C}, {0x3194, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E09}, {0x3195, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x56DB}, {0x3196, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E0A}, {0x3197, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E2D}, {0x3198, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E0B}, {0x3199, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7532}, {0x319A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E59}, {0x319B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E19}, {0x319C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E01}, {0x319D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5929}, {0x319E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5730}, {0x319F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4EBA}, {0x3200, 0, 3 | DECOMP_COMPAT, 2441}, {0x3201, 0, 3 | DECOMP_COMPAT, 2444}, {0x3202, 0, 3 | DECOMP_COMPAT, 2447}, {0x3203, 0, 3 | DECOMP_COMPAT, 2450}, {0x3204, 0, 3 | DECOMP_COMPAT, 2453}, {0x3205, 0, 3 | DECOMP_COMPAT, 2456}, {0x3206, 0, 3 | DECOMP_COMPAT, 2459}, {0x3207, 0, 3 | DECOMP_COMPAT, 2462}, {0x3208, 0, 3 | DECOMP_COMPAT, 2465}, {0x3209, 0, 3 | DECOMP_COMPAT, 2468}, {0x320A, 0, 3 | DECOMP_COMPAT, 2471}, {0x320B, 0, 3 | DECOMP_COMPAT, 2474}, {0x320C, 0, 3 | DECOMP_COMPAT, 2477}, {0x320D, 0, 3 | DECOMP_COMPAT, 2480}, {0x320E, 0, 4 | DECOMP_COMPAT, 2483}, {0x320F, 0, 4 | DECOMP_COMPAT, 2487}, {0x3210, 0, 4 | DECOMP_COMPAT, 2491}, {0x3211, 0, 4 | DECOMP_COMPAT, 2495}, {0x3212, 0, 4 | DECOMP_COMPAT, 2499}, {0x3213, 0, 4 | DECOMP_COMPAT, 2503}, {0x3214, 0, 4 | DECOMP_COMPAT, 2507}, {0x3215, 0, 4 | DECOMP_COMPAT, 2511}, {0x3216, 0, 4 | DECOMP_COMPAT, 2515}, {0x3217, 0, 4 | DECOMP_COMPAT, 2519}, {0x3218, 0, 4 | DECOMP_COMPAT, 2523}, {0x3219, 0, 4 | DECOMP_COMPAT, 2527}, {0x321A, 0, 4 | DECOMP_COMPAT, 2531}, {0x321B, 0, 4 | DECOMP_COMPAT, 2535}, {0x321C, 0, 4 | DECOMP_COMPAT, 2539}, {0x321D, 0, 7 | DECOMP_COMPAT, 2543}, {0x321E, 0, 6 | DECOMP_COMPAT, 2550}, {0x3220, 0, 3 | DECOMP_COMPAT, 2556}, {0x3221, 0, 3 | DECOMP_COMPAT, 2559}, {0x3222, 0, 3 | DECOMP_COMPAT, 2562}, {0x3223, 0, 3 | DECOMP_COMPAT, 2565}, {0x3224, 0, 3 | DECOMP_COMPAT, 2568}, {0x3225, 0, 3 | DECOMP_COMPAT, 2571}, {0x3226, 0, 3 | DECOMP_COMPAT, 2574}, {0x3227, 0, 3 | DECOMP_COMPAT, 2577}, {0x3228, 0, 3 | DECOMP_COMPAT, 2580}, {0x3229, 0, 3 | DECOMP_COMPAT, 2583}, {0x322A, 0, 3 | DECOMP_COMPAT, 2586}, {0x322B, 0, 3 | DECOMP_COMPAT, 2589}, {0x322C, 0, 3 | DECOMP_COMPAT, 2592}, {0x322D, 0, 3 | DECOMP_COMPAT, 2595}, {0x322E, 0, 3 | DECOMP_COMPAT, 2598}, {0x322F, 0, 3 | DECOMP_COMPAT, 2601}, {0x3230, 0, 3 | DECOMP_COMPAT, 2604}, {0x3231, 0, 3 | DECOMP_COMPAT, 2607}, {0x3232, 0, 3 | DECOMP_COMPAT, 2610}, {0x3233, 0, 3 | DECOMP_COMPAT, 2613}, {0x3234, 0, 3 | DECOMP_COMPAT, 2616}, {0x3235, 0, 3 | DECOMP_COMPAT, 2619}, {0x3236, 0, 3 | DECOMP_COMPAT, 2622}, {0x3237, 0, 3 | DECOMP_COMPAT, 2625}, {0x3238, 0, 3 | DECOMP_COMPAT, 2628}, {0x3239, 0, 3 | DECOMP_COMPAT, 2631}, {0x323A, 0, 3 | DECOMP_COMPAT, 2634}, {0x323B, 0, 3 | DECOMP_COMPAT, 2637}, {0x323C, 0, 3 | DECOMP_COMPAT, 2640}, {0x323D, 0, 3 | DECOMP_COMPAT, 2643}, {0x323E, 0, 3 | DECOMP_COMPAT, 2646}, {0x323F, 0, 3 | DECOMP_COMPAT, 2649}, {0x3240, 0, 3 | DECOMP_COMPAT, 2652}, {0x3241, 0, 3 | DECOMP_COMPAT, 2655}, {0x3242, 0, 3 | DECOMP_COMPAT, 2658}, {0x3243, 0, 3 | DECOMP_COMPAT, 2661}, {0x3244, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x554F}, {0x3245, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5E7C}, {0x3246, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6587}, {0x3247, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7B8F}, {0x3250, 0, 3 | DECOMP_COMPAT, 2664}, {0x3251, 0, 2 | DECOMP_COMPAT, 2667}, {0x3252, 0, 2 | DECOMP_COMPAT, 2669}, {0x3253, 0, 2 | DECOMP_COMPAT, 2671}, {0x3254, 0, 2 | DECOMP_COMPAT, 2673}, {0x3255, 0, 2 | DECOMP_COMPAT, 2675}, {0x3256, 0, 2 | DECOMP_COMPAT, 2677}, {0x3257, 0, 2 | DECOMP_COMPAT, 2679}, {0x3258, 0, 2 | DECOMP_COMPAT, 2681}, {0x3259, 0, 2 | DECOMP_COMPAT, 2683}, {0x325A, 0, 2 | DECOMP_COMPAT, 2685}, {0x325B, 0, 2 | DECOMP_COMPAT, 2687}, {0x325C, 0, 2 | DECOMP_COMPAT, 2689}, {0x325D, 0, 2 | DECOMP_COMPAT, 2691}, {0x325E, 0, 2 | DECOMP_COMPAT, 2693}, {0x325F, 0, 2 | DECOMP_COMPAT, 2695}, {0x3260, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1100}, {0x3261, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1102}, {0x3262, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1103}, {0x3263, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1105}, {0x3264, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1106}, {0x3265, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1107}, {0x3266, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1109}, {0x3267, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110B}, {0x3268, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110C}, {0x3269, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110E}, {0x326A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x110F}, {0x326B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1110}, {0x326C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1111}, {0x326D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x1112}, {0x326E, 0, 2 | DECOMP_COMPAT, 2697}, {0x326F, 0, 2 | DECOMP_COMPAT, 2699}, {0x3270, 0, 2 | DECOMP_COMPAT, 2701}, {0x3271, 0, 2 | DECOMP_COMPAT, 2703}, {0x3272, 0, 2 | DECOMP_COMPAT, 2705}, {0x3273, 0, 2 | DECOMP_COMPAT, 2707}, {0x3274, 0, 2 | DECOMP_COMPAT, 2709}, {0x3275, 0, 2 | DECOMP_COMPAT, 2711}, {0x3276, 0, 2 | DECOMP_COMPAT, 2713}, {0x3277, 0, 2 | DECOMP_COMPAT, 2715}, {0x3278, 0, 2 | DECOMP_COMPAT, 2717}, {0x3279, 0, 2 | DECOMP_COMPAT, 2719}, {0x327A, 0, 2 | DECOMP_COMPAT, 2721}, {0x327B, 0, 2 | DECOMP_COMPAT, 2723}, {0x327C, 0, 5 | DECOMP_COMPAT, 2725}, {0x327D, 0, 4 | DECOMP_COMPAT, 2730}, {0x327E, 0, 2 | DECOMP_COMPAT, 2734}, {0x3280, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E00}, {0x3281, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E8C}, {0x3282, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E09}, {0x3283, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x56DB}, {0x3284, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E94}, {0x3285, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x516D}, {0x3286, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E03}, {0x3287, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x516B}, {0x3288, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E5D}, {0x3289, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5341}, {0x328A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6708}, {0x328B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x706B}, {0x328C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6C34}, {0x328D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6728}, {0x328E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x91D1}, {0x328F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x571F}, {0x3290, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x65E5}, {0x3291, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x682A}, {0x3292, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6709}, {0x3293, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x793E}, {0x3294, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x540D}, {0x3295, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7279}, {0x3296, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8CA1}, {0x3297, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x795D}, {0x3298, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x52B4}, {0x3299, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x79D8}, {0x329A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7537}, {0x329B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5973}, {0x329C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9069}, {0x329D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x512A}, {0x329E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5370}, {0x329F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6CE8}, {0x32A0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x9805}, {0x32A1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4F11}, {0x32A2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5199}, {0x32A3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6B63}, {0x32A4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E0A}, {0x32A5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E2D}, {0x32A6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E0B}, {0x32A7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5DE6}, {0x32A8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53F3}, {0x32A9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x533B}, {0x32AA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5B97}, {0x32AB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5B66}, {0x32AC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x76E3}, {0x32AD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4F01}, {0x32AE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8CC7}, {0x32AF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5354}, {0x32B0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x591C}, {0x32B1, 0, 2 | DECOMP_COMPAT, 2736}, {0x32B2, 0, 2 | DECOMP_COMPAT, 2738}, {0x32B3, 0, 2 | DECOMP_COMPAT, 2740}, {0x32B4, 0, 2 | DECOMP_COMPAT, 2742}, {0x32B5, 0, 2 | DECOMP_COMPAT, 2744}, {0x32B6, 0, 2 | DECOMP_COMPAT, 2746}, {0x32B7, 0, 2 | DECOMP_COMPAT, 2748}, {0x32B8, 0, 2 | DECOMP_COMPAT, 2750}, {0x32B9, 0, 2 | DECOMP_COMPAT, 2752}, {0x32BA, 0, 2 | DECOMP_COMPAT, 2754}, {0x32BB, 0, 2 | DECOMP_COMPAT, 2756}, {0x32BC, 0, 2 | DECOMP_COMPAT, 2758}, {0x32BD, 0, 2 | DECOMP_COMPAT, 2760}, {0x32BE, 0, 2 | DECOMP_COMPAT, 2762}, {0x32BF, 0, 2 | DECOMP_COMPAT, 2764}, {0x32C0, 0, 2 | DECOMP_COMPAT, 2766}, {0x32C1, 0, 2 | DECOMP_COMPAT, 2768}, {0x32C2, 0, 2 | DECOMP_COMPAT, 2770}, {0x32C3, 0, 2 | DECOMP_COMPAT, 2772}, {0x32C4, 0, 2 | DECOMP_COMPAT, 2774}, {0x32C5, 0, 2 | DECOMP_COMPAT, 2776}, {0x32C6, 0, 2 | DECOMP_COMPAT, 2778}, {0x32C7, 0, 2 | DECOMP_COMPAT, 2780}, {0x32C8, 0, 2 | DECOMP_COMPAT, 2782}, {0x32C9, 0, 3 | DECOMP_COMPAT, 2784}, {0x32CA, 0, 3 | DECOMP_COMPAT, 2787}, {0x32CB, 0, 3 | DECOMP_COMPAT, 2790}, {0x32CC, 0, 2 | DECOMP_COMPAT, 2793}, {0x32CD, 0, 3 | DECOMP_COMPAT, 2795}, {0x32CE, 0, 2 | DECOMP_COMPAT, 2798}, {0x32CF, 0, 3 | DECOMP_COMPAT, 2800}, {0x32D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A2}, {0x32D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A4}, {0x32D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A6}, {0x32D3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A8}, {0x32D4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AA}, {0x32D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AB}, {0x32D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AD}, {0x32D7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AF}, {0x32D8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B1}, {0x32D9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B3}, {0x32DA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B5}, {0x32DB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B7}, {0x32DC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B9}, {0x32DD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30BB}, {0x32DE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30BD}, {0x32DF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30BF}, {0x32E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C1}, {0x32E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C4}, {0x32E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C6}, {0x32E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C8}, {0x32E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CA}, {0x32E5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CB}, {0x32E6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CC}, {0x32E7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CD}, {0x32E8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CE}, {0x32E9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CF}, {0x32EA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30D2}, {0x32EB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30D5}, {0x32EC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30D8}, {0x32ED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30DB}, {0x32EE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30DE}, {0x32EF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30DF}, {0x32F0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E0}, {0x32F1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E1}, {0x32F2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E2}, {0x32F3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E4}, {0x32F4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E6}, {0x32F5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E8}, {0x32F6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E9}, {0x32F7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EA}, {0x32F8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EB}, {0x32F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EC}, {0x32FA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30ED}, {0x32FB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EF}, {0x32FC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30F0}, {0x32FD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30F1}, {0x32FE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30F2}, {0x32FF, 0, 2 | DECOMP_COMPAT, 2803}, {0x3300, 0, 4 | DECOMP_COMPAT, 2805}, {0x3301, 0, 4 | DECOMP_COMPAT, 2809}, {0x3302, 0, 4 | DECOMP_COMPAT, 2813}, {0x3303, 0, 3 | DECOMP_COMPAT, 2817}, {0x3304, 0, 4 | DECOMP_COMPAT, 2820}, {0x3305, 0, 3 | DECOMP_COMPAT, 2824}, {0x3306, 0, 3 | DECOMP_COMPAT, 2827}, {0x3307, 0, 5 | DECOMP_COMPAT, 2830}, {0x3308, 0, 4 | DECOMP_COMPAT, 2835}, {0x3309, 0, 3 | DECOMP_COMPAT, 2839}, {0x330A, 0, 3 | DECOMP_COMPAT, 2842}, {0x330B, 0, 3 | DECOMP_COMPAT, 2845}, {0x330C, 0, 4 | DECOMP_COMPAT, 2848}, {0x330D, 0, 4 | DECOMP_COMPAT, 2852}, {0x330E, 0, 3 | DECOMP_COMPAT, 2856}, {0x330F, 0, 3 | DECOMP_COMPAT, 2859}, {0x3310, 0, 2 | DECOMP_COMPAT, 2862}, {0x3311, 0, 3 | DECOMP_COMPAT, 2864}, {0x3312, 0, 4 | DECOMP_COMPAT, 2867}, {0x3313, 0, 4 | DECOMP_COMPAT, 2871}, {0x3314, 0, 2 | DECOMP_COMPAT, 2875}, {0x3315, 0, 5 | DECOMP_COMPAT, 2877}, {0x3316, 0, 6 | DECOMP_COMPAT, 2882}, {0x3317, 0, 5 | DECOMP_COMPAT, 2888}, {0x3318, 0, 3 | DECOMP_COMPAT, 2893}, {0x3319, 0, 5 | DECOMP_COMPAT, 2896}, {0x331A, 0, 5 | DECOMP_COMPAT, 2901}, {0x331B, 0, 4 | DECOMP_COMPAT, 2906}, {0x331C, 0, 3 | DECOMP_COMPAT, 2910}, {0x331D, 0, 3 | DECOMP_COMPAT, 2913}, {0x331E, 0, 3 | DECOMP_COMPAT, 2916}, {0x331F, 0, 4 | DECOMP_COMPAT, 2919}, {0x3320, 0, 5 | DECOMP_COMPAT, 2923}, {0x3321, 0, 4 | DECOMP_COMPAT, 2928}, {0x3322, 0, 3 | DECOMP_COMPAT, 2932}, {0x3323, 0, 3 | DECOMP_COMPAT, 2935}, {0x3324, 0, 3 | DECOMP_COMPAT, 2938}, {0x3325, 0, 2 | DECOMP_COMPAT, 2941}, {0x3326, 0, 2 | DECOMP_COMPAT, 2943}, {0x3327, 0, 2 | DECOMP_COMPAT, 2945}, {0x3328, 0, 2 | DECOMP_COMPAT, 2947}, {0x3329, 0, 3 | DECOMP_COMPAT, 2949}, {0x332A, 0, 3 | DECOMP_COMPAT, 2952}, {0x332B, 0, 5 | DECOMP_COMPAT, 2955}, {0x332C, 0, 3 | DECOMP_COMPAT, 2960}, {0x332D, 0, 4 | DECOMP_COMPAT, 2963}, {0x332E, 0, 5 | DECOMP_COMPAT, 2967}, {0x332F, 0, 3 | DECOMP_COMPAT, 2972}, {0x3330, 0, 2 | DECOMP_COMPAT, 2975}, {0x3331, 0, 2 | DECOMP_COMPAT, 2977}, {0x3332, 0, 5 | DECOMP_COMPAT, 2979}, {0x3333, 0, 4 | DECOMP_COMPAT, 2984}, {0x3334, 0, 5 | DECOMP_COMPAT, 2988}, {0x3335, 0, 3 | DECOMP_COMPAT, 2993}, {0x3336, 0, 5 | DECOMP_COMPAT, 2996}, {0x3337, 0, 2 | DECOMP_COMPAT, 3001}, {0x3338, 0, 3 | DECOMP_COMPAT, 3003}, {0x3339, 0, 3 | DECOMP_COMPAT, 3006}, {0x333A, 0, 3 | DECOMP_COMPAT, 3009}, {0x333B, 0, 3 | DECOMP_COMPAT, 3012}, {0x333C, 0, 3 | DECOMP_COMPAT, 3015}, {0x333D, 0, 4 | DECOMP_COMPAT, 3018}, {0x333E, 0, 3 | DECOMP_COMPAT, 3022}, {0x333F, 0, 2 | DECOMP_COMPAT, 3025}, {0x3340, 0, 3 | DECOMP_COMPAT, 3027}, {0x3341, 0, 3 | DECOMP_COMPAT, 3030}, {0x3342, 0, 3 | DECOMP_COMPAT, 3033}, {0x3343, 0, 4 | DECOMP_COMPAT, 3036}, {0x3344, 0, 3 | DECOMP_COMPAT, 3040}, {0x3345, 0, 3 | DECOMP_COMPAT, 3043}, {0x3346, 0, 3 | DECOMP_COMPAT, 3046}, {0x3347, 0, 5 | DECOMP_COMPAT, 3049}, {0x3348, 0, 4 | DECOMP_COMPAT, 3054}, {0x3349, 0, 2 | DECOMP_COMPAT, 3058}, {0x334A, 0, 5 | DECOMP_COMPAT, 3060}, {0x334B, 0, 2 | DECOMP_COMPAT, 3065}, {0x334C, 0, 4 | DECOMP_COMPAT, 3067}, {0x334D, 0, 4 | DECOMP_COMPAT, 3071}, {0x334E, 0, 3 | DECOMP_COMPAT, 3075}, {0x334F, 0, 3 | DECOMP_COMPAT, 3078}, {0x3350, 0, 3 | DECOMP_COMPAT, 3081}, {0x3351, 0, 4 | DECOMP_COMPAT, 3084}, {0x3352, 0, 2 | DECOMP_COMPAT, 3088}, {0x3353, 0, 3 | DECOMP_COMPAT, 3090}, {0x3354, 0, 4 | DECOMP_COMPAT, 3093}, {0x3355, 0, 2 | DECOMP_COMPAT, 3097}, {0x3356, 0, 5 | DECOMP_COMPAT, 3099}, {0x3357, 0, 3 | DECOMP_COMPAT, 3104}, {0x3358, 0, 2 | DECOMP_COMPAT, 3107}, {0x3359, 0, 2 | DECOMP_COMPAT, 3109}, {0x335A, 0, 2 | DECOMP_COMPAT, 3111}, {0x335B, 0, 2 | DECOMP_COMPAT, 3113}, {0x335C, 0, 2 | DECOMP_COMPAT, 3115}, {0x335D, 0, 2 | DECOMP_COMPAT, 3117}, {0x335E, 0, 2 | DECOMP_COMPAT, 3119}, {0x335F, 0, 2 | DECOMP_COMPAT, 3121}, {0x3360, 0, 2 | DECOMP_COMPAT, 3123}, {0x3361, 0, 2 | DECOMP_COMPAT, 3125}, {0x3362, 0, 3 | DECOMP_COMPAT, 3127}, {0x3363, 0, 3 | DECOMP_COMPAT, 3130}, {0x3364, 0, 3 | DECOMP_COMPAT, 3133}, {0x3365, 0, 3 | DECOMP_COMPAT, 3136}, {0x3366, 0, 3 | DECOMP_COMPAT, 3139}, {0x3367, 0, 3 | DECOMP_COMPAT, 3142}, {0x3368, 0, 3 | DECOMP_COMPAT, 3145}, {0x3369, 0, 3 | DECOMP_COMPAT, 3148}, {0x336A, 0, 3 | DECOMP_COMPAT, 3151}, {0x336B, 0, 3 | DECOMP_COMPAT, 3154}, {0x336C, 0, 3 | DECOMP_COMPAT, 3157}, {0x336D, 0, 3 | DECOMP_COMPAT, 3160}, {0x336E, 0, 3 | DECOMP_COMPAT, 3163}, {0x336F, 0, 3 | DECOMP_COMPAT, 3166}, {0x3370, 0, 3 | DECOMP_COMPAT, 3169}, {0x3371, 0, 3 | DECOMP_COMPAT, 3172}, {0x3372, 0, 2 | DECOMP_COMPAT, 3175}, {0x3373, 0, 2 | DECOMP_COMPAT, 3177}, {0x3374, 0, 3 | DECOMP_COMPAT, 3179}, {0x3375, 0, 2 | DECOMP_COMPAT, 3182}, {0x3376, 0, 2 | DECOMP_COMPAT, 3184}, {0x3377, 0, 2 | DECOMP_COMPAT, 3186}, {0x3378, 0, 3 | DECOMP_COMPAT, 3188}, {0x3379, 0, 3 | DECOMP_COMPAT, 3191}, {0x337A, 0, 2 | DECOMP_COMPAT, 3194}, {0x337B, 0, 2 | DECOMP_COMPAT, 3196}, {0x337C, 0, 2 | DECOMP_COMPAT, 3198}, {0x337D, 0, 2 | DECOMP_COMPAT, 3200}, {0x337E, 0, 2 | DECOMP_COMPAT, 3202}, {0x337F, 0, 4 | DECOMP_COMPAT, 3204}, {0x3380, 0, 2 | DECOMP_COMPAT, 3208}, {0x3381, 0, 2 | DECOMP_COMPAT, 3210}, {0x3382, 0, 2 | DECOMP_COMPAT, 3212}, {0x3383, 0, 2 | DECOMP_COMPAT, 3214}, {0x3384, 0, 2 | DECOMP_COMPAT, 3216}, {0x3385, 0, 2 | DECOMP_COMPAT, 3218}, {0x3386, 0, 2 | DECOMP_COMPAT, 3220}, {0x3387, 0, 2 | DECOMP_COMPAT, 3222}, {0x3388, 0, 3 | DECOMP_COMPAT, 3224}, {0x3389, 0, 4 | DECOMP_COMPAT, 3227}, {0x338A, 0, 2 | DECOMP_COMPAT, 3231}, {0x338B, 0, 2 | DECOMP_COMPAT, 3233}, {0x338C, 0, 2 | DECOMP_COMPAT, 3235}, {0x338D, 0, 2 | DECOMP_COMPAT, 3237}, {0x338E, 0, 2 | DECOMP_COMPAT, 3239}, {0x338F, 0, 2 | DECOMP_COMPAT, 3241}, {0x3390, 0, 2 | DECOMP_COMPAT, 3243}, {0x3391, 0, 3 | DECOMP_COMPAT, 3245}, {0x3392, 0, 3 | DECOMP_COMPAT, 3248}, {0x3393, 0, 3 | DECOMP_COMPAT, 3251}, {0x3394, 0, 3 | DECOMP_COMPAT, 3254}, {0x3395, 0, 2 | DECOMP_COMPAT, 3257}, {0x3396, 0, 2 | DECOMP_COMPAT, 3259}, {0x3397, 0, 2 | DECOMP_COMPAT, 3261}, {0x3398, 0, 2 | DECOMP_COMPAT, 3263}, {0x3399, 0, 2 | DECOMP_COMPAT, 3265}, {0x339A, 0, 2 | DECOMP_COMPAT, 3267}, {0x339B, 0, 2 | DECOMP_COMPAT, 3269}, {0x339C, 0, 2 | DECOMP_COMPAT, 3271}, {0x339D, 0, 2 | DECOMP_COMPAT, 3273}, {0x339E, 0, 2 | DECOMP_COMPAT, 3275}, {0x339F, 0, 3 | DECOMP_COMPAT, 3277}, {0x33A0, 0, 3 | DECOMP_COMPAT, 3280}, {0x33A1, 0, 2 | DECOMP_COMPAT, 3283}, {0x33A2, 0, 3 | DECOMP_COMPAT, 3285}, {0x33A3, 0, 3 | DECOMP_COMPAT, 3288}, {0x33A4, 0, 3 | DECOMP_COMPAT, 3291}, {0x33A5, 0, 2 | DECOMP_COMPAT, 3294}, {0x33A6, 0, 3 | DECOMP_COMPAT, 3296}, {0x33A7, 0, 3 | DECOMP_COMPAT, 3299}, {0x33A8, 0, 4 | DECOMP_COMPAT, 3302}, {0x33A9, 0, 2 | DECOMP_COMPAT, 3306}, {0x33AA, 0, 3 | DECOMP_COMPAT, 3308}, {0x33AB, 0, 3 | DECOMP_COMPAT, 3311}, {0x33AC, 0, 3 | DECOMP_COMPAT, 3314}, {0x33AD, 0, 3 | DECOMP_COMPAT, 3317}, {0x33AE, 0, 5 | DECOMP_COMPAT, 3320}, {0x33AF, 0, 6 | DECOMP_COMPAT, 3325}, {0x33B0, 0, 2 | DECOMP_COMPAT, 3331}, {0x33B1, 0, 2 | DECOMP_COMPAT, 3333}, {0x33B2, 0, 2 | DECOMP_COMPAT, 3335}, {0x33B3, 0, 2 | DECOMP_COMPAT, 3337}, {0x33B4, 0, 2 | DECOMP_COMPAT, 3339}, {0x33B5, 0, 2 | DECOMP_COMPAT, 3341}, {0x33B6, 0, 2 | DECOMP_COMPAT, 3343}, {0x33B7, 0, 2 | DECOMP_COMPAT, 3345}, {0x33B8, 0, 2 | DECOMP_COMPAT, 3347}, {0x33B9, 0, 2 | DECOMP_COMPAT, 3349}, {0x33BA, 0, 2 | DECOMP_COMPAT, 3351}, {0x33BB, 0, 2 | DECOMP_COMPAT, 3353}, {0x33BC, 0, 2 | DECOMP_COMPAT, 3355}, {0x33BD, 0, 2 | DECOMP_COMPAT, 3357}, {0x33BE, 0, 2 | DECOMP_COMPAT, 3359}, {0x33BF, 0, 2 | DECOMP_COMPAT, 3361}, {0x33C0, 0, 2 | DECOMP_COMPAT, 3363}, {0x33C1, 0, 2 | DECOMP_COMPAT, 3365}, {0x33C2, 0, 4 | DECOMP_COMPAT, 3367}, {0x33C3, 0, 2 | DECOMP_COMPAT, 3371}, {0x33C4, 0, 2 | DECOMP_COMPAT, 3373}, {0x33C5, 0, 2 | DECOMP_COMPAT, 3375}, {0x33C6, 0, 4 | DECOMP_COMPAT, 3377}, {0x33C7, 0, 3 | DECOMP_COMPAT, 3381}, {0x33C8, 0, 2 | DECOMP_COMPAT, 3384}, {0x33C9, 0, 2 | DECOMP_COMPAT, 3386}, {0x33CA, 0, 2 | DECOMP_COMPAT, 3388}, {0x33CB, 0, 2 | DECOMP_COMPAT, 3390}, {0x33CC, 0, 2 | DECOMP_COMPAT, 3392}, {0x33CD, 0, 2 | DECOMP_COMPAT, 3394}, {0x33CE, 0, 2 | DECOMP_COMPAT, 3396}, {0x33CF, 0, 2 | DECOMP_COMPAT, 3398}, {0x33D0, 0, 2 | DECOMP_COMPAT, 3400}, {0x33D1, 0, 2 | DECOMP_COMPAT, 3402}, {0x33D2, 0, 3 | DECOMP_COMPAT, 3404}, {0x33D3, 0, 2 | DECOMP_COMPAT, 3407}, {0x33D4, 0, 2 | DECOMP_COMPAT, 3409}, {0x33D5, 0, 3 | DECOMP_COMPAT, 3411}, {0x33D6, 0, 3 | DECOMP_COMPAT, 3414}, {0x33D7, 0, 2 | DECOMP_COMPAT, 3417}, {0x33D8, 0, 4 | DECOMP_COMPAT, 3419}, {0x33D9, 0, 3 | DECOMP_COMPAT, 3423}, {0x33DA, 0, 2 | DECOMP_COMPAT, 3426}, {0x33DB, 0, 2 | DECOMP_COMPAT, 3428}, {0x33DC, 0, 2 | DECOMP_COMPAT, 3430}, {0x33DD, 0, 2 | DECOMP_COMPAT, 3432}, {0x33DE, 0, 3 | DECOMP_COMPAT, 3434}, {0x33DF, 0, 3 | DECOMP_COMPAT, 3437}, {0x33E0, 0, 2 | DECOMP_COMPAT, 3440}, {0x33E1, 0, 2 | DECOMP_COMPAT, 3442}, {0x33E2, 0, 2 | DECOMP_COMPAT, 3444}, {0x33E3, 0, 2 | DECOMP_COMPAT, 3446}, {0x33E4, 0, 2 | DECOMP_COMPAT, 3448}, {0x33E5, 0, 2 | DECOMP_COMPAT, 3450}, {0x33E6, 0, 2 | DECOMP_COMPAT, 3452}, {0x33E7, 0, 2 | DECOMP_COMPAT, 3454}, {0x33E8, 0, 2 | DECOMP_COMPAT, 3456}, {0x33E9, 0, 3 | DECOMP_COMPAT, 3458}, {0x33EA, 0, 3 | DECOMP_COMPAT, 3461}, {0x33EB, 0, 3 | DECOMP_COMPAT, 3464}, {0x33EC, 0, 3 | DECOMP_COMPAT, 3467}, {0x33ED, 0, 3 | DECOMP_COMPAT, 3470}, {0x33EE, 0, 3 | DECOMP_COMPAT, 3473}, {0x33EF, 0, 3 | DECOMP_COMPAT, 3476}, {0x33F0, 0, 3 | DECOMP_COMPAT, 3479}, {0x33F1, 0, 3 | DECOMP_COMPAT, 3482}, {0x33F2, 0, 3 | DECOMP_COMPAT, 3485}, {0x33F3, 0, 3 | DECOMP_COMPAT, 3488}, {0x33F4, 0, 3 | DECOMP_COMPAT, 3491}, {0x33F5, 0, 3 | DECOMP_COMPAT, 3494}, {0x33F6, 0, 3 | DECOMP_COMPAT, 3497}, {0x33F7, 0, 3 | DECOMP_COMPAT, 3500}, {0x33F8, 0, 3 | DECOMP_COMPAT, 3503}, {0x33F9, 0, 3 | DECOMP_COMPAT, 3506}, {0x33FA, 0, 3 | DECOMP_COMPAT, 3509}, {0x33FB, 0, 3 | DECOMP_COMPAT, 3512}, {0x33FC, 0, 3 | DECOMP_COMPAT, 3515}, {0x33FD, 0, 3 | DECOMP_COMPAT, 3518}, {0x33FE, 0, 3 | DECOMP_COMPAT, 3521}, {0x33FF, 0, 3 | DECOMP_COMPAT, 3524}, {0xA66F, 230, 0, 0}, {0xA674, 230, 0, 0}, {0xA675, 230, 0, 0}, {0xA676, 230, 0, 0}, {0xA677, 230, 0, 0}, {0xA678, 230, 0, 0}, {0xA679, 230, 0, 0}, {0xA67A, 230, 0, 0}, {0xA67B, 230, 0, 0}, {0xA67C, 230, 0, 0}, {0xA67D, 230, 0, 0}, {0xA69C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x044A}, {0xA69D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x044C}, {0xA69E, 230, 0, 0}, {0xA69F, 230, 0, 0}, {0xA6F0, 230, 0, 0}, {0xA6F1, 230, 0, 0}, {0xA770, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0xA76F}, {0xA7F8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0126}, {0xA7F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0153}, {0xA806, 9, 0, 0}, {0xA82C, 9, 0, 0}, {0xA8C4, 9, 0, 0}, {0xA8E0, 230, 0, 0}, {0xA8E1, 230, 0, 0}, {0xA8E2, 230, 0, 0}, {0xA8E3, 230, 0, 0}, {0xA8E4, 230, 0, 0}, {0xA8E5, 230, 0, 0}, {0xA8E6, 230, 0, 0}, {0xA8E7, 230, 0, 0}, {0xA8E8, 230, 0, 0}, {0xA8E9, 230, 0, 0}, {0xA8EA, 230, 0, 0}, {0xA8EB, 230, 0, 0}, {0xA8EC, 230, 0, 0}, {0xA8ED, 230, 0, 0}, {0xA8EE, 230, 0, 0}, {0xA8EF, 230, 0, 0}, {0xA8F0, 230, 0, 0}, {0xA8F1, 230, 0, 0}, {0xA92B, 220, 0, 0}, {0xA92C, 220, 0, 0}, {0xA92D, 220, 0, 0}, {0xA953, 9, 0, 0}, {0xA9B3, 7, 0, 0}, {0xA9C0, 9, 0, 0}, {0xAAB0, 230, 0, 0}, {0xAAB2, 230, 0, 0}, {0xAAB3, 230, 0, 0}, {0xAAB4, 220, 0, 0}, {0xAAB7, 230, 0, 0}, {0xAAB8, 230, 0, 0}, {0xAABE, 230, 0, 0}, {0xAABF, 230, 0, 0}, {0xAAC1, 230, 0, 0}, {0xAAF6, 9, 0, 0}, {0xAB5C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0xA727}, {0xAB5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0xAB37}, {0xAB5E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x026B}, {0xAB5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0xAB52}, {0xAB69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x028D}, {0xABED, 9, 0, 0}, {0xF900, 0, 1 | DECOMP_INLINE, 0x8C48}, {0xF901, 0, 1 | DECOMP_INLINE, 0x66F4}, {0xF902, 0, 1 | DECOMP_INLINE, 0x8ECA}, {0xF903, 0, 1 | DECOMP_INLINE, 0x8CC8}, {0xF904, 0, 1 | DECOMP_INLINE, 0x6ED1}, {0xF905, 0, 1 | DECOMP_INLINE, 0x4E32}, {0xF906, 0, 1 | DECOMP_INLINE, 0x53E5}, {0xF907, 0, 1 | DECOMP_INLINE, 0x9F9C}, {0xF908, 0, 1 | DECOMP_INLINE, 0x9F9C}, {0xF909, 0, 1 | DECOMP_INLINE, 0x5951}, {0xF90A, 0, 1 | DECOMP_INLINE, 0x91D1}, {0xF90B, 0, 1 | DECOMP_INLINE, 0x5587}, {0xF90C, 0, 1 | DECOMP_INLINE, 0x5948}, {0xF90D, 0, 1 | DECOMP_INLINE, 0x61F6}, {0xF90E, 0, 1 | DECOMP_INLINE, 0x7669}, {0xF90F, 0, 1 | DECOMP_INLINE, 0x7F85}, {0xF910, 0, 1 | DECOMP_INLINE, 0x863F}, {0xF911, 0, 1 | DECOMP_INLINE, 0x87BA}, {0xF912, 0, 1 | DECOMP_INLINE, 0x88F8}, {0xF913, 0, 1 | DECOMP_INLINE, 0x908F}, {0xF914, 0, 1 | DECOMP_INLINE, 0x6A02}, {0xF915, 0, 1 | DECOMP_INLINE, 0x6D1B}, {0xF916, 0, 1 | DECOMP_INLINE, 0x70D9}, {0xF917, 0, 1 | DECOMP_INLINE, 0x73DE}, {0xF918, 0, 1 | DECOMP_INLINE, 0x843D}, {0xF919, 0, 1 | DECOMP_INLINE, 0x916A}, {0xF91A, 0, 1 | DECOMP_INLINE, 0x99F1}, {0xF91B, 0, 1 | DECOMP_INLINE, 0x4E82}, {0xF91C, 0, 1 | DECOMP_INLINE, 0x5375}, {0xF91D, 0, 1 | DECOMP_INLINE, 0x6B04}, {0xF91E, 0, 1 | DECOMP_INLINE, 0x721B}, {0xF91F, 0, 1 | DECOMP_INLINE, 0x862D}, {0xF920, 0, 1 | DECOMP_INLINE, 0x9E1E}, {0xF921, 0, 1 | DECOMP_INLINE, 0x5D50}, {0xF922, 0, 1 | DECOMP_INLINE, 0x6FEB}, {0xF923, 0, 1 | DECOMP_INLINE, 0x85CD}, {0xF924, 0, 1 | DECOMP_INLINE, 0x8964}, {0xF925, 0, 1 | DECOMP_INLINE, 0x62C9}, {0xF926, 0, 1 | DECOMP_INLINE, 0x81D8}, {0xF927, 0, 1 | DECOMP_INLINE, 0x881F}, {0xF928, 0, 1 | DECOMP_INLINE, 0x5ECA}, {0xF929, 0, 1 | DECOMP_INLINE, 0x6717}, {0xF92A, 0, 1 | DECOMP_INLINE, 0x6D6A}, {0xF92B, 0, 1 | DECOMP_INLINE, 0x72FC}, {0xF92C, 0, 1 | DECOMP_INLINE, 0x90CE}, {0xF92D, 0, 1 | DECOMP_INLINE, 0x4F86}, {0xF92E, 0, 1 | DECOMP_INLINE, 0x51B7}, {0xF92F, 0, 1 | DECOMP_INLINE, 0x52DE}, {0xF930, 0, 1 | DECOMP_INLINE, 0x64C4}, {0xF931, 0, 1 | DECOMP_INLINE, 0x6AD3}, {0xF932, 0, 1 | DECOMP_INLINE, 0x7210}, {0xF933, 0, 1 | DECOMP_INLINE, 0x76E7}, {0xF934, 0, 1 | DECOMP_INLINE, 0x8001}, {0xF935, 0, 1 | DECOMP_INLINE, 0x8606}, {0xF936, 0, 1 | DECOMP_INLINE, 0x865C}, {0xF937, 0, 1 | DECOMP_INLINE, 0x8DEF}, {0xF938, 0, 1 | DECOMP_INLINE, 0x9732}, {0xF939, 0, 1 | DECOMP_INLINE, 0x9B6F}, {0xF93A, 0, 1 | DECOMP_INLINE, 0x9DFA}, {0xF93B, 0, 1 | DECOMP_INLINE, 0x788C}, {0xF93C, 0, 1 | DECOMP_INLINE, 0x797F}, {0xF93D, 0, 1 | DECOMP_INLINE, 0x7DA0}, {0xF93E, 0, 1 | DECOMP_INLINE, 0x83C9}, {0xF93F, 0, 1 | DECOMP_INLINE, 0x9304}, {0xF940, 0, 1 | DECOMP_INLINE, 0x9E7F}, {0xF941, 0, 1 | DECOMP_INLINE, 0x8AD6}, {0xF942, 0, 1 | DECOMP_INLINE, 0x58DF}, {0xF943, 0, 1 | DECOMP_INLINE, 0x5F04}, {0xF944, 0, 1 | DECOMP_INLINE, 0x7C60}, {0xF945, 0, 1 | DECOMP_INLINE, 0x807E}, {0xF946, 0, 1 | DECOMP_INLINE, 0x7262}, {0xF947, 0, 1 | DECOMP_INLINE, 0x78CA}, {0xF948, 0, 1 | DECOMP_INLINE, 0x8CC2}, {0xF949, 0, 1 | DECOMP_INLINE, 0x96F7}, {0xF94A, 0, 1 | DECOMP_INLINE, 0x58D8}, {0xF94B, 0, 1 | DECOMP_INLINE, 0x5C62}, {0xF94C, 0, 1 | DECOMP_INLINE, 0x6A13}, {0xF94D, 0, 1 | DECOMP_INLINE, 0x6DDA}, {0xF94E, 0, 1 | DECOMP_INLINE, 0x6F0F}, {0xF94F, 0, 1 | DECOMP_INLINE, 0x7D2F}, {0xF950, 0, 1 | DECOMP_INLINE, 0x7E37}, {0xF951, 0, 1 | DECOMP_INLINE, 0x964B}, {0xF952, 0, 1 | DECOMP_INLINE, 0x52D2}, {0xF953, 0, 1 | DECOMP_INLINE, 0x808B}, {0xF954, 0, 1 | DECOMP_INLINE, 0x51DC}, {0xF955, 0, 1 | DECOMP_INLINE, 0x51CC}, {0xF956, 0, 1 | DECOMP_INLINE, 0x7A1C}, {0xF957, 0, 1 | DECOMP_INLINE, 0x7DBE}, {0xF958, 0, 1 | DECOMP_INLINE, 0x83F1}, {0xF959, 0, 1 | DECOMP_INLINE, 0x9675}, {0xF95A, 0, 1 | DECOMP_INLINE, 0x8B80}, {0xF95B, 0, 1 | DECOMP_INLINE, 0x62CF}, {0xF95C, 0, 1 | DECOMP_INLINE, 0x6A02}, {0xF95D, 0, 1 | DECOMP_INLINE, 0x8AFE}, {0xF95E, 0, 1 | DECOMP_INLINE, 0x4E39}, {0xF95F, 0, 1 | DECOMP_INLINE, 0x5BE7}, {0xF960, 0, 1 | DECOMP_INLINE, 0x6012}, {0xF961, 0, 1 | DECOMP_INLINE, 0x7387}, {0xF962, 0, 1 | DECOMP_INLINE, 0x7570}, {0xF963, 0, 1 | DECOMP_INLINE, 0x5317}, {0xF964, 0, 1 | DECOMP_INLINE, 0x78FB}, {0xF965, 0, 1 | DECOMP_INLINE, 0x4FBF}, {0xF966, 0, 1 | DECOMP_INLINE, 0x5FA9}, {0xF967, 0, 1 | DECOMP_INLINE, 0x4E0D}, {0xF968, 0, 1 | DECOMP_INLINE, 0x6CCC}, {0xF969, 0, 1 | DECOMP_INLINE, 0x6578}, {0xF96A, 0, 1 | DECOMP_INLINE, 0x7D22}, {0xF96B, 0, 1 | DECOMP_INLINE, 0x53C3}, {0xF96C, 0, 1 | DECOMP_INLINE, 0x585E}, {0xF96D, 0, 1 | DECOMP_INLINE, 0x7701}, {0xF96E, 0, 1 | DECOMP_INLINE, 0x8449}, {0xF96F, 0, 1 | DECOMP_INLINE, 0x8AAA}, {0xF970, 0, 1 | DECOMP_INLINE, 0x6BBA}, {0xF971, 0, 1 | DECOMP_INLINE, 0x8FB0}, {0xF972, 0, 1 | DECOMP_INLINE, 0x6C88}, {0xF973, 0, 1 | DECOMP_INLINE, 0x62FE}, {0xF974, 0, 1 | DECOMP_INLINE, 0x82E5}, {0xF975, 0, 1 | DECOMP_INLINE, 0x63A0}, {0xF976, 0, 1 | DECOMP_INLINE, 0x7565}, {0xF977, 0, 1 | DECOMP_INLINE, 0x4EAE}, {0xF978, 0, 1 | DECOMP_INLINE, 0x5169}, {0xF979, 0, 1 | DECOMP_INLINE, 0x51C9}, {0xF97A, 0, 1 | DECOMP_INLINE, 0x6881}, {0xF97B, 0, 1 | DECOMP_INLINE, 0x7CE7}, {0xF97C, 0, 1 | DECOMP_INLINE, 0x826F}, {0xF97D, 0, 1 | DECOMP_INLINE, 0x8AD2}, {0xF97E, 0, 1 | DECOMP_INLINE, 0x91CF}, {0xF97F, 0, 1 | DECOMP_INLINE, 0x52F5}, {0xF980, 0, 1 | DECOMP_INLINE, 0x5442}, {0xF981, 0, 1 | DECOMP_INLINE, 0x5973}, {0xF982, 0, 1 | DECOMP_INLINE, 0x5EEC}, {0xF983, 0, 1 | DECOMP_INLINE, 0x65C5}, {0xF984, 0, 1 | DECOMP_INLINE, 0x6FFE}, {0xF985, 0, 1 | DECOMP_INLINE, 0x792A}, {0xF986, 0, 1 | DECOMP_INLINE, 0x95AD}, {0xF987, 0, 1 | DECOMP_INLINE, 0x9A6A}, {0xF988, 0, 1 | DECOMP_INLINE, 0x9E97}, {0xF989, 0, 1 | DECOMP_INLINE, 0x9ECE}, {0xF98A, 0, 1 | DECOMP_INLINE, 0x529B}, {0xF98B, 0, 1 | DECOMP_INLINE, 0x66C6}, {0xF98C, 0, 1 | DECOMP_INLINE, 0x6B77}, {0xF98D, 0, 1 | DECOMP_INLINE, 0x8F62}, {0xF98E, 0, 1 | DECOMP_INLINE, 0x5E74}, {0xF98F, 0, 1 | DECOMP_INLINE, 0x6190}, {0xF990, 0, 1 | DECOMP_INLINE, 0x6200}, {0xF991, 0, 1 | DECOMP_INLINE, 0x649A}, {0xF992, 0, 1 | DECOMP_INLINE, 0x6F23}, {0xF993, 0, 1 | DECOMP_INLINE, 0x7149}, {0xF994, 0, 1 | DECOMP_INLINE, 0x7489}, {0xF995, 0, 1 | DECOMP_INLINE, 0x79CA}, {0xF996, 0, 1 | DECOMP_INLINE, 0x7DF4}, {0xF997, 0, 1 | DECOMP_INLINE, 0x806F}, {0xF998, 0, 1 | DECOMP_INLINE, 0x8F26}, {0xF999, 0, 1 | DECOMP_INLINE, 0x84EE}, {0xF99A, 0, 1 | DECOMP_INLINE, 0x9023}, {0xF99B, 0, 1 | DECOMP_INLINE, 0x934A}, {0xF99C, 0, 1 | DECOMP_INLINE, 0x5217}, {0xF99D, 0, 1 | DECOMP_INLINE, 0x52A3}, {0xF99E, 0, 1 | DECOMP_INLINE, 0x54BD}, {0xF99F, 0, 1 | DECOMP_INLINE, 0x70C8}, {0xF9A0, 0, 1 | DECOMP_INLINE, 0x88C2}, {0xF9A1, 0, 1 | DECOMP_INLINE, 0x8AAA}, {0xF9A2, 0, 1 | DECOMP_INLINE, 0x5EC9}, {0xF9A3, 0, 1 | DECOMP_INLINE, 0x5FF5}, {0xF9A4, 0, 1 | DECOMP_INLINE, 0x637B}, {0xF9A5, 0, 1 | DECOMP_INLINE, 0x6BAE}, {0xF9A6, 0, 1 | DECOMP_INLINE, 0x7C3E}, {0xF9A7, 0, 1 | DECOMP_INLINE, 0x7375}, {0xF9A8, 0, 1 | DECOMP_INLINE, 0x4EE4}, {0xF9A9, 0, 1 | DECOMP_INLINE, 0x56F9}, {0xF9AA, 0, 1 | DECOMP_INLINE, 0x5BE7}, {0xF9AB, 0, 1 | DECOMP_INLINE, 0x5DBA}, {0xF9AC, 0, 1 | DECOMP_INLINE, 0x601C}, {0xF9AD, 0, 1 | DECOMP_INLINE, 0x73B2}, {0xF9AE, 0, 1 | DECOMP_INLINE, 0x7469}, {0xF9AF, 0, 1 | DECOMP_INLINE, 0x7F9A}, {0xF9B0, 0, 1 | DECOMP_INLINE, 0x8046}, {0xF9B1, 0, 1 | DECOMP_INLINE, 0x9234}, {0xF9B2, 0, 1 | DECOMP_INLINE, 0x96F6}, {0xF9B3, 0, 1 | DECOMP_INLINE, 0x9748}, {0xF9B4, 0, 1 | DECOMP_INLINE, 0x9818}, {0xF9B5, 0, 1 | DECOMP_INLINE, 0x4F8B}, {0xF9B6, 0, 1 | DECOMP_INLINE, 0x79AE}, {0xF9B7, 0, 1 | DECOMP_INLINE, 0x91B4}, {0xF9B8, 0, 1 | DECOMP_INLINE, 0x96B8}, {0xF9B9, 0, 1 | DECOMP_INLINE, 0x60E1}, {0xF9BA, 0, 1 | DECOMP_INLINE, 0x4E86}, {0xF9BB, 0, 1 | DECOMP_INLINE, 0x50DA}, {0xF9BC, 0, 1 | DECOMP_INLINE, 0x5BEE}, {0xF9BD, 0, 1 | DECOMP_INLINE, 0x5C3F}, {0xF9BE, 0, 1 | DECOMP_INLINE, 0x6599}, {0xF9BF, 0, 1 | DECOMP_INLINE, 0x6A02}, {0xF9C0, 0, 1 | DECOMP_INLINE, 0x71CE}, {0xF9C1, 0, 1 | DECOMP_INLINE, 0x7642}, {0xF9C2, 0, 1 | DECOMP_INLINE, 0x84FC}, {0xF9C3, 0, 1 | DECOMP_INLINE, 0x907C}, {0xF9C4, 0, 1 | DECOMP_INLINE, 0x9F8D}, {0xF9C5, 0, 1 | DECOMP_INLINE, 0x6688}, {0xF9C6, 0, 1 | DECOMP_INLINE, 0x962E}, {0xF9C7, 0, 1 | DECOMP_INLINE, 0x5289}, {0xF9C8, 0, 1 | DECOMP_INLINE, 0x677B}, {0xF9C9, 0, 1 | DECOMP_INLINE, 0x67F3}, {0xF9CA, 0, 1 | DECOMP_INLINE, 0x6D41}, {0xF9CB, 0, 1 | DECOMP_INLINE, 0x6E9C}, {0xF9CC, 0, 1 | DECOMP_INLINE, 0x7409}, {0xF9CD, 0, 1 | DECOMP_INLINE, 0x7559}, {0xF9CE, 0, 1 | DECOMP_INLINE, 0x786B}, {0xF9CF, 0, 1 | DECOMP_INLINE, 0x7D10}, {0xF9D0, 0, 1 | DECOMP_INLINE, 0x985E}, {0xF9D1, 0, 1 | DECOMP_INLINE, 0x516D}, {0xF9D2, 0, 1 | DECOMP_INLINE, 0x622E}, {0xF9D3, 0, 1 | DECOMP_INLINE, 0x9678}, {0xF9D4, 0, 1 | DECOMP_INLINE, 0x502B}, {0xF9D5, 0, 1 | DECOMP_INLINE, 0x5D19}, {0xF9D6, 0, 1 | DECOMP_INLINE, 0x6DEA}, {0xF9D7, 0, 1 | DECOMP_INLINE, 0x8F2A}, {0xF9D8, 0, 1 | DECOMP_INLINE, 0x5F8B}, {0xF9D9, 0, 1 | DECOMP_INLINE, 0x6144}, {0xF9DA, 0, 1 | DECOMP_INLINE, 0x6817}, {0xF9DB, 0, 1 | DECOMP_INLINE, 0x7387}, {0xF9DC, 0, 1 | DECOMP_INLINE, 0x9686}, {0xF9DD, 0, 1 | DECOMP_INLINE, 0x5229}, {0xF9DE, 0, 1 | DECOMP_INLINE, 0x540F}, {0xF9DF, 0, 1 | DECOMP_INLINE, 0x5C65}, {0xF9E0, 0, 1 | DECOMP_INLINE, 0x6613}, {0xF9E1, 0, 1 | DECOMP_INLINE, 0x674E}, {0xF9E2, 0, 1 | DECOMP_INLINE, 0x68A8}, {0xF9E3, 0, 1 | DECOMP_INLINE, 0x6CE5}, {0xF9E4, 0, 1 | DECOMP_INLINE, 0x7406}, {0xF9E5, 0, 1 | DECOMP_INLINE, 0x75E2}, {0xF9E6, 0, 1 | DECOMP_INLINE, 0x7F79}, {0xF9E7, 0, 1 | DECOMP_INLINE, 0x88CF}, {0xF9E8, 0, 1 | DECOMP_INLINE, 0x88E1}, {0xF9E9, 0, 1 | DECOMP_INLINE, 0x91CC}, {0xF9EA, 0, 1 | DECOMP_INLINE, 0x96E2}, {0xF9EB, 0, 1 | DECOMP_INLINE, 0x533F}, {0xF9EC, 0, 1 | DECOMP_INLINE, 0x6EBA}, {0xF9ED, 0, 1 | DECOMP_INLINE, 0x541D}, {0xF9EE, 0, 1 | DECOMP_INLINE, 0x71D0}, {0xF9EF, 0, 1 | DECOMP_INLINE, 0x7498}, {0xF9F0, 0, 1 | DECOMP_INLINE, 0x85FA}, {0xF9F1, 0, 1 | DECOMP_INLINE, 0x96A3}, {0xF9F2, 0, 1 | DECOMP_INLINE, 0x9C57}, {0xF9F3, 0, 1 | DECOMP_INLINE, 0x9E9F}, {0xF9F4, 0, 1 | DECOMP_INLINE, 0x6797}, {0xF9F5, 0, 1 | DECOMP_INLINE, 0x6DCB}, {0xF9F6, 0, 1 | DECOMP_INLINE, 0x81E8}, {0xF9F7, 0, 1 | DECOMP_INLINE, 0x7ACB}, {0xF9F8, 0, 1 | DECOMP_INLINE, 0x7B20}, {0xF9F9, 0, 1 | DECOMP_INLINE, 0x7C92}, {0xF9FA, 0, 1 | DECOMP_INLINE, 0x72C0}, {0xF9FB, 0, 1 | DECOMP_INLINE, 0x7099}, {0xF9FC, 0, 1 | DECOMP_INLINE, 0x8B58}, {0xF9FD, 0, 1 | DECOMP_INLINE, 0x4EC0}, {0xF9FE, 0, 1 | DECOMP_INLINE, 0x8336}, {0xF9FF, 0, 1 | DECOMP_INLINE, 0x523A}, {0xFA00, 0, 1 | DECOMP_INLINE, 0x5207}, {0xFA01, 0, 1 | DECOMP_INLINE, 0x5EA6}, {0xFA02, 0, 1 | DECOMP_INLINE, 0x62D3}, {0xFA03, 0, 1 | DECOMP_INLINE, 0x7CD6}, {0xFA04, 0, 1 | DECOMP_INLINE, 0x5B85}, {0xFA05, 0, 1 | DECOMP_INLINE, 0x6D1E}, {0xFA06, 0, 1 | DECOMP_INLINE, 0x66B4}, {0xFA07, 0, 1 | DECOMP_INLINE, 0x8F3B}, {0xFA08, 0, 1 | DECOMP_INLINE, 0x884C}, {0xFA09, 0, 1 | DECOMP_INLINE, 0x964D}, {0xFA0A, 0, 1 | DECOMP_INLINE, 0x898B}, {0xFA0B, 0, 1 | DECOMP_INLINE, 0x5ED3}, {0xFA0C, 0, 1 | DECOMP_INLINE, 0x5140}, {0xFA0D, 0, 1 | DECOMP_INLINE, 0x55C0}, {0xFA10, 0, 1 | DECOMP_INLINE, 0x585A}, {0xFA12, 0, 1 | DECOMP_INLINE, 0x6674}, {0xFA15, 0, 1 | DECOMP_INLINE, 0x51DE}, {0xFA16, 0, 1 | DECOMP_INLINE, 0x732A}, {0xFA17, 0, 1 | DECOMP_INLINE, 0x76CA}, {0xFA18, 0, 1 | DECOMP_INLINE, 0x793C}, {0xFA19, 0, 1 | DECOMP_INLINE, 0x795E}, {0xFA1A, 0, 1 | DECOMP_INLINE, 0x7965}, {0xFA1B, 0, 1 | DECOMP_INLINE, 0x798F}, {0xFA1C, 0, 1 | DECOMP_INLINE, 0x9756}, {0xFA1D, 0, 1 | DECOMP_INLINE, 0x7CBE}, {0xFA1E, 0, 1 | DECOMP_INLINE, 0x7FBD}, {0xFA20, 0, 1 | DECOMP_INLINE, 0x8612}, {0xFA22, 0, 1 | DECOMP_INLINE, 0x8AF8}, {0xFA25, 0, 1 | DECOMP_INLINE, 0x9038}, {0xFA26, 0, 1 | DECOMP_INLINE, 0x90FD}, {0xFA2A, 0, 1 | DECOMP_INLINE, 0x98EF}, {0xFA2B, 0, 1 | DECOMP_INLINE, 0x98FC}, {0xFA2C, 0, 1 | DECOMP_INLINE, 0x9928}, {0xFA2D, 0, 1 | DECOMP_INLINE, 0x9DB4}, {0xFA2E, 0, 1 | DECOMP_INLINE, 0x90DE}, {0xFA2F, 0, 1 | DECOMP_INLINE, 0x96B7}, {0xFA30, 0, 1 | DECOMP_INLINE, 0x4FAE}, {0xFA31, 0, 1 | DECOMP_INLINE, 0x50E7}, {0xFA32, 0, 1 | DECOMP_INLINE, 0x514D}, {0xFA33, 0, 1 | DECOMP_INLINE, 0x52C9}, {0xFA34, 0, 1 | DECOMP_INLINE, 0x52E4}, {0xFA35, 0, 1 | DECOMP_INLINE, 0x5351}, {0xFA36, 0, 1 | DECOMP_INLINE, 0x559D}, {0xFA37, 0, 1 | DECOMP_INLINE, 0x5606}, {0xFA38, 0, 1 | DECOMP_INLINE, 0x5668}, {0xFA39, 0, 1 | DECOMP_INLINE, 0x5840}, {0xFA3A, 0, 1 | DECOMP_INLINE, 0x58A8}, {0xFA3B, 0, 1 | DECOMP_INLINE, 0x5C64}, {0xFA3C, 0, 1 | DECOMP_INLINE, 0x5C6E}, {0xFA3D, 0, 1 | DECOMP_INLINE, 0x6094}, {0xFA3E, 0, 1 | DECOMP_INLINE, 0x6168}, {0xFA3F, 0, 1 | DECOMP_INLINE, 0x618E}, {0xFA40, 0, 1 | DECOMP_INLINE, 0x61F2}, {0xFA41, 0, 1 | DECOMP_INLINE, 0x654F}, {0xFA42, 0, 1 | DECOMP_INLINE, 0x65E2}, {0xFA43, 0, 1 | DECOMP_INLINE, 0x6691}, {0xFA44, 0, 1 | DECOMP_INLINE, 0x6885}, {0xFA45, 0, 1 | DECOMP_INLINE, 0x6D77}, {0xFA46, 0, 1 | DECOMP_INLINE, 0x6E1A}, {0xFA47, 0, 1 | DECOMP_INLINE, 0x6F22}, {0xFA48, 0, 1 | DECOMP_INLINE, 0x716E}, {0xFA49, 0, 1 | DECOMP_INLINE, 0x722B}, {0xFA4A, 0, 1 | DECOMP_INLINE, 0x7422}, {0xFA4B, 0, 1 | DECOMP_INLINE, 0x7891}, {0xFA4C, 0, 1 | DECOMP_INLINE, 0x793E}, {0xFA4D, 0, 1 | DECOMP_INLINE, 0x7949}, {0xFA4E, 0, 1 | DECOMP_INLINE, 0x7948}, {0xFA4F, 0, 1 | DECOMP_INLINE, 0x7950}, {0xFA50, 0, 1 | DECOMP_INLINE, 0x7956}, {0xFA51, 0, 1 | DECOMP_INLINE, 0x795D}, {0xFA52, 0, 1 | DECOMP_INLINE, 0x798D}, {0xFA53, 0, 1 | DECOMP_INLINE, 0x798E}, {0xFA54, 0, 1 | DECOMP_INLINE, 0x7A40}, {0xFA55, 0, 1 | DECOMP_INLINE, 0x7A81}, {0xFA56, 0, 1 | DECOMP_INLINE, 0x7BC0}, {0xFA57, 0, 1 | DECOMP_INLINE, 0x7DF4}, {0xFA58, 0, 1 | DECOMP_INLINE, 0x7E09}, {0xFA59, 0, 1 | DECOMP_INLINE, 0x7E41}, {0xFA5A, 0, 1 | DECOMP_INLINE, 0x7F72}, {0xFA5B, 0, 1 | DECOMP_INLINE, 0x8005}, {0xFA5C, 0, 1 | DECOMP_INLINE, 0x81ED}, {0xFA5D, 0, 1 | DECOMP_INLINE, 0x8279}, {0xFA5E, 0, 1 | DECOMP_INLINE, 0x8279}, {0xFA5F, 0, 1 | DECOMP_INLINE, 0x8457}, {0xFA60, 0, 1 | DECOMP_INLINE, 0x8910}, {0xFA61, 0, 1 | DECOMP_INLINE, 0x8996}, {0xFA62, 0, 1 | DECOMP_INLINE, 0x8B01}, {0xFA63, 0, 1 | DECOMP_INLINE, 0x8B39}, {0xFA64, 0, 1 | DECOMP_INLINE, 0x8CD3}, {0xFA65, 0, 1 | DECOMP_INLINE, 0x8D08}, {0xFA66, 0, 1 | DECOMP_INLINE, 0x8FB6}, {0xFA67, 0, 1 | DECOMP_INLINE, 0x9038}, {0xFA68, 0, 1 | DECOMP_INLINE, 0x96E3}, {0xFA69, 0, 1 | DECOMP_INLINE, 0x97FF}, {0xFA6A, 0, 1 | DECOMP_INLINE, 0x983B}, {0xFA6B, 0, 1 | DECOMP_INLINE, 0x6075}, {0xFA6C, 0, 1, 3527}, {0xFA6D, 0, 1 | DECOMP_INLINE, 0x8218}, {0xFA70, 0, 1 | DECOMP_INLINE, 0x4E26}, {0xFA71, 0, 1 | DECOMP_INLINE, 0x51B5}, {0xFA72, 0, 1 | DECOMP_INLINE, 0x5168}, {0xFA73, 0, 1 | DECOMP_INLINE, 0x4F80}, {0xFA74, 0, 1 | DECOMP_INLINE, 0x5145}, {0xFA75, 0, 1 | DECOMP_INLINE, 0x5180}, {0xFA76, 0, 1 | DECOMP_INLINE, 0x52C7}, {0xFA77, 0, 1 | DECOMP_INLINE, 0x52FA}, {0xFA78, 0, 1 | DECOMP_INLINE, 0x559D}, {0xFA79, 0, 1 | DECOMP_INLINE, 0x5555}, {0xFA7A, 0, 1 | DECOMP_INLINE, 0x5599}, {0xFA7B, 0, 1 | DECOMP_INLINE, 0x55E2}, {0xFA7C, 0, 1 | DECOMP_INLINE, 0x585A}, {0xFA7D, 0, 1 | DECOMP_INLINE, 0x58B3}, {0xFA7E, 0, 1 | DECOMP_INLINE, 0x5944}, {0xFA7F, 0, 1 | DECOMP_INLINE, 0x5954}, {0xFA80, 0, 1 | DECOMP_INLINE, 0x5A62}, {0xFA81, 0, 1 | DECOMP_INLINE, 0x5B28}, {0xFA82, 0, 1 | DECOMP_INLINE, 0x5ED2}, {0xFA83, 0, 1 | DECOMP_INLINE, 0x5ED9}, {0xFA84, 0, 1 | DECOMP_INLINE, 0x5F69}, {0xFA85, 0, 1 | DECOMP_INLINE, 0x5FAD}, {0xFA86, 0, 1 | DECOMP_INLINE, 0x60D8}, {0xFA87, 0, 1 | DECOMP_INLINE, 0x614E}, {0xFA88, 0, 1 | DECOMP_INLINE, 0x6108}, {0xFA89, 0, 1 | DECOMP_INLINE, 0x618E}, {0xFA8A, 0, 1 | DECOMP_INLINE, 0x6160}, {0xFA8B, 0, 1 | DECOMP_INLINE, 0x61F2}, {0xFA8C, 0, 1 | DECOMP_INLINE, 0x6234}, {0xFA8D, 0, 1 | DECOMP_INLINE, 0x63C4}, {0xFA8E, 0, 1 | DECOMP_INLINE, 0x641C}, {0xFA8F, 0, 1 | DECOMP_INLINE, 0x6452}, {0xFA90, 0, 1 | DECOMP_INLINE, 0x6556}, {0xFA91, 0, 1 | DECOMP_INLINE, 0x6674}, {0xFA92, 0, 1 | DECOMP_INLINE, 0x6717}, {0xFA93, 0, 1 | DECOMP_INLINE, 0x671B}, {0xFA94, 0, 1 | DECOMP_INLINE, 0x6756}, {0xFA95, 0, 1 | DECOMP_INLINE, 0x6B79}, {0xFA96, 0, 1 | DECOMP_INLINE, 0x6BBA}, {0xFA97, 0, 1 | DECOMP_INLINE, 0x6D41}, {0xFA98, 0, 1 | DECOMP_INLINE, 0x6EDB}, {0xFA99, 0, 1 | DECOMP_INLINE, 0x6ECB}, {0xFA9A, 0, 1 | DECOMP_INLINE, 0x6F22}, {0xFA9B, 0, 1 | DECOMP_INLINE, 0x701E}, {0xFA9C, 0, 1 | DECOMP_INLINE, 0x716E}, {0xFA9D, 0, 1 | DECOMP_INLINE, 0x77A7}, {0xFA9E, 0, 1 | DECOMP_INLINE, 0x7235}, {0xFA9F, 0, 1 | DECOMP_INLINE, 0x72AF}, {0xFAA0, 0, 1 | DECOMP_INLINE, 0x732A}, {0xFAA1, 0, 1 | DECOMP_INLINE, 0x7471}, {0xFAA2, 0, 1 | DECOMP_INLINE, 0x7506}, {0xFAA3, 0, 1 | DECOMP_INLINE, 0x753B}, {0xFAA4, 0, 1 | DECOMP_INLINE, 0x761D}, {0xFAA5, 0, 1 | DECOMP_INLINE, 0x761F}, {0xFAA6, 0, 1 | DECOMP_INLINE, 0x76CA}, {0xFAA7, 0, 1 | DECOMP_INLINE, 0x76DB}, {0xFAA8, 0, 1 | DECOMP_INLINE, 0x76F4}, {0xFAA9, 0, 1 | DECOMP_INLINE, 0x774A}, {0xFAAA, 0, 1 | DECOMP_INLINE, 0x7740}, {0xFAAB, 0, 1 | DECOMP_INLINE, 0x78CC}, {0xFAAC, 0, 1 | DECOMP_INLINE, 0x7AB1}, {0xFAAD, 0, 1 | DECOMP_INLINE, 0x7BC0}, {0xFAAE, 0, 1 | DECOMP_INLINE, 0x7C7B}, {0xFAAF, 0, 1 | DECOMP_INLINE, 0x7D5B}, {0xFAB0, 0, 1 | DECOMP_INLINE, 0x7DF4}, {0xFAB1, 0, 1 | DECOMP_INLINE, 0x7F3E}, {0xFAB2, 0, 1 | DECOMP_INLINE, 0x8005}, {0xFAB3, 0, 1 | DECOMP_INLINE, 0x8352}, {0xFAB4, 0, 1 | DECOMP_INLINE, 0x83EF}, {0xFAB5, 0, 1 | DECOMP_INLINE, 0x8779}, {0xFAB6, 0, 1 | DECOMP_INLINE, 0x8941}, {0xFAB7, 0, 1 | DECOMP_INLINE, 0x8986}, {0xFAB8, 0, 1 | DECOMP_INLINE, 0x8996}, {0xFAB9, 0, 1 | DECOMP_INLINE, 0x8ABF}, {0xFABA, 0, 1 | DECOMP_INLINE, 0x8AF8}, {0xFABB, 0, 1 | DECOMP_INLINE, 0x8ACB}, {0xFABC, 0, 1 | DECOMP_INLINE, 0x8B01}, {0xFABD, 0, 1 | DECOMP_INLINE, 0x8AFE}, {0xFABE, 0, 1 | DECOMP_INLINE, 0x8AED}, {0xFABF, 0, 1 | DECOMP_INLINE, 0x8B39}, {0xFAC0, 0, 1 | DECOMP_INLINE, 0x8B8A}, {0xFAC1, 0, 1 | DECOMP_INLINE, 0x8D08}, {0xFAC2, 0, 1 | DECOMP_INLINE, 0x8F38}, {0xFAC3, 0, 1 | DECOMP_INLINE, 0x9072}, {0xFAC4, 0, 1 | DECOMP_INLINE, 0x9199}, {0xFAC5, 0, 1 | DECOMP_INLINE, 0x9276}, {0xFAC6, 0, 1 | DECOMP_INLINE, 0x967C}, {0xFAC7, 0, 1 | DECOMP_INLINE, 0x96E3}, {0xFAC8, 0, 1 | DECOMP_INLINE, 0x9756}, {0xFAC9, 0, 1 | DECOMP_INLINE, 0x97DB}, {0xFACA, 0, 1 | DECOMP_INLINE, 0x97FF}, {0xFACB, 0, 1 | DECOMP_INLINE, 0x980B}, {0xFACC, 0, 1 | DECOMP_INLINE, 0x983B}, {0xFACD, 0, 1 | DECOMP_INLINE, 0x9B12}, {0xFACE, 0, 1 | DECOMP_INLINE, 0x9F9C}, {0xFACF, 0, 1, 3528}, {0xFAD0, 0, 1, 3529}, {0xFAD1, 0, 1, 3530}, {0xFAD2, 0, 1 | DECOMP_INLINE, 0x3B9D}, {0xFAD3, 0, 1 | DECOMP_INLINE, 0x4018}, {0xFAD4, 0, 1 | DECOMP_INLINE, 0x4039}, {0xFAD5, 0, 1, 3531}, {0xFAD6, 0, 1, 3532}, {0xFAD7, 0, 1, 3533}, {0xFAD8, 0, 1 | DECOMP_INLINE, 0x9F43}, {0xFAD9, 0, 1 | DECOMP_INLINE, 0x9F8E}, {0xFB00, 0, 2 | DECOMP_COMPAT, 3534}, {0xFB01, 0, 2 | DECOMP_COMPAT, 3536}, {0xFB02, 0, 2 | DECOMP_COMPAT, 3538}, {0xFB03, 0, 3 | DECOMP_COMPAT, 3540}, {0xFB04, 0, 3 | DECOMP_COMPAT, 3543}, {0xFB05, 0, 2 | DECOMP_COMPAT, 3546}, {0xFB06, 0, 2 | DECOMP_COMPAT, 3548}, {0xFB13, 0, 2 | DECOMP_COMPAT, 3550}, {0xFB14, 0, 2 | DECOMP_COMPAT, 3552}, {0xFB15, 0, 2 | DECOMP_COMPAT, 3554}, {0xFB16, 0, 2 | DECOMP_COMPAT, 3556}, {0xFB17, 0, 2 | DECOMP_COMPAT, 3558}, {0xFB1D, 0, 2 | DECOMP_NO_COMPOSE, 3560}, /* in exclusion list */ {0xFB1E, 26, 0, 0}, {0xFB1F, 0, 2 | DECOMP_NO_COMPOSE, 3562}, /* in exclusion list */ {0xFB20, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05E2}, {0xFB21, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D0}, {0xFB22, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D3}, {0xFB23, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05D4}, {0xFB24, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05DB}, {0xFB25, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05DC}, {0xFB26, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05DD}, {0xFB27, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05E8}, {0xFB28, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x05EA}, {0xFB29, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002B}, {0xFB2A, 0, 2 | DECOMP_NO_COMPOSE, 3564}, /* in exclusion list */ {0xFB2B, 0, 2 | DECOMP_NO_COMPOSE, 3566}, /* in exclusion list */ {0xFB2C, 0, 2 | DECOMP_NO_COMPOSE, 3568}, /* in exclusion list */ {0xFB2D, 0, 2 | DECOMP_NO_COMPOSE, 3570}, /* in exclusion list */ {0xFB2E, 0, 2 | DECOMP_NO_COMPOSE, 3572}, /* in exclusion list */ {0xFB2F, 0, 2 | DECOMP_NO_COMPOSE, 3574}, /* in exclusion list */ {0xFB30, 0, 2 | DECOMP_NO_COMPOSE, 3576}, /* in exclusion list */ {0xFB31, 0, 2 | DECOMP_NO_COMPOSE, 3578}, /* in exclusion list */ {0xFB32, 0, 2 | DECOMP_NO_COMPOSE, 3580}, /* in exclusion list */ {0xFB33, 0, 2 | DECOMP_NO_COMPOSE, 3582}, /* in exclusion list */ {0xFB34, 0, 2 | DECOMP_NO_COMPOSE, 3584}, /* in exclusion list */ {0xFB35, 0, 2 | DECOMP_NO_COMPOSE, 3586}, /* in exclusion list */ {0xFB36, 0, 2 | DECOMP_NO_COMPOSE, 3588}, /* in exclusion list */ {0xFB38, 0, 2 | DECOMP_NO_COMPOSE, 3590}, /* in exclusion list */ {0xFB39, 0, 2 | DECOMP_NO_COMPOSE, 3592}, /* in exclusion list */ {0xFB3A, 0, 2 | DECOMP_NO_COMPOSE, 3594}, /* in exclusion list */ {0xFB3B, 0, 2 | DECOMP_NO_COMPOSE, 3596}, /* in exclusion list */ {0xFB3C, 0, 2 | DECOMP_NO_COMPOSE, 3598}, /* in exclusion list */ {0xFB3E, 0, 2 | DECOMP_NO_COMPOSE, 3600}, /* in exclusion list */ {0xFB40, 0, 2 | DECOMP_NO_COMPOSE, 3602}, /* in exclusion list */ {0xFB41, 0, 2 | DECOMP_NO_COMPOSE, 3604}, /* in exclusion list */ {0xFB43, 0, 2 | DECOMP_NO_COMPOSE, 3606}, /* in exclusion list */ {0xFB44, 0, 2 | DECOMP_NO_COMPOSE, 3608}, /* in exclusion list */ {0xFB46, 0, 2 | DECOMP_NO_COMPOSE, 3610}, /* in exclusion list */ {0xFB47, 0, 2 | DECOMP_NO_COMPOSE, 3612}, /* in exclusion list */ {0xFB48, 0, 2 | DECOMP_NO_COMPOSE, 3614}, /* in exclusion list */ {0xFB49, 0, 2 | DECOMP_NO_COMPOSE, 3616}, /* in exclusion list */ {0xFB4A, 0, 2 | DECOMP_NO_COMPOSE, 3618}, /* in exclusion list */ {0xFB4B, 0, 2 | DECOMP_NO_COMPOSE, 3620}, /* in exclusion list */ {0xFB4C, 0, 2 | DECOMP_NO_COMPOSE, 3622}, /* in exclusion list */ {0xFB4D, 0, 2 | DECOMP_NO_COMPOSE, 3624}, /* in exclusion list */ {0xFB4E, 0, 2 | DECOMP_NO_COMPOSE, 3626}, /* in exclusion list */ {0xFB4F, 0, 2 | DECOMP_COMPAT, 3628}, {0xFB50, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0671}, {0xFB51, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0671}, {0xFB52, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067B}, {0xFB53, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067B}, {0xFB54, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067B}, {0xFB55, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067B}, {0xFB56, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067E}, {0xFB57, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067E}, {0xFB58, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067E}, {0xFB59, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067E}, {0xFB5A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0680}, {0xFB5B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0680}, {0xFB5C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0680}, {0xFB5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0680}, {0xFB5E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067A}, {0xFB5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067A}, {0xFB60, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067A}, {0xFB61, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067A}, {0xFB62, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067F}, {0xFB63, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067F}, {0xFB64, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067F}, {0xFB65, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x067F}, {0xFB66, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0679}, {0xFB67, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0679}, {0xFB68, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0679}, {0xFB69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0679}, {0xFB6A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A4}, {0xFB6B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A4}, {0xFB6C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A4}, {0xFB6D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A4}, {0xFB6E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A6}, {0xFB6F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A6}, {0xFB70, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A6}, {0xFB71, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A6}, {0xFB72, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0684}, {0xFB73, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0684}, {0xFB74, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0684}, {0xFB75, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0684}, {0xFB76, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0683}, {0xFB77, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0683}, {0xFB78, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0683}, {0xFB79, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0683}, {0xFB7A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0686}, {0xFB7B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0686}, {0xFB7C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0686}, {0xFB7D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0686}, {0xFB7E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0687}, {0xFB7F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0687}, {0xFB80, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0687}, {0xFB81, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0687}, {0xFB82, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x068D}, {0xFB83, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x068D}, {0xFB84, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x068C}, {0xFB85, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x068C}, {0xFB86, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x068E}, {0xFB87, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x068E}, {0xFB88, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0688}, {0xFB89, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0688}, {0xFB8A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0698}, {0xFB8B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0698}, {0xFB8C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0691}, {0xFB8D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0691}, {0xFB8E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A9}, {0xFB8F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A9}, {0xFB90, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A9}, {0xFB91, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A9}, {0xFB92, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AF}, {0xFB93, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AF}, {0xFB94, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AF}, {0xFB95, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AF}, {0xFB96, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B3}, {0xFB97, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B3}, {0xFB98, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B3}, {0xFB99, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B3}, {0xFB9A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B1}, {0xFB9B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B1}, {0xFB9C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B1}, {0xFB9D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06B1}, {0xFB9E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BA}, {0xFB9F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BA}, {0xFBA0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BB}, {0xFBA1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BB}, {0xFBA2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BB}, {0xFBA3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BB}, {0xFBA4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C0}, {0xFBA5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C0}, {0xFBA6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C1}, {0xFBA7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C1}, {0xFBA8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C1}, {0xFBA9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C1}, {0xFBAA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BE}, {0xFBAB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BE}, {0xFBAC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BE}, {0xFBAD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BE}, {0xFBAE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D2}, {0xFBAF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D2}, {0xFBB0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D3}, {0xFBB1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D3}, {0xFBD3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AD}, {0xFBD4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AD}, {0xFBD5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AD}, {0xFBD6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06AD}, {0xFBD7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C7}, {0xFBD8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C7}, {0xFBD9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C6}, {0xFBDA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C6}, {0xFBDB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C8}, {0xFBDC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C8}, {0xFBDD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0677}, {0xFBDE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06CB}, {0xFBDF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06CB}, {0xFBE0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C5}, {0xFBE1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C5}, {0xFBE2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C9}, {0xFBE3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06C9}, {0xFBE4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D0}, {0xFBE5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D0}, {0xFBE6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D0}, {0xFBE7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06D0}, {0xFBE8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0649}, {0xFBE9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0649}, {0xFBEA, 0, 2 | DECOMP_COMPAT, 3630}, {0xFBEB, 0, 2 | DECOMP_COMPAT, 3632}, {0xFBEC, 0, 2 | DECOMP_COMPAT, 3634}, {0xFBED, 0, 2 | DECOMP_COMPAT, 3636}, {0xFBEE, 0, 2 | DECOMP_COMPAT, 3638}, {0xFBEF, 0, 2 | DECOMP_COMPAT, 3640}, {0xFBF0, 0, 2 | DECOMP_COMPAT, 3642}, {0xFBF1, 0, 2 | DECOMP_COMPAT, 3644}, {0xFBF2, 0, 2 | DECOMP_COMPAT, 3646}, {0xFBF3, 0, 2 | DECOMP_COMPAT, 3648}, {0xFBF4, 0, 2 | DECOMP_COMPAT, 3650}, {0xFBF5, 0, 2 | DECOMP_COMPAT, 3652}, {0xFBF6, 0, 2 | DECOMP_COMPAT, 3654}, {0xFBF7, 0, 2 | DECOMP_COMPAT, 3656}, {0xFBF8, 0, 2 | DECOMP_COMPAT, 3658}, {0xFBF9, 0, 2 | DECOMP_COMPAT, 3660}, {0xFBFA, 0, 2 | DECOMP_COMPAT, 3662}, {0xFBFB, 0, 2 | DECOMP_COMPAT, 3664}, {0xFBFC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06CC}, {0xFBFD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06CC}, {0xFBFE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06CC}, {0xFBFF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06CC}, {0xFC00, 0, 2 | DECOMP_COMPAT, 3666}, {0xFC01, 0, 2 | DECOMP_COMPAT, 3668}, {0xFC02, 0, 2 | DECOMP_COMPAT, 3670}, {0xFC03, 0, 2 | DECOMP_COMPAT, 3672}, {0xFC04, 0, 2 | DECOMP_COMPAT, 3674}, {0xFC05, 0, 2 | DECOMP_COMPAT, 3676}, {0xFC06, 0, 2 | DECOMP_COMPAT, 3678}, {0xFC07, 0, 2 | DECOMP_COMPAT, 3680}, {0xFC08, 0, 2 | DECOMP_COMPAT, 3682}, {0xFC09, 0, 2 | DECOMP_COMPAT, 3684}, {0xFC0A, 0, 2 | DECOMP_COMPAT, 3686}, {0xFC0B, 0, 2 | DECOMP_COMPAT, 3688}, {0xFC0C, 0, 2 | DECOMP_COMPAT, 3690}, {0xFC0D, 0, 2 | DECOMP_COMPAT, 3692}, {0xFC0E, 0, 2 | DECOMP_COMPAT, 3694}, {0xFC0F, 0, 2 | DECOMP_COMPAT, 3696}, {0xFC10, 0, 2 | DECOMP_COMPAT, 3698}, {0xFC11, 0, 2 | DECOMP_COMPAT, 3700}, {0xFC12, 0, 2 | DECOMP_COMPAT, 3702}, {0xFC13, 0, 2 | DECOMP_COMPAT, 3704}, {0xFC14, 0, 2 | DECOMP_COMPAT, 3706}, {0xFC15, 0, 2 | DECOMP_COMPAT, 3708}, {0xFC16, 0, 2 | DECOMP_COMPAT, 3710}, {0xFC17, 0, 2 | DECOMP_COMPAT, 3712}, {0xFC18, 0, 2 | DECOMP_COMPAT, 3714}, {0xFC19, 0, 2 | DECOMP_COMPAT, 3716}, {0xFC1A, 0, 2 | DECOMP_COMPAT, 3718}, {0xFC1B, 0, 2 | DECOMP_COMPAT, 3720}, {0xFC1C, 0, 2 | DECOMP_COMPAT, 3722}, {0xFC1D, 0, 2 | DECOMP_COMPAT, 3724}, {0xFC1E, 0, 2 | DECOMP_COMPAT, 3726}, {0xFC1F, 0, 2 | DECOMP_COMPAT, 3728}, {0xFC20, 0, 2 | DECOMP_COMPAT, 3730}, {0xFC21, 0, 2 | DECOMP_COMPAT, 3732}, {0xFC22, 0, 2 | DECOMP_COMPAT, 3734}, {0xFC23, 0, 2 | DECOMP_COMPAT, 3736}, {0xFC24, 0, 2 | DECOMP_COMPAT, 3738}, {0xFC25, 0, 2 | DECOMP_COMPAT, 3740}, {0xFC26, 0, 2 | DECOMP_COMPAT, 3742}, {0xFC27, 0, 2 | DECOMP_COMPAT, 3744}, {0xFC28, 0, 2 | DECOMP_COMPAT, 3746}, {0xFC29, 0, 2 | DECOMP_COMPAT, 3748}, {0xFC2A, 0, 2 | DECOMP_COMPAT, 3750}, {0xFC2B, 0, 2 | DECOMP_COMPAT, 3752}, {0xFC2C, 0, 2 | DECOMP_COMPAT, 3754}, {0xFC2D, 0, 2 | DECOMP_COMPAT, 3756}, {0xFC2E, 0, 2 | DECOMP_COMPAT, 3758}, {0xFC2F, 0, 2 | DECOMP_COMPAT, 3760}, {0xFC30, 0, 2 | DECOMP_COMPAT, 3762}, {0xFC31, 0, 2 | DECOMP_COMPAT, 3764}, {0xFC32, 0, 2 | DECOMP_COMPAT, 3766}, {0xFC33, 0, 2 | DECOMP_COMPAT, 3768}, {0xFC34, 0, 2 | DECOMP_COMPAT, 3770}, {0xFC35, 0, 2 | DECOMP_COMPAT, 3772}, {0xFC36, 0, 2 | DECOMP_COMPAT, 3774}, {0xFC37, 0, 2 | DECOMP_COMPAT, 3776}, {0xFC38, 0, 2 | DECOMP_COMPAT, 3778}, {0xFC39, 0, 2 | DECOMP_COMPAT, 3780}, {0xFC3A, 0, 2 | DECOMP_COMPAT, 3782}, {0xFC3B, 0, 2 | DECOMP_COMPAT, 3784}, {0xFC3C, 0, 2 | DECOMP_COMPAT, 3786}, {0xFC3D, 0, 2 | DECOMP_COMPAT, 3788}, {0xFC3E, 0, 2 | DECOMP_COMPAT, 3790}, {0xFC3F, 0, 2 | DECOMP_COMPAT, 3792}, {0xFC40, 0, 2 | DECOMP_COMPAT, 3794}, {0xFC41, 0, 2 | DECOMP_COMPAT, 3796}, {0xFC42, 0, 2 | DECOMP_COMPAT, 3798}, {0xFC43, 0, 2 | DECOMP_COMPAT, 3800}, {0xFC44, 0, 2 | DECOMP_COMPAT, 3802}, {0xFC45, 0, 2 | DECOMP_COMPAT, 3804}, {0xFC46, 0, 2 | DECOMP_COMPAT, 3806}, {0xFC47, 0, 2 | DECOMP_COMPAT, 3808}, {0xFC48, 0, 2 | DECOMP_COMPAT, 3810}, {0xFC49, 0, 2 | DECOMP_COMPAT, 3812}, {0xFC4A, 0, 2 | DECOMP_COMPAT, 3814}, {0xFC4B, 0, 2 | DECOMP_COMPAT, 3816}, {0xFC4C, 0, 2 | DECOMP_COMPAT, 3818}, {0xFC4D, 0, 2 | DECOMP_COMPAT, 3820}, {0xFC4E, 0, 2 | DECOMP_COMPAT, 3822}, {0xFC4F, 0, 2 | DECOMP_COMPAT, 3824}, {0xFC50, 0, 2 | DECOMP_COMPAT, 3826}, {0xFC51, 0, 2 | DECOMP_COMPAT, 3828}, {0xFC52, 0, 2 | DECOMP_COMPAT, 3830}, {0xFC53, 0, 2 | DECOMP_COMPAT, 3832}, {0xFC54, 0, 2 | DECOMP_COMPAT, 3834}, {0xFC55, 0, 2 | DECOMP_COMPAT, 3836}, {0xFC56, 0, 2 | DECOMP_COMPAT, 3838}, {0xFC57, 0, 2 | DECOMP_COMPAT, 3840}, {0xFC58, 0, 2 | DECOMP_COMPAT, 3842}, {0xFC59, 0, 2 | DECOMP_COMPAT, 3844}, {0xFC5A, 0, 2 | DECOMP_COMPAT, 3846}, {0xFC5B, 0, 2 | DECOMP_COMPAT, 3848}, {0xFC5C, 0, 2 | DECOMP_COMPAT, 3850}, {0xFC5D, 0, 2 | DECOMP_COMPAT, 3852}, {0xFC5E, 0, 3 | DECOMP_COMPAT, 3854}, {0xFC5F, 0, 3 | DECOMP_COMPAT, 3857}, {0xFC60, 0, 3 | DECOMP_COMPAT, 3860}, {0xFC61, 0, 3 | DECOMP_COMPAT, 3863}, {0xFC62, 0, 3 | DECOMP_COMPAT, 3866}, {0xFC63, 0, 3 | DECOMP_COMPAT, 3869}, {0xFC64, 0, 2 | DECOMP_COMPAT, 3872}, {0xFC65, 0, 2 | DECOMP_COMPAT, 3874}, {0xFC66, 0, 2 | DECOMP_COMPAT, 3876}, {0xFC67, 0, 2 | DECOMP_COMPAT, 3878}, {0xFC68, 0, 2 | DECOMP_COMPAT, 3880}, {0xFC69, 0, 2 | DECOMP_COMPAT, 3882}, {0xFC6A, 0, 2 | DECOMP_COMPAT, 3884}, {0xFC6B, 0, 2 | DECOMP_COMPAT, 3886}, {0xFC6C, 0, 2 | DECOMP_COMPAT, 3888}, {0xFC6D, 0, 2 | DECOMP_COMPAT, 3890}, {0xFC6E, 0, 2 | DECOMP_COMPAT, 3892}, {0xFC6F, 0, 2 | DECOMP_COMPAT, 3894}, {0xFC70, 0, 2 | DECOMP_COMPAT, 3896}, {0xFC71, 0, 2 | DECOMP_COMPAT, 3898}, {0xFC72, 0, 2 | DECOMP_COMPAT, 3900}, {0xFC73, 0, 2 | DECOMP_COMPAT, 3902}, {0xFC74, 0, 2 | DECOMP_COMPAT, 3904}, {0xFC75, 0, 2 | DECOMP_COMPAT, 3906}, {0xFC76, 0, 2 | DECOMP_COMPAT, 3908}, {0xFC77, 0, 2 | DECOMP_COMPAT, 3910}, {0xFC78, 0, 2 | DECOMP_COMPAT, 3912}, {0xFC79, 0, 2 | DECOMP_COMPAT, 3914}, {0xFC7A, 0, 2 | DECOMP_COMPAT, 3916}, {0xFC7B, 0, 2 | DECOMP_COMPAT, 3918}, {0xFC7C, 0, 2 | DECOMP_COMPAT, 3920}, {0xFC7D, 0, 2 | DECOMP_COMPAT, 3922}, {0xFC7E, 0, 2 | DECOMP_COMPAT, 3924}, {0xFC7F, 0, 2 | DECOMP_COMPAT, 3926}, {0xFC80, 0, 2 | DECOMP_COMPAT, 3928}, {0xFC81, 0, 2 | DECOMP_COMPAT, 3930}, {0xFC82, 0, 2 | DECOMP_COMPAT, 3932}, {0xFC83, 0, 2 | DECOMP_COMPAT, 3934}, {0xFC84, 0, 2 | DECOMP_COMPAT, 3936}, {0xFC85, 0, 2 | DECOMP_COMPAT, 3938}, {0xFC86, 0, 2 | DECOMP_COMPAT, 3940}, {0xFC87, 0, 2 | DECOMP_COMPAT, 3942}, {0xFC88, 0, 2 | DECOMP_COMPAT, 3944}, {0xFC89, 0, 2 | DECOMP_COMPAT, 3946}, {0xFC8A, 0, 2 | DECOMP_COMPAT, 3948}, {0xFC8B, 0, 2 | DECOMP_COMPAT, 3950}, {0xFC8C, 0, 2 | DECOMP_COMPAT, 3952}, {0xFC8D, 0, 2 | DECOMP_COMPAT, 3954}, {0xFC8E, 0, 2 | DECOMP_COMPAT, 3956}, {0xFC8F, 0, 2 | DECOMP_COMPAT, 3958}, {0xFC90, 0, 2 | DECOMP_COMPAT, 3960}, {0xFC91, 0, 2 | DECOMP_COMPAT, 3962}, {0xFC92, 0, 2 | DECOMP_COMPAT, 3964}, {0xFC93, 0, 2 | DECOMP_COMPAT, 3966}, {0xFC94, 0, 2 | DECOMP_COMPAT, 3968}, {0xFC95, 0, 2 | DECOMP_COMPAT, 3970}, {0xFC96, 0, 2 | DECOMP_COMPAT, 3972}, {0xFC97, 0, 2 | DECOMP_COMPAT, 3974}, {0xFC98, 0, 2 | DECOMP_COMPAT, 3976}, {0xFC99, 0, 2 | DECOMP_COMPAT, 3978}, {0xFC9A, 0, 2 | DECOMP_COMPAT, 3980}, {0xFC9B, 0, 2 | DECOMP_COMPAT, 3982}, {0xFC9C, 0, 2 | DECOMP_COMPAT, 3984}, {0xFC9D, 0, 2 | DECOMP_COMPAT, 3986}, {0xFC9E, 0, 2 | DECOMP_COMPAT, 3988}, {0xFC9F, 0, 2 | DECOMP_COMPAT, 3990}, {0xFCA0, 0, 2 | DECOMP_COMPAT, 3992}, {0xFCA1, 0, 2 | DECOMP_COMPAT, 3994}, {0xFCA2, 0, 2 | DECOMP_COMPAT, 3996}, {0xFCA3, 0, 2 | DECOMP_COMPAT, 3998}, {0xFCA4, 0, 2 | DECOMP_COMPAT, 4000}, {0xFCA5, 0, 2 | DECOMP_COMPAT, 4002}, {0xFCA6, 0, 2 | DECOMP_COMPAT, 4004}, {0xFCA7, 0, 2 | DECOMP_COMPAT, 4006}, {0xFCA8, 0, 2 | DECOMP_COMPAT, 4008}, {0xFCA9, 0, 2 | DECOMP_COMPAT, 4010}, {0xFCAA, 0, 2 | DECOMP_COMPAT, 4012}, {0xFCAB, 0, 2 | DECOMP_COMPAT, 4014}, {0xFCAC, 0, 2 | DECOMP_COMPAT, 4016}, {0xFCAD, 0, 2 | DECOMP_COMPAT, 4018}, {0xFCAE, 0, 2 | DECOMP_COMPAT, 4020}, {0xFCAF, 0, 2 | DECOMP_COMPAT, 4022}, {0xFCB0, 0, 2 | DECOMP_COMPAT, 4024}, {0xFCB1, 0, 2 | DECOMP_COMPAT, 4026}, {0xFCB2, 0, 2 | DECOMP_COMPAT, 4028}, {0xFCB3, 0, 2 | DECOMP_COMPAT, 4030}, {0xFCB4, 0, 2 | DECOMP_COMPAT, 4032}, {0xFCB5, 0, 2 | DECOMP_COMPAT, 4034}, {0xFCB6, 0, 2 | DECOMP_COMPAT, 4036}, {0xFCB7, 0, 2 | DECOMP_COMPAT, 4038}, {0xFCB8, 0, 2 | DECOMP_COMPAT, 4040}, {0xFCB9, 0, 2 | DECOMP_COMPAT, 4042}, {0xFCBA, 0, 2 | DECOMP_COMPAT, 4044}, {0xFCBB, 0, 2 | DECOMP_COMPAT, 4046}, {0xFCBC, 0, 2 | DECOMP_COMPAT, 4048}, {0xFCBD, 0, 2 | DECOMP_COMPAT, 4050}, {0xFCBE, 0, 2 | DECOMP_COMPAT, 4052}, {0xFCBF, 0, 2 | DECOMP_COMPAT, 4054}, {0xFCC0, 0, 2 | DECOMP_COMPAT, 4056}, {0xFCC1, 0, 2 | DECOMP_COMPAT, 4058}, {0xFCC2, 0, 2 | DECOMP_COMPAT, 4060}, {0xFCC3, 0, 2 | DECOMP_COMPAT, 4062}, {0xFCC4, 0, 2 | DECOMP_COMPAT, 4064}, {0xFCC5, 0, 2 | DECOMP_COMPAT, 4066}, {0xFCC6, 0, 2 | DECOMP_COMPAT, 4068}, {0xFCC7, 0, 2 | DECOMP_COMPAT, 4070}, {0xFCC8, 0, 2 | DECOMP_COMPAT, 4072}, {0xFCC9, 0, 2 | DECOMP_COMPAT, 4074}, {0xFCCA, 0, 2 | DECOMP_COMPAT, 4076}, {0xFCCB, 0, 2 | DECOMP_COMPAT, 4078}, {0xFCCC, 0, 2 | DECOMP_COMPAT, 4080}, {0xFCCD, 0, 2 | DECOMP_COMPAT, 4082}, {0xFCCE, 0, 2 | DECOMP_COMPAT, 4084}, {0xFCCF, 0, 2 | DECOMP_COMPAT, 4086}, {0xFCD0, 0, 2 | DECOMP_COMPAT, 4088}, {0xFCD1, 0, 2 | DECOMP_COMPAT, 4090}, {0xFCD2, 0, 2 | DECOMP_COMPAT, 4092}, {0xFCD3, 0, 2 | DECOMP_COMPAT, 4094}, {0xFCD4, 0, 2 | DECOMP_COMPAT, 4096}, {0xFCD5, 0, 2 | DECOMP_COMPAT, 4098}, {0xFCD6, 0, 2 | DECOMP_COMPAT, 4100}, {0xFCD7, 0, 2 | DECOMP_COMPAT, 4102}, {0xFCD8, 0, 2 | DECOMP_COMPAT, 4104}, {0xFCD9, 0, 2 | DECOMP_COMPAT, 4106}, {0xFCDA, 0, 2 | DECOMP_COMPAT, 4108}, {0xFCDB, 0, 2 | DECOMP_COMPAT, 4110}, {0xFCDC, 0, 2 | DECOMP_COMPAT, 4112}, {0xFCDD, 0, 2 | DECOMP_COMPAT, 4114}, {0xFCDE, 0, 2 | DECOMP_COMPAT, 4116}, {0xFCDF, 0, 2 | DECOMP_COMPAT, 4118}, {0xFCE0, 0, 2 | DECOMP_COMPAT, 4120}, {0xFCE1, 0, 2 | DECOMP_COMPAT, 4122}, {0xFCE2, 0, 2 | DECOMP_COMPAT, 4124}, {0xFCE3, 0, 2 | DECOMP_COMPAT, 4126}, {0xFCE4, 0, 2 | DECOMP_COMPAT, 4128}, {0xFCE5, 0, 2 | DECOMP_COMPAT, 4130}, {0xFCE6, 0, 2 | DECOMP_COMPAT, 4132}, {0xFCE7, 0, 2 | DECOMP_COMPAT, 4134}, {0xFCE8, 0, 2 | DECOMP_COMPAT, 4136}, {0xFCE9, 0, 2 | DECOMP_COMPAT, 4138}, {0xFCEA, 0, 2 | DECOMP_COMPAT, 4140}, {0xFCEB, 0, 2 | DECOMP_COMPAT, 4142}, {0xFCEC, 0, 2 | DECOMP_COMPAT, 4144}, {0xFCED, 0, 2 | DECOMP_COMPAT, 4146}, {0xFCEE, 0, 2 | DECOMP_COMPAT, 4148}, {0xFCEF, 0, 2 | DECOMP_COMPAT, 4150}, {0xFCF0, 0, 2 | DECOMP_COMPAT, 4152}, {0xFCF1, 0, 2 | DECOMP_COMPAT, 4154}, {0xFCF2, 0, 3 | DECOMP_COMPAT, 4156}, {0xFCF3, 0, 3 | DECOMP_COMPAT, 4159}, {0xFCF4, 0, 3 | DECOMP_COMPAT, 4162}, {0xFCF5, 0, 2 | DECOMP_COMPAT, 4165}, {0xFCF6, 0, 2 | DECOMP_COMPAT, 4167}, {0xFCF7, 0, 2 | DECOMP_COMPAT, 4169}, {0xFCF8, 0, 2 | DECOMP_COMPAT, 4171}, {0xFCF9, 0, 2 | DECOMP_COMPAT, 4173}, {0xFCFA, 0, 2 | DECOMP_COMPAT, 4175}, {0xFCFB, 0, 2 | DECOMP_COMPAT, 4177}, {0xFCFC, 0, 2 | DECOMP_COMPAT, 4179}, {0xFCFD, 0, 2 | DECOMP_COMPAT, 4181}, {0xFCFE, 0, 2 | DECOMP_COMPAT, 4183}, {0xFCFF, 0, 2 | DECOMP_COMPAT, 4185}, {0xFD00, 0, 2 | DECOMP_COMPAT, 4187}, {0xFD01, 0, 2 | DECOMP_COMPAT, 4189}, {0xFD02, 0, 2 | DECOMP_COMPAT, 4191}, {0xFD03, 0, 2 | DECOMP_COMPAT, 4193}, {0xFD04, 0, 2 | DECOMP_COMPAT, 4195}, {0xFD05, 0, 2 | DECOMP_COMPAT, 4197}, {0xFD06, 0, 2 | DECOMP_COMPAT, 4199}, {0xFD07, 0, 2 | DECOMP_COMPAT, 4201}, {0xFD08, 0, 2 | DECOMP_COMPAT, 4203}, {0xFD09, 0, 2 | DECOMP_COMPAT, 4205}, {0xFD0A, 0, 2 | DECOMP_COMPAT, 4207}, {0xFD0B, 0, 2 | DECOMP_COMPAT, 4209}, {0xFD0C, 0, 2 | DECOMP_COMPAT, 4211}, {0xFD0D, 0, 2 | DECOMP_COMPAT, 4213}, {0xFD0E, 0, 2 | DECOMP_COMPAT, 4215}, {0xFD0F, 0, 2 | DECOMP_COMPAT, 4217}, {0xFD10, 0, 2 | DECOMP_COMPAT, 4219}, {0xFD11, 0, 2 | DECOMP_COMPAT, 4221}, {0xFD12, 0, 2 | DECOMP_COMPAT, 4223}, {0xFD13, 0, 2 | DECOMP_COMPAT, 4225}, {0xFD14, 0, 2 | DECOMP_COMPAT, 4227}, {0xFD15, 0, 2 | DECOMP_COMPAT, 4229}, {0xFD16, 0, 2 | DECOMP_COMPAT, 4231}, {0xFD17, 0, 2 | DECOMP_COMPAT, 4233}, {0xFD18, 0, 2 | DECOMP_COMPAT, 4235}, {0xFD19, 0, 2 | DECOMP_COMPAT, 4237}, {0xFD1A, 0, 2 | DECOMP_COMPAT, 4239}, {0xFD1B, 0, 2 | DECOMP_COMPAT, 4241}, {0xFD1C, 0, 2 | DECOMP_COMPAT, 4243}, {0xFD1D, 0, 2 | DECOMP_COMPAT, 4245}, {0xFD1E, 0, 2 | DECOMP_COMPAT, 4247}, {0xFD1F, 0, 2 | DECOMP_COMPAT, 4249}, {0xFD20, 0, 2 | DECOMP_COMPAT, 4251}, {0xFD21, 0, 2 | DECOMP_COMPAT, 4253}, {0xFD22, 0, 2 | DECOMP_COMPAT, 4255}, {0xFD23, 0, 2 | DECOMP_COMPAT, 4257}, {0xFD24, 0, 2 | DECOMP_COMPAT, 4259}, {0xFD25, 0, 2 | DECOMP_COMPAT, 4261}, {0xFD26, 0, 2 | DECOMP_COMPAT, 4263}, {0xFD27, 0, 2 | DECOMP_COMPAT, 4265}, {0xFD28, 0, 2 | DECOMP_COMPAT, 4267}, {0xFD29, 0, 2 | DECOMP_COMPAT, 4269}, {0xFD2A, 0, 2 | DECOMP_COMPAT, 4271}, {0xFD2B, 0, 2 | DECOMP_COMPAT, 4273}, {0xFD2C, 0, 2 | DECOMP_COMPAT, 4275}, {0xFD2D, 0, 2 | DECOMP_COMPAT, 4277}, {0xFD2E, 0, 2 | DECOMP_COMPAT, 4279}, {0xFD2F, 0, 2 | DECOMP_COMPAT, 4281}, {0xFD30, 0, 2 | DECOMP_COMPAT, 4283}, {0xFD31, 0, 2 | DECOMP_COMPAT, 4285}, {0xFD32, 0, 2 | DECOMP_COMPAT, 4287}, {0xFD33, 0, 2 | DECOMP_COMPAT, 4289}, {0xFD34, 0, 2 | DECOMP_COMPAT, 4291}, {0xFD35, 0, 2 | DECOMP_COMPAT, 4293}, {0xFD36, 0, 2 | DECOMP_COMPAT, 4295}, {0xFD37, 0, 2 | DECOMP_COMPAT, 4297}, {0xFD38, 0, 2 | DECOMP_COMPAT, 4299}, {0xFD39, 0, 2 | DECOMP_COMPAT, 4301}, {0xFD3A, 0, 2 | DECOMP_COMPAT, 4303}, {0xFD3B, 0, 2 | DECOMP_COMPAT, 4305}, {0xFD3C, 0, 2 | DECOMP_COMPAT, 4307}, {0xFD3D, 0, 2 | DECOMP_COMPAT, 4309}, {0xFD50, 0, 3 | DECOMP_COMPAT, 4311}, {0xFD51, 0, 3 | DECOMP_COMPAT, 4314}, {0xFD52, 0, 3 | DECOMP_COMPAT, 4317}, {0xFD53, 0, 3 | DECOMP_COMPAT, 4320}, {0xFD54, 0, 3 | DECOMP_COMPAT, 4323}, {0xFD55, 0, 3 | DECOMP_COMPAT, 4326}, {0xFD56, 0, 3 | DECOMP_COMPAT, 4329}, {0xFD57, 0, 3 | DECOMP_COMPAT, 4332}, {0xFD58, 0, 3 | DECOMP_COMPAT, 4335}, {0xFD59, 0, 3 | DECOMP_COMPAT, 4338}, {0xFD5A, 0, 3 | DECOMP_COMPAT, 4341}, {0xFD5B, 0, 3 | DECOMP_COMPAT, 4344}, {0xFD5C, 0, 3 | DECOMP_COMPAT, 4347}, {0xFD5D, 0, 3 | DECOMP_COMPAT, 4350}, {0xFD5E, 0, 3 | DECOMP_COMPAT, 4353}, {0xFD5F, 0, 3 | DECOMP_COMPAT, 4356}, {0xFD60, 0, 3 | DECOMP_COMPAT, 4359}, {0xFD61, 0, 3 | DECOMP_COMPAT, 4362}, {0xFD62, 0, 3 | DECOMP_COMPAT, 4365}, {0xFD63, 0, 3 | DECOMP_COMPAT, 4368}, {0xFD64, 0, 3 | DECOMP_COMPAT, 4371}, {0xFD65, 0, 3 | DECOMP_COMPAT, 4374}, {0xFD66, 0, 3 | DECOMP_COMPAT, 4377}, {0xFD67, 0, 3 | DECOMP_COMPAT, 4380}, {0xFD68, 0, 3 | DECOMP_COMPAT, 4383}, {0xFD69, 0, 3 | DECOMP_COMPAT, 4386}, {0xFD6A, 0, 3 | DECOMP_COMPAT, 4389}, {0xFD6B, 0, 3 | DECOMP_COMPAT, 4392}, {0xFD6C, 0, 3 | DECOMP_COMPAT, 4395}, {0xFD6D, 0, 3 | DECOMP_COMPAT, 4398}, {0xFD6E, 0, 3 | DECOMP_COMPAT, 4401}, {0xFD6F, 0, 3 | DECOMP_COMPAT, 4404}, {0xFD70, 0, 3 | DECOMP_COMPAT, 4407}, {0xFD71, 0, 3 | DECOMP_COMPAT, 4410}, {0xFD72, 0, 3 | DECOMP_COMPAT, 4413}, {0xFD73, 0, 3 | DECOMP_COMPAT, 4416}, {0xFD74, 0, 3 | DECOMP_COMPAT, 4419}, {0xFD75, 0, 3 | DECOMP_COMPAT, 4422}, {0xFD76, 0, 3 | DECOMP_COMPAT, 4425}, {0xFD77, 0, 3 | DECOMP_COMPAT, 4428}, {0xFD78, 0, 3 | DECOMP_COMPAT, 4431}, {0xFD79, 0, 3 | DECOMP_COMPAT, 4434}, {0xFD7A, 0, 3 | DECOMP_COMPAT, 4437}, {0xFD7B, 0, 3 | DECOMP_COMPAT, 4440}, {0xFD7C, 0, 3 | DECOMP_COMPAT, 4443}, {0xFD7D, 0, 3 | DECOMP_COMPAT, 4446}, {0xFD7E, 0, 3 | DECOMP_COMPAT, 4449}, {0xFD7F, 0, 3 | DECOMP_COMPAT, 4452}, {0xFD80, 0, 3 | DECOMP_COMPAT, 4455}, {0xFD81, 0, 3 | DECOMP_COMPAT, 4458}, {0xFD82, 0, 3 | DECOMP_COMPAT, 4461}, {0xFD83, 0, 3 | DECOMP_COMPAT, 4464}, {0xFD84, 0, 3 | DECOMP_COMPAT, 4467}, {0xFD85, 0, 3 | DECOMP_COMPAT, 4470}, {0xFD86, 0, 3 | DECOMP_COMPAT, 4473}, {0xFD87, 0, 3 | DECOMP_COMPAT, 4476}, {0xFD88, 0, 3 | DECOMP_COMPAT, 4479}, {0xFD89, 0, 3 | DECOMP_COMPAT, 4482}, {0xFD8A, 0, 3 | DECOMP_COMPAT, 4485}, {0xFD8B, 0, 3 | DECOMP_COMPAT, 4488}, {0xFD8C, 0, 3 | DECOMP_COMPAT, 4491}, {0xFD8D, 0, 3 | DECOMP_COMPAT, 4494}, {0xFD8E, 0, 3 | DECOMP_COMPAT, 4497}, {0xFD8F, 0, 3 | DECOMP_COMPAT, 4500}, {0xFD92, 0, 3 | DECOMP_COMPAT, 4503}, {0xFD93, 0, 3 | DECOMP_COMPAT, 4506}, {0xFD94, 0, 3 | DECOMP_COMPAT, 4509}, {0xFD95, 0, 3 | DECOMP_COMPAT, 4512}, {0xFD96, 0, 3 | DECOMP_COMPAT, 4515}, {0xFD97, 0, 3 | DECOMP_COMPAT, 4518}, {0xFD98, 0, 3 | DECOMP_COMPAT, 4521}, {0xFD99, 0, 3 | DECOMP_COMPAT, 4524}, {0xFD9A, 0, 3 | DECOMP_COMPAT, 4527}, {0xFD9B, 0, 3 | DECOMP_COMPAT, 4530}, {0xFD9C, 0, 3 | DECOMP_COMPAT, 4533}, {0xFD9D, 0, 3 | DECOMP_COMPAT, 4536}, {0xFD9E, 0, 3 | DECOMP_COMPAT, 4539}, {0xFD9F, 0, 3 | DECOMP_COMPAT, 4542}, {0xFDA0, 0, 3 | DECOMP_COMPAT, 4545}, {0xFDA1, 0, 3 | DECOMP_COMPAT, 4548}, {0xFDA2, 0, 3 | DECOMP_COMPAT, 4551}, {0xFDA3, 0, 3 | DECOMP_COMPAT, 4554}, {0xFDA4, 0, 3 | DECOMP_COMPAT, 4557}, {0xFDA5, 0, 3 | DECOMP_COMPAT, 4560}, {0xFDA6, 0, 3 | DECOMP_COMPAT, 4563}, {0xFDA7, 0, 3 | DECOMP_COMPAT, 4566}, {0xFDA8, 0, 3 | DECOMP_COMPAT, 4569}, {0xFDA9, 0, 3 | DECOMP_COMPAT, 4572}, {0xFDAA, 0, 3 | DECOMP_COMPAT, 4575}, {0xFDAB, 0, 3 | DECOMP_COMPAT, 4578}, {0xFDAC, 0, 3 | DECOMP_COMPAT, 4581}, {0xFDAD, 0, 3 | DECOMP_COMPAT, 4584}, {0xFDAE, 0, 3 | DECOMP_COMPAT, 4587}, {0xFDAF, 0, 3 | DECOMP_COMPAT, 4590}, {0xFDB0, 0, 3 | DECOMP_COMPAT, 4593}, {0xFDB1, 0, 3 | DECOMP_COMPAT, 4596}, {0xFDB2, 0, 3 | DECOMP_COMPAT, 4599}, {0xFDB3, 0, 3 | DECOMP_COMPAT, 4602}, {0xFDB4, 0, 3 | DECOMP_COMPAT, 4605}, {0xFDB5, 0, 3 | DECOMP_COMPAT, 4608}, {0xFDB6, 0, 3 | DECOMP_COMPAT, 4611}, {0xFDB7, 0, 3 | DECOMP_COMPAT, 4614}, {0xFDB8, 0, 3 | DECOMP_COMPAT, 4617}, {0xFDB9, 0, 3 | DECOMP_COMPAT, 4620}, {0xFDBA, 0, 3 | DECOMP_COMPAT, 4623}, {0xFDBB, 0, 3 | DECOMP_COMPAT, 4626}, {0xFDBC, 0, 3 | DECOMP_COMPAT, 4629}, {0xFDBD, 0, 3 | DECOMP_COMPAT, 4632}, {0xFDBE, 0, 3 | DECOMP_COMPAT, 4635}, {0xFDBF, 0, 3 | DECOMP_COMPAT, 4638}, {0xFDC0, 0, 3 | DECOMP_COMPAT, 4641}, {0xFDC1, 0, 3 | DECOMP_COMPAT, 4644}, {0xFDC2, 0, 3 | DECOMP_COMPAT, 4647}, {0xFDC3, 0, 3 | DECOMP_COMPAT, 4650}, {0xFDC4, 0, 3 | DECOMP_COMPAT, 4653}, {0xFDC5, 0, 3 | DECOMP_COMPAT, 4656}, {0xFDC6, 0, 3 | DECOMP_COMPAT, 4659}, {0xFDC7, 0, 3 | DECOMP_COMPAT, 4662}, {0xFDF0, 0, 3 | DECOMP_COMPAT, 4665}, {0xFDF1, 0, 3 | DECOMP_COMPAT, 4668}, {0xFDF2, 0, 4 | DECOMP_COMPAT, 4671}, {0xFDF3, 0, 4 | DECOMP_COMPAT, 4675}, {0xFDF4, 0, 4 | DECOMP_COMPAT, 4679}, {0xFDF5, 0, 4 | DECOMP_COMPAT, 4683}, {0xFDF6, 0, 4 | DECOMP_COMPAT, 4687}, {0xFDF7, 0, 4 | DECOMP_COMPAT, 4691}, {0xFDF8, 0, 4 | DECOMP_COMPAT, 4695}, {0xFDF9, 0, 3 | DECOMP_COMPAT, 4699}, {0xFDFA, 0, 18 | DECOMP_COMPAT, 4702}, {0xFDFB, 0, 8 | DECOMP_COMPAT, 4720}, {0xFDFC, 0, 4 | DECOMP_COMPAT, 4728}, {0xFE10, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002C}, {0xFE11, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3001}, {0xFE12, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3002}, {0xFE13, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003A}, {0xFE14, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003B}, {0xFE15, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0021}, {0xFE16, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003F}, {0xFE17, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3016}, {0xFE18, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3017}, {0xFE19, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2026}, {0xFE20, 230, 0, 0}, {0xFE21, 230, 0, 0}, {0xFE22, 230, 0, 0}, {0xFE23, 230, 0, 0}, {0xFE24, 230, 0, 0}, {0xFE25, 230, 0, 0}, {0xFE26, 230, 0, 0}, {0xFE27, 220, 0, 0}, {0xFE28, 220, 0, 0}, {0xFE29, 220, 0, 0}, {0xFE2A, 220, 0, 0}, {0xFE2B, 220, 0, 0}, {0xFE2C, 220, 0, 0}, {0xFE2D, 220, 0, 0}, {0xFE2E, 230, 0, 0}, {0xFE2F, 230, 0, 0}, {0xFE30, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2025}, {0xFE31, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2014}, {0xFE32, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2013}, {0xFE33, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005F}, {0xFE34, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005F}, {0xFE35, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0028}, {0xFE36, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0029}, {0xFE37, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007B}, {0xFE38, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007D}, {0xFE39, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3014}, {0xFE3A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3015}, {0xFE3B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3010}, {0xFE3C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3011}, {0xFE3D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300A}, {0xFE3E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300B}, {0xFE3F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3008}, {0xFE40, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3009}, {0xFE41, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300C}, {0xFE42, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300D}, {0xFE43, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300E}, {0xFE44, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300F}, {0xFE47, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005B}, {0xFE48, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005D}, {0xFE49, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x203E}, {0xFE4A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x203E}, {0xFE4B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x203E}, {0xFE4C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x203E}, {0xFE4D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005F}, {0xFE4E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005F}, {0xFE4F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005F}, {0xFE50, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002C}, {0xFE51, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3001}, {0xFE52, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002E}, {0xFE54, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003B}, {0xFE55, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003A}, {0xFE56, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003F}, {0xFE57, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0021}, {0xFE58, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2014}, {0xFE59, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0028}, {0xFE5A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0029}, {0xFE5B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007B}, {0xFE5C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007D}, {0xFE5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3014}, {0xFE5E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3015}, {0xFE5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0023}, {0xFE60, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0026}, {0xFE61, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002A}, {0xFE62, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002B}, {0xFE63, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002D}, {0xFE64, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003C}, {0xFE65, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003E}, {0xFE66, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003D}, {0xFE68, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005C}, {0xFE69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0024}, {0xFE6A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0025}, {0xFE6B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0040}, {0xFE70, 0, 2 | DECOMP_COMPAT, 4732}, {0xFE71, 0, 2 | DECOMP_COMPAT, 4734}, {0xFE72, 0, 2 | DECOMP_COMPAT, 4736}, {0xFE74, 0, 2 | DECOMP_COMPAT, 4738}, {0xFE76, 0, 2 | DECOMP_COMPAT, 4740}, {0xFE77, 0, 2 | DECOMP_COMPAT, 4742}, {0xFE78, 0, 2 | DECOMP_COMPAT, 4744}, {0xFE79, 0, 2 | DECOMP_COMPAT, 4746}, {0xFE7A, 0, 2 | DECOMP_COMPAT, 4748}, {0xFE7B, 0, 2 | DECOMP_COMPAT, 4750}, {0xFE7C, 0, 2 | DECOMP_COMPAT, 4752}, {0xFE7D, 0, 2 | DECOMP_COMPAT, 4754}, {0xFE7E, 0, 2 | DECOMP_COMPAT, 4756}, {0xFE7F, 0, 2 | DECOMP_COMPAT, 4758}, {0xFE80, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0621}, {0xFE81, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0622}, {0xFE82, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0622}, {0xFE83, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0623}, {0xFE84, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0623}, {0xFE85, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0624}, {0xFE86, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0624}, {0xFE87, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0625}, {0xFE88, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0625}, {0xFE89, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0626}, {0xFE8A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0626}, {0xFE8B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0626}, {0xFE8C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0626}, {0xFE8D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0627}, {0xFE8E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0627}, {0xFE8F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0xFE90, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0xFE91, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0xFE92, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0xFE93, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0629}, {0xFE94, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0629}, {0xFE95, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0xFE96, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0xFE97, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0xFE98, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0xFE99, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0xFE9A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0xFE9B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0xFE9C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0xFE9D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0xFE9E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0xFE9F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0xFEA0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0xFEA1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0xFEA2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0xFEA3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0xFEA4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0xFEA5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0xFEA6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0xFEA7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0xFEA8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0xFEA9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062F}, {0xFEAA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062F}, {0xFEAB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0630}, {0xFEAC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0630}, {0xFEAD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0631}, {0xFEAE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0631}, {0xFEAF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0632}, {0xFEB0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0632}, {0xFEB1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0xFEB2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0xFEB3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0xFEB4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0xFEB5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0xFEB6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0xFEB7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0xFEB8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0xFEB9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0xFEBA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0xFEBB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0xFEBC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0xFEBD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0xFEBE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0xFEBF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0xFEC0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0xFEC1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0xFEC2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0xFEC3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0xFEC4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0xFEC5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0xFEC6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0xFEC7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0xFEC8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0xFEC9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0xFECA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0xFECB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0xFECC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0xFECD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0xFECE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0xFECF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0xFED0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0xFED1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0xFED2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0xFED3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0xFED4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0xFED5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0xFED6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0xFED7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0xFED8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0xFED9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0xFEDA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0xFEDB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0xFEDC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0xFEDD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0xFEDE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0xFEDF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0xFEE0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0xFEE1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0xFEE2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0xFEE3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0xFEE4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0xFEE5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0xFEE6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0xFEE7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0xFEE8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0xFEE9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0xFEEA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0xFEEB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0xFEEC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0xFEED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0648}, {0xFEEE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0648}, {0xFEEF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0649}, {0xFEF0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0649}, {0xFEF1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0xFEF2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0xFEF3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0xFEF4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0xFEF5, 0, 2 | DECOMP_COMPAT, 4760}, {0xFEF6, 0, 2 | DECOMP_COMPAT, 4762}, {0xFEF7, 0, 2 | DECOMP_COMPAT, 4764}, {0xFEF8, 0, 2 | DECOMP_COMPAT, 4766}, {0xFEF9, 0, 2 | DECOMP_COMPAT, 4768}, {0xFEFA, 0, 2 | DECOMP_COMPAT, 4770}, {0xFEFB, 0, 2 | DECOMP_COMPAT, 4772}, {0xFEFC, 0, 2 | DECOMP_COMPAT, 4774}, {0xFF01, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0021}, {0xFF02, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0022}, {0xFF03, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0023}, {0xFF04, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0024}, {0xFF05, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0025}, {0xFF06, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0026}, {0xFF07, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0027}, {0xFF08, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0028}, {0xFF09, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0029}, {0xFF0A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002A}, {0xFF0B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002B}, {0xFF0C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002C}, {0xFF0D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002D}, {0xFF0E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002E}, {0xFF0F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x002F}, {0xFF10, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0xFF11, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0xFF12, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0xFF13, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0xFF14, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0xFF15, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0xFF16, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0xFF17, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0xFF18, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0xFF19, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0xFF1A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003A}, {0xFF1B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003B}, {0xFF1C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003C}, {0xFF1D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003D}, {0xFF1E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003E}, {0xFF1F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x003F}, {0xFF20, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0040}, {0xFF21, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0xFF22, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0xFF23, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0xFF24, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0xFF25, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0xFF26, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0xFF27, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0xFF28, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0xFF29, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0xFF2A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0xFF2B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0xFF2C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0xFF2D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0xFF2E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0xFF2F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0xFF30, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0xFF31, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0xFF32, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0xFF33, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0xFF34, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0xFF35, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0xFF36, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0xFF37, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0xFF38, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0xFF39, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0xFF3A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0xFF3B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005B}, {0xFF3C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005C}, {0xFF3D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005D}, {0xFF3E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005E}, {0xFF3F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005F}, {0xFF40, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0060}, {0xFF41, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0xFF42, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0xFF43, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0xFF44, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0xFF45, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0xFF46, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0xFF47, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0xFF48, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0xFF49, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0xFF4A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0xFF4B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0xFF4C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0xFF4D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0xFF4E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0xFF4F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0xFF50, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0xFF51, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0xFF52, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0xFF53, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0xFF54, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0xFF55, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0xFF56, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0xFF57, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0xFF58, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0xFF59, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0xFF5A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0xFF5B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007B}, {0xFF5C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007C}, {0xFF5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007D}, {0xFF5E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007E}, {0xFF5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2985}, {0xFF60, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2986}, {0xFF61, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3002}, {0xFF62, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300C}, {0xFF63, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x300D}, {0xFF64, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3001}, {0xFF65, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30FB}, {0xFF66, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30F2}, {0xFF67, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A1}, {0xFF68, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A3}, {0xFF69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A5}, {0xFF6A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A7}, {0xFF6B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A9}, {0xFF6C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E3}, {0xFF6D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E5}, {0xFF6E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E7}, {0xFF6F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C3}, {0xFF70, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30FC}, {0xFF71, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A2}, {0xFF72, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A4}, {0xFF73, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A6}, {0xFF74, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30A8}, {0xFF75, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AA}, {0xFF76, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AB}, {0xFF77, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AD}, {0xFF78, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30AF}, {0xFF79, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B1}, {0xFF7A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B3}, {0xFF7B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B5}, {0xFF7C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B7}, {0xFF7D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B9}, {0xFF7E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30BB}, {0xFF7F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30BD}, {0xFF80, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30BF}, {0xFF81, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C1}, {0xFF82, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C4}, {0xFF83, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C6}, {0xFF84, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C8}, {0xFF85, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CA}, {0xFF86, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CB}, {0xFF87, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CC}, {0xFF88, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CD}, {0xFF89, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CE}, {0xFF8A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30CF}, {0xFF8B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30D2}, {0xFF8C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30D5}, {0xFF8D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30D8}, {0xFF8E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30DB}, {0xFF8F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30DE}, {0xFF90, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30DF}, {0xFF91, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E0}, {0xFF92, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E1}, {0xFF93, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E2}, {0xFF94, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E4}, {0xFF95, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E6}, {0xFF96, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E8}, {0xFF97, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30E9}, {0xFF98, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EA}, {0xFF99, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EB}, {0xFF9A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EC}, {0xFF9B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30ED}, {0xFF9C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30EF}, {0xFF9D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30F3}, {0xFF9E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3099}, {0xFF9F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x309A}, {0xFFA0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3164}, {0xFFA1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3131}, {0xFFA2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3132}, {0xFFA3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3133}, {0xFFA4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3134}, {0xFFA5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3135}, {0xFFA6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3136}, {0xFFA7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3137}, {0xFFA8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3138}, {0xFFA9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3139}, {0xFFAA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x313A}, {0xFFAB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x313B}, {0xFFAC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x313C}, {0xFFAD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x313D}, {0xFFAE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x313E}, {0xFFAF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x313F}, {0xFFB0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3140}, {0xFFB1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3141}, {0xFFB2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3142}, {0xFFB3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3143}, {0xFFB4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3144}, {0xFFB5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3145}, {0xFFB6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3146}, {0xFFB7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3147}, {0xFFB8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3148}, {0xFFB9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3149}, {0xFFBA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x314A}, {0xFFBB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x314B}, {0xFFBC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x314C}, {0xFFBD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x314D}, {0xFFBE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x314E}, {0xFFC2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x314F}, {0xFFC3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3150}, {0xFFC4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3151}, {0xFFC5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3152}, {0xFFC6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3153}, {0xFFC7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3154}, {0xFFCA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3155}, {0xFFCB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3156}, {0xFFCC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3157}, {0xFFCD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3158}, {0xFFCE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3159}, {0xFFCF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x315A}, {0xFFD2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x315B}, {0xFFD3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x315C}, {0xFFD4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x315D}, {0xFFD5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x315E}, {0xFFD6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x315F}, {0xFFD7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3160}, {0xFFDA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3161}, {0xFFDB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3162}, {0xFFDC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x3163}, {0xFFE0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00A2}, {0xFFE1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00A3}, {0xFFE2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00AC}, {0xFFE3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00AF}, {0xFFE4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00A6}, {0xFFE5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x00A5}, {0xFFE6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x20A9}, {0xFFE8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2502}, {0xFFE9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2190}, {0xFFEA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2191}, {0xFFEB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2192}, {0xFFEC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2193}, {0xFFED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x25A0}, {0xFFEE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x25CB}, {0x101FD, 220, 0, 0}, {0x102E0, 220, 0, 0}, {0x10376, 230, 0, 0}, {0x10377, 230, 0, 0}, {0x10378, 230, 0, 0}, {0x10379, 230, 0, 0}, {0x1037A, 230, 0, 0}, {0x10A0D, 220, 0, 0}, {0x10A0F, 230, 0, 0}, {0x10A38, 230, 0, 0}, {0x10A39, 1, 0, 0}, {0x10A3A, 220, 0, 0}, {0x10A3F, 9, 0, 0}, {0x10AE5, 230, 0, 0}, {0x10AE6, 220, 0, 0}, {0x10D24, 230, 0, 0}, {0x10D25, 230, 0, 0}, {0x10D26, 230, 0, 0}, {0x10D27, 230, 0, 0}, {0x10EAB, 230, 0, 0}, {0x10EAC, 230, 0, 0}, {0x10F46, 220, 0, 0}, {0x10F47, 220, 0, 0}, {0x10F48, 230, 0, 0}, {0x10F49, 230, 0, 0}, {0x10F4A, 230, 0, 0}, {0x10F4B, 220, 0, 0}, {0x10F4C, 230, 0, 0}, {0x10F4D, 220, 0, 0}, {0x10F4E, 220, 0, 0}, {0x10F4F, 220, 0, 0}, {0x10F50, 220, 0, 0}, {0x11046, 9, 0, 0}, {0x1107F, 9, 0, 0}, {0x1109A, 0, 2, 4776}, {0x1109C, 0, 2, 4778}, {0x110AB, 0, 2, 4780}, {0x110B9, 9, 0, 0}, {0x110BA, 7, 0, 0}, {0x11100, 230, 0, 0}, {0x11101, 230, 0, 0}, {0x11102, 230, 0, 0}, {0x1112E, 0, 2, 4782}, {0x1112F, 0, 2, 4784}, {0x11133, 9, 0, 0}, {0x11134, 9, 0, 0}, {0x11173, 7, 0, 0}, {0x111C0, 9, 0, 0}, {0x111CA, 7, 0, 0}, {0x11235, 9, 0, 0}, {0x11236, 7, 0, 0}, {0x112E9, 7, 0, 0}, {0x112EA, 9, 0, 0}, {0x1133B, 7, 0, 0}, {0x1133C, 7, 0, 0}, {0x1134B, 0, 2, 4786}, {0x1134C, 0, 2, 4788}, {0x1134D, 9, 0, 0}, {0x11366, 230, 0, 0}, {0x11367, 230, 0, 0}, {0x11368, 230, 0, 0}, {0x11369, 230, 0, 0}, {0x1136A, 230, 0, 0}, {0x1136B, 230, 0, 0}, {0x1136C, 230, 0, 0}, {0x11370, 230, 0, 0}, {0x11371, 230, 0, 0}, {0x11372, 230, 0, 0}, {0x11373, 230, 0, 0}, {0x11374, 230, 0, 0}, {0x11442, 9, 0, 0}, {0x11446, 7, 0, 0}, {0x1145E, 230, 0, 0}, {0x114BB, 0, 2, 4790}, {0x114BC, 0, 2, 4792}, {0x114BE, 0, 2, 4794}, {0x114C2, 9, 0, 0}, {0x114C3, 7, 0, 0}, {0x115BA, 0, 2, 4796}, {0x115BB, 0, 2, 4798}, {0x115BF, 9, 0, 0}, {0x115C0, 7, 0, 0}, {0x1163F, 9, 0, 0}, {0x116B6, 9, 0, 0}, {0x116B7, 7, 0, 0}, {0x1172B, 9, 0, 0}, {0x11839, 9, 0, 0}, {0x1183A, 7, 0, 0}, {0x11938, 0, 2, 4800}, {0x1193D, 9, 0, 0}, {0x1193E, 9, 0, 0}, {0x11943, 7, 0, 0}, {0x119E0, 9, 0, 0}, {0x11A34, 9, 0, 0}, {0x11A47, 9, 0, 0}, {0x11A99, 9, 0, 0}, {0x11C3F, 9, 0, 0}, {0x11D42, 7, 0, 0}, {0x11D44, 9, 0, 0}, {0x11D45, 9, 0, 0}, {0x11D97, 9, 0, 0}, {0x16AF0, 1, 0, 0}, {0x16AF1, 1, 0, 0}, {0x16AF2, 1, 0, 0}, {0x16AF3, 1, 0, 0}, {0x16AF4, 1, 0, 0}, {0x16B30, 230, 0, 0}, {0x16B31, 230, 0, 0}, {0x16B32, 230, 0, 0}, {0x16B33, 230, 0, 0}, {0x16B34, 230, 0, 0}, {0x16B35, 230, 0, 0}, {0x16B36, 230, 0, 0}, {0x16FF0, 6, 0, 0}, {0x16FF1, 6, 0, 0}, {0x1BC9E, 1, 0, 0}, {0x1D15E, 0, 2 | DECOMP_NO_COMPOSE, 4802}, /* in exclusion list */ {0x1D15F, 0, 2 | DECOMP_NO_COMPOSE, 4804}, /* in exclusion list */ {0x1D160, 0, 2 | DECOMP_NO_COMPOSE, 4806}, /* in exclusion list */ {0x1D161, 0, 2 | DECOMP_NO_COMPOSE, 4808}, /* in exclusion list */ {0x1D162, 0, 2 | DECOMP_NO_COMPOSE, 4810}, /* in exclusion list */ {0x1D163, 0, 2 | DECOMP_NO_COMPOSE, 4812}, /* in exclusion list */ {0x1D164, 0, 2 | DECOMP_NO_COMPOSE, 4814}, /* in exclusion list */ {0x1D165, 216, 0, 0}, {0x1D166, 216, 0, 0}, {0x1D167, 1, 0, 0}, {0x1D168, 1, 0, 0}, {0x1D169, 1, 0, 0}, {0x1D16D, 226, 0, 0}, {0x1D16E, 216, 0, 0}, {0x1D16F, 216, 0, 0}, {0x1D170, 216, 0, 0}, {0x1D171, 216, 0, 0}, {0x1D172, 216, 0, 0}, {0x1D17B, 220, 0, 0}, {0x1D17C, 220, 0, 0}, {0x1D17D, 220, 0, 0}, {0x1D17E, 220, 0, 0}, {0x1D17F, 220, 0, 0}, {0x1D180, 220, 0, 0}, {0x1D181, 220, 0, 0}, {0x1D182, 220, 0, 0}, {0x1D185, 230, 0, 0}, {0x1D186, 230, 0, 0}, {0x1D187, 230, 0, 0}, {0x1D188, 230, 0, 0}, {0x1D189, 230, 0, 0}, {0x1D18A, 220, 0, 0}, {0x1D18B, 220, 0, 0}, {0x1D1AA, 230, 0, 0}, {0x1D1AB, 230, 0, 0}, {0x1D1AC, 230, 0, 0}, {0x1D1AD, 230, 0, 0}, {0x1D1BB, 0, 2 | DECOMP_NO_COMPOSE, 4816}, /* in exclusion list */ {0x1D1BC, 0, 2 | DECOMP_NO_COMPOSE, 4818}, /* in exclusion list */ {0x1D1BD, 0, 2 | DECOMP_NO_COMPOSE, 4820}, /* in exclusion list */ {0x1D1BE, 0, 2 | DECOMP_NO_COMPOSE, 4822}, /* in exclusion list */ {0x1D1BF, 0, 2 | DECOMP_NO_COMPOSE, 4824}, /* in exclusion list */ {0x1D1C0, 0, 2 | DECOMP_NO_COMPOSE, 4826}, /* in exclusion list */ {0x1D242, 230, 0, 0}, {0x1D243, 230, 0, 0}, {0x1D244, 230, 0, 0}, {0x1D400, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D401, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D402, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D403, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D404, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D405, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D406, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D407, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D408, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D409, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D40A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D40B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D40C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D40D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D40E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D40F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D410, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D411, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D412, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D413, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D414, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D415, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D416, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D417, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D418, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D419, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D41A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D41B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D41C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D41D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D41E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D41F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D420, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D421, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D422, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D423, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D424, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D425, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D426, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D427, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D428, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D429, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D42A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D42B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D42C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D42D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D42E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D42F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D430, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D431, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D432, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D433, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D434, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D435, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D436, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D437, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D438, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D439, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D43A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D43B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D43C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D43D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D43E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D43F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D440, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D441, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D442, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D443, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D444, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D445, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D446, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D447, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D448, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D449, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D44A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D44B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D44C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D44D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D44E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D44F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D450, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D451, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D452, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D453, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D454, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D456, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D457, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D458, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D459, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D45A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D45B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D45C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D45D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D45E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D45F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D460, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D461, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D462, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D463, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D464, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D465, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D466, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D467, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D468, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D469, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D46A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D46B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D46C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D46D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D46E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D46F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D470, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D471, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D472, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D473, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D474, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D475, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D476, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D477, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D478, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D479, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D47A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D47B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D47C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D47D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D47E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D47F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D480, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D481, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D482, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D483, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D484, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D485, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D486, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D487, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D488, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D489, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D48A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D48B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D48C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D48D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D48E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D48F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D490, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D491, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D492, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D493, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D494, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D495, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D496, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D497, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D498, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D499, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D49A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D49B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D49C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D49E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D49F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D4A2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D4A5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D4A6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D4A9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D4AA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D4AB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D4AC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D4AE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D4AF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D4B0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D4B1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D4B2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D4B3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D4B4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D4B5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D4B6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D4B7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D4B8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D4B9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D4BB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D4BD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D4BE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D4BF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D4C0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D4C1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D4C2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D4C3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D4C5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D4C6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D4C7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D4C8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D4C9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D4CA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D4CB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D4CC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D4CD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D4CE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D4CF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D4D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D4D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D4D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D4D3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D4D4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D4D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D4D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D4D7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D4D8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D4D9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D4DA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D4DB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D4DC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D4DD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D4DE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D4DF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D4E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D4E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D4E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D4E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D4E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D4E5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D4E6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D4E7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D4E8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D4E9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D4EA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D4EB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D4EC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D4ED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D4EE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D4EF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D4F0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D4F1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D4F2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D4F3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D4F4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D4F5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D4F6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D4F7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D4F8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D4F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D4FA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D4FB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D4FC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D4FD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D4FE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D4FF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D500, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D501, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D502, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D503, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D504, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D505, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D507, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D508, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D509, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D50A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D50D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D50E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D50F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D510, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D511, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D512, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D513, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D514, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D516, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D517, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D518, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D519, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D51A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D51B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D51C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D51E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D51F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D520, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D521, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D522, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D523, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D524, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D525, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D526, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D527, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D528, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D529, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D52A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D52B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D52C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D52D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D52E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D52F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D530, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D531, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D532, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D533, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D534, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D535, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D536, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D537, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D538, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D539, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D53B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D53C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D53D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D53E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D540, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D541, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D542, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D543, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D544, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D546, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D54A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D54B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D54C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D54D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D54E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D54F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D550, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D552, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D553, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D554, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D555, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D556, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D557, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D558, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D559, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D55A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D55B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D55C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D55D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D55E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D55F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D560, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D561, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D562, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D563, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D564, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D565, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D566, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D567, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D568, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D569, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D56A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D56B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D56C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D56D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D56E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D56F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D570, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D571, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D572, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D573, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D574, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D575, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D576, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D577, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D578, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D579, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D57A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D57B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D57C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D57D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D57E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D57F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D580, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D581, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D582, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D583, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D584, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D585, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D586, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D587, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D588, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D589, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D58A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D58B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D58C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D58D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D58E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D58F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D590, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D591, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D592, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D593, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D594, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D595, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D596, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D597, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D598, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D599, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D59A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D59B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D59C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D59D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D59E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D59F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D5A0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D5A1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D5A2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D5A3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D5A4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D5A5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D5A6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D5A7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D5A8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D5A9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D5AA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D5AB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D5AC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D5AD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D5AE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D5AF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D5B0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D5B1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D5B2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D5B3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D5B4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D5B5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D5B6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D5B7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D5B8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D5B9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D5BA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D5BB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D5BC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D5BD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D5BE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D5BF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D5C0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D5C1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D5C2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D5C3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D5C4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D5C5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D5C6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D5C7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D5C8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D5C9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D5CA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D5CB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D5CC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D5CD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D5CE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D5CF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D5D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D5D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D5D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D5D3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D5D4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D5D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D5D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D5D7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D5D8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D5D9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D5DA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D5DB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D5DC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D5DD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D5DE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D5DF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D5E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D5E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D5E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D5E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D5E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D5E5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D5E6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D5E7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D5E8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D5E9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D5EA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D5EB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D5EC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D5ED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D5EE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D5EF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D5F0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D5F1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D5F2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D5F3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D5F4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D5F5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D5F6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D5F7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D5F8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D5F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D5FA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D5FB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D5FC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D5FD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D5FE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D5FF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D600, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D601, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D602, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D603, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D604, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D605, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D606, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D607, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D608, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D609, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D60A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D60B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D60C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D60D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D60E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D60F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D610, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D611, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D612, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D613, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D614, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D615, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D616, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D617, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D618, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D619, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D61A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D61B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D61C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D61D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D61E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D61F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D620, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D621, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D622, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D623, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D624, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D625, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D626, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D627, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D628, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D629, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D62A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D62B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D62C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D62D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D62E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D62F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D630, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D631, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D632, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D633, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D634, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D635, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D636, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D637, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D638, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D639, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D63A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D63B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D63C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D63D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D63E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D63F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D640, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D641, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D642, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D643, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D644, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D645, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D646, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D647, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D648, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D649, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D64A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D64B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D64C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D64D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D64E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D64F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D650, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D651, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D652, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D653, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D654, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D655, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D656, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D657, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D658, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D659, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D65A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D65B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D65C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D65D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D65E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D65F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D660, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D661, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D662, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D663, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D664, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D665, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D666, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D667, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D668, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D669, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D66A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D66B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D66C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D66D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D66E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D66F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D670, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1D671, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1D672, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1D673, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1D674, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1D675, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1D676, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1D677, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1D678, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1D679, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1D67A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1D67B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1D67C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1D67D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1D67E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1D67F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1D680, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1D681, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1D682, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1D683, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1D684, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1D685, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1D686, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1D687, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1D688, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1D689, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1D68A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0061}, {0x1D68B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0062}, {0x1D68C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0063}, {0x1D68D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0064}, {0x1D68E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0065}, {0x1D68F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0066}, {0x1D690, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0067}, {0x1D691, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0068}, {0x1D692, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0069}, {0x1D693, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006A}, {0x1D694, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006B}, {0x1D695, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006C}, {0x1D696, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006D}, {0x1D697, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006E}, {0x1D698, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x006F}, {0x1D699, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0070}, {0x1D69A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0071}, {0x1D69B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0072}, {0x1D69C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0073}, {0x1D69D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0074}, {0x1D69E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0075}, {0x1D69F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0076}, {0x1D6A0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0077}, {0x1D6A1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0078}, {0x1D6A2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0079}, {0x1D6A3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x007A}, {0x1D6A4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0131}, {0x1D6A5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0237}, {0x1D6A8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0391}, {0x1D6A9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0392}, {0x1D6AA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0393}, {0x1D6AB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0394}, {0x1D6AC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0395}, {0x1D6AD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0396}, {0x1D6AE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0397}, {0x1D6AF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0398}, {0x1D6B0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0399}, {0x1D6B1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039A}, {0x1D6B2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039B}, {0x1D6B3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039C}, {0x1D6B4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039D}, {0x1D6B5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039E}, {0x1D6B6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039F}, {0x1D6B7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A0}, {0x1D6B8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A1}, {0x1D6B9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F4}, {0x1D6BA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A3}, {0x1D6BB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A4}, {0x1D6BC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A5}, {0x1D6BD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A6}, {0x1D6BE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A7}, {0x1D6BF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A8}, {0x1D6C0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A9}, {0x1D6C1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2207}, {0x1D6C2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B1}, {0x1D6C3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D6C4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D6C5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B4}, {0x1D6C6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B5}, {0x1D6C7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B6}, {0x1D6C8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B7}, {0x1D6C9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x1D6CA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B9}, {0x1D6CB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BA}, {0x1D6CC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BB}, {0x1D6CD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BC}, {0x1D6CE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BD}, {0x1D6CF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BE}, {0x1D6D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BF}, {0x1D6D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x1D6D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x1D6D3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C2}, {0x1D6D4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C3}, {0x1D6D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C4}, {0x1D6D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C5}, {0x1D6D7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D6D8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D6D9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C8}, {0x1D6DA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C9}, {0x1D6DB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2202}, {0x1D6DC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F5}, {0x1D6DD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D1}, {0x1D6DE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F0}, {0x1D6DF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D5}, {0x1D6E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F1}, {0x1D6E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D6}, {0x1D6E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0391}, {0x1D6E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0392}, {0x1D6E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0393}, {0x1D6E5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0394}, {0x1D6E6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0395}, {0x1D6E7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0396}, {0x1D6E8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0397}, {0x1D6E9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0398}, {0x1D6EA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0399}, {0x1D6EB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039A}, {0x1D6EC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039B}, {0x1D6ED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039C}, {0x1D6EE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039D}, {0x1D6EF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039E}, {0x1D6F0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039F}, {0x1D6F1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A0}, {0x1D6F2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A1}, {0x1D6F3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F4}, {0x1D6F4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A3}, {0x1D6F5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A4}, {0x1D6F6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A5}, {0x1D6F7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A6}, {0x1D6F8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A7}, {0x1D6F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A8}, {0x1D6FA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A9}, {0x1D6FB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2207}, {0x1D6FC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B1}, {0x1D6FD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D6FE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D6FF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B4}, {0x1D700, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B5}, {0x1D701, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B6}, {0x1D702, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B7}, {0x1D703, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x1D704, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B9}, {0x1D705, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BA}, {0x1D706, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BB}, {0x1D707, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BC}, {0x1D708, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BD}, {0x1D709, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BE}, {0x1D70A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BF}, {0x1D70B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x1D70C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x1D70D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C2}, {0x1D70E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C3}, {0x1D70F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C4}, {0x1D710, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C5}, {0x1D711, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D712, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D713, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C8}, {0x1D714, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C9}, {0x1D715, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2202}, {0x1D716, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F5}, {0x1D717, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D1}, {0x1D718, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F0}, {0x1D719, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D5}, {0x1D71A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F1}, {0x1D71B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D6}, {0x1D71C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0391}, {0x1D71D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0392}, {0x1D71E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0393}, {0x1D71F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0394}, {0x1D720, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0395}, {0x1D721, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0396}, {0x1D722, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0397}, {0x1D723, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0398}, {0x1D724, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0399}, {0x1D725, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039A}, {0x1D726, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039B}, {0x1D727, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039C}, {0x1D728, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039D}, {0x1D729, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039E}, {0x1D72A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039F}, {0x1D72B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A0}, {0x1D72C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A1}, {0x1D72D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F4}, {0x1D72E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A3}, {0x1D72F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A4}, {0x1D730, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A5}, {0x1D731, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A6}, {0x1D732, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A7}, {0x1D733, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A8}, {0x1D734, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A9}, {0x1D735, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2207}, {0x1D736, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B1}, {0x1D737, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D738, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D739, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B4}, {0x1D73A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B5}, {0x1D73B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B6}, {0x1D73C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B7}, {0x1D73D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x1D73E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B9}, {0x1D73F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BA}, {0x1D740, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BB}, {0x1D741, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BC}, {0x1D742, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BD}, {0x1D743, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BE}, {0x1D744, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BF}, {0x1D745, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x1D746, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x1D747, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C2}, {0x1D748, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C3}, {0x1D749, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C4}, {0x1D74A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C5}, {0x1D74B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D74C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D74D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C8}, {0x1D74E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C9}, {0x1D74F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2202}, {0x1D750, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F5}, {0x1D751, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D1}, {0x1D752, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F0}, {0x1D753, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D5}, {0x1D754, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F1}, {0x1D755, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D6}, {0x1D756, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0391}, {0x1D757, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0392}, {0x1D758, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0393}, {0x1D759, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0394}, {0x1D75A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0395}, {0x1D75B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0396}, {0x1D75C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0397}, {0x1D75D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0398}, {0x1D75E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0399}, {0x1D75F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039A}, {0x1D760, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039B}, {0x1D761, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039C}, {0x1D762, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039D}, {0x1D763, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039E}, {0x1D764, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039F}, {0x1D765, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A0}, {0x1D766, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A1}, {0x1D767, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F4}, {0x1D768, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A3}, {0x1D769, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A4}, {0x1D76A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A5}, {0x1D76B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A6}, {0x1D76C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A7}, {0x1D76D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A8}, {0x1D76E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A9}, {0x1D76F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2207}, {0x1D770, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B1}, {0x1D771, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D772, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D773, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B4}, {0x1D774, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B5}, {0x1D775, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B6}, {0x1D776, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B7}, {0x1D777, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x1D778, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B9}, {0x1D779, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BA}, {0x1D77A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BB}, {0x1D77B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BC}, {0x1D77C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BD}, {0x1D77D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BE}, {0x1D77E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BF}, {0x1D77F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x1D780, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x1D781, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C2}, {0x1D782, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C3}, {0x1D783, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C4}, {0x1D784, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C5}, {0x1D785, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D786, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D787, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C8}, {0x1D788, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C9}, {0x1D789, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2202}, {0x1D78A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F5}, {0x1D78B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D1}, {0x1D78C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F0}, {0x1D78D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D5}, {0x1D78E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F1}, {0x1D78F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D6}, {0x1D790, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0391}, {0x1D791, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0392}, {0x1D792, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0393}, {0x1D793, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0394}, {0x1D794, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0395}, {0x1D795, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0396}, {0x1D796, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0397}, {0x1D797, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0398}, {0x1D798, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0399}, {0x1D799, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039A}, {0x1D79A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039B}, {0x1D79B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039C}, {0x1D79C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039D}, {0x1D79D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039E}, {0x1D79E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x039F}, {0x1D79F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A0}, {0x1D7A0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A1}, {0x1D7A1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F4}, {0x1D7A2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A3}, {0x1D7A3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A4}, {0x1D7A4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A5}, {0x1D7A5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A6}, {0x1D7A6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A7}, {0x1D7A7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A8}, {0x1D7A8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03A9}, {0x1D7A9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2207}, {0x1D7AA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B1}, {0x1D7AB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B2}, {0x1D7AC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B3}, {0x1D7AD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B4}, {0x1D7AE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B5}, {0x1D7AF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B6}, {0x1D7B0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B7}, {0x1D7B1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B8}, {0x1D7B2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03B9}, {0x1D7B3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BA}, {0x1D7B4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BB}, {0x1D7B5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BC}, {0x1D7B6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BD}, {0x1D7B7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BE}, {0x1D7B8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03BF}, {0x1D7B9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C0}, {0x1D7BA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C1}, {0x1D7BB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C2}, {0x1D7BC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C3}, {0x1D7BD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C4}, {0x1D7BE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C5}, {0x1D7BF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C6}, {0x1D7C0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C7}, {0x1D7C1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C8}, {0x1D7C2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03C9}, {0x1D7C3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x2202}, {0x1D7C4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F5}, {0x1D7C5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D1}, {0x1D7C6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F0}, {0x1D7C7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D5}, {0x1D7C8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03F1}, {0x1D7C9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03D6}, {0x1D7CA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03DC}, {0x1D7CB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x03DD}, {0x1D7CE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x1D7CF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x1D7D0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x1D7D1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x1D7D2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x1D7D3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x1D7D4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x1D7D5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x1D7D6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x1D7D7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x1D7D8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x1D7D9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x1D7DA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x1D7DB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x1D7DC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x1D7DD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x1D7DE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x1D7DF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x1D7E0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x1D7E1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x1D7E2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x1D7E3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x1D7E4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x1D7E5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x1D7E6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x1D7E7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x1D7E8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x1D7E9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x1D7EA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x1D7EB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x1D7EC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x1D7ED, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x1D7EE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x1D7EF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x1D7F0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x1D7F1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x1D7F2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x1D7F3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x1D7F4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x1D7F5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x1D7F6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x1D7F7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x1D7F8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x1D7F9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x1D7FA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x1D7FB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x1D7FC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x1D7FD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x1D7FE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x1D7FF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x1E000, 230, 0, 0}, {0x1E001, 230, 0, 0}, {0x1E002, 230, 0, 0}, {0x1E003, 230, 0, 0}, {0x1E004, 230, 0, 0}, {0x1E005, 230, 0, 0}, {0x1E006, 230, 0, 0}, {0x1E008, 230, 0, 0}, {0x1E009, 230, 0, 0}, {0x1E00A, 230, 0, 0}, {0x1E00B, 230, 0, 0}, {0x1E00C, 230, 0, 0}, {0x1E00D, 230, 0, 0}, {0x1E00E, 230, 0, 0}, {0x1E00F, 230, 0, 0}, {0x1E010, 230, 0, 0}, {0x1E011, 230, 0, 0}, {0x1E012, 230, 0, 0}, {0x1E013, 230, 0, 0}, {0x1E014, 230, 0, 0}, {0x1E015, 230, 0, 0}, {0x1E016, 230, 0, 0}, {0x1E017, 230, 0, 0}, {0x1E018, 230, 0, 0}, {0x1E01B, 230, 0, 0}, {0x1E01C, 230, 0, 0}, {0x1E01D, 230, 0, 0}, {0x1E01E, 230, 0, 0}, {0x1E01F, 230, 0, 0}, {0x1E020, 230, 0, 0}, {0x1E021, 230, 0, 0}, {0x1E023, 230, 0, 0}, {0x1E024, 230, 0, 0}, {0x1E026, 230, 0, 0}, {0x1E027, 230, 0, 0}, {0x1E028, 230, 0, 0}, {0x1E029, 230, 0, 0}, {0x1E02A, 230, 0, 0}, {0x1E130, 230, 0, 0}, {0x1E131, 230, 0, 0}, {0x1E132, 230, 0, 0}, {0x1E133, 230, 0, 0}, {0x1E134, 230, 0, 0}, {0x1E135, 230, 0, 0}, {0x1E136, 230, 0, 0}, {0x1E2EC, 230, 0, 0}, {0x1E2ED, 230, 0, 0}, {0x1E2EE, 230, 0, 0}, {0x1E2EF, 230, 0, 0}, {0x1E8D0, 220, 0, 0}, {0x1E8D1, 220, 0, 0}, {0x1E8D2, 220, 0, 0}, {0x1E8D3, 220, 0, 0}, {0x1E8D4, 220, 0, 0}, {0x1E8D5, 220, 0, 0}, {0x1E8D6, 220, 0, 0}, {0x1E944, 230, 0, 0}, {0x1E945, 230, 0, 0}, {0x1E946, 230, 0, 0}, {0x1E947, 230, 0, 0}, {0x1E948, 230, 0, 0}, {0x1E949, 230, 0, 0}, {0x1E94A, 7, 0, 0}, {0x1EE00, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0627}, {0x1EE01, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0x1EE02, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0x1EE03, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062F}, {0x1EE05, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0648}, {0x1EE06, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0632}, {0x1EE07, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0x1EE08, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0x1EE09, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0x1EE0A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0x1EE0B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0x1EE0C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0x1EE0D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0x1EE0E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0x1EE0F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0x1EE10, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0x1EE11, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0x1EE12, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0x1EE13, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0631}, {0x1EE14, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0x1EE15, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0x1EE16, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0x1EE17, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0x1EE18, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0630}, {0x1EE19, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0x1EE1A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0x1EE1B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0x1EE1C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x066E}, {0x1EE1D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BA}, {0x1EE1E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A1}, {0x1EE1F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x066F}, {0x1EE21, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0x1EE22, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0x1EE24, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0x1EE27, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0x1EE29, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0x1EE2A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0x1EE2B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0x1EE2C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0x1EE2D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0x1EE2E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0x1EE2F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0x1EE30, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0x1EE31, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0x1EE32, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0x1EE34, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0x1EE35, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0x1EE36, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0x1EE37, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0x1EE39, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0x1EE3B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0x1EE42, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0x1EE47, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0x1EE49, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0x1EE4B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0x1EE4D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0x1EE4E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0x1EE4F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0x1EE51, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0x1EE52, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0x1EE54, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0x1EE57, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0x1EE59, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0x1EE5B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0x1EE5D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06BA}, {0x1EE5F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x066F}, {0x1EE61, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0x1EE62, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0x1EE64, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0x1EE67, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0x1EE68, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0x1EE69, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0x1EE6A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0643}, {0x1EE6C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0x1EE6D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0x1EE6E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0x1EE6F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0x1EE70, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0x1EE71, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0x1EE72, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0x1EE74, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0x1EE75, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0x1EE76, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0x1EE77, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0x1EE79, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0x1EE7A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0x1EE7B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0x1EE7C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x066E}, {0x1EE7E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x06A1}, {0x1EE80, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0627}, {0x1EE81, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0x1EE82, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0x1EE83, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062F}, {0x1EE84, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0647}, {0x1EE85, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0648}, {0x1EE86, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0632}, {0x1EE87, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0x1EE88, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0x1EE89, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0x1EE8B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0x1EE8C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0x1EE8D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0x1EE8E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0x1EE8F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0x1EE90, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0x1EE91, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0x1EE92, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0x1EE93, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0631}, {0x1EE94, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0x1EE95, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0x1EE96, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0x1EE97, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0x1EE98, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0630}, {0x1EE99, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0x1EE9A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0x1EE9B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0x1EEA1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0628}, {0x1EEA2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062C}, {0x1EEA3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062F}, {0x1EEA5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0648}, {0x1EEA6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0632}, {0x1EEA7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062D}, {0x1EEA8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0637}, {0x1EEA9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x064A}, {0x1EEAB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0644}, {0x1EEAC, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0645}, {0x1EEAD, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0646}, {0x1EEAE, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0633}, {0x1EEAF, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0639}, {0x1EEB0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0641}, {0x1EEB1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0635}, {0x1EEB2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0642}, {0x1EEB3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0631}, {0x1EEB4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0634}, {0x1EEB5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062A}, {0x1EEB6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062B}, {0x1EEB7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x062E}, {0x1EEB8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0630}, {0x1EEB9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0636}, {0x1EEBA, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0638}, {0x1EEBB, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x063A}, {0x1F100, 0, 2 | DECOMP_COMPAT, 4828}, {0x1F101, 0, 2 | DECOMP_COMPAT, 4830}, {0x1F102, 0, 2 | DECOMP_COMPAT, 4832}, {0x1F103, 0, 2 | DECOMP_COMPAT, 4834}, {0x1F104, 0, 2 | DECOMP_COMPAT, 4836}, {0x1F105, 0, 2 | DECOMP_COMPAT, 4838}, {0x1F106, 0, 2 | DECOMP_COMPAT, 4840}, {0x1F107, 0, 2 | DECOMP_COMPAT, 4842}, {0x1F108, 0, 2 | DECOMP_COMPAT, 4844}, {0x1F109, 0, 2 | DECOMP_COMPAT, 4846}, {0x1F10A, 0, 2 | DECOMP_COMPAT, 4848}, {0x1F110, 0, 3 | DECOMP_COMPAT, 4850}, {0x1F111, 0, 3 | DECOMP_COMPAT, 4853}, {0x1F112, 0, 3 | DECOMP_COMPAT, 4856}, {0x1F113, 0, 3 | DECOMP_COMPAT, 4859}, {0x1F114, 0, 3 | DECOMP_COMPAT, 4862}, {0x1F115, 0, 3 | DECOMP_COMPAT, 4865}, {0x1F116, 0, 3 | DECOMP_COMPAT, 4868}, {0x1F117, 0, 3 | DECOMP_COMPAT, 4871}, {0x1F118, 0, 3 | DECOMP_COMPAT, 4874}, {0x1F119, 0, 3 | DECOMP_COMPAT, 4877}, {0x1F11A, 0, 3 | DECOMP_COMPAT, 4880}, {0x1F11B, 0, 3 | DECOMP_COMPAT, 4883}, {0x1F11C, 0, 3 | DECOMP_COMPAT, 4886}, {0x1F11D, 0, 3 | DECOMP_COMPAT, 4889}, {0x1F11E, 0, 3 | DECOMP_COMPAT, 4892}, {0x1F11F, 0, 3 | DECOMP_COMPAT, 4895}, {0x1F120, 0, 3 | DECOMP_COMPAT, 4898}, {0x1F121, 0, 3 | DECOMP_COMPAT, 4901}, {0x1F122, 0, 3 | DECOMP_COMPAT, 4904}, {0x1F123, 0, 3 | DECOMP_COMPAT, 4907}, {0x1F124, 0, 3 | DECOMP_COMPAT, 4910}, {0x1F125, 0, 3 | DECOMP_COMPAT, 4913}, {0x1F126, 0, 3 | DECOMP_COMPAT, 4916}, {0x1F127, 0, 3 | DECOMP_COMPAT, 4919}, {0x1F128, 0, 3 | DECOMP_COMPAT, 4922}, {0x1F129, 0, 3 | DECOMP_COMPAT, 4925}, {0x1F12A, 0, 3 | DECOMP_COMPAT, 4928}, {0x1F12B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1F12C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1F12D, 0, 2 | DECOMP_COMPAT, 4931}, {0x1F12E, 0, 2 | DECOMP_COMPAT, 4933}, {0x1F130, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0041}, {0x1F131, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0042}, {0x1F132, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0043}, {0x1F133, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0044}, {0x1F134, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0045}, {0x1F135, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0046}, {0x1F136, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0047}, {0x1F137, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0048}, {0x1F138, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0049}, {0x1F139, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004A}, {0x1F13A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004B}, {0x1F13B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004C}, {0x1F13C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004D}, {0x1F13D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004E}, {0x1F13E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x004F}, {0x1F13F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0050}, {0x1F140, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0051}, {0x1F141, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0052}, {0x1F142, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0053}, {0x1F143, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0054}, {0x1F144, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0055}, {0x1F145, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0056}, {0x1F146, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0057}, {0x1F147, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0058}, {0x1F148, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0059}, {0x1F149, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x005A}, {0x1F14A, 0, 2 | DECOMP_COMPAT, 4935}, {0x1F14B, 0, 2 | DECOMP_COMPAT, 4937}, {0x1F14C, 0, 2 | DECOMP_COMPAT, 4939}, {0x1F14D, 0, 2 | DECOMP_COMPAT, 4941}, {0x1F14E, 0, 3 | DECOMP_COMPAT, 4943}, {0x1F14F, 0, 2 | DECOMP_COMPAT, 4946}, {0x1F16A, 0, 2 | DECOMP_COMPAT, 4948}, {0x1F16B, 0, 2 | DECOMP_COMPAT, 4950}, {0x1F16C, 0, 2 | DECOMP_COMPAT, 4952}, {0x1F190, 0, 2 | DECOMP_COMPAT, 4954}, {0x1F200, 0, 2 | DECOMP_COMPAT, 4956}, {0x1F201, 0, 2 | DECOMP_COMPAT, 4958}, {0x1F202, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30B5}, {0x1F210, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x624B}, {0x1F211, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5B57}, {0x1F212, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53CC}, {0x1F213, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x30C7}, {0x1F214, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E8C}, {0x1F215, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x591A}, {0x1F216, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x89E3}, {0x1F217, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5929}, {0x1F218, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4EA4}, {0x1F219, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6620}, {0x1F21A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7121}, {0x1F21B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6599}, {0x1F21C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x524D}, {0x1F21D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F8C}, {0x1F21E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x518D}, {0x1F21F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x65B0}, {0x1F220, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x521D}, {0x1F221, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7D42}, {0x1F222, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x751F}, {0x1F223, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8CA9}, {0x1F224, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x58F0}, {0x1F225, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5439}, {0x1F226, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6F14}, {0x1F227, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6295}, {0x1F228, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6355}, {0x1F229, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E00}, {0x1F22A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E09}, {0x1F22B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x904A}, {0x1F22C, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5DE6}, {0x1F22D, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x4E2D}, {0x1F22E, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53F3}, {0x1F22F, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6307}, {0x1F230, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x8D70}, {0x1F231, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6253}, {0x1F232, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7981}, {0x1F233, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7A7A}, {0x1F234, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5408}, {0x1F235, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6E80}, {0x1F236, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6709}, {0x1F237, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x6708}, {0x1F238, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x7533}, {0x1F239, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5272}, {0x1F23A, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x55B6}, {0x1F23B, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x914D}, {0x1F240, 0, 3 | DECOMP_COMPAT, 4960}, {0x1F241, 0, 3 | DECOMP_COMPAT, 4963}, {0x1F242, 0, 3 | DECOMP_COMPAT, 4966}, {0x1F243, 0, 3 | DECOMP_COMPAT, 4969}, {0x1F244, 0, 3 | DECOMP_COMPAT, 4972}, {0x1F245, 0, 3 | DECOMP_COMPAT, 4975}, {0x1F246, 0, 3 | DECOMP_COMPAT, 4978}, {0x1F247, 0, 3 | DECOMP_COMPAT, 4981}, {0x1F248, 0, 3 | DECOMP_COMPAT, 4984}, {0x1F250, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x5F97}, {0x1F251, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x53EF}, {0x1FBF0, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0030}, {0x1FBF1, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0031}, {0x1FBF2, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0032}, {0x1FBF3, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0033}, {0x1FBF4, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0034}, {0x1FBF5, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0035}, {0x1FBF6, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0036}, {0x1FBF7, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0037}, {0x1FBF8, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0038}, {0x1FBF9, 0, 1 | DECOMP_COMPAT | DECOMP_INLINE, 0x0039}, {0x2F800, 0, 1 | DECOMP_INLINE, 0x4E3D}, {0x2F801, 0, 1 | DECOMP_INLINE, 0x4E38}, {0x2F802, 0, 1 | DECOMP_INLINE, 0x4E41}, {0x2F803, 0, 1, 4987}, {0x2F804, 0, 1 | DECOMP_INLINE, 0x4F60}, {0x2F805, 0, 1 | DECOMP_INLINE, 0x4FAE}, {0x2F806, 0, 1 | DECOMP_INLINE, 0x4FBB}, {0x2F807, 0, 1 | DECOMP_INLINE, 0x5002}, {0x2F808, 0, 1 | DECOMP_INLINE, 0x507A}, {0x2F809, 0, 1 | DECOMP_INLINE, 0x5099}, {0x2F80A, 0, 1 | DECOMP_INLINE, 0x50E7}, {0x2F80B, 0, 1 | DECOMP_INLINE, 0x50CF}, {0x2F80C, 0, 1 | DECOMP_INLINE, 0x349E}, {0x2F80D, 0, 1, 4988}, {0x2F80E, 0, 1 | DECOMP_INLINE, 0x514D}, {0x2F80F, 0, 1 | DECOMP_INLINE, 0x5154}, {0x2F810, 0, 1 | DECOMP_INLINE, 0x5164}, {0x2F811, 0, 1 | DECOMP_INLINE, 0x5177}, {0x2F812, 0, 1, 4989}, {0x2F813, 0, 1 | DECOMP_INLINE, 0x34B9}, {0x2F814, 0, 1 | DECOMP_INLINE, 0x5167}, {0x2F815, 0, 1 | DECOMP_INLINE, 0x518D}, {0x2F816, 0, 1, 4990}, {0x2F817, 0, 1 | DECOMP_INLINE, 0x5197}, {0x2F818, 0, 1 | DECOMP_INLINE, 0x51A4}, {0x2F819, 0, 1 | DECOMP_INLINE, 0x4ECC}, {0x2F81A, 0, 1 | DECOMP_INLINE, 0x51AC}, {0x2F81B, 0, 1 | DECOMP_INLINE, 0x51B5}, {0x2F81C, 0, 1, 4991}, {0x2F81D, 0, 1 | DECOMP_INLINE, 0x51F5}, {0x2F81E, 0, 1 | DECOMP_INLINE, 0x5203}, {0x2F81F, 0, 1 | DECOMP_INLINE, 0x34DF}, {0x2F820, 0, 1 | DECOMP_INLINE, 0x523B}, {0x2F821, 0, 1 | DECOMP_INLINE, 0x5246}, {0x2F822, 0, 1 | DECOMP_INLINE, 0x5272}, {0x2F823, 0, 1 | DECOMP_INLINE, 0x5277}, {0x2F824, 0, 1 | DECOMP_INLINE, 0x3515}, {0x2F825, 0, 1 | DECOMP_INLINE, 0x52C7}, {0x2F826, 0, 1 | DECOMP_INLINE, 0x52C9}, {0x2F827, 0, 1 | DECOMP_INLINE, 0x52E4}, {0x2F828, 0, 1 | DECOMP_INLINE, 0x52FA}, {0x2F829, 0, 1 | DECOMP_INLINE, 0x5305}, {0x2F82A, 0, 1 | DECOMP_INLINE, 0x5306}, {0x2F82B, 0, 1 | DECOMP_INLINE, 0x5317}, {0x2F82C, 0, 1 | DECOMP_INLINE, 0x5349}, {0x2F82D, 0, 1 | DECOMP_INLINE, 0x5351}, {0x2F82E, 0, 1 | DECOMP_INLINE, 0x535A}, {0x2F82F, 0, 1 | DECOMP_INLINE, 0x5373}, {0x2F830, 0, 1 | DECOMP_INLINE, 0x537D}, {0x2F831, 0, 1 | DECOMP_INLINE, 0x537F}, {0x2F832, 0, 1 | DECOMP_INLINE, 0x537F}, {0x2F833, 0, 1 | DECOMP_INLINE, 0x537F}, {0x2F834, 0, 1, 4992}, {0x2F835, 0, 1 | DECOMP_INLINE, 0x7070}, {0x2F836, 0, 1 | DECOMP_INLINE, 0x53CA}, {0x2F837, 0, 1 | DECOMP_INLINE, 0x53DF}, {0x2F838, 0, 1, 4993}, {0x2F839, 0, 1 | DECOMP_INLINE, 0x53EB}, {0x2F83A, 0, 1 | DECOMP_INLINE, 0x53F1}, {0x2F83B, 0, 1 | DECOMP_INLINE, 0x5406}, {0x2F83C, 0, 1 | DECOMP_INLINE, 0x549E}, {0x2F83D, 0, 1 | DECOMP_INLINE, 0x5438}, {0x2F83E, 0, 1 | DECOMP_INLINE, 0x5448}, {0x2F83F, 0, 1 | DECOMP_INLINE, 0x5468}, {0x2F840, 0, 1 | DECOMP_INLINE, 0x54A2}, {0x2F841, 0, 1 | DECOMP_INLINE, 0x54F6}, {0x2F842, 0, 1 | DECOMP_INLINE, 0x5510}, {0x2F843, 0, 1 | DECOMP_INLINE, 0x5553}, {0x2F844, 0, 1 | DECOMP_INLINE, 0x5563}, {0x2F845, 0, 1 | DECOMP_INLINE, 0x5584}, {0x2F846, 0, 1 | DECOMP_INLINE, 0x5584}, {0x2F847, 0, 1 | DECOMP_INLINE, 0x5599}, {0x2F848, 0, 1 | DECOMP_INLINE, 0x55AB}, {0x2F849, 0, 1 | DECOMP_INLINE, 0x55B3}, {0x2F84A, 0, 1 | DECOMP_INLINE, 0x55C2}, {0x2F84B, 0, 1 | DECOMP_INLINE, 0x5716}, {0x2F84C, 0, 1 | DECOMP_INLINE, 0x5606}, {0x2F84D, 0, 1 | DECOMP_INLINE, 0x5717}, {0x2F84E, 0, 1 | DECOMP_INLINE, 0x5651}, {0x2F84F, 0, 1 | DECOMP_INLINE, 0x5674}, {0x2F850, 0, 1 | DECOMP_INLINE, 0x5207}, {0x2F851, 0, 1 | DECOMP_INLINE, 0x58EE}, {0x2F852, 0, 1 | DECOMP_INLINE, 0x57CE}, {0x2F853, 0, 1 | DECOMP_INLINE, 0x57F4}, {0x2F854, 0, 1 | DECOMP_INLINE, 0x580D}, {0x2F855, 0, 1 | DECOMP_INLINE, 0x578B}, {0x2F856, 0, 1 | DECOMP_INLINE, 0x5832}, {0x2F857, 0, 1 | DECOMP_INLINE, 0x5831}, {0x2F858, 0, 1 | DECOMP_INLINE, 0x58AC}, {0x2F859, 0, 1, 4994}, {0x2F85A, 0, 1 | DECOMP_INLINE, 0x58F2}, {0x2F85B, 0, 1 | DECOMP_INLINE, 0x58F7}, {0x2F85C, 0, 1 | DECOMP_INLINE, 0x5906}, {0x2F85D, 0, 1 | DECOMP_INLINE, 0x591A}, {0x2F85E, 0, 1 | DECOMP_INLINE, 0x5922}, {0x2F85F, 0, 1 | DECOMP_INLINE, 0x5962}, {0x2F860, 0, 1, 4995}, {0x2F861, 0, 1, 4996}, {0x2F862, 0, 1 | DECOMP_INLINE, 0x59EC}, {0x2F863, 0, 1 | DECOMP_INLINE, 0x5A1B}, {0x2F864, 0, 1 | DECOMP_INLINE, 0x5A27}, {0x2F865, 0, 1 | DECOMP_INLINE, 0x59D8}, {0x2F866, 0, 1 | DECOMP_INLINE, 0x5A66}, {0x2F867, 0, 1 | DECOMP_INLINE, 0x36EE}, {0x2F868, 0, 1 | DECOMP_INLINE, 0x36FC}, {0x2F869, 0, 1 | DECOMP_INLINE, 0x5B08}, {0x2F86A, 0, 1 | DECOMP_INLINE, 0x5B3E}, {0x2F86B, 0, 1 | DECOMP_INLINE, 0x5B3E}, {0x2F86C, 0, 1, 4997}, {0x2F86D, 0, 1 | DECOMP_INLINE, 0x5BC3}, {0x2F86E, 0, 1 | DECOMP_INLINE, 0x5BD8}, {0x2F86F, 0, 1 | DECOMP_INLINE, 0x5BE7}, {0x2F870, 0, 1 | DECOMP_INLINE, 0x5BF3}, {0x2F871, 0, 1, 4998}, {0x2F872, 0, 1 | DECOMP_INLINE, 0x5BFF}, {0x2F873, 0, 1 | DECOMP_INLINE, 0x5C06}, {0x2F874, 0, 1 | DECOMP_INLINE, 0x5F53}, {0x2F875, 0, 1 | DECOMP_INLINE, 0x5C22}, {0x2F876, 0, 1 | DECOMP_INLINE, 0x3781}, {0x2F877, 0, 1 | DECOMP_INLINE, 0x5C60}, {0x2F878, 0, 1 | DECOMP_INLINE, 0x5C6E}, {0x2F879, 0, 1 | DECOMP_INLINE, 0x5CC0}, {0x2F87A, 0, 1 | DECOMP_INLINE, 0x5C8D}, {0x2F87B, 0, 1, 4999}, {0x2F87C, 0, 1 | DECOMP_INLINE, 0x5D43}, {0x2F87D, 0, 1, 5000}, {0x2F87E, 0, 1 | DECOMP_INLINE, 0x5D6E}, {0x2F87F, 0, 1 | DECOMP_INLINE, 0x5D6B}, {0x2F880, 0, 1 | DECOMP_INLINE, 0x5D7C}, {0x2F881, 0, 1 | DECOMP_INLINE, 0x5DE1}, {0x2F882, 0, 1 | DECOMP_INLINE, 0x5DE2}, {0x2F883, 0, 1 | DECOMP_INLINE, 0x382F}, {0x2F884, 0, 1 | DECOMP_INLINE, 0x5DFD}, {0x2F885, 0, 1 | DECOMP_INLINE, 0x5E28}, {0x2F886, 0, 1 | DECOMP_INLINE, 0x5E3D}, {0x2F887, 0, 1 | DECOMP_INLINE, 0x5E69}, {0x2F888, 0, 1 | DECOMP_INLINE, 0x3862}, {0x2F889, 0, 1, 5001}, {0x2F88A, 0, 1 | DECOMP_INLINE, 0x387C}, {0x2F88B, 0, 1 | DECOMP_INLINE, 0x5EB0}, {0x2F88C, 0, 1 | DECOMP_INLINE, 0x5EB3}, {0x2F88D, 0, 1 | DECOMP_INLINE, 0x5EB6}, {0x2F88E, 0, 1 | DECOMP_INLINE, 0x5ECA}, {0x2F88F, 0, 1, 5002}, {0x2F890, 0, 1 | DECOMP_INLINE, 0x5EFE}, {0x2F891, 0, 1, 5003}, {0x2F892, 0, 1, 5004}, {0x2F893, 0, 1 | DECOMP_INLINE, 0x8201}, {0x2F894, 0, 1 | DECOMP_INLINE, 0x5F22}, {0x2F895, 0, 1 | DECOMP_INLINE, 0x5F22}, {0x2F896, 0, 1 | DECOMP_INLINE, 0x38C7}, {0x2F897, 0, 1, 5005}, {0x2F898, 0, 1, 5006}, {0x2F899, 0, 1 | DECOMP_INLINE, 0x5F62}, {0x2F89A, 0, 1 | DECOMP_INLINE, 0x5F6B}, {0x2F89B, 0, 1 | DECOMP_INLINE, 0x38E3}, {0x2F89C, 0, 1 | DECOMP_INLINE, 0x5F9A}, {0x2F89D, 0, 1 | DECOMP_INLINE, 0x5FCD}, {0x2F89E, 0, 1 | DECOMP_INLINE, 0x5FD7}, {0x2F89F, 0, 1 | DECOMP_INLINE, 0x5FF9}, {0x2F8A0, 0, 1 | DECOMP_INLINE, 0x6081}, {0x2F8A1, 0, 1 | DECOMP_INLINE, 0x393A}, {0x2F8A2, 0, 1 | DECOMP_INLINE, 0x391C}, {0x2F8A3, 0, 1 | DECOMP_INLINE, 0x6094}, {0x2F8A4, 0, 1, 5007}, {0x2F8A5, 0, 1 | DECOMP_INLINE, 0x60C7}, {0x2F8A6, 0, 1 | DECOMP_INLINE, 0x6148}, {0x2F8A7, 0, 1 | DECOMP_INLINE, 0x614C}, {0x2F8A8, 0, 1 | DECOMP_INLINE, 0x614E}, {0x2F8A9, 0, 1 | DECOMP_INLINE, 0x614C}, {0x2F8AA, 0, 1 | DECOMP_INLINE, 0x617A}, {0x2F8AB, 0, 1 | DECOMP_INLINE, 0x618E}, {0x2F8AC, 0, 1 | DECOMP_INLINE, 0x61B2}, {0x2F8AD, 0, 1 | DECOMP_INLINE, 0x61A4}, {0x2F8AE, 0, 1 | DECOMP_INLINE, 0x61AF}, {0x2F8AF, 0, 1 | DECOMP_INLINE, 0x61DE}, {0x2F8B0, 0, 1 | DECOMP_INLINE, 0x61F2}, {0x2F8B1, 0, 1 | DECOMP_INLINE, 0x61F6}, {0x2F8B2, 0, 1 | DECOMP_INLINE, 0x6210}, {0x2F8B3, 0, 1 | DECOMP_INLINE, 0x621B}, {0x2F8B4, 0, 1 | DECOMP_INLINE, 0x625D}, {0x2F8B5, 0, 1 | DECOMP_INLINE, 0x62B1}, {0x2F8B6, 0, 1 | DECOMP_INLINE, 0x62D4}, {0x2F8B7, 0, 1 | DECOMP_INLINE, 0x6350}, {0x2F8B8, 0, 1, 5008}, {0x2F8B9, 0, 1 | DECOMP_INLINE, 0x633D}, {0x2F8BA, 0, 1 | DECOMP_INLINE, 0x62FC}, {0x2F8BB, 0, 1 | DECOMP_INLINE, 0x6368}, {0x2F8BC, 0, 1 | DECOMP_INLINE, 0x6383}, {0x2F8BD, 0, 1 | DECOMP_INLINE, 0x63E4}, {0x2F8BE, 0, 1, 5009}, {0x2F8BF, 0, 1 | DECOMP_INLINE, 0x6422}, {0x2F8C0, 0, 1 | DECOMP_INLINE, 0x63C5}, {0x2F8C1, 0, 1 | DECOMP_INLINE, 0x63A9}, {0x2F8C2, 0, 1 | DECOMP_INLINE, 0x3A2E}, {0x2F8C3, 0, 1 | DECOMP_INLINE, 0x6469}, {0x2F8C4, 0, 1 | DECOMP_INLINE, 0x647E}, {0x2F8C5, 0, 1 | DECOMP_INLINE, 0x649D}, {0x2F8C6, 0, 1 | DECOMP_INLINE, 0x6477}, {0x2F8C7, 0, 1 | DECOMP_INLINE, 0x3A6C}, {0x2F8C8, 0, 1 | DECOMP_INLINE, 0x654F}, {0x2F8C9, 0, 1 | DECOMP_INLINE, 0x656C}, {0x2F8CA, 0, 1, 5010}, {0x2F8CB, 0, 1 | DECOMP_INLINE, 0x65E3}, {0x2F8CC, 0, 1 | DECOMP_INLINE, 0x66F8}, {0x2F8CD, 0, 1 | DECOMP_INLINE, 0x6649}, {0x2F8CE, 0, 1 | DECOMP_INLINE, 0x3B19}, {0x2F8CF, 0, 1 | DECOMP_INLINE, 0x6691}, {0x2F8D0, 0, 1 | DECOMP_INLINE, 0x3B08}, {0x2F8D1, 0, 1 | DECOMP_INLINE, 0x3AE4}, {0x2F8D2, 0, 1 | DECOMP_INLINE, 0x5192}, {0x2F8D3, 0, 1 | DECOMP_INLINE, 0x5195}, {0x2F8D4, 0, 1 | DECOMP_INLINE, 0x6700}, {0x2F8D5, 0, 1 | DECOMP_INLINE, 0x669C}, {0x2F8D6, 0, 1 | DECOMP_INLINE, 0x80AD}, {0x2F8D7, 0, 1 | DECOMP_INLINE, 0x43D9}, {0x2F8D8, 0, 1 | DECOMP_INLINE, 0x6717}, {0x2F8D9, 0, 1 | DECOMP_INLINE, 0x671B}, {0x2F8DA, 0, 1 | DECOMP_INLINE, 0x6721}, {0x2F8DB, 0, 1 | DECOMP_INLINE, 0x675E}, {0x2F8DC, 0, 1 | DECOMP_INLINE, 0x6753}, {0x2F8DD, 0, 1, 5011}, {0x2F8DE, 0, 1 | DECOMP_INLINE, 0x3B49}, {0x2F8DF, 0, 1 | DECOMP_INLINE, 0x67FA}, {0x2F8E0, 0, 1 | DECOMP_INLINE, 0x6785}, {0x2F8E1, 0, 1 | DECOMP_INLINE, 0x6852}, {0x2F8E2, 0, 1 | DECOMP_INLINE, 0x6885}, {0x2F8E3, 0, 1, 5012}, {0x2F8E4, 0, 1 | DECOMP_INLINE, 0x688E}, {0x2F8E5, 0, 1 | DECOMP_INLINE, 0x681F}, {0x2F8E6, 0, 1 | DECOMP_INLINE, 0x6914}, {0x2F8E7, 0, 1 | DECOMP_INLINE, 0x3B9D}, {0x2F8E8, 0, 1 | DECOMP_INLINE, 0x6942}, {0x2F8E9, 0, 1 | DECOMP_INLINE, 0x69A3}, {0x2F8EA, 0, 1 | DECOMP_INLINE, 0x69EA}, {0x2F8EB, 0, 1 | DECOMP_INLINE, 0x6AA8}, {0x2F8EC, 0, 1, 5013}, {0x2F8ED, 0, 1 | DECOMP_INLINE, 0x6ADB}, {0x2F8EE, 0, 1 | DECOMP_INLINE, 0x3C18}, {0x2F8EF, 0, 1 | DECOMP_INLINE, 0x6B21}, {0x2F8F0, 0, 1, 5014}, {0x2F8F1, 0, 1 | DECOMP_INLINE, 0x6B54}, {0x2F8F2, 0, 1 | DECOMP_INLINE, 0x3C4E}, {0x2F8F3, 0, 1 | DECOMP_INLINE, 0x6B72}, {0x2F8F4, 0, 1 | DECOMP_INLINE, 0x6B9F}, {0x2F8F5, 0, 1 | DECOMP_INLINE, 0x6BBA}, {0x2F8F6, 0, 1 | DECOMP_INLINE, 0x6BBB}, {0x2F8F7, 0, 1, 5015}, {0x2F8F8, 0, 1, 5016}, {0x2F8F9, 0, 1, 5017}, {0x2F8FA, 0, 1 | DECOMP_INLINE, 0x6C4E}, {0x2F8FB, 0, 1, 5018}, {0x2F8FC, 0, 1 | DECOMP_INLINE, 0x6CBF}, {0x2F8FD, 0, 1 | DECOMP_INLINE, 0x6CCD}, {0x2F8FE, 0, 1 | DECOMP_INLINE, 0x6C67}, {0x2F8FF, 0, 1 | DECOMP_INLINE, 0x6D16}, {0x2F900, 0, 1 | DECOMP_INLINE, 0x6D3E}, {0x2F901, 0, 1 | DECOMP_INLINE, 0x6D77}, {0x2F902, 0, 1 | DECOMP_INLINE, 0x6D41}, {0x2F903, 0, 1 | DECOMP_INLINE, 0x6D69}, {0x2F904, 0, 1 | DECOMP_INLINE, 0x6D78}, {0x2F905, 0, 1 | DECOMP_INLINE, 0x6D85}, {0x2F906, 0, 1, 5019}, {0x2F907, 0, 1 | DECOMP_INLINE, 0x6D34}, {0x2F908, 0, 1 | DECOMP_INLINE, 0x6E2F}, {0x2F909, 0, 1 | DECOMP_INLINE, 0x6E6E}, {0x2F90A, 0, 1 | DECOMP_INLINE, 0x3D33}, {0x2F90B, 0, 1 | DECOMP_INLINE, 0x6ECB}, {0x2F90C, 0, 1 | DECOMP_INLINE, 0x6EC7}, {0x2F90D, 0, 1, 5020}, {0x2F90E, 0, 1 | DECOMP_INLINE, 0x6DF9}, {0x2F90F, 0, 1 | DECOMP_INLINE, 0x6F6E}, {0x2F910, 0, 1, 5021}, {0x2F911, 0, 1, 5022}, {0x2F912, 0, 1 | DECOMP_INLINE, 0x6FC6}, {0x2F913, 0, 1 | DECOMP_INLINE, 0x7039}, {0x2F914, 0, 1 | DECOMP_INLINE, 0x701E}, {0x2F915, 0, 1 | DECOMP_INLINE, 0x701B}, {0x2F916, 0, 1 | DECOMP_INLINE, 0x3D96}, {0x2F917, 0, 1 | DECOMP_INLINE, 0x704A}, {0x2F918, 0, 1 | DECOMP_INLINE, 0x707D}, {0x2F919, 0, 1 | DECOMP_INLINE, 0x7077}, {0x2F91A, 0, 1 | DECOMP_INLINE, 0x70AD}, {0x2F91B, 0, 1, 5023}, {0x2F91C, 0, 1 | DECOMP_INLINE, 0x7145}, {0x2F91D, 0, 1, 5024}, {0x2F91E, 0, 1 | DECOMP_INLINE, 0x719C}, {0x2F91F, 0, 1, 5025}, {0x2F920, 0, 1 | DECOMP_INLINE, 0x7228}, {0x2F921, 0, 1 | DECOMP_INLINE, 0x7235}, {0x2F922, 0, 1 | DECOMP_INLINE, 0x7250}, {0x2F923, 0, 1, 5026}, {0x2F924, 0, 1 | DECOMP_INLINE, 0x7280}, {0x2F925, 0, 1 | DECOMP_INLINE, 0x7295}, {0x2F926, 0, 1, 5027}, {0x2F927, 0, 1, 5028}, {0x2F928, 0, 1 | DECOMP_INLINE, 0x737A}, {0x2F929, 0, 1 | DECOMP_INLINE, 0x738B}, {0x2F92A, 0, 1 | DECOMP_INLINE, 0x3EAC}, {0x2F92B, 0, 1 | DECOMP_INLINE, 0x73A5}, {0x2F92C, 0, 1 | DECOMP_INLINE, 0x3EB8}, {0x2F92D, 0, 1 | DECOMP_INLINE, 0x3EB8}, {0x2F92E, 0, 1 | DECOMP_INLINE, 0x7447}, {0x2F92F, 0, 1 | DECOMP_INLINE, 0x745C}, {0x2F930, 0, 1 | DECOMP_INLINE, 0x7471}, {0x2F931, 0, 1 | DECOMP_INLINE, 0x7485}, {0x2F932, 0, 1 | DECOMP_INLINE, 0x74CA}, {0x2F933, 0, 1 | DECOMP_INLINE, 0x3F1B}, {0x2F934, 0, 1 | DECOMP_INLINE, 0x7524}, {0x2F935, 0, 1, 5029}, {0x2F936, 0, 1 | DECOMP_INLINE, 0x753E}, {0x2F937, 0, 1, 5030}, {0x2F938, 0, 1 | DECOMP_INLINE, 0x7570}, {0x2F939, 0, 1, 5031}, {0x2F93A, 0, 1 | DECOMP_INLINE, 0x7610}, {0x2F93B, 0, 1, 5032}, {0x2F93C, 0, 1, 5033}, {0x2F93D, 0, 1, 5034}, {0x2F93E, 0, 1 | DECOMP_INLINE, 0x3FFC}, {0x2F93F, 0, 1 | DECOMP_INLINE, 0x4008}, {0x2F940, 0, 1 | DECOMP_INLINE, 0x76F4}, {0x2F941, 0, 1, 5035}, {0x2F942, 0, 1, 5036}, {0x2F943, 0, 1, 5037}, {0x2F944, 0, 1, 5038}, {0x2F945, 0, 1 | DECOMP_INLINE, 0x771E}, {0x2F946, 0, 1 | DECOMP_INLINE, 0x771F}, {0x2F947, 0, 1 | DECOMP_INLINE, 0x771F}, {0x2F948, 0, 1 | DECOMP_INLINE, 0x774A}, {0x2F949, 0, 1 | DECOMP_INLINE, 0x4039}, {0x2F94A, 0, 1 | DECOMP_INLINE, 0x778B}, {0x2F94B, 0, 1 | DECOMP_INLINE, 0x4046}, {0x2F94C, 0, 1 | DECOMP_INLINE, 0x4096}, {0x2F94D, 0, 1, 5039}, {0x2F94E, 0, 1 | DECOMP_INLINE, 0x784E}, {0x2F94F, 0, 1 | DECOMP_INLINE, 0x788C}, {0x2F950, 0, 1 | DECOMP_INLINE, 0x78CC}, {0x2F951, 0, 1 | DECOMP_INLINE, 0x40E3}, {0x2F952, 0, 1, 5040}, {0x2F953, 0, 1 | DECOMP_INLINE, 0x7956}, {0x2F954, 0, 1, 5041}, {0x2F955, 0, 1, 5042}, {0x2F956, 0, 1 | DECOMP_INLINE, 0x798F}, {0x2F957, 0, 1 | DECOMP_INLINE, 0x79EB}, {0x2F958, 0, 1 | DECOMP_INLINE, 0x412F}, {0x2F959, 0, 1 | DECOMP_INLINE, 0x7A40}, {0x2F95A, 0, 1 | DECOMP_INLINE, 0x7A4A}, {0x2F95B, 0, 1 | DECOMP_INLINE, 0x7A4F}, {0x2F95C, 0, 1, 5043}, {0x2F95D, 0, 1, 5044}, {0x2F95E, 0, 1, 5045}, {0x2F95F, 0, 1 | DECOMP_INLINE, 0x7AEE}, {0x2F960, 0, 1 | DECOMP_INLINE, 0x4202}, {0x2F961, 0, 1, 5046}, {0x2F962, 0, 1 | DECOMP_INLINE, 0x7BC6}, {0x2F963, 0, 1 | DECOMP_INLINE, 0x7BC9}, {0x2F964, 0, 1 | DECOMP_INLINE, 0x4227}, {0x2F965, 0, 1, 5047}, {0x2F966, 0, 1 | DECOMP_INLINE, 0x7CD2}, {0x2F967, 0, 1 | DECOMP_INLINE, 0x42A0}, {0x2F968, 0, 1 | DECOMP_INLINE, 0x7CE8}, {0x2F969, 0, 1 | DECOMP_INLINE, 0x7CE3}, {0x2F96A, 0, 1 | DECOMP_INLINE, 0x7D00}, {0x2F96B, 0, 1, 5048}, {0x2F96C, 0, 1 | DECOMP_INLINE, 0x7D63}, {0x2F96D, 0, 1 | DECOMP_INLINE, 0x4301}, {0x2F96E, 0, 1 | DECOMP_INLINE, 0x7DC7}, {0x2F96F, 0, 1 | DECOMP_INLINE, 0x7E02}, {0x2F970, 0, 1 | DECOMP_INLINE, 0x7E45}, {0x2F971, 0, 1 | DECOMP_INLINE, 0x4334}, {0x2F972, 0, 1, 5049}, {0x2F973, 0, 1, 5050}, {0x2F974, 0, 1 | DECOMP_INLINE, 0x4359}, {0x2F975, 0, 1, 5051}, {0x2F976, 0, 1 | DECOMP_INLINE, 0x7F7A}, {0x2F977, 0, 1, 5052}, {0x2F978, 0, 1 | DECOMP_INLINE, 0x7F95}, {0x2F979, 0, 1 | DECOMP_INLINE, 0x7FFA}, {0x2F97A, 0, 1 | DECOMP_INLINE, 0x8005}, {0x2F97B, 0, 1, 5053}, {0x2F97C, 0, 1, 5054}, {0x2F97D, 0, 1 | DECOMP_INLINE, 0x8060}, {0x2F97E, 0, 1, 5055}, {0x2F97F, 0, 1 | DECOMP_INLINE, 0x8070}, {0x2F980, 0, 1, 5056}, {0x2F981, 0, 1 | DECOMP_INLINE, 0x43D5}, {0x2F982, 0, 1 | DECOMP_INLINE, 0x80B2}, {0x2F983, 0, 1 | DECOMP_INLINE, 0x8103}, {0x2F984, 0, 1 | DECOMP_INLINE, 0x440B}, {0x2F985, 0, 1 | DECOMP_INLINE, 0x813E}, {0x2F986, 0, 1 | DECOMP_INLINE, 0x5AB5}, {0x2F987, 0, 1, 5057}, {0x2F988, 0, 1, 5058}, {0x2F989, 0, 1, 5059}, {0x2F98A, 0, 1, 5060}, {0x2F98B, 0, 1 | DECOMP_INLINE, 0x8201}, {0x2F98C, 0, 1 | DECOMP_INLINE, 0x8204}, {0x2F98D, 0, 1 | DECOMP_INLINE, 0x8F9E}, {0x2F98E, 0, 1 | DECOMP_INLINE, 0x446B}, {0x2F98F, 0, 1 | DECOMP_INLINE, 0x8291}, {0x2F990, 0, 1 | DECOMP_INLINE, 0x828B}, {0x2F991, 0, 1 | DECOMP_INLINE, 0x829D}, {0x2F992, 0, 1 | DECOMP_INLINE, 0x52B3}, {0x2F993, 0, 1 | DECOMP_INLINE, 0x82B1}, {0x2F994, 0, 1 | DECOMP_INLINE, 0x82B3}, {0x2F995, 0, 1 | DECOMP_INLINE, 0x82BD}, {0x2F996, 0, 1 | DECOMP_INLINE, 0x82E6}, {0x2F997, 0, 1, 5061}, {0x2F998, 0, 1 | DECOMP_INLINE, 0x82E5}, {0x2F999, 0, 1 | DECOMP_INLINE, 0x831D}, {0x2F99A, 0, 1 | DECOMP_INLINE, 0x8363}, {0x2F99B, 0, 1 | DECOMP_INLINE, 0x83AD}, {0x2F99C, 0, 1 | DECOMP_INLINE, 0x8323}, {0x2F99D, 0, 1 | DECOMP_INLINE, 0x83BD}, {0x2F99E, 0, 1 | DECOMP_INLINE, 0x83E7}, {0x2F99F, 0, 1 | DECOMP_INLINE, 0x8457}, {0x2F9A0, 0, 1 | DECOMP_INLINE, 0x8353}, {0x2F9A1, 0, 1 | DECOMP_INLINE, 0x83CA}, {0x2F9A2, 0, 1 | DECOMP_INLINE, 0x83CC}, {0x2F9A3, 0, 1 | DECOMP_INLINE, 0x83DC}, {0x2F9A4, 0, 1, 5062}, {0x2F9A5, 0, 1, 5063}, {0x2F9A6, 0, 1, 5064}, {0x2F9A7, 0, 1 | DECOMP_INLINE, 0x452B}, {0x2F9A8, 0, 1 | DECOMP_INLINE, 0x84F1}, {0x2F9A9, 0, 1 | DECOMP_INLINE, 0x84F3}, {0x2F9AA, 0, 1 | DECOMP_INLINE, 0x8516}, {0x2F9AB, 0, 1, 5065}, {0x2F9AC, 0, 1 | DECOMP_INLINE, 0x8564}, {0x2F9AD, 0, 1, 5066}, {0x2F9AE, 0, 1 | DECOMP_INLINE, 0x455D}, {0x2F9AF, 0, 1 | DECOMP_INLINE, 0x4561}, {0x2F9B0, 0, 1, 5067}, {0x2F9B1, 0, 1, 5068}, {0x2F9B2, 0, 1 | DECOMP_INLINE, 0x456B}, {0x2F9B3, 0, 1 | DECOMP_INLINE, 0x8650}, {0x2F9B4, 0, 1 | DECOMP_INLINE, 0x865C}, {0x2F9B5, 0, 1 | DECOMP_INLINE, 0x8667}, {0x2F9B6, 0, 1 | DECOMP_INLINE, 0x8669}, {0x2F9B7, 0, 1 | DECOMP_INLINE, 0x86A9}, {0x2F9B8, 0, 1 | DECOMP_INLINE, 0x8688}, {0x2F9B9, 0, 1 | DECOMP_INLINE, 0x870E}, {0x2F9BA, 0, 1 | DECOMP_INLINE, 0x86E2}, {0x2F9BB, 0, 1 | DECOMP_INLINE, 0x8779}, {0x2F9BC, 0, 1 | DECOMP_INLINE, 0x8728}, {0x2F9BD, 0, 1 | DECOMP_INLINE, 0x876B}, {0x2F9BE, 0, 1 | DECOMP_INLINE, 0x8786}, {0x2F9BF, 0, 1 | DECOMP_INLINE, 0x45D7}, {0x2F9C0, 0, 1 | DECOMP_INLINE, 0x87E1}, {0x2F9C1, 0, 1 | DECOMP_INLINE, 0x8801}, {0x2F9C2, 0, 1 | DECOMP_INLINE, 0x45F9}, {0x2F9C3, 0, 1 | DECOMP_INLINE, 0x8860}, {0x2F9C4, 0, 1 | DECOMP_INLINE, 0x8863}, {0x2F9C5, 0, 1, 5069}, {0x2F9C6, 0, 1 | DECOMP_INLINE, 0x88D7}, {0x2F9C7, 0, 1 | DECOMP_INLINE, 0x88DE}, {0x2F9C8, 0, 1 | DECOMP_INLINE, 0x4635}, {0x2F9C9, 0, 1 | DECOMP_INLINE, 0x88FA}, {0x2F9CA, 0, 1 | DECOMP_INLINE, 0x34BB}, {0x2F9CB, 0, 1, 5070}, {0x2F9CC, 0, 1, 5071}, {0x2F9CD, 0, 1 | DECOMP_INLINE, 0x46BE}, {0x2F9CE, 0, 1 | DECOMP_INLINE, 0x46C7}, {0x2F9CF, 0, 1 | DECOMP_INLINE, 0x8AA0}, {0x2F9D0, 0, 1 | DECOMP_INLINE, 0x8AED}, {0x2F9D1, 0, 1 | DECOMP_INLINE, 0x8B8A}, {0x2F9D2, 0, 1 | DECOMP_INLINE, 0x8C55}, {0x2F9D3, 0, 1, 5072}, {0x2F9D4, 0, 1 | DECOMP_INLINE, 0x8CAB}, {0x2F9D5, 0, 1 | DECOMP_INLINE, 0x8CC1}, {0x2F9D6, 0, 1 | DECOMP_INLINE, 0x8D1B}, {0x2F9D7, 0, 1 | DECOMP_INLINE, 0x8D77}, {0x2F9D8, 0, 1, 5073}, {0x2F9D9, 0, 1, 5074}, {0x2F9DA, 0, 1 | DECOMP_INLINE, 0x8DCB}, {0x2F9DB, 0, 1 | DECOMP_INLINE, 0x8DBC}, {0x2F9DC, 0, 1 | DECOMP_INLINE, 0x8DF0}, {0x2F9DD, 0, 1, 5075}, {0x2F9DE, 0, 1 | DECOMP_INLINE, 0x8ED4}, {0x2F9DF, 0, 1 | DECOMP_INLINE, 0x8F38}, {0x2F9E0, 0, 1, 5076}, {0x2F9E1, 0, 1, 5077}, {0x2F9E2, 0, 1 | DECOMP_INLINE, 0x9094}, {0x2F9E3, 0, 1 | DECOMP_INLINE, 0x90F1}, {0x2F9E4, 0, 1 | DECOMP_INLINE, 0x9111}, {0x2F9E5, 0, 1, 5078}, {0x2F9E6, 0, 1 | DECOMP_INLINE, 0x911B}, {0x2F9E7, 0, 1 | DECOMP_INLINE, 0x9238}, {0x2F9E8, 0, 1 | DECOMP_INLINE, 0x92D7}, {0x2F9E9, 0, 1 | DECOMP_INLINE, 0x92D8}, {0x2F9EA, 0, 1 | DECOMP_INLINE, 0x927C}, {0x2F9EB, 0, 1 | DECOMP_INLINE, 0x93F9}, {0x2F9EC, 0, 1 | DECOMP_INLINE, 0x9415}, {0x2F9ED, 0, 1, 5079}, {0x2F9EE, 0, 1 | DECOMP_INLINE, 0x958B}, {0x2F9EF, 0, 1 | DECOMP_INLINE, 0x4995}, {0x2F9F0, 0, 1 | DECOMP_INLINE, 0x95B7}, {0x2F9F1, 0, 1, 5080}, {0x2F9F2, 0, 1 | DECOMP_INLINE, 0x49E6}, {0x2F9F3, 0, 1 | DECOMP_INLINE, 0x96C3}, {0x2F9F4, 0, 1 | DECOMP_INLINE, 0x5DB2}, {0x2F9F5, 0, 1 | DECOMP_INLINE, 0x9723}, {0x2F9F6, 0, 1, 5081}, {0x2F9F7, 0, 1, 5082}, {0x2F9F8, 0, 1 | DECOMP_INLINE, 0x4A6E}, {0x2F9F9, 0, 1 | DECOMP_INLINE, 0x4A76}, {0x2F9FA, 0, 1 | DECOMP_INLINE, 0x97E0}, {0x2F9FB, 0, 1, 5083}, {0x2F9FC, 0, 1 | DECOMP_INLINE, 0x4AB2}, {0x2F9FD, 0, 1, 5084}, {0x2F9FE, 0, 1 | DECOMP_INLINE, 0x980B}, {0x2F9FF, 0, 1 | DECOMP_INLINE, 0x980B}, {0x2FA00, 0, 1 | DECOMP_INLINE, 0x9829}, {0x2FA01, 0, 1, 5085}, {0x2FA02, 0, 1 | DECOMP_INLINE, 0x98E2}, {0x2FA03, 0, 1 | DECOMP_INLINE, 0x4B33}, {0x2FA04, 0, 1 | DECOMP_INLINE, 0x9929}, {0x2FA05, 0, 1 | DECOMP_INLINE, 0x99A7}, {0x2FA06, 0, 1 | DECOMP_INLINE, 0x99C2}, {0x2FA07, 0, 1 | DECOMP_INLINE, 0x99FE}, {0x2FA08, 0, 1 | DECOMP_INLINE, 0x4BCE}, {0x2FA09, 0, 1, 5086}, {0x2FA0A, 0, 1 | DECOMP_INLINE, 0x9B12}, {0x2FA0B, 0, 1 | DECOMP_INLINE, 0x9C40}, {0x2FA0C, 0, 1 | DECOMP_INLINE, 0x9CFD}, {0x2FA0D, 0, 1 | DECOMP_INLINE, 0x4CCE}, {0x2FA0E, 0, 1 | DECOMP_INLINE, 0x4CED}, {0x2FA0F, 0, 1 | DECOMP_INLINE, 0x9D67}, {0x2FA10, 0, 1, 5087}, {0x2FA11, 0, 1 | DECOMP_INLINE, 0x4CF8}, {0x2FA12, 0, 1, 5088}, {0x2FA13, 0, 1, 5089}, {0x2FA14, 0, 1, 5090}, {0x2FA15, 0, 1 | DECOMP_INLINE, 0x9EBB}, {0x2FA16, 0, 1 | DECOMP_INLINE, 0x4D56}, {0x2FA17, 0, 1 | DECOMP_INLINE, 0x9EF9}, {0x2FA18, 0, 1 | DECOMP_INLINE, 0x9EFE}, {0x2FA19, 0, 1 | DECOMP_INLINE, 0x9F05}, {0x2FA1A, 0, 1 | DECOMP_INLINE, 0x9F0F}, {0x2FA1B, 0, 1 | DECOMP_INLINE, 0x9F16}, {0x2FA1C, 0, 1 | DECOMP_INLINE, 0x9F3B}, {0x2FA1D, 0, 1, 5091} }; /* codepoints array */ static const uint32 UnicodeDecomp_codepoints[5092] = { /* 0 */ 0x0020, 0x0308, /* 2 */ 0x0020, 0x0304, /* 4 */ 0x0020, 0x0301, /* 6 */ 0x0020, 0x0327, /* 8 */ 0x0031, 0x2044, 0x0034, /* 11 */ 0x0031, 0x2044, 0x0032, /* 14 */ 0x0033, 0x2044, 0x0034, /* 17 */ 0x0041, 0x0300, /* 19 */ 0x0041, 0x0301, /* 21 */ 0x0041, 0x0302, /* 23 */ 0x0041, 0x0303, /* 25 */ 0x0041, 0x0308, /* 27 */ 0x0041, 0x030A, /* 29 */ 0x0043, 0x0327, /* 31 */ 0x0045, 0x0300, /* 33 */ 0x0045, 0x0301, /* 35 */ 0x0045, 0x0302, /* 37 */ 0x0045, 0x0308, /* 39 */ 0x0049, 0x0300, /* 41 */ 0x0049, 0x0301, /* 43 */ 0x0049, 0x0302, /* 45 */ 0x0049, 0x0308, /* 47 */ 0x004E, 0x0303, /* 49 */ 0x004F, 0x0300, /* 51 */ 0x004F, 0x0301, /* 53 */ 0x004F, 0x0302, /* 55 */ 0x004F, 0x0303, /* 57 */ 0x004F, 0x0308, /* 59 */ 0x0055, 0x0300, /* 61 */ 0x0055, 0x0301, /* 63 */ 0x0055, 0x0302, /* 65 */ 0x0055, 0x0308, /* 67 */ 0x0059, 0x0301, /* 69 */ 0x0061, 0x0300, /* 71 */ 0x0061, 0x0301, /* 73 */ 0x0061, 0x0302, /* 75 */ 0x0061, 0x0303, /* 77 */ 0x0061, 0x0308, /* 79 */ 0x0061, 0x030A, /* 81 */ 0x0063, 0x0327, /* 83 */ 0x0065, 0x0300, /* 85 */ 0x0065, 0x0301, /* 87 */ 0x0065, 0x0302, /* 89 */ 0x0065, 0x0308, /* 91 */ 0x0069, 0x0300, /* 93 */ 0x0069, 0x0301, /* 95 */ 0x0069, 0x0302, /* 97 */ 0x0069, 0x0308, /* 99 */ 0x006E, 0x0303, /* 101 */ 0x006F, 0x0300, /* 103 */ 0x006F, 0x0301, /* 105 */ 0x006F, 0x0302, /* 107 */ 0x006F, 0x0303, /* 109 */ 0x006F, 0x0308, /* 111 */ 0x0075, 0x0300, /* 113 */ 0x0075, 0x0301, /* 115 */ 0x0075, 0x0302, /* 117 */ 0x0075, 0x0308, /* 119 */ 0x0079, 0x0301, /* 121 */ 0x0079, 0x0308, /* 123 */ 0x0041, 0x0304, /* 125 */ 0x0061, 0x0304, /* 127 */ 0x0041, 0x0306, /* 129 */ 0x0061, 0x0306, /* 131 */ 0x0041, 0x0328, /* 133 */ 0x0061, 0x0328, /* 135 */ 0x0043, 0x0301, /* 137 */ 0x0063, 0x0301, /* 139 */ 0x0043, 0x0302, /* 141 */ 0x0063, 0x0302, /* 143 */ 0x0043, 0x0307, /* 145 */ 0x0063, 0x0307, /* 147 */ 0x0043, 0x030C, /* 149 */ 0x0063, 0x030C, /* 151 */ 0x0044, 0x030C, /* 153 */ 0x0064, 0x030C, /* 155 */ 0x0045, 0x0304, /* 157 */ 0x0065, 0x0304, /* 159 */ 0x0045, 0x0306, /* 161 */ 0x0065, 0x0306, /* 163 */ 0x0045, 0x0307, /* 165 */ 0x0065, 0x0307, /* 167 */ 0x0045, 0x0328, /* 169 */ 0x0065, 0x0328, /* 171 */ 0x0045, 0x030C, /* 173 */ 0x0065, 0x030C, /* 175 */ 0x0047, 0x0302, /* 177 */ 0x0067, 0x0302, /* 179 */ 0x0047, 0x0306, /* 181 */ 0x0067, 0x0306, /* 183 */ 0x0047, 0x0307, /* 185 */ 0x0067, 0x0307, /* 187 */ 0x0047, 0x0327, /* 189 */ 0x0067, 0x0327, /* 191 */ 0x0048, 0x0302, /* 193 */ 0x0068, 0x0302, /* 195 */ 0x0049, 0x0303, /* 197 */ 0x0069, 0x0303, /* 199 */ 0x0049, 0x0304, /* 201 */ 0x0069, 0x0304, /* 203 */ 0x0049, 0x0306, /* 205 */ 0x0069, 0x0306, /* 207 */ 0x0049, 0x0328, /* 209 */ 0x0069, 0x0328, /* 211 */ 0x0049, 0x0307, /* 213 */ 0x0049, 0x004A, /* 215 */ 0x0069, 0x006A, /* 217 */ 0x004A, 0x0302, /* 219 */ 0x006A, 0x0302, /* 221 */ 0x004B, 0x0327, /* 223 */ 0x006B, 0x0327, /* 225 */ 0x004C, 0x0301, /* 227 */ 0x006C, 0x0301, /* 229 */ 0x004C, 0x0327, /* 231 */ 0x006C, 0x0327, /* 233 */ 0x004C, 0x030C, /* 235 */ 0x006C, 0x030C, /* 237 */ 0x004C, 0x00B7, /* 239 */ 0x006C, 0x00B7, /* 241 */ 0x004E, 0x0301, /* 243 */ 0x006E, 0x0301, /* 245 */ 0x004E, 0x0327, /* 247 */ 0x006E, 0x0327, /* 249 */ 0x004E, 0x030C, /* 251 */ 0x006E, 0x030C, /* 253 */ 0x02BC, 0x006E, /* 255 */ 0x004F, 0x0304, /* 257 */ 0x006F, 0x0304, /* 259 */ 0x004F, 0x0306, /* 261 */ 0x006F, 0x0306, /* 263 */ 0x004F, 0x030B, /* 265 */ 0x006F, 0x030B, /* 267 */ 0x0052, 0x0301, /* 269 */ 0x0072, 0x0301, /* 271 */ 0x0052, 0x0327, /* 273 */ 0x0072, 0x0327, /* 275 */ 0x0052, 0x030C, /* 277 */ 0x0072, 0x030C, /* 279 */ 0x0053, 0x0301, /* 281 */ 0x0073, 0x0301, /* 283 */ 0x0053, 0x0302, /* 285 */ 0x0073, 0x0302, /* 287 */ 0x0053, 0x0327, /* 289 */ 0x0073, 0x0327, /* 291 */ 0x0053, 0x030C, /* 293 */ 0x0073, 0x030C, /* 295 */ 0x0054, 0x0327, /* 297 */ 0x0074, 0x0327, /* 299 */ 0x0054, 0x030C, /* 301 */ 0x0074, 0x030C, /* 303 */ 0x0055, 0x0303, /* 305 */ 0x0075, 0x0303, /* 307 */ 0x0055, 0x0304, /* 309 */ 0x0075, 0x0304, /* 311 */ 0x0055, 0x0306, /* 313 */ 0x0075, 0x0306, /* 315 */ 0x0055, 0x030A, /* 317 */ 0x0075, 0x030A, /* 319 */ 0x0055, 0x030B, /* 321 */ 0x0075, 0x030B, /* 323 */ 0x0055, 0x0328, /* 325 */ 0x0075, 0x0328, /* 327 */ 0x0057, 0x0302, /* 329 */ 0x0077, 0x0302, /* 331 */ 0x0059, 0x0302, /* 333 */ 0x0079, 0x0302, /* 335 */ 0x0059, 0x0308, /* 337 */ 0x005A, 0x0301, /* 339 */ 0x007A, 0x0301, /* 341 */ 0x005A, 0x0307, /* 343 */ 0x007A, 0x0307, /* 345 */ 0x005A, 0x030C, /* 347 */ 0x007A, 0x030C, /* 349 */ 0x004F, 0x031B, /* 351 */ 0x006F, 0x031B, /* 353 */ 0x0055, 0x031B, /* 355 */ 0x0075, 0x031B, /* 357 */ 0x0044, 0x017D, /* 359 */ 0x0044, 0x017E, /* 361 */ 0x0064, 0x017E, /* 363 */ 0x004C, 0x004A, /* 365 */ 0x004C, 0x006A, /* 367 */ 0x006C, 0x006A, /* 369 */ 0x004E, 0x004A, /* 371 */ 0x004E, 0x006A, /* 373 */ 0x006E, 0x006A, /* 375 */ 0x0041, 0x030C, /* 377 */ 0x0061, 0x030C, /* 379 */ 0x0049, 0x030C, /* 381 */ 0x0069, 0x030C, /* 383 */ 0x004F, 0x030C, /* 385 */ 0x006F, 0x030C, /* 387 */ 0x0055, 0x030C, /* 389 */ 0x0075, 0x030C, /* 391 */ 0x00DC, 0x0304, /* 393 */ 0x00FC, 0x0304, /* 395 */ 0x00DC, 0x0301, /* 397 */ 0x00FC, 0x0301, /* 399 */ 0x00DC, 0x030C, /* 401 */ 0x00FC, 0x030C, /* 403 */ 0x00DC, 0x0300, /* 405 */ 0x00FC, 0x0300, /* 407 */ 0x00C4, 0x0304, /* 409 */ 0x00E4, 0x0304, /* 411 */ 0x0226, 0x0304, /* 413 */ 0x0227, 0x0304, /* 415 */ 0x00C6, 0x0304, /* 417 */ 0x00E6, 0x0304, /* 419 */ 0x0047, 0x030C, /* 421 */ 0x0067, 0x030C, /* 423 */ 0x004B, 0x030C, /* 425 */ 0x006B, 0x030C, /* 427 */ 0x004F, 0x0328, /* 429 */ 0x006F, 0x0328, /* 431 */ 0x01EA, 0x0304, /* 433 */ 0x01EB, 0x0304, /* 435 */ 0x01B7, 0x030C, /* 437 */ 0x0292, 0x030C, /* 439 */ 0x006A, 0x030C, /* 441 */ 0x0044, 0x005A, /* 443 */ 0x0044, 0x007A, /* 445 */ 0x0064, 0x007A, /* 447 */ 0x0047, 0x0301, /* 449 */ 0x0067, 0x0301, /* 451 */ 0x004E, 0x0300, /* 453 */ 0x006E, 0x0300, /* 455 */ 0x00C5, 0x0301, /* 457 */ 0x00E5, 0x0301, /* 459 */ 0x00C6, 0x0301, /* 461 */ 0x00E6, 0x0301, /* 463 */ 0x00D8, 0x0301, /* 465 */ 0x00F8, 0x0301, /* 467 */ 0x0041, 0x030F, /* 469 */ 0x0061, 0x030F, /* 471 */ 0x0041, 0x0311, /* 473 */ 0x0061, 0x0311, /* 475 */ 0x0045, 0x030F, /* 477 */ 0x0065, 0x030F, /* 479 */ 0x0045, 0x0311, /* 481 */ 0x0065, 0x0311, /* 483 */ 0x0049, 0x030F, /* 485 */ 0x0069, 0x030F, /* 487 */ 0x0049, 0x0311, /* 489 */ 0x0069, 0x0311, /* 491 */ 0x004F, 0x030F, /* 493 */ 0x006F, 0x030F, /* 495 */ 0x004F, 0x0311, /* 497 */ 0x006F, 0x0311, /* 499 */ 0x0052, 0x030F, /* 501 */ 0x0072, 0x030F, /* 503 */ 0x0052, 0x0311, /* 505 */ 0x0072, 0x0311, /* 507 */ 0x0055, 0x030F, /* 509 */ 0x0075, 0x030F, /* 511 */ 0x0055, 0x0311, /* 513 */ 0x0075, 0x0311, /* 515 */ 0x0053, 0x0326, /* 517 */ 0x0073, 0x0326, /* 519 */ 0x0054, 0x0326, /* 521 */ 0x0074, 0x0326, /* 523 */ 0x0048, 0x030C, /* 525 */ 0x0068, 0x030C, /* 527 */ 0x0041, 0x0307, /* 529 */ 0x0061, 0x0307, /* 531 */ 0x0045, 0x0327, /* 533 */ 0x0065, 0x0327, /* 535 */ 0x00D6, 0x0304, /* 537 */ 0x00F6, 0x0304, /* 539 */ 0x00D5, 0x0304, /* 541 */ 0x00F5, 0x0304, /* 543 */ 0x004F, 0x0307, /* 545 */ 0x006F, 0x0307, /* 547 */ 0x022E, 0x0304, /* 549 */ 0x022F, 0x0304, /* 551 */ 0x0059, 0x0304, /* 553 */ 0x0079, 0x0304, /* 555 */ 0x0020, 0x0306, /* 557 */ 0x0020, 0x0307, /* 559 */ 0x0020, 0x030A, /* 561 */ 0x0020, 0x0328, /* 563 */ 0x0020, 0x0303, /* 565 */ 0x0020, 0x030B, /* 567 */ 0x0308, 0x0301, /* 569 */ 0x0020, 0x0345, /* 571 */ 0x0020, 0x0301, /* 573 */ 0x00A8, 0x0301, /* 575 */ 0x0391, 0x0301, /* 577 */ 0x0395, 0x0301, /* 579 */ 0x0397, 0x0301, /* 581 */ 0x0399, 0x0301, /* 583 */ 0x039F, 0x0301, /* 585 */ 0x03A5, 0x0301, /* 587 */ 0x03A9, 0x0301, /* 589 */ 0x03CA, 0x0301, /* 591 */ 0x0399, 0x0308, /* 593 */ 0x03A5, 0x0308, /* 595 */ 0x03B1, 0x0301, /* 597 */ 0x03B5, 0x0301, /* 599 */ 0x03B7, 0x0301, /* 601 */ 0x03B9, 0x0301, /* 603 */ 0x03CB, 0x0301, /* 605 */ 0x03B9, 0x0308, /* 607 */ 0x03C5, 0x0308, /* 609 */ 0x03BF, 0x0301, /* 611 */ 0x03C5, 0x0301, /* 613 */ 0x03C9, 0x0301, /* 615 */ 0x03D2, 0x0301, /* 617 */ 0x03D2, 0x0308, /* 619 */ 0x0415, 0x0300, /* 621 */ 0x0415, 0x0308, /* 623 */ 0x0413, 0x0301, /* 625 */ 0x0406, 0x0308, /* 627 */ 0x041A, 0x0301, /* 629 */ 0x0418, 0x0300, /* 631 */ 0x0423, 0x0306, /* 633 */ 0x0418, 0x0306, /* 635 */ 0x0438, 0x0306, /* 637 */ 0x0435, 0x0300, /* 639 */ 0x0435, 0x0308, /* 641 */ 0x0433, 0x0301, /* 643 */ 0x0456, 0x0308, /* 645 */ 0x043A, 0x0301, /* 647 */ 0x0438, 0x0300, /* 649 */ 0x0443, 0x0306, /* 651 */ 0x0474, 0x030F, /* 653 */ 0x0475, 0x030F, /* 655 */ 0x0416, 0x0306, /* 657 */ 0x0436, 0x0306, /* 659 */ 0x0410, 0x0306, /* 661 */ 0x0430, 0x0306, /* 663 */ 0x0410, 0x0308, /* 665 */ 0x0430, 0x0308, /* 667 */ 0x0415, 0x0306, /* 669 */ 0x0435, 0x0306, /* 671 */ 0x04D8, 0x0308, /* 673 */ 0x04D9, 0x0308, /* 675 */ 0x0416, 0x0308, /* 677 */ 0x0436, 0x0308, /* 679 */ 0x0417, 0x0308, /* 681 */ 0x0437, 0x0308, /* 683 */ 0x0418, 0x0304, /* 685 */ 0x0438, 0x0304, /* 687 */ 0x0418, 0x0308, /* 689 */ 0x0438, 0x0308, /* 691 */ 0x041E, 0x0308, /* 693 */ 0x043E, 0x0308, /* 695 */ 0x04E8, 0x0308, /* 697 */ 0x04E9, 0x0308, /* 699 */ 0x042D, 0x0308, /* 701 */ 0x044D, 0x0308, /* 703 */ 0x0423, 0x0304, /* 705 */ 0x0443, 0x0304, /* 707 */ 0x0423, 0x0308, /* 709 */ 0x0443, 0x0308, /* 711 */ 0x0423, 0x030B, /* 713 */ 0x0443, 0x030B, /* 715 */ 0x0427, 0x0308, /* 717 */ 0x0447, 0x0308, /* 719 */ 0x042B, 0x0308, /* 721 */ 0x044B, 0x0308, /* 723 */ 0x0565, 0x0582, /* 725 */ 0x0627, 0x0653, /* 727 */ 0x0627, 0x0654, /* 729 */ 0x0648, 0x0654, /* 731 */ 0x0627, 0x0655, /* 733 */ 0x064A, 0x0654, /* 735 */ 0x0627, 0x0674, /* 737 */ 0x0648, 0x0674, /* 739 */ 0x06C7, 0x0674, /* 741 */ 0x064A, 0x0674, /* 743 */ 0x06D5, 0x0654, /* 745 */ 0x06C1, 0x0654, /* 747 */ 0x06D2, 0x0654, /* 749 */ 0x0928, 0x093C, /* 751 */ 0x0930, 0x093C, /* 753 */ 0x0933, 0x093C, /* 755 */ 0x0915, 0x093C, /* 757 */ 0x0916, 0x093C, /* 759 */ 0x0917, 0x093C, /* 761 */ 0x091C, 0x093C, /* 763 */ 0x0921, 0x093C, /* 765 */ 0x0922, 0x093C, /* 767 */ 0x092B, 0x093C, /* 769 */ 0x092F, 0x093C, /* 771 */ 0x09C7, 0x09BE, /* 773 */ 0x09C7, 0x09D7, /* 775 */ 0x09A1, 0x09BC, /* 777 */ 0x09A2, 0x09BC, /* 779 */ 0x09AF, 0x09BC, /* 781 */ 0x0A32, 0x0A3C, /* 783 */ 0x0A38, 0x0A3C, /* 785 */ 0x0A16, 0x0A3C, /* 787 */ 0x0A17, 0x0A3C, /* 789 */ 0x0A1C, 0x0A3C, /* 791 */ 0x0A2B, 0x0A3C, /* 793 */ 0x0B47, 0x0B56, /* 795 */ 0x0B47, 0x0B3E, /* 797 */ 0x0B47, 0x0B57, /* 799 */ 0x0B21, 0x0B3C, /* 801 */ 0x0B22, 0x0B3C, /* 803 */ 0x0B92, 0x0BD7, /* 805 */ 0x0BC6, 0x0BBE, /* 807 */ 0x0BC7, 0x0BBE, /* 809 */ 0x0BC6, 0x0BD7, /* 811 */ 0x0C46, 0x0C56, /* 813 */ 0x0CBF, 0x0CD5, /* 815 */ 0x0CC6, 0x0CD5, /* 817 */ 0x0CC6, 0x0CD6, /* 819 */ 0x0CC6, 0x0CC2, /* 821 */ 0x0CCA, 0x0CD5, /* 823 */ 0x0D46, 0x0D3E, /* 825 */ 0x0D47, 0x0D3E, /* 827 */ 0x0D46, 0x0D57, /* 829 */ 0x0DD9, 0x0DCA, /* 831 */ 0x0DD9, 0x0DCF, /* 833 */ 0x0DDC, 0x0DCA, /* 835 */ 0x0DD9, 0x0DDF, /* 837 */ 0x0E4D, 0x0E32, /* 839 */ 0x0ECD, 0x0EB2, /* 841 */ 0x0EAB, 0x0E99, /* 843 */ 0x0EAB, 0x0EA1, /* 845 */ 0x0F42, 0x0FB7, /* 847 */ 0x0F4C, 0x0FB7, /* 849 */ 0x0F51, 0x0FB7, /* 851 */ 0x0F56, 0x0FB7, /* 853 */ 0x0F5B, 0x0FB7, /* 855 */ 0x0F40, 0x0FB5, /* 857 */ 0x0F71, 0x0F72, /* 859 */ 0x0F71, 0x0F74, /* 861 */ 0x0FB2, 0x0F80, /* 863 */ 0x0FB2, 0x0F81, /* 865 */ 0x0FB3, 0x0F80, /* 867 */ 0x0FB3, 0x0F81, /* 869 */ 0x0F71, 0x0F80, /* 871 */ 0x0F92, 0x0FB7, /* 873 */ 0x0F9C, 0x0FB7, /* 875 */ 0x0FA1, 0x0FB7, /* 877 */ 0x0FA6, 0x0FB7, /* 879 */ 0x0FAB, 0x0FB7, /* 881 */ 0x0F90, 0x0FB5, /* 883 */ 0x1025, 0x102E, /* 885 */ 0x1B05, 0x1B35, /* 887 */ 0x1B07, 0x1B35, /* 889 */ 0x1B09, 0x1B35, /* 891 */ 0x1B0B, 0x1B35, /* 893 */ 0x1B0D, 0x1B35, /* 895 */ 0x1B11, 0x1B35, /* 897 */ 0x1B3A, 0x1B35, /* 899 */ 0x1B3C, 0x1B35, /* 901 */ 0x1B3E, 0x1B35, /* 903 */ 0x1B3F, 0x1B35, /* 905 */ 0x1B42, 0x1B35, /* 907 */ 0x0041, 0x0325, /* 909 */ 0x0061, 0x0325, /* 911 */ 0x0042, 0x0307, /* 913 */ 0x0062, 0x0307, /* 915 */ 0x0042, 0x0323, /* 917 */ 0x0062, 0x0323, /* 919 */ 0x0042, 0x0331, /* 921 */ 0x0062, 0x0331, /* 923 */ 0x00C7, 0x0301, /* 925 */ 0x00E7, 0x0301, /* 927 */ 0x0044, 0x0307, /* 929 */ 0x0064, 0x0307, /* 931 */ 0x0044, 0x0323, /* 933 */ 0x0064, 0x0323, /* 935 */ 0x0044, 0x0331, /* 937 */ 0x0064, 0x0331, /* 939 */ 0x0044, 0x0327, /* 941 */ 0x0064, 0x0327, /* 943 */ 0x0044, 0x032D, /* 945 */ 0x0064, 0x032D, /* 947 */ 0x0112, 0x0300, /* 949 */ 0x0113, 0x0300, /* 951 */ 0x0112, 0x0301, /* 953 */ 0x0113, 0x0301, /* 955 */ 0x0045, 0x032D, /* 957 */ 0x0065, 0x032D, /* 959 */ 0x0045, 0x0330, /* 961 */ 0x0065, 0x0330, /* 963 */ 0x0228, 0x0306, /* 965 */ 0x0229, 0x0306, /* 967 */ 0x0046, 0x0307, /* 969 */ 0x0066, 0x0307, /* 971 */ 0x0047, 0x0304, /* 973 */ 0x0067, 0x0304, /* 975 */ 0x0048, 0x0307, /* 977 */ 0x0068, 0x0307, /* 979 */ 0x0048, 0x0323, /* 981 */ 0x0068, 0x0323, /* 983 */ 0x0048, 0x0308, /* 985 */ 0x0068, 0x0308, /* 987 */ 0x0048, 0x0327, /* 989 */ 0x0068, 0x0327, /* 991 */ 0x0048, 0x032E, /* 993 */ 0x0068, 0x032E, /* 995 */ 0x0049, 0x0330, /* 997 */ 0x0069, 0x0330, /* 999 */ 0x00CF, 0x0301, /* 1001 */ 0x00EF, 0x0301, /* 1003 */ 0x004B, 0x0301, /* 1005 */ 0x006B, 0x0301, /* 1007 */ 0x004B, 0x0323, /* 1009 */ 0x006B, 0x0323, /* 1011 */ 0x004B, 0x0331, /* 1013 */ 0x006B, 0x0331, /* 1015 */ 0x004C, 0x0323, /* 1017 */ 0x006C, 0x0323, /* 1019 */ 0x1E36, 0x0304, /* 1021 */ 0x1E37, 0x0304, /* 1023 */ 0x004C, 0x0331, /* 1025 */ 0x006C, 0x0331, /* 1027 */ 0x004C, 0x032D, /* 1029 */ 0x006C, 0x032D, /* 1031 */ 0x004D, 0x0301, /* 1033 */ 0x006D, 0x0301, /* 1035 */ 0x004D, 0x0307, /* 1037 */ 0x006D, 0x0307, /* 1039 */ 0x004D, 0x0323, /* 1041 */ 0x006D, 0x0323, /* 1043 */ 0x004E, 0x0307, /* 1045 */ 0x006E, 0x0307, /* 1047 */ 0x004E, 0x0323, /* 1049 */ 0x006E, 0x0323, /* 1051 */ 0x004E, 0x0331, /* 1053 */ 0x006E, 0x0331, /* 1055 */ 0x004E, 0x032D, /* 1057 */ 0x006E, 0x032D, /* 1059 */ 0x00D5, 0x0301, /* 1061 */ 0x00F5, 0x0301, /* 1063 */ 0x00D5, 0x0308, /* 1065 */ 0x00F5, 0x0308, /* 1067 */ 0x014C, 0x0300, /* 1069 */ 0x014D, 0x0300, /* 1071 */ 0x014C, 0x0301, /* 1073 */ 0x014D, 0x0301, /* 1075 */ 0x0050, 0x0301, /* 1077 */ 0x0070, 0x0301, /* 1079 */ 0x0050, 0x0307, /* 1081 */ 0x0070, 0x0307, /* 1083 */ 0x0052, 0x0307, /* 1085 */ 0x0072, 0x0307, /* 1087 */ 0x0052, 0x0323, /* 1089 */ 0x0072, 0x0323, /* 1091 */ 0x1E5A, 0x0304, /* 1093 */ 0x1E5B, 0x0304, /* 1095 */ 0x0052, 0x0331, /* 1097 */ 0x0072, 0x0331, /* 1099 */ 0x0053, 0x0307, /* 1101 */ 0x0073, 0x0307, /* 1103 */ 0x0053, 0x0323, /* 1105 */ 0x0073, 0x0323, /* 1107 */ 0x015A, 0x0307, /* 1109 */ 0x015B, 0x0307, /* 1111 */ 0x0160, 0x0307, /* 1113 */ 0x0161, 0x0307, /* 1115 */ 0x1E62, 0x0307, /* 1117 */ 0x1E63, 0x0307, /* 1119 */ 0x0054, 0x0307, /* 1121 */ 0x0074, 0x0307, /* 1123 */ 0x0054, 0x0323, /* 1125 */ 0x0074, 0x0323, /* 1127 */ 0x0054, 0x0331, /* 1129 */ 0x0074, 0x0331, /* 1131 */ 0x0054, 0x032D, /* 1133 */ 0x0074, 0x032D, /* 1135 */ 0x0055, 0x0324, /* 1137 */ 0x0075, 0x0324, /* 1139 */ 0x0055, 0x0330, /* 1141 */ 0x0075, 0x0330, /* 1143 */ 0x0055, 0x032D, /* 1145 */ 0x0075, 0x032D, /* 1147 */ 0x0168, 0x0301, /* 1149 */ 0x0169, 0x0301, /* 1151 */ 0x016A, 0x0308, /* 1153 */ 0x016B, 0x0308, /* 1155 */ 0x0056, 0x0303, /* 1157 */ 0x0076, 0x0303, /* 1159 */ 0x0056, 0x0323, /* 1161 */ 0x0076, 0x0323, /* 1163 */ 0x0057, 0x0300, /* 1165 */ 0x0077, 0x0300, /* 1167 */ 0x0057, 0x0301, /* 1169 */ 0x0077, 0x0301, /* 1171 */ 0x0057, 0x0308, /* 1173 */ 0x0077, 0x0308, /* 1175 */ 0x0057, 0x0307, /* 1177 */ 0x0077, 0x0307, /* 1179 */ 0x0057, 0x0323, /* 1181 */ 0x0077, 0x0323, /* 1183 */ 0x0058, 0x0307, /* 1185 */ 0x0078, 0x0307, /* 1187 */ 0x0058, 0x0308, /* 1189 */ 0x0078, 0x0308, /* 1191 */ 0x0059, 0x0307, /* 1193 */ 0x0079, 0x0307, /* 1195 */ 0x005A, 0x0302, /* 1197 */ 0x007A, 0x0302, /* 1199 */ 0x005A, 0x0323, /* 1201 */ 0x007A, 0x0323, /* 1203 */ 0x005A, 0x0331, /* 1205 */ 0x007A, 0x0331, /* 1207 */ 0x0068, 0x0331, /* 1209 */ 0x0074, 0x0308, /* 1211 */ 0x0077, 0x030A, /* 1213 */ 0x0079, 0x030A, /* 1215 */ 0x0061, 0x02BE, /* 1217 */ 0x017F, 0x0307, /* 1219 */ 0x0041, 0x0323, /* 1221 */ 0x0061, 0x0323, /* 1223 */ 0x0041, 0x0309, /* 1225 */ 0x0061, 0x0309, /* 1227 */ 0x00C2, 0x0301, /* 1229 */ 0x00E2, 0x0301, /* 1231 */ 0x00C2, 0x0300, /* 1233 */ 0x00E2, 0x0300, /* 1235 */ 0x00C2, 0x0309, /* 1237 */ 0x00E2, 0x0309, /* 1239 */ 0x00C2, 0x0303, /* 1241 */ 0x00E2, 0x0303, /* 1243 */ 0x1EA0, 0x0302, /* 1245 */ 0x1EA1, 0x0302, /* 1247 */ 0x0102, 0x0301, /* 1249 */ 0x0103, 0x0301, /* 1251 */ 0x0102, 0x0300, /* 1253 */ 0x0103, 0x0300, /* 1255 */ 0x0102, 0x0309, /* 1257 */ 0x0103, 0x0309, /* 1259 */ 0x0102, 0x0303, /* 1261 */ 0x0103, 0x0303, /* 1263 */ 0x1EA0, 0x0306, /* 1265 */ 0x1EA1, 0x0306, /* 1267 */ 0x0045, 0x0323, /* 1269 */ 0x0065, 0x0323, /* 1271 */ 0x0045, 0x0309, /* 1273 */ 0x0065, 0x0309, /* 1275 */ 0x0045, 0x0303, /* 1277 */ 0x0065, 0x0303, /* 1279 */ 0x00CA, 0x0301, /* 1281 */ 0x00EA, 0x0301, /* 1283 */ 0x00CA, 0x0300, /* 1285 */ 0x00EA, 0x0300, /* 1287 */ 0x00CA, 0x0309, /* 1289 */ 0x00EA, 0x0309, /* 1291 */ 0x00CA, 0x0303, /* 1293 */ 0x00EA, 0x0303, /* 1295 */ 0x1EB8, 0x0302, /* 1297 */ 0x1EB9, 0x0302, /* 1299 */ 0x0049, 0x0309, /* 1301 */ 0x0069, 0x0309, /* 1303 */ 0x0049, 0x0323, /* 1305 */ 0x0069, 0x0323, /* 1307 */ 0x004F, 0x0323, /* 1309 */ 0x006F, 0x0323, /* 1311 */ 0x004F, 0x0309, /* 1313 */ 0x006F, 0x0309, /* 1315 */ 0x00D4, 0x0301, /* 1317 */ 0x00F4, 0x0301, /* 1319 */ 0x00D4, 0x0300, /* 1321 */ 0x00F4, 0x0300, /* 1323 */ 0x00D4, 0x0309, /* 1325 */ 0x00F4, 0x0309, /* 1327 */ 0x00D4, 0x0303, /* 1329 */ 0x00F4, 0x0303, /* 1331 */ 0x1ECC, 0x0302, /* 1333 */ 0x1ECD, 0x0302, /* 1335 */ 0x01A0, 0x0301, /* 1337 */ 0x01A1, 0x0301, /* 1339 */ 0x01A0, 0x0300, /* 1341 */ 0x01A1, 0x0300, /* 1343 */ 0x01A0, 0x0309, /* 1345 */ 0x01A1, 0x0309, /* 1347 */ 0x01A0, 0x0303, /* 1349 */ 0x01A1, 0x0303, /* 1351 */ 0x01A0, 0x0323, /* 1353 */ 0x01A1, 0x0323, /* 1355 */ 0x0055, 0x0323, /* 1357 */ 0x0075, 0x0323, /* 1359 */ 0x0055, 0x0309, /* 1361 */ 0x0075, 0x0309, /* 1363 */ 0x01AF, 0x0301, /* 1365 */ 0x01B0, 0x0301, /* 1367 */ 0x01AF, 0x0300, /* 1369 */ 0x01B0, 0x0300, /* 1371 */ 0x01AF, 0x0309, /* 1373 */ 0x01B0, 0x0309, /* 1375 */ 0x01AF, 0x0303, /* 1377 */ 0x01B0, 0x0303, /* 1379 */ 0x01AF, 0x0323, /* 1381 */ 0x01B0, 0x0323, /* 1383 */ 0x0059, 0x0300, /* 1385 */ 0x0079, 0x0300, /* 1387 */ 0x0059, 0x0323, /* 1389 */ 0x0079, 0x0323, /* 1391 */ 0x0059, 0x0309, /* 1393 */ 0x0079, 0x0309, /* 1395 */ 0x0059, 0x0303, /* 1397 */ 0x0079, 0x0303, /* 1399 */ 0x03B1, 0x0313, /* 1401 */ 0x03B1, 0x0314, /* 1403 */ 0x1F00, 0x0300, /* 1405 */ 0x1F01, 0x0300, /* 1407 */ 0x1F00, 0x0301, /* 1409 */ 0x1F01, 0x0301, /* 1411 */ 0x1F00, 0x0342, /* 1413 */ 0x1F01, 0x0342, /* 1415 */ 0x0391, 0x0313, /* 1417 */ 0x0391, 0x0314, /* 1419 */ 0x1F08, 0x0300, /* 1421 */ 0x1F09, 0x0300, /* 1423 */ 0x1F08, 0x0301, /* 1425 */ 0x1F09, 0x0301, /* 1427 */ 0x1F08, 0x0342, /* 1429 */ 0x1F09, 0x0342, /* 1431 */ 0x03B5, 0x0313, /* 1433 */ 0x03B5, 0x0314, /* 1435 */ 0x1F10, 0x0300, /* 1437 */ 0x1F11, 0x0300, /* 1439 */ 0x1F10, 0x0301, /* 1441 */ 0x1F11, 0x0301, /* 1443 */ 0x0395, 0x0313, /* 1445 */ 0x0395, 0x0314, /* 1447 */ 0x1F18, 0x0300, /* 1449 */ 0x1F19, 0x0300, /* 1451 */ 0x1F18, 0x0301, /* 1453 */ 0x1F19, 0x0301, /* 1455 */ 0x03B7, 0x0313, /* 1457 */ 0x03B7, 0x0314, /* 1459 */ 0x1F20, 0x0300, /* 1461 */ 0x1F21, 0x0300, /* 1463 */ 0x1F20, 0x0301, /* 1465 */ 0x1F21, 0x0301, /* 1467 */ 0x1F20, 0x0342, /* 1469 */ 0x1F21, 0x0342, /* 1471 */ 0x0397, 0x0313, /* 1473 */ 0x0397, 0x0314, /* 1475 */ 0x1F28, 0x0300, /* 1477 */ 0x1F29, 0x0300, /* 1479 */ 0x1F28, 0x0301, /* 1481 */ 0x1F29, 0x0301, /* 1483 */ 0x1F28, 0x0342, /* 1485 */ 0x1F29, 0x0342, /* 1487 */ 0x03B9, 0x0313, /* 1489 */ 0x03B9, 0x0314, /* 1491 */ 0x1F30, 0x0300, /* 1493 */ 0x1F31, 0x0300, /* 1495 */ 0x1F30, 0x0301, /* 1497 */ 0x1F31, 0x0301, /* 1499 */ 0x1F30, 0x0342, /* 1501 */ 0x1F31, 0x0342, /* 1503 */ 0x0399, 0x0313, /* 1505 */ 0x0399, 0x0314, /* 1507 */ 0x1F38, 0x0300, /* 1509 */ 0x1F39, 0x0300, /* 1511 */ 0x1F38, 0x0301, /* 1513 */ 0x1F39, 0x0301, /* 1515 */ 0x1F38, 0x0342, /* 1517 */ 0x1F39, 0x0342, /* 1519 */ 0x03BF, 0x0313, /* 1521 */ 0x03BF, 0x0314, /* 1523 */ 0x1F40, 0x0300, /* 1525 */ 0x1F41, 0x0300, /* 1527 */ 0x1F40, 0x0301, /* 1529 */ 0x1F41, 0x0301, /* 1531 */ 0x039F, 0x0313, /* 1533 */ 0x039F, 0x0314, /* 1535 */ 0x1F48, 0x0300, /* 1537 */ 0x1F49, 0x0300, /* 1539 */ 0x1F48, 0x0301, /* 1541 */ 0x1F49, 0x0301, /* 1543 */ 0x03C5, 0x0313, /* 1545 */ 0x03C5, 0x0314, /* 1547 */ 0x1F50, 0x0300, /* 1549 */ 0x1F51, 0x0300, /* 1551 */ 0x1F50, 0x0301, /* 1553 */ 0x1F51, 0x0301, /* 1555 */ 0x1F50, 0x0342, /* 1557 */ 0x1F51, 0x0342, /* 1559 */ 0x03A5, 0x0314, /* 1561 */ 0x1F59, 0x0300, /* 1563 */ 0x1F59, 0x0301, /* 1565 */ 0x1F59, 0x0342, /* 1567 */ 0x03C9, 0x0313, /* 1569 */ 0x03C9, 0x0314, /* 1571 */ 0x1F60, 0x0300, /* 1573 */ 0x1F61, 0x0300, /* 1575 */ 0x1F60, 0x0301, /* 1577 */ 0x1F61, 0x0301, /* 1579 */ 0x1F60, 0x0342, /* 1581 */ 0x1F61, 0x0342, /* 1583 */ 0x03A9, 0x0313, /* 1585 */ 0x03A9, 0x0314, /* 1587 */ 0x1F68, 0x0300, /* 1589 */ 0x1F69, 0x0300, /* 1591 */ 0x1F68, 0x0301, /* 1593 */ 0x1F69, 0x0301, /* 1595 */ 0x1F68, 0x0342, /* 1597 */ 0x1F69, 0x0342, /* 1599 */ 0x03B1, 0x0300, /* 1601 */ 0x03B5, 0x0300, /* 1603 */ 0x03B7, 0x0300, /* 1605 */ 0x03B9, 0x0300, /* 1607 */ 0x03BF, 0x0300, /* 1609 */ 0x03C5, 0x0300, /* 1611 */ 0x03C9, 0x0300, /* 1613 */ 0x1F00, 0x0345, /* 1615 */ 0x1F01, 0x0345, /* 1617 */ 0x1F02, 0x0345, /* 1619 */ 0x1F03, 0x0345, /* 1621 */ 0x1F04, 0x0345, /* 1623 */ 0x1F05, 0x0345, /* 1625 */ 0x1F06, 0x0345, /* 1627 */ 0x1F07, 0x0345, /* 1629 */ 0x1F08, 0x0345, /* 1631 */ 0x1F09, 0x0345, /* 1633 */ 0x1F0A, 0x0345, /* 1635 */ 0x1F0B, 0x0345, /* 1637 */ 0x1F0C, 0x0345, /* 1639 */ 0x1F0D, 0x0345, /* 1641 */ 0x1F0E, 0x0345, /* 1643 */ 0x1F0F, 0x0345, /* 1645 */ 0x1F20, 0x0345, /* 1647 */ 0x1F21, 0x0345, /* 1649 */ 0x1F22, 0x0345, /* 1651 */ 0x1F23, 0x0345, /* 1653 */ 0x1F24, 0x0345, /* 1655 */ 0x1F25, 0x0345, /* 1657 */ 0x1F26, 0x0345, /* 1659 */ 0x1F27, 0x0345, /* 1661 */ 0x1F28, 0x0345, /* 1663 */ 0x1F29, 0x0345, /* 1665 */ 0x1F2A, 0x0345, /* 1667 */ 0x1F2B, 0x0345, /* 1669 */ 0x1F2C, 0x0345, /* 1671 */ 0x1F2D, 0x0345, /* 1673 */ 0x1F2E, 0x0345, /* 1675 */ 0x1F2F, 0x0345, /* 1677 */ 0x1F60, 0x0345, /* 1679 */ 0x1F61, 0x0345, /* 1681 */ 0x1F62, 0x0345, /* 1683 */ 0x1F63, 0x0345, /* 1685 */ 0x1F64, 0x0345, /* 1687 */ 0x1F65, 0x0345, /* 1689 */ 0x1F66, 0x0345, /* 1691 */ 0x1F67, 0x0345, /* 1693 */ 0x1F68, 0x0345, /* 1695 */ 0x1F69, 0x0345, /* 1697 */ 0x1F6A, 0x0345, /* 1699 */ 0x1F6B, 0x0345, /* 1701 */ 0x1F6C, 0x0345, /* 1703 */ 0x1F6D, 0x0345, /* 1705 */ 0x1F6E, 0x0345, /* 1707 */ 0x1F6F, 0x0345, /* 1709 */ 0x03B1, 0x0306, /* 1711 */ 0x03B1, 0x0304, /* 1713 */ 0x1F70, 0x0345, /* 1715 */ 0x03B1, 0x0345, /* 1717 */ 0x03AC, 0x0345, /* 1719 */ 0x03B1, 0x0342, /* 1721 */ 0x1FB6, 0x0345, /* 1723 */ 0x0391, 0x0306, /* 1725 */ 0x0391, 0x0304, /* 1727 */ 0x0391, 0x0300, /* 1729 */ 0x0391, 0x0345, /* 1731 */ 0x0020, 0x0313, /* 1733 */ 0x0020, 0x0313, /* 1735 */ 0x0020, 0x0342, /* 1737 */ 0x00A8, 0x0342, /* 1739 */ 0x1F74, 0x0345, /* 1741 */ 0x03B7, 0x0345, /* 1743 */ 0x03AE, 0x0345, /* 1745 */ 0x03B7, 0x0342, /* 1747 */ 0x1FC6, 0x0345, /* 1749 */ 0x0395, 0x0300, /* 1751 */ 0x0397, 0x0300, /* 1753 */ 0x0397, 0x0345, /* 1755 */ 0x1FBF, 0x0300, /* 1757 */ 0x1FBF, 0x0301, /* 1759 */ 0x1FBF, 0x0342, /* 1761 */ 0x03B9, 0x0306, /* 1763 */ 0x03B9, 0x0304, /* 1765 */ 0x03CA, 0x0300, /* 1767 */ 0x03B9, 0x0342, /* 1769 */ 0x03CA, 0x0342, /* 1771 */ 0x0399, 0x0306, /* 1773 */ 0x0399, 0x0304, /* 1775 */ 0x0399, 0x0300, /* 1777 */ 0x1FFE, 0x0300, /* 1779 */ 0x1FFE, 0x0301, /* 1781 */ 0x1FFE, 0x0342, /* 1783 */ 0x03C5, 0x0306, /* 1785 */ 0x03C5, 0x0304, /* 1787 */ 0x03CB, 0x0300, /* 1789 */ 0x03C1, 0x0313, /* 1791 */ 0x03C1, 0x0314, /* 1793 */ 0x03C5, 0x0342, /* 1795 */ 0x03CB, 0x0342, /* 1797 */ 0x03A5, 0x0306, /* 1799 */ 0x03A5, 0x0304, /* 1801 */ 0x03A5, 0x0300, /* 1803 */ 0x03A1, 0x0314, /* 1805 */ 0x00A8, 0x0300, /* 1807 */ 0x1F7C, 0x0345, /* 1809 */ 0x03C9, 0x0345, /* 1811 */ 0x03CE, 0x0345, /* 1813 */ 0x03C9, 0x0342, /* 1815 */ 0x1FF6, 0x0345, /* 1817 */ 0x039F, 0x0300, /* 1819 */ 0x03A9, 0x0300, /* 1821 */ 0x03A9, 0x0345, /* 1823 */ 0x0020, 0x0314, /* 1825 */ 0x0020, 0x0333, /* 1827 */ 0x002E, 0x002E, /* 1829 */ 0x002E, 0x002E, 0x002E, /* 1832 */ 0x2032, 0x2032, /* 1834 */ 0x2032, 0x2032, 0x2032, /* 1837 */ 0x2035, 0x2035, /* 1839 */ 0x2035, 0x2035, 0x2035, /* 1842 */ 0x0021, 0x0021, /* 1844 */ 0x0020, 0x0305, /* 1846 */ 0x003F, 0x003F, /* 1848 */ 0x003F, 0x0021, /* 1850 */ 0x0021, 0x003F, /* 1852 */ 0x2032, 0x2032, 0x2032, 0x2032, /* 1856 */ 0x0052, 0x0073, /* 1858 */ 0x0061, 0x002F, 0x0063, /* 1861 */ 0x0061, 0x002F, 0x0073, /* 1864 */ 0x00B0, 0x0043, /* 1866 */ 0x0063, 0x002F, 0x006F, /* 1869 */ 0x0063, 0x002F, 0x0075, /* 1872 */ 0x00B0, 0x0046, /* 1874 */ 0x004E, 0x006F, /* 1876 */ 0x0053, 0x004D, /* 1878 */ 0x0054, 0x0045, 0x004C, /* 1881 */ 0x0054, 0x004D, /* 1883 */ 0x0046, 0x0041, 0x0058, /* 1886 */ 0x0031, 0x2044, 0x0037, /* 1889 */ 0x0031, 0x2044, 0x0039, /* 1892 */ 0x0031, 0x2044, 0x0031, 0x0030, /* 1896 */ 0x0031, 0x2044, 0x0033, /* 1899 */ 0x0032, 0x2044, 0x0033, /* 1902 */ 0x0031, 0x2044, 0x0035, /* 1905 */ 0x0032, 0x2044, 0x0035, /* 1908 */ 0x0033, 0x2044, 0x0035, /* 1911 */ 0x0034, 0x2044, 0x0035, /* 1914 */ 0x0031, 0x2044, 0x0036, /* 1917 */ 0x0035, 0x2044, 0x0036, /* 1920 */ 0x0031, 0x2044, 0x0038, /* 1923 */ 0x0033, 0x2044, 0x0038, /* 1926 */ 0x0035, 0x2044, 0x0038, /* 1929 */ 0x0037, 0x2044, 0x0038, /* 1932 */ 0x0031, 0x2044, /* 1934 */ 0x0049, 0x0049, /* 1936 */ 0x0049, 0x0049, 0x0049, /* 1939 */ 0x0049, 0x0056, /* 1941 */ 0x0056, 0x0049, /* 1943 */ 0x0056, 0x0049, 0x0049, /* 1946 */ 0x0056, 0x0049, 0x0049, 0x0049, /* 1950 */ 0x0049, 0x0058, /* 1952 */ 0x0058, 0x0049, /* 1954 */ 0x0058, 0x0049, 0x0049, /* 1957 */ 0x0069, 0x0069, /* 1959 */ 0x0069, 0x0069, 0x0069, /* 1962 */ 0x0069, 0x0076, /* 1964 */ 0x0076, 0x0069, /* 1966 */ 0x0076, 0x0069, 0x0069, /* 1969 */ 0x0076, 0x0069, 0x0069, 0x0069, /* 1973 */ 0x0069, 0x0078, /* 1975 */ 0x0078, 0x0069, /* 1977 */ 0x0078, 0x0069, 0x0069, /* 1980 */ 0x0030, 0x2044, 0x0033, /* 1983 */ 0x2190, 0x0338, /* 1985 */ 0x2192, 0x0338, /* 1987 */ 0x2194, 0x0338, /* 1989 */ 0x21D0, 0x0338, /* 1991 */ 0x21D4, 0x0338, /* 1993 */ 0x21D2, 0x0338, /* 1995 */ 0x2203, 0x0338, /* 1997 */ 0x2208, 0x0338, /* 1999 */ 0x220B, 0x0338, /* 2001 */ 0x2223, 0x0338, /* 2003 */ 0x2225, 0x0338, /* 2005 */ 0x222B, 0x222B, /* 2007 */ 0x222B, 0x222B, 0x222B, /* 2010 */ 0x222E, 0x222E, /* 2012 */ 0x222E, 0x222E, 0x222E, /* 2015 */ 0x223C, 0x0338, /* 2017 */ 0x2243, 0x0338, /* 2019 */ 0x2245, 0x0338, /* 2021 */ 0x2248, 0x0338, /* 2023 */ 0x003D, 0x0338, /* 2025 */ 0x2261, 0x0338, /* 2027 */ 0x224D, 0x0338, /* 2029 */ 0x003C, 0x0338, /* 2031 */ 0x003E, 0x0338, /* 2033 */ 0x2264, 0x0338, /* 2035 */ 0x2265, 0x0338, /* 2037 */ 0x2272, 0x0338, /* 2039 */ 0x2273, 0x0338, /* 2041 */ 0x2276, 0x0338, /* 2043 */ 0x2277, 0x0338, /* 2045 */ 0x227A, 0x0338, /* 2047 */ 0x227B, 0x0338, /* 2049 */ 0x2282, 0x0338, /* 2051 */ 0x2283, 0x0338, /* 2053 */ 0x2286, 0x0338, /* 2055 */ 0x2287, 0x0338, /* 2057 */ 0x22A2, 0x0338, /* 2059 */ 0x22A8, 0x0338, /* 2061 */ 0x22A9, 0x0338, /* 2063 */ 0x22AB, 0x0338, /* 2065 */ 0x227C, 0x0338, /* 2067 */ 0x227D, 0x0338, /* 2069 */ 0x2291, 0x0338, /* 2071 */ 0x2292, 0x0338, /* 2073 */ 0x22B2, 0x0338, /* 2075 */ 0x22B3, 0x0338, /* 2077 */ 0x22B4, 0x0338, /* 2079 */ 0x22B5, 0x0338, /* 2081 */ 0x0031, 0x0030, /* 2083 */ 0x0031, 0x0031, /* 2085 */ 0x0031, 0x0032, /* 2087 */ 0x0031, 0x0033, /* 2089 */ 0x0031, 0x0034, /* 2091 */ 0x0031, 0x0035, /* 2093 */ 0x0031, 0x0036, /* 2095 */ 0x0031, 0x0037, /* 2097 */ 0x0031, 0x0038, /* 2099 */ 0x0031, 0x0039, /* 2101 */ 0x0032, 0x0030, /* 2103 */ 0x0028, 0x0031, 0x0029, /* 2106 */ 0x0028, 0x0032, 0x0029, /* 2109 */ 0x0028, 0x0033, 0x0029, /* 2112 */ 0x0028, 0x0034, 0x0029, /* 2115 */ 0x0028, 0x0035, 0x0029, /* 2118 */ 0x0028, 0x0036, 0x0029, /* 2121 */ 0x0028, 0x0037, 0x0029, /* 2124 */ 0x0028, 0x0038, 0x0029, /* 2127 */ 0x0028, 0x0039, 0x0029, /* 2130 */ 0x0028, 0x0031, 0x0030, 0x0029, /* 2134 */ 0x0028, 0x0031, 0x0031, 0x0029, /* 2138 */ 0x0028, 0x0031, 0x0032, 0x0029, /* 2142 */ 0x0028, 0x0031, 0x0033, 0x0029, /* 2146 */ 0x0028, 0x0031, 0x0034, 0x0029, /* 2150 */ 0x0028, 0x0031, 0x0035, 0x0029, /* 2154 */ 0x0028, 0x0031, 0x0036, 0x0029, /* 2158 */ 0x0028, 0x0031, 0x0037, 0x0029, /* 2162 */ 0x0028, 0x0031, 0x0038, 0x0029, /* 2166 */ 0x0028, 0x0031, 0x0039, 0x0029, /* 2170 */ 0x0028, 0x0032, 0x0030, 0x0029, /* 2174 */ 0x0031, 0x002E, /* 2176 */ 0x0032, 0x002E, /* 2178 */ 0x0033, 0x002E, /* 2180 */ 0x0034, 0x002E, /* 2182 */ 0x0035, 0x002E, /* 2184 */ 0x0036, 0x002E, /* 2186 */ 0x0037, 0x002E, /* 2188 */ 0x0038, 0x002E, /* 2190 */ 0x0039, 0x002E, /* 2192 */ 0x0031, 0x0030, 0x002E, /* 2195 */ 0x0031, 0x0031, 0x002E, /* 2198 */ 0x0031, 0x0032, 0x002E, /* 2201 */ 0x0031, 0x0033, 0x002E, /* 2204 */ 0x0031, 0x0034, 0x002E, /* 2207 */ 0x0031, 0x0035, 0x002E, /* 2210 */ 0x0031, 0x0036, 0x002E, /* 2213 */ 0x0031, 0x0037, 0x002E, /* 2216 */ 0x0031, 0x0038, 0x002E, /* 2219 */ 0x0031, 0x0039, 0x002E, /* 2222 */ 0x0032, 0x0030, 0x002E, /* 2225 */ 0x0028, 0x0061, 0x0029, /* 2228 */ 0x0028, 0x0062, 0x0029, /* 2231 */ 0x0028, 0x0063, 0x0029, /* 2234 */ 0x0028, 0x0064, 0x0029, /* 2237 */ 0x0028, 0x0065, 0x0029, /* 2240 */ 0x0028, 0x0066, 0x0029, /* 2243 */ 0x0028, 0x0067, 0x0029, /* 2246 */ 0x0028, 0x0068, 0x0029, /* 2249 */ 0x0028, 0x0069, 0x0029, /* 2252 */ 0x0028, 0x006A, 0x0029, /* 2255 */ 0x0028, 0x006B, 0x0029, /* 2258 */ 0x0028, 0x006C, 0x0029, /* 2261 */ 0x0028, 0x006D, 0x0029, /* 2264 */ 0x0028, 0x006E, 0x0029, /* 2267 */ 0x0028, 0x006F, 0x0029, /* 2270 */ 0x0028, 0x0070, 0x0029, /* 2273 */ 0x0028, 0x0071, 0x0029, /* 2276 */ 0x0028, 0x0072, 0x0029, /* 2279 */ 0x0028, 0x0073, 0x0029, /* 2282 */ 0x0028, 0x0074, 0x0029, /* 2285 */ 0x0028, 0x0075, 0x0029, /* 2288 */ 0x0028, 0x0076, 0x0029, /* 2291 */ 0x0028, 0x0077, 0x0029, /* 2294 */ 0x0028, 0x0078, 0x0029, /* 2297 */ 0x0028, 0x0079, 0x0029, /* 2300 */ 0x0028, 0x007A, 0x0029, /* 2303 */ 0x222B, 0x222B, 0x222B, 0x222B, /* 2307 */ 0x003A, 0x003A, 0x003D, /* 2310 */ 0x003D, 0x003D, /* 2312 */ 0x003D, 0x003D, 0x003D, /* 2315 */ 0x2ADD, 0x0338, /* 2317 */ 0x304B, 0x3099, /* 2319 */ 0x304D, 0x3099, /* 2321 */ 0x304F, 0x3099, /* 2323 */ 0x3051, 0x3099, /* 2325 */ 0x3053, 0x3099, /* 2327 */ 0x3055, 0x3099, /* 2329 */ 0x3057, 0x3099, /* 2331 */ 0x3059, 0x3099, /* 2333 */ 0x305B, 0x3099, /* 2335 */ 0x305D, 0x3099, /* 2337 */ 0x305F, 0x3099, /* 2339 */ 0x3061, 0x3099, /* 2341 */ 0x3064, 0x3099, /* 2343 */ 0x3066, 0x3099, /* 2345 */ 0x3068, 0x3099, /* 2347 */ 0x306F, 0x3099, /* 2349 */ 0x306F, 0x309A, /* 2351 */ 0x3072, 0x3099, /* 2353 */ 0x3072, 0x309A, /* 2355 */ 0x3075, 0x3099, /* 2357 */ 0x3075, 0x309A, /* 2359 */ 0x3078, 0x3099, /* 2361 */ 0x3078, 0x309A, /* 2363 */ 0x307B, 0x3099, /* 2365 */ 0x307B, 0x309A, /* 2367 */ 0x3046, 0x3099, /* 2369 */ 0x0020, 0x3099, /* 2371 */ 0x0020, 0x309A, /* 2373 */ 0x309D, 0x3099, /* 2375 */ 0x3088, 0x308A, /* 2377 */ 0x30AB, 0x3099, /* 2379 */ 0x30AD, 0x3099, /* 2381 */ 0x30AF, 0x3099, /* 2383 */ 0x30B1, 0x3099, /* 2385 */ 0x30B3, 0x3099, /* 2387 */ 0x30B5, 0x3099, /* 2389 */ 0x30B7, 0x3099, /* 2391 */ 0x30B9, 0x3099, /* 2393 */ 0x30BB, 0x3099, /* 2395 */ 0x30BD, 0x3099, /* 2397 */ 0x30BF, 0x3099, /* 2399 */ 0x30C1, 0x3099, /* 2401 */ 0x30C4, 0x3099, /* 2403 */ 0x30C6, 0x3099, /* 2405 */ 0x30C8, 0x3099, /* 2407 */ 0x30CF, 0x3099, /* 2409 */ 0x30CF, 0x309A, /* 2411 */ 0x30D2, 0x3099, /* 2413 */ 0x30D2, 0x309A, /* 2415 */ 0x30D5, 0x3099, /* 2417 */ 0x30D5, 0x309A, /* 2419 */ 0x30D8, 0x3099, /* 2421 */ 0x30D8, 0x309A, /* 2423 */ 0x30DB, 0x3099, /* 2425 */ 0x30DB, 0x309A, /* 2427 */ 0x30A6, 0x3099, /* 2429 */ 0x30EF, 0x3099, /* 2431 */ 0x30F0, 0x3099, /* 2433 */ 0x30F1, 0x3099, /* 2435 */ 0x30F2, 0x3099, /* 2437 */ 0x30FD, 0x3099, /* 2439 */ 0x30B3, 0x30C8, /* 2441 */ 0x0028, 0x1100, 0x0029, /* 2444 */ 0x0028, 0x1102, 0x0029, /* 2447 */ 0x0028, 0x1103, 0x0029, /* 2450 */ 0x0028, 0x1105, 0x0029, /* 2453 */ 0x0028, 0x1106, 0x0029, /* 2456 */ 0x0028, 0x1107, 0x0029, /* 2459 */ 0x0028, 0x1109, 0x0029, /* 2462 */ 0x0028, 0x110B, 0x0029, /* 2465 */ 0x0028, 0x110C, 0x0029, /* 2468 */ 0x0028, 0x110E, 0x0029, /* 2471 */ 0x0028, 0x110F, 0x0029, /* 2474 */ 0x0028, 0x1110, 0x0029, /* 2477 */ 0x0028, 0x1111, 0x0029, /* 2480 */ 0x0028, 0x1112, 0x0029, /* 2483 */ 0x0028, 0x1100, 0x1161, 0x0029, /* 2487 */ 0x0028, 0x1102, 0x1161, 0x0029, /* 2491 */ 0x0028, 0x1103, 0x1161, 0x0029, /* 2495 */ 0x0028, 0x1105, 0x1161, 0x0029, /* 2499 */ 0x0028, 0x1106, 0x1161, 0x0029, /* 2503 */ 0x0028, 0x1107, 0x1161, 0x0029, /* 2507 */ 0x0028, 0x1109, 0x1161, 0x0029, /* 2511 */ 0x0028, 0x110B, 0x1161, 0x0029, /* 2515 */ 0x0028, 0x110C, 0x1161, 0x0029, /* 2519 */ 0x0028, 0x110E, 0x1161, 0x0029, /* 2523 */ 0x0028, 0x110F, 0x1161, 0x0029, /* 2527 */ 0x0028, 0x1110, 0x1161, 0x0029, /* 2531 */ 0x0028, 0x1111, 0x1161, 0x0029, /* 2535 */ 0x0028, 0x1112, 0x1161, 0x0029, /* 2539 */ 0x0028, 0x110C, 0x116E, 0x0029, /* 2543 */ 0x0028, 0x110B, 0x1169, 0x110C, 0x1165, 0x11AB, 0x0029, /* 2550 */ 0x0028, 0x110B, 0x1169, 0x1112, 0x116E, 0x0029, /* 2556 */ 0x0028, 0x4E00, 0x0029, /* 2559 */ 0x0028, 0x4E8C, 0x0029, /* 2562 */ 0x0028, 0x4E09, 0x0029, /* 2565 */ 0x0028, 0x56DB, 0x0029, /* 2568 */ 0x0028, 0x4E94, 0x0029, /* 2571 */ 0x0028, 0x516D, 0x0029, /* 2574 */ 0x0028, 0x4E03, 0x0029, /* 2577 */ 0x0028, 0x516B, 0x0029, /* 2580 */ 0x0028, 0x4E5D, 0x0029, /* 2583 */ 0x0028, 0x5341, 0x0029, /* 2586 */ 0x0028, 0x6708, 0x0029, /* 2589 */ 0x0028, 0x706B, 0x0029, /* 2592 */ 0x0028, 0x6C34, 0x0029, /* 2595 */ 0x0028, 0x6728, 0x0029, /* 2598 */ 0x0028, 0x91D1, 0x0029, /* 2601 */ 0x0028, 0x571F, 0x0029, /* 2604 */ 0x0028, 0x65E5, 0x0029, /* 2607 */ 0x0028, 0x682A, 0x0029, /* 2610 */ 0x0028, 0x6709, 0x0029, /* 2613 */ 0x0028, 0x793E, 0x0029, /* 2616 */ 0x0028, 0x540D, 0x0029, /* 2619 */ 0x0028, 0x7279, 0x0029, /* 2622 */ 0x0028, 0x8CA1, 0x0029, /* 2625 */ 0x0028, 0x795D, 0x0029, /* 2628 */ 0x0028, 0x52B4, 0x0029, /* 2631 */ 0x0028, 0x4EE3, 0x0029, /* 2634 */ 0x0028, 0x547C, 0x0029, /* 2637 */ 0x0028, 0x5B66, 0x0029, /* 2640 */ 0x0028, 0x76E3, 0x0029, /* 2643 */ 0x0028, 0x4F01, 0x0029, /* 2646 */ 0x0028, 0x8CC7, 0x0029, /* 2649 */ 0x0028, 0x5354, 0x0029, /* 2652 */ 0x0028, 0x796D, 0x0029, /* 2655 */ 0x0028, 0x4F11, 0x0029, /* 2658 */ 0x0028, 0x81EA, 0x0029, /* 2661 */ 0x0028, 0x81F3, 0x0029, /* 2664 */ 0x0050, 0x0054, 0x0045, /* 2667 */ 0x0032, 0x0031, /* 2669 */ 0x0032, 0x0032, /* 2671 */ 0x0032, 0x0033, /* 2673 */ 0x0032, 0x0034, /* 2675 */ 0x0032, 0x0035, /* 2677 */ 0x0032, 0x0036, /* 2679 */ 0x0032, 0x0037, /* 2681 */ 0x0032, 0x0038, /* 2683 */ 0x0032, 0x0039, /* 2685 */ 0x0033, 0x0030, /* 2687 */ 0x0033, 0x0031, /* 2689 */ 0x0033, 0x0032, /* 2691 */ 0x0033, 0x0033, /* 2693 */ 0x0033, 0x0034, /* 2695 */ 0x0033, 0x0035, /* 2697 */ 0x1100, 0x1161, /* 2699 */ 0x1102, 0x1161, /* 2701 */ 0x1103, 0x1161, /* 2703 */ 0x1105, 0x1161, /* 2705 */ 0x1106, 0x1161, /* 2707 */ 0x1107, 0x1161, /* 2709 */ 0x1109, 0x1161, /* 2711 */ 0x110B, 0x1161, /* 2713 */ 0x110C, 0x1161, /* 2715 */ 0x110E, 0x1161, /* 2717 */ 0x110F, 0x1161, /* 2719 */ 0x1110, 0x1161, /* 2721 */ 0x1111, 0x1161, /* 2723 */ 0x1112, 0x1161, /* 2725 */ 0x110E, 0x1161, 0x11B7, 0x1100, 0x1169, /* 2730 */ 0x110C, 0x116E, 0x110B, 0x1174, /* 2734 */ 0x110B, 0x116E, /* 2736 */ 0x0033, 0x0036, /* 2738 */ 0x0033, 0x0037, /* 2740 */ 0x0033, 0x0038, /* 2742 */ 0x0033, 0x0039, /* 2744 */ 0x0034, 0x0030, /* 2746 */ 0x0034, 0x0031, /* 2748 */ 0x0034, 0x0032, /* 2750 */ 0x0034, 0x0033, /* 2752 */ 0x0034, 0x0034, /* 2754 */ 0x0034, 0x0035, /* 2756 */ 0x0034, 0x0036, /* 2758 */ 0x0034, 0x0037, /* 2760 */ 0x0034, 0x0038, /* 2762 */ 0x0034, 0x0039, /* 2764 */ 0x0035, 0x0030, /* 2766 */ 0x0031, 0x6708, /* 2768 */ 0x0032, 0x6708, /* 2770 */ 0x0033, 0x6708, /* 2772 */ 0x0034, 0x6708, /* 2774 */ 0x0035, 0x6708, /* 2776 */ 0x0036, 0x6708, /* 2778 */ 0x0037, 0x6708, /* 2780 */ 0x0038, 0x6708, /* 2782 */ 0x0039, 0x6708, /* 2784 */ 0x0031, 0x0030, 0x6708, /* 2787 */ 0x0031, 0x0031, 0x6708, /* 2790 */ 0x0031, 0x0032, 0x6708, /* 2793 */ 0x0048, 0x0067, /* 2795 */ 0x0065, 0x0072, 0x0067, /* 2798 */ 0x0065, 0x0056, /* 2800 */ 0x004C, 0x0054, 0x0044, /* 2803 */ 0x4EE4, 0x548C, /* 2805 */ 0x30A2, 0x30D1, 0x30FC, 0x30C8, /* 2809 */ 0x30A2, 0x30EB, 0x30D5, 0x30A1, /* 2813 */ 0x30A2, 0x30F3, 0x30DA, 0x30A2, /* 2817 */ 0x30A2, 0x30FC, 0x30EB, /* 2820 */ 0x30A4, 0x30CB, 0x30F3, 0x30B0, /* 2824 */ 0x30A4, 0x30F3, 0x30C1, /* 2827 */ 0x30A6, 0x30A9, 0x30F3, /* 2830 */ 0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C9, /* 2835 */ 0x30A8, 0x30FC, 0x30AB, 0x30FC, /* 2839 */ 0x30AA, 0x30F3, 0x30B9, /* 2842 */ 0x30AA, 0x30FC, 0x30E0, /* 2845 */ 0x30AB, 0x30A4, 0x30EA, /* 2848 */ 0x30AB, 0x30E9, 0x30C3, 0x30C8, /* 2852 */ 0x30AB, 0x30ED, 0x30EA, 0x30FC, /* 2856 */ 0x30AC, 0x30ED, 0x30F3, /* 2859 */ 0x30AC, 0x30F3, 0x30DE, /* 2862 */ 0x30AE, 0x30AC, /* 2864 */ 0x30AE, 0x30CB, 0x30FC, /* 2867 */ 0x30AD, 0x30E5, 0x30EA, 0x30FC, /* 2871 */ 0x30AE, 0x30EB, 0x30C0, 0x30FC, /* 2875 */ 0x30AD, 0x30ED, /* 2877 */ 0x30AD, 0x30ED, 0x30B0, 0x30E9, 0x30E0, /* 2882 */ 0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB, /* 2888 */ 0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8, /* 2893 */ 0x30B0, 0x30E9, 0x30E0, /* 2896 */ 0x30B0, 0x30E9, 0x30E0, 0x30C8, 0x30F3, /* 2901 */ 0x30AF, 0x30EB, 0x30BC, 0x30A4, 0x30ED, /* 2906 */ 0x30AF, 0x30ED, 0x30FC, 0x30CD, /* 2910 */ 0x30B1, 0x30FC, 0x30B9, /* 2913 */ 0x30B3, 0x30EB, 0x30CA, /* 2916 */ 0x30B3, 0x30FC, 0x30DD, /* 2919 */ 0x30B5, 0x30A4, 0x30AF, 0x30EB, /* 2923 */ 0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0, /* 2928 */ 0x30B7, 0x30EA, 0x30F3, 0x30B0, /* 2932 */ 0x30BB, 0x30F3, 0x30C1, /* 2935 */ 0x30BB, 0x30F3, 0x30C8, /* 2938 */ 0x30C0, 0x30FC, 0x30B9, /* 2941 */ 0x30C7, 0x30B7, /* 2943 */ 0x30C9, 0x30EB, /* 2945 */ 0x30C8, 0x30F3, /* 2947 */ 0x30CA, 0x30CE, /* 2949 */ 0x30CE, 0x30C3, 0x30C8, /* 2952 */ 0x30CF, 0x30A4, 0x30C4, /* 2955 */ 0x30D1, 0x30FC, 0x30BB, 0x30F3, 0x30C8, /* 2960 */ 0x30D1, 0x30FC, 0x30C4, /* 2963 */ 0x30D0, 0x30FC, 0x30EC, 0x30EB, /* 2967 */ 0x30D4, 0x30A2, 0x30B9, 0x30C8, 0x30EB, /* 2972 */ 0x30D4, 0x30AF, 0x30EB, /* 2975 */ 0x30D4, 0x30B3, /* 2977 */ 0x30D3, 0x30EB, /* 2979 */ 0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C9, /* 2984 */ 0x30D5, 0x30A3, 0x30FC, 0x30C8, /* 2988 */ 0x30D6, 0x30C3, 0x30B7, 0x30A7, 0x30EB, /* 2993 */ 0x30D5, 0x30E9, 0x30F3, /* 2996 */ 0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB, /* 3001 */ 0x30DA, 0x30BD, /* 3003 */ 0x30DA, 0x30CB, 0x30D2, /* 3006 */ 0x30D8, 0x30EB, 0x30C4, /* 3009 */ 0x30DA, 0x30F3, 0x30B9, /* 3012 */ 0x30DA, 0x30FC, 0x30B8, /* 3015 */ 0x30D9, 0x30FC, 0x30BF, /* 3018 */ 0x30DD, 0x30A4, 0x30F3, 0x30C8, /* 3022 */ 0x30DC, 0x30EB, 0x30C8, /* 3025 */ 0x30DB, 0x30F3, /* 3027 */ 0x30DD, 0x30F3, 0x30C9, /* 3030 */ 0x30DB, 0x30FC, 0x30EB, /* 3033 */ 0x30DB, 0x30FC, 0x30F3, /* 3036 */ 0x30DE, 0x30A4, 0x30AF, 0x30ED, /* 3040 */ 0x30DE, 0x30A4, 0x30EB, /* 3043 */ 0x30DE, 0x30C3, 0x30CF, /* 3046 */ 0x30DE, 0x30EB, 0x30AF, /* 3049 */ 0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3, /* 3054 */ 0x30DF, 0x30AF, 0x30ED, 0x30F3, /* 3058 */ 0x30DF, 0x30EA, /* 3060 */ 0x30DF, 0x30EA, 0x30D0, 0x30FC, 0x30EB, /* 3065 */ 0x30E1, 0x30AC, /* 3067 */ 0x30E1, 0x30AC, 0x30C8, 0x30F3, /* 3071 */ 0x30E1, 0x30FC, 0x30C8, 0x30EB, /* 3075 */ 0x30E4, 0x30FC, 0x30C9, /* 3078 */ 0x30E4, 0x30FC, 0x30EB, /* 3081 */ 0x30E6, 0x30A2, 0x30F3, /* 3084 */ 0x30EA, 0x30C3, 0x30C8, 0x30EB, /* 3088 */ 0x30EA, 0x30E9, /* 3090 */ 0x30EB, 0x30D4, 0x30FC, /* 3093 */ 0x30EB, 0x30FC, 0x30D6, 0x30EB, /* 3097 */ 0x30EC, 0x30E0, /* 3099 */ 0x30EC, 0x30F3, 0x30C8, 0x30B2, 0x30F3, /* 3104 */ 0x30EF, 0x30C3, 0x30C8, /* 3107 */ 0x0030, 0x70B9, /* 3109 */ 0x0031, 0x70B9, /* 3111 */ 0x0032, 0x70B9, /* 3113 */ 0x0033, 0x70B9, /* 3115 */ 0x0034, 0x70B9, /* 3117 */ 0x0035, 0x70B9, /* 3119 */ 0x0036, 0x70B9, /* 3121 */ 0x0037, 0x70B9, /* 3123 */ 0x0038, 0x70B9, /* 3125 */ 0x0039, 0x70B9, /* 3127 */ 0x0031, 0x0030, 0x70B9, /* 3130 */ 0x0031, 0x0031, 0x70B9, /* 3133 */ 0x0031, 0x0032, 0x70B9, /* 3136 */ 0x0031, 0x0033, 0x70B9, /* 3139 */ 0x0031, 0x0034, 0x70B9, /* 3142 */ 0x0031, 0x0035, 0x70B9, /* 3145 */ 0x0031, 0x0036, 0x70B9, /* 3148 */ 0x0031, 0x0037, 0x70B9, /* 3151 */ 0x0031, 0x0038, 0x70B9, /* 3154 */ 0x0031, 0x0039, 0x70B9, /* 3157 */ 0x0032, 0x0030, 0x70B9, /* 3160 */ 0x0032, 0x0031, 0x70B9, /* 3163 */ 0x0032, 0x0032, 0x70B9, /* 3166 */ 0x0032, 0x0033, 0x70B9, /* 3169 */ 0x0032, 0x0034, 0x70B9, /* 3172 */ 0x0068, 0x0050, 0x0061, /* 3175 */ 0x0064, 0x0061, /* 3177 */ 0x0041, 0x0055, /* 3179 */ 0x0062, 0x0061, 0x0072, /* 3182 */ 0x006F, 0x0056, /* 3184 */ 0x0070, 0x0063, /* 3186 */ 0x0064, 0x006D, /* 3188 */ 0x0064, 0x006D, 0x00B2, /* 3191 */ 0x0064, 0x006D, 0x00B3, /* 3194 */ 0x0049, 0x0055, /* 3196 */ 0x5E73, 0x6210, /* 3198 */ 0x662D, 0x548C, /* 3200 */ 0x5927, 0x6B63, /* 3202 */ 0x660E, 0x6CBB, /* 3204 */ 0x682A, 0x5F0F, 0x4F1A, 0x793E, /* 3208 */ 0x0070, 0x0041, /* 3210 */ 0x006E, 0x0041, /* 3212 */ 0x03BC, 0x0041, /* 3214 */ 0x006D, 0x0041, /* 3216 */ 0x006B, 0x0041, /* 3218 */ 0x004B, 0x0042, /* 3220 */ 0x004D, 0x0042, /* 3222 */ 0x0047, 0x0042, /* 3224 */ 0x0063, 0x0061, 0x006C, /* 3227 */ 0x006B, 0x0063, 0x0061, 0x006C, /* 3231 */ 0x0070, 0x0046, /* 3233 */ 0x006E, 0x0046, /* 3235 */ 0x03BC, 0x0046, /* 3237 */ 0x03BC, 0x0067, /* 3239 */ 0x006D, 0x0067, /* 3241 */ 0x006B, 0x0067, /* 3243 */ 0x0048, 0x007A, /* 3245 */ 0x006B, 0x0048, 0x007A, /* 3248 */ 0x004D, 0x0048, 0x007A, /* 3251 */ 0x0047, 0x0048, 0x007A, /* 3254 */ 0x0054, 0x0048, 0x007A, /* 3257 */ 0x03BC, 0x2113, /* 3259 */ 0x006D, 0x2113, /* 3261 */ 0x0064, 0x2113, /* 3263 */ 0x006B, 0x2113, /* 3265 */ 0x0066, 0x006D, /* 3267 */ 0x006E, 0x006D, /* 3269 */ 0x03BC, 0x006D, /* 3271 */ 0x006D, 0x006D, /* 3273 */ 0x0063, 0x006D, /* 3275 */ 0x006B, 0x006D, /* 3277 */ 0x006D, 0x006D, 0x00B2, /* 3280 */ 0x0063, 0x006D, 0x00B2, /* 3283 */ 0x006D, 0x00B2, /* 3285 */ 0x006B, 0x006D, 0x00B2, /* 3288 */ 0x006D, 0x006D, 0x00B3, /* 3291 */ 0x0063, 0x006D, 0x00B3, /* 3294 */ 0x006D, 0x00B3, /* 3296 */ 0x006B, 0x006D, 0x00B3, /* 3299 */ 0x006D, 0x2215, 0x0073, /* 3302 */ 0x006D, 0x2215, 0x0073, 0x00B2, /* 3306 */ 0x0050, 0x0061, /* 3308 */ 0x006B, 0x0050, 0x0061, /* 3311 */ 0x004D, 0x0050, 0x0061, /* 3314 */ 0x0047, 0x0050, 0x0061, /* 3317 */ 0x0072, 0x0061, 0x0064, /* 3320 */ 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, /* 3325 */ 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0x00B2, /* 3331 */ 0x0070, 0x0073, /* 3333 */ 0x006E, 0x0073, /* 3335 */ 0x03BC, 0x0073, /* 3337 */ 0x006D, 0x0073, /* 3339 */ 0x0070, 0x0056, /* 3341 */ 0x006E, 0x0056, /* 3343 */ 0x03BC, 0x0056, /* 3345 */ 0x006D, 0x0056, /* 3347 */ 0x006B, 0x0056, /* 3349 */ 0x004D, 0x0056, /* 3351 */ 0x0070, 0x0057, /* 3353 */ 0x006E, 0x0057, /* 3355 */ 0x03BC, 0x0057, /* 3357 */ 0x006D, 0x0057, /* 3359 */ 0x006B, 0x0057, /* 3361 */ 0x004D, 0x0057, /* 3363 */ 0x006B, 0x03A9, /* 3365 */ 0x004D, 0x03A9, /* 3367 */ 0x0061, 0x002E, 0x006D, 0x002E, /* 3371 */ 0x0042, 0x0071, /* 3373 */ 0x0063, 0x0063, /* 3375 */ 0x0063, 0x0064, /* 3377 */ 0x0043, 0x2215, 0x006B, 0x0067, /* 3381 */ 0x0043, 0x006F, 0x002E, /* 3384 */ 0x0064, 0x0042, /* 3386 */ 0x0047, 0x0079, /* 3388 */ 0x0068, 0x0061, /* 3390 */ 0x0048, 0x0050, /* 3392 */ 0x0069, 0x006E, /* 3394 */ 0x004B, 0x004B, /* 3396 */ 0x004B, 0x004D, /* 3398 */ 0x006B, 0x0074, /* 3400 */ 0x006C, 0x006D, /* 3402 */ 0x006C, 0x006E, /* 3404 */ 0x006C, 0x006F, 0x0067, /* 3407 */ 0x006C, 0x0078, /* 3409 */ 0x006D, 0x0062, /* 3411 */ 0x006D, 0x0069, 0x006C, /* 3414 */ 0x006D, 0x006F, 0x006C, /* 3417 */ 0x0050, 0x0048, /* 3419 */ 0x0070, 0x002E, 0x006D, 0x002E, /* 3423 */ 0x0050, 0x0050, 0x004D, /* 3426 */ 0x0050, 0x0052, /* 3428 */ 0x0073, 0x0072, /* 3430 */ 0x0053, 0x0076, /* 3432 */ 0x0057, 0x0062, /* 3434 */ 0x0056, 0x2215, 0x006D, /* 3437 */ 0x0041, 0x2215, 0x006D, /* 3440 */ 0x0031, 0x65E5, /* 3442 */ 0x0032, 0x65E5, /* 3444 */ 0x0033, 0x65E5, /* 3446 */ 0x0034, 0x65E5, /* 3448 */ 0x0035, 0x65E5, /* 3450 */ 0x0036, 0x65E5, /* 3452 */ 0x0037, 0x65E5, /* 3454 */ 0x0038, 0x65E5, /* 3456 */ 0x0039, 0x65E5, /* 3458 */ 0x0031, 0x0030, 0x65E5, /* 3461 */ 0x0031, 0x0031, 0x65E5, /* 3464 */ 0x0031, 0x0032, 0x65E5, /* 3467 */ 0x0031, 0x0033, 0x65E5, /* 3470 */ 0x0031, 0x0034, 0x65E5, /* 3473 */ 0x0031, 0x0035, 0x65E5, /* 3476 */ 0x0031, 0x0036, 0x65E5, /* 3479 */ 0x0031, 0x0037, 0x65E5, /* 3482 */ 0x0031, 0x0038, 0x65E5, /* 3485 */ 0x0031, 0x0039, 0x65E5, /* 3488 */ 0x0032, 0x0030, 0x65E5, /* 3491 */ 0x0032, 0x0031, 0x65E5, /* 3494 */ 0x0032, 0x0032, 0x65E5, /* 3497 */ 0x0032, 0x0033, 0x65E5, /* 3500 */ 0x0032, 0x0034, 0x65E5, /* 3503 */ 0x0032, 0x0035, 0x65E5, /* 3506 */ 0x0032, 0x0036, 0x65E5, /* 3509 */ 0x0032, 0x0037, 0x65E5, /* 3512 */ 0x0032, 0x0038, 0x65E5, /* 3515 */ 0x0032, 0x0039, 0x65E5, /* 3518 */ 0x0033, 0x0030, 0x65E5, /* 3521 */ 0x0033, 0x0031, 0x65E5, /* 3524 */ 0x0067, 0x0061, 0x006C, /* 3527 */ 0x242EE, /* 3528 */ 0x2284A, /* 3529 */ 0x22844, /* 3530 */ 0x233D5, /* 3531 */ 0x25249, /* 3532 */ 0x25CD0, /* 3533 */ 0x27ED3, /* 3534 */ 0x0066, 0x0066, /* 3536 */ 0x0066, 0x0069, /* 3538 */ 0x0066, 0x006C, /* 3540 */ 0x0066, 0x0066, 0x0069, /* 3543 */ 0x0066, 0x0066, 0x006C, /* 3546 */ 0x017F, 0x0074, /* 3548 */ 0x0073, 0x0074, /* 3550 */ 0x0574, 0x0576, /* 3552 */ 0x0574, 0x0565, /* 3554 */ 0x0574, 0x056B, /* 3556 */ 0x057E, 0x0576, /* 3558 */ 0x0574, 0x056D, /* 3560 */ 0x05D9, 0x05B4, /* 3562 */ 0x05F2, 0x05B7, /* 3564 */ 0x05E9, 0x05C1, /* 3566 */ 0x05E9, 0x05C2, /* 3568 */ 0xFB49, 0x05C1, /* 3570 */ 0xFB49, 0x05C2, /* 3572 */ 0x05D0, 0x05B7, /* 3574 */ 0x05D0, 0x05B8, /* 3576 */ 0x05D0, 0x05BC, /* 3578 */ 0x05D1, 0x05BC, /* 3580 */ 0x05D2, 0x05BC, /* 3582 */ 0x05D3, 0x05BC, /* 3584 */ 0x05D4, 0x05BC, /* 3586 */ 0x05D5, 0x05BC, /* 3588 */ 0x05D6, 0x05BC, /* 3590 */ 0x05D8, 0x05BC, /* 3592 */ 0x05D9, 0x05BC, /* 3594 */ 0x05DA, 0x05BC, /* 3596 */ 0x05DB, 0x05BC, /* 3598 */ 0x05DC, 0x05BC, /* 3600 */ 0x05DE, 0x05BC, /* 3602 */ 0x05E0, 0x05BC, /* 3604 */ 0x05E1, 0x05BC, /* 3606 */ 0x05E3, 0x05BC, /* 3608 */ 0x05E4, 0x05BC, /* 3610 */ 0x05E6, 0x05BC, /* 3612 */ 0x05E7, 0x05BC, /* 3614 */ 0x05E8, 0x05BC, /* 3616 */ 0x05E9, 0x05BC, /* 3618 */ 0x05EA, 0x05BC, /* 3620 */ 0x05D5, 0x05B9, /* 3622 */ 0x05D1, 0x05BF, /* 3624 */ 0x05DB, 0x05BF, /* 3626 */ 0x05E4, 0x05BF, /* 3628 */ 0x05D0, 0x05DC, /* 3630 */ 0x0626, 0x0627, /* 3632 */ 0x0626, 0x0627, /* 3634 */ 0x0626, 0x06D5, /* 3636 */ 0x0626, 0x06D5, /* 3638 */ 0x0626, 0x0648, /* 3640 */ 0x0626, 0x0648, /* 3642 */ 0x0626, 0x06C7, /* 3644 */ 0x0626, 0x06C7, /* 3646 */ 0x0626, 0x06C6, /* 3648 */ 0x0626, 0x06C6, /* 3650 */ 0x0626, 0x06C8, /* 3652 */ 0x0626, 0x06C8, /* 3654 */ 0x0626, 0x06D0, /* 3656 */ 0x0626, 0x06D0, /* 3658 */ 0x0626, 0x06D0, /* 3660 */ 0x0626, 0x0649, /* 3662 */ 0x0626, 0x0649, /* 3664 */ 0x0626, 0x0649, /* 3666 */ 0x0626, 0x062C, /* 3668 */ 0x0626, 0x062D, /* 3670 */ 0x0626, 0x0645, /* 3672 */ 0x0626, 0x0649, /* 3674 */ 0x0626, 0x064A, /* 3676 */ 0x0628, 0x062C, /* 3678 */ 0x0628, 0x062D, /* 3680 */ 0x0628, 0x062E, /* 3682 */ 0x0628, 0x0645, /* 3684 */ 0x0628, 0x0649, /* 3686 */ 0x0628, 0x064A, /* 3688 */ 0x062A, 0x062C, /* 3690 */ 0x062A, 0x062D, /* 3692 */ 0x062A, 0x062E, /* 3694 */ 0x062A, 0x0645, /* 3696 */ 0x062A, 0x0649, /* 3698 */ 0x062A, 0x064A, /* 3700 */ 0x062B, 0x062C, /* 3702 */ 0x062B, 0x0645, /* 3704 */ 0x062B, 0x0649, /* 3706 */ 0x062B, 0x064A, /* 3708 */ 0x062C, 0x062D, /* 3710 */ 0x062C, 0x0645, /* 3712 */ 0x062D, 0x062C, /* 3714 */ 0x062D, 0x0645, /* 3716 */ 0x062E, 0x062C, /* 3718 */ 0x062E, 0x062D, /* 3720 */ 0x062E, 0x0645, /* 3722 */ 0x0633, 0x062C, /* 3724 */ 0x0633, 0x062D, /* 3726 */ 0x0633, 0x062E, /* 3728 */ 0x0633, 0x0645, /* 3730 */ 0x0635, 0x062D, /* 3732 */ 0x0635, 0x0645, /* 3734 */ 0x0636, 0x062C, /* 3736 */ 0x0636, 0x062D, /* 3738 */ 0x0636, 0x062E, /* 3740 */ 0x0636, 0x0645, /* 3742 */ 0x0637, 0x062D, /* 3744 */ 0x0637, 0x0645, /* 3746 */ 0x0638, 0x0645, /* 3748 */ 0x0639, 0x062C, /* 3750 */ 0x0639, 0x0645, /* 3752 */ 0x063A, 0x062C, /* 3754 */ 0x063A, 0x0645, /* 3756 */ 0x0641, 0x062C, /* 3758 */ 0x0641, 0x062D, /* 3760 */ 0x0641, 0x062E, /* 3762 */ 0x0641, 0x0645, /* 3764 */ 0x0641, 0x0649, /* 3766 */ 0x0641, 0x064A, /* 3768 */ 0x0642, 0x062D, /* 3770 */ 0x0642, 0x0645, /* 3772 */ 0x0642, 0x0649, /* 3774 */ 0x0642, 0x064A, /* 3776 */ 0x0643, 0x0627, /* 3778 */ 0x0643, 0x062C, /* 3780 */ 0x0643, 0x062D, /* 3782 */ 0x0643, 0x062E, /* 3784 */ 0x0643, 0x0644, /* 3786 */ 0x0643, 0x0645, /* 3788 */ 0x0643, 0x0649, /* 3790 */ 0x0643, 0x064A, /* 3792 */ 0x0644, 0x062C, /* 3794 */ 0x0644, 0x062D, /* 3796 */ 0x0644, 0x062E, /* 3798 */ 0x0644, 0x0645, /* 3800 */ 0x0644, 0x0649, /* 3802 */ 0x0644, 0x064A, /* 3804 */ 0x0645, 0x062C, /* 3806 */ 0x0645, 0x062D, /* 3808 */ 0x0645, 0x062E, /* 3810 */ 0x0645, 0x0645, /* 3812 */ 0x0645, 0x0649, /* 3814 */ 0x0645, 0x064A, /* 3816 */ 0x0646, 0x062C, /* 3818 */ 0x0646, 0x062D, /* 3820 */ 0x0646, 0x062E, /* 3822 */ 0x0646, 0x0645, /* 3824 */ 0x0646, 0x0649, /* 3826 */ 0x0646, 0x064A, /* 3828 */ 0x0647, 0x062C, /* 3830 */ 0x0647, 0x0645, /* 3832 */ 0x0647, 0x0649, /* 3834 */ 0x0647, 0x064A, /* 3836 */ 0x064A, 0x062C, /* 3838 */ 0x064A, 0x062D, /* 3840 */ 0x064A, 0x062E, /* 3842 */ 0x064A, 0x0645, /* 3844 */ 0x064A, 0x0649, /* 3846 */ 0x064A, 0x064A, /* 3848 */ 0x0630, 0x0670, /* 3850 */ 0x0631, 0x0670, /* 3852 */ 0x0649, 0x0670, /* 3854 */ 0x0020, 0x064C, 0x0651, /* 3857 */ 0x0020, 0x064D, 0x0651, /* 3860 */ 0x0020, 0x064E, 0x0651, /* 3863 */ 0x0020, 0x064F, 0x0651, /* 3866 */ 0x0020, 0x0650, 0x0651, /* 3869 */ 0x0020, 0x0651, 0x0670, /* 3872 */ 0x0626, 0x0631, /* 3874 */ 0x0626, 0x0632, /* 3876 */ 0x0626, 0x0645, /* 3878 */ 0x0626, 0x0646, /* 3880 */ 0x0626, 0x0649, /* 3882 */ 0x0626, 0x064A, /* 3884 */ 0x0628, 0x0631, /* 3886 */ 0x0628, 0x0632, /* 3888 */ 0x0628, 0x0645, /* 3890 */ 0x0628, 0x0646, /* 3892 */ 0x0628, 0x0649, /* 3894 */ 0x0628, 0x064A, /* 3896 */ 0x062A, 0x0631, /* 3898 */ 0x062A, 0x0632, /* 3900 */ 0x062A, 0x0645, /* 3902 */ 0x062A, 0x0646, /* 3904 */ 0x062A, 0x0649, /* 3906 */ 0x062A, 0x064A, /* 3908 */ 0x062B, 0x0631, /* 3910 */ 0x062B, 0x0632, /* 3912 */ 0x062B, 0x0645, /* 3914 */ 0x062B, 0x0646, /* 3916 */ 0x062B, 0x0649, /* 3918 */ 0x062B, 0x064A, /* 3920 */ 0x0641, 0x0649, /* 3922 */ 0x0641, 0x064A, /* 3924 */ 0x0642, 0x0649, /* 3926 */ 0x0642, 0x064A, /* 3928 */ 0x0643, 0x0627, /* 3930 */ 0x0643, 0x0644, /* 3932 */ 0x0643, 0x0645, /* 3934 */ 0x0643, 0x0649, /* 3936 */ 0x0643, 0x064A, /* 3938 */ 0x0644, 0x0645, /* 3940 */ 0x0644, 0x0649, /* 3942 */ 0x0644, 0x064A, /* 3944 */ 0x0645, 0x0627, /* 3946 */ 0x0645, 0x0645, /* 3948 */ 0x0646, 0x0631, /* 3950 */ 0x0646, 0x0632, /* 3952 */ 0x0646, 0x0645, /* 3954 */ 0x0646, 0x0646, /* 3956 */ 0x0646, 0x0649, /* 3958 */ 0x0646, 0x064A, /* 3960 */ 0x0649, 0x0670, /* 3962 */ 0x064A, 0x0631, /* 3964 */ 0x064A, 0x0632, /* 3966 */ 0x064A, 0x0645, /* 3968 */ 0x064A, 0x0646, /* 3970 */ 0x064A, 0x0649, /* 3972 */ 0x064A, 0x064A, /* 3974 */ 0x0626, 0x062C, /* 3976 */ 0x0626, 0x062D, /* 3978 */ 0x0626, 0x062E, /* 3980 */ 0x0626, 0x0645, /* 3982 */ 0x0626, 0x0647, /* 3984 */ 0x0628, 0x062C, /* 3986 */ 0x0628, 0x062D, /* 3988 */ 0x0628, 0x062E, /* 3990 */ 0x0628, 0x0645, /* 3992 */ 0x0628, 0x0647, /* 3994 */ 0x062A, 0x062C, /* 3996 */ 0x062A, 0x062D, /* 3998 */ 0x062A, 0x062E, /* 4000 */ 0x062A, 0x0645, /* 4002 */ 0x062A, 0x0647, /* 4004 */ 0x062B, 0x0645, /* 4006 */ 0x062C, 0x062D, /* 4008 */ 0x062C, 0x0645, /* 4010 */ 0x062D, 0x062C, /* 4012 */ 0x062D, 0x0645, /* 4014 */ 0x062E, 0x062C, /* 4016 */ 0x062E, 0x0645, /* 4018 */ 0x0633, 0x062C, /* 4020 */ 0x0633, 0x062D, /* 4022 */ 0x0633, 0x062E, /* 4024 */ 0x0633, 0x0645, /* 4026 */ 0x0635, 0x062D, /* 4028 */ 0x0635, 0x062E, /* 4030 */ 0x0635, 0x0645, /* 4032 */ 0x0636, 0x062C, /* 4034 */ 0x0636, 0x062D, /* 4036 */ 0x0636, 0x062E, /* 4038 */ 0x0636, 0x0645, /* 4040 */ 0x0637, 0x062D, /* 4042 */ 0x0638, 0x0645, /* 4044 */ 0x0639, 0x062C, /* 4046 */ 0x0639, 0x0645, /* 4048 */ 0x063A, 0x062C, /* 4050 */ 0x063A, 0x0645, /* 4052 */ 0x0641, 0x062C, /* 4054 */ 0x0641, 0x062D, /* 4056 */ 0x0641, 0x062E, /* 4058 */ 0x0641, 0x0645, /* 4060 */ 0x0642, 0x062D, /* 4062 */ 0x0642, 0x0645, /* 4064 */ 0x0643, 0x062C, /* 4066 */ 0x0643, 0x062D, /* 4068 */ 0x0643, 0x062E, /* 4070 */ 0x0643, 0x0644, /* 4072 */ 0x0643, 0x0645, /* 4074 */ 0x0644, 0x062C, /* 4076 */ 0x0644, 0x062D, /* 4078 */ 0x0644, 0x062E, /* 4080 */ 0x0644, 0x0645, /* 4082 */ 0x0644, 0x0647, /* 4084 */ 0x0645, 0x062C, /* 4086 */ 0x0645, 0x062D, /* 4088 */ 0x0645, 0x062E, /* 4090 */ 0x0645, 0x0645, /* 4092 */ 0x0646, 0x062C, /* 4094 */ 0x0646, 0x062D, /* 4096 */ 0x0646, 0x062E, /* 4098 */ 0x0646, 0x0645, /* 4100 */ 0x0646, 0x0647, /* 4102 */ 0x0647, 0x062C, /* 4104 */ 0x0647, 0x0645, /* 4106 */ 0x0647, 0x0670, /* 4108 */ 0x064A, 0x062C, /* 4110 */ 0x064A, 0x062D, /* 4112 */ 0x064A, 0x062E, /* 4114 */ 0x064A, 0x0645, /* 4116 */ 0x064A, 0x0647, /* 4118 */ 0x0626, 0x0645, /* 4120 */ 0x0626, 0x0647, /* 4122 */ 0x0628, 0x0645, /* 4124 */ 0x0628, 0x0647, /* 4126 */ 0x062A, 0x0645, /* 4128 */ 0x062A, 0x0647, /* 4130 */ 0x062B, 0x0645, /* 4132 */ 0x062B, 0x0647, /* 4134 */ 0x0633, 0x0645, /* 4136 */ 0x0633, 0x0647, /* 4138 */ 0x0634, 0x0645, /* 4140 */ 0x0634, 0x0647, /* 4142 */ 0x0643, 0x0644, /* 4144 */ 0x0643, 0x0645, /* 4146 */ 0x0644, 0x0645, /* 4148 */ 0x0646, 0x0645, /* 4150 */ 0x0646, 0x0647, /* 4152 */ 0x064A, 0x0645, /* 4154 */ 0x064A, 0x0647, /* 4156 */ 0x0640, 0x064E, 0x0651, /* 4159 */ 0x0640, 0x064F, 0x0651, /* 4162 */ 0x0640, 0x0650, 0x0651, /* 4165 */ 0x0637, 0x0649, /* 4167 */ 0x0637, 0x064A, /* 4169 */ 0x0639, 0x0649, /* 4171 */ 0x0639, 0x064A, /* 4173 */ 0x063A, 0x0649, /* 4175 */ 0x063A, 0x064A, /* 4177 */ 0x0633, 0x0649, /* 4179 */ 0x0633, 0x064A, /* 4181 */ 0x0634, 0x0649, /* 4183 */ 0x0634, 0x064A, /* 4185 */ 0x062D, 0x0649, /* 4187 */ 0x062D, 0x064A, /* 4189 */ 0x062C, 0x0649, /* 4191 */ 0x062C, 0x064A, /* 4193 */ 0x062E, 0x0649, /* 4195 */ 0x062E, 0x064A, /* 4197 */ 0x0635, 0x0649, /* 4199 */ 0x0635, 0x064A, /* 4201 */ 0x0636, 0x0649, /* 4203 */ 0x0636, 0x064A, /* 4205 */ 0x0634, 0x062C, /* 4207 */ 0x0634, 0x062D, /* 4209 */ 0x0634, 0x062E, /* 4211 */ 0x0634, 0x0645, /* 4213 */ 0x0634, 0x0631, /* 4215 */ 0x0633, 0x0631, /* 4217 */ 0x0635, 0x0631, /* 4219 */ 0x0636, 0x0631, /* 4221 */ 0x0637, 0x0649, /* 4223 */ 0x0637, 0x064A, /* 4225 */ 0x0639, 0x0649, /* 4227 */ 0x0639, 0x064A, /* 4229 */ 0x063A, 0x0649, /* 4231 */ 0x063A, 0x064A, /* 4233 */ 0x0633, 0x0649, /* 4235 */ 0x0633, 0x064A, /* 4237 */ 0x0634, 0x0649, /* 4239 */ 0x0634, 0x064A, /* 4241 */ 0x062D, 0x0649, /* 4243 */ 0x062D, 0x064A, /* 4245 */ 0x062C, 0x0649, /* 4247 */ 0x062C, 0x064A, /* 4249 */ 0x062E, 0x0649, /* 4251 */ 0x062E, 0x064A, /* 4253 */ 0x0635, 0x0649, /* 4255 */ 0x0635, 0x064A, /* 4257 */ 0x0636, 0x0649, /* 4259 */ 0x0636, 0x064A, /* 4261 */ 0x0634, 0x062C, /* 4263 */ 0x0634, 0x062D, /* 4265 */ 0x0634, 0x062E, /* 4267 */ 0x0634, 0x0645, /* 4269 */ 0x0634, 0x0631, /* 4271 */ 0x0633, 0x0631, /* 4273 */ 0x0635, 0x0631, /* 4275 */ 0x0636, 0x0631, /* 4277 */ 0x0634, 0x062C, /* 4279 */ 0x0634, 0x062D, /* 4281 */ 0x0634, 0x062E, /* 4283 */ 0x0634, 0x0645, /* 4285 */ 0x0633, 0x0647, /* 4287 */ 0x0634, 0x0647, /* 4289 */ 0x0637, 0x0645, /* 4291 */ 0x0633, 0x062C, /* 4293 */ 0x0633, 0x062D, /* 4295 */ 0x0633, 0x062E, /* 4297 */ 0x0634, 0x062C, /* 4299 */ 0x0634, 0x062D, /* 4301 */ 0x0634, 0x062E, /* 4303 */ 0x0637, 0x0645, /* 4305 */ 0x0638, 0x0645, /* 4307 */ 0x0627, 0x064B, /* 4309 */ 0x0627, 0x064B, /* 4311 */ 0x062A, 0x062C, 0x0645, /* 4314 */ 0x062A, 0x062D, 0x062C, /* 4317 */ 0x062A, 0x062D, 0x062C, /* 4320 */ 0x062A, 0x062D, 0x0645, /* 4323 */ 0x062A, 0x062E, 0x0645, /* 4326 */ 0x062A, 0x0645, 0x062C, /* 4329 */ 0x062A, 0x0645, 0x062D, /* 4332 */ 0x062A, 0x0645, 0x062E, /* 4335 */ 0x062C, 0x0645, 0x062D, /* 4338 */ 0x062C, 0x0645, 0x062D, /* 4341 */ 0x062D, 0x0645, 0x064A, /* 4344 */ 0x062D, 0x0645, 0x0649, /* 4347 */ 0x0633, 0x062D, 0x062C, /* 4350 */ 0x0633, 0x062C, 0x062D, /* 4353 */ 0x0633, 0x062C, 0x0649, /* 4356 */ 0x0633, 0x0645, 0x062D, /* 4359 */ 0x0633, 0x0645, 0x062D, /* 4362 */ 0x0633, 0x0645, 0x062C, /* 4365 */ 0x0633, 0x0645, 0x0645, /* 4368 */ 0x0633, 0x0645, 0x0645, /* 4371 */ 0x0635, 0x062D, 0x062D, /* 4374 */ 0x0635, 0x062D, 0x062D, /* 4377 */ 0x0635, 0x0645, 0x0645, /* 4380 */ 0x0634, 0x062D, 0x0645, /* 4383 */ 0x0634, 0x062D, 0x0645, /* 4386 */ 0x0634, 0x062C, 0x064A, /* 4389 */ 0x0634, 0x0645, 0x062E, /* 4392 */ 0x0634, 0x0645, 0x062E, /* 4395 */ 0x0634, 0x0645, 0x0645, /* 4398 */ 0x0634, 0x0645, 0x0645, /* 4401 */ 0x0636, 0x062D, 0x0649, /* 4404 */ 0x0636, 0x062E, 0x0645, /* 4407 */ 0x0636, 0x062E, 0x0645, /* 4410 */ 0x0637, 0x0645, 0x062D, /* 4413 */ 0x0637, 0x0645, 0x062D, /* 4416 */ 0x0637, 0x0645, 0x0645, /* 4419 */ 0x0637, 0x0645, 0x064A, /* 4422 */ 0x0639, 0x062C, 0x0645, /* 4425 */ 0x0639, 0x0645, 0x0645, /* 4428 */ 0x0639, 0x0645, 0x0645, /* 4431 */ 0x0639, 0x0645, 0x0649, /* 4434 */ 0x063A, 0x0645, 0x0645, /* 4437 */ 0x063A, 0x0645, 0x064A, /* 4440 */ 0x063A, 0x0645, 0x0649, /* 4443 */ 0x0641, 0x062E, 0x0645, /* 4446 */ 0x0641, 0x062E, 0x0645, /* 4449 */ 0x0642, 0x0645, 0x062D, /* 4452 */ 0x0642, 0x0645, 0x0645, /* 4455 */ 0x0644, 0x062D, 0x0645, /* 4458 */ 0x0644, 0x062D, 0x064A, /* 4461 */ 0x0644, 0x062D, 0x0649, /* 4464 */ 0x0644, 0x062C, 0x062C, /* 4467 */ 0x0644, 0x062C, 0x062C, /* 4470 */ 0x0644, 0x062E, 0x0645, /* 4473 */ 0x0644, 0x062E, 0x0645, /* 4476 */ 0x0644, 0x0645, 0x062D, /* 4479 */ 0x0644, 0x0645, 0x062D, /* 4482 */ 0x0645, 0x062D, 0x062C, /* 4485 */ 0x0645, 0x062D, 0x0645, /* 4488 */ 0x0645, 0x062D, 0x064A, /* 4491 */ 0x0645, 0x062C, 0x062D, /* 4494 */ 0x0645, 0x062C, 0x0645, /* 4497 */ 0x0645, 0x062E, 0x062C, /* 4500 */ 0x0645, 0x062E, 0x0645, /* 4503 */ 0x0645, 0x062C, 0x062E, /* 4506 */ 0x0647, 0x0645, 0x062C, /* 4509 */ 0x0647, 0x0645, 0x0645, /* 4512 */ 0x0646, 0x062D, 0x0645, /* 4515 */ 0x0646, 0x062D, 0x0649, /* 4518 */ 0x0646, 0x062C, 0x0645, /* 4521 */ 0x0646, 0x062C, 0x0645, /* 4524 */ 0x0646, 0x062C, 0x0649, /* 4527 */ 0x0646, 0x0645, 0x064A, /* 4530 */ 0x0646, 0x0645, 0x0649, /* 4533 */ 0x064A, 0x0645, 0x0645, /* 4536 */ 0x064A, 0x0645, 0x0645, /* 4539 */ 0x0628, 0x062E, 0x064A, /* 4542 */ 0x062A, 0x062C, 0x064A, /* 4545 */ 0x062A, 0x062C, 0x0649, /* 4548 */ 0x062A, 0x062E, 0x064A, /* 4551 */ 0x062A, 0x062E, 0x0649, /* 4554 */ 0x062A, 0x0645, 0x064A, /* 4557 */ 0x062A, 0x0645, 0x0649, /* 4560 */ 0x062C, 0x0645, 0x064A, /* 4563 */ 0x062C, 0x062D, 0x0649, /* 4566 */ 0x062C, 0x0645, 0x0649, /* 4569 */ 0x0633, 0x062E, 0x0649, /* 4572 */ 0x0635, 0x062D, 0x064A, /* 4575 */ 0x0634, 0x062D, 0x064A, /* 4578 */ 0x0636, 0x062D, 0x064A, /* 4581 */ 0x0644, 0x062C, 0x064A, /* 4584 */ 0x0644, 0x0645, 0x064A, /* 4587 */ 0x064A, 0x062D, 0x064A, /* 4590 */ 0x064A, 0x062C, 0x064A, /* 4593 */ 0x064A, 0x0645, 0x064A, /* 4596 */ 0x0645, 0x0645, 0x064A, /* 4599 */ 0x0642, 0x0645, 0x064A, /* 4602 */ 0x0646, 0x062D, 0x064A, /* 4605 */ 0x0642, 0x0645, 0x062D, /* 4608 */ 0x0644, 0x062D, 0x0645, /* 4611 */ 0x0639, 0x0645, 0x064A, /* 4614 */ 0x0643, 0x0645, 0x064A, /* 4617 */ 0x0646, 0x062C, 0x062D, /* 4620 */ 0x0645, 0x062E, 0x064A, /* 4623 */ 0x0644, 0x062C, 0x0645, /* 4626 */ 0x0643, 0x0645, 0x0645, /* 4629 */ 0x0644, 0x062C, 0x0645, /* 4632 */ 0x0646, 0x062C, 0x062D, /* 4635 */ 0x062C, 0x062D, 0x064A, /* 4638 */ 0x062D, 0x062C, 0x064A, /* 4641 */ 0x0645, 0x062C, 0x064A, /* 4644 */ 0x0641, 0x0645, 0x064A, /* 4647 */ 0x0628, 0x062D, 0x064A, /* 4650 */ 0x0643, 0x0645, 0x0645, /* 4653 */ 0x0639, 0x062C, 0x0645, /* 4656 */ 0x0635, 0x0645, 0x0645, /* 4659 */ 0x0633, 0x062E, 0x064A, /* 4662 */ 0x0646, 0x062C, 0x064A, /* 4665 */ 0x0635, 0x0644, 0x06D2, /* 4668 */ 0x0642, 0x0644, 0x06D2, /* 4671 */ 0x0627, 0x0644, 0x0644, 0x0647, /* 4675 */ 0x0627, 0x0643, 0x0628, 0x0631, /* 4679 */ 0x0645, 0x062D, 0x0645, 0x062F, /* 4683 */ 0x0635, 0x0644, 0x0639, 0x0645, /* 4687 */ 0x0631, 0x0633, 0x0648, 0x0644, /* 4691 */ 0x0639, 0x0644, 0x064A, 0x0647, /* 4695 */ 0x0648, 0x0633, 0x0644, 0x0645, /* 4699 */ 0x0635, 0x0644, 0x0649, /* 4702 */ 0x0635, 0x0644, 0x0649, 0x0020, 0x0627, 0x0644, 0x0644, 0x0647, 0x0020, 0x0639, 0x0644, 0x064A, 0x0647, 0x0020, 0x0648, 0x0633, 0x0644, 0x0645, /* 4720 */ 0x062C, 0x0644, 0x0020, 0x062C, 0x0644, 0x0627, 0x0644, 0x0647, /* 4728 */ 0x0631, 0x06CC, 0x0627, 0x0644, /* 4732 */ 0x0020, 0x064B, /* 4734 */ 0x0640, 0x064B, /* 4736 */ 0x0020, 0x064C, /* 4738 */ 0x0020, 0x064D, /* 4740 */ 0x0020, 0x064E, /* 4742 */ 0x0640, 0x064E, /* 4744 */ 0x0020, 0x064F, /* 4746 */ 0x0640, 0x064F, /* 4748 */ 0x0020, 0x0650, /* 4750 */ 0x0640, 0x0650, /* 4752 */ 0x0020, 0x0651, /* 4754 */ 0x0640, 0x0651, /* 4756 */ 0x0020, 0x0652, /* 4758 */ 0x0640, 0x0652, /* 4760 */ 0x0644, 0x0622, /* 4762 */ 0x0644, 0x0622, /* 4764 */ 0x0644, 0x0623, /* 4766 */ 0x0644, 0x0623, /* 4768 */ 0x0644, 0x0625, /* 4770 */ 0x0644, 0x0625, /* 4772 */ 0x0644, 0x0627, /* 4774 */ 0x0644, 0x0627, /* 4776 */ 0x11099, 0x110BA, /* 4778 */ 0x1109B, 0x110BA, /* 4780 */ 0x110A5, 0x110BA, /* 4782 */ 0x11131, 0x11127, /* 4784 */ 0x11132, 0x11127, /* 4786 */ 0x11347, 0x1133E, /* 4788 */ 0x11347, 0x11357, /* 4790 */ 0x114B9, 0x114BA, /* 4792 */ 0x114B9, 0x114B0, /* 4794 */ 0x114B9, 0x114BD, /* 4796 */ 0x115B8, 0x115AF, /* 4798 */ 0x115B9, 0x115AF, /* 4800 */ 0x11935, 0x11930, /* 4802 */ 0x1D157, 0x1D165, /* 4804 */ 0x1D158, 0x1D165, /* 4806 */ 0x1D15F, 0x1D16E, /* 4808 */ 0x1D15F, 0x1D16F, /* 4810 */ 0x1D15F, 0x1D170, /* 4812 */ 0x1D15F, 0x1D171, /* 4814 */ 0x1D15F, 0x1D172, /* 4816 */ 0x1D1B9, 0x1D165, /* 4818 */ 0x1D1BA, 0x1D165, /* 4820 */ 0x1D1BB, 0x1D16E, /* 4822 */ 0x1D1BC, 0x1D16E, /* 4824 */ 0x1D1BB, 0x1D16F, /* 4826 */ 0x1D1BC, 0x1D16F, /* 4828 */ 0x0030, 0x002E, /* 4830 */ 0x0030, 0x002C, /* 4832 */ 0x0031, 0x002C, /* 4834 */ 0x0032, 0x002C, /* 4836 */ 0x0033, 0x002C, /* 4838 */ 0x0034, 0x002C, /* 4840 */ 0x0035, 0x002C, /* 4842 */ 0x0036, 0x002C, /* 4844 */ 0x0037, 0x002C, /* 4846 */ 0x0038, 0x002C, /* 4848 */ 0x0039, 0x002C, /* 4850 */ 0x0028, 0x0041, 0x0029, /* 4853 */ 0x0028, 0x0042, 0x0029, /* 4856 */ 0x0028, 0x0043, 0x0029, /* 4859 */ 0x0028, 0x0044, 0x0029, /* 4862 */ 0x0028, 0x0045, 0x0029, /* 4865 */ 0x0028, 0x0046, 0x0029, /* 4868 */ 0x0028, 0x0047, 0x0029, /* 4871 */ 0x0028, 0x0048, 0x0029, /* 4874 */ 0x0028, 0x0049, 0x0029, /* 4877 */ 0x0028, 0x004A, 0x0029, /* 4880 */ 0x0028, 0x004B, 0x0029, /* 4883 */ 0x0028, 0x004C, 0x0029, /* 4886 */ 0x0028, 0x004D, 0x0029, /* 4889 */ 0x0028, 0x004E, 0x0029, /* 4892 */ 0x0028, 0x004F, 0x0029, /* 4895 */ 0x0028, 0x0050, 0x0029, /* 4898 */ 0x0028, 0x0051, 0x0029, /* 4901 */ 0x0028, 0x0052, 0x0029, /* 4904 */ 0x0028, 0x0053, 0x0029, /* 4907 */ 0x0028, 0x0054, 0x0029, /* 4910 */ 0x0028, 0x0055, 0x0029, /* 4913 */ 0x0028, 0x0056, 0x0029, /* 4916 */ 0x0028, 0x0057, 0x0029, /* 4919 */ 0x0028, 0x0058, 0x0029, /* 4922 */ 0x0028, 0x0059, 0x0029, /* 4925 */ 0x0028, 0x005A, 0x0029, /* 4928 */ 0x3014, 0x0053, 0x3015, /* 4931 */ 0x0043, 0x0044, /* 4933 */ 0x0057, 0x005A, /* 4935 */ 0x0048, 0x0056, /* 4937 */ 0x004D, 0x0056, /* 4939 */ 0x0053, 0x0044, /* 4941 */ 0x0053, 0x0053, /* 4943 */ 0x0050, 0x0050, 0x0056, /* 4946 */ 0x0057, 0x0043, /* 4948 */ 0x004D, 0x0043, /* 4950 */ 0x004D, 0x0044, /* 4952 */ 0x004D, 0x0052, /* 4954 */ 0x0044, 0x004A, /* 4956 */ 0x307B, 0x304B, /* 4958 */ 0x30B3, 0x30B3, /* 4960 */ 0x3014, 0x672C, 0x3015, /* 4963 */ 0x3014, 0x4E09, 0x3015, /* 4966 */ 0x3014, 0x4E8C, 0x3015, /* 4969 */ 0x3014, 0x5B89, 0x3015, /* 4972 */ 0x3014, 0x70B9, 0x3015, /* 4975 */ 0x3014, 0x6253, 0x3015, /* 4978 */ 0x3014, 0x76D7, 0x3015, /* 4981 */ 0x3014, 0x52DD, 0x3015, /* 4984 */ 0x3014, 0x6557, 0x3015, /* 4987 */ 0x20122, /* 4988 */ 0x2063A, /* 4989 */ 0x2051C, /* 4990 */ 0x2054B, /* 4991 */ 0x291DF, /* 4992 */ 0x20A2C, /* 4993 */ 0x20B63, /* 4994 */ 0x214E4, /* 4995 */ 0x216A8, /* 4996 */ 0x216EA, /* 4997 */ 0x219C8, /* 4998 */ 0x21B18, /* 4999 */ 0x21DE4, /* 5000 */ 0x21DE6, /* 5001 */ 0x22183, /* 5002 */ 0x2A392, /* 5003 */ 0x22331, /* 5004 */ 0x22331, /* 5005 */ 0x232B8, /* 5006 */ 0x261DA, /* 5007 */ 0x226D4, /* 5008 */ 0x22B0C, /* 5009 */ 0x22BF1, /* 5010 */ 0x2300A, /* 5011 */ 0x233C3, /* 5012 */ 0x2346D, /* 5013 */ 0x236A3, /* 5014 */ 0x238A7, /* 5015 */ 0x23A8D, /* 5016 */ 0x21D0B, /* 5017 */ 0x23AFA, /* 5018 */ 0x23CBC, /* 5019 */ 0x23D1E, /* 5020 */ 0x23ED1, /* 5021 */ 0x23F5E, /* 5022 */ 0x23F8E, /* 5023 */ 0x20525, /* 5024 */ 0x24263, /* 5025 */ 0x243AB, /* 5026 */ 0x24608, /* 5027 */ 0x24735, /* 5028 */ 0x24814, /* 5029 */ 0x24C36, /* 5030 */ 0x24C92, /* 5031 */ 0x2219F, /* 5032 */ 0x24FA1, /* 5033 */ 0x24FB8, /* 5034 */ 0x25044, /* 5035 */ 0x250F3, /* 5036 */ 0x250F2, /* 5037 */ 0x25119, /* 5038 */ 0x25133, /* 5039 */ 0x2541D, /* 5040 */ 0x25626, /* 5041 */ 0x2569A, /* 5042 */ 0x256C5, /* 5043 */ 0x2597C, /* 5044 */ 0x25AA7, /* 5045 */ 0x25AA7, /* 5046 */ 0x25BAB, /* 5047 */ 0x25C80, /* 5048 */ 0x25F86, /* 5049 */ 0x26228, /* 5050 */ 0x26247, /* 5051 */ 0x262D9, /* 5052 */ 0x2633E, /* 5053 */ 0x264DA, /* 5054 */ 0x26523, /* 5055 */ 0x265A8, /* 5056 */ 0x2335F, /* 5057 */ 0x267A7, /* 5058 */ 0x267B5, /* 5059 */ 0x23393, /* 5060 */ 0x2339C, /* 5061 */ 0x26B3C, /* 5062 */ 0x26C36, /* 5063 */ 0x26D6B, /* 5064 */ 0x26CD5, /* 5065 */ 0x273CA, /* 5066 */ 0x26F2C, /* 5067 */ 0x26FB1, /* 5068 */ 0x270D2, /* 5069 */ 0x27667, /* 5070 */ 0x278AE, /* 5071 */ 0x27966, /* 5072 */ 0x27CA8, /* 5073 */ 0x27F2F, /* 5074 */ 0x20804, /* 5075 */ 0x208DE, /* 5076 */ 0x285D2, /* 5077 */ 0x285ED, /* 5078 */ 0x2872E, /* 5079 */ 0x28BFA, /* 5080 */ 0x28D77, /* 5081 */ 0x29145, /* 5082 */ 0x2921A, /* 5083 */ 0x2940A, /* 5084 */ 0x29496, /* 5085 */ 0x295B6, /* 5086 */ 0x29B30, /* 5087 */ 0x2A0CE, /* 5088 */ 0x2A105, /* 5089 */ 0x2A20E, /* 5090 */ 0x2A291, /* 5091 */ 0x2A600 }; pgbouncer-1.24.1/include/common/protocol.h0000644000175000000000000000570014777762222015436 00000000000000/*------------------------------------------------------------------------- * * protocol.h * Definitions of the request/response codes for the wire protocol. * * * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/include/libpq/protocol.h * *------------------------------------------------------------------------- */ #ifndef PROTOCOL_H #define PROTOCOL_H /* These are the request codes sent by the frontend. */ #define PqMsg_Bind 'B' #define PqMsg_Close 'C' #define PqMsg_Describe 'D' #define PqMsg_Execute 'E' #define PqMsg_FunctionCall 'F' #define PqMsg_Flush 'H' #define PqMsg_Parse 'P' #define PqMsg_Query 'Q' #define PqMsg_Sync 'S' #define PqMsg_Terminate 'X' #define PqMsg_CopyFail 'f' #define PqMsg_GSSResponse 'p' #define PqMsg_PasswordMessage 'p' #define PqMsg_SASLInitialResponse 'p' #define PqMsg_SASLResponse 'p' /* These are the response codes sent by the backend. */ #define PqMsg_ParseComplete '1' #define PqMsg_BindComplete '2' #define PqMsg_CloseComplete '3' #define PqMsg_NotificationResponse 'A' #define PqMsg_CommandComplete 'C' #define PqMsg_DataRow 'D' #define PqMsg_ErrorResponse 'E' #define PqMsg_CopyInResponse 'G' #define PqMsg_CopyOutResponse 'H' #define PqMsg_EmptyQueryResponse 'I' #define PqMsg_BackendKeyData 'K' #define PqMsg_NoticeResponse 'N' #define PqMsg_AuthenticationRequest 'R' #define PqMsg_ParameterStatus 'S' #define PqMsg_RowDescription 'T' #define PqMsg_FunctionCallResponse 'V' #define PqMsg_CopyBothResponse 'W' #define PqMsg_ReadyForQuery 'Z' #define PqMsg_NoData 'n' #define PqMsg_PortalSuspended 's' #define PqMsg_ParameterDescription 't' #define PqMsg_NegotiateProtocolVersion 'v' /* These are the codes sent by both the frontend and backend. */ #define PqMsg_CopyDone 'c' #define PqMsg_CopyData 'd' /* These are the codes sent by parallel workers to leader processes. */ #define PqMsg_Progress 'P' /* These are the authentication request codes sent by the backend. */ #define AUTH_REQ_OK 0 /* User is authenticated */ #define AUTH_REQ_KRB4 1 /* Kerberos V4. Not supported any more. */ #define AUTH_REQ_KRB5 2 /* Kerberos V5. Not supported any more. */ #define AUTH_REQ_PASSWORD 3 /* Password */ #define AUTH_REQ_CRYPT 4 /* crypt password. Not supported any more. */ #define AUTH_REQ_MD5 5 /* md5 password */ /* 6 is available. It was used for SCM creds, not supported any more. */ #define AUTH_REQ_GSS 7 /* GSSAPI without wrap() */ #define AUTH_REQ_GSS_CONT 8 /* Continue GSS exchanges */ #define AUTH_REQ_SSPI 9 /* SSPI negotiate without wrap() */ #define AUTH_REQ_SASL 10 /* Begin SASL authentication */ #define AUTH_REQ_SASL_CONT 11 /* Continue SASL authentication */ #define AUTH_REQ_SASL_FIN 12 /* Final SASL message */ #define AUTH_REQ_MAX AUTH_REQ_SASL_FIN /* maximum AUTH_REQ_* value */ #endif /* PROTOCOL_H */ pgbouncer-1.24.1/include/common/postgres_compat.h0000644000175000000000000000165314777762222017011 00000000000000/* * Various things to allow source files from postgresql code to be * used in pgbouncer. pgbouncer's system.h needs to be included * before this. */ /* from c.h */ #include #include #define int8 int8_t #define uint8 uint8_t #define uint16 uint16_t #define uint32 uint32_t #define lengthof(array) (sizeof (array) / sizeof ((array)[0])) #define pg_hton32(x) htobe32(x) #define pg_attribute_noreturn() _NORETURN #define HIGHBIT (0x80) #define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT) /* sha2.h compat */ #define pg_sha256_ctx struct sha256_ctx #define PG_SHA256_BLOCK_LENGTH SHA256_BLOCK_SIZE #define PG_SHA256_DIGEST_LENGTH SHA256_DIGEST_LENGTH #define pg_sha256_init(ctx) sha256_reset(ctx) #define pg_sha256_update(ctx, data, len) sha256_update(ctx, data, len) #define pg_sha256_final(ctx, dst) sha256_final(ctx, dst) /* define this to use non-server code paths */ #define FRONTEND pgbouncer-1.24.1/include/common/scram-common.h0000644000175000000000000000450514777762222016172 00000000000000/*------------------------------------------------------------------------- * * scram-common.h * Declarations for helper functions used for SCRAM authentication * * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/include/common/scram-common.h * *------------------------------------------------------------------------- */ #ifndef SCRAM_COMMON_H #define SCRAM_COMMON_H #include "common/postgres_compat.h" //#include "common/sha2.h" #include "usual/crypto/sha256.h" /* Name of SCRAM mechanisms per IANA */ #define SCRAM_SHA_256_NAME "SCRAM-SHA-256" #define SCRAM_SHA_256_PLUS_NAME "SCRAM-SHA-256-PLUS" /* with channel binding */ /* Length of SCRAM keys (client and server) */ #define SCRAM_KEY_LEN PG_SHA256_DIGEST_LENGTH /* length of HMAC */ #define SHA256_HMAC_B PG_SHA256_BLOCK_LENGTH /* * Size of random nonce generated in the authentication exchange. This * is in "raw" number of bytes, the actual nonces sent over the wire are * encoded using only ASCII-printable characters. */ #define SCRAM_RAW_NONCE_LEN 18 /* * Length of salt when generating new secrets, in bytes. (It will be stored * and sent over the wire encoded in Base64.) 16 bytes is what the example in * RFC 7677 uses. */ #define SCRAM_DEFAULT_SALT_LEN 16 /* * Default number of iterations when generating secret. Should be at least * 4096 per RFC 7677. */ #define SCRAM_DEFAULT_ITERATIONS 4096 /* * Context data for HMAC used in SCRAM authentication. */ typedef struct { pg_sha256_ctx sha256ctx; uint8 k_opad[SHA256_HMAC_B]; } scram_HMAC_ctx; extern void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen); extern void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen); extern void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx); extern void scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result); extern void scram_H(const uint8 *str, int len, uint8 *result); extern void scram_ClientKey(const uint8 *salted_password, uint8 *result); extern void scram_ServerKey(const uint8 *salted_password, uint8 *result); extern char *scram_build_secret(const char *salt, int saltlen, int iterations, const char *password); #endif /* SCRAM_COMMON_H */ pgbouncer-1.24.1/include/common/unicode_combining_table.h0000644000175000000000000000731214777762222020420 00000000000000/* generated by src/common/unicode/generate-unicode_combining_table.pl, do not edit */ static const struct mbinterval combining[] = { {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F}, {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711}, {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x07FD, 0x07FD}, {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827}, {0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D3, 0x08E1}, {0x08E3, 0x0902}, {0x093A, 0x093A}, {0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, {0x09FE, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A51}, {0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0AFA, 0x0B01}, {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B44}, {0x0B4D, 0x0B56}, {0x0B62, 0x0B63}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C81}, {0x0CBC, 0x0CBC}, {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, {0x0CE2, 0x0CE3}, {0x0D00, 0x0D01}, {0x0D3B, 0x0D3C}, {0x0D41, 0x0D44}, {0x0D4D, 0x0D4D}, {0x0D62, 0x0D63}, {0x0D81, 0x0D81}, {0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD6}, {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1037}, {0x1039, 0x103A}, {0x103D, 0x103E}, {0x1058, 0x1059}, {0x105E, 0x1060}, {0x1071, 0x1074}, {0x1082, 0x1082}, {0x1085, 0x1086}, {0x108D, 0x108D}, {0x109D, 0x109D}, {0x135D, 0x135F}, {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD}, {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9}, {0x1920, 0x1922}, {0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B}, {0x1A17, 0x1A18}, {0x1A1B, 0x1A1B}, {0x1A56, 0x1A56}, {0x1A58, 0x1A60}, {0x1A62, 0x1A62}, {0x1A65, 0x1A6C}, {0x1A73, 0x1A7F}, {0x1AB0, 0x1B03}, {0x1B34, 0x1B34}, {0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42}, {0x1B6B, 0x1B73}, {0x1B80, 0x1B81}, {0x1BA2, 0x1BA5}, {0x1BA8, 0x1BA9}, {0x1BAB, 0x1BAD}, {0x1BE6, 0x1BE6}, {0x1BE8, 0x1BE9}, {0x1BED, 0x1BED}, {0x1BEF, 0x1BF1}, {0x1C2C, 0x1C33}, {0x1C36, 0x1C37}, {0x1CD0, 0x1CD2}, {0x1CD4, 0x1CE0}, {0x1CE2, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF4, 0x1CF4}, {0x1CF8, 0x1CF9}, {0x1DC0, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, {0x2DE0, 0x2DFF}, {0x302A, 0x302D}, {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, {0xA80B, 0xA80B}, {0xA825, 0xA826}, {0xA82C, 0xA82C}, {0xA8C4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA8FF, 0xA8FF}, {0xA926, 0xA92D}, {0xA947, 0xA951}, {0xA980, 0xA982}, {0xA9B3, 0xA9B3}, {0xA9B6, 0xA9B9}, {0xA9BC, 0xA9BD}, {0xA9E5, 0xA9E5}, {0xAA29, 0xAA2E}, {0xAA31, 0xAA32}, {0xAA35, 0xAA36}, {0xAA43, 0xAA43}, {0xAA4C, 0xAA4C}, {0xAA7C, 0xAA7C}, {0xAAB0, 0xAAB0}, {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, {0xAAC1, 0xAAC1}, {0xAAEC, 0xAAED}, {0xAAF6, 0xAAF6}, {0xABE5, 0xABE5}, {0xABE8, 0xABE8}, {0xABED, 0xABED}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, }; pgbouncer-1.24.1/include/common/base64.h0000644000175000000000000000100314777762222014651 00000000000000/* * base64.h * Encoding and decoding routines for base64 without whitespace * support. * * Portions Copyright (c) 2001-2020, PostgreSQL Global Development Group * * src/include/common/base64.h */ #ifndef BASE64_H #define BASE64_H /* base 64 */ extern int pg_b64_encode(const char *src, int len, char *dst, int dstlen); extern int pg_b64_decode(const char *src, int len, char *dst, int dstlen); extern int pg_b64_enc_len(int srclen); extern int pg_b64_dec_len(int srclen); #endif /* BASE64_H */ pgbouncer-1.24.1/include/proto.h0000644000175000000000000000625214777762222013453 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* old style V2 header: len:4b code:4b */ #define OLD_HEADER_LEN 8 /* new style V3 packet header len - type:1b, len:4b */ #define NEW_HEADER_LEN 5 /* * parsed packet header, plus whatever data is * available in SBuf for this packet. * * if (pkt->len == mbuf_avail(&pkt->data)) * packet is fully in buffer * * get_header() points pkt->data.pos after header. * to packet body. */ struct PktHdr { unsigned type; unsigned len; struct MBuf data; }; bool get_header(struct MBuf *data, PktHdr *pkt) _MUSTCHECK; bool send_pooler_error(PgSocket *client, bool send_ready, const char *sqlstate, bool level_fatal, const char *msg) /*_MUSTCHECK*/; void log_server_error(const char *note, PktHdr *pkt); void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p, const char **sqlstate_p); bool add_welcome_parameter(PgPool *pool, const char *key, const char *val) _MUSTCHECK; void finish_welcome_msg(PgSocket *server); bool welcome_client(PgSocket *client) _MUSTCHECK; bool answer_authreq(PgSocket *server, PktHdr *pkt) _MUSTCHECK; bool send_startup_packet(PgSocket *server) _MUSTCHECK; bool send_sslreq_packet(PgSocket *server) _MUSTCHECK; int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...) _MUSTCHECK; /* reset the packet header and free the backing buffer */ static inline void free_header(PktHdr *pkt) { mbuf_free(&pkt->data); pkt->type = 0; pkt->len = 0; } /* is packet completely in our buffer */ static inline bool incomplete_pkt(const PktHdr *pkt) { return mbuf_written(&pkt->data) != pkt->len; } /* is packet header completely in buffer */ static inline bool incomplete_header(const struct MBuf *data) { uint32_t avail = mbuf_avail_for_read(data); if (avail >= OLD_HEADER_LEN) return false; if (avail < NEW_HEADER_LEN) return true; /* is it old V2 header? */ return data->data[data->read_pos] == 0; } /* * rewind the body of a v3 packet. Does not work for v2 packets, e.g. startup * packets */ static inline void pkt_rewind_v3(PktHdr *pkt) { pkt->data.read_pos = NEW_HEADER_LEN; } /* * rewind the body of a v2 packet. Does not work for v3 packets, i.e. * everything except a startup packet */ static inline void pkt_rewind_v2(PktHdr *pkt) { pkt->data.read_pos = OLD_HEADER_LEN; } /* one char desc */ static inline char pkt_desc(const PktHdr *pkt) { return pkt->type > 256 ? '!' : pkt->type; } pgbouncer-1.24.1/include/hba.h0000644000175000000000000000372414777762222013043 00000000000000/* * Host-Based-Access-control file support. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define ADDRESS_ALL 1 #define NAME_ALL 1 #define NAME_SAMEUSER 2 #define NAME_REPLICATION 4 enum RuleType { RULE_LOCAL, RULE_HOST, RULE_HOSTSSL, RULE_HOSTNOSSL, }; struct HBAAddress { unsigned int flags; int family; uint8_t addr[16]; uint8_t mask[16]; }; struct HBAName { unsigned int flags; struct StrSet *name_set; }; struct HBARule { struct List node; enum RuleType rule_type; int rule_method; struct HBAAddress address; struct HBAName db_name; struct HBAName user_name; struct IdentMap *identmap; int hba_linenr; }; struct HBA { struct List rules; }; struct Mapping { struct List node; char *system_user_name; char *postgres_user_name; unsigned int name_flags; }; struct IdentMap { struct List node; char *map_name; struct List mappings; }; struct Ident { struct List maps; }; struct Ident *ident_load_map(const char *fn); void ident_free(struct Ident *ident); struct HBA *hba_load_rules(const char *fn, struct Ident *ident); void hba_free(struct HBA *hba); struct HBARule *hba_eval(struct HBA *hba, PgAddr *addr, bool is_tls, ReplicationType replication, const char *dbname, const char *username); pgbouncer-1.24.1/include/iobuf.h0000644000175000000000000000706414777762222013416 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * Temporary buffer for single i/o. * * Pattern: * * iobuf_get_and_reset() * start: * iobuf_recv() * loop: * if (new_pkt) * iobuf_parse() * * if (send) { * iobuf_tag_send() * } else { * send_pending() * iobuf_tag_skip() * } * if (more-unparsed) * goto loop; * send_pending(); */ /* * 0 .. done_pos -- sent * done_pos .. parse_pos -- parsed, to send * parse_pos .. recv_pos -- received, to parse */ struct iobuf { unsigned done_pos; unsigned parse_pos; unsigned recv_pos; uint8_t buf[FLEX_ARRAY]; }; typedef struct iobuf IOBuf; static inline bool iobuf_sane(const IOBuf *io) { return (io == NULL) || (io->parse_pos >= io->done_pos && io->recv_pos >= io->parse_pos && (unsigned)cf_sbuf_len >= io->recv_pos); } static inline bool iobuf_empty(const IOBuf *io) { return io == NULL || io->done_pos == io->recv_pos; } /* unsent amount */ static inline unsigned iobuf_amount_pending(const IOBuf *buf) { return buf->parse_pos - buf->done_pos; } /* max possible to parse (tag_send/tag_skip) */ static inline unsigned iobuf_amount_parse(const IOBuf *buf) { return buf->recv_pos - buf->parse_pos; } /* max possible to recv */ static inline unsigned iobuf_amount_recv(const IOBuf *buf) { return cf_sbuf_len - buf->recv_pos; } /* put all unparsed to mbuf */ static inline unsigned iobuf_parse_all(const IOBuf *buf, struct MBuf *mbuf) { unsigned avail = iobuf_amount_parse(buf); const uint8_t *pos = buf->buf + buf->parse_pos; mbuf_init_fixed_reader(mbuf, pos, avail); return avail; } /* put all unparsed to mbuf, with size limit */ static inline unsigned iobuf_parse_limit(const IOBuf *buf, struct MBuf *mbuf, unsigned limit) { unsigned avail = iobuf_amount_parse(buf); const uint8_t *pos = buf->buf + buf->parse_pos; if (avail > limit) avail = limit; mbuf_init_fixed_reader(mbuf, pos, avail); return avail; } static inline void iobuf_tag_send(IOBuf *io, unsigned len) { Assert(len > 0 && len <= iobuf_amount_parse(io)); io->parse_pos += len; } static inline void iobuf_tag_skip(IOBuf *io, unsigned len) { Assert(io->parse_pos == io->done_pos); /* no send pending */ Assert(len > 0 && len <= iobuf_amount_parse(io)); io->parse_pos += len; io->done_pos = io->parse_pos; } static inline void iobuf_try_resync(IOBuf *io, unsigned small_pkt) { unsigned avail = io->recv_pos - io->done_pos; if (avail == 0) { if (io->recv_pos > 0) io->recv_pos = io->parse_pos = io->done_pos = 0; } else if (avail <= small_pkt && io->done_pos > 0) { memmove(io->buf, io->buf + io->done_pos, avail); io->parse_pos -= io->done_pos; io->recv_pos = avail; io->done_pos = 0; } } static inline void iobuf_reset(IOBuf *io) { io->recv_pos = io->parse_pos = io->done_pos = 0; } pgbouncer-1.24.1/include/scram.h0000644000175000000000000000520014777762222013405 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * SCRAM support */ #include void free_scram_state(ScramState *scram_state); typedef enum PasswordType { PASSWORD_TYPE_PLAINTEXT = 0, PASSWORD_TYPE_MD5, PASSWORD_TYPE_SCRAM_SHA_256 } PasswordType; PasswordType get_password_type(const char *shadow_pass); /* * Functions for communicating as a client with the server */ char *build_client_first_message(ScramState *scram_state); char *build_client_final_message(ScramState *scram_state, const PgCredentials *credentials, const char *server_nonce, const char *salt, int saltlen, int iterations); bool read_server_first_message(PgSocket *server, char *input, char **server_nonce_p, char **salt_p, int *saltlen_p, int *iterations_p); bool read_server_final_message(PgSocket *server, char *input, char *ServerSignature); bool verify_server_signature(ScramState *scram_state, const PgCredentials *credentials, const char *ServerSignature); /* * Functions for communicating as a server to the client */ bool read_client_first_message(PgSocket *client, char *input, char *cbind_flag_p, char **client_first_message_bare_p, char **client_nonce_p); bool read_client_final_message(PgSocket *client, const uint8_t *raw_input, char *input, const char **client_final_nonce_p, char **proof_p); char *build_server_first_message(ScramState *scram_state, const char *username, const char *stored_secret); char *build_server_final_message(ScramState *scram_state); bool verify_final_nonce(const ScramState *scram_state, const char *client_final_nonce); bool verify_client_proof(ScramState *state, const char *ClientProof); bool scram_verify_plain_password(PgSocket *client, const char *username, const char *password, const char *secret); pgbouncer-1.24.1/include/loader.h0000644000175000000000000000235514777762222013556 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* connstring parsing */ bool parse_database(void *base, const char *name, const char *connstr) _MUSTCHECK; bool parse_peer(void *base, const char *name, const char *connstr) _MUSTCHECK; bool parse_user(void *base, const char *name, const char *params) _MUSTCHECK; /* user file parsing */ bool load_auth_file(const char *fn) /* _MUSTCHECK */; bool loader_users_check(void) /* _MUSTCHECK */; pgbouncer-1.24.1/include/stats.h0000644000175000000000000000233514777762222013444 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void stats_setup(void); bool admin_database_stats(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; bool admin_database_stats_totals(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; bool admin_database_stats_averages(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; bool show_stat_totals(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; pgbouncer-1.24.1/include/admin.h0000644000175000000000000000266314777762222013402 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool admin_handle_client(PgSocket *client, PktHdr *pkt) _MUSTCHECK; bool admin_pre_login(PgSocket *client, const char *username) _MUSTCHECK; bool admin_post_login(PgSocket *client) _MUSTCHECK; void admin_setup(void); bool admin_error(PgSocket *console, const char *fmt, ...) _PRINTF(2, 3) /* _MUSTCHECK */; void admin_pause_done(void); void admin_wait_close_done(void); bool admin_flush(PgSocket *admin, PktBuf *buf, const char *desc) /* _MUSTCHECK */; bool admin_ready(PgSocket *admin, const char *desc) _MUSTCHECK; void admin_handle_cancel(PgSocket *client); void admin_cleanup(void); pgbouncer-1.24.1/pyproject.toml0000644000175000000000000000231014777762222013417 00000000000000[tool.pytest.ini_options] addopts = [ "--import-mode=prepend", "--showlocals", "--tb=short", ] markers = [ "md5: uses MD5, will fail in FIPS mode", ] timeout = 30 # The asyncio_mode setting doesn't work on outdated versions of pytest-asyncio. # For python 3.6 no version is available that supports the asyncio_mode # setting, and for some of the older OSes Python 3.6 is the newest python # available. So @pytest.mark.asyncio is used everywhere instead. Once none of # our supported OSes require usage we can drop those marks and rely on the # asyncio_mode setting instead. # On these outdated versions having this setting in the config will throw a # warning, but we still add it anyway. The reason is that by adding it async # fixtures work on all versions, and are pretty much impossible to make work # otherwise without lots of version checks. So having a warning on outdated # versions is a small price to pay to not have to worry about that. asyncio_mode = 'auto' # Make test discovery quicker norecursedirs = [ '*.egg', '.*', '__pycache__', 'venv', 'src', 'lib', 'uthash', ] [tool.isort] profile = 'black' skip = 'lib,uthash' [tool.black] extend-exclude = 'lib|uthash' pgbouncer-1.24.1/lib/0000755000000000000000000000000014777762567011334 500000000000000pgbouncer-1.24.1/lib/COPYRIGHT0000644000175000000000000000151214777762223012550 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ pgbouncer-1.24.1/lib/m4/0000755000000000000000000000000014777762567011654 500000000000000pgbouncer-1.24.1/lib/m4/usual.m40000644000175000000000000003113214777762223013171 00000000000000dnl Those depend on correct order: dnl AC_USUAL_INIT dnl AC_USUAL_PROGRAM_CHECK dnl AC_USUAL_HEADER_CHECK dnl AC_USUAL_TYPE_CHECK dnl AC_USUAL_FUNCTION_CHECK dnl Order does not matter: dnl AC_USUAL_CASSERT dnl AC_USUAL_WERROR dnl AC_USUAL_DEBUG dnl Optional features: dnl AC_USUAL_UREGEX dnl AC_USUAL_GETADDRINFO_A dnl AC_USUAL_TLS dnl Catching missing pkg-config m4_pattern_forbid([^PKG_])dnl dnl dnl AC_USUAL_INIT: dnl - Sets PORTNAME=win32/unix dnl - If building from separate dir, writes top-level Makefile (antimake) dnl dnl Also defines port-specific flags: dnl _GNU_SOURCE, _WIN32_WINNT, WIN32_LEAN_AND_MEAN dnl AC_DEFUN([AC_USUAL_INIT], [ # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi AC_CANONICAL_HOST AC_MSG_CHECKING([target host type]) xhost="$host_alias" if test "x$xhost" = "x"; then xhost=`uname -s` fi case "$xhost" in *cygwin* | *mingw* | *pw32* | *MINGW*) LIBS="$LIBS -lws2_32" PORTNAME=win32;; *) PORTNAME=unix ;; esac AC_SUBST(PORTNAME) AC_MSG_RESULT([$PORTNAME]) dnl Set the flags before any feature tests. if test "$PORTNAME" = "win32"; then AC_DEFINE([WIN32_LEAN_AND_MEAN], [1], [Define to request cleaner win32 headers.]) AC_DEFINE([WINVER], [0x0600], [Define to max win32 API version (0x0600=Vista).]) fi AC_DEFINE([_GNU_SOURCE], [1], [Define to get some GNU functions in headers.]) dnl Package-specific data AC_SUBST([pkgdatadir], ['${datarootdir}'/${PACKAGE_TARNAME}]) dnl pkgconfig files AC_SUBST([pkgconfigdir], ['${libdir}/pkgconfig']) ]) dnl Old name for initial checks AC_DEFUN([AC_USUAL_PORT_CHECK], [AC_USUAL_INIT]) dnl dnl AC_USUAL_PROGRAM_CHECK: Simple C environment: CC, CPP, INSTALL dnl AC_DEFUN([AC_USUAL_PROGRAM_CHECK], [ AC_PROG_CC_STDC AC_PROG_CPP dnl Check if compiler supports __func__ AC_CACHE_CHECK([whether compiler supports __func__], pgac_cv_funcname_func, [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[printf("%s\n", __func__);]])], [pgac_cv_funcname_func=yes], [pgac_cv_funcname_func=no])]) if test x"$pgac_cv_funcname_func" = xyes ; then AC_DEFINE(HAVE_FUNCNAME__FUNC, 1, [Define to 1 if your compiler understands __func__.]) fi dnl Check if linker supports -Wl,--as-needed if test "$GCC" = "yes"; then old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--as-needed" AC_MSG_CHECKING([whether linker supports --as-needed]) AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) LDFLAGS="$old_LDFLAGS"]) fi dnl Check if compiler supports gcc-style dependencies AC_MSG_CHECKING([whether compiler supports dependency generation]) old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -MD -MP -MT conftest.o -MF conftest.o.d" AC_COMPILE_IFELSE([AC_LANG_SOURCE([void foo(void){}])], [HAVE_CC_DEPFLAG=yes], [HAVE_CC_DEPFLAG=no]) rm -f conftest.d CFLAGS="$old_CFLAGS" AC_MSG_RESULT([$HAVE_CC_DEPFLAG]) AC_SUBST(HAVE_CC_DEPFLAG) dnl Pick good warning flags for gcc WFLAGS="" if test x"$GCC" = xyes; then AC_MSG_CHECKING([for working warning switches]) good_CFLAGS="$CFLAGS" flags="-Wall -Wextra" # turn off noise from Wextra flags="$flags -Wno-unused-parameter -Wno-missing-field-initializers" # Wextra does not turn those on? flags="$flags -Wmissing-prototypes -Wpointer-arith -Wendif-labels" flags="$flags -Wdeclaration-after-statement -Wold-style-definition" flags="$flags -Wstrict-prototypes -Wundef -Wformat=2" flags="$flags -Wuninitialized -Wmissing-format-attribute" for f in $flags; do CFLAGS="$good_CFLAGS $WFLAGS $f" AC_COMPILE_IFELSE([AC_LANG_SOURCE([void foo(void){}])], [WFLAGS="$WFLAGS $f"]) done # avoid -Wextra if missing-field.initializers does not work echo "$WFLAGS" | grep missing-field-initializers > /dev/null \ || WFLAGS=`echo "$WFLAGS"|sed 's/ -Wextra//'` CFLAGS="$good_CFLAGS" AC_MSG_RESULT([done]) fi AC_SUBST(WFLAGS) AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_EGREP AC_PROG_AWK dnl AC_PROG_MKDIR_P and AC_PROG_SED are from newer autotools m4_ifdef([AC_PROG_MKDIR_P], [ AC_PROG_MKDIR_P ], [ MKDIR_P="mkdir -p" AC_SUBST(MKDIR_P) ]) m4_ifdef([AC_PROG_SED], [ AC_PROG_SED ], [ SED="sed" AC_SUBST(SED) ]) dnl Convert relative path to absolute path. case "$ac_install_sh" in ./*) ac_install_sh="`pwd`/${ac_install_sh}" ;; ../*) ac_install_sh="`pwd`/${ac_install_sh}" ;; esac case "$INSTALL" in ./*) INSTALL="`pwd`/${INSTALL}" ;; ../*) INSTALL="`pwd`/${INSTALL}" ;; esac case "$MKDIR_P" in ./*) MKDIR_P="`pwd`/${MKDIR_P}" ;; ../*) MKDIR_P="`pwd`/${MKDIR_P}" ;; esac AC_CHECK_TOOL([STRIP], [strip]) AC_CHECK_TOOL([RANLIB], [ranlib], [true]) AC_CHECK_TOOL([AR], [ar]) ARFLAGS=rcu AC_SUBST(ARFLAGS) ]) dnl dnl AC_USUAL_TYPE_CHECK: Basic types for C dnl AC_DEFUN([AC_USUAL_TYPE_CHECK], [ AC_C_INLINE AC_C_RESTRICT AC_C_BIGENDIAN AC_SYS_LARGEFILE AC_TYPE_PID_T AC_TYPE_UID_T AC_TYPE_SIZE_T ]) dnl dnl AC_USUAL_HEADER_CHECK: Basic headers dnl AC_DEFUN([AC_USUAL_HEADER_CHECK], [ AC_CHECK_HEADERS([inttypes.h stdbool.h unistd.h sys/time.h]) AC_CHECK_HEADERS([sys/socket.h poll.h sys/un.h]) AC_CHECK_HEADERS([arpa/inet.h netinet/in.h netinet/tcp.h]) AC_CHECK_HEADERS([sys/param.h sys/uio.h pwd.h grp.h]) AC_CHECK_HEADERS([sys/wait.h sys/mman.h syslog.h netdb.h dlfcn.h]) AC_CHECK_HEADERS([err.h pthread.h endian.h sys/endian.h byteswap.h]) AC_CHECK_HEADERS([malloc.h regex.h getopt.h fnmatch.h]) AC_CHECK_HEADERS([langinfo.h xlocale.h linux/random.h]) dnl ucred.h may have prereqs AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif ]) ]) dnl dnl AC_USUAL_FUNCTION_CHECK: Basic functions dnl AC_DEFUN([AC_USUAL_FUNCTION_CHECK], [ ### Functions provided if missing dnl AC_CHECK_FUNCS(basename dirname) # unstable, provide always AC_CHECK_FUNCS(strlcpy strlcat strnlen strsep getpeereid sigaction sigqueue) AC_CHECK_FUNCS(memmem memrchr mempcpy) AC_CHECK_FUNCS(inet_ntop inet_pton poll getline regcomp) AC_CHECK_FUNCS(err errx warn warnx getprogname setprogname) AC_CHECK_FUNCS(posix_memalign memalign valloc explicit_bzero memset_s reallocarray) AC_CHECK_FUNCS(getopt getopt_long getopt_long_only) AC_CHECK_FUNCS(fls flsl flsll ffs ffsl ffsll) AC_CHECK_FUNCS(fnmatch mbsnrtowcs nl_langinfo strtod_l strtonum) AC_CHECK_FUNCS(asprintf vasprintf timegm) ### Functions provided only on win32 AC_CHECK_FUNCS(localtime_r gettimeofday recvmsg sendmsg usleep getrusage) ### Functions used by libusual itself AC_CHECK_FUNCS(syslog mmap getpeerucred arc4random_buf getentropy getrandom) ### win32: link with ws2_32 AC_SEARCH_LIBS(WSAGetLastError, ws2_32) AC_FUNC_STRERROR_R ### AC_MSG_CHECKING([for integer enc/dec functions]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_ENDIAN_H #include #endif char p[[]] = "01234567"; int main(void) { be16enc(p, 0); be32enc(p, 1); be64enc(p, 2); le16enc(p, 2); le32enc(p, 3); le64enc(p, 4); return (int)(be16dec(p) + be32dec(p) + be64dec(p)) + (int)(le16dec(p) + le32dec(p) + le64dec(p)); } ])], [ AC_MSG_RESULT([found]) AC_DEFINE([HAVE_ENCDEC_FUNCS], [1], [Define if *enc & *dec functions are available]) ], [AC_MSG_RESULT([not found])]) ]) dnl dnl AC_USUAL_CASSERT: --enable-cassert switch to set macro CASSERT dnl AC_DEFUN([AC_USUAL_CASSERT], [ AC_ARG_ENABLE(cassert, AS_HELP_STRING([--enable-cassert],[turn on assert checking in code])) AC_MSG_CHECKING([whether to enable asserts]) if test "$enable_cassert" = "yes"; then AC_DEFINE(CASSERT, 1, [Define to enable assert checking]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ]) dnl dnl AC_USUAL_WERROR: --enable-werror switch to turn warnings into errors dnl AC_DEFUN([AC_USUAL_WERROR], [ AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror],[add -Werror to CFLAGS])) AC_MSG_CHECKING([whether to fail on warnings]) if test "$enable_werror" = "yes"; then CFLAGS="$CFLAGS -Werror" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ]) dnl dnl AC_USUAL_DEBUG: --disable-debug switch to strip binary dnl AC_DEFUN([AC_USUAL_DEBUG], [ AC_ARG_ENABLE(debug, AS_HELP_STRING([--disable-debug],[strip binary]), [], [enable_debug=yes]) AC_MSG_CHECKING([whether to build debug binary]) if test "$enable_debug" = "yes"; then LDFLAGS="-g $LDFLAGS" BININSTALL="$INSTALL" AC_MSG_RESULT([yes]) else BININSTALL="$INSTALL -s" AC_MSG_RESULT([no]) fi AC_SUBST(enable_debug) ]) dnl dnl AC_USUAL_UREGEX: --with-uregex dnl dnl Allow override of system regex dnl AC_DEFUN([AC_USUAL_UREGEX], [ AC_MSG_CHECKING([whether to force internal regex]) uregex=no AC_ARG_WITH(uregex, AS_HELP_STRING([--with-uregex],[force use of internal regex]), [ if test "$withval" = "yes"; then uregex=yes fi ], []) if test "$uregex" = "yes"; then AC_MSG_RESULT([yes]) AC_DEFINE(USE_INTERNAL_REGEX, 1, [Define to force use of uRegex.]) else AC_MSG_RESULT([no]) fi ]) dnl AC_USUAL_UREGEX dnl dnl AC_USUAL_GETADDRINFO_A - getaddrinfo_a() is required dnl AC_DEFUN([AC_USUAL_GETADDRINFO_A], [ AC_SEARCH_LIBS(getaddrinfo_a, anl) AC_CACHE_CHECK([whether to use native getaddinfo_a], ac_cv_usual_glibc_gaia, [AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #ifdef HAVE_NETDB_H #include #endif ]], [[ #if __GLIBC_PREREQ(2,9) getaddrinfo_a(0,NULL,0,NULL); #else none or broken #endif ]])], [ac_cv_usual_glibc_gaia=yes], [ac_cv_usual_glibc_gaia=no])]) if test x"$ac_cv_usual_glibc_gaia" = xyes ; then AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define to 1 if you have the getaddrinfo_a() function.]) else AX_PTHREAD(, [AC_MSG_RESULT([Threads not available and fallback getaddrinfo_a() non-functional.])]) CC="$PTHREAD_CC" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" fi ]) dnl dnl AC_USUAL_TLS: --with-openssl [ / --with-gnutls ? ] dnl dnl AC_USUAL_TLS - prefer-yes: dnl default - search for libssl, error if not found dnl --with-openssl - search for libssl, error if not found dnl --with-openssl=pfx - search for libssl, error if not found, use pfx dnl --without-openssl - no tls dnl AC_DEFUN([AC_USUAL_TLS],[ dnl values: no, libssl, auto tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="" AC_MSG_CHECKING([for OpenSSL]) AC_ARG_WITH(openssl, [AS_HELP_STRING([--without-openssl], [do not build with OpenSSL support]) AS_HELP_STRING([--with-openssl@<:@=PREFIX@:>@], [specify where OpenSSL is installed])], [ if test "$withval" = "no"; then tls_support=no elif test "$withval" = "yes"; then tls_support=libssl TLS_LIBS="-lssl -lcrypto" else tls_support=libssl TLS_CPPFLAGS="-I$withval/include" TLS_LDFLAGS="-L$withval/lib" TLS_LIBS="-lssl -lcrypto" fi ], [ tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="-lssl -lcrypto" ]) dnl check if libssl works if test "$tls_support" = "auto" -o "$tls_support" = "libssl"; then AC_DEFINE(USUAL_LIBSSL_FOR_TLS, 1, [Use libssl for TLS.]) AC_DEFINE(OPENSSL_API_COMPAT, [0x00908000L], [Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.]) tmp_LIBS="$LIBS" tmp_LDFLAGS="$LDFLAGS" tmp_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$TLS_CPPFLAGS $CPPFLAGS" LDFLAGS="$TLS_LDFLAGS $LDFLAGS" LIBS="$TLS_LIBS $LIBS" AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[SSL_CTX *ctx = SSL_CTX_new(SSLv23_method());]])], [ tls_support=yes; AC_MSG_RESULT([found])], [ AC_MSG_ERROR([not found]) ]) dnl check LibreSSL-only APIs AC_CHECK_FUNCS(SSL_CTX_use_certificate_chain_mem SSL_CTX_load_verify_mem asn1_time_parse) CPPFLAGS="$tmp_CPPFLAGS" LDFLAGS="$tmp_LDFLAGS" LIBS="$tmp_LIBS" dnl Pick default root CA file cafile=auto AC_MSG_CHECKING([for root CA certs]) AC_ARG_WITH(root-ca-file, AS_HELP_STRING([--with-root-ca-file=FILE], [specify where the root CA certificates are]), [ if test "$withval" = "no"; then : elif test "$withval" = "yes"; then : else cafile="$withval" fi ]) if test "$cafile" = "auto"; then for cafile in \ /etc/ssl/certs/ca-certificates.crt \ /etc/pki/tls/certs/ca-bundle.crt \ /etc/ssl/ca-bundle.pem \ /etc/pki/tls/cacert.pem \ /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \ /etc/ssl/cert.pem do if test -f "$cafile"; then break fi done fi AC_DEFINE_UNQUOTED(USUAL_TLS_CA_FILE, ["$cafile"], [Path to root CA certs.]) AC_MSG_RESULT([$cafile]) else AC_MSG_RESULT([no]) fi AC_SUBST(tls_support) AC_SUBST(TLS_CPPFLAGS) AC_SUBST(TLS_LDFLAGS) AC_SUBST(TLS_LIBS) ]) dnl AC_USUAL_TLS pgbouncer-1.24.1/lib/m4/antimake.m40000644000175000000000000000033414777762223013631 00000000000000dnl dnl AMK_INIT: Generate initial makefile dnl AC_DEFUN([AMK_INIT], [ # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi ]) pgbouncer-1.24.1/lib/m4/ax_pthread.m40000644000175000000000000005272414777762223014171 00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # Copyright (c) 2019 Marc Stevens # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 27 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items with a "," contain both # C compiler flags (before ",") and linker flags (after ","). Other items # starting with a "-" are C compiler flags, and remaining items are # library names, except for "none" which indicates that we try without # any flags at all, and "pthread-config" which is a program returning # the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" ;; esac # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) # Note that for GCC and Clang -pthread generally implies -lpthread, # except when -nostdlib is passed. # This is problematic using libtool to build C++ shared libraries with pthread: # [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 # [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 # [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 # To solve this, first try -pthread together with -lpthread for GCC AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) # Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first AS_IF([test "x$ax_pthread_clang" = "xyes"], [ax_pthread_flags="-pthread,-lpthread -pthread"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; *,*) PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void *some_global = NULL; static void routine(void *a) { /* To avoid any unused-parameter or unused-but-set-parameter warning. */ some_global = a; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT; return i;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD pgbouncer-1.24.1/lib/find_modules.sh0000755000175000000000000000167014777762223014271 00000000000000#! /bin/sh set -e top="$1" # sanity check test -n "$top" || { echo "usage: $0 USUAL_DIR SRC ..." >&2 exit 1 } test -f "$top/usual/base.h" || { echo "usage: $0 USUAL_DIR SRC ..." >&2 exit 1 } shift test -n "$1" || exit 0 test -n "$AWK" || AWK=awk # return uniq module names, exclude already found ones grep_usual() { excl='excl["config"]=1' for m in $m_done; do excl="$excl;excl[\"$m\"]=1" done prog=' BEGIN { '"$excl"' } /^#include[ \t]*[<"]usual\// { p1 = index($0, "/"); p2 = index($0, "."); m = substr($0, p1+1, p2-p1-1); if (!excl[m]) print m; }' $AWK "$prog" "$@" | sort -u } # return module filename globs make_pats() { for m in "$@"; do echo "$top/usual/$m*.[ch]" done } # loop over grep until all mods are found m_done="" m_tocheck=`grep_usual "$@"` while test -n "$m_tocheck"; do m_done="$m_done $m_tocheck" pats=`make_pats $m_tocheck` m_tocheck=`grep_usual $pats` done # done echo $m_done pgbouncer-1.24.1/lib/usual/0000755000000000000000000000000014777762567012465 500000000000000pgbouncer-1.24.1/lib/usual/getopt.h0000644000175000000000000000670014777762223014065 00000000000000/* * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * getopt compat. * * This module provides getopt() and getopt_long(). */ #ifndef _USUAL_GETOPT_H_ #define _USUAL_GETOPT_H_ #include #ifndef NEED_USUAL_GETOPT #if !defined(HAVE_GETOPT_H) || !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_LONG) #define NEED_USUAL_GETOPT #endif #endif #ifndef NEED_USUAL_GETOPT /* Use system getopt */ #include #else /* NEED_USUAL_GETOPT */ /* avoid name collision */ #define optarg usual_optarg #define opterr usual_opterr #define optind usual_optind #define optopt usual_optopt #define getopt(a,b,c) usual_getopt(a,b,c) #define getopt_long(a,b,c,d,e) usual_getopt_long(a,b,c,d,e) /** argument to current option, or NULL if it has none */ extern char *optarg; /** Current position in arg string. Starts from 1. Setting to 0 resets state. */ extern int optind; /** whether getopt() should print error messages on problems. Default: 1. */ extern int opterr; /** Option char which caused error */ extern int optopt; /** long option takes no argument */ #define no_argument 0 /** long option requires argument */ #define required_argument 1 /** long option has optional argument */ #define optional_argument 2 /** Long option description */ struct option { /** name of long option */ const char *name; /** * whether option takes an argument. * One of no_argument, required_argument, and optional_argument. */ int has_arg; /** if not NULL, set *flag to val when option found */ int *flag; /** if flag not NULL, value to set *flag to; else return value */ int val; }; /** Compat: getopt */ int getopt(int argc, char *argv[], const char *options); /** Compat: getopt_long */ int getopt_long(int argc, char *argv[], const char *options, const struct option *longopts, int *longindex); /** Compat: getopt_long_only */ int getopt_long_only(int nargc, char *argv[], const char *options, const struct option *long_options, int *idx); #endif /* NEED_USUAL_GETOPT */ #endif /* !_USUAL_GETOPT_H_ */ pgbouncer-1.24.1/lib/usual/fileutil.h0000644000175000000000000000331714777762223014401 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * File access utils. */ #ifndef _USUAL_FILEUTIL_H_ #define _USUAL_FILEUTIL_H_ #include #include /** Info about mapped file */ struct MappedFile { int fd; unsigned len; void *ptr; }; /** Signature for per-line callback */ typedef bool (*procline_cb)(void *arg, const char *line, ssize_t len); /** Read file into memory */ void *load_file(const char *fn, size_t *len_p); /** Loop over lines in file */ bool foreach_line(const char *fn, procline_cb proc_line, void *arg); /** Get file size */ ssize_t file_size(const char *fn); /** Map file into memory */ int map_file(struct MappedFile *m, const char *fname, int rw) _MUSTCHECK; /** Unmap previously mapped file */ void unmap_file(struct MappedFile *m); #if !defined(HAVE_GETLINE) #define getline(a,b,c) compat_getline(a,b,c) /** * Compat: Read line from file */ int getline(char **line_p, size_t *size_p, void *f); #endif #endif pgbouncer-1.24.1/lib/usual/endian.h0000644000175000000000000002133214777762223014017 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Endianess conversion, convert integers to bytes. */ #ifndef _USUAL_ENDIAN_H_ #define _USUAL_ENDIAN_H_ #include #include /* * Need to include OS headers even if unused, so our * definitions stay in use. */ #ifdef HAVE_ENDIAN_H #include #endif #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_BYTESWAP_H #include #endif /* * Is unaligned access to integers OK? Does not apply to floats. * * OK: x86, amd64, arm >= v6, ppc */ #if defined(__amd64__) || defined(__i386__) || defined(__ppc__) || defined(__ppc64__) \ || defined(__ARM_FEATURE_UNALIGNED) \ || defined(_M_IX86) || defined(_M_X64) || defined(_M_PPC) \ || (defined(_M_ARM) && _M_ARM >= 6) #define WORDS_UNALIGNED_ACCESS_OK #endif /* * Ignore OS defines, as they may define only some subset of functions. * * Instead try to use compiler builtins. */ #undef bswap16 #undef bswap32 #undef bswap64 #undef htobe16 #undef htobe32 #undef htobe64 #undef htole16 #undef htole32 #undef htole64 #undef be16toh #undef be32toh #undef be64toh #undef le16toh #undef le32toh #undef le64toh #undef be16dec #undef be32dec #undef be64dec #undef le16dec #undef le32dec #undef le64dec #undef h16dec #undef h32dec #undef h64dec #undef be16enc #undef be32enc #undef be64enc #undef le16enc #undef le32enc #undef le64enc #undef h16enc #undef h32enc #undef h64enc /* * Redefine to avoid conflicts. */ #define bswap16(x) usual_bswap16(x) #define bswap32(x) usual_bswap32(x) #define bswap64(x) usual_bswap64(x) #define be16dec(p) usual_be16dec(p) #define be32dec(p) usual_be32dec(p) #define be64dec(p) usual_be64dec(p) #define le16dec(p) usual_le16dec(p) #define le32dec(p) usual_le32dec(p) #define le64dec(p) usual_le64dec(p) #define h16dec(p) usual_h16dec(p) #define h32dec(p) usual_h32dec(p) #define h64dec(p) usual_h64dec(p) #define be16enc(p, x) usual_be16enc(p, x) #define be32enc(p, x) usual_be32enc(p, x) #define be64enc(p, x) usual_be64enc(p, x) #define le16enc(p, x) usual_le16enc(p, x) #define le32enc(p, x) usual_le32enc(p, x) #define le64enc(p, x) usual_le64enc(p, x) #define h16enc(p, x) usual_h16enc(p, x) #define h32enc(p, x) usual_h32enc(p, x) #define h64enc(p, x) usual_h64enc(p, x) /** * @name Always swap. * * @{ */ /** Swap 16-bit int */ static inline uint16_t bswap16(uint16_t x) { #if _COMPILER_GNUC(4, 8) || __has_builtin(__builtin_bswap16) return __builtin_bswap16(x); #else return (x << 8) | (x >> 8); #endif } /** Swap 32-bit int */ static inline uint32_t bswap32(uint32_t x) { #if _COMPILER_GNUC(4, 3) || __has_builtin(__builtin_bswap32) return __builtin_bswap32(x); #else x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF); return (x << 16) | (x >> 16); #endif } /** Swap 64-bit int */ static inline uint64_t bswap64(uint64_t x) { #if _COMPILER_GNUC(4, 3) || __has_builtin(__builtin_bswap64) return __builtin_bswap64(x); #else return ((uint64_t)bswap32(x) << 32) | bswap32(x >> 32); #endif } /** * @} * * @name Convert host-endian int to BE/LE. * * @{ */ #ifdef WORDS_BIGENDIAN /** Convert native 16-bit int to big-endian */ #define htobe16(x) ((uint16_t)(x)) /** Convert native 32-bit int to big-endian */ #define htobe32(x) ((uint32_t)(x)) /** Convert native 64-bit int to big-endian */ #define htobe64(x) ((uint64_t)(x)) /** Convert native 16-bit int to little-endian */ #define htole16(x) bswap16(x) /** Convert native 32-bit int to little-endian */ #define htole32(x) bswap32(x) /** Convert native 64-bit int to little-endian */ #define htole64(x) bswap64(x) /** Convert big-endian 16-bit int to host-endian */ #define be16toh(x) ((uint16_t)(x)) /** Convert big-endian 32-bit int to host-endian */ #define be32toh(x) ((uint32_t)(x)) /** Convert big-endian 64-bit int to host-endian */ #define be64toh(x) ((uint64_t)(x)) /** Convert little-endian 16-bit int to host-endian */ #define le16toh(x) bswap16(x) /** Convert little-endian 32-bit int to host-endian */ #define le32toh(x) bswap32(x) /** Convert little-endian 64-bit int to host-endian */ #define le64toh(x) bswap64(x) #else /* !WORDS_BIGENDIAN */ /** Convert native 16-bit int to big-endian */ #define htobe16(x) bswap16(x) /** Convert native 32-bit int to big-endian */ #define htobe32(x) bswap32(x) /** Convert native 64-bit int to big-endian */ #define htobe64(x) bswap64(x) /** Convert native 16-bit int to little-endian */ #define htole16(x) ((uint16_t)(x)) /** Convert native 32-bit int to little-endian */ #define htole32(x) ((uint32_t)(x)) /** Convert native 64-bit int to little-endian */ #define htole64(x) ((uint64_t)(x)) /** Convert big-endian 16-bit int to host-endian */ #define be16toh(x) bswap16(x) /** Convert big-endian 32-bit int to host-endian */ #define be32toh(x) bswap32(x) /** Convert big-endian 64-bit int to host-endian */ #define be64toh(x) bswap64(x) /** Convert little-endian 64-bit int to host-endian */ #define le16toh(x) ((uint16_t)(x)) /** Convert little-endian 64-bit int to host-endian */ #define le32toh(x) ((uint32_t)(x)) /** Convert little-endian 64-bit int to host-endian */ #define le64toh(x) ((uint64_t)(x)) #endif /** * @} * * @name Read integer values from memory and convert to host format. * * @{ */ /** Read big-endian 16-bit int from memory */ static inline uint16_t be16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe16(tmp); } /** Read big-endian 32-bit int from memory */ static inline uint32_t be32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe32(tmp); } /** Read big-endian 64-bit int from memory */ static inline uint64_t be64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe64(tmp); } /** Read little-endian 16-bit int from memory */ static inline uint16_t le16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole16(tmp); } /** Read little-endian 32-bit int from memory */ static inline uint32_t le32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole32(tmp); } /** Read little-endian 64-bit int from memory */ static inline uint64_t le64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole64(tmp); } /** Read host-endian 16-bit int from memory */ static inline uint16_t h16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** Read host-endian 32-bit int from memory */ static inline uint32_t h32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** Read host-endian 64-bit int from memory */ static inline uint64_t h64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** * @} * * @name Convert host value to LE/BE and write to memory * * @{ */ /** Write big-endian 16-bit int to memory */ static inline void be16enc(void *p, uint16_t x) { uint16_t tmp = htobe16(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write big-endian 32-bit int to memory */ static inline void be32enc(void *p, uint32_t x) { uint32_t tmp = htobe32(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write big-endian 64-bit int to memory */ static inline void be64enc(void *p, uint64_t x) { uint64_t tmp = htobe64(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 16-bit int to memory */ static inline void le16enc(void *p, uint16_t x) { uint16_t tmp = htole16(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 32-bit int to memory */ static inline void le32enc(void *p, uint32_t x) { uint32_t tmp = htole32(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 64-bit int to memory */ static inline void le64enc(void *p, uint64_t x) { uint64_t tmp = htole64(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write host-endian 16-bit int to memory */ static inline void h16enc(void *p, uint16_t x) { memcpy(p, &x, sizeof(x)); } /** Write host-endian 32-bit int to memory */ static inline void h32enc(void *p, uint32_t x) { memcpy(p, &x, sizeof(x)); } /** Write host-endian 64-bit int to memory */ static inline void h64enc(void *p, uint64_t x) { memcpy(p, &x, sizeof(x)); } /** @} */ #endif /* _USUAL_ENDIAN_H_ */ pgbouncer-1.24.1/lib/usual/json.h0000644000175000000000000002324014777762223013532 00000000000000/* * Read and write JSON. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Read and write JSON. * * Features: * - Robust - does not crash or assert() on invalid data. * - Fast memory allocation - it uses single pooled area * for all objects thus alloc/free are fast. * - Strict UTF8 validation. * - Full int64_t and double passthrough, except NaN and +-Infinity. * - Proper number I/O even in weird locales. * * Optional features for JSON config files, off by default: * - Allow C comments. * - Allow extra comma in dict/list. */ #ifndef _USUAL_JSON_ #define _USUAL_JSON_ #include #include /** * JSON value types * * Returned by json_value_type(). */ enum JsonValueType { JSON_NULL = 1, /**< Null value */ JSON_BOOL, /**< Boolean value */ JSON_INT, /**< Integer value */ JSON_FLOAT, /**< Float value */ JSON_STRING, /**< String value */ JSON_LIST, /**< JSON list */ JSON_DICT, /**< JSON "object", which is key->value map */ }; /** Options for JSON parser. */ enum JsonParseOptions { /** Default - do strict parsing. No comments, no extra comma. */ JSON_STRICT = 0, /** Allow comments, allow extra comma. Useful for JSON in config files. */ JSON_PARSE_RELAXED = 1, /** Do not validate UTF-8. The default behavior is to validate UTF-8. */ JSON_PARSE_IGNORE_ENCODING = 2, }; /** * @struct JsonValue * * Json value */ struct JsonValue; /** * @struct JsonContext * * Allocation context. */ struct JsonContext; /** Callback for dict iterator */ typedef bool (*json_dict_iter_callback_f)(void *arg, struct JsonValue *key, struct JsonValue *val); /** Callback for list iterator */ typedef bool (*json_list_iter_callback_f)(void *arg, struct JsonValue *elem); /** * @name Allocation context. * * @{ */ /** Create allocation context */ struct JsonContext *json_new_context(const void *cx_mem, size_t initial_mem); /** Create allocation context */ void json_free_context(struct JsonContext *ctx); /** Create allocation context */ const char *json_strerror(struct JsonContext *ctx); /** * @} * * @name Parse JSON * * @{ */ /** Parse JSON string */ struct JsonValue *json_parse(struct JsonContext *ctx, const char *src, size_t length); /** Set parsing options */ void json_set_options(struct JsonContext *ctx, unsigned int options); /** * @} * * @name Examine single value * * @{ */ /** Return type for value */ enum JsonValueType json_value_type(struct JsonValue *jv); /** * Return element size. * * For JSON strings, it's bytes in string, for list and * dict it returns number of elements. */ size_t json_value_size(struct JsonValue *jv); /** Return true if value is null */ static inline bool json_value_is_null(struct JsonValue *jv) { return json_value_type(jv) == JSON_NULL; } /** Return true if value is boolean */ static inline bool json_value_is_bool(struct JsonValue *jv) { return json_value_type(jv) == JSON_BOOL; } /** Return true if value is int */ static inline bool json_value_is_int(struct JsonValue *jv) { return json_value_type(jv) == JSON_INT; } /** Return true if value is float */ static inline bool json_value_is_float(struct JsonValue *jv) { return json_value_type(jv) == JSON_FLOAT; } /** Return true if value is string */ static inline bool json_value_is_string(struct JsonValue *jv) { return json_value_type(jv) == JSON_STRING; } /** Return true if value is list */ static inline bool json_value_is_list(struct JsonValue *jv) { return json_value_type(jv) == JSON_LIST; } /** Return true if value is dict */ static inline bool json_value_is_dict(struct JsonValue *jv) { return json_value_type(jv) == JSON_DICT; } /** Get bool value */ bool json_value_as_bool(struct JsonValue *jv, bool *dst_p); /** Get int value */ bool json_value_as_int(struct JsonValue *jv, int64_t *dst_p); /** Get double value */ bool json_value_as_float(struct JsonValue *jv, double *dst_p); /** Get string value */ bool json_value_as_string(struct JsonValue *jv, const char **dst_p, size_t *size_p); /** * @} * * @name Get values from dict * * @{ */ /** Get key value from dict */ bool json_dict_get_value(struct JsonValue *dict, const char *key, struct JsonValue **val_p); /** Return true if value is null or missing */ bool json_dict_is_null(struct JsonValue *jv, const char *key); /** Get boolean value from dict */ bool json_dict_get_bool(struct JsonValue *jv, const char *key, bool *dst_p); /** Get int value from dict */ bool json_dict_get_int(struct JsonValue *jv, const char *key, int64_t *dst_p); /** Get float value from dict */ bool json_dict_get_float(struct JsonValue *jv, const char *key, double *dst_p); /** Get string value from dict */ bool json_dict_get_string(struct JsonValue *jv, const char *key, const char **dst_p, size_t *len_p); /** Get sub-dict from dict */ bool json_dict_get_dict(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** Get list from dict */ bool json_dict_get_list(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /* * Optional elements */ /** Get optional bool from dict */ bool json_dict_get_opt_bool(struct JsonValue *jv, const char *key, bool *dst_p); /** Get optional int from dict */ bool json_dict_get_opt_int(struct JsonValue *jv, const char *key, int64_t *dst_p); /** Get optional float from dict */ bool json_dict_get_opt_float(struct JsonValue *jv, const char *key, double *dst_p); /** Get optional string from dict */ bool json_dict_get_opt_string(struct JsonValue *jv, const char *key, const char **dst_p, size_t *len_p); /** Get optional list from dict */ bool json_dict_get_opt_list(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** Get optional dict from dict */ bool json_dict_get_opt_dict(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** * @} * * @name Get values from list. * * @{ */ /** Get value from list */ bool json_list_get_value(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** Return true if value is null or missing */ bool json_list_is_null(struct JsonValue *list, size_t index); /** Get bool from list */ bool json_list_get_bool(struct JsonValue *list, size_t index, bool *val_p); /** Get int from list */ bool json_list_get_int(struct JsonValue *list, size_t index, int64_t *val_p); /** Get float from list */ bool json_list_get_float(struct JsonValue *list, size_t index, double *val_p); /** Get string from list */ bool json_list_get_string(struct JsonValue *list, size_t index, const char **val_p, size_t *len_p); /** Get list value from list */ bool json_list_get_list(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** Get dict value from list */ bool json_list_get_dict(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** * @} * * @name Iterate over elements in list/dict. * * @{ */ /** Walk over dict elements */ bool json_dict_iter(struct JsonValue *dict, json_dict_iter_callback_f cb_func, void *cb_arg); /** Walk over list elements */ bool json_list_iter(struct JsonValue *list, json_list_iter_callback_f cb_func, void *cb_arg); /** * @} * * @name Output JSON. * * @{ */ /** Render JSON object as string */ bool json_render(struct MBuf *dst, struct JsonValue *jv); /** * @} * * @name Create new values. * * @{ */ /** Create NULL value */ struct JsonValue *json_new_null(struct JsonContext *ctx); /** Create bool value */ struct JsonValue *json_new_bool(struct JsonContext *ctx, bool val); /** Create int value */ struct JsonValue *json_new_int(struct JsonContext *ctx, int64_t val); /** Create float value */ struct JsonValue *json_new_float(struct JsonContext *ctx, double val); /** Create string value */ struct JsonValue *json_new_string(struct JsonContext *ctx, const char *val); /** Create dict value */ struct JsonValue *json_new_dict(struct JsonContext *ctx); /** Create list value */ struct JsonValue *json_new_list(struct JsonContext *ctx); /** * @} * * @name Add values to containers. * * @{ */ /** Add value to list */ bool json_list_append(struct JsonValue *list, struct JsonValue *elem); /** Add null to list */ bool json_list_append_null(struct JsonValue *list); /** Add bool to list */ bool json_list_append_bool(struct JsonValue *list, bool val); /** Add int to list */ bool json_list_append_int(struct JsonValue *list, int64_t val); /** Add float to list */ bool json_list_append_float(struct JsonValue *list, double val); /** Add string to list */ bool json_list_append_string(struct JsonValue *list, const char *val); /** Add element to dict */ bool json_dict_put(struct JsonValue *dict, const char *key, struct JsonValue *val); /** Add null to dict */ bool json_dict_put_null(struct JsonValue *dict, const char *key); /** Add bool to dict */ bool json_dict_put_bool(struct JsonValue *dict, const char *key, bool val); /** Add int to dict */ bool json_dict_put_int(struct JsonValue *dict, const char *key, int64_t val); /** Add float to dict */ bool json_dict_put_float(struct JsonValue *dict, const char *key, double val); /** Add string to dict */ bool json_dict_put_string(struct JsonValue *dict, const char *key, const char *str); /** * @} */ #endif pgbouncer-1.24.1/lib/usual/wchar.c0000644000175000000000000000652114777762223013663 00000000000000/* * wchar utility functions. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include wchar_t *mbstr_decode(const char *str, int str_len, int *wlen_p, wchar_t *wbuf, int wbuf_len, bool allow_invalid) { mbstate_t ps; int clen; wchar_t *dst, *w, *wend; const char *s; const char *str_end; int wmax; if (str_len < 0) str_len = strlen(str); str_end = str + str_len; /* max number of wchar_t that the output can take plus zero-terminator */ wmax = str_len + 1; if (wbuf != NULL && wmax < wbuf_len) { dst = wbuf; } else { dst = malloc(sizeof(wchar_t) * wmax); if (!dst) return NULL; } /* try full decode at once */ s = str; memset(&ps, 0, sizeof(ps)); clen = mbsnrtowcs(dst, &s, str_len, wmax, &ps); if (clen >= 0) { if (wlen_p) *wlen_p = clen; dst[clen] = 0; return dst; } if (!allow_invalid) goto fail; /* full decode failed, decode chars one-by-one */ s = str; w = dst; wend = dst + wmax - 1; memset(&ps, 0, sizeof(ps)); while (s < str_end && w < wend) { clen = mbrtowc(w, s, str_end - s, &ps); if (clen > 0) { /* single char */ w++; s += clen; } else if (clen == 0) { /* string end */ break; } else if (allow_invalid) { /* allow invalid encoding */ memset(&ps, 0, sizeof(ps)); *w++ = (unsigned char)*s++; } else { /* invalid encoding */ goto fail; } } /* make sure we got string end */ if (s < str_end && *s != '\0') goto fail; *w = 0; if (wlen_p) *wlen_p = w - dst; return dst; fail: if (dst != wbuf) free(dst); return NULL; } wctype_t wctype_wcsn(const wchar_t *name, unsigned int namelen) { char buf[10]; unsigned int i; if (namelen >= sizeof(buf)) return (wctype_t)0; for (i = 0; i < namelen; i++) { wchar_t c = name[i]; if (c < 0x20 || c > 127) return (wctype_t)0; buf[i] = c; } buf[i] = 0; return wctype(buf); } #ifndef HAVE_MBSNRTOWCS size_t mbsnrtowcs(wchar_t *dst, const char **src_p, size_t srclen, size_t dstlen, mbstate_t *ps) { int clen; const char *s, *s_end; wchar_t *w; mbstate_t pstmp; size_t count = 0; if (!ps) { memset(&pstmp, 0, sizeof(pstmp)); ps = &pstmp; } s = *src_p; s_end = s + srclen; w = dst; while (s < s_end) { if (w && count >= dstlen) { /* dst is full */ break; } clen = mbrtowc(w, s, s_end - s, ps); if (clen > 0) { /* proper character */ if (w) w++; count++; s += clen; } else if (clen < 0) { /* invalid encoding */ *src_p = s; return (size_t)(-1); } else { /* end of string */ if (w) *w = 0; *src_p = NULL; return count; } } /* end due to srclen */ *src_p = s; return count; } #endif pgbouncer-1.24.1/lib/usual/bits.h0000644000175000000000000001263514777762223013530 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Bit arithmetics. * * - is_power_of_2 * - ffs, ffsl, ffsll * - fls, flsl, flsll * - rol16, rol32, rol64 * - ror16, ror32, ror64 */ #ifndef _USUAL_BITS_H_ #define _USUAL_BITS_H_ #include #include #include /** Checks if integer has only one bit set */ static inline bool is_power_of_2(unsigned int n) { return (n > 0) && !(n & (n - 1)); } /* * Single-eval and type-safe rol/ror */ /** Rotate 16-bit int to left */ static inline uint16_t rol16(uint16_t v, int s) { return (v << s) | (v >> (16 - s)); } /** Rotate 32-bit int to left */ static inline uint32_t rol32(uint32_t v, int s) { return (v << s) | (v >> (32 - s)); } /** Rotate 64-bit int to left */ static inline uint64_t rol64(uint64_t v, int s) { return (v << s) | (v >> (64 - s)); } /** Rotate 16-bit int to right */ static inline uint16_t ror16(uint16_t v, int s) { return rol16(v, 16 - s); } /** Rotate 32-bit int to right */ static inline uint32_t ror32(uint32_t v, int s) { return rol32(v, 32 - s); } /** Rotate 64-bit int to right */ static inline uint64_t ror64(uint64_t v, int s) { return rol64(v, 64 - s); } /* * fls(int) * flsl(long) * flsll(long long) * * find MSB bit set, 1-based ofs, 0 if arg == 0 */ #undef fls #undef flsl #undef flsll #define fls(x) usual_fls(x) #define flsl(x) usual_flsl(x) #define flsll(x) usual_flsll(x) #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_clzll) #define _USUAL_FLS_(sfx, type) \ return (x == 0) ? 0 : ((8*sizeof(type)) - __builtin_clz ## sfx(x)) #else #define _USUAL_FLS_(sfx, type) \ unsigned type u = x; \ unsigned int bit; \ if (x == 0) return 0; \ /* count from smallest bit, assuming small values */ \ for (bit = 1; u > 1; bit++) u >>= 1; \ return bit #endif /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int fls(int x) { _USUAL_FLS_(, int); } /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int flsl(long x) { _USUAL_FLS_(l, long); } /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int flsll(long long x) { _USUAL_FLS_(ll, long long); } #undef _USUAL_FLS_ /* * ffs(int) * ffsl(long) * ffsll(long long) * * find LSB bit set, 1-based ofs, 0 if arg == 0 */ #undef ffs #undef ffsl #undef ffsll #define ffs(x) usual_ffs(x) #define ffsl(x) usual_ffsl(x) #define ffsll(x) usual_ffsll(x) #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_ffsll) #define _USUAL_FFS_(sfx, type) \ return __builtin_ffs ## sfx((unsigned type)(x)) #else #define _USUAL_FFS_(sfx, type) \ unsigned int bit; \ unsigned type u = x; \ if (!x) return 0; \ /* count from smallest bit, assuming small values */ \ for (bit = 1; !(u & 1); bit++) u >>= 1; \ return bit #endif /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffs(int x) { _USUAL_FFS_(, int); } /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffsl(long x) { _USUAL_FFS_(l, long); } /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffsll(long long x) { _USUAL_FFS_(ll, long long); } #undef _USUAL_FFS_ /* * Multiply and check overflow. */ #define _USUAL_MUL_SAFE_(type, max) \ type unsafe = (type)(1) << (sizeof(type) * 8/2); /* sqrt(max+1) */ \ if (a < unsafe && b < unsafe) \ goto safe; \ if (!a || !b) \ goto safe; \ if ((max / a) >= b) \ goto safe; \ return false; \ safe: \ *res_p = a * b; \ return true; /** Multiply with overflow check for 'unsigned int' */ static inline bool safe_mul_uint(unsigned int *res_p, unsigned int a, unsigned int b) { _USUAL_MUL_SAFE_(unsigned int, UINT_MAX); } /** Multiply with overflow check for 'unsigned long' */ static inline bool safe_mul_ulong(unsigned long *res_p, unsigned long a, unsigned long b) { _USUAL_MUL_SAFE_(unsigned long, ULONG_MAX); } /** Multiply with overflow check for 'uint8_t' */ static inline bool safe_mul_uint8(uint8_t *res_p, uint8_t a, uint8_t b) { _USUAL_MUL_SAFE_(uint8_t, UINT8_MAX); } /** Multiply with overflow check for 'uint16_t' */ static inline bool safe_mul_uint16(uint16_t *res_p, uint16_t a, uint16_t b) { _USUAL_MUL_SAFE_(uint16_t, UINT16_MAX); } /** Multiply with overflow check for 'uint32_t' */ static inline bool safe_mul_uint32(uint32_t *res_p, uint32_t a, uint32_t b) { _USUAL_MUL_SAFE_(uint32_t, UINT32_MAX); } /** Multiply with overflow check for 'uint64_t' */ static inline bool safe_mul_uint64(uint64_t *res_p, uint64_t a, uint64_t b) { _USUAL_MUL_SAFE_(uint64_t, UINT64_MAX); } /** Multiply with overflow check for 'size_t' */ static inline bool safe_mul_size(size_t *res_p, size_t a, size_t b) { _USUAL_MUL_SAFE_(size_t, SIZE_MAX); } #undef _USUAL_MUL_SAFE_ #endif pgbouncer-1.24.1/lib/usual/regex.h0000644000175000000000000001532714777762223013702 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * POSIX regular expession API, provided by either libc or internally. * * The internal regex engine is only activated if OS does not provide * @ref uregex_links "" (eg. Windows) or if * --with-internal-regex is used when configuring @ref libusual. * * @section uregex Features of internal regex (uregex). * * Simple recursive matcher, only features are small size * and POSIX compatibility. Supports both Extended Regular Expressions (ERE) * and Basic Regular Expressions (BRE). * * @section uregex_syntax Supported syntax * @code * Both: . * ^ $ [] [[:cname:]] * ERE: () {} | + ? * BRE: \(\) \{\} \1-9 * @endcode * * With REG_RELAXED_SYNTAX, following common escapes will be available: * @code * Both: \b\B\d\D\s\S\w\W * BRE: \| * ERE: \1-9 * @endcode * * With REG_RELAXED_MATCHING it returns the first match found after applying * leftmost-longest to all elements. It skips the combinatorics to turn it * into guaranteed-longest match. * * @section uregex_skip Skipped POSIX features * - collation classes: [[. .]] * - equivalence classes: [[= =]] * - char ranges by locale order: [a-z] (byte order will be used) * - multi-byte chars: UTF-8 * * @section uregex_globaldefs Global defines * - USUAL_RELAXED_REGEX * - USE_INTERNAL_REGEX * * @section uregex_links Compatibility * * - * POSIX-2008 spec - by default uRegex run in mode where only * features specified by POSIX are available. * * - * AT\&T Research regex(3) regression tests - uRegex follows the interpretation * given there and fully passes the testsuite. */ #ifndef _USUAL_REGEX_H_ #define _USUAL_REGEX_H_ #include #if !defined(USE_INTERNAL_REGEX) && defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) #define USE_SYSTEM_REGEX #endif #ifdef USE_SYSTEM_REGEX #include #else /* * uRegex defines */ /** * @name Standard flags to regcomp() * @{ */ /** Use POSIX Extended Regex Syntax instead of Basic Syntax */ #define REG_EXTENDED (1 << 0) /** Do case-insensitive matching */ #define REG_ICASE (1 << 1) /** Do case-insensitive matching */ #define REG_NOSUB (1 << 2) /** Do case-insensitive matching */ #define REG_NEWLINE (1 << 3) /* @} */ /** * @name Standard flags to regexec() * @{ */ /** The start of string is not beginning of line, so ^ should not match */ #define REG_NOTBOL (1 << 4) /** The end of string is not end of line, so $ should not match */ #define REG_NOTEOL (1 << 5) /* @} */ /** * @name Standard error codes * @{ */ /** Match not found */ #define REG_NOMATCH 1 /** Bad {} repeat specification */ #define REG_BADBR 2 /** General problem with regular expression */ #define REG_BADPAT 3 /** Repeat used without preceding non-repeat element */ #define REG_BADRPT 4 /** Syntax error with {} */ #define REG_EBRACE 5 /** Syntax error with [] */ #define REG_EBRACK 6 /** Bad collation reference */ #define REG_ECOLLATE 7 /** Bad character class reference */ #define REG_ECTYPE 8 /** Trailing backslack */ #define REG_EESCAPE 9 /** Syntax error with () */ #define REG_EPAREN 10 /** Bad endpoint in range */ #define REG_ERANGE 11 /** No memory */ #define REG_ESPACE 12 /** Bad subgroup reference */ #define REG_ESUBREG 13 /* @} */ /** * @name Other defines * @{ */ #undef RE_DUP_MAX /** Max count user can enter via {} */ #define RE_DUP_MAX 0x7ffe /* @} */ /** * @name Non-standard flags for regcomp() * @{ */ /** * Allow few common non-standard escapes: * @code * \b - word-change * \B - not word change * \d - digit * \D - non-digit * \s - space * \S - non-space * \w - word char * \W - non-word char * \/ - / * @endcode */ #define REG_RELAXED_SYNTAX (1 << 14) /** * Dont permute groups in attempt to get longest match. * * May give minor speed win at the expense of strict * POSIX compatibility. */ #define REG_RELAXED_MATCHING (1 << 15) /** Turn on both REG_RELAXED_SYNTAX and REG_RELAXED_MATCHING */ #define REG_RELAXED (REG_RELAXED_SYNTAX | REG_RELAXED_MATCHING) /* @} */ /* turn them permanently on */ #ifdef USUAL_RELAXED_REGEX #undef REG_EXTENDED #define REG_EXTENDED (1 | REG_RELAXED) #endif /** * Compiled regex. * * It has only one standard field - re_nsub, * rest are implementation-specific. */ typedef struct { /** Number of subgroups in expression */ int re_nsub; void *internal; } regex_t; /** Type for offset in match */ typedef long regoff_t; /** Match location */ typedef struct { regoff_t rm_so; /**< Start offset */ regoff_t rm_eo; /**< End offset */ } regmatch_t; /* avoid name conflicts */ #define regcomp(a,b,c) usual_regcomp(a,b,c) #define regexec(a,b,c,d,e) usual_regexec(a,b,c,d,e) #define regerror(a,b,c,d) usual_regerror(a,b,c,d) #define regfree(a) usual_regfree(a) /** * Compile regex. * * @param rx Pre-allocated @ref regex_t structure to fill. * @param re Regex as zero-terminated string. * @param flags See above for regcomp() flags. */ int regcomp(regex_t *rx, const char *re, int flags); /** * Execute regex on a string. * * @param rx Regex previously initialized with regcomp() * @param str Zero-terminated string to match * @param nmatch Number of matches in pmatch * @param pmatch Array of matches. * @param eflags Execution flags. Supported flags: @ref REG_NOTBOL, @ref REG_NOTEOL */ int regexec(const regex_t *rx, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags); /** * Give error description. * * @param err Error code returned by regcomp() or regexec() * @param rx Regex structure used in regcomp() or regexec() * @param dst Destination buffer * @param dstlen Size of dst */ size_t regerror(int err, const regex_t *rx, char *dst, size_t dstlen); /** * Free resources allocated by regcomp(). * @param rx Regex previously filled by regcomp() */ void regfree(regex_t *rx); #endif /* !USE_SYSTEM_REGEX */ #endif /* _USUAL_REGEX_H_ */ pgbouncer-1.24.1/lib/usual/logging.h0000644000175000000000000001166214777762223014214 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Logging framework for unix services. * * * Supported outputs: * - syslog * - log file * - stderr * * @section logging_prefix Logging context * * It is possible to pass context info to all logging calls * and later add details to log lines or to filter based on it. * * Each call references 2 macros: * - LOG_CONTEXT_DEF - which can define/call any variables * - LOG_CONTEXT - which should return a pointer variable. * * Later, global callback function \ref logging_prefix_cb * will get this pointer with destination buffer and can either * add more info for log line or tell to skip logging this message. */ #ifndef _USUAL_LOGGING_H_ #define _USUAL_LOGGING_H_ #include /* internal log levels */ enum LogLevel { LG_FATAL = 0, LG_ERROR = 1, LG_WARNING = 2, LG_STATS = 3, LG_INFO = 4, LG_DEBUG = 5, LG_NOISE = 6, }; #ifndef LOG_CONTEXT_DEF /** Example: Prepare dummy context pointer */ #define LOG_CONTEXT_DEF void *_log_ctx = NULL #endif #ifndef LOG_CONTEXT /** Example: Reference dummy context pointer */ #define LOG_CONTEXT _log_ctx #endif /** * Signature for logging_prefix_cb. Return value is either added string length in dst * or negative value to skip logging. */ typedef int (*logging_prefix_fn_t)(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen); /** * Optional global callback for each log line. * * It can either add info to log message or skip logging it. */ extern logging_prefix_fn_t logging_prefix_cb; /** * Global verbosity level. * * 0 - show only info level msgs (default) * 1 - show debug msgs (log_debug) * 2 - show noise msgs (log_noise) */ extern int cf_verbose; /** * Toggle logging to stderr. Default: 1. * daemon.c turns this off if goes to background */ extern int cf_quiet; /** * Logfile location, default NULL */ extern const char *cf_logfile; /** Syslog on/off */ extern int cf_syslog; /** ident for syslog, if NULL syslog is disabled (default) */ extern const char *cf_syslog_ident; /** Facility name */ extern const char *cf_syslog_facility; /** Max log level for syslog writer */ extern enum LogLevel cf_syslog_level; /** Max log level for logfile writer */ extern enum LogLevel cf_logfile_level; /** Max log level for stderr writer */ extern enum LogLevel cf_stderr_level; /* * Internal API. */ /* non-fatal logging */ void log_generic(enum LogLevel level, void *ctx, const char *s, ...) _PRINTF(3, 4); /* this is also defined in base.h for Assert() */ void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *s, ...) _PRINTF(6, 7); /* * Public API */ /** Log error message */ #define log_error(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_ERROR, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log warning message */ #define log_warning(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_WARNING, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log stats (liveness) message */ #define log_stats(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_STATS, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log info message */ #define log_info(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_INFO, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log debug message */ #define log_debug(...) do { LOG_CONTEXT_DEF; \ if (unlikely(cf_verbose > 0)) \ log_generic(LG_DEBUG, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log debug noise */ #define log_noise(...) do { LOG_CONTEXT_DEF; \ if (unlikely(cf_verbose > 1)) \ log_generic(LG_NOISE, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log and die. It also logs source location */ #define fatal(...) do { LOG_CONTEXT_DEF; \ log_fatal(__FILE__, __LINE__, __func__, false, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** Log strerror and die. Error message also includes strerror(errno) */ #define fatal_perror(...) do { LOG_CONTEXT_DEF; \ log_fatal(__FILE__, __LINE__, __func__, true, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** Less verbose fatal() */ #define die(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_FATAL, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** * Close open logfiles and syslog. * * Useful when rotating log files. */ void reset_logging(void); #endif pgbouncer-1.24.1/lib/usual/statlist.h0000644000175000000000000001010514777762223014424 00000000000000/* * Wrapper for list.h that keeps track of number of items. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Circular list that keep track of stats about the list. * * Currenly only count of abjects currently in list * is kept track of. The plan was to track more, * like max, but it was not useful enough. */ #ifndef _USUAL_STATLIST_H_ #define _USUAL_STATLIST_H_ #include /** * Header structure for StatList. */ struct StatList { /** Actual list head */ struct List head; /** Count of objects currently in list */ int cur_count; #ifdef LIST_DEBUG /** List name */ const char *name; #endif }; /** Define and initialize StatList head */ #ifdef LIST_DEBUG #define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0, #var } #else #define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0 } #endif /** Add to the start of the list */ static inline void statlist_prepend(struct StatList *list, struct List *item) { list_prepend(&list->head, item); list->cur_count++; } /** Add to the end of the list */ static inline void statlist_append(struct StatList *list, struct List *item) { list_append(&list->head, item); list->cur_count++; } /** Remove element from the list */ static inline void statlist_remove(struct StatList *list, struct List *item) { list_del(item); list->cur_count--; /* Assert(list->cur_count >= 0); */ } /** Initialize StatList head */ static inline void statlist_init(struct StatList *list, const char *name) { list_init(&list->head); list->cur_count = 0; #ifdef LIST_DEBUG list->name = name; #endif } /** return number of elements currently in list */ static inline int statlist_count(const struct StatList *list) { /* Assert(list->cur_count > 0 || list_empty(&list->head)); */ return list->cur_count; } /** remove and return first element */ static inline struct List *statlist_pop(struct StatList *list) { struct List *item = list_pop(&list->head); if (item) list->cur_count--; /* Assert(list->cur_count >= 0); */ return item; } /** Return first element */ static inline struct List *statlist_first(const struct StatList *list) { return list_first(&list->head); } /** Return last element */ static inline struct List *statlist_last(const struct StatList *list) { return list_last(&list->head); } /** Is list empty */ static inline bool statlist_empty(const struct StatList *list) { return list_empty(&list->head); } /** Loop over list */ #define statlist_for_each(item, list) list_for_each(item, &((list)->head)) /** Loop over list backwards */ #define statlist_for_each_reverse(item, list) list_for_each_reverse(item, &((list)->head)) /** Loop over list safely, so that elements can be removed during */ #define statlist_for_each_safe(item, list, tmp) list_for_each_safe(item, &((list)->head), tmp) /** Loop over list backwards safely, so that elements can be removed during */ #define statlist_for_each_reverse_safe(item, list, tmp) list_for_each_reverse_safe(item, &((list)->head), tmp) /** Put intem before another */ static inline void statlist_put_before(struct StatList *list, struct List *item, struct List *pos) { list_append(pos, item); list->cur_count++; } /** Put item after another */ static inline void statlist_put_after(struct StatList *list, struct List *item, struct List *pos) { list_prepend(pos, item); list->cur_count++; } #endif /* __LIST_H_ */ pgbouncer-1.24.1/lib/usual/signal.h0000644000175000000000000000503414777762223014037 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Signals compat. * * general * - sigaction() -> signal() * * win32: * - SIGALRM, alarm(), signal(SIGALRM), sigaction(SIGALRM) * - kill(pid, 0) */ #ifndef _USUAL_SIGNAL_H_ #define _USUAL_SIGNAL_H_ #include #include /* * Compat sigval, detect based on siginfo_t.si_code. */ #if !defined(SI_QUEUE) && !defined(HAVE_SIGQUEUE) union sigval { int sival_int; void *sival_ptr; }; #endif /* * Compat sigevent */ #ifndef SIGEV_NONE #define SIGEV_NONE 0 #define SIGEV_SIGNAL 1 #define SIGEV_THREAD 2 struct sigevent { int sigev_notify; int sigev_signo; union sigval sigev_value; void (*sigev_notify_function)(union sigval); }; #endif /* * Compat sigaction() */ #ifndef HAVE_SIGACTION #define SA_SIGINFO 1 #define SA_RESTART 2 typedef struct siginfo_t siginfo_t; struct sigaction { union { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); }; int sa_flags; int sa_mask; }; #define sigemptyset(s) #define sigfillset(s) #define sigaddset(s, sig) #define sigdelset(s, sig) #define sigaction(a,b,c) compat_sigaction(a,b,c) int sigaction(int sig, const struct sigaction *sa, struct sigaction *old); #endif /* * win32 compat: * kill(), alarm, SIGALRM */ #ifdef WIN32 #define SIGALRM 1023 #define SIGBUS 1022 unsigned alarm(unsigned); int kill(int pid, int sig); typedef void (*_sighandler_t)(int); static inline _sighandler_t wrap_signal(int sig, _sighandler_t func) { /* sigaction has custom handling for SIGALRM */ if (sig == SIGALRM) { struct sigaction sa, oldsa; sa.sa_handler = func; sa.sa_flags = sa.sa_mask = 0; sigaction(SIGALRM, &sa, &oldsa); return oldsa.sa_handler; } else if (sig == SIGBUS) { return NULL; } return signal(sig, func); } #define signal(a,b) wrap_signal(a,b) #endif #endif pgbouncer-1.24.1/lib/usual/signal.c0000644000175000000000000000545614777762223014042 00000000000000/* * Signal compat. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * alarm() for win32 */ #ifdef WIN32 struct AlarmCtx { struct sigaction sa; HANDLE event; HANDLE thread; int secs; }; static volatile struct AlarmCtx actx; static DWORD WINAPI w32_alarm_thread(LPVOID arg) { DWORD wres; unsigned msecs; loop: if (actx.secs > 0) { msecs = actx.secs * 1000; } else { msecs = INFINITE; } wres = WaitForSingleObject(actx.event, msecs); if (wres == WAIT_OBJECT_0) { goto loop; } else if (wres == WAIT_TIMEOUT) { actx.secs = 0; if (actx.sa.sa_handler) actx.sa.sa_handler(SIGALRM); goto loop; } else { Sleep(1000); goto loop; } return 0; } unsigned int alarm(unsigned int secs) { actx.secs = secs; /* create event */ if (!actx.event) { actx.event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!actx.event) return 0; } /* create or notify thread */ if (!actx.thread) { actx.thread = CreateThread(NULL, 0, w32_alarm_thread, NULL, 0, NULL); } else { SetEvent(actx.event); } return 0; } #endif #ifndef HAVE_SIGACTION int sigaction(int sig, const struct sigaction *sa, struct sigaction *old) { #ifdef WIN32 if (sig == SIGALRM) { if (old) *old = actx.sa; if (sa) actx.sa = *sa; else actx.sa.sa_handler = NULL; return 0; } #endif old->sa_handler = signal(sig, sa->sa_handler); if (old->sa_handler == SIG_ERR) return -1; return 0; } #endif #ifdef WIN32 /* Only sig=0 is supported, to detect if process is running (ESRCH->not) */ int kill(int pid, int sig) { HANDLE hProcess; DWORD exitCode; int ret = 0; /* handle only sig == 0 */ if (sig != 0) { errno = EINVAL; return -1; } hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if (hProcess == NULL) { if (GetLastError() == ERROR_INVALID_PARAMETER) ret = ESRCH; else ret = EPERM; } else { /* OpenProcess may succed for exited processes */ if (GetExitCodeProcess(hProcess, &exitCode)) { if (exitCode != STILL_ACTIVE) ret = ESRCH; } CloseHandle(hProcess); } if (ret) { errno = ret; return -1; } else return 0; } #endif pgbouncer-1.24.1/lib/usual/netdb.c0000644000175000000000000001104414777762223013647 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2010 Marko Kreen, Skype Technologies * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* is compat function needed? */ #ifndef HAVE_GETADDRINFO_A /* full compat if threads are available */ #ifdef HAVE_PTHREAD #include #include /* * Basic blocking lookup */ static void gaia_lookup(pthread_t origin, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct gaicb *g; int i, res; for (i = 0; i < nitems; i++) { g = list[i]; res = getaddrinfo(g->ar_name, g->ar_service, g->ar_request, &g->ar_result); g->_state = res; } if (!sevp || sevp->sigev_notify == SIGEV_NONE) { /* do nothing */ } else if (sevp->sigev_notify == SIGEV_SIGNAL) { /* send signal */ pthread_kill(origin, sevp->sigev_signo); } else if (sevp->sigev_notify == SIGEV_THREAD) { /* call function */ sevp->sigev_notify_function(sevp->sigev_value); } } /* * Thread to run blocking lookup in */ struct GAIAContext { struct List req_list; pthread_cond_t cond; pthread_mutex_t lock; pthread_t thread; }; struct GAIARequest { struct List node; pthread_t origin; int nitems; struct sigevent sev; struct gaicb *list[FLEX_ARRAY]; }; #define RQ_SIZE(n) (offsetof(struct GAIARequest,list) + (n)*(sizeof(struct gaicb *))) static void gaia_lock_reqs(struct GAIAContext *ctx) { pthread_mutex_lock(&ctx->lock); } static void gaia_unlock_reqs(struct GAIAContext *ctx) { pthread_mutex_unlock(&ctx->lock); } static void *gaia_lookup_thread(void *arg) { struct GAIAContext *ctx = arg; struct GAIARequest *rq; struct List *el; gaia_lock_reqs(ctx); while (1) { el = list_pop(&ctx->req_list); if (!el) { pthread_cond_wait(&ctx->cond, &ctx->lock); continue; } gaia_unlock_reqs(ctx); rq = container_of(el, struct GAIARequest, node); gaia_lookup(rq->origin, rq->list, rq->nitems, &rq->sev); free(rq); gaia_lock_reqs(ctx); } return NULL; } /* * Functions run in user thread */ static int gaia_post_request(struct GAIAContext *ctx, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct GAIARequest *rq; rq = malloc(RQ_SIZE(nitems)); if (!rq) return EAI_MEMORY; list_init(&rq->node); rq->origin = pthread_self(); rq->nitems = nitems; if (sevp) rq->sev = *sevp; else rq->sev.sigev_notify = SIGEV_NONE; memcpy(rq->list, list, sizeof(struct gaicb *)); gaia_lock_reqs(ctx); list_append(&ctx->req_list, &rq->node); gaia_unlock_reqs(ctx); pthread_cond_signal(&ctx->cond); return 0; } static struct GAIAContext *gaia_create_context(void) { struct GAIAContext *ctx; int err; ctx = malloc(sizeof(*ctx)); if (!ctx) return NULL; list_init(&ctx->req_list); err = pthread_cond_init(&ctx->cond, NULL); if (err) goto failed; err = pthread_mutex_init(&ctx->lock, NULL); if (err) goto failed; err = pthread_create(&ctx->thread, NULL, gaia_lookup_thread, ctx); if (err) goto failed; return ctx; failed: free(ctx); errno = err; return NULL; } /* * Final interface */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) { static struct GAIAContext *ctx; if (nitems <= 0) return 0; if (sevp && sevp->sigev_notify != SIGEV_NONE && sevp->sigev_notify != SIGEV_SIGNAL && sevp->sigev_notify != SIGEV_THREAD) goto einval; if (mode == GAI_WAIT) { gaia_lookup(pthread_self(), list, nitems, sevp); return 0; } else if (mode == GAI_NOWAIT) { if (!ctx) { ctx = gaia_create_context(); if (!ctx) return EAI_MEMORY; } return gaia_post_request(ctx, list, nitems, sevp); } einval: errno = EINVAL; return EAI_SYSTEM; } #else /* without threads not much to do */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) { errno = ENOSYS; return EAI_SYSTEM; } #endif /* !HAVE_PTHREAD_H */ #endif /* !HAVE_GETADDRINFO_A */ pgbouncer-1.24.1/lib/usual/time.c0000644000175000000000000001140714777762223013514 00000000000000/* * Common time functions. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include char *format_time_ms(usec_t time, char *dest, unsigned destlen) { struct tm *tm, tmbuf; struct timeval tv; time_t sec; if (!time) { gettimeofday(&tv, NULL); } else { tv.tv_sec = time / USEC; tv.tv_usec = time % USEC; } sec = tv.tv_sec; tm = localtime_r(&sec, &tmbuf); snprintf(dest, destlen, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000), tzname[tm->tm_isdst > 0 ? 1 : 0]); return dest; } char *format_time_s(usec_t time, char *dest, unsigned destlen) { time_t s; struct tm tbuf, *tm; if (!time) { struct timeval tv; gettimeofday(&tv, NULL); s = tv.tv_sec; } else { s = time / USEC; } tm = localtime_r(&s, &tbuf); snprintf(dest, destlen, "%04d-%02d-%02d %02d:%02d:%02d %s", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tzname[tm->tm_isdst > 0 ? 1 : 0]); return dest; } /* read current time */ usec_t get_time_usec(void) { struct timeval tv; gettimeofday(&tv, NULL); return (usec_t)tv.tv_sec * USEC + tv.tv_usec; } static usec_t _time_cache; /* read cached time */ usec_t get_cached_time(void) { if (!_time_cache) _time_cache = get_time_usec(); return _time_cache; } /* forget cached time, let next read fill it */ void reset_time_cache(void) { _time_cache = 0; } /* * win32 compat */ #ifdef WIN32 /* unix epoch (1970) in seconds from windows epoch (1601) */ #define UNIX_EPOCH 11644473600LL /* 1 sec in 100 nsec units */ #define FT_SEC 10000000LL static void ft2tv(FILETIME *src, struct timeval *dest, bool use_epoch) { ULARGE_INTEGER tmp; tmp.LowPart = src->dwLowDateTime; tmp.HighPart = src->dwHighDateTime; dest->tv_sec = (tmp.QuadPart / FT_SEC) - (use_epoch ? UNIX_EPOCH : 0); dest->tv_usec = (tmp.QuadPart % FT_SEC) / 10; } #ifndef HAVE_GETTIMEOFDAY int gettimeofday(struct timeval * tp, void * tzp) { FILETIME file_time; SYSTEMTIME system_time; /* read UTC timestamp */ GetSystemTime(&system_time); SystemTimeToFileTime(&system_time, &file_time); /* convert to timeval */ ft2tv(&file_time, tp, true); return 0; } #endif /* !HAVE_GETTIMEOFDAY */ #ifndef HAVE_LOCALTIME_R struct tm *localtime_r(const time_t *tp, struct tm *result) { ULARGE_INTEGER utc; FILETIME ft_utc; SYSTEMTIME st_utc, st_local; /* convert time_t to FILETIME */ utc.QuadPart = (*tp + UNIX_EPOCH) * FT_SEC; ft_utc.dwLowDateTime = utc.LowPart; ft_utc.dwHighDateTime = utc.HighPart; /* split to parts and get local time */ if (!FileTimeToSystemTime(&ft_utc, &st_utc)) return NULL; if (!SystemTimeToTzSpecificLocalTime(NULL, &st_utc, &st_local)) return NULL; /* fill struct tm */ result->tm_sec = st_local.wSecond; result->tm_min = st_local.wMinute; result->tm_hour = st_local.wHour; result->tm_mday = st_local.wDay; result->tm_mon = st_local.wMonth - 1; result->tm_year = st_local.wYear - 1900; result->tm_wday = st_local.wDayOfWeek; result->tm_yday = 0; result->tm_isdst = -1; return result; } #endif /* !HAVE_LOCALTIME_R */ #ifndef HAVE_GETRUSAGE int getrusage(int who, struct rusage *r_usage) { FILETIME tcreate, texit, tkern, tuser; if (who != RUSAGE_SELF) { errno = EINVAL; return -1; } if (!GetProcessTimes(GetCurrentProcess(), &tcreate, &texit, &tkern, &tuser)) return -1; ft2tv(&tuser, &r_usage->ru_utime, false); ft2tv(&tkern, &r_usage->ru_stime, false); return 0; } #endif /* !HAVE_GETRUSAGE */ #endif /* WIN32 */ #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm) { #ifdef WIN32 return _mkgmtime(tm); #else char buf[128], *tz, *old = NULL; time_t secs; tz = getenv("TZ"); if (tz) { old = strdup(tz); if (!old) { strlcpy(buf, tz, sizeof buf); old = buf; } } setenv("TZ", "", 1); tzset(); secs = mktime(tm); if (old) { setenv("TZ", old, 1); } else { unsetenv("TZ"); } tzset(); if (old && old != buf) free(old); return secs; #endif } #endif /* HAVE_TIMEGM */ pgbouncer-1.24.1/lib/usual/mempool.h0000644000175000000000000000217014777762223014230 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Simple memory pool for variable-length allocations. */ #ifndef _USUAL_MEMPOOL_H_ #define _USUAL_MEMPOOL_H_ #include /** Pool Reference */ struct MemPool; /** Allocate from pool */ void *mempool_alloc(struct MemPool **pool, unsigned size) _MALLOC; /** Release all memory in pool */ void mempool_destroy(struct MemPool **pool); #endif pgbouncer-1.24.1/lib/usual/list.h0000644000175000000000000000757114777762223013545 00000000000000/* * Circular doubly linked list implementation. * * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Circular doubly linked list. */ #ifndef _USUAL_LIST_H_ #define _USUAL_LIST_H_ #include /** * Structure for both list nodes and heads. * * It is meant to be embedded in parent structure, * which can be acquired with container_of(). */ struct List { /** Pointer to next node or head. */ struct List *next; /** Pointer to previous node or head. */ struct List *prev; }; /** Define and initialize emtpy list head */ #define LIST(var) struct List var = { &var, &var } /** Initialize empty list head. */ static inline void list_init(struct List *list) { list->next = list->prev = list; } /** Is list empty? */ static inline int list_empty(const struct List *list) { return list->next == list; } /** Add item to the start of the list */ static inline struct List *list_prepend(struct List *list, struct List *item) { item->next = list->next; item->prev = list; list->next->prev = item; list->next = item; return item; } /** Add item to the end of the list */ static inline struct List *list_append(struct List *list, struct List *item) { item->next = list; item->prev = list->prev; list->prev->next = item; list->prev = item; return item; } /** Remove item from list */ static inline struct List *list_del(struct List *item) { item->prev->next = item->next; item->next->prev = item->prev; item->next = item->prev = item; return item; } /** Remove first from list and return */ static inline struct List *list_pop(struct List *list) { if (list_empty(list)) return NULL; return list_del(list->next); } /** Get first elem from list */ static inline struct List *list_first(const struct List *list) { if (list_empty(list)) return NULL; return list->next; } /** Get last elem from list */ static inline struct List *list_last(const struct List *list) { if (list_empty(list)) return NULL; return list->prev; } /** Remove first elem from list and return with casting */ #define list_pop_type(list, typ, field) \ (list_empty(list) ? NULL \ : container_of(list_del((list)->next), typ, field)) /** Loop over list */ #define list_for_each(item, list) \ for ((item) = (list)->next; \ (item) != (list); \ (item) = (item)->next) /** Loop over list backwards */ #define list_for_each_reverse(item, list) \ for ((item) = (list)->prev; \ (item) != (list); \ (item) = (item)->prev) /** Loop over list and allow removing item */ #define list_for_each_safe(item, list, tmp) \ for ((item) = (list)->next, (tmp) = (list)->next->next; \ (item) != (list); \ (item) = (tmp), (tmp) = (tmp)->next) /** Loop over list backwards and allow removing item */ #define list_for_each_reverse_safe(item, list, tmp) \ for ((item) = (list)->prev, (tmp) = (list)->prev->prev; \ (item) != (list); \ (item) = (tmp), (tmp) = (tmp)->prev) /** Comparator function signature for list_sort() */ typedef int (*list_cmp_f)(const struct List *a, const struct List *b); /** * Sort list. * * This implementation uses stable merge sort which operates in-place. */ void list_sort(struct List *list, list_cmp_f cmp_func); #endif pgbouncer-1.24.1/lib/usual/pgutil_kwlookup.h0000644000175000000000000005423314777762223016026 00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf -m5 usual/pgutil_kwlookup.g */ /* Computed positions: -k'1-2,6,9,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif /* maximum key range = 296, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int pg_keyword_lookup_hash (register const char *str, register size_t len) { static const unsigned short asso_values[] = { 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 38, 125, 31, 64, 10, 96, 60, 125, 26, 7, 5, 13, 63, 10, 12, 70, 312, 5, 19, 3, 71, 131, 65, 50, 77, 3, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[8]]; /*FALLTHROUGH*/ case 8: case 7: case 6: hval += asso_values[(unsigned char)str[5]]; /*FALLTHROUGH*/ case 5: case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const char * pg_keyword_lookup_real (register const char *str, register size_t len) { enum { TOTAL_KEYWORDS = 148, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 17, MIN_HASH_VALUE = 16, MAX_HASH_VALUE = 311 }; struct pgkw_t { char pgkw_str16[sizeof("treat")]; char pgkw_str22[sizeof("true")]; char pgkw_str24[sizeof("or")]; char pgkw_str27[sizeof("order")]; char pgkw_str28[sizeof("not")]; char pgkw_str29[sizeof("to")]; char pgkw_str30[sizeof("left")]; char pgkw_str31[sizeof("least")]; char pgkw_str32[sizeof("real")]; char pgkw_str33[sizeof("join")]; char pgkw_str34[sizeof("on")]; char pgkw_str36[sizeof("none")]; char pgkw_str37[sizeof("else")]; char pgkw_str39[sizeof("right")]; char pgkw_str41[sizeof("select")]; char pgkw_str42[sizeof("int")]; char pgkw_str43[sizeof("time")]; char pgkw_str44[sizeof("inout")]; char pgkw_str45[sizeof("some")]; char pgkw_str46[sizeof("inner")]; char pgkw_str47[sizeof("limit")]; char pgkw_str48[sizeof("in")]; char pgkw_str51[sizeof("nchar")]; char pgkw_str52[sizeof("into")]; char pgkw_str53[sizeof("like")]; char pgkw_str54[sizeof("ilike")]; char pgkw_str55[sizeof("notnull")]; char pgkw_str56[sizeof("table")]; char pgkw_str57[sizeof("localtime")]; char pgkw_str58[sizeof("integer")]; char pgkw_str60[sizeof("cross")]; char pgkw_str62[sizeof("create")]; char pgkw_str63[sizeof("collate")]; char pgkw_str64[sizeof("references")]; char pgkw_str66[sizeof("is")]; char pgkw_str67[sizeof("all")]; char pgkw_str68[sizeof("analyze")]; char pgkw_str69[sizeof("column")]; char pgkw_str70[sizeof("intersect")]; char pgkw_str71[sizeof("constraint")]; char pgkw_str72[sizeof("except")]; char pgkw_str73[sizeof("grant")]; char pgkw_str75[sizeof("trim")]; char pgkw_str76[sizeof("cast")]; char pgkw_str77[sizeof("isnull")]; char pgkw_str78[sizeof("as")]; char pgkw_str79[sizeof("national")]; char pgkw_str80[sizeof("coalesce")]; char pgkw_str83[sizeof("case")]; char pgkw_str84[sizeof("analyse")]; char pgkw_str85[sizeof("row")]; char pgkw_str86[sizeof("greatest")]; char pgkw_str87[sizeof("end")]; char pgkw_str88[sizeof("new")]; char pgkw_str89[sizeof("out")]; char pgkw_str90[sizeof("do")]; char pgkw_str91[sizeof("asc")]; char pgkw_str92[sizeof("old")]; char pgkw_str93[sizeof("outer")]; char pgkw_str95[sizeof("similar")]; char pgkw_str96[sizeof("union")]; char pgkw_str97[sizeof("default")]; char pgkw_str98[sizeof("null")]; char pgkw_str99[sizeof("user")]; char pgkw_str100[sizeof("leading")]; char pgkw_str101[sizeof("extract")]; char pgkw_str102[sizeof("trailing")]; char pgkw_str103[sizeof("only")]; char pgkw_str104[sizeof("exists")]; char pgkw_str106[sizeof("natural")]; char pgkw_str107[sizeof("unique")]; char pgkw_str108[sizeof("dec")]; char pgkw_str109[sizeof("desc")]; char pgkw_str111[sizeof("distinct")]; char pgkw_str112[sizeof("deferrable")]; char pgkw_str115[sizeof("and")]; char pgkw_str116[sizeof("for")]; char pgkw_str117[sizeof("float")]; char pgkw_str119[sizeof("smallint")]; char pgkw_str120[sizeof("offset")]; char pgkw_str122[sizeof("localtimestamp")]; char pgkw_str123[sizeof("precision")]; char pgkw_str125[sizeof("array")]; char pgkw_str126[sizeof("position")]; char pgkw_str127[sizeof("freeze")]; char pgkw_str128[sizeof("any")]; char pgkw_str129[sizeof("session_user")]; char pgkw_str130[sizeof("setof")]; char pgkw_str132[sizeof("decimal")]; char pgkw_str133[sizeof("xmlforest")]; char pgkw_str134[sizeof("asymmetric")]; char pgkw_str135[sizeof("xmlroot")]; char pgkw_str136[sizeof("xmlparse")]; char pgkw_str137[sizeof("current_time")]; char pgkw_str138[sizeof("xmlconcat")]; char pgkw_str139[sizeof("current_role")]; char pgkw_str140[sizeof("group")]; char pgkw_str142[sizeof("then")]; char pgkw_str144[sizeof("xmlpi")]; char pgkw_str145[sizeof("numeric")]; char pgkw_str146[sizeof("xmlelement")]; char pgkw_str147[sizeof("concurrently")]; char pgkw_str149[sizeof("false")]; char pgkw_str152[sizeof("over")]; char pgkw_str153[sizeof("xmlserialize")]; char pgkw_str154[sizeof("returning")]; char pgkw_str155[sizeof("using")]; char pgkw_str157[sizeof("bit")]; char pgkw_str160[sizeof("placing")]; char pgkw_str162[sizeof("between")]; char pgkw_str163[sizeof("bigint")]; char pgkw_str164[sizeof("primary")]; char pgkw_str165[sizeof("char")]; char pgkw_str166[sizeof("check")]; char pgkw_str168[sizeof("from")]; char pgkw_str170[sizeof("symmetric")]; char pgkw_str175[sizeof("authorization")]; char pgkw_str177[sizeof("verbose")]; char pgkw_str181[sizeof("timestamp")]; char pgkw_str183[sizeof("current_schema")]; char pgkw_str184[sizeof("full")]; char pgkw_str185[sizeof("foreign")]; char pgkw_str186[sizeof("xmlexists")]; char pgkw_str188[sizeof("interval")]; char pgkw_str192[sizeof("boolean")]; char pgkw_str198[sizeof("current_date")]; char pgkw_str200[sizeof("current_user")]; char pgkw_str202[sizeof("current_timestamp")]; char pgkw_str204[sizeof("when")]; char pgkw_str205[sizeof("where")]; char pgkw_str206[sizeof("character")]; char pgkw_str207[sizeof("off")]; char pgkw_str208[sizeof("overlaps")]; char pgkw_str213[sizeof("values")]; char pgkw_str218[sizeof("current_catalog")]; char pgkw_str219[sizeof("varchar")]; char pgkw_str220[sizeof("with")]; char pgkw_str224[sizeof("substring")]; char pgkw_str227[sizeof("window")]; char pgkw_str236[sizeof("fetch")]; char pgkw_str237[sizeof("initially")]; char pgkw_str265[sizeof("overlay")]; char pgkw_str266[sizeof("both")]; char pgkw_str272[sizeof("variadic")]; char pgkw_str273[sizeof("xmlattributes")]; char pgkw_str279[sizeof("nullif")]; char pgkw_str289[sizeof("having")]; char pgkw_str311[sizeof("binary")]; }; static const struct pgkw_t pgkw_contents = { "treat", "true", "or", "order", "not", "to", "left", "least", "real", "join", "on", "none", "else", "right", "select", "int", "time", "inout", "some", "inner", "limit", "in", "nchar", "into", "like", "ilike", "notnull", "table", "localtime", "integer", "cross", "create", "collate", "references", "is", "all", "analyze", "column", "intersect", "constraint", "except", "grant", "trim", "cast", "isnull", "as", "national", "coalesce", "case", "analyse", "row", "greatest", "end", "new", "out", "do", "asc", "old", "outer", "similar", "union", "default", "null", "user", "leading", "extract", "trailing", "only", "exists", "natural", "unique", "dec", "desc", "distinct", "deferrable", "and", "for", "float", "smallint", "offset", "localtimestamp", "precision", "array", "position", "freeze", "any", "session_user", "setof", "decimal", "xmlforest", "asymmetric", "xmlroot", "xmlparse", "current_time", "xmlconcat", "current_role", "group", "then", "xmlpi", "numeric", "xmlelement", "concurrently", "false", "over", "xmlserialize", "returning", "using", "bit", "placing", "between", "bigint", "primary", "char", "check", "from", "symmetric", "authorization", "verbose", "timestamp", "current_schema", "full", "foreign", "xmlexists", "interval", "boolean", "current_date", "current_user", "current_timestamp", "when", "where", "character", "off", "overlaps", "values", "current_catalog", "varchar", "with", "substring", "window", "fetch", "initially", "overlay", "both", "variadic", "xmlattributes", "nullif", "having", "binary" }; #define pgkw ((const char *) &pgkw_contents) static const int wordlist[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str16, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str22, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str24, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str27, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str28, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str29, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str30, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str31, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str32, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str33, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str34, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str36, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str37, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str39, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str41, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str42, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str43, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str44, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str45, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str46, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str47, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str48, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str51, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str52, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str53, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str54, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str55, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str56, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str57, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str58, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str60, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str62, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str63, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str64, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str66, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str67, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str68, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str69, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str70, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str71, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str72, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str73, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str75, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str76, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str77, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str78, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str79, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str80, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str83, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str84, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str85, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str86, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str87, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str88, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str89, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str90, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str91, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str92, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str93, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str95, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str96, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str97, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str98, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str99, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str100, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str101, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str102, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str103, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str104, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str106, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str107, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str108, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str109, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str111, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str112, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str115, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str116, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str117, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str119, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str120, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str122, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str123, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str125, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str126, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str127, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str128, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str129, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str130, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str132, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str133, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str134, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str135, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str136, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str137, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str138, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str139, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str140, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str142, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str144, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str145, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str146, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str147, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str149, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str152, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str153, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str154, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str155, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str157, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str160, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str162, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str163, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str164, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str165, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str166, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str168, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str170, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str175, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str177, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str181, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str183, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str184, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str185, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str186, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str188, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str192, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str198, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str200, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str202, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str204, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str205, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str206, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str207, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str208, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str213, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str218, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str219, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str220, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str224, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str227, -1, -1, -1, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str236, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str265, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str266, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str272, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str273, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str279, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str289, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(size_t)&((struct pgkw_t *)0)->pgkw_str311 }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = pg_keyword_lookup_hash (str, len); if (key <= MAX_HASH_VALUE) { register int o = wordlist[key]; if (o >= 0) { register const char *s = o + pgkw; if (*str == *s && !strcmp (str + 1, s + 1)) return s; } } } return 0; } pgbouncer-1.24.1/lib/usual/base_win32.h0000644000175000000000000000665714777762223014532 00000000000000/* * Random win32 compat. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_BASE_WIN32_H_ #define _USUAL_BASE_WIN32_H_ #include #include #include #include #ifndef ECONNABORTED #define ECONNABORTED WSAECONNABORTED #endif #ifndef EMSGSIZE #define EMSGSIZE WSAEMSGSIZE #endif #ifndef EINPROGRESS #define EINPROGRESS WSAEWOULDBLOCK /* WSAEINPROGRESS */ #endif #ifndef SHUT_RDWR #define SHUT_RDWR SD_BOTH #endif #ifndef ENOTCONN #define ENOTCONN WSAENOTCONN #endif #ifndef ECONNRESET #define ECONNRESET WSAECONNRESET #endif #undef EAGAIN #define EAGAIN WSAEWOULDBLOCK /* WSAEAGAIN */ #ifndef EAFNOSUPPORT #define EAFNOSUPPORT ENOSYS #endif #ifndef AI_ADDRCONFIG #define AI_ADDRCONFIG 0 #endif /* dummy types / functions */ #define hstrerror strerror #define getuid() (6667) #define setsid() getpid() #define setgid(x) (-1) #define setuid(x) (-1) #define fork() (errno = ENOSYS, -1) #define geteuid() getuid() static inline int setgroups(int ngroups, const gid_t *gidsets) { errno = EINVAL; return -1; } #define chown(f, u, g) (-1) #define srandom(s) srand(s) #define random() rand() #ifdef _MSC_VER #define snprintf(fmt, ...) _snprintf(fmt, __VA_ARGS__) static inline int strcasecmp(const char *a, const char *b) { return _stricmp(a, b); } static inline int strncasecmp(const char *a, const char *b, size_t cnt) { return _strnicmp(a, b, cnt); } typedef int ssize_t; #endif /* getrlimit() */ #define RLIMIT_NOFILE -1 struct rlimit { int rlim_cur; int rlim_max; }; static inline int getrlimit(int res, struct rlimit *dst) { dst->rlim_cur = dst->rlim_max = -1; return 0; } /* dummy getpwnam() */ struct passwd { char *pw_name; char *pw_passwd; uid_t pw_uid; pid_t pw_gid; char *pw_gecos; char *pw_dir; char *pw_shell; }; static inline struct passwd *getpwnam(const char *u) { return NULL; } static inline struct passwd *getpwuid(uid_t uid) { return NULL; } /* dummy getgrnam() */ struct group { char *gr_name; char *gr_passwd; gid_t gr_gid; char **gr_mem; }; static inline struct group *getgrnam(const char *g) { return NULL; } static inline struct group *getgrgid(gid_t gid) { return NULL; } /* format specifiers that should be in */ #ifndef HAVE_INTTYPES_H #define PRId8 "d" #define PRId16 "d" #define PRId32 "d" #define PRId64 "I64d" #define PRIi8 "d" #define PRIi16 "d" #define PRIi32 "d" #define PRIi64 "I64d" #define PRIo8 "o" #define PRIo16 "o" #define PRIo32 "o" #define PRIo64 "I64o" #define PRIu8 "u" #define PRIu16 "u" #define PRIu32 "u" #define PRIu64 "I64u" #define PRIx8 "x" #define PRIx16 "x" #define PRIx32 "x" #define PRIx64 "I64x" #define PRIX8 "X" #define PRIX16 "X" #define PRIX32 "X" #define PRIX64 "I64X" #endif #endif pgbouncer-1.24.1/lib/usual/psrandom.c0000644000175000000000000000745214777762223014406 00000000000000/* * Pseudo-random bytes. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* Written in 2014 by Sebastiano Vigna (vigna@acm.org) To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See . */ /* This is the fastest generator passing BigCrush without systematic failures, but due to the relatively short period it is acceptable only for applications with a mild amount of parallelism; otherwise, use a xorshift1024* generator. The state must be seeded so that it is not everywhere zero. If you have a nonzero 64-bit seed, we suggest to pass it twice through MurmurHash3's avalanching function. */ static inline uint64_t xorshift128plus_core(uint64_t a, uint64_t b, uint64_t *sb) { b ^= b << 23; b ^= a ^ (b >> 17) ^ (a >> 26); *sb = b; return a + b; } /* * End-user APIs for 128-bit and 1024-bit states. */ /* 128-bit state. Period: 2**128 - 1 */ uint64_t xorshift128plus(uint64_t *s0, uint64_t *s1) { /* swap s0 and s1, calculate new s1 */ uint64_t a = *s1, b = *s0; *s0 = a; return xorshift128plus_core(a, b, s1); } #define XS1K_STATE 16 #define XS1K_MASK (XS1K_STATE - 1) /* 1024-bit state. Period: 2**1024 - 1 */ uint64_t xorshift1024plus(uint64_t state[XS1K_STATE], unsigned int counter) { uint64_t *s0 = &state[counter & XS1K_MASK]; uint64_t *s1 = &state[(counter + 1) & XS1K_MASK]; return xorshift128plus_core(*s0, *s1, s1); } /* * csrandom()-style API on top that. */ static uint64_t ps_state[XS1K_STATE]; static uint32_t ps_init, ps_counter, ps_cache; static void ps_initial_seed(void) { csrandom_bytes(ps_state, sizeof ps_state); ps_init = 1; } void pseudo_random_seed(uint64_t a, uint64_t b) { uint64_t X1 = 123456789, X2 = 987654321; int i; /* xorshift does not like all-zero value */ if (a + X1) a += X1; if (b + X2) b += X2; /* fill all state */ for (i = XS1K_STATE - 1; i >= 0; i--) ps_state[i] = xorshift128plus(&a, &b); ps_init = 1; ps_counter = 0; } uint32_t pseudo_random(void) { uint64_t val; if (!ps_init) ps_initial_seed(); if (ps_init == 2) { ps_init = 1; return ps_cache; } val = xorshift1024plus(ps_state, ps_counter++); ps_cache = val >> 32; ps_init = 2; return val; } void pseudo_random_bytes(void *dst, size_t count) { uint32_t val; uint8_t *p = dst; while (count >= 4) { val = pseudo_random(); le32enc(p, val); count -= 4; p += 4; } if (count > 0) { for (val = pseudo_random(); count > 0; count--) { *p++ = val; val >>= 8; } } } uint32_t pseudo_random_range(uint32_t upper_bound) { uint32_t mod, lim, val; if (upper_bound <= 1) return 0; /* 2**32 % x == (2**32 - x) % x */ mod = -upper_bound % upper_bound; /* wait for value in range [0 .. 2**32-mod) */ lim = -mod; /* loop until good value appears */ while (1) { val = pseudo_random(); if (val < lim || lim == 0) return val % upper_bound; } } pgbouncer-1.24.1/lib/usual/shlist.h0000644000175000000000000001046214777762223014071 00000000000000/* * Circular list for shared mem. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Circular list for shared mem. * * Instead of pointers, it uses offsets from list head. */ #ifndef _USUAL_SHLIST_H_ #define _USUAL_SHLIST_H_ #include /* clang: pointers are hard */ #if defined(__clang__) #define __shlist_clang_workaround__ volatile #else #define __shlist_clang_workaround__ #endif /** List node/head. Uses offsets from current node instead of direct pointers. */ struct SHList { ptrdiff_t next; ptrdiff_t prev; }; /* * Calculate offset relative to base. * * Instead of using some third pointer (eg. shmem start) as base, * we use node itself as base. This results in simpler APi * and also means that empty node appears as zero-filled. */ /** Get next element in list */ static inline struct SHList *shlist_get_next(const struct SHList *node) { char *p = (char *)node + node->next; return (struct SHList *)p; } /** Get prev element in list */ static inline struct SHList *shlist_get_prev(const struct SHList *node) { char *p = (char *)node + node->prev; return (struct SHList *)p; } static inline void _shlist_set_next(__shlist_clang_workaround__ struct SHList *node, const struct SHList *next) { node->next = (char *)next - (char *)node; } static inline void _shlist_set_prev(__shlist_clang_workaround__ struct SHList *node, const struct SHList *prev) { node->prev = (char *)prev - (char *)node; } /* * List operations. */ /** Initialize list head */ static inline void shlist_init(struct SHList *list) { list->next = 0; list->prev = 0; } /** Insert as last element */ static inline void shlist_append(struct SHList *list, struct SHList *node) { struct SHList *last; last = shlist_get_prev(list); _shlist_set_next(node, list); _shlist_set_prev(node, last); _shlist_set_next(last, node); _shlist_set_prev(list, node); } /** Insert as first element */ static inline void shlist_prepend(struct SHList *list, struct SHList *node) { struct SHList *first; first = shlist_get_next(list); _shlist_set_next(node, first); _shlist_set_prev(node, list); _shlist_set_next(list, node); _shlist_set_prev(first, node); } /** Remove an node */ static inline void shlist_remove(struct SHList *node) { struct SHList *next = shlist_get_next(node); struct SHList *prev = shlist_get_prev(node); _shlist_set_prev(next, prev); _shlist_set_next(prev, next); shlist_init(node); } /** No elements? */ static inline bool shlist_empty(const struct SHList *list) { return list->next == 0; } /** Return first elem */ static inline struct SHList *shlist_first(const struct SHList *list) { if (shlist_empty(list)) return NULL; return shlist_get_next(list); } /** Return last elem */ static inline struct SHList *shlist_last(const struct SHList *list) { if (shlist_empty(list)) return NULL; return shlist_get_prev(list); } /** Remove first elem */ static inline struct SHList *shlist_pop(struct SHList *list) { struct SHList *node = shlist_first(list); if (node) shlist_remove(node); return node; } /** Remove and return specific type of elem */ #define shlist_pop_type(list, type, field) ( \ shlist_empty(list) ? NULL : container_of(shlist_pop(list), type, field)) /** Loop over list */ #define shlist_for_each(node, list) \ for ((node) = shlist_get_next(list); \ (node) != (list); \ (node) = shlist_get_next(node)) /** Loop over list and allow removing node */ #define shlist_for_each_safe(node, list, tmp) \ for ((node) = shlist_get_next(list), (tmp) = shlist_get_next(node); \ (node) != (list); \ (node) = (tmp), (tmp) = shlist_get_next(node)) #endif pgbouncer-1.24.1/lib/usual/config_msvc.h0000644000175000000000000000447614777762223015070 00000000000000 /* Define to 1 if you have the header file. */ #define HAVE_MALLOC_H 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "https://libusual.github.com" /* Define to the full name of this package. */ #define PACKAGE_NAME "libusual" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "libusual 0.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "libusual" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "0.1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to request cleaner win32 headers. */ #define WIN32_LEAN_AND_MEAN 1 /* Define to max win32 API version (0x0501=XP). */ //#define WINVER 0x0501 #define WINVER 0x0600 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined(_M_IX86) || defined(_M_X64) /* # undef WORDS_BIGENDIAN */ #else #error "Unsupported MSVC target CPU" #endif /* Define to `int' if doesn't define. */ #define gid_t int /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #define inline __inline #endif /* Define to `int' if does not define. */ #define pid_t int /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ #ifndef restrict #define restrict #endif /* Define to `int' if doesn't define. */ #define uid_t int #define _CRT_SECURE_NO_WARNINGS 1 #ifndef WIN32 #define WIN32 1 #endif pgbouncer-1.24.1/lib/usual/fileutil.c0000644000175000000000000000642714777762223014401 00000000000000/* * File access utils. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #include /* * Load text file into C string. */ void *load_file(const char *fn, size_t *len_p) { struct stat st; char *buf = NULL; int res; FILE *f; int save_errno; f = fopen(fn, "r"); if (!f) return NULL; res = fstat(fileno(f), &st); if (res < 0) { save_errno = errno; fclose(f); errno = save_errno; return NULL; } buf = malloc(st.st_size + 1); if (!buf) { save_errno = errno; fclose(f); errno = save_errno; return NULL; } if ((res = fread(buf, 1, st.st_size, f)) < 0) { save_errno = errno; free(buf); fclose(f); errno = save_errno; return NULL; } fclose(f); buf[res] = 0; if (len_p) *len_p = res; return buf; } /* * Read file line-by-line, call user func on each. */ bool foreach_line(const char *fn, procline_cb proc_line, void *arg) { char *ln = NULL; size_t len = 0; ssize_t res; FILE *f = fopen(fn, "rb"); bool ok = false; if (!f) return false; while (1) { res = getline(&ln, &len, f); if (res < 0) { if (feof(f)) ok = true; break; } if (!proc_line(arg, ln, res)) break; } fclose(f); free(ln); return ok; } /* * Find file size. */ ssize_t file_size(const char *fn) { struct stat st; if (stat(fn, &st) < 0) return -1; return st.st_size; } /* * Map a file into mem. */ #ifdef HAVE_MMAP int map_file(struct MappedFile *m, const char *fname, int rw) { struct stat st; m->fd = open(fname, rw ? O_RDWR : O_RDONLY); if (m->fd < 0) return -1; if (fstat(m->fd, &st) < 0) { close(m->fd); return -1; } m->len = st.st_size; m->ptr = mmap(NULL, m->len, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, m->fd, 0); if (m->ptr == MAP_FAILED) { close(m->fd); return -1; } return 0; } void unmap_file(struct MappedFile *m) { munmap(m->ptr, m->len); close(m->fd); m->ptr = NULL; m->fd = 0; } #endif #ifndef HAVE_GETLINE /* * Read line from FILE with dynamic allocation. */ int getline(char **line_p, size_t *size_p, void *_f) { FILE *f = _f; char *p; int len = 0; if (!*line_p || *size_p < 128) { p = realloc(*line_p, 512); if (!p) return -1; *line_p = p; *size_p = 512; } while (1) { p = fgets(*line_p + len, *size_p - len, f); if (!p) return len ? len : -1; len += strlen(p); if ((*line_p)[len - 1] == '\n') return len; p = realloc(*line_p, *size_p * 2); if (!p) return -1; *line_p = p; *size_p *= 2; } } #endif pgbouncer-1.24.1/lib/usual/heap.c0000644000175000000000000000740014777762223013471 00000000000000/* * Binary Heap. * * Copyright (c) 2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include struct Heap { void **data; unsigned allocated; unsigned used; heap_is_better_f is_better; heap_save_pos_f save_pos; CxMem *cx; }; /* * Low-level operations. */ static unsigned get_parent(unsigned i) { return (i - 1) / 2; } static unsigned get_child(unsigned i, unsigned child_nr) { return 2*i + 1 + child_nr; } static bool is_better(struct Heap *h, unsigned i1, unsigned i2) { return h->is_better(h->data[i1], h->data[i2]); } static void set(struct Heap *h, unsigned i, void *ptr) { h->data[i] = ptr; if (h->save_pos) h->save_pos(ptr, i); } static void swap(struct Heap *h, unsigned i1, unsigned i2) { void *tmp = h->data[i1]; set(h, i1, h->data[i2]); set(h, i2, tmp); } static void bubble_up(struct Heap *h, unsigned i) { unsigned p; while (i > 0) { p = get_parent(i); if (!is_better(h, i, p)) break; swap(h, i, p); i = p; } } static void bubble_down(struct Heap *h, unsigned i) { unsigned c = get_child(i, 0); while (c < h->used) { if (c + 1 < h->used) { if (is_better(h, c + 1, c)) c = c + 1; } if (!is_better(h, c, i)) break; swap(h, i, c); i = c; c = get_child(i, 0); } } static void rebalance(struct Heap *h, unsigned pos) { if (pos == 0) { bubble_down(h, pos); } else if (pos == h->used - 1) { bubble_up(h, pos); } else if (is_better(h, pos, get_parent(pos))) { bubble_up(h, pos); } else { bubble_down(h, pos); } } /* * Actual API. */ struct Heap *heap_create(heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx) { struct Heap *h; h = cx_alloc0(cx, sizeof(*h)); if (!h) return NULL; h->save_pos = save_pos_cb; h->is_better = is_better_cb; h->cx = cx; return h; } void heap_destroy(struct Heap *h) { if (h) { cx_free(h->cx, h->data); cx_free(h->cx, h); } } bool heap_reserve(struct Heap *h, unsigned extra) { void *tmp; unsigned newalloc; if (h->used + extra < h->allocated) return true; newalloc = h->allocated * 2; if (newalloc < 32) newalloc = 32; if (newalloc < h->used + extra) newalloc = h->used + extra; tmp = cx_realloc(h->cx, h->data, newalloc * sizeof(void *)); if (!tmp) return false; h->data = tmp; h->allocated = newalloc; return true; } void *heap_top(struct Heap *h) { return (h->used > 0) ? h->data[0] : NULL; } bool heap_push(struct Heap *h, void *ptr) { unsigned pos; if (h->used >= h->allocated) { if (!heap_reserve(h, 1)) return false; } pos = h->used++; set(h, pos, ptr); bubble_up(h, pos); return true; } void *heap_remove(struct Heap *h, unsigned pos) { unsigned last; void *obj; if (pos >= h->used) return NULL; obj = h->data[pos]; last = --h->used; if (pos < last) { set(h, pos, h->data[last]); rebalance(h, pos); } h->data[last] = NULL; return obj; } void *heap_pop(struct Heap *h) { return heap_remove(h, 0); } unsigned heap_size(struct Heap *h) { return h->used; } void *heap_get_obj(struct Heap *h, unsigned pos) { if (pos < h->used) return h->data[pos]; return NULL; } pgbouncer-1.24.1/lib/usual/mdict.c0000644000175000000000000001430614777762223013657 00000000000000/* * A string to string dictionary. */ #include #include #include #include #include struct MDict { struct CBTree *tree; CxMem *cx; }; struct MDictElem { struct MBuf key; struct MBuf val; }; /* hook for CBTree */ static size_t mdict_getkey(void *ctx, void *obj, const void **dst_p) { struct MDictElem *el = obj; *dst_p = mbuf_data(&el->key); return mbuf_written(&el->key); } static bool mdict_free_obj(void *ctx, void *obj) { struct MDictElem *el = obj; struct MDict *dict = ctx; cx_free(dict->cx, mbuf_data(&el->key)); cx_free(dict->cx, mbuf_data(&el->val)); cx_free(dict->cx, el); return true; } struct MDict *mdict_new(CxMem *cx) { struct MDict *dict; dict = cx_alloc(cx, sizeof(struct MDict)); if (!dict) return NULL; dict->cx = cx; dict->tree = cbtree_create(mdict_getkey, mdict_free_obj, dict, cx); if (!dict->tree) { cx_free(cx, dict); return NULL; } return dict; } void mdict_free(struct MDict *dict) { if (dict) { cbtree_destroy(dict->tree); cx_free(dict->cx, dict); } } const struct MBuf *mdict_get_buf(struct MDict *dict, const char *key, unsigned klen) { struct MDictElem *el = cbtree_lookup(dict->tree, key, klen); if (!el) return NULL; return &el->val; } const char *mdict_get_str(struct MDict *dict, const char *key, unsigned klen) { const struct MBuf *val = mdict_get_buf(dict, key, klen); return val ? mbuf_data(val) : NULL; } bool mdict_put_str(struct MDict *dict, const char *key, unsigned klen, const char *val, unsigned vlen) { char *kptr, *vptr = NULL; struct MDictElem *el; if (val) { vptr = cx_alloc(dict->cx, vlen + 1); if (!vptr) return false; memcpy(vptr, val, vlen); vptr[vlen] = 0; } el = cbtree_lookup(dict->tree, key, klen); if (el) { cx_free(dict->cx, mbuf_data(&el->val)); mbuf_init_fixed_reader(&el->val, vptr, vlen); } else { kptr = cx_alloc(dict->cx, klen + 1); if (!kptr) return false; memcpy(kptr, key, klen); kptr[klen] = 0; el = cx_alloc(dict->cx, sizeof(*el)); if (!el) return false; mbuf_init_fixed_reader(&el->key, kptr, klen); mbuf_init_fixed_reader(&el->val, vptr, vlen); if (!cbtree_insert(dict->tree, el)) return false; } return true; } bool mdict_del_key(struct MDict *dict, const char *key, unsigned klen) { return cbtree_delete(dict->tree, key, klen); } /* * walk over key-val pairs */ struct WalkerCtx { mdict_walker_f cb_func; void *cb_arg; }; static bool walk_helper(void *arg, void *elem) { struct WalkerCtx *ctx = arg; struct MDictElem *el = elem; return ctx->cb_func(ctx->cb_arg, &el->key, &el->val); } bool mdict_walk(struct MDict *dict, mdict_walker_f cb_func, void *cb_arg) { struct WalkerCtx ctx; ctx.cb_func = cb_func; ctx.cb_arg = cb_arg; return cbtree_walk(dict->tree, walk_helper, &ctx); } /* * urldecode */ static int gethex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static void *urldec_str(CxMem *cx, const char **src_p, const char *end, unsigned *len_p) { const char *s; char *d, *dst; int c, len = 0; /* estimate size */ for (s = *src_p; s < end; s++) { if (*s == '%') s += 2; else if (*s == '&' || *s == '=') break; len++; } /* allocate room */ d = dst = cx_alloc(cx, len + 1); if (!dst) return NULL; /* write out */ for (s = *src_p; s < end; ) { if (*s == '%') { int h1, h2; if (s + 3 > end) goto err; h1 = gethex(s[1]); h2 = gethex(s[2]); if (h1 < 0 || h2 < 0) goto err; c = (h1 << 4) | h2; s += 3; *d++ = c; } else if (*s == '+') { *d++ = ' '; s++; } else if (*s == '&' || *s == '=') { break; } else { *d++ = *s++; } } *d = 0; *len_p = d - dst; *src_p = s; return dst; err: cx_free(cx, dst); return NULL; } bool mdict_urldecode(struct MDict *dict, const char *str, unsigned len) { const char *s = str; const char *end = s + len; char *k, *v; unsigned klen, vlen; struct MDictElem *el; while (s < end) { v = NULL; vlen = 0; el = NULL; /* read key */ k = urldec_str(dict->cx, &s, end, &klen); if (!k) goto fail; /* read value */ if (s < end && *s == '=') { s++; v = urldec_str(dict->cx, &s, end, &vlen); if (!v) goto fail; } if (s < end && *s == '&') s++; /* insert value */ el = cbtree_lookup(dict->tree, k, klen); if (el) { cx_free(dict->cx, mbuf_data(&el->val)); mbuf_init_fixed_reader(&el->val, v, vlen); } else { el = cx_alloc(dict->cx, sizeof(*el)); if (!el) goto fail; mbuf_init_fixed_reader(&el->key, k, klen); mbuf_init_fixed_reader(&el->val, v, vlen); if (!cbtree_insert(dict->tree, el)) goto fail; } } return true; fail: if (k) cx_free(dict->cx, k); if (v) cx_free(dict->cx, v); if (el) cx_free(dict->cx, el); return false; } /* * urlencode */ struct UrlEncCtx { struct MBuf *dst; bool is_first; }; static bool urlenc_str(struct MBuf *dst, const struct MBuf *str) { static const char hextbl[] = "0123456789abcdef"; unsigned len = mbuf_written(str); const unsigned char *s = mbuf_data(str); const unsigned char *end = s + len; bool ok; for (; s < end; s++) { if (*s == ' ') { ok = mbuf_write_byte(dst, '+'); } else if ((*s < 128) && isalnum(*s)) { ok = mbuf_write_byte(dst, *s); } else if (*s == '.' || *s == '_') { ok = mbuf_write_byte(dst, *s); } else { ok = mbuf_write_byte(dst, '%'); ok = ok && mbuf_write_byte(dst, hextbl[*s >> 4]); ok = ok && mbuf_write_byte(dst, hextbl[*s & 15]); } if (!ok) return false; } return true; } static bool urlenc_elem(void *arg, const struct MBuf *key, const struct MBuf *val) { struct UrlEncCtx *ctx = arg; bool ok; if (ctx->is_first) { ctx->is_first = false; } else { ok = mbuf_write_byte(ctx->dst, '&'); if (!ok) return false; } ok = urlenc_str(ctx->dst, key); if (!ok) return false; if (mbuf_data(val) != NULL) { ok = mbuf_write_byte(ctx->dst, '='); if (!ok) return false; ok = urlenc_str(ctx->dst, val); if (!ok) return false; } return true; } bool mdict_urlencode(struct MDict *dict, struct MBuf *dst) { struct UrlEncCtx ctx; ctx.is_first = true; ctx.dst = dst; return mdict_walk(dict, urlenc_elem, &ctx); } pgbouncer-1.24.1/lib/usual/safeio.c0000644000175000000000000001217314777762223014025 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Wrappers around regular I/O functions (send/recv/read/write) * that survive EINTR and also can log problems. */ #include #include #include #include #include ssize_t safe_read(int fd, void *buf, size_t len) { ssize_t res; loop: res = read(fd, buf, len); if (res < 0 && errno == EINTR) goto loop; return res; } ssize_t safe_write(int fd, const void *buf, size_t len) { ssize_t res; loop: res = write(fd, buf, len); if (res < 0 && errno == EINTR) goto loop; return res; } ssize_t safe_recv(int fd, void *buf, size_t len, int flags) { ssize_t res; char ebuf[128]; loop: res = recv(fd, buf, len, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_recv(%d, %zu) = %s", fd, len, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_recv(%d, %zu) = %zd", fd, len, res); return res; } ssize_t safe_send(int fd, const void *buf, size_t len, int flags) { ssize_t res; char ebuf[128]; loop: res = send(fd, buf, len, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_send(%d, %zu) = %s", fd, len, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_send(%d, %zu) = %zd", fd, len, res); return res; } int safe_close(int fd) { int res; #ifndef WIN32 /* * POSIX says close() can return EINTR but fd state is "undefined" * later. Seems Linux and BSDs close the fd anyway and EINTR is * simply informative. Thus retry is dangerous. */ res = close(fd); #else /* * Seems on windows it can returns proper EINTR but only when * WSACancelBlockingCall() is called. As we don't do it, * ignore EINTR on win32 too. */ res = closesocket(fd); #endif if (res < 0) { char ebuf[128]; log_warning("safe_close(%d) = %s", fd, strerror_r(errno, ebuf, sizeof(ebuf))); } else if (cf_verbose > 2) { log_noise("safe_close(%d) = %d", fd, res); } /* ignore EINTR */ if (res < 0 && errno == EINTR) return 0; return res; } ssize_t safe_recvmsg(int fd, struct msghdr *msg, int flags) { ssize_t res; char ebuf[128]; loop: res = recvmsg(fd, msg, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_warning("safe_recvmsg(%d, msg, %d) = %s", fd, flags, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_recvmsg(%d, msg, %d) = %zd", fd, flags, res); return res; } ssize_t safe_sendmsg(int fd, const struct msghdr *msg, int flags) { ssize_t res; int msgerr_count = 0; char ebuf[128]; loop: res = sendmsg(fd, msg, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) { log_warning("safe_sendmsg(%d, msg[%d,%d], %d) = %s", fd, (int)msg->msg_iov[0].iov_len, (int)msg->msg_controllen, flags, strerror_r(errno, ebuf, sizeof(ebuf))); /* with ancillary data on blocking socket OSX returns * EMSGSIZE instead of blocking. try to solve it by waiting */ if (errno == EMSGSIZE && msgerr_count < 20) { struct timeval tv = {1, 0}; log_warning("trying to sleep a bit"); select(0, NULL, NULL, NULL, &tv); msgerr_count++; goto loop; } } else if (cf_verbose > 2) log_noise("safe_sendmsg(%d, msg, %d) = %zd", fd, flags, res); return res; } int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) { int res; char buf[128]; char ebuf[128]; loop: res = connect(fd, sa, sa_len); if (res < 0 && errno == EINTR) goto loop; if (res < 0 && (errno != EINPROGRESS || cf_verbose > 2)) log_noise("connect(%d, %s) = %s", fd, sa2str(sa, buf, sizeof(buf)), strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("connect(%d, %s) = %d", fd, sa2str(sa, buf, sizeof(buf)), res); return res; } int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len_p) { int res; char buf[128]; char ebuf[128]; loop: res = accept(fd, sa, sa_len_p); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_accept(%d) = %s", fd, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) { if (sa->sa_family == AF_UNIX) /* sa2str() won't work here since accept() doesn't set sun_path */ log_noise("safe_accept(%d) = %d (unix)", fd, res); else log_noise("safe_accept(%d) = %d (%s)", fd, res, sa2str(sa, buf, sizeof(buf))); } return res; } pgbouncer-1.24.1/lib/usual/utf8.h0000644000175000000000000000375314777762223013456 00000000000000/** @file * Low-level UTF8 handling. */ /* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_UTF8_H_ #define _USUAL_UTF8_H_ #include /** * Parse Unicode codepoint from UTF8 stream. * * On invalid UTF8 sequence returns negative byte value and * inreases src_p by one. * * @param src_p Location of data pointer. Will be incremented in-place. * @param srcend Pointer to end of data. * @return UNOCODE codepoint or negative byte value on error. */ int utf8_get_char(const char **src_p, const char *srcend); /** * Write Unicode codepoint as UTF8 sequence. * * Skips invalid Unicode values without error. * * @param c Unicode codepoint. * @param dst_p Location of dest pointer, will be increased in-place. * @param dstend Pointer to end of buffer. * @return false if not room, true otherwise. */ bool utf8_put_char(unsigned int c, char **dst_p, const char *dstend); /** Return UTF8 seq length based on unicode codepoint */ int utf8_char_size(unsigned int c); /** Return UTF8 seq length based on first byte */ int utf8_seq_size(unsigned char c); /** Return sequence length if all bytes are valid, 0 otherwise. */ int utf8_validate_seq(const char *src, const char *srcend); bool utf8_validate_string(const char *src, const char *end); #endif pgbouncer-1.24.1/lib/usual/pgsocket.c0000644000175000000000000002032714777762223014376 00000000000000/* * Async Postgres connection. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #define MAX_QRY_ARGS 32 /* PgSocket.wait_type */ enum WType { W_NONE = 0, W_SOCK, W_TIME }; typedef void (*libev_cb)(int sock, short flags, void *arg); struct PgSocket { /* libevent state */ struct event ev; /* track wait state */ enum WType wait_type; /* EV_READ / EV_WRITE */ uint8_t wait_event; /* should connect after sleep */ bool reconnect; /* current connection */ PGconn *con; /* user handler */ pgs_handler_f handler_func; void *handler_arg; /* saved connect string */ char *connstr; /* custom base or NULL */ struct event_base *base; /* temp place for resultset */ PGresult *last_result; usec_t connect_time; usec_t lifetime; }; /* report event to user callback */ static void send_event(struct PgSocket *db, enum PgEvent ev) { db->handler_func(db, db->handler_arg, ev, NULL); } /* wait socket event from libevent */ static void wait_event(struct PgSocket *db, short ev, libev_cb fn) { Assert(!db->wait_type); event_set(&db->ev, PQsocket(db->con), ev, fn, db); if (db->base) event_base_set(db->base, &db->ev); if (event_add(&db->ev, NULL) < 0) die("event_add failed: %s", strerror(errno)); db->wait_type = W_SOCK; db->wait_event = ev; } /* wait timeout from libevent */ static void timeout_cb(evutil_socket_t sock, short flags, void *arg) { struct PgSocket *db = arg; db->wait_type = W_NONE; if (db->reconnect) { db->reconnect = false; pgs_connect(db); } else { send_event(db, PGS_TIMEOUT); } } /* some error happened */ static void conn_error(struct PgSocket *db, enum PgEvent ev, const char *desc) { log_error("connection error: %s", desc); log_error("libpq: %s", PQerrorMessage(db->con)); send_event(db, ev); } /* report previously stored result */ static void report_last_result(struct PgSocket *db) { PGresult *res = db->last_result; if (!res) return; db->last_result = NULL; switch (PQresultStatus(res)) { default: log_error("%s: %s", PQdb(db->con), PQresultErrorMessage(res)); /* fallthrough */ case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: case PGRES_COPY_OUT: case PGRES_COPY_IN: db->handler_func(db, db->handler_arg, PGS_RESULT_OK, res); } PQclear(res); } /* * Called when select() told that conn is avail for reading. * * It should call postgres handlers and then change state if needed. * * Because the callback may want to close the connection when processing * last resultset, the PGresult handover is delayed one step. */ static void result_cb(evutil_socket_t sock, short flags, void *arg) { struct PgSocket *db = arg; PGresult *res; db->wait_type = W_NONE; if (!PQconsumeInput(db->con)) { conn_error(db, PGS_RESULT_BAD, "PQconsumeInput"); return; } /* loop until PQgetResult returns NULL */ while (db->con) { /* incomplete result? */ if (PQisBusy(db->con)) { wait_event(db, EV_READ, result_cb); return; } /* next result */ res = PQgetResult(db->con); if (!res) break; report_last_result(db); db->last_result = res; } report_last_result(db); } static void flush(struct PgSocket *db); static void send_cb(evutil_socket_t sock, short flags, void *arg) { struct PgSocket *db = arg; db->wait_type = W_NONE; flush(db); } /* handle connect states */ static void connect_cb(evutil_socket_t sock, short flags, void *arg) { struct PgSocket *db = arg; PostgresPollingStatusType poll_res; db->wait_type = W_NONE; poll_res = PQconnectPoll(db->con); switch (poll_res) { case PGRES_POLLING_WRITING: wait_event(db, EV_WRITE, connect_cb); break; case PGRES_POLLING_READING: wait_event(db, EV_READ, connect_cb); break; case PGRES_POLLING_OK: db->connect_time = get_time_usec(); send_event(db, PGS_CONNECT_OK); break; default: conn_error(db, PGS_CONNECT_FAILED, "PQconnectPoll"); } } /* send query to server */ static void flush(struct PgSocket *db) { int res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, PGS_RESULT_BAD, "PQflush"); } /* override default notice receiver */ static void custom_notice_receiver(void *arg, const PGresult *res) { /* do nothing */ } /* * Public API */ struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *handler_arg) { struct PgSocket *db; db = calloc(1, sizeof(*db)); if (!db) return NULL; db->handler_func = fn; db->handler_arg = handler_arg; db->connstr = strdup(connstr); if (!db->connstr) { pgs_free(db); return NULL; } return db; } void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base) { pgs->base = base; } void pgs_set_lifetime(struct PgSocket *pgs, double lifetime) { pgs->lifetime = USEC * lifetime; } void pgs_connect(struct PgSocket *db) { if (db->con) pgs_disconnect(db); db->con = PQconnectStart(db->connstr); if (db->con == NULL) { conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart"); return; } if (PQstatus(db->con) == CONNECTION_BAD) { conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart"); return; } PQsetNoticeReceiver(db->con, custom_notice_receiver, db); wait_event(db, EV_WRITE, connect_cb); } void pgs_disconnect(struct PgSocket *db) { if (db->wait_type) { event_del(&db->ev); db->wait_type = W_NONE; db->reconnect = false; } if (db->con) { PQfinish(db->con); db->con = NULL; } if (db->last_result) { PQclear(db->last_result); db->last_result = NULL; } } void pgs_free(struct PgSocket *db) { if (db) { pgs_disconnect(db); free(db->connstr); free(db); } } void pgs_sleep(struct PgSocket *db, double timeout) { struct timeval tv; Assert(!db->wait_type); if (db->con && db->lifetime) { usec_t now = get_time_usec(); if (db->connect_time + db->lifetime < now) { pgs_disconnect(db); db->reconnect = true; } } tv.tv_sec = timeout; tv.tv_usec = (timeout - tv.tv_sec) * USEC; evtimer_set(&db->ev, timeout_cb, db); if (db->base) event_base_set(db->base, &db->ev); if (evtimer_add(&db->ev, &tv) < 0) die("evtimer_add failed: %s", strerror(errno)); db->wait_type = W_TIME; } void pgs_reconnect(struct PgSocket *db, double timeout) { pgs_disconnect(db); pgs_sleep(db, timeout); db->reconnect = true; } void pgs_send_query_simple(struct PgSocket *db, const char *q) { int res; log_noise("%s", q); res = PQsendQuery(db->con, q); if (!res) { conn_error(db, PGS_RESULT_BAD, "PQsendQuery"); return; } flush(db); } void pgs_send_query_params(struct PgSocket *db, const char *q, int cnt, ...) { int i; va_list ap; const char * args[MAX_QRY_ARGS]; if (cnt < 0 || cnt > MAX_QRY_ARGS) { log_warning("bad query arg cnt"); send_event(db, PGS_RESULT_BAD); return; } va_start(ap, cnt); for (i = 0; i < cnt; i++) args[i] = va_arg(ap, char *); va_end(ap); pgs_send_query_params_list(db, q, cnt, args); } void pgs_send_query_params_list(struct PgSocket *db, const char *q, int cnt, const char *args[]) { int res; log_noise("%s", q); res = PQsendQueryParams(db->con, q, cnt, NULL, args, NULL, NULL, 0); if (!res) { conn_error(db, PGS_RESULT_BAD, "PQsendQueryParams"); return; } flush(db); } int pgs_connection_valid(struct PgSocket *db) { return (db->con != NULL); } PGconn *pgs_get_connection(struct PgSocket *db) { return db->con; } bool pgs_waiting_for_reply(struct PgSocket *db) { if (!db->con) return false; if (PQstatus(db->con) != CONNECTION_OK) return false; return (db->wait_type == W_SOCK) && (db->wait_event == EV_READ); } pgbouncer-1.24.1/lib/usual/hashing/0000755000000000000000000000000014777762567014106 500000000000000pgbouncer-1.24.1/lib/usual/hashing/siphash.h0000644000175000000000000000207414777762223015643 00000000000000/* * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SipHash-2-4 */ #ifndef _USUAL_HASHING_SIPHASH_H_ #define _USUAL_HASHING_SIPHASH_H_ #include /** Calculate SipHash-2-4 checksum */ uint64_t siphash24(const void *data, size_t len, uint64_t k0, uint64_t k1); uint64_t siphash24_secure(const void *data, size_t len); #endif pgbouncer-1.24.1/lib/usual/hashing/lookup3.c0000644000175000000000000000270514777762223015574 00000000000000/* * The contents of this file are public domain. * * Based on: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */ /* * Compact version of Bob Jenkins' lookup3.c hash. */ #include #include #define rot(x, k) (((x)<<(k)) | ((x)>>(32-(k)))) #define mix(a, b, c) do { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } while (0) #define final(a, b, c) do { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c, 4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } while (0) /* variable length copy of ~6 bytes, avoid call to libc */ static inline void simple_memcpy(void *dst_, const void *src_, size_t len) { const uint8_t *src = src_; uint8_t *dst = dst_; while (len--) *dst++ = *src++; } uint64_t hash_lookup3(const void *data, size_t len) { uint32_t a, b, c; uint32_t buf[3]; const uint8_t *p = data; a = b = c = 0xdeadbeef + len; if (len == 0) goto done; while (len > 12) { memcpy(buf, p, 12); a += buf[0]; b += buf[1]; c += buf[2]; mix(a, b, c); p += 12; len -= 12; } buf[0] = buf[1] = buf[2] = 0; simple_memcpy(buf, p, len); a += buf[0]; b += buf[1]; c += buf[2]; final(a, b, c); done: return ((uint64_t)b << 32) | c; } pgbouncer-1.24.1/lib/usual/hashing/siphash.c0000644000175000000000000000517114777762223015637 00000000000000/* * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define SIP_ROUND1 \ v0 += v1; v1 = rol64(v1, 13); v1 ^= v0; v0 = rol64(v0, 32); \ v2 += v3; v3 = rol64(v3, 16); v3 ^= v2; \ v0 += v3; v3 = rol64(v3, 21); v3 ^= v0; \ v2 += v1; v1 = rol64(v1, 17); v1 ^= v2; v2 = rol64(v2, 32) #define SIP_ROUND2 SIP_ROUND1; SIP_ROUND1 #define SIP_ROUND4 SIP_ROUND2; SIP_ROUND2 #define SIP_ROUNDS(n) SIP_ROUND ## n #define sip_compress(n) \ do { \ v3 ^= m; \ SIP_ROUNDS(n); \ v0 ^= m; \ } while (0) #define sip_finalize(n) \ do { \ v2 ^= 0xff; \ SIP_ROUNDS(n); \ } while (0) uint64_t siphash24(const void *data, size_t len, uint64_t k0, uint64_t k1) { const uint8_t *s = data; const uint8_t *end = s + len - (len % 8); uint64_t v0 = k0 ^ UINT64_C(0x736f6d6570736575); uint64_t v1 = k1 ^ UINT64_C(0x646f72616e646f6d); uint64_t v2 = k0 ^ UINT64_C(0x6c7967656e657261); uint64_t v3 = k1 ^ UINT64_C(0x7465646279746573); uint64_t m; for (; s < end; s += 8) { m = le64dec(s); sip_compress(2); } m = (uint64_t)len << 56; switch (len & 7) { case 7: m |= (uint64_t)s[6] << 48; /* fallthrough */ case 6: m |= (uint64_t)s[5] << 40; /* fallthrough */ case 5: m |= (uint64_t)s[4] << 32; /* fallthrough */ case 4: m |= (uint64_t)s[3] << 24; /* fallthrough */ case 3: m |= (uint64_t)s[2] << 16; /* fallthrough */ case 2: m |= (uint64_t)s[1] << 8; /* fallthrough */ case 1: m |= (uint64_t)s[0]; break; case 0: break; } sip_compress(2); sip_finalize(4); return (v0 ^ v1 ^ v2 ^ v3); } uint64_t siphash24_secure(const void *data, size_t len) { static bool initialized; static uint64_t k0, k1; if (!initialized) { k0 = ((uint64_t)csrandom() << 32) | csrandom(); k1 = ((uint64_t)csrandom() << 32) | csrandom(); initialized = true; } return siphash24(data, len, k0, k1); } pgbouncer-1.24.1/lib/usual/hashing/memhash.c0000644000175000000000000000303514777762223015617 00000000000000/* * memhash.h - Randomized in-memory hashing. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include uint32_t memhash_seed(const void *data, size_t len, uint32_t seed) { if (sizeof(void *) == 8 || sizeof(long) == 8) { uint64_t hash[2]; hash[0] = seed; hash[1] = 0; spookyhash(data, len, &hash[0], &hash[1]); return hash[0]; } else { return xxhash(data, len, seed); } } uint32_t memhash(const void *data, size_t len) { static bool initialized; static uint32_t rand_seed; if (!initialized) { initialized = true; rand_seed = csrandom(); } return memhash_seed(data, len, rand_seed); } uint32_t memhash_string(const char *s) { return memhash(s, strlen(s)); } pgbouncer-1.24.1/lib/usual/hashing/crc32.c0000644000175000000000000001021014777762223015102 00000000000000/* * CRC32. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include static const uint32_t crc_tab[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; static inline uint32_t crc32(uint32_t prev, uint8_t c) { return crc_tab[(prev ^ c) & 0xFF] ^ (prev >> 8); } uint32_t calc_crc32(const void *data, size_t len, uint32_t init) { const uint8_t *p = data; uint32_t crc = init ^ (~0); while (len--) crc = crc32(crc, *p++); return crc ^ (~0); } pgbouncer-1.24.1/lib/usual/hashing/memhash.h0000644000175000000000000000255014777762223015625 00000000000000/* * memhash.h - Randomized in-memory hashing. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Randomized in-memory hashing. * * Functions here use randomized seed and pick fastest hash * for current CPU. */ #ifndef _USUAL_HASHING_MEMHASH_H_ #define _USUAL_HASHING_MEMHASH_H_ #include /** * Hash data. */ uint32_t memhash(const void *data, size_t len); /** * Hash zero-terminated string. */ uint32_t memhash_string(const char *s); /** * Hash with given seed. Result is not randomized, * but still may vary on different CPU-s. */ uint32_t memhash_seed(const void *data, size_t len, uint32_t seed); #endif pgbouncer-1.24.1/lib/usual/hashing/spooky.h0000644000175000000000000000045014777762223015524 00000000000000/** * @file * * Jenkins SpookyHash V2 - fast hash for 64-bit CPUs. */ #ifndef _USUAL_HASHING_SPOOKY_H_ #define _USUAL_HASHING_SPOOKY_H_ #include /** * Run SpookyHash on data. */ void spookyhash(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2); #endif pgbouncer-1.24.1/lib/usual/hashing/lookup3.h0000644000175000000000000000040614777762223015575 00000000000000/** * @file * * Jenkins' lookup3 non-cryptographic hash. */ #ifndef _USUAL_HASHING_LOOKUP3_H_ #define _USUAL_HASHING_LOOKUP3_H_ #include /** * Calculate 64-bit hash over data */ uint64_t hash_lookup3(const void *data, size_t len); #endif pgbouncer-1.24.1/lib/usual/hashing/spooky.c0000644000175000000000000002537414777762223015533 00000000000000/* * SpookyHash: a 128-bit noncryptographic hash function * By Bob Jenkins, public domain * Oct 31 2010: alpha, framework + SpookyHash::Mix appears right * Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right * Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas * Feb 2 2012: production, same bits as beta * Feb 5 2012: adjusted definitions of uint* to be more portable * Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. * August 5 2012: SpookyV2 (different results) * * Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. * All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. * * This was developed for and tested on 64-bit x86-compatible processors. * It assumes the processor is little-endian. There is a macro * controlling whether unaligned reads are allowed (by default they are). * This should be an equally good hash on big-endian machines, but it will * compute different results on them than on little-endian machines. * * Google's CityHash has similar specs to SpookyHash, and CityHash is faster * on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders * of magnitude slower. CRCs are two or more times slower, but unlike * SpookyHash, they have nice math for combining the CRCs of pieces to form * the CRCs of wholes. There are also cryptographic hashes, but those are even * slower than MD5. */ #include #include #include #ifdef WORDS_UNALIGNED_ACCESS_OK #define ALLOW_UNALIGNED_READS 1 #else #define ALLOW_UNALIGNED_READS 0 #endif /* number of uint64_t's in internal state */ #define sc_numVars 12 /* size of the internal state */ #define sc_blockSize (sc_numVars*8) /* size of buffer of unhashed data, in bytes */ #define sc_bufSize (2*sc_blockSize) /* * sc_const: a constant which: * - is not zero * - is odd * - is a not-very-regular mix of 1's and 0's * - does not need any other special mathematical properties */ static const uint64_t sc_const = 0xdeadbeefdeadbeefLL; /* * This is used if the input is 96 bytes long or longer. * * The internal state is fully overwritten every 96 bytes. * Every input bit appears to cause at least 128 bits of entropy * before 96 other bytes are combined, when run forward or backward * For every input bit, * Two inputs differing in just that input bit * Where "differ" means xor or subtraction * And the base value is random * When run forward or backwards one Mix * I tried 3 pairs of each; they all differed by at least 212 bits. */ #define Mix(data, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11) \ do { \ s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = rol64(s0,11); s11 += s1; \ s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = rol64(s1,32); s0 += s2; \ s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = rol64(s2,43); s1 += s3; \ s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = rol64(s3,31); s2 += s4; \ s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = rol64(s4,17); s3 += s5; \ s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = rol64(s5,28); s4 += s6; \ s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = rol64(s6,39); s5 += s7; \ s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = rol64(s7,57); s6 += s8; \ s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = rol64(s8,55); s7 += s9; \ s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = rol64(s9,54); s8 += s10; \ s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = rol64(s10,22); s9 += s11; \ s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = rol64(s11,46); s10 += s0; \ } while (0) /* * Mix all 12 inputs together so that h0, h1 are a hash of them all. * * For two inputs differing in just the input bits * Where "differ" means xor or subtraction * And the base value is random, or a counting value starting at that bit * The final result will have each bit of h0, h1 flip * For every input bit, * with probability 50 +- .3% * For every pair of input bits, * with probability 50 +- 3% * * This does not rely on the last Mix() call having already mixed some. * Two iterations was almost good enough for a 64-bit result, but a * 128-bit result is reported, so End() does three iterations. */ #define EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \ do { \ h11+= h1; h2 ^= h11; h1 = rol64(h1,44); \ h0 += h2; h3 ^= h0; h2 = rol64(h2,15); \ h1 += h3; h4 ^= h1; h3 = rol64(h3,34); \ h2 += h4; h5 ^= h2; h4 = rol64(h4,21); \ h3 += h5; h6 ^= h3; h5 = rol64(h5,38); \ h4 += h6; h7 ^= h4; h6 = rol64(h6,33); \ h5 += h7; h8 ^= h5; h7 = rol64(h7,10); \ h6 += h8; h9 ^= h6; h8 = rol64(h8,13); \ h7 += h9; h10^= h7; h9 = rol64(h9,38); \ h8 += h10; h11^= h8; h10= rol64(h10,53); \ h9 += h11; h0 ^= h9; h11= rol64(h11,42); \ h10+= h0; h1 ^= h10; h0 = rol64(h0,54); \ } while (0) #define End(data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \ do { \ h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3]; \ h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7]; \ h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11]; \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ } while (0) /* * The goal is for each bit of the input to expand into 128 bits of * apparent entropy before it is fully overwritten. * n trials both set and cleared at least m bits of h0 h1 h2 h3 * n: 2 m: 29 * n: 3 m: 46 * n: 4 m: 57 * n: 5 m: 107 * n: 6 m: 146 * n: 7 m: 152 * when run forwards or backwards * for all 1-bit and 2-bit diffs * with diffs defined by either xor or subtraction * with a base of all zeros plus a counter, or plus another bit, or random */ #define ShortMix(h0, h1, h2, h3) \ do { \ h2 = rol64(h2,50); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,52); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,30); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,41); h1 += h2; h3 ^= h1; \ h2 = rol64(h2,54); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,48); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,38); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,37); h1 += h2; h3 ^= h1; \ h2 = rol64(h2,62); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,34); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,5); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,36); h1 += h2; h3 ^= h1; \ } while (0) /* * Mix all 4 inputs together so that h0, h1 are a hash of them all. * * For two inputs differing in just the input bits * Where "differ" means xor or subtraction * And the base value is random, or a counting value starting at that bit * The final result will have each bit of h0, h1 flip * For every input bit, * with probability 50 +- .3% (it is probably better than that) * For every pair of input bits, * with probability 50 +- .75% (the worst case is approximately that) */ #define ShortEnd(h0, h1, h2, h3) \ do { \ h3 ^= h2; h2 = rol64(h2,15); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,52); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,26); h1 += h0; \ h2 ^= h1; h1 = rol64(h1,51); h2 += h1; \ h3 ^= h2; h2 = rol64(h2,28); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,9); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,47); h1 += h0; \ h2 ^= h1; h1 = rol64(h1,54); h2 += h1; \ h3 ^= h2; h2 = rol64(h2,32); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,25); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,63); h1 += h0; \ } while (0) /* * Short is used for messages under 192 bytes in length * Short has a low startup cost, the normal mode is good for long * keys, the cost crossover is at about 192 bytes. The two modes were * held to the same quality bar. */ static void Short(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2) { uint64_t buf[2*sc_numVars]; union { const uint8_t *p8; uint32_t *p32; uint64_t *p64; size_t i; } u; size_t remainder = length%32; uint64_t a=*hash1; uint64_t b=*hash2; uint64_t c=sc_const; uint64_t d=sc_const; u.p8 = (const uint8_t *)message; if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) { memcpy(buf, message, length); u.p64 = buf; } if (length > 15) { const uint64_t *end = u.p64 + (length/32)*4; /* handle all complete sets of 32 bytes */ for (; u.p64 < end; u.p64 += 4) { c += u.p64[0]; d += u.p64[1]; ShortMix(a,b,c,d); a += u.p64[2]; b += u.p64[3]; } /* Handle the case of 16+ remaining bytes. */ if (remainder >= 16) { c += u.p64[0]; d += u.p64[1]; ShortMix(a,b,c,d); u.p64 += 2; remainder -= 16; } } /* Handle the last 0..15 bytes, and its length */ d += ((uint64_t)length) << 56; switch (remainder) { case 15: d += ((uint64_t)u.p8[14]) << 48; /* fallthrough */ case 14: d += ((uint64_t)u.p8[13]) << 40; /* fallthrough */ case 13: d += ((uint64_t)u.p8[12]) << 32; /* fallthrough */ case 12: d += u.p32[2]; c += u.p64[0]; break; case 11: d += ((uint64_t)u.p8[10]) << 16; /* fallthrough */ case 10: d += ((uint64_t)u.p8[9]) << 8; /* fallthrough */ case 9: d += (uint64_t)u.p8[8]; /* fallthrough */ case 8: c += u.p64[0]; break; case 7: c += ((uint64_t)u.p8[6]) << 48; /* fallthrough */ case 6: c += ((uint64_t)u.p8[5]) << 40; /* fallthrough */ case 5: c += ((uint64_t)u.p8[4]) << 32; /* fallthrough */ case 4: c += u.p32[0]; break; case 3: c += ((uint64_t)u.p8[2]) << 16; /* fallthrough */ case 2: c += ((uint64_t)u.p8[1]) << 8; /* fallthrough */ case 1: c += (uint64_t)u.p8[0]; break; case 0: c += sc_const; d += sc_const; } ShortEnd(a,b,c,d); *hash1 = a; *hash2 = b; } /* do the whole hash in one call */ void spookyhash(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2) { uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; uint64_t buf[sc_numVars]; uint64_t *end; union { const uint8_t *p8; uint64_t *p64; } u; size_t remainder; if (length < sc_bufSize) { Short(message, length, hash1, hash2); return; } h0 = h3 = h6 = h9 = *hash1; h1 = h4 = h7 = h10 = *hash2; h2 = h5 = h8 = h11 = sc_const; u.p8 = (const uint8_t *)message; end = u.p64 + (length/sc_blockSize)*sc_numVars; /* handle all whole sc_blockSize blocks of bytes */ if (ALLOW_UNALIGNED_READS || (((uintptr_t)u.p8 & 0x7) == 0)) { while (u.p64 < end) { Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); u.p64 += sc_numVars; } } else { while (u.p64 < end) { memcpy(buf, u.p64, sc_blockSize); Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); u.p64 += sc_numVars; } } /* handle the last partial block of sc_blockSize bytes */ remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); memcpy(buf, end, remainder); memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); ((uint8_t *)buf)[sc_blockSize-1] = remainder; /* do some final mixing */ End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); *hash1 = h0; *hash2 = h1; } pgbouncer-1.24.1/lib/usual/hashing/xxhash.c0000644000175000000000000000547214777762223015507 00000000000000/* xxHash - Fast Hash algorithm Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ #include #include #include #define PRIME32_1 2654435761U #define PRIME32_2 2246822519U #define PRIME32_3 3266489917U #define PRIME32_4 668265263U #define PRIME32_5 374761393U #define read32(p) h32dec(p) uint32_t xxhash(const void *input, size_t len, uint32_t seed) { const uint8_t *p = (const uint8_t *)input; const uint8_t * const bEnd = p + len; uint32_t h32; if (len >= 16) { const uint8_t * const limit = bEnd - 16; uint32_t v1, v2, v3, v4; v1 = seed + PRIME32_1 + PRIME32_2; v2 = seed + PRIME32_2; v3 = seed + 0; v4 = seed - PRIME32_1; do { v1 += read32(p) * PRIME32_2; v1 = rol32(v1, 13); v1 *= PRIME32_1; p += 4; v2 += read32(p) * PRIME32_2; v2 = rol32(v2, 13); v2 *= PRIME32_1; p += 4; v3 += read32(p) * PRIME32_2; v3 = rol32(v3, 13); v3 *= PRIME32_1; p += 4; v4 += read32(p) * PRIME32_2; v4 = rol32(v4, 13); v4 *= PRIME32_1; p += 4; } while (p <= limit); h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18); } else { h32 = seed + PRIME32_5; } h32 += len; while (p <= bEnd - 4) { h32 += read32(p) * PRIME32_3; h32 = rol32(h32, 17) * PRIME32_4 ; p += 4; } while (p < bEnd) { h32 += (*p) * PRIME32_5; h32 = rol32(h32, 11) * PRIME32_1 ; p++; } h32 ^= h32 >> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } pgbouncer-1.24.1/lib/usual/hashing/xxhash.h0000644000175000000000000000355214777762223015511 00000000000000/* xxHash - Fast Hash algorithm Header File Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ /** * @file * * xxHash - fast hash for 32-bit cpu's. */ #ifndef _USUAL_HASHING_XXHASH_H_ #define _USUAL_HASHING_XXHASH_H_ #include /** * Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". */ uint32_t xxhash(const void *input, size_t len, uint32_t seed); #endif pgbouncer-1.24.1/lib/usual/hashing/crc32.h0000644000175000000000000000176214777762223015123 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * CRC32 checksum. */ #ifndef _USUAL_HASHING_CRC32_H_ #define _USUAL_HASHING_CRC32_H_ #include /** Calculate CRC32 checksum */ uint32_t calc_crc32(const void *data, size_t len, uint32_t init); #endif pgbouncer-1.24.1/lib/usual/base.h0000644000175000000000000002054314777762223013476 00000000000000/** @file * Basic C environment. */ /* * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_BASE_H_ #define _USUAL_BASE_H_ #ifdef USUAL_TEST_CONFIG #include "test_config.h" #elif defined(_MSC_VER) #include #else #include #endif /* solaris is broken otherwise */ #if defined(__sun) #define _XPG4_2 #define __EXTENSIONS__ #endif /* C11 */ #ifndef __STDC_WANT_LIB_EXT1__ #define __STDC_WANT_LIB_EXT1__ 1 #endif #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #include #ifdef HAVE_INTTYPES_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDBOOL_H #include #else /* we really want bool type */ typedef enum { true=1, false=0 } bool; #endif #ifdef WIN32 #include #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) #else #define DLLEXPORT #define DLLIMPORT #endif /** give offset of a field inside struct */ #ifndef offsetof #define offsetof(type, field) ((unsigned long)&(((type *)0)->field)) #endif /** given pointer to field inside struct, return pointer to struct */ #ifndef container_of #define container_of(ptr, type, field) ((type *)((char *)(ptr) - offsetof(type, field))) #endif /** get alignment requirement for a type */ #ifndef alignof #define alignof(type) offsetof(struct { char c; type t; }, t) #endif /** power-of-2 alignment */ #ifndef CUSTOM_ALIGN #define CUSTOM_ALIGN(x, a) (((uintptr_t)(x) + (uintptr_t)(a) - 1) & ~((uintptr_t)(a) - 1)) #endif /** preferred alignment */ #ifndef ALIGN #define ALIGN(x) CUSTOM_ALIGN(x, sizeof(long)) #endif /** number of elements in array */ #define ARRAY_NELEM(a) (sizeof(a) / sizeof((a)[0])) /** * Compat helper to specify array with unknown length. * * Usage: * * @code * char flex_string[FLEX_ARRAY]; * @endcode */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define FLEX_ARRAY #elif defined(__GNUC__) && (__GNUC__ >= 3) #define FLEX_ARRAY #else #define FLEX_ARRAY 1 #endif /** Make string token from C expression */ #define STR(x) _STR_(x) #define _STR_(x) #x /** Make single C token from 2 separate tokens */ #define CONCAT(a, b) _CONCAT_(a, b) #define _CONCAT_(a, b) a ## b /** Make single C token from 3 separate tokens */ #define CONCAT3(a, b, c) _CONCAT3_(a, b, c) #define _CONCAT3_(a, b, c) a ## b ## c /** Make single C token from 4 separate tokens */ #define CONCAT4(a, b, c, d) _CONCAT4_(a, b, c, d) #define _CONCAT4_(a, b, c, d) a ## b ## c ## d /** Pre-processor macro for current function name. */ #ifndef HAVE_FUNCNAME__FUNC #define __func__ __FUNCTION__ #endif /** * @name Compiler checks, mainly for internal usage. * * @{ */ /** Pre-processor macro to check if compiler is GCC with high enough version */ #if defined(__GNUC__) #define _COMPILER_GNUC(maj,min) ((__GNUC__ > (maj)) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min))) #else #define _COMPILER_GNUC(maj,min) (0) #endif /** Pre-processor macro to check if compiler is CLANG with high enough version */ #if defined(__clang__) #define _COMPILER_CLANG(maj,min) ((__clang_major__ > (maj)) || (__clang_major__ == (maj) && __clang_minor__ >= (min))) #else #define _COMPILER_CLANG(maj,min) (0) #endif /** Pre-processor macro to check if compiler is Visual C with high enough version */ #if defined(_MSC_VER) #define _COMPILER_MSC(ver) (_MSC_VER >= (ver)) #else #define _COMPILER_MSC(ver) (0) #endif /** Pre-processor macro to check if compiler is Intel CC with high enough version */ #if defined(__INTEL_COMPILER) #define _COMPILER_ICC(ver) (__INTEL_COMPILER >= (ver)) #else #define _COMPILER_ICC(ver) (0) #endif /* * clang compat * * They work only if the compiler is clang, * return 0 otherwise. */ #ifndef __has_builtin #define __has_builtin(x) (0) #endif #ifndef __has_feature #define __has_feature(x) (0) #endif #ifndef __has_extension #define __has_extension(x) __has_feature(x) #endif #ifndef __has_attribute #define __has_attribute(x) (0) #endif /* * clang macros that cannot be defined here: * __is_identifier * __has_include * __has_include_next * __has_warning */ /** * @} * * @name Function/struct attributes. * * @{ */ /** Disable padding for structure */ #ifndef _MSC_VER #define _PACKED __attribute__((packed)) #endif /* * make compiler do something useful */ /** Show warning if function result is not used */ #if _COMPILER_GNUC(4,0) || __has_attribute(warn_unused_result) #define _MUSTCHECK __attribute__((warn_unused_result)) #else #define _MUSTCHECK #endif /** Show warning if used */ #if _COMPILER_GNUC(4,0) || __has_attribute(deprecated) #define _DEPRECATED __attribute__((deprecated)) #else #define _DEPRECATED #endif /** Check printf-style format and arg sanity */ #if _COMPILER_GNUC(4,0) || __has_attribute(printf) #ifdef __MINGW32__ #define _PRINTF(fmtpos, argpos) __attribute__((format(__MINGW_PRINTF_FORMAT, fmtpos, argpos))) #else #define _PRINTF(fmtpos, argpos) __attribute__((format(printf, fmtpos, argpos))) #endif #else #define _PRINTF(fmtpos, argpos) #endif /** Function returns new pointer */ #if _COMPILER_GNUC(4,0) || __has_attribute(malloc) #define _MALLOC __attribute__((malloc)) #else #define _MALLOC #endif /** Disable 'unused' warning for function/argument. */ #if _COMPILER_GNUC(4,0) || __has_attribute(unused) #define _UNUSED __attribute__((unused)) #else #define _UNUSED #endif /** Do not inline function. */ #if _COMPILER_GNUC(4,0) || __has_attribute(noinline) #define _NOINLINE __attribute__((noinline)) #else #define _NOINLINE #endif /** Indicates that function never returns */ #if _COMPILER_GNUC(4,0) || __has_attribute(noreturn) #define _NORETURN __attribute__((noreturn)) #else #define _NORETURN #endif /** Hint for compiler that expression (x) is likely to be true */ #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_expect) #define likely(x) __builtin_expect(!!(x), 1) #else #define likely(x) (x) #endif /** Hint for compiler that expression (x) is likely to be false */ #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_expect) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define unlikely(x) (x) #endif /* @} */ /** * Compile-time assert. * * Expression must be evaluatable at compile time. * If false, stop compilation with message. * * It can be used in either global or function scope. */ #ifndef static_assert #if _COMPILER_GNUC(4,6) || _COMPILER_MSC(1600) || __has_feature(c_static_assert) /* Version for new compilers */ #define static_assert(expr, msg) _Static_assert(expr, msg) #else /* Version for old compilers */ #define static_assert(expr, msg) enum { CONCAT4(static_assert_failure_, __LINE__, _, __COUNTER__) = 1/(1 != (1 + (expr))) } #endif #endif /* !static_assert */ /** assert() that uses module */ #ifndef Assert #ifdef CASSERT void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *s, ...) _PRINTF(6, 7); #define Assert(e) \ do { \ if (unlikely(!(e))) { \ log_fatal(__FILE__, __LINE__, __func__, false, NULL, \ "Assert(%s) failed", #e); \ abort(); \ } \ } while (0) #else #define Assert(e) #endif #endif /** Zeroing malloc */ _MUSTCHECK static inline void *zmalloc(size_t len) { return calloc(1, len); } #ifndef HAVE_POSIX_MEMALIGN #define posix_memalign(a,b,c) usual_memalign(a,b,c) /** Compat: posix_memalign() */ int posix_memalign(void **ptr_p, size_t align, size_t len); #endif #ifndef HAVE_REALLOCARRAY #define reallocarray(a,b,c) usual_reallocarray(a,b,c) /** * Same as realloc(), but safely calculates total size. */ void *reallocarray(void *p, size_t count, size_t size); #endif #endif pgbouncer-1.24.1/lib/usual/socket.h0000644000175000000000000000754514777762223014063 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Socket compat, few utils. * * Socket headers included: * - win32: * - win32: * - * - * - * - * - * - * - */ #ifndef _USUAL_SOCKET_H_ #define _USUAL_SOCKET_H_ #include #ifdef WIN32 #include #include #include #endif #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_SYS_UN_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifndef INADDR_NONE /** Compat: Some systems (Solaris) does not define INADDR_NONE */ #define INADDR_NONE ((unsigned long) -1) #endif /** * Usual socket setup. * * - Disallow SIGPIPE * - Set close-on-exec flag * - Call \ref socket_set_nonblocking() with given flag */ bool socket_setup(int sock, bool non_block); /** * Flip sockets non-blocking flag */ bool socket_set_nonblocking(int sock, bool non_block); /** * Set sockets keepalive flags. * * @param fd TCP socket * @param onoff Whether to set keepalive on or off. * @param keepidle How long the socket must be idle before keepalive packets are sent * @param keepintvl How big period between consecutive keepalive packets. * @param keepcnt How many keepalive packets to send before considering socket dead. */ bool socket_set_keepalive(int fd, int onoff, int keepidle, int keepintvl, int keepcnt); /** * Convert struct sockaddr to stirng. * * Supports: ipv4, ipv5, unix sockets. */ const char *sa2str(const struct sockaddr *sa, char *buf, size_t buflen); #ifndef HAVE_INET_NTOP #undef inet_ntop #define inet_ntop(a,b,c,d) usual_inet_ntop(a,b,c,d) /** Compat: inet_ntop() */ const char *inet_ntop(int af, const void *src, char *dst, int cnt); #endif #ifndef HAVE_INET_PTON #undef inet_pton #define inet_pton(a,b,c) usual_inet_pton(a,b,c) /** Compat: inet_pton() */ int inet_pton(int af, const char *src, void *dst); #endif #ifndef HAVE_GETPEEREID #define getpeereid(a,b,c) compat_getpeereid(a,b,c) /** Get user id of UNIX socket peer */ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p); #endif #define getpeercreds(a,b,c,d) usual_getpeercreds(a,b,c,d) /** Get info of UNIX socket peer */ int getpeercreds(int fd, uid_t *uid_p, gid_t *gid_p, pid_t *pid_p); #if !defined(HAVE_POLL) #define POLLIN (1 << 0) #define POLLOUT (1 << 1) #define POLLHUP (1 << 2) #define POLLPRI (1 << 3) #define POLLNVAL (1 << 4) #define POLLERR (1 << 5) #define poll(a,b,c) compat_poll(a,b,c) struct pollfd { int fd; short events; short revents; }; typedef unsigned long nfds_t; /** Compat: select-based poll() */ int poll(struct pollfd *fds, nfds_t nfds, int timeout_ms); #endif #ifdef WIN32 #define socketpair(a,b,c,d) win32_socketpair(a,b,c,d) /** Compat: socketpair() for win32 */ int socketpair(int d, int typ, int proto, int sv[2]); #endif #endif pgbouncer-1.24.1/lib/usual/list.c0000644000175000000000000000373214777762223013533 00000000000000/* * Circular doubly linked list implementation. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* merge 2 ordered arrays into one */ static struct List *merge(list_cmp_f cmp_func, struct List *p, struct List *q) { struct List res[1], *tail = res, *e; while (p && q) { if (cmp_func(p, q) <= 0) { e = p; p = p->next; } else { e = q; q = q->next; } tail->next = e; tail = e; } tail->next = p ? p : q; return res->next; } /* * non-recursive merge sort * * uses singly-linked NULL-terminated arrays internally. */ void list_sort(struct List *list, list_cmp_f cmp_func) { int i, top = 0; struct List *p; struct List *stack[64]; if (list_empty(list)) return; /* merge small sorted fragments into larger ones */ while (list->next != list) { p = list->next; list->next = p->next; p->next = NULL; for (i = 0; (i < top) && stack[i]; i++) { p = merge(cmp_func, stack[i], p); stack[i] = NULL; } stack[i] = p; if (i == top) top++; } /* merge remaining fragments */ for (p = NULL, i = 0; i < top; i++) p = merge(cmp_func, stack[i], p); /* restore proper List */ list->next = p; for (p = list; p->next; p = p->next) p->next->prev = p; list->prev = p; p->next = list; } pgbouncer-1.24.1/lib/usual/daemon.h0000644000175000000000000000220314777762223014020 00000000000000/** @file * Daemonization & pidfile handling. */ /* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_DAEMON_H_ #define _USUAL_DAEMON_H_ #include /** * Read a pid from pidfile and send a signal to it. */ bool signal_pidfile(const char *pidfile, int sig); /** * Daemonize process and write pidfile. */ void daemonize(const char *pidfile, bool go_background); #endif pgbouncer-1.24.1/lib/usual/strpool.h0000644000175000000000000000350214777762223014262 00000000000000/* * Pool for shared strings. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Storage for shared strings. * * This provides refcounted searchable string pool for cases * where lot of objects reference same strings. */ #ifndef _USUAL_STRPOOL_H_ #define _USUAL_STRPOOL_H_ #include /** Handle for the pool */ struct StrPool; /** Pooled String */ struct PStr { /** Parent pool */ struct StrPool *pool; /** String length */ size_t len; /** Reference count */ int refcnt; /** Zero-terminated value */ char str[FLEX_ARRAY]; }; /** Create new pool */ struct StrPool *strpool_create(CxMem *ca); /** Release pool */ void strpool_free(struct StrPool *sp); /** Return either existing or new PStr for given value */ struct PStr *strpool_get(struct StrPool *sp, const char *str, ssize_t len); /** Increase reference count for existing PStr */ void strpool_incref(struct PStr *str); /** Decrease reference count for existing PStr */ void strpool_decref(struct PStr *str); /** Return count of strings in the pool */ int strpool_total(struct StrPool *sp); #endif pgbouncer-1.24.1/lib/usual/cbtree.c0000644000175000000000000001755714777762223014036 00000000000000/* * Crit-bit tree / binary radix tree. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Associates a C string with user pointer (called "obj"). * * Requires it's own internal nodes, thus not embeddable * to user structs. */ #include #include /* * - Childs are either other nodes or user pointers. * User pointers have lowest bit set. * * - All nodes have both childs. * * - Keys are handled as having infinite length, * zero-filled after actual end. */ struct Node { struct Node *child[2]; size_t bitpos; }; struct CBTree { struct Node *root; cbtree_getkey_func obj_key_cb; cbtree_walker_func obj_free_cb; void *cb_ctx; CxMem *cx; }; #define SAME_KEY SIZE_MAX #define MAX_KEY (SIZE_MAX / 8) /* * Low-level operations. */ /* does ptr point to user object or slot */ static inline bool is_node(void *ptr) { return ((uintptr_t)(ptr) & 1) == 0; } /* flag pointer as pointing to user object */ static inline void *set_external(const void *obj) { return (void*)((uintptr_t)(obj) | 1); } /* remove flag from user pointer */ static inline void *get_external(void *extval) { return (void*)((uintptr_t)(extval) & (~1)); } /* get specific bit from string */ static inline unsigned int get_bit(size_t bitpos, const unsigned char *key, size_t klen) { size_t pos = bitpos / 8; unsigned int bit = 7 - (bitpos % 8); return (pos < klen) && (key[pos] & (1 << bit)); } /* use callback to get key for a stored object */ static inline size_t get_key(struct CBTree *tree, void *obj, const void **key_p) { return tree->obj_key_cb(tree->cb_ctx, obj, key_p); } /* check if object key matches argument */ static inline bool key_matches(struct CBTree *tree, void *obj, const void *key, size_t klen) { const void *o_key; size_t o_klen; o_klen = get_key(tree, obj, &o_key); return (o_klen == klen) && (memcmp(key, o_key, klen) == 0); } /* Find first differing bit on 2 strings. */ static size_t find_crit_bit(const unsigned char *a, size_t alen, const unsigned char *b, size_t blen) { unsigned char av, bv, c, pos; size_t i; size_t minlen = (alen > blen) ? blen : alen; size_t maxlen = (alen > blen) ? alen : blen; /* find differing byte in common data */ for (i = 0; i < minlen; i++) { av = a[i]; bv = b[i]; if (av != bv) goto found; } /* find differing byte when one side is zero-filled */ for (; i < maxlen; i++) { av = (i < alen) ? a[i] : 0; bv = (i < blen) ? b[i] : 0; if (av != bv) goto found; } return SAME_KEY; found: /* calculate bits that differ */ c = av ^ bv; /* find the first one */ pos = 8 - fls(c); return i * 8 + pos; } /* * Lookup */ /* walk nodes until external pointer is found */ static void *raw_lookup(struct CBTree *tree, const void *key, size_t klen) { struct Node *node = tree->root; unsigned int bit; while (is_node(node)) { bit = get_bit(node->bitpos, key, klen); node = node->child[bit]; } return get_external(node); } /* actual lookup. returns obj ptr or NULL of not found */ void *cbtree_lookup(struct CBTree *tree, const void *key, size_t klen) { void *obj; if (!tree->root) return NULL; /* find match based on bits we know about */ obj = raw_lookup(tree, key, klen); /* need to check if the object actually matches */ if (key_matches(tree, obj, key, klen)) return obj; return NULL; } /* * Insertion. */ /* node allocation */ static struct Node *new_node(struct CBTree *tree) { struct Node *node = cx_alloc(tree->cx, sizeof(*node)); if (!node) return NULL; memset(node, 0, sizeof(*node)); return node; } /* insert into empty tree */ static bool insert_first(struct CBTree *tree, void *obj) { tree->root = set_external(obj); return true; } /* insert into specific bit-position */ static bool insert_at(struct CBTree *tree, size_t newbit, const void *key, size_t klen, void *obj) { /* location of current node/obj pointer under examination */ struct Node **pos = &tree->root; struct Node *node; unsigned int bit; while (is_node(*pos) && ((*pos)->bitpos < newbit)) { bit = get_bit((*pos)->bitpos, key, klen); pos = &(*pos)->child[bit]; } bit = get_bit(newbit, key, klen); node = new_node(tree); if (!node) return false; node->bitpos = newbit; node->child[bit] = set_external(obj); node->child[bit ^ 1] = *pos; *pos = node; return true; } /* actual insert: returns true -> insert ok or key found, false -> alloc failure */ bool cbtree_insert(struct CBTree *tree, void *obj) { const void *key, *old_key; size_t newbit, klen, old_klen; void *old_obj; if (!tree->root) return insert_first(tree, obj); /* current key */ klen = get_key(tree, obj, &key); if (klen > MAX_KEY) return false; /* nearest key in tree */ old_obj = raw_lookup(tree, key, klen); old_klen = get_key(tree, old_obj, &old_key); /* first differing bit is the target position */ newbit = find_crit_bit(key, klen, old_key, old_klen); if (newbit == SAME_KEY) return false; return insert_at(tree, newbit, key, klen, obj); } /* * Key deletion. */ /* true -> object was found and removed, false -> not found */ bool cbtree_delete(struct CBTree *tree, const void *key, size_t klen) { void *obj, *tmp; unsigned bit = 0; /* location of current node/obj pointer under examination */ struct Node **pos = &tree->root; /* if 'pos' has user obj, prev_pos has internal node pointing to it */ struct Node **prev_pos = NULL; if (!tree->root) return false; /* match bits we know about */ while (is_node(*pos)) { bit = get_bit((*pos)->bitpos, key, klen); prev_pos = pos; pos = &(*pos)->child[bit]; } /* does the key actually matches */ obj = get_external(*pos); if (!key_matches(tree, obj, key, klen)) return false; if (tree->obj_free_cb) tree->obj_free_cb(tree->cb_ctx, obj); /* drop the internal node pointing to our key */ if (prev_pos) { tmp = *prev_pos; *prev_pos = (*prev_pos)->child[bit ^ 1]; cx_free(tree->cx, tmp); } else { tree->root = NULL; } return true; } /* * Management. */ struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb, cbtree_walker_func obj_free_cb, void *cb_ctx, CxMem *cx) { struct CBTree *tree = cx_alloc(cx, sizeof(*tree)); if (!tree) return NULL; tree->root = NULL; tree->cb_ctx = cb_ctx; tree->obj_key_cb = obj_key_cb; tree->obj_free_cb = obj_free_cb; tree->cx = cx; return tree; } /* recursive freeing */ static void destroy_node(struct CBTree *tree, struct Node *node) { if (is_node(node)) { destroy_node(tree, node->child[0]); destroy_node(tree, node->child[1]); cx_free(tree->cx, node); } else if (tree->obj_free_cb) { void *obj = get_external(node); tree->obj_free_cb(tree->cb_ctx, obj); } } /* Free tree and all it's internal nodes. */ void cbtree_destroy(struct CBTree *tree) { if (tree->root) destroy_node(tree, tree->root); tree->root = NULL; cx_free(tree->cx, tree); } /* * walk over tree */ static bool walk(struct Node *node, cbtree_walker_func cb_func, void *cb_arg) { if (!is_node(node)) return cb_func(cb_arg, get_external(node)); return walk(node->child[0], cb_func, cb_arg) && walk(node->child[1], cb_func, cb_arg); } bool cbtree_walk(struct CBTree *tree, cbtree_walker_func cb_func, void *cb_arg) { if (!tree->root) return true; return walk(tree->root, cb_func, cb_arg); } pgbouncer-1.24.1/lib/usual/pthread.h0000644000175000000000000000401214777762223014204 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Pthreads compat for win32. */ #ifndef _USUAL_PTHREAD_H_ #define _USUAL_PTHREAD_H_ #include #ifdef HAVE_PTHREAD_H #include #else #ifdef WIN32 #define pthread_create(a,b,c,d) compat_pthread_create(a,b,c,d) #define pthread_mutex_init(a,b) compat_pthread_mutex_init(a,b) #define pthread_mutex_destroy(a) compat_pthread_mutex_destroy(a) #define pthread_mutex_lock(a) compat_pthread_mutex_lock(a) #define pthread_mutex_unlock(a) compat_pthread_mutex_unlock(a) #define pthread_join(a,b) compat_pthread_join(a,b) #define pthread_once(a,b) compat_pthread_once(a,b) typedef HANDLE pthread_t; typedef HANDLE pthread_mutex_t; typedef int pthread_attr_t; int pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*fn)(void *), void *arg); int pthread_mutex_init(pthread_mutex_t *lock, void *unused); int pthread_mutex_destroy(pthread_mutex_t *lock); int pthread_mutex_lock(pthread_mutex_t *lock); int pthread_mutex_unlock(pthread_mutex_t *lock); int pthread_join(pthread_t *t, void **ret); #ifdef INIT_ONCE_STATIC_INIT #define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT typedef INIT_ONCE pthread_once_t; int pthread_once(pthread_once_t *once, void (*once_func)(void)); #endif #endif /* WIN32 */ #endif /* HAVE_PTHREAD_H */ #endif pgbouncer-1.24.1/lib/usual/pthread.c0000644000175000000000000000460514777762223014207 00000000000000/* * Pthreads compat. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifndef HAVE_PTHREAD_H #ifdef WIN32 /* * basic pthreads for win32. */ struct _w32thread { void *(*fn)(void *); void *arg; }; static DWORD WINAPI w32launcher(LPVOID arg) { struct _w32thread *info = arg; info->fn(info->arg); free(info); return 0; } int pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*fn)(void *), void *arg) { struct _w32thread *info = calloc(1, sizeof(*info)); if (!info) return -1; info->fn = fn; info->arg = arg; *t = CreateThread(NULL, 0, w32launcher, info, 0, NULL); if (*t == NULL) return -1; return 0; } int pthread_join(pthread_t *t, void **ret) { if (WaitForSingleObject(*t, INFINITE) != WAIT_OBJECT_0) return -1; CloseHandle(*t); return 0; } int pthread_mutex_init(pthread_mutex_t *lock, void *unused) { *lock = CreateMutex(NULL, FALSE, NULL); if (*lock == NULL) return -1; return 0; } int pthread_mutex_destroy(pthread_mutex_t *lock) { if (*lock) { CloseHandle(*lock); *lock = NULL; } return 0; } int pthread_mutex_lock(pthread_mutex_t *lock) { if (WaitForSingleObject(*lock, INFINITE) != WAIT_OBJECT_0) return -1; return 0; } int pthread_mutex_unlock(pthread_mutex_t *lock) { if (!ReleaseMutex(*lock)) return -1; return 0; } #ifdef INIT_ONCE_STATIC_INIT typedef void (*once_posix_cb_t)(void); static BOOL once_wrapper(PINIT_ONCE once, void *arg, void **ctx) { once_posix_cb_t cb = arg; arg(); return TRUE; } int pthread_once(pthread_once_t *once, void (*once_func)(void)) { return InitOnceExecuteOnce(once, once_wrapper, once_func, NULL) ? 0 : -1; } #endif #endif /* win32 */ #endif /* !HAVE_PTHREAD_H */ pgbouncer-1.24.1/lib/usual/socket_win32.h0000644000175000000000000001350214777762223015073 00000000000000/* * Socket compat code for win32. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_SOCKET_WIN32_H_ #define _USUAL_SOCKET_WIN32_H_ /* if found, likely a mistake */ #undef HAVE_INET_NTOP #undef HAVE_INET_PTON typedef int socklen_t; #define in_addr_t uint32_t /* * make recvmsg/sendmsg and fd related code compile */ struct iovec { void *iov_base; /* Base address. */ size_t iov_len; /* Length. */ }; struct msghdr { void *msg_name; int msg_namelen; struct iovec *msg_iov; int msg_iovlen; void *msg_control; int msg_controllen; int msg_flags; }; #ifndef SCM_RIGHTS #define SCM_RIGHTS 1 #endif #ifndef CMSG_FIRSTHDR struct cmsghdr { int cmsg_len; int cmsg_level; int cmsg_type; }; #define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1)) #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \ & ~(sizeof (size_t) - 1)) #define CMSG_LEN(len) ((int)(CMSG_ALIGN(sizeof(struct cmsghdr))+(len))) #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= (int)sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #define CMSG_NXTHDR(mhdr, cmsg) \ (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \ (((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len) \ + CMSG_ALIGN(sizeof(struct cmsghdr)) > \ (u_char *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \ (struct cmsghdr *)NULL : \ (struct cmsghdr *)((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len)))) #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+CMSG_ALIGN(len)) #endif /* * unify WSAGetLastError() with errno. * * and convert int <-> SOCKET. */ /* int <-> socket */ #define FD2S(fd) ((intptr_t)(fd)) #define S2FD(fd) ((int)(fd)) /* socket <-> HANDLE, plain casts */ #define FD2H(fd) ((HANDLE)FD2S(fd)) #define H2FD(h) S2FD((SOCKET)(h)) static inline int ewrap(int res) { if (res < 0) errno = WSAGetLastError(); return res; } /* proper signature for setsockopt */ static inline int w_setsockopt(int fd, int level, int optname, const void *optval, int optlen) { return ewrap(setsockopt(FD2S(fd), level, optname, optval, optlen)); } #define setsockopt(a,b,c,d,e) w_setsockopt(a,b,c,d,e) /* proper signature for send */ static inline ssize_t w_send(int fd, const void *buf, size_t len, int flags) { return ewrap(send(FD2S(fd), buf, len, flags)); } #define send(a,b,c,d) w_send(a,b,c,d) /* proper signature for recv */ static inline ssize_t w_recv(int fd, void *buf, size_t len, int flags) { return ewrap(recv(FD2S(fd), buf, len, flags)); } #define recv(a,b,c,d) w_recv(a,b,c,d) #define getsockopt(a,b,c,d,e) ewrap(getsockopt(FD2S(a),b,c,d,e)) #define connect(a,b,c) ewrap(connect(FD2S(a),b,c)) #define socket(a,b,c) ewrap(S2FD(socket(a,b,c))) #define bind(a,b,c) ewrap(bind(FD2S(a),b,c)) #define listen(a,b) ewrap(listen(FD2S(a),b)) #define accept(a,b,c) ewrap(accept(FD2S(a),b,c)) #define getpeername(a,b,c) ewrap(getpeername(FD2S(a),b,c)) #define getsockname(a,b,c) ewrap(getsockname(FD2S(a),b,c)) #define select(a,b,c,d,e) ewrap(select(a,b,c,d,e)) static inline struct hostent *w_gethostbyname(const char *n) { struct hostent *res = gethostbyname(n); if (!res) errno = WSAGetLastError(); return res; } #define gethostbyname(a) w_gethostbyname(a) /* make unix socket related code compile */ struct sockaddr_un { short sun_family; char sun_path[128]; }; /* sendmsg is not used */ static inline int sendmsg(int s, const struct msghdr *m, int flags) { if (m->msg_iovlen != 1) { errno = EINVAL; return -1; } return send(s, m->msg_iov[0].iov_base, m->msg_iov[0].iov_len, flags); } /* recvmsg() is, but only with one iov */ static inline int recvmsg(int s, struct msghdr *m, int flags) { if (m->msg_iovlen != 1) { errno = EINVAL; return -1; } if (m->msg_controllen) m->msg_controllen = 0; return recv(s, m->msg_iov[0].iov_base, m->msg_iov[0].iov_len, flags); } /* * fcntl */ #define F_GETFD 1 #define F_SETFD 2 #define F_GETFL 3 #define F_SETFL 4 #define O_NONBLOCK 1 #define FD_CLOEXEC HANDLE_FLAG_INHERIT static inline int fcntl(int fd, int cmd, long arg) { ULONG lval; DWORD dval; switch (cmd) { case F_GETFD: if (GetHandleInformation(FD2H(fd), &dval)) return dval; errno = EINVAL; return -1; case F_SETFD: /* set FD_CLOEXEC */ if (SetHandleInformation(FD2H(fd), FD_CLOEXEC, arg)) return 0; errno = EINVAL; return -1; case F_GETFL: /* O_NONBLOCK? */ return 0; case F_SETFL: /* set O_NONBLOCK */ lval = (arg & O_NONBLOCK) ? 1 : 0; if (ioctlsocket(FD2S(fd), FIONBIO, &lval) == SOCKET_ERROR) { errno = WSAGetLastError(); return -1; } return 0; default: errno = EINVAL; return -1; } } /* * SIO_KEEPALIVE_VALS for mingw32 */ #if !defined(SIO_KEEPALIVE_VALS) #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) struct tcp_keepalive { u_long onoff; u_long keepalivetime; u_long keepaliveinterval; }; #endif /* * Use native poll() if available */ #if !defined(HAVE_POLL) && defined(POLLIN) #define HAVE_POLL #define poll(a,b,c) usual_poll(a,b,c) static inline int poll(struct pollfd *fds, int nfds, int timeout) { return WSAPoll(fds, nfds, timeout); } #endif #endif pgbouncer-1.24.1/lib/usual/base.c0000644000175000000000000000330214777762223013463 00000000000000/* * Basic C environment. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #if defined(HAVE_MALLOC_H) && defined(__darwin__) #include #endif /* define posix_memalign() only when possible to emulate */ #if !defined(HAVE_POSIX_MEMALIGN) \ && (defined(HAVE_MEMALIGN) || defined(HAVE_VALLOC)) int posix_memalign(void **ptr_p, size_t align, size_t len) { void *p; int ret, old_errno = errno; #ifdef HAVE_MEMALIGN p = memalign(align, len); #else /* !HAVE_MEMALIGN */ #ifdef HAVE_VALLOC /* assuming less than pagesize alignment */ p = valloc(len); #endif /* !VALLOC */ #endif /* !MEMALIGN */ *ptr_p = p; if (p) return 0; /* on error restore old errno */ ret = errno; errno = old_errno; return ret; } #endif #ifndef HAVE_REALLOCARRAY void *reallocarray(void *p, size_t count, size_t size) { size_t total; if (!safe_mul_size(&total, count, size)) { errno = ENOMEM; return NULL; } return realloc(p, total); } #endif pgbouncer-1.24.1/lib/usual/err.c0000644000175000000000000000505614777762223013351 00000000000000/* * Cmdline error reporting. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #ifndef HAVE_SETPROGNAME static const char *progname; #endif #ifndef HAVE_ERR void err(int e, const char *fmt, ...) { char buf[1024], ebuf[256]; va_list ap; int olderrno = errno; if (fmt) { va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); errx(e, "%s: %s", buf, strerror_r(olderrno, ebuf, sizeof(ebuf))); } else { errx(e, "%s", strerror_r(olderrno, ebuf, sizeof(ebuf))); } } #endif #ifndef HAVE_ERRX void errx(int e, const char *fmt, ...) { va_list ap; if (progname) fprintf(stderr, "%s: ", progname); if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } fprintf(stderr, "\n"); exit(e); } #endif #ifndef HAVE_WARN void warn(const char *fmt, ...) { char buf[1024], ebuf[256]; va_list ap; int olderrno = errno; if (fmt) { va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); warnx("%s: %s", buf, strerror_r(olderrno, ebuf, sizeof(ebuf))); } else { warnx("%s", strerror_r(olderrno, ebuf, sizeof(ebuf))); } } #endif #ifndef HAVE_WARNX void warnx(const char *fmt, ...) { va_list ap; if (progname) fprintf(stderr, "%s: ", progname); if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } } #endif #ifndef HAVE_SETPROGNAME void setprogname(const char *s) { const char *ss = strrchr(s, '/'); progname = ss ? (ss + 1) : s; } #endif #ifndef HAVE_GETPROGNAME const char *getprogname(void) { return progname; } #endif void *xmalloc(size_t len) { void *p = malloc(len); if (!p) err(1, "no mem"); return p; } void *xrealloc(void *p, size_t len) { void *p2 = realloc(p, len); if (!p2) err(1, "no mem"); return p2; } char *xstrdup(const char *s) { void *s2 = strdup(s); if (!s2) err(1, "no mem"); return s2; } pgbouncer-1.24.1/lib/usual/socket_pton.c0000644000175000000000000001224014777762223015102 00000000000000/* $OpenBSD: inet_pton.c,v 1.8 2010/05/06 15:47:14 claudio Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #include #ifndef HAVE_INET_PTON #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #define u_char uint8_t #define u_int unsigned int /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, u_char *dst); static int inet_pton6(const char *src, u_char *dst); /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(int af, const char *src, void *dst) { switch (af) { case AF_INET: return (inet_pton4(src, dst)); case AF_INET6: return (inet_pton6(src, dst)); default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, u_char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; u_char tmp[INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { u_int new = *tp * 10 + (pch - digits); if (new > 255) return (0); if (! saw_digit) { if (++octets > 4) return (0); saw_digit = 1; } *tp = new; } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); *++tp = 0; saw_digit = 0; } else return (0); } if (octets < 4) return (0); memcpy(dst, tmp, INADDRSZ); return (1); } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * does not touch `dst' unless it's returning 1. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, u_char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit, count_xdigit; u_int val; memset((tp = tmp), '\0', IN6ADDRSZ); endp = tp + IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; saw_xdigit = count_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { if (count_xdigit >= 4) return (0); val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return (0); saw_xdigit = 1; count_xdigit++; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return (0); colonp = tp; continue; } else if (*src == '\0') { return (0); } if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; saw_xdigit = 0; count_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += INADDRSZ; saw_xdigit = 0; count_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return (0); } if (saw_xdigit) { if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return (0); for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, IN6ADDRSZ); return (1); } #endif pgbouncer-1.24.1/lib/usual/mbuf.c0000644000175000000000000000117314777762223013506 00000000000000 /* * Safe and easy access to memory buffer. */ #include bool mbuf_make_room(struct MBuf *buf, unsigned len) { unsigned new_alloc = buf->alloc_len; void *ptr; /* is it a dynamic buffer */ if (buf->reader || buf->fixed) return false; /* maybe there is enough room already */ if (buf->write_pos + len <= buf->alloc_len) return true; if (new_alloc == 0) new_alloc = 128; /* calc new alloc size */ while (new_alloc < buf->write_pos + len) new_alloc *= 2; /* realloc */ ptr = realloc(buf->data, new_alloc); if (!ptr) return false; buf->data = ptr; buf->alloc_len = new_alloc; return true; } pgbouncer-1.24.1/lib/usual/cxextra.c0000644000175000000000000001633314777762223014237 00000000000000 /* * Extra allocators */ #include #include #include #include /* * Tools for allocators. */ static inline void *p_move(const void *p, int ofs) { return (char *)p + ofs; } /* * sample exit-on-failure wrapper */ static void *nofail_alloc(void *next, size_t len) { void *p = cx_alloc(next, len); if (!p) exit(1); return p; } static void *nofail_realloc(void *next, void *ptr, size_t len) { void *p = cx_realloc(next, ptr, len); if (!p) exit(1); return p; } static void nofail_free(void *next, void *ptr) { cx_free(next, ptr); } static void nofail_destroy(void *next) { cx_destroy(next); } const struct CxOps cx_nofail_ops = { nofail_alloc, nofail_realloc, nofail_free, nofail_destroy, }; const struct CxMem cx_libc_nofail = { &cx_nofail_ops, (void*)&cx_libc_allocator, }; /* * Append-only pool. */ struct CxPoolSeg { struct CxPoolSeg *prev; unsigned char *seg_start; unsigned char *seg_pos; unsigned char *seg_end; }; struct CxPool { struct CxMem this; const struct CxMem *parent; struct CxPoolSeg *last; unsigned char *last_ptr; unsigned int align; bool allow_free_first; struct CxPoolSeg first_seg; }; #define POOL_HDR ALIGN(sizeof(struct CxPoolSeg)) static struct CxPoolSeg *new_seg(struct CxPool *pool, size_t nsize) { struct CxPoolSeg *seg; unsigned char *ptr; size_t alloc = POOL_HDR + nsize; seg = cx_alloc(pool->parent, alloc); if (seg == NULL) return NULL; ptr = (unsigned char *)seg; seg->seg_start = (void *)CUSTOM_ALIGN(ptr + POOL_HDR, pool->align); seg->seg_pos = seg->seg_start; seg->seg_end = (unsigned char *)seg + alloc; seg->prev = pool->last; pool->last = seg; pool->last_ptr = NULL; return seg; } static void *pool_alloc(void *ctx, size_t size) { struct CxPool *pool = ctx; struct CxPoolSeg *seg = pool->last; void *ptr; unsigned nsize; size = CUSTOM_ALIGN(size, pool->align); if (seg && seg->seg_pos + size <= seg->seg_end) { ptr = seg->seg_pos; seg->seg_pos += size; pool->last_ptr = ptr; return ptr; } else { nsize = seg ? (2 * (seg->seg_end - seg->seg_start)) : 512; while (nsize < size) nsize *= 2; seg = new_seg(pool, nsize); if (!seg) return NULL; ptr = seg->seg_pos; seg->seg_pos += size; pool->last_ptr = ptr; return ptr; } } /* free only last item */ static void pool_free(void *ctx, void *ptr) { struct CxPool *pool = ctx; struct CxPoolSeg *cur = pool->last; if (pool->last_ptr != ptr) return; cur->seg_pos = (void *)ptr; pool->last_ptr = NULL; } static size_t pool_guess_old_len(struct CxPool *pool, unsigned char *ptr) { struct CxPoolSeg *seg = pool->last; unsigned char *cstart; while (seg) { cstart = (void *)CUSTOM_ALIGN((seg + 1), pool->align); if (ptr >= cstart && ptr < seg->seg_pos) return seg->seg_pos - ptr; seg = seg->prev; } return 0; } /* realloc only last item properly, otherwise do new alloc */ static void *pool_realloc(void *ctx, void *ptr, size_t len) { struct CxPool *pool = ctx; struct CxPoolSeg *seg = pool->last; unsigned char *p = ptr; size_t olen; if (pool->last_ptr != ptr) { olen = pool_guess_old_len(pool, ptr); p = pool_alloc(ctx, len); if (!p) return NULL; if (olen > len) olen = len; memcpy(p, ptr, olen); return p; } olen = seg->seg_pos - p; if (seg->seg_pos - olen + len <= seg->seg_end) { seg->seg_pos = p + len; return p; } else { p = pool_alloc(ctx, len); if (!p) return NULL; memcpy(p, ptr, olen); return p; } } static void pool_destroy(void *ctx) { struct CxPool *pool = ctx; struct CxPoolSeg *cur, *prev; if (!pool) return; for (cur = pool->last; cur; ) { prev = cur->prev; if (!prev) break; cx_free(pool->parent, cur); cur = prev; } if (pool->allow_free_first) cx_free(pool->parent, pool); } static const struct CxOps pool_ops = { pool_alloc, pool_realloc, pool_free, pool_destroy, }; /* * public functions */ CxMem *cx_new_pool_from_area(CxMem *parent, void *buf, size_t size, bool allow_free, unsigned int align) { struct CxPool *head; if (size < sizeof(struct CxPool)) return NULL; if (align == 0) align = 8; else if (!is_power_of_2(align)) return NULL; head = buf; memset(head, 0, sizeof(struct CxPool)); head->parent = parent; head->this.ops = &pool_ops; head->this.ctx = head; head->last = &head->first_seg; head->allow_free_first = allow_free; head->align = align; head->first_seg.seg_start = (void *)CUSTOM_ALIGN(head + 1, align); head->first_seg.seg_pos = head->first_seg.seg_start; head->first_seg.seg_end = (unsigned char *)head + size; return &head->this; } CxMem *cx_new_pool(CxMem *parent, size_t initial_size, unsigned int align) { void *area; size_t size; if (initial_size < 1024) initial_size = 1024; size = sizeof(struct CxPool) + initial_size; area = cx_alloc(parent, size); if (!area) return NULL; return cx_new_pool_from_area(parent, area, size, true, align); } /* * tree alloc */ #define TREE_HDR (int)(sizeof(struct CxTreeItem)) struct CxTree { struct CxMem this; CxMem *real; struct List alloc_list; struct List subtree_node; struct List subtree_list; }; /* header for each allocation */ struct CxTreeItem { struct List node; }; static void *tree_alloc(void *ctx, size_t len) { struct CxTree *tree = ctx; struct CxTreeItem *item; item = cx_alloc(tree->real, TREE_HDR + len); if (!item) return NULL; list_init(&item->node); list_append(&tree->alloc_list, &item->node); return p_move(item, TREE_HDR); } static void *tree_realloc(void *ctx, void *ptr, size_t len) { struct CxTree *t = ctx; struct CxTreeItem *item, *item2; item = p_move(ptr, -TREE_HDR); list_del(&item->node); item2 = cx_realloc(t->real, item, TREE_HDR + len); if (item2) { list_append(&t->alloc_list, &item2->node); return p_move(item2, TREE_HDR); } else { list_append(&t->alloc_list, &item->node); return NULL; } } static void tree_free(void *ctx, void *ptr) { struct CxTree *t = ctx; struct CxTreeItem *item; item = p_move(ptr, -TREE_HDR); list_del(&item->node); cx_free(t->real, item); } static void tree_destroy(void *ctx) { struct CxTree *tree = ctx, *sub; struct CxTreeItem *item; struct List *el, *tmp; /* unregister from parent */ list_del(&tree->subtree_node); /* free elements */ list_for_each_safe(el, &tree->alloc_list, tmp) { list_del(el); item = container_of(el, struct CxTreeItem, node); cx_free(tree->real, item); } /* free subtrees */ list_for_each_safe(el, &tree->subtree_list, tmp) { sub = container_of(el, struct CxTree, subtree_node); tree_destroy(sub); } /* free base struct */ cx_free(tree->real, tree); } static const struct CxOps tree_ops = { tree_alloc, tree_realloc, tree_free, tree_destroy, }; CxMem *cx_new_tree(CxMem *cx) { struct CxTree *t, *parent = NULL; CxMem *real = cx; /* * Try to allocate from real allocator. Otherwise allocations * will have double headers. */ if (cx->ops == &tree_ops) { parent = cx->ctx; real = parent->real; } /* initialize */ t = cx_alloc(real, sizeof(*t)); if (!t) return NULL; t->real = real; t->this.ops = &tree_ops; t->this.ctx = t; list_init(&t->alloc_list); list_init(&t->subtree_node); list_init(&t->subtree_list); /* register at parent */ if (parent) list_append(&parent->subtree_list, &t->subtree_node); return &t->this; } pgbouncer-1.24.1/lib/usual/crypto/0000755000000000000000000000000014777762567014005 500000000000000pgbouncer-1.24.1/lib/usual/crypto/sha1.c0000644000175000000000000000740014777762223014730 00000000000000/* * SHA1 implementation based on RFC3174. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define bufpos(ctx) ((ctx)->nbytes & (SHA1_BLOCK_SIZE - 1)) /* * SHA1 core. */ #define W(n) (buf[(n) & 15]) #define setW(n, val) W(n) = val /* base SHA1 operation */ #define SHA1OP(_t, fn, K) do { \ uint32_t tmp, t = (_t); \ if (t >= 16) { \ tmp = W(t - 3) ^ W(t - 8) ^ W(t - 14) ^ W(t - 16); \ setW(t, rol32(tmp, 1)); \ } else { \ /* convert endianess on first go */ \ setW(t, be32toh(W(t))); \ } \ tmp = rol32(a, 5) + fn(b, c, d) + e + W(t) + K; \ e = d; d = c; c = rol32(b, 30); b = a; a = tmp; \ } while (0) /* mix functions */ #define F0(b, c, d) (d ^ (b & (c ^ d))) #define F1(b, c, d) (b ^ c ^ d) #define F2(b, c, d) ((b & c) | (b & d) | (c & d)) #define F3(b, c, d) (b ^ c ^ d) /* operation details for each round */ #define SHA1R0(t) SHA1OP(t, F0, 0x5a827999) #define SHA1R1(t) SHA1OP(t, F1, 0x6ed9eba1) #define SHA1R2(t) SHA1OP(t, F2, 0x8f1bbcdc) #define SHA1R3(t) SHA1OP(t, F3, 0xca62c1d6) /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R20(R, t) R16(R, t+0); R4(R, t+16) static void sha1_core(struct sha1_ctx * ctx, uint32_t *buf) { uint32_t a, b, c, d, e; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; e = ctx->e; R20(SHA1R0, 0); R20(SHA1R1, 20); R20(SHA1R2, 40); R20(SHA1R3, 60); ctx->a += a; ctx->b += b; ctx->c += c; ctx->d += d; ctx->e += e; } /* * Public API. */ void sha1_reset(struct sha1_ctx *ctx) { ctx->nbytes = 0; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->e = 0xc3d2e1f0; } void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = (uint8_t *)ctx->buf; while (len > 0) { n = SHA1_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha1_core(ctx, ctx->buf); } } void sha1_final(struct sha1_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA1_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); /* add padding */ pad_len = SHA1_BLOCK_SIZE - 8 - pos; if (pad_len <= 0) pad_len += SHA1_BLOCK_SIZE; sha1_update(ctx, padding, pad_len); /* add length */ ctx->buf[14] = htobe32(nbits >> 32); ctx->buf[15] = htobe32(nbits); /* final result */ sha1_core(ctx, ctx->buf); be32enc(dst + 0*4, ctx->a); be32enc(dst + 1*4, ctx->b); be32enc(dst + 2*4, ctx->c); be32enc(dst + 3*4, ctx->d); be32enc(dst + 4*4, ctx->e); } /* * DigestInfo */ static const struct DigestInfo sha1_info = { (DigestInitFunc *)sha1_reset, (DigestUpdateFunc *)sha1_update, (DigestFinalFunc *)sha1_final, sizeof(struct sha1_ctx), SHA1_DIGEST_LENGTH, SHA1_BLOCK_SIZE }; const struct DigestInfo *digest_SHA1(void) { return &sha1_info; } pgbouncer-1.24.1/lib/usual/crypto/chacha.h0000644000175000000000000000327514777762223015316 00000000000000/* * ChaCha cipher. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * ChaCha cipher. */ #ifndef _CHACHA_CLEAN_H_ #define _CHACHA_CLEAN_H_ #include #define CHACHA_KEY_SIZE 32 #define CHACHA_IV_SIZE 8 #define CHACHA_BLOCK_SIZE 64 /** * ChaCha state. */ struct ChaCha { uint32_t state[16]; union { uint32_t output32[16]; uint8_t output8[16*4]; } u; unsigned int pos; }; /** * Set 256-bit key. */ void chacha_set_key_256(struct ChaCha *ctx, const void *key); /** * Set 128-bit key. */ void chacha_set_key_128(struct ChaCha *ctx, const void *key); /** * Set 2x32-bit counter and 8-byte IV. */ void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv); /** * Extract plain keystream. */ void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes); /** * XOR data with keystream. */ void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes); #endif pgbouncer-1.24.1/lib/usual/crypto/sha512.c0000644000175000000000000001705214777762223015103 00000000000000/* * SHA2-512 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R64(R, t) R16(R, t+0); R16(R, t+16); R16(R, t+32); R16(R, t+48); #define bufpos(ctx) ((ctx)->nbytes & (SHA512_BLOCK_SIZE - 1)) /* * initial values */ static const uint64_t H384[8] = { UINT64_C(0xcbbb9d5dc1059ed8), UINT64_C(0x629a292a367cd507), UINT64_C(0x9159015a3070dd17), UINT64_C(0x152fecd8f70e5939), UINT64_C(0x67332667ffc00b31), UINT64_C(0x8eb44a8768581511), UINT64_C(0xdb0c2e0d64f98fa7), UINT64_C(0x47b5481dbefa4fa4), }; static const uint64_t H512[8] = { UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179), }; /* * constants for mixing */ static const uint64_t K[80] = { UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817), }; /* * mixing */ #define CH(x,y,z) ((x & y) ^ ((~x) & z)) #define MAJ(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) #define E0(x) (ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39)) #define E1(x) (ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41)) #define O0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) #define O1(x) (ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6)) #define W(n) (ctx->buf.words[(n) & 15]) #define setW(n,v) W(n) = (v) #define SHA512_ROUND(_t) do { \ uint64_t tmp1, tmp2, t = (_t); \ if (t >= 16) { \ setW(t, O1(W(t - 2)) + W(t - 7) + O0(W(t - 15)) + W(t - 16)); \ } else { \ /* convert endianess on first go */ \ setW(t, be64toh(W(t))); \ } \ tmp1 = h + E1(e) + CH(e,f,g) + K[k_pos++] + W(t); \ tmp2 = E0(a) + MAJ(a,b,c); \ h = g; g = f; f = e; e = d + tmp1; d = c; c = b; b = a; a = tmp1 + tmp2; \ } while (0) /* * actual core */ static void sha512_core(struct sha512_ctx *ctx) { uint64_t *state = ctx->state; uint64_t a = state[0], b = state[1], c = state[2], d = state[3]; uint64_t e = state[4], f = state[5], g = state[6], h = state[7]; unsigned k_pos = 0; R16(SHA512_ROUND, 0); while (k_pos < 80) { R16(SHA512_ROUND, 16); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } /* * Public API for SHA512. */ void sha512_reset(struct sha512_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H512, sizeof(H512)); } void sha512_update(struct sha512_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = ctx->buf.raw; while (len > 0) { n = SHA512_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha512_core(ctx); } } void sha512_final(struct sha512_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA512_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int i, pad_len; /* add padding */ pad_len = SHA512_BLOCK_SIZE - 16 - bufpos(ctx); if (pad_len <= 0) pad_len += SHA512_BLOCK_SIZE; sha512_update(ctx, padding, pad_len); /* add length */ ctx->buf.words[14] = 0; ctx->buf.words[15] = htobe64(nbits); /* final result */ sha512_core(ctx); for (i = 0; i < SHA512_DIGEST_LENGTH / 8; i++) be64enc(dst + i*8, ctx->state[i]); } /* * Public API for SHA384. */ void sha384_reset(struct sha512_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H384, sizeof(H384)); } void sha384_update(struct sha512_ctx *ctx, const void *data, unsigned int len) { sha512_update(ctx, data, len); } void sha384_final(struct sha512_ctx *ctx, uint8_t *dst) { uint8_t buf[SHA512_DIGEST_LENGTH]; sha512_final(ctx, buf); memcpy(dst, buf, SHA384_DIGEST_LENGTH); memset(buf, 0, sizeof(buf)); } /* * DigestInfo */ const struct DigestInfo *digest_SHA384(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha384_reset, (DigestUpdateFunc *)sha384_update, (DigestFinalFunc *)sha384_final, sizeof(struct sha512_ctx), SHA384_DIGEST_LENGTH, SHA384_BLOCK_SIZE }; return &info; } const struct DigestInfo *digest_SHA512(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha512_reset, (DigestUpdateFunc *)sha512_update, (DigestFinalFunc *)sha512_final, sizeof(struct sha512_ctx), SHA512_DIGEST_LENGTH, SHA512_BLOCK_SIZE }; return &info; } pgbouncer-1.24.1/lib/usual/crypto/digest.c0000644000175000000000000000365114777762223015357 00000000000000/* * Common API for cryptographic digests. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct DigestContext { const struct DigestInfo *impl; CxMem *cx; uint64_t state[1]; }; struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx) { struct DigestContext *ctx; unsigned alloc; alloc = offsetof(struct DigestContext, state) + impl->state_len; ctx = cx_alloc(cx, alloc); if (!ctx) return NULL; ctx->impl = impl; ctx->cx = cx; impl->init(ctx->state); return ctx; } void digest_update(struct DigestContext *ctx, const void *data, size_t len) { ctx->impl->update(ctx->state, data, len); } void digest_final(struct DigestContext *ctx, uint8_t *res) { ctx->impl->final(ctx->state, res); } void digest_reset(struct DigestContext *ctx) { ctx->impl->init(ctx->state); } void digest_free(struct DigestContext *ctx) { CxMem *cx = ctx->cx; unsigned alloc = offsetof(struct DigestContext, state) + ctx->impl->state_len; memset(ctx, 0, alloc); cx_free(cx, ctx); } unsigned digest_block_len(struct DigestContext *ctx) { return ctx->impl->block_len; } unsigned digest_result_len(struct DigestContext *ctx) { return ctx->impl->result_len; } pgbouncer-1.24.1/lib/usual/crypto/keccak.c0000644000175000000000000010530314777762223015316 00000000000000/* * Keccak implementation for SHA3 parameters. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on public-domain Keccak-inplace.c and Keccak-inplace32BI.c * implementations from Keccak reference code: * * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, * Michaël Peeters and Gilles Van Assche. For more information, feedback or * questions, please refer to our website: http://keccak.noekeon.org/ * * Implementation by Ronny Van Keer and the designers, * hereby denoted as "the implementer". * * To the extent possible under law, the implementer has waived all copyright * and related or neighboring rights to the source code in this file. * http://creativecommons.org/publicdomain/zero/1.0/ * * 32-bit word interlacing algorithm: * * Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */ #include #include #include #include #include /* For SHA3 variant of Keccak */ #define KECCAK_ROUNDS 24 /* * Enforce minimal code size. If this is not defined, use * faster unrolled implementation. */ /* #define KECCAK_SMALL */ #ifdef KECCAK_SMALL #define KECCAK_64BIT #endif /* * Decide whether to use 64- or 32-bit implementation. */ #if !defined(KECCAK_64BIT) && !defined(KECCAK_32BIT) #if !defined(LONG_MAX) && !defined(UINTPTR_MAX) #error "Need LONG_MAX & UINTPTR_MAX" #endif /* If neither is defined, try to autodetect */ #if (LONG_MAX > 0xFFFFFFFF) || (UINTPTR_MAX > 0xFFFFFFFF) /* use 64-bit implementation if 'long' or 'uintptr_t' is 64-bit */ #define KECCAK_64BIT #else /* otherwise, use 32-bit implementation */ #define KECCAK_32BIT #endif #endif #ifdef KECCAK_64BIT /* * 64-bit implementation - one lane is one 64-bit word. */ /* round constants */ static const uint64_t RoundConstants64[KECCAK_ROUNDS] = { UINT64_C(0x0000000000000001), UINT64_C(0x0000000000008082), UINT64_C(0x800000000000808A), UINT64_C(0x8000000080008000), UINT64_C(0x000000000000808B), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008009), UINT64_C(0x000000000000008A), UINT64_C(0x0000000000000088), UINT64_C(0x0000000080008009), UINT64_C(0x000000008000000A), UINT64_C(0x000000008000808B), UINT64_C(0x800000000000008B), UINT64_C(0x8000000000008089), UINT64_C(0x8000000000008003), UINT64_C(0x8000000000008002), UINT64_C(0x8000000000000080), UINT64_C(0x000000000000800A), UINT64_C(0x800000008000000A), UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008080), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008008), }; #ifdef KECCAK_SMALL /* * Minimal code implementation */ static const uint8_t RhoRot[24] = { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 }; static const uint8_t PiLane[24] = { 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 }; static void keccak_f(struct KeccakContext *ctx) { int i, j; uint64_t *A = ctx->u.state64; uint64_t tmpbuf[5 + 2], *tmp = tmpbuf + 1; uint64_t d, c1, c2; for (j = 0; j < KECCAK_ROUNDS; j++) { /* Theta step */ for (i = 0; i < 5; i++) tmp[i] = A[0*5 + i] ^ A[1*5 + i] ^ A[2*5 + i] ^ A[3*5 + i] ^ A[4*5 + i]; tmpbuf[0] = tmp[4]; tmpbuf[6] = tmp[0]; for (i = 0; i < 5; i++) { d = tmp[i-1] ^ rol64(tmp[i+1], 1); A[0 + i] ^= d; A[5 + i] ^= d; A[10 + i] ^= d; A[15 + i] ^= d; A[20 + i] ^= d; } /* Rho + Pi step */ c1 = A[PiLane[23]]; for (i = 0; i < 24; i++) { c2 = A[PiLane[i]]; A[PiLane[i]] = rol64(c1, RhoRot[i]); c1 = c2; } /* Chi step */ for (i = 0; i < 25; ) { tmp[0] = A[i+0]; tmp[1] = A[i+1]; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & tmp[0]; i++; A[i] ^= ~tmp[0] & tmp[1]; i++; } /* Iota step */ A[0] ^= RoundConstants64[j]; } } #else /* !KECCAK_SMALL - fast 64-bit */ static void keccak_f(struct KeccakContext *ctx) { uint64_t *state = ctx->u.state64; uint64_t Ba, Be, Bi, Bo, Bu; uint64_t Ca, Ce, Ci, Co, Cu; uint64_t Da, De, Di, Do, Du; int i; #define Aba state[ 0] #define Abe state[ 1] #define Abi state[ 2] #define Abo state[ 3] #define Abu state[ 4] #define Aga state[ 5] #define Age state[ 6] #define Agi state[ 7] #define Ago state[ 8] #define Agu state[ 9] #define Aka state[10] #define Ake state[11] #define Aki state[12] #define Ako state[13] #define Aku state[14] #define Ama state[15] #define Ame state[16] #define Ami state[17] #define Amo state[18] #define Amu state[19] #define Asa state[20] #define Ase state[21] #define Asi state[22] #define Aso state[23] #define Asu state[24] for (i = 0; i < KECCAK_ROUNDS; i += 4) { /* Code for 4 rounds */ Ca = Aba^Aga^Aka^Ama^Asa; Ce = Abe^Age^Ake^Ame^Ase; Ci = Abi^Agi^Aki^Ami^Asi; Co = Abo^Ago^Ako^Amo^Aso; Cu = Abu^Agu^Aku^Amu^Asu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Age^De), 44); Bi = rol64((Aki^Di), 43); Bo = rol64((Amo^Do), 21); Bu = rol64((Asu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+0]; Age = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Bi = rol64((Aka^Da), 3); Bo = rol64((Ame^De), 45); Bu = rol64((Asi^Di), 61); Ba = rol64((Abo^Do), 28); Be = rol64((Agu^Du), 20); Aka = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Asa^Da), 18); Ba = rol64((Abe^De), 1); Be = rol64((Agi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Amu^Du), 8); Asa = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Be = rol64((Aga^Da), 36); Bi = rol64((Ake^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Aso^Do), 56); Ba = rol64((Abu^Du), 27); Aga = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Bo = rol64((Ama^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Abi^Di), 62); Be = rol64((Ago^Do), 55); Bi = rol64((Aku^Du), 39); Ama = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Ca = Aba^Aka^Asa^Aga^Ama; Ce = Age^Ame^Abe^Ake^Ase; Ci = Aki^Asi^Agi^Ami^Abi; Co = Amo^Abo^Ako^Aso^Ago; Cu = Asu^Agu^Amu^Abu^Aku; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Ame^De), 44); Bi = rol64((Agi^Di), 43); Bo = rol64((Aso^Do), 21); Bu = rol64((Aku^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+1]; Ame = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Bi = rol64((Asa^Da), 3); Bo = rol64((Ake^De), 45); Bu = rol64((Abi^Di), 61); Ba = rol64((Amo^Do), 28); Be = rol64((Agu^Du), 20); Asa = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Ama^Da), 18); Ba = rol64((Age^De), 1); Be = rol64((Asi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Abu^Du), 8); Ama = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Be = rol64((Aka^Da), 36); Bi = rol64((Abe^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Ago^Do), 56); Ba = rol64((Asu^Du), 27); Aka = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Bo = rol64((Aga^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Aki^Di), 62); Be = rol64((Abo^Do), 55); Bi = rol64((Amu^Du), 39); Aga = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Ca = Aba^Asa^Ama^Aka^Aga; Ce = Ame^Ake^Age^Abe^Ase; Ci = Agi^Abi^Asi^Ami^Aki; Co = Aso^Amo^Ako^Ago^Abo; Cu = Aku^Agu^Abu^Asu^Amu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Ake^De), 44); Bi = rol64((Asi^Di), 43); Bo = rol64((Ago^Do), 21); Bu = rol64((Amu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+2]; Ake = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Bi = rol64((Ama^Da), 3); Bo = rol64((Abe^De), 45); Bu = rol64((Aki^Di), 61); Ba = rol64((Aso^Do), 28); Be = rol64((Agu^Du), 20); Ama = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Aga^Da), 18); Ba = rol64((Ame^De), 1); Be = rol64((Abi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Asu^Du), 8); Aga = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Be = rol64((Asa^Da), 36); Bi = rol64((Age^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Abo^Do), 56); Ba = rol64((Aku^Du), 27); Asa = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Bo = rol64((Aka^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Agi^Di), 62); Be = rol64((Amo^Do), 55); Bi = rol64((Abu^Du), 39); Aka = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Ca = Aba^Ama^Aga^Asa^Aka; Ce = Ake^Abe^Ame^Age^Ase; Ci = Asi^Aki^Abi^Ami^Agi; Co = Ago^Aso^Ako^Abo^Amo; Cu = Amu^Agu^Asu^Aku^Abu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Abe^De), 44); Bi = rol64((Abi^Di), 43); Bo = rol64((Abo^Do), 21); Bu = rol64((Abu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+3]; Abe = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Bi = rol64((Aga^Da), 3); Bo = rol64((Age^De), 45); Bu = rol64((Agi^Di), 61); Ba = rol64((Ago^Do), 28); Be = rol64((Agu^Du), 20); Aga = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Aka^Da), 18); Ba = rol64((Ake^De), 1); Be = rol64((Aki^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Aku^Du), 8); Aka = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Be = rol64((Ama^Da), 36); Bi = rol64((Ame^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Amo^Do), 56); Ba = rol64((Amu^Du), 27); Ama = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Bo = rol64((Asa^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Asi^Di), 62); Be = rol64((Aso^Do), 55); Bi = rol64((Asu^Du), 39); Asa = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); } } #endif /* !KECCAK_SMALL */ static inline void xor_lane(struct KeccakContext *ctx, int lane, uint64_t val) { ctx->u.state64[lane] ^= val; } static void extract(uint8_t *dst, const struct KeccakContext *ctx, int startLane, int laneCount) { const uint64_t *src = ctx->u.state64 + startLane; while (laneCount--) { le64enc(dst, *src++); dst += 8; } } #else /* KECCAK_32BIT */ /* * 32-bit implementation - one 64-bit lane is mapped * to two interleaved 32-bit words. */ static const uint32_t RoundConstants32[2*KECCAK_ROUNDS] = { 0x00000001, 0x00000000, 0x00000000, 0x00000089, 0x00000000, 0x8000008b, 0x00000000, 0x80008080, 0x00000001, 0x0000008b, 0x00000001, 0x00008000, 0x00000001, 0x80008088, 0x00000001, 0x80000082, 0x00000000, 0x0000000b, 0x00000000, 0x0000000a, 0x00000001, 0x00008082, 0x00000000, 0x00008003, 0x00000001, 0x0000808b, 0x00000001, 0x8000000b, 0x00000001, 0x8000008a, 0x00000001, 0x80000081, 0x00000000, 0x80000081, 0x00000000, 0x80000008, 0x00000000, 0x00000083, 0x00000000, 0x80008003, 0x00000001, 0x80008088, 0x00000000, 0x80000088, 0x00000001, 0x00008000, 0x00000000, 0x80008082, }; #define KeccakAtoD_round0() \ Cx = Abu0^Agu0^Aku0^Amu0^Asu0; \ Du1 = Abe1^Age1^Ake1^Ame1^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Abu1^Agu1^Aku1^Amu1^Asu1; \ Du0 = Abe0^Age0^Ake0^Ame0^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Abi0^Agi0^Aki0^Ami0^Asi0; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Abi1^Agi1^Aki1^Ami1^Asi1; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Aga0^Aka0^Ama0^Asa0; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Aga1^Aka1^Ama1^Asa1; \ De1 = Cz^Cw; \ \ Cy = Abo1^Ago1^Ako1^Amo1^Aso1; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Abo0^Ago0^Ako0^Amo0^Aso0; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round1() \ Cx = Asu0^Agu0^Amu0^Abu1^Aku1; \ Du1 = Age1^Ame0^Abe0^Ake1^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Asu1^Agu1^Amu1^Abu0^Aku0; \ Du0 = Age0^Ame1^Abe1^Ake0^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Aki1^Asi1^Agi0^Ami1^Abi0; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Aki0^Asi0^Agi1^Ami0^Abi1; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Aka1^Asa0^Aga0^Ama1; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Aka0^Asa1^Aga1^Ama0; \ De1 = Cz^Cw; \ \ Cy = Amo0^Abo1^Ako0^Aso1^Ago0; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Amo1^Abo0^Ako1^Aso0^Ago1; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round2() \ Cx = Aku1^Agu0^Abu1^Asu1^Amu1; \ Du1 = Ame0^Ake0^Age0^Abe0^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Aku0^Agu1^Abu0^Asu0^Amu0; \ Du0 = Ame1^Ake1^Age1^Abe1^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Agi1^Abi1^Asi1^Ami0^Aki1; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Agi0^Abi0^Asi0^Ami1^Aki0; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Asa1^Ama1^Aka1^Aga1; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Asa0^Ama0^Aka0^Aga0; \ De1 = Cz^Cw; \ \ Cy = Aso0^Amo0^Ako1^Ago0^Abo0; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Aso1^Amo1^Ako0^Ago1^Abo1; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round3() \ Cx = Amu1^Agu0^Asu1^Aku0^Abu0; \ Du1 = Ake0^Abe1^Ame1^Age0^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Amu0^Agu1^Asu0^Aku1^Abu1; \ Du0 = Ake1^Abe0^Ame0^Age1^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Asi0^Aki0^Abi1^Ami1^Agi1; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Asi1^Aki1^Abi0^Ami0^Agi0; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Ama0^Aga1^Asa1^Aka0; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Ama1^Aga0^Asa0^Aka1; \ De1 = Cz^Cw; \ \ Cy = Ago1^Aso0^Ako0^Abo0^Amo1; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Ago0^Aso1^Ako1^Abo1^Amo0; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; static void keccak_f(struct KeccakContext *ctx) { uint32_t *state = ctx->u.state32; uint32_t Da0, De0, Di0, Do0, Du0; uint32_t Da1, De1, Di1, Do1, Du1; uint32_t Ca0, Ce0, Ci0, Co0, Cu0; uint32_t Cx, Cy, Cz, Cw; int i; #define Ba Ca0 #define Be Ce0 #define Bi Ci0 #define Bo Co0 #define Bu Cu0 #define Aba0 state[ 0] #define Aba1 state[ 1] #define Abe0 state[ 2] #define Abe1 state[ 3] #define Abi0 state[ 4] #define Abi1 state[ 5] #define Abo0 state[ 6] #define Abo1 state[ 7] #define Abu0 state[ 8] #define Abu1 state[ 9] #define Aga0 state[10] #define Aga1 state[11] #define Age0 state[12] #define Age1 state[13] #define Agi0 state[14] #define Agi1 state[15] #define Ago0 state[16] #define Ago1 state[17] #define Agu0 state[18] #define Agu1 state[19] #define Aka0 state[20] #define Aka1 state[21] #define Ake0 state[22] #define Ake1 state[23] #define Aki0 state[24] #define Aki1 state[25] #define Ako0 state[26] #define Ako1 state[27] #define Aku0 state[28] #define Aku1 state[29] #define Ama0 state[30] #define Ama1 state[31] #define Ame0 state[32] #define Ame1 state[33] #define Ami0 state[34] #define Ami1 state[35] #define Amo0 state[36] #define Amo1 state[37] #define Amu0 state[38] #define Amu1 state[39] #define Asa0 state[40] #define Asa1 state[41] #define Ase0 state[42] #define Ase1 state[43] #define Asi0 state[44] #define Asi1 state[45] #define Aso0 state[46] #define Aso1 state[47] #define Asu0 state[48] #define Asu1 state[49] for (i = 0; i < KECCAK_ROUNDS*2; i += 8) { /* Code for 4 rounds */ KeccakAtoD_round0(); Ba = (Aba0^Da0); Be = rol32((Age0^De0), 22); Bi = rol32((Aki1^Di1), 22); Bo = rol32((Amo1^Do1), 11); Bu = rol32((Asu0^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+0]; Age0 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Age1^De1), 22); Bi = rol32((Aki0^Di0), 21); Bo = rol32((Amo0^Do0), 10); Bu = rol32((Asu1^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+1]; Age1 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Bi = rol32((Aka1^Da1), 2); Bo = rol32((Ame1^De1), 23); Bu = rol32((Asi1^Di1), 31); Ba = rol32((Abo0^Do0), 14); Be = rol32((Agu0^Du0), 10); Aka1 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Aka0^Da0), 1); Bo = rol32((Ame0^De0), 22); Bu = rol32((Asi0^Di0), 30); Ba = rol32((Abo1^Do1), 14); Be = rol32((Agu1^Du1), 10); Aka0 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Asa0^Da0), 9); Ba = rol32((Abe1^De1), 1); Be = rol32((Agi0^Di0), 3); Bi = rol32((Ako1^Do1), 13); Bo = rol32((Amu0^Du0), 4); Asa0 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Bu = rol32((Asa1^Da1), 9); Ba = (Abe0^De0); Be = rol32((Agi1^Di1), 3); Bi = rol32((Ako0^Do0), 12); Bo = rol32((Amu1^Du1), 4); Asa1 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Be = rol32((Aga0^Da0), 18); Bi = rol32((Ake0^De0), 5); Bo = rol32((Ami1^Di1), 8); Bu = rol32((Aso0^Do0), 28); Ba = rol32((Abu1^Du1), 14); Aga0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Be = rol32((Aga1^Da1), 18); Bi = rol32((Ake1^De1), 5); Bo = rol32((Ami0^Di0), 7); Bu = rol32((Aso1^Do1), 28); Ba = rol32((Abu0^Du0), 13); Aga1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Bo = rol32((Ama1^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Abi0^Di0), 31); Be = rol32((Ago1^Do1), 28); Bi = rol32((Aku1^Du1), 20); Ama1 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Bo = rol32((Ama0^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Abi1^Di1), 31); Be = rol32((Ago0^Do0), 27); Bi = rol32((Aku0^Du0), 19); Ama0 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); KeccakAtoD_round1(); Ba = (Aba0^Da0); Be = rol32((Ame1^De0), 22); Bi = rol32((Agi1^Di1), 22); Bo = rol32((Aso1^Do1), 11); Bu = rol32((Aku1^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+2]; Ame1 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Ame0^De1), 22); Bi = rol32((Agi0^Di0), 21); Bo = rol32((Aso0^Do0), 10); Bu = rol32((Aku0^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+3]; Ame0 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Bi = rol32((Asa1^Da1), 2); Bo = rol32((Ake1^De1), 23); Bu = rol32((Abi1^Di1), 31); Ba = rol32((Amo1^Do0), 14); Be = rol32((Agu0^Du0), 10); Asa1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Asa0^Da0), 1); Bo = rol32((Ake0^De0), 22); Bu = rol32((Abi0^Di0), 30); Ba = rol32((Amo0^Do1), 14); Be = rol32((Agu1^Du1), 10); Asa0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Ama1^Da0), 9); Ba = rol32((Age1^De1), 1); Be = rol32((Asi1^Di0), 3); Bi = rol32((Ako0^Do1), 13); Bo = rol32((Abu1^Du0), 4); Ama1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Bu = rol32((Ama0^Da1), 9); Ba = (Age0^De0); Be = rol32((Asi0^Di1), 3); Bi = rol32((Ako1^Do0), 12); Bo = rol32((Abu0^Du1), 4); Ama0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Be = rol32((Aka1^Da0), 18); Bi = rol32((Abe1^De0), 5); Bo = rol32((Ami0^Di1), 8); Bu = rol32((Ago1^Do0), 28); Ba = rol32((Asu1^Du1), 14); Aka1 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Be = rol32((Aka0^Da1), 18); Bi = rol32((Abe0^De1), 5); Bo = rol32((Ami1^Di0), 7); Bu = rol32((Ago0^Do1), 28); Ba = rol32((Asu0^Du0), 13); Aka0 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Bo = rol32((Aga1^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Aki1^Di0), 31); Be = rol32((Abo1^Do1), 28); Bi = rol32((Amu1^Du1), 20); Aga1 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Bo = rol32((Aga0^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Aki0^Di1), 31); Be = rol32((Abo0^Do0), 27); Bi = rol32((Amu0^Du0), 19); Aga0 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); KeccakAtoD_round2(); Ba = (Aba0^Da0); Be = rol32((Ake1^De0), 22); Bi = rol32((Asi0^Di1), 22); Bo = rol32((Ago0^Do1), 11); Bu = rol32((Amu1^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+4]; Ake1 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Ake0^De1), 22); Bi = rol32((Asi1^Di0), 21); Bo = rol32((Ago1^Do0), 10); Bu = rol32((Amu0^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+5]; Ake0 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Bi = rol32((Ama0^Da1), 2); Bo = rol32((Abe0^De1), 23); Bu = rol32((Aki0^Di1), 31); Ba = rol32((Aso1^Do0), 14); Be = rol32((Agu0^Du0), 10); Ama0 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Ama1^Da0), 1); Bo = rol32((Abe1^De0), 22); Bu = rol32((Aki1^Di0), 30); Ba = rol32((Aso0^Do1), 14); Be = rol32((Agu1^Du1), 10); Ama1 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aga1^Da0), 9); Ba = rol32((Ame0^De1), 1); Be = rol32((Abi1^Di0), 3); Bi = rol32((Ako1^Do1), 13); Bo = rol32((Asu1^Du0), 4); Aga1 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aga0^Da1), 9); Ba = (Ame1^De0); Be = rol32((Abi0^Di1), 3); Bi = rol32((Ako0^Do0), 12); Bo = rol32((Asu0^Du1), 4); Aga0 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Be = rol32((Asa1^Da0), 18); Bi = rol32((Age1^De0), 5); Bo = rol32((Ami1^Di1), 8); Bu = rol32((Abo1^Do0), 28); Ba = rol32((Aku0^Du1), 14); Asa1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Be = rol32((Asa0^Da1), 18); Bi = rol32((Age0^De1), 5); Bo = rol32((Ami0^Di0), 7); Bu = rol32((Abo0^Do1), 28); Ba = rol32((Aku1^Du0), 13); Asa0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Bo = rol32((Aka0^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Agi1^Di0), 31); Be = rol32((Amo0^Do1), 28); Bi = rol32((Abu0^Du1), 20); Aka0 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Bo = rol32((Aka1^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Agi0^Di1), 31); Be = rol32((Amo1^Do0), 27); Bi = rol32((Abu1^Du0), 19); Aka1 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); KeccakAtoD_round3(); Ba = (Aba0^Da0); Be = rol32((Abe0^De0), 22); Bi = rol32((Abi0^Di1), 22); Bo = rol32((Abo0^Do1), 11); Bu = rol32((Abu0^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+6]; Abe0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Abe1^De1), 22); Bi = rol32((Abi1^Di0), 21); Bo = rol32((Abo1^Do0), 10); Bu = rol32((Abu1^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+7]; Abe1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Bi = rol32((Aga0^Da1), 2); Bo = rol32((Age0^De1), 23); Bu = rol32((Agi0^Di1), 31); Ba = rol32((Ago0^Do0), 14); Be = rol32((Agu0^Du0), 10); Aga0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Aga1^Da0), 1); Bo = rol32((Age1^De0), 22); Bu = rol32((Agi1^Di0), 30); Ba = rol32((Ago1^Do1), 14); Be = rol32((Agu1^Du1), 10); Aga1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aka0^Da0), 9); Ba = rol32((Ake0^De1), 1); Be = rol32((Aki0^Di0), 3); Bi = rol32((Ako0^Do1), 13); Bo = rol32((Aku0^Du0), 4); Aka0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Bu = rol32((Aka1^Da1), 9); Ba = (Ake1^De0); Be = rol32((Aki1^Di1), 3); Bi = rol32((Ako1^Do0), 12); Bo = rol32((Aku1^Du1), 4); Aka1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Be = rol32((Ama0^Da0), 18); Bi = rol32((Ame0^De0), 5); Bo = rol32((Ami0^Di1), 8); Bu = rol32((Amo0^Do0), 28); Ba = rol32((Amu0^Du1), 14); Ama0 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Be = rol32((Ama1^Da1), 18); Bi = rol32((Ame1^De1), 5); Bo = rol32((Ami1^Di0), 7); Bu = rol32((Amo1^Do1), 28); Ba = rol32((Amu1^Du0), 13); Ama1 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Bo = rol32((Asa0^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Asi0^Di0), 31); Be = rol32((Aso0^Do1), 28); Bi = rol32((Asu0^Du1), 20); Asa0 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Bo = rol32((Asa1^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Asi1^Di1), 31); Be = rol32((Aso1^Do0), 27); Bi = rol32((Asu1^Du0), 19); Asa1 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); } } static void xor_lane(struct KeccakContext *ctx, int lane, uint64_t val) { uint32_t x0, x1, t; uint32_t *dst = ctx->u.state32 + lane*2; x0 = val; t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); x1 = val >> 32; t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); dst[0] ^= (x0 & 0x0000FFFF) | (x1 << 16); dst[1] ^= (x0 >> 16) | (x1 & 0xFFFF0000); } static void extract(uint8_t *dst, const struct KeccakContext *ctx, int startLane, int laneCount) { const uint32_t *src = ctx->u.state32 + startLane * 2; uint32_t t, x0, x1; while (laneCount--) { x0 = *src++; x1 = *src++; t = (x0 & 0x0000FFFF) | (x1 << 16); x1 = (x0 >> 16) | (x1 & 0xFFFF0000); x0 = t; t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); le32enc(dst + 0, x0); le32enc(dst + 4, x1); dst += 8; } } #endif /* KECCAK_32BIT */ /* * Common code */ static void xor_byte(struct KeccakContext *ctx, int nbyte, uint8_t val) { int o = nbyte / 8; int s = (nbyte % 8) * 8; xor_lane(ctx, o, (uint64_t)(val) << s); } static void add_bytes(struct KeccakContext *ctx, const uint8_t *p, unsigned int ofs, unsigned int len) { uint64_t w; unsigned int m = ofs % 8; /* partial word */ if (m) { m = 8 - m; if (m > len) m = len; while (m--) { xor_byte(ctx, ofs++, *p++); len--; } } /* full words */ while (len >= 8) { w = le64dec(p); xor_lane(ctx, ofs / 8, w); ofs += 8; p += 8; len -= 8; } /* partial word */ while (len--) xor_byte(ctx, ofs++, *p++); } static void extract_bytes(struct KeccakContext *ctx, uint8_t *dst, unsigned int ofs, unsigned int count) { uint8_t lanebuf[8]; unsigned int n, avail; if (ofs % 8 != 0 || count < 8) { avail = 8 - ofs % 8; n = (avail > count) ? count : avail; extract(lanebuf, ctx, ofs/8, 1); memcpy(dst, lanebuf + ofs%8, n); dst += n; ofs += n; count -= n; } if (count > 8) { n = count / 8; extract(dst, ctx, ofs/8, n); dst += n*8; ofs += n*8; count -= n*8; } if (count > 0) { extract(lanebuf, ctx, ofs/8, 1); memcpy(dst, lanebuf, count); } memset(lanebuf, 0, sizeof(lanebuf)); } static inline void permute_if_needed(struct KeccakContext *ctx) { if (ctx->pos == ctx->rbytes) { keccak_f(ctx); ctx->pos = 0; } } /* * Public API */ int keccak_init(struct KeccakContext *ctx, unsigned int capacity) { if (capacity % 8 != 0 || capacity < 8 || capacity > (1600 - 8)) return 0; memset(ctx, 0, sizeof(struct KeccakContext)); ctx->rbytes = (1600 - capacity) / 8; return 1; } void keccak_absorb(struct KeccakContext *ctx, const void *data, size_t len) { unsigned int n, avail; const uint8_t *src = data; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; add_bytes(ctx, src, ctx->pos, n); src += n; len -= n; ctx->pos += n; permute_if_needed(ctx); } } void keccak_squeeze(struct KeccakContext *ctx, uint8_t *dst, size_t len) { unsigned int avail, n; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_squeeze_xor(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail, i; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); for (i = 0; i < n; i++) dst[i] ^= src[i]; ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_encrypt(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; add_bytes(ctx, src, ctx->pos, n); extract_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_decrypt(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail, i; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); for (i = 0; i < n; i++) dst[i] ^= src[i]; add_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_pad(struct KeccakContext *ctx, const void *pad, size_t len) { const uint8_t *src = pad; if (len > 0) { if (len > 1) { keccak_absorb(ctx, src, len - 1); src += len - 1; } xor_byte(ctx, ctx->pos, src[0]); xor_byte(ctx, ctx->rbytes - 1, 0x80); } keccak_f(ctx); ctx->pos = 0; } void keccak_rewind(struct KeccakContext *ctx) { ctx->pos = 0; } void keccak_forget(struct KeccakContext *ctx) { unsigned int rem = ctx->rbytes % 8; uint8_t buf[8]; memset(ctx->u.state32, 0, ctx->rbytes - rem); if (rem) { extract_bytes(ctx, buf, ctx->rbytes - rem, rem); add_bytes(ctx, buf, ctx->rbytes - rem, rem); memset(buf, 0, sizeof(buf)); } ctx->pos = 0; } pgbouncer-1.24.1/lib/usual/crypto/chacha.c0000644000175000000000000001051214777762223015301 00000000000000/* * ChaCha cipher. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on: chacha-ref.c version 20080118 / D. J. Bernstein / Public domain. */ #include #include #include #define CHACHA_ROUNDS 20 #define QUARTERROUND(in, out, a, b, c, d) \ do { \ out[a] = in[a] + in[b]; out[d] = rol32(in[d] ^ out[a], 16); \ out[c] = in[c] + out[d]; out[b] = rol32(in[b] ^ out[c], 12); \ out[a] = out[a] + out[b]; out[d] = rol32(out[d] ^ out[a], 8); \ out[c] = out[c] + out[d]; out[b] = rol32(out[b] ^ out[c], 7); \ } while (0) #define OUTPUT(a,b,c,d) \ do { \ output[a] = htole32(x[a] + input[a]); \ output[b] = htole32(x[b] + input[b]); \ output[c] = htole32(x[c] + input[c]); \ output[d] = htole32(x[d] + input[d]); \ } while (0) /* mix full state. needs 2 call sites to avoid inlining */ static void chacha_mix(struct ChaCha *ctx) { const uint32_t *input = ctx->state; uint32_t *output = ctx->u.output32; int i; uint32_t x[16]; /* first "column" round */ QUARTERROUND(input, x, 0, 4, 8, 12); QUARTERROUND(input, x, 1, 5, 9, 13); QUARTERROUND(input, x, 2, 6, 10, 14); QUARTERROUND(input, x, 3, 7, 11, 15); for (i = 0; i < CHACHA_ROUNDS/2 - 1; i++) { /* "diagonal" round */ QUARTERROUND(x, x, 0, 5, 10, 15); QUARTERROUND(x, x, 1, 6, 11, 12); QUARTERROUND(x, x, 2, 7, 8, 13); QUARTERROUND(x, x, 3, 4, 9, 14); /* "column" round */ QUARTERROUND(x, x, 0, 4, 8, 12); QUARTERROUND(x, x, 1, 5, 9, 13); QUARTERROUND(x, x, 2, 6, 10, 14); QUARTERROUND(x, x, 3, 7, 11, 15); } /* last "diagonal" round */ QUARTERROUND(x, x, 0, 5, 10, 15); OUTPUT(0, 5, 10, 15); QUARTERROUND(x, x, 1, 6, 11, 12); OUTPUT(1, 6, 11, 12); QUARTERROUND(x, x, 2, 7, 8, 13); OUTPUT(2, 7, 8, 13); QUARTERROUND(x, x, 3, 4, 9, 14); OUTPUT(3, 4, 9, 14); ctx->pos = 0; ctx->state[12]++; if (!ctx->state[12]) ctx->state[13]++; } void chacha_set_key_256(struct ChaCha *ctx, const void *key) { unsigned int i; memcpy(&ctx->state[0], "expand 32-byte k", 16); memcpy(&ctx->state[4], key, 32); for (i = 0; i < 12; i++) ctx->state[i] = le32toh(ctx->state[i]); ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_set_key_128(struct ChaCha *ctx, const void *key) { unsigned int i; memcpy(&ctx->state[0], "expand 16-byte k", 16); memcpy(&ctx->state[4], key, 16); memcpy(&ctx->state[8], key, 16); for (i = 0; i < 12; i++) ctx->state[i] = le32toh(ctx->state[i]); ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv) { const uint8_t *_iv = iv; ctx->state[12] = counter_low; ctx->state[13] = counter_high; if (_iv) { ctx->state[14] = le32dec(_iv); ctx->state[15] = le32dec(_iv + 4); } ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes) { unsigned int n, avail; const uint8_t *ks = ctx->u.output8; uint8_t *dst = stream; while (bytes > 0) { if (ctx->pos >= CHACHA_BLOCK_SIZE) chacha_mix(ctx); avail = CHACHA_BLOCK_SIZE - ctx->pos; n = (bytes > avail) ? avail : bytes; memcpy(dst, ks + ctx->pos, n); bytes -= n; dst += n; ctx->pos += n; } } void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes) { unsigned int i, n, avail; const uint8_t *ks = ctx->u.output8; const uint8_t *src = plain; uint8_t *dst = encrypted; while (bytes > 0) { if (ctx->pos >= CHACHA_BLOCK_SIZE) chacha_mix(ctx); avail = CHACHA_BLOCK_SIZE - ctx->pos; n = (bytes > avail) ? avail : bytes; for (i = 0; i < n; i++) dst[i] = src[i] ^ ks[i]; bytes -= n; dst += n; src += n; ctx->pos += n; } } pgbouncer-1.24.1/lib/usual/crypto/md5.h0000644000175000000000000000263414777762223014572 00000000000000/* * MD5 implementation based on RFC1321. * * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * MD5 cryptographic hash. */ #ifndef _USUAL_CRYPTO_MD5_H_ #define _USUAL_CRYPTO_MD5_H_ #include /** Block length for MD5 */ #define MD5_BLOCK_LENGTH 64 /** Result length for MD5 */ #define MD5_DIGEST_LENGTH 16 /** MD5 state */ struct md5_ctx { uint64_t nbytes; uint32_t a, b, c, d; uint32_t buf[16]; }; /** Clean state */ void md5_reset(struct md5_ctx *ctx); /** Update state with more data */ void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len); /** Get final result */ void md5_final(struct md5_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.24.1/lib/usual/crypto/sha1.h0000644000175000000000000000264514777762223014743 00000000000000/* * SHA1 implementation based on RFC3174. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA1 implementation. */ #ifndef _USUAL_CRYPTO_SHA1_H_ #define _USUAL_CRYPTO_SHA1_H_ #include /** Block length for SHA1 */ #define SHA1_BLOCK_SIZE 64 /** Result length for SHA1 */ #define SHA1_DIGEST_LENGTH 20 /** SHA1 state */ struct sha1_ctx { uint64_t nbytes; uint32_t a, b, c, d, e; uint32_t buf[SHA1_BLOCK_SIZE / 4]; }; /** Clean state */ void sha1_reset(struct sha1_ctx *ctx); /** Update state with more data */ void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len); /** Get final result */ void sha1_final(struct sha1_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.24.1/lib/usual/crypto/csrandom.c0000644000175000000000000000644614777762223015713 00000000000000/* * Cryptographically Secure Randomness. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_ARC4RANDOM_BUF /* * Simply wrap arc4random_buf() API. */ uint32_t csrandom(void) { return arc4random(); } void csrandom_bytes(void *buf, size_t nbytes) { arc4random_buf(buf, nbytes); } uint32_t csrandom_range(uint32_t upper_bound) { return arc4random_uniform(upper_bound); } #else /* !HAVE_ARC4RANDOM_BUF */ #define USE_KECCAK #ifdef USE_KECCAK /* * Keccak-based PRNG. */ static struct KeccakPRNG prng_keccak; static void impl_init(void) { char buf[32]; if (getentropy(buf, sizeof(buf)) != 0) errx(1, "Cannot get system entropy"); if (!keccak_prng_init(&prng_keccak, 576)) errx(1, "Cannot initialize PRNG"); keccak_prng_add_data(&prng_keccak, buf, sizeof(buf)); explicit_bzero(buf, sizeof(buf)); } static void impl_extract(void *buf, size_t nbytes) { keccak_prng_extract(&prng_keccak, buf, nbytes); } #else /* * ChaCha-based PRNG. */ static struct ChaCha prng_chacha; static void impl_init(void) { uint8_t buf[CHACHA_KEY_SIZE + CHACHA_IV_SIZE]; if (getentropy(buf, sizeof(buf)) != 0) errx(1, "Cannot get system entropy"); chacha_set_key_256(&prng_chacha, buf); chacha_set_nonce(&prng_chacha, 0, 0, buf + CHACHA_KEY_SIZE); explicit_bzero(buf, sizeof(buf)); } static void impl_extract(void *buf, size_t nbytes) { chacha_keystream(&prng_chacha, buf, nbytes); } #endif /* * Locking */ static pid_t last_pid = -1; static void prng_lock(void) { } static void prng_unlock(void) { } /* * Make sure state is initialized. */ static void prng_check_and_lock(void) { bool reseed = false; pid_t new_pid; prng_lock(); new_pid = getpid(); if (new_pid != last_pid) { reseed = true; last_pid = new_pid; } if (reseed) impl_init(); } /* * Public API follows */ void csrandom_bytes(void *buf, size_t nbytes) { prng_check_and_lock(); impl_extract(buf, nbytes); prng_unlock(); } uint32_t csrandom(void) { uint32_t val; csrandom_bytes(&val, sizeof(val)); return val; } uint32_t csrandom_range(uint32_t upper_bound) { uint32_t mod, lim, val; if (upper_bound <= 1) return 0; /* 2**32 % x == (2**32 - x) % x */ mod = -upper_bound % upper_bound; /* wait for value in range [0 .. 2**32-mod) */ lim = -mod; /* loop until good value appears */ while (1) { val = csrandom(); if (val < lim || lim == 0) return val % upper_bound; } } #endif /* !HAVE_ARC4RANDOM_BUF */ pgbouncer-1.24.1/lib/usual/crypto/sha512.h0000644000175000000000000000372514777762223015112 00000000000000/* * SHA2-512 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA512 and SHA384 cryptographic hashes. */ #ifndef _USUAL_CRYPTO_SHA512_H_ #define _USUAL_CRYPTO_SHA512_H_ #include /** SHA384 block size in bytes */ #define SHA384_BLOCK_SIZE (16*8) /** SHA512 block size in bytes */ #define SHA512_BLOCK_SIZE (16*8) /** SHA384 result length in bytes */ #define SHA384_DIGEST_LENGTH (384/8) /** SHA512 result length in bytes */ #define SHA512_DIGEST_LENGTH (512/8) /** * State structure for both SHA512 and SHA384. */ struct sha512_ctx { union { uint64_t words[16]; uint8_t raw[16 * 8]; } buf; uint64_t state[8]; uint64_t nbytes; }; /** Initialize structure for SHA512 */ void sha512_reset(struct sha512_ctx *ctx); /** Process more data */ void sha512_update(struct sha512_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha512_final(struct sha512_ctx *ctx, uint8_t *dst); /** Initialize structure for SHA384 */ void sha384_reset(struct sha512_ctx *ctx); /** Process more data */ void sha384_update(struct sha512_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha384_final(struct sha512_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.24.1/lib/usual/crypto/keccak_prng.c0000644000175000000000000000304014777762223016337 00000000000000/* * PRNG based on Keccak. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include bool keccak_prng_init(struct KeccakPRNG *prng, int capacity) { if (!keccak_init(&prng->ctx, capacity)) return false; prng->extracting = false; prng->have_data = false; return true; } void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len) { if (prng->extracting) { keccak_rewind(&prng->ctx); prng->extracting = false; } keccak_absorb(&prng->ctx, data, len); if (!prng->have_data && len > 0) prng->have_data = true; } bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len) { if (!prng->have_data) return false; if (!prng->extracting) { keccak_pad(&prng->ctx, "\x01", 1); prng->extracting = true; } keccak_squeeze(&prng->ctx, data, len); return true; } pgbouncer-1.24.1/lib/usual/crypto/sha256.h0000644000175000000000000000372514777762223015117 00000000000000/* * SHA2-256 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA256 and SHA224 cryptographic hashes. */ #ifndef _USUAL_CRYPTO_SHA256_H_ #define _USUAL_CRYPTO_SHA256_H_ #include /** SHA224 block size in bytes */ #define SHA224_BLOCK_SIZE (16*4) /** SHA256 block size in bytes */ #define SHA256_BLOCK_SIZE (16*4) /** SHA224 result length in bytes */ #define SHA224_DIGEST_LENGTH (224/8) /** SHA256 result length in bytes */ #define SHA256_DIGEST_LENGTH (256/8) /** * State structure for both SHA256 and SHA224. */ struct sha256_ctx { union { uint32_t words[16]; uint8_t raw[16 * 4]; } buf; uint32_t state[8]; uint64_t nbytes; }; /** Initialize structure for SHA256 */ void sha256_reset(struct sha256_ctx *ctx); /** Process more data */ void sha256_update(struct sha256_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha256_final(struct sha256_ctx *ctx, uint8_t *dst); /** Initialize structure for SHA224 */ void sha224_reset(struct sha256_ctx *ctx); /** Process more data */ void sha224_update(struct sha256_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha224_final(struct sha256_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.24.1/lib/usual/crypto/keccak_prng.h0000644000175000000000000000330514777762223016350 00000000000000/* * PRNG based on Keccak. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Implements PRNG mode for Keccak sponge function. */ #ifndef _USUAL_CRYPTO_KECCAK_PRNG_H_ #define _USUAL_CRYPTO_KECCAK_PRNG_H_ #include /** * State structure. */ struct KeccakPRNG { struct KeccakContext ctx; bool extracting; bool have_data; }; /** * Setup Keccak with specified capacity. * * @param prng State structure to be initialized. * @param capacity Keccak capacity in bits. * @return False if invalid capacity, true otherwise. */ bool keccak_prng_init(struct KeccakPRNG *prng, int capacity); /** * Merge entropy data into state. */ void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len); /** * Extract PRNG bytes from state. * * @return True, if extraction was successful. False if state has not been initialzed with keccak_prng_add_data(). */ bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len); #endif pgbouncer-1.24.1/lib/usual/crypto/md5.c0000644000175000000000000001304214777762223014560 00000000000000/* * MD5 implementation based on RFC1321. * * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* * Support functions. */ #define bufpos(ctx) ((ctx)->nbytes & (MD5_BLOCK_LENGTH - 1)) static inline void swap_words(uint32_t *w, int n) { #ifdef WORDS_BIGENDIAN for (; n > 0; w++, n--) *w = le32toh(*w); #endif } /* * MD5 core. */ #define F(X,Y,Z) ((X & Y) | ((~X) & Z)) #define G(X,Y,Z) ((X & Z) | (Y & (~Z))) #define H(X,Y,Z) (X ^ Y ^ Z) #define I(X,Y,Z) (Y ^ (X | (~Z))) #define OP(fn, a, b, c, d, k, s, T_i) \ a = b + rol32(a + fn(b, c, d) + X[k] + T_i, s) static void md5_mix(struct md5_ctx *ctx, const uint32_t *X) { uint32_t a, b, c, d; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; /* Round 1. */ OP(F, a, b, c, d, 0, 7, 0xd76aa478); OP(F, d, a, b, c, 1, 12, 0xe8c7b756); OP(F, c, d, a, b, 2, 17, 0x242070db); OP(F, b, c, d, a, 3, 22, 0xc1bdceee); OP(F, a, b, c, d, 4, 7, 0xf57c0faf); OP(F, d, a, b, c, 5, 12, 0x4787c62a); OP(F, c, d, a, b, 6, 17, 0xa8304613); OP(F, b, c, d, a, 7, 22, 0xfd469501); OP(F, a, b, c, d, 8, 7, 0x698098d8); OP(F, d, a, b, c, 9, 12, 0x8b44f7af); OP(F, c, d, a, b, 10, 17, 0xffff5bb1); OP(F, b, c, d, a, 11, 22, 0x895cd7be); OP(F, a, b, c, d, 12, 7, 0x6b901122); OP(F, d, a, b, c, 13, 12, 0xfd987193); OP(F, c, d, a, b, 14, 17, 0xa679438e); OP(F, b, c, d, a, 15, 22, 0x49b40821); /* Round 2. */ OP(G, a, b, c, d, 1, 5, 0xf61e2562); OP(G, d, a, b, c, 6, 9, 0xc040b340); OP(G, c, d, a, b, 11, 14, 0x265e5a51); OP(G, b, c, d, a, 0, 20, 0xe9b6c7aa); OP(G, a, b, c, d, 5, 5, 0xd62f105d); OP(G, d, a, b, c, 10, 9, 0x02441453); OP(G, c, d, a, b, 15, 14, 0xd8a1e681); OP(G, b, c, d, a, 4, 20, 0xe7d3fbc8); OP(G, a, b, c, d, 9, 5, 0x21e1cde6); OP(G, d, a, b, c, 14, 9, 0xc33707d6); OP(G, c, d, a, b, 3, 14, 0xf4d50d87); OP(G, b, c, d, a, 8, 20, 0x455a14ed); OP(G, a, b, c, d, 13, 5, 0xa9e3e905); OP(G, d, a, b, c, 2, 9, 0xfcefa3f8); OP(G, c, d, a, b, 7, 14, 0x676f02d9); OP(G, b, c, d, a, 12, 20, 0x8d2a4c8a); /* Round 3. */ OP(H, a, b, c, d, 5, 4, 0xfffa3942); OP(H, d, a, b, c, 8, 11, 0x8771f681); OP(H, c, d, a, b, 11, 16, 0x6d9d6122); OP(H, b, c, d, a, 14, 23, 0xfde5380c); OP(H, a, b, c, d, 1, 4, 0xa4beea44); OP(H, d, a, b, c, 4, 11, 0x4bdecfa9); OP(H, c, d, a, b, 7, 16, 0xf6bb4b60); OP(H, b, c, d, a, 10, 23, 0xbebfbc70); OP(H, a, b, c, d, 13, 4, 0x289b7ec6); OP(H, d, a, b, c, 0, 11, 0xeaa127fa); OP(H, c, d, a, b, 3, 16, 0xd4ef3085); OP(H, b, c, d, a, 6, 23, 0x04881d05); OP(H, a, b, c, d, 9, 4, 0xd9d4d039); OP(H, d, a, b, c, 12, 11, 0xe6db99e5); OP(H, c, d, a, b, 15, 16, 0x1fa27cf8); OP(H, b, c, d, a, 2, 23, 0xc4ac5665); /* Round 4. */ OP(I, a, b, c, d, 0, 6, 0xf4292244); OP(I, d, a, b, c, 7, 10, 0x432aff97); OP(I, c, d, a, b, 14, 15, 0xab9423a7); OP(I, b, c, d, a, 5, 21, 0xfc93a039); OP(I, a, b, c, d, 12, 6, 0x655b59c3); OP(I, d, a, b, c, 3, 10, 0x8f0ccc92); OP(I, c, d, a, b, 10, 15, 0xffeff47d); OP(I, b, c, d, a, 1, 21, 0x85845dd1); OP(I, a, b, c, d, 8, 6, 0x6fa87e4f); OP(I, d, a, b, c, 15, 10, 0xfe2ce6e0); OP(I, c, d, a, b, 6, 15, 0xa3014314); OP(I, b, c, d, a, 13, 21, 0x4e0811a1); OP(I, a, b, c, d, 4, 6, 0xf7537e82); OP(I, d, a, b, c, 11, 10, 0xbd3af235); OP(I, c, d, a, b, 2, 15, 0x2ad7d2bb); OP(I, b, c, d, a, 9, 21, 0xeb86d391); ctx->a += a; ctx->b += b; ctx->c += c; ctx->d += d; } /* * Public API. */ void md5_reset(struct md5_ctx *ctx) { ctx->nbytes = 0; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; } void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *ptr = data; uint8_t *buf = (uint8_t *)ctx->buf; while (len > 0) { n = MD5_BLOCK_LENGTH - bufpos(ctx); if (n > len) n = len; memcpy(buf + bufpos(ctx), ptr, n); ptr += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) { swap_words(ctx->buf, 16); md5_mix(ctx, ctx->buf); } } } void md5_final(struct md5_ctx *ctx, uint8_t *dst) { static const uint8_t padding[MD5_BLOCK_LENGTH] = { 0x80 }; uint64_t final_len = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); /* add padding */ pad_len = MD5_BLOCK_LENGTH - 8 - pos; if (pad_len <= 0) pad_len += MD5_BLOCK_LENGTH; md5_update(ctx, padding, pad_len); /* add length directly */ swap_words(ctx->buf, 14); ctx->buf[14] = final_len; ctx->buf[15] = final_len >> 32; /* final result */ md5_mix(ctx, ctx->buf); le32enc(dst + 0, ctx->a); le32enc(dst + 4, ctx->b); le32enc(dst + 8, ctx->c); le32enc(dst + 12, ctx->d); } /* * DigestInfo */ static const struct DigestInfo md5 = { (DigestInitFunc *)md5_reset, (DigestUpdateFunc *)md5_update, (DigestFinalFunc *)md5_final, sizeof(struct md5_ctx), MD5_DIGEST_LENGTH, MD5_BLOCK_LENGTH }; const struct DigestInfo *digest_MD5(void) { return &md5; } pgbouncer-1.24.1/lib/usual/crypto/sha256.c0000644000175000000000000001327314777762223015111 00000000000000/* * SHA2-256 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R64(R, t) R16(R, t+0); R16(R, t+16); R16(R, t+32); R16(R, t+48); #define bufpos(ctx) ((ctx)->nbytes & (SHA256_BLOCK_SIZE - 1)) /* * initial values */ static const uint32_t H224[8] = { 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, }; static const uint32_t H256[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, }; /* * constants for mixing */ static const uint32_t K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; /* * mixing */ #define CH(x,y,z) ((x & y) ^ ((~x) & z)) #define MAJ(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) #define E0(x) (ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22)) #define E1(x) (ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25)) #define O0(x) (ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3)) #define O1(x) (ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10)) #define W(n) (ctx->buf.words[(n) & 15]) #define setW(n,v) W(n) = (v) #define SHA256_ROUND(_t) do { \ uint32_t tmp1, tmp2, t = (_t); \ if (t >= 16) { \ setW(t, O1(W(t - 2)) + W(t - 7) + O0(W(t - 15)) + W(t - 16)); \ } else { \ /* convert endianess on first go */ \ setW(t, be32toh(W(t))); \ } \ tmp1 = h + E1(e) + CH(e,f,g) + K[k_pos++] + W(t); \ tmp2 = E0(a) + MAJ(a,b,c); \ h = g; g = f; f = e; e = d + tmp1; d = c; c = b; b = a; a = tmp1 + tmp2; \ } while (0) /* * actual core */ static void sha256_core(struct sha256_ctx *ctx) { uint32_t *state = ctx->state; uint32_t a = state[0], b = state[1], c = state[2], d = state[3]; uint32_t e = state[4], f = state[5], g = state[6], h = state[7]; unsigned k_pos = 0; R16(SHA256_ROUND, 0); while (k_pos < 64) { R16(SHA256_ROUND, 16); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } /* * Public API for SHA256. */ void sha256_reset(struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H256, sizeof(H256)); } void sha256_update(struct sha256_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = ctx->buf.raw; while (len > 0) { n = SHA256_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha256_core(ctx); } } void sha256_final(struct sha256_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA256_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); int i; /* add padding */ pad_len = SHA256_BLOCK_SIZE - 8 - pos; if (pad_len <= 0) pad_len += SHA256_BLOCK_SIZE; sha256_update(ctx, padding, pad_len); /* add length */ ctx->buf.words[14] = htobe32(nbits >> 32); ctx->buf.words[15] = htobe32(nbits); /* final result */ sha256_core(ctx); for (i = 0; i < SHA256_DIGEST_LENGTH / 4; i++) be32enc(dst + i*4, ctx->state[i]); } /* * Public API for SHA224. */ void sha224_reset(struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H224, sizeof(H224)); } void sha224_update(struct sha256_ctx *ctx, const void *data, unsigned int len) { sha256_update(ctx, data, len); } void sha224_final(struct sha256_ctx *ctx, uint8_t *dst) { uint8_t buf[SHA256_DIGEST_LENGTH]; sha256_final(ctx, buf); memcpy(dst, buf, SHA224_DIGEST_LENGTH); memset(buf, 0, sizeof(buf)); } /* * DigestInfo */ const struct DigestInfo *digest_SHA224(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha224_reset, (DigestUpdateFunc *)sha224_update, (DigestFinalFunc *)sha224_final, sizeof(struct sha256_ctx), SHA224_DIGEST_LENGTH, SHA224_BLOCK_SIZE }; return &info; } const struct DigestInfo *digest_SHA256(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha256_reset, (DigestUpdateFunc *)sha256_update, (DigestFinalFunc *)sha256_final, sizeof(struct sha256_ctx), SHA256_DIGEST_LENGTH, SHA256_BLOCK_SIZE }; return &info; } pgbouncer-1.24.1/lib/usual/crypto/sha3.h0000644000175000000000000000710714777762223014743 00000000000000/* * SHA3 implementation. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA3 variants of Keccak. * * SHA3-X are fixed-length hashes, SHAKE is variable-length. */ #ifndef _USUAL_CRYPTO_SHA3_H_ #define _USUAL_CRYPTO_SHA3_H_ #include /** Keccak capacity area for SHA3-224, in bits */ #define SHA3_224_CAPACITY 448 /** Keccak capacity area for SHA3-256, in bits */ #define SHA3_256_CAPACITY 512 /** Keccak capacity area for SHA3-384, in bits */ #define SHA3_384_CAPACITY 768 /** Keccak capacity area for SHA3-512, in bits */ #define SHA3_512_CAPACITY 1024 /** Keccak capacity area for SHAKE128, in bits */ #define SHAKE128_CAPACITY 256 /** Keccak capacity area for SHAKE256, in bits */ #define SHAKE256_CAPACITY 512 /** Result length of SHA3-224, in bytes */ #define SHA3_224_DIGEST_LENGTH (224/8) /** Result length of SHA3-256, in bytes */ #define SHA3_256_DIGEST_LENGTH (256/8) /** Result length of SHA3-384, in bytes */ #define SHA3_384_DIGEST_LENGTH (384/8) /** Result length of SHA3-512, in bytes */ #define SHA3_512_DIGEST_LENGTH (512/8) /** Result length of SHAKE128, in bytes */ #define SHAKE128_DIGEST_LENGTH (256/8) /** Result length of SHAKE256, in bytes */ #define SHAKE256_DIGEST_LENGTH (512/8) /** Block size of SHA3-224, in bytes */ #define SHA3_224_BLOCK_SIZE ((1600 - SHA3_224_CAPACITY) / 8) /** Block size of SHA3-256, in bytes */ #define SHA3_256_BLOCK_SIZE ((1600 - SHA3_256_CAPACITY) / 8) /** Block size of SHA3-384, in bytes */ #define SHA3_384_BLOCK_SIZE ((1600 - SHA3_384_CAPACITY) / 8) /** Block size of SHA3-512, in bytes */ #define SHA3_512_BLOCK_SIZE ((1600 - SHA3_512_CAPACITY) / 8) /** Block size of SHAKE128, in bytes */ #define SHAKE128_BLOCK_SIZE ((1600 - SHAKE128_CAPACITY) / 8) /** Block size of SHAKE256, in bytes */ #define SHAKE256_BLOCK_SIZE ((1600 - SHAKE256_CAPACITY) / 8) /** * State structure. */ struct SHA3Context { struct KeccakContext kctx; bool padded; uint8_t pad; unsigned int obytes; }; /** Initialize state for SHA3-224 */ void sha3_224_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-256 */ void sha3_256_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-384 */ void sha3_384_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-512 */ void sha3_512_reset(struct SHA3Context *ctx); /** Process data, update state */ void sha3_update(struct SHA3Context *ctx, const void *ptr, unsigned len); /** Calculate final result */ void sha3_final(struct SHA3Context *ctx, void *dst); /** Initialize state for SHAKE128 */ void shake128_reset(struct SHA3Context *ctx); /** Initialize state for SHAKE256 */ void shake256_reset(struct SHA3Context *ctx); /** Process data, update state */ void shake_update(struct SHA3Context *ctx, const void *ptr, unsigned len); /** Output variable amount of result data */ void shake_extract(struct SHA3Context *ctx, void *dst, unsigned count); #endif pgbouncer-1.24.1/lib/usual/crypto/hmac.h0000644000175000000000000000303014777762223015004 00000000000000/* * HMAC implementation based on OpenBSD * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * HMAC-SHA1 implementation (RFC2104). */ #ifndef _USUAL_CRYPTO_HMAC_H_ #define _USUAL_CRYPTO_HMAC_H_ #include /** HMAC Context */ struct HMAC; /** Create context with key */ struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx); /** Free context */ void hmac_free(struct HMAC *ctx); /** Initialize context */ void hmac_reset(struct HMAC *ctx); /** Hash more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len); /** Get final result */ void hmac_final(struct HMAC *ctx, uint8_t *dst); unsigned hmac_block_len(struct HMAC *ctx); unsigned hmac_result_len(struct HMAC *ctx); #endif /* _USUAL_HMAC_H_ */ pgbouncer-1.24.1/lib/usual/crypto/hmac.c0000644000175000000000000000553714777762223015015 00000000000000/* * HMAC implementation based on OpenBSD hmac.c * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct HMAC { struct DigestContext *hash; CxMem *cx; uint8_t *ipad; uint8_t *opad; }; struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx) { struct DigestContext *hash; struct HMAC *hmac; unsigned bs = impl->block_len; unsigned i; /* load hash */ hash = digest_new(impl, cx); if (!hash) return NULL; /* struct setup */ hmac = cx_alloc0(cx, sizeof(struct HMAC) + 2*bs); if (!hmac) { digest_free(hash); return NULL; } hmac->hash = hash; hmac->cx = cx; hmac->ipad = (uint8_t *)(hmac + 1); hmac->opad = hmac->ipad + bs; /* copy key to pads */ if (key_len > bs) { digest_update(hash, key, key_len); digest_final(hash, hmac->ipad); digest_reset(hash); memcpy(hmac->opad, hmac->ipad, digest_result_len(hash)); } else { memcpy(hmac->ipad, key, key_len); memcpy(hmac->opad, key, key_len); } /* calculate pads */ for (i = 0; i < bs; i++) { hmac->ipad[i] ^= 0x36; hmac->opad[i] ^= 0x5c; } /* prepare for user data */ digest_update(hmac->hash, hmac->ipad, bs); return hmac; } /* Free context */ void hmac_free(struct HMAC *ctx) { digest_free(ctx->hash); cx_free(ctx->cx, ctx); } /* Clean HMAC state */ void hmac_reset(struct HMAC *ctx) { unsigned bs = digest_block_len(ctx->hash); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->ipad, bs); } /* Update HMAC state with more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len) { digest_update(ctx->hash, data, len); } /* Get final HMAC result */ void hmac_final(struct HMAC *ctx, uint8_t *dst) { unsigned bs = digest_block_len(ctx->hash); unsigned rs = digest_result_len(ctx->hash); digest_final(ctx->hash, dst); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->opad, bs); digest_update(ctx->hash, dst, rs); digest_final(ctx->hash, dst); } unsigned hmac_block_len(struct HMAC *ctx) { return digest_block_len(ctx->hash); } unsigned hmac_result_len(struct HMAC *ctx) { return digest_result_len(ctx->hash); } pgbouncer-1.24.1/lib/usual/crypto/keccak.h0000644000175000000000000000450514777762223015325 00000000000000/* * Keccak implementation. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Simple API to Keccak1600 permutation + sponge. */ #ifndef _USUAL_CRYPTO_KECCAK_H_ #define _USUAL_CRYPTO_KECCAK_H_ #include /** * Keccak state structure for all modes. */ struct KeccakContext { /* 5*5*64 bit state */ union { uint64_t state64[25]; uint32_t state32[2*25]; } u; uint32_t pos; /* current byte position in buffer */ uint32_t rbytes; /* rate (= block size) in bytes */ }; /** * Set up state with specified capacity. * * Returns 1 if successful, 0 if invalid capacity. */ int keccak_init(struct KeccakContext *ctx, unsigned int capacity); /** * Hash additional data. */ void keccak_absorb(struct KeccakContext *ctx, const void *data, size_t len); /** * Extract bytes from state. */ void keccak_squeeze(struct KeccakContext *ctx, uint8_t *dst, size_t len); /** * Extract bytes from state, XOR into data. */ void keccak_squeeze_xor(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * XOR data into state and return it. */ void keccak_encrypt(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * XOR state with data and return it. */ void keccak_decrypt(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * Hash pad suffix. */ void keccak_pad(struct KeccakContext *ctx, const void *data, size_t len); /** * Move internal position to start of buffer. * * Useful for PRNG/duplex modes. */ void keccak_rewind(struct KeccakContext *ctx); /** * Clear rate bits. */ void keccak_forget(struct KeccakContext *ctx); #endif pgbouncer-1.24.1/lib/usual/crypto/entropy.c0000644000175000000000000001127014777762223015574 00000000000000/* * Load entropy. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #if defined(HAVE_GETRANDOM) #include #elif defined(HAVE_LINUX_RANDOM_H) #include #include #endif /* * Load system entropy. */ #ifndef HAVE_GETENTROPY /* * win32 */ #if defined(_WIN32) || defined(_WIN64) #define HAVE_getentropy_win32 /* * Windows * * It's possible to get entropy via: * - CryptGenRandom. Uses RtlGenRandom, requires CryptoAPI. * - rand_s(). Uses RtlGenRandom, Requires VS2005 CRT, WindowsXP+. * Missing in mingw32, exists in mingw64. * - RtlGenRandom(). Internal func, no proper public definition. * There is broken def in that does not have NTAPI. * Need to link or load from advapi32.dll. */ typedef BOOLEAN APIENTRY (*rtlgenrandom_t)(void *, ULONG); static int getentropy_win32(void *dst, size_t len) { HMODULE lib; rtlgenrandom_t fn; int res = -1; lib = LoadLibrary("advapi32.dll"); if (lib) { fn = (rtlgenrandom_t)(void(*)(void))GetProcAddress(lib, "SystemFunction036"); if (fn && fn(dst, len)) res = 0; FreeLibrary(lib); } if (res < 0) errno = EIO; return res; } #endif /* WIN32 */ /* * Linux getrandom() */ #if defined(HAVE_GETRANDOM) || (defined(GRND_RANDOM) && defined(SYS_getrandom)) #define HAVE_getentropy_getrandom #ifndef HAVE_GETRANDOM static int getrandom(void *dst, size_t len, unsigned int flags) { return syscall(SYS_getrandom, dst, len, flags); } #endif static int getentropy_getrandom(void *dst, size_t len) { int res; retry: res = getrandom(dst, len, 0); if (res < 0) { if (errno == EINTR) goto retry; return -1; } if ((size_t)res == len) return 0; errno = EIO; return -1; } #endif /* getrandom */ /* * Generic /dev/urandom */ #ifndef HAVE_getentropy_win32 #define HAVE_getentropy_devrandom #include #include #include /* open and check device node */ static int open_devrandom(const char *dev) { int fd; int oflags = O_RDONLY; #ifdef O_CLOEXEC oflags |= O_CLOEXEC; #endif open_loop: fd = open(dev, oflags); if (fd == -1) { if (errno == EINTR) goto open_loop; return -1; } #ifndef O_CLOEXEC { int res; res = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); if (res != 0) goto fail; } #endif /* * Lightly verify that the device node looks sane */ { struct stat st; if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) goto fail; } #ifdef RNDGETENTCNT { int cnt; if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) goto fail; } #endif /* seems fine */ return fd; fail: close(fd); return -1; } /* * Read normal random devices under /dev. */ static const char *devlist[] = { "/dev/urandom", "/dev/random", NULL, }; static int getentropy_devrandom(void *dst, size_t bytes) { uint8_t *d = dst; size_t need = bytes; int fd, res; unsigned int i; for (i = 0; devlist[i]; i++) { reopen: fd = open_devrandom(devlist[i]); if (fd == -1) continue; while (need > 0) { res = read(fd, d, need); if (res > 0) { /* successful read */ need -= res; d += res; } else if (res == 0) { /* eof - open again */ close(fd); goto reopen; } else if (errno == EINTR) { /* signal - retry read */ } else { close(fd); /* random error, fail */ return -1; } } close(fd); return 0; } errno = EIO; return -1; } #endif /* devrandom */ /* * Export BSD-style getentropy(). */ int getentropy(void *dst, size_t bytes) { int res = -1; int old_errno = errno; if (bytes > 256) { errno = EIO; return res; } #ifdef HAVE_getentropy_win32 if (res != 0) { res = getentropy_win32(dst, bytes); } #endif #ifdef HAVE_getentropy_getrandom if (res != 0) { res = getentropy_getrandom(dst, bytes); } #endif #ifdef HAVE_getentropy_devrandom if (res != 0) { res = getentropy_devrandom(dst, bytes); } #endif if (res == 0) errno = old_errno; return res; } #endif /* !HAVE_GETENTROPY */ pgbouncer-1.24.1/lib/usual/crypto/csrandom.h0000644000175000000000000000231014777762223015702 00000000000000/* * Cryptographically Secure Randomness. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Cryptographically Secure Randomness. */ #ifndef _USUAL_CRYPTO_CSRANDOM_H_ #define _USUAL_CRYPTO_CSRANDOM_H_ #include /** * Return random uint32_t. */ uint32_t csrandom(void); /** * Return unsigned integer in range. */ uint32_t csrandom_range(uint32_t upper_bound); /** * Fill buffer with random bytes. */ void csrandom_bytes(void *buf, size_t nbytes); #endif pgbouncer-1.24.1/lib/usual/crypto/digest.h0000644000175000000000000000616714777762223015371 00000000000000/* * Common API for cryptographic digests. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Common API for cryptographic digests. */ #ifndef _USUAL_CRYPTO_DIGEST_H_ #define _USUAL_CRYPTO_DIGEST_H_ #include typedef void (DigestInitFunc)(void *ctx); typedef void (DigestUpdateFunc)(void *ctx, const void *, unsigned); typedef void (DigestFinalFunc)(void *ctx, uint8_t *); /** * Algoright info. */ struct DigestInfo { DigestInitFunc *init; DigestUpdateFunc *update; DigestFinalFunc *final; short state_len; short result_len; short block_len; }; /** * Algoright instance. */ struct DigestContext; /** * Allocate and initialize new algorithm instance. */ struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx); /** Hash more data */ void digest_update(struct DigestContext *ctx, const void *data, size_t len); /** * Get final result. * * To re-use same instance, digest_reset() must be called first. */ void digest_final(struct DigestContext *ctx, uint8_t *res); /** * Prepares instance for new data. */ void digest_reset(struct DigestContext *ctx); /** * Free instance. */ void digest_free(struct DigestContext *ctx); /** * Hash function block length in bytes. */ unsigned digest_block_len(struct DigestContext *ctx); /** * Hash function result length in bytes. */ unsigned digest_result_len(struct DigestContext *ctx); /* * Declare algorithm info's here instead per-also headers * to avoid unnecessary dependencies. */ /** MD5 message digest */ const struct DigestInfo *digest_MD5(void); /** SHA1 message digest */ const struct DigestInfo *digest_SHA1(void); /** SHA224 message digest */ const struct DigestInfo *digest_SHA224(void); /** SHA256 message digest */ const struct DigestInfo *digest_SHA256(void); /** SHA384 message digest */ const struct DigestInfo *digest_SHA384(void); /** SHA512 message digest */ const struct DigestInfo *digest_SHA512(void); /** SHA3-224 message digest */ const struct DigestInfo *digest_SHA3_224(void); /** SHA3-256 message digest */ const struct DigestInfo *digest_SHA3_256(void); /** SHA3-384 message digest */ const struct DigestInfo *digest_SHA3_384(void); /** SHA3-512 message digest */ const struct DigestInfo *digest_SHA3_512(void); /** SHAKE128 in regular digest mode */ const struct DigestInfo *digest_SHAKE128(void); /** SHAKE256 in regular digest mode */ const struct DigestInfo *digest_SHAKE256(void); #endif pgbouncer-1.24.1/lib/usual/crypto/entropy.h0000644000175000000000000000222414777762223015600 00000000000000/* * Load entropy from kernel. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Load entropy from OS. */ #ifndef _USUAL_CRYPTO_ENTROPY_H_ #define _USUAL_CRYPTO_ENTROPY_H_ #include #ifndef HAVE_GETENTROPY #define getentropy(dst, len) usual_getentropy(dst, len) /** * Fetch entropy from OS kernel. */ int getentropy(void *dst, size_t len); #endif /* !HAVE_GETENTROPY */ #endif /* _USUAL_CRYPTO_ENTROPY_H_ */ pgbouncer-1.24.1/lib/usual/crypto/sha3.c0000644000175000000000000001051614777762223014734 00000000000000/* * SHA3 implementation. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define PAD_SHA3 0x06 #define PAD_SHAKE 0x1f void sha3_224_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_224_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_224_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_256_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_256_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_256_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_384_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_384_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_384_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_512_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_512_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_512_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void shake128_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHAKE128_CAPACITY); ctx->padded = 0; ctx->obytes = SHAKE128_DIGEST_LENGTH; ctx->pad = PAD_SHAKE; } void shake256_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHAKE256_CAPACITY); ctx->padded = 0; ctx->obytes = SHAKE256_DIGEST_LENGTH; ctx->pad = PAD_SHAKE; } void sha3_update(struct SHA3Context *ctx, const void *ptr, unsigned len) { keccak_absorb(&ctx->kctx, ptr, len); } void sha3_final(struct SHA3Context *ctx, void *dst) { if (!ctx->padded) { keccak_pad(&ctx->kctx, &ctx->pad, 1); ctx->padded = 1; } keccak_squeeze(&ctx->kctx, dst, ctx->obytes); } void shake_update(struct SHA3Context *ctx, const void *ptr, unsigned len) { keccak_absorb(&ctx->kctx, ptr, len); } void shake_extract(struct SHA3Context *ctx, void *dst, unsigned count) { if (!ctx->padded) { keccak_pad(&ctx->kctx, &ctx->pad, 1); ctx->padded = 1; } keccak_squeeze(&ctx->kctx, dst, count); } /* * DigestInfo */ static const struct DigestInfo sha3_224_info = { (DigestInitFunc *)sha3_224_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_224_DIGEST_LENGTH, SHA3_224_BLOCK_SIZE }; static const struct DigestInfo sha3_256_info = { (DigestInitFunc *)sha3_256_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_256_DIGEST_LENGTH, SHA3_256_BLOCK_SIZE }; static const struct DigestInfo sha3_384_info = { (DigestInitFunc *)sha3_384_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_384_DIGEST_LENGTH, SHA3_384_BLOCK_SIZE }; static const struct DigestInfo sha3_512_info = { (DigestInitFunc *)sha3_512_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_512_DIGEST_LENGTH, SHA3_512_BLOCK_SIZE }; static const struct DigestInfo shake128_info = { (DigestInitFunc *)shake128_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHAKE128_DIGEST_LENGTH, SHAKE128_BLOCK_SIZE }; static const struct DigestInfo shake256_info = { (DigestInitFunc *)shake256_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHAKE256_DIGEST_LENGTH, SHAKE256_BLOCK_SIZE }; const struct DigestInfo *digest_SHA3_224(void) { return &sha3_224_info; } const struct DigestInfo *digest_SHA3_256(void) { return &sha3_256_info; } const struct DigestInfo *digest_SHA3_384(void) { return &sha3_384_info; } const struct DigestInfo *digest_SHA3_512(void) { return &sha3_512_info; } const struct DigestInfo *digest_SHAKE128(void) { return &shake128_info; } const struct DigestInfo *digest_SHAKE256(void) { return &shake256_info; } pgbouncer-1.24.1/lib/usual/dlfcn.h0000644000175000000000000000254114777762223013650 00000000000000/* * Dynamic library loading. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_DLFCN_H_ #define _USUAL_DLFCN_H_ #ifdef HAVE_DLFCN_H #include #elif defined(_WIN32) #define dlopen(a,b) usual_dlopen(a,b) #define dlsym(a,b) usual_dlsym(a,b) #define dlclose(a) usual_dlclose(a) #define dlerror(...) usual_dlerror(__VA_ARGS__) /* * win32: Minimal dlopen, dlsym, dlclose, dlerror compat. */ #define RTLD_LAZY 1 #define RTLD_NOW 2 void *dlopen(const char *fn, int flag); void *dlsym(void *hptr, const char *fname); int dlclose(void *hptr); const char *dlerror(void); #endif /* _WIN32 */ #endif /* !_USUAL_DLFCN_H_ */ pgbouncer-1.24.1/lib/usual/mbuf.h0000644000175000000000000001727514777762223013525 00000000000000 /** \file * Safe and easy access to memory buffer. */ #ifndef _USUAL_MBUF_H_ #define _USUAL_MBUF_H_ #include #include /** MBuf structure. Allocated by user, can be in stack. */ struct MBuf { uint8_t *data; unsigned read_pos; unsigned write_pos; unsigned alloc_len; bool reader; bool fixed; }; /** Format fragment for *printf() */ #define MBUF_FMT ".*s" /** Argument layout for *printf() */ #define MBUF_ARG(m) ((m) ? mbuf_written(m) : 6), ((m) ? (const char *)mbuf_data(m) : "(null)") /* * Init functions */ /** Initialize R/O buffer to fixed memory area. */ static inline void mbuf_init_fixed_reader(struct MBuf *buf, const void *ptr, unsigned len) { buf->data = (uint8_t *)ptr; buf->read_pos = 0; buf->write_pos = len; buf->alloc_len = len; buf->reader = true; buf->fixed = true; } /** Initialize R/W buffer to fixed memory area. */ static inline void mbuf_init_fixed_writer(struct MBuf *buf, void *ptr, unsigned len) { buf->data = (uint8_t *)ptr; buf->read_pos = 0; buf->write_pos = 0; buf->alloc_len = len; buf->reader = false; buf->fixed = true; } /** Initialize R/W buffer to dynamically allocated memory area. */ static inline void mbuf_init_dynamic(struct MBuf *buf) { buf->data = NULL; buf->read_pos = 0; buf->write_pos = 0; buf->alloc_len = 0; buf->reader = false; buf->fixed = false; } /** Free dynamically allocated area, if exists. */ static inline void mbuf_free(struct MBuf *buf) { if (buf->data) { if (!buf->fixed) free(buf->data); memset(buf, 0, sizeof(*buf)); } } /* * Reset functions. */ /** Move read cursor to start of buffer. */ static inline void mbuf_rewind_reader(struct MBuf *buf) { buf->read_pos = 0; } /** Move both read and write cursor to start of buffer. */ static inline void mbuf_rewind_writer(struct MBuf *buf) { if (!buf->reader) { buf->read_pos = 0; buf->write_pos = 0; } } /* * Info functions. */ /** How many bytes can be read with read cursor. */ static inline unsigned mbuf_avail_for_read(const struct MBuf *buf) { return buf->write_pos - buf->read_pos; } /** How many bytes can be written with write cursor, without realloc. */ static inline unsigned mbuf_avail_for_write(const struct MBuf *buf) { if (!buf->reader && buf->alloc_len > buf->write_pos) return buf->alloc_len - buf->write_pos; return 0; } /** How many data bytes are in buffer. */ static inline unsigned mbuf_written(const struct MBuf *buf) { return buf->write_pos; } /** How many bytes have been read from buffer */ static inline unsigned mbuf_consumed(const struct MBuf *buf) { return buf->read_pos; } /** Return pointer to data area. */ static inline void *mbuf_data(const struct MBuf *buf) { return buf->data; } /** Do the mbufs contain same data. */ static inline bool mbuf_eq(const struct MBuf *buf1, const struct MBuf *buf2) { if (buf1 == buf2) return true; if (!buf1 || !buf2 || (mbuf_written(buf1) != mbuf_written(buf2))) return false; return memcmp(mbuf_data(buf1), mbuf_data(buf2), mbuf_written(buf1)) == 0; } /** Complare mbuf to asciiz string */ static inline bool mbuf_eq_str(const struct MBuf *buf1, const char *s) { struct MBuf tmp; mbuf_init_fixed_reader(&tmp, s, strlen(s)); return mbuf_eq(buf1, &tmp); } /* * Read functions. */ /** Read a byte from read cursor. */ _MUSTCHECK static inline bool mbuf_get_byte(struct MBuf *buf, uint8_t *dst_p) { if (buf->read_pos + 1 > buf->write_pos) return false; *dst_p = buf->data[buf->read_pos++]; return true; } /** Read big-endian uint16 from read cursor. */ _MUSTCHECK static inline bool mbuf_get_char(struct MBuf *buf, char *dst_p) { if (buf->read_pos + 1 > buf->write_pos) return false; *dst_p = buf->data[buf->read_pos++]; return true; } _MUSTCHECK static inline bool mbuf_get_uint16be(struct MBuf *buf, uint16_t *dst_p) { unsigned a, b; if (buf->read_pos + 2 > buf->write_pos) return false; a = buf->data[buf->read_pos++]; b = buf->data[buf->read_pos++]; *dst_p = (a << 8) | b; return true; } /** Read big-endian uint32 from read cursor. */ _MUSTCHECK static inline bool mbuf_get_uint32be(struct MBuf *buf, uint32_t *dst_p) { unsigned a, b, c, d; if (buf->read_pos + 4 > buf->write_pos) return false; a = buf->data[buf->read_pos++]; b = buf->data[buf->read_pos++]; c = buf->data[buf->read_pos++]; d = buf->data[buf->read_pos++]; *dst_p = (a << 24) | (b << 16) | (c << 8) | d; return true; } /** Get reference to len bytes from read cursor. */ _MUSTCHECK static inline bool mbuf_get_uint64be(struct MBuf *buf, uint64_t *dst_p) { uint32_t a, b; if (!mbuf_get_uint32be(buf, &a) || !mbuf_get_uint32be(buf, &b)) return false; *dst_p = ((uint64_t)a << 32) | b; return true; } _MUSTCHECK static inline bool mbuf_get_bytes(struct MBuf *buf, unsigned len, const uint8_t **dst_p) { if (buf->read_pos + len > buf->write_pos) return false; *dst_p = buf->data + buf->read_pos; buf->read_pos += len; return true; } /** Get reference to asciiz string from read cursor. */ _MUSTCHECK static inline bool mbuf_get_chars(struct MBuf *buf, unsigned len, const char **dst_p) { if (buf->read_pos + len > buf->write_pos) return false; *dst_p = (char *)buf->data + buf->read_pos; buf->read_pos += len; return true; } _MUSTCHECK static inline bool mbuf_get_string(struct MBuf *buf, const char **dst_p) { const char *res = (char *)buf->data + buf->read_pos; const uint8_t *nul = memchr(res, 0, mbuf_avail_for_read(buf)); if (!nul) return false; *dst_p = res; buf->read_pos = nul + 1 - buf->data; return true; } /* * Write functions. */ /** Allocate more room if needed and the mbuf allows. */ _MUSTCHECK bool mbuf_make_room(struct MBuf *buf, unsigned len); /** Write a byte to write cursor. */ _MUSTCHECK static inline bool mbuf_write_byte(struct MBuf *buf, uint8_t val) { if (buf->write_pos + 1 > buf->alloc_len && !mbuf_make_room(buf, 1)) return false; buf->data[buf->write_pos++] = val; return true; } /** Write len bytes to write cursor. */ _MUSTCHECK static inline bool mbuf_write(struct MBuf *buf, const void *ptr, unsigned len) { if (buf->write_pos + len > buf->alloc_len && !mbuf_make_room(buf, len)) return false; if (len > 0) memcpy(buf->data + buf->write_pos, ptr, len); buf->write_pos += len; return true; } /** writes full contents of another mbuf, without touching it */ _MUSTCHECK static inline bool mbuf_write_raw_mbuf(struct MBuf *dst, struct MBuf *src) { return mbuf_write(dst, src->data, src->write_pos); } /** writes partial contents of another mbuf, with touching it */ _MUSTCHECK static inline bool mbuf_write_mbuf(struct MBuf *dst, struct MBuf *src, unsigned len) { const uint8_t *data; if (!mbuf_get_bytes(src, len, &data)) return false; if (!mbuf_write(dst, data, len)) { src->read_pos -= len; return false; } return true; } /** Fiil mbuf with byte value */ _MUSTCHECK static inline bool mbuf_fill(struct MBuf *buf, uint8_t byte, unsigned len) { if (buf->write_pos + len > buf->alloc_len && !mbuf_make_room(buf, len)) return false; memset(buf->data + buf->write_pos, byte, len); buf->write_pos += len; return true; } /** remove some data from mbuf */ _MUSTCHECK static inline bool mbuf_cut(struct MBuf *buf, unsigned ofs, unsigned len) { if (buf->reader) return false; if (ofs + len < buf->write_pos) { unsigned endofs = ofs + len; memmove(buf->data + ofs, buf->data + endofs, buf->write_pos - endofs); buf->write_pos -= len; } else if (ofs < buf->write_pos) { buf->write_pos = ofs; } return true; } static inline void mbuf_copy(const struct MBuf *src, struct MBuf *dst) { *dst = *src; } _MUSTCHECK static inline bool mbuf_slice(struct MBuf *src, unsigned len, struct MBuf *dst) { if (len > mbuf_avail_for_read(src)) return false; mbuf_init_fixed_reader(dst, src->data + src->read_pos, len); src->read_pos += len; return true; } #endif pgbouncer-1.24.1/lib/usual/ctype.h0000644000175000000000000000575114777762223013714 00000000000000/* * ctype wrappers * * Copyright (c) 2011 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * ctype compat. * * Provides wrappers that make sure the functions work on 'char' values. * * @note * POSIX requires that these functions accept EOF/-1 in addition * to ordinary byte values. That means when working on 'char', * the functions cannot differetiate between 0xFF and EOF. * As no code should give EOF to functions and no code * should depend whether 0xFF is labeled ispunct() or not, * it seems no worthwhile to fix it. */ #ifndef _USUAL_CTYPE_H_ #define _USUAL_CTYPE_H_ #include #include #ifndef isblank #define isblank usual_isblank static inline int isblank(int c) { return (c == ' ') || (c == '\t'); } #endif /* keep right signature, cast to uchar internally */ #define _WRAP_CTYPE_FN(name) \ static inline int safe_ ## name (int c) { \ return name((unsigned char)(c)); \ } _WRAP_CTYPE_FN(isalnum) #undef isalnum /** Safe isalnum */ #define isalnum safe_isalnum _WRAP_CTYPE_FN(isalpha) #undef isalpha /** Safe isalpha */ #define isalpha safe_isalpha _WRAP_CTYPE_FN(isascii) #undef isascii /** Safe isascii */ #define isascii safe_isascii _WRAP_CTYPE_FN(isblank) #undef isblank /** Safe isblank */ #define isblank safe_isblank _WRAP_CTYPE_FN(iscntrl) #undef iscntrl /** Safe iscntrl */ #define iscntrl safe_iscntrl _WRAP_CTYPE_FN(isdigit) #undef isdigit /** Safe isdigit */ #define isdigit safe_isdigit _WRAP_CTYPE_FN(isgraph) #undef isgraph /** Safe isgraph */ #define isgraph safe_isgraph _WRAP_CTYPE_FN(islower) #undef islower /** Safe islower */ #define islower safe_islower _WRAP_CTYPE_FN(isprint) #undef isprint /** Safe isprint */ #define isprint safe_isprint _WRAP_CTYPE_FN(ispunct) #undef ispunct /** Safe ispunct */ #define ispunct safe_ispunct _WRAP_CTYPE_FN(isspace) #undef isspace /** Safe isspace */ #define isspace safe_isspace _WRAP_CTYPE_FN(isupper) #undef isupper /** Safe isupper */ #define isupper safe_isupper _WRAP_CTYPE_FN(isxdigit) #undef isxdigit /** Safe isxdigit */ #define isxdigit safe_isxdigit _WRAP_CTYPE_FN(tolower) #undef tolower /** Safe tolower */ #define tolower safe_tolower _WRAP_CTYPE_FN(toupper) #undef toupper /** Safe toupper */ #define toupper safe_toupper #undef _WRAP_CTYPE_FN #endif /* _USUAL_CTYPE_H_ */ pgbouncer-1.24.1/lib/usual/talloc.h0000644000175000000000000005520114777762223014041 00000000000000/* * talloc - implementation of Talloc API. * * Copyright (c) 2013 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Talloc - hierarchical memory allocator. * * This is reimplementation of Samba's talloc: https://talloc.samba.org/ * * Features: * - Any allocated object can be parent to new objects. * - Any object can have more then one parent. * - References. * - Change parent. * - Built on top of API. * * Missing features: * - Pools. Use pools instead. * * It mostly compatible with original so that Samba's documentation is usable, * but it does not try to be bug-for-bug compatible. */ #ifndef _USUAL_TALLOC_H_ #define _USUAL_TALLOC_H_ #include /* avoid cxalloc.h include */ struct CxMem; /** * Type for untyped pointers that are usable as talloc parent. * * For situations where (void*) needs to be used. There * it might be used to use TALLOC_CTX to document that * talloc pointer is needed. */ typedef void TALLOC_CTX; /** * Destructor signature. * * If it returns -1, talloc_free() cancels its operation * and also returns -1. * * @param ptr Object to be freed. * @returns 0 on success, -1 otherwise. */ typedef int (*talloc_destructor_f)(void *ptr); /** * Give name to "unnamed" allocations. * * By default it generates name that contains * talloc API function name, source file and line number. * * It can be redefined in user source files. */ #ifndef TALLOC_POS #define TALLOC_POS(apifunc) apifunc "@" __FILE__ ":" STR(__LINE__) #endif /** * Free and set pointer to NULL. */ #define TALLOC_FREE(ptr) do { talloc_free(ptr); (ptr) = NULL; } while (0) /* * Internal functions used in macros. */ void *_talloc_const_name(const void *parent, size_t elem_size, size_t count, bool zero_fill, const char *name) _MALLOC; void *_talloc_format_name(const void *parent, size_t elem_size, size_t count, bool zero_fill, const char *fmt, ...) _PRINTF(5,6) _MALLOC; int _talloc_unlink(const void *parent, const void *ptr, const char *source_pos); int _talloc_free(const void *ptr, const char *source_pos); void _talloc_free_children(const void *ptr, const char *source_pos); void *_talloc_realloc(const void *parent, void *ptr, size_t elem_size, size_t count, const char *name); void *_talloc_get_type_abort(const void *ptr, const char *name); void *_talloc_move(const void *new_parent, void **ptr_p); void *_talloc_reference_named(const void *new_parent, const void *ptr, const char *name); /** * @name Allocate object and give name based on C type. * * Object names are set automatically based on C type. * * @{ */ /** * Allocate memory for C type. * * Returned object will have memory for sizeof(type) bytes. * * @param parent Parent context or NULL. * @param #type C type of object. * @returns New object or NULL on error. */ #ifdef DOXYGEN type *talloc(const void *parent, #type); #else #define talloc(parent, type) \ (type *)_talloc_const_name(parent, sizeof(type), 1, false, #type) #endif /** * Allocate zero-filled memory for C type. * * Returned object will have memory for sizeof(type) bytes. * * @param parent Parent context or NULL. * @param #type C type of object. * @returns New object or NULL on error. */ #ifdef DOXYGEN type* talloc_zero(const void *parent, #type); #else #define talloc_zero(parent, type) \ (type *)_talloc_const_name(parent, sizeof(type), 1, true, #type) #endif /** * Allocate array of elements of type given. * * size = count * sizeof(type); * * @param parent Parent context or NULL. * @param type C type. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_array(const void *parent, #type, size_t count); #else #define talloc_array(parent, type, count) \ (type *)_talloc_const_name(parent, sizeof(type), count, false, #type) #endif /** * Allocate zero-filled array of elements of type. * * size = count * sizeof(type); * * @param parent Parent context or NULL. * @param type C type. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_zero_array(const void *parent, #type, size_t count); #else #define talloc_zero_array(parent, type, count) \ (type *)_talloc_const_name(parent, sizeof(type), count, true, #type) #endif /** * @} * * @name Allocate object and give custom name. * * @{ */ /** * Allocate named object. * * Name pointer is used directly, so it should not change. * * @param parent Parent context or NULL. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_named_const(const void *parent, size_t size, const char *name); #else #define talloc_named_const(parent, size, name) \ _talloc_const_name(parent, size, 1, false, name) #endif /** * Allocate zero-filled memory for named object. * * Name pointer is used directly, so it should not change. * * @param parent Parent context or NULL. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_zero_named_const(const void *parent, size_t size, const char *name); #else #define talloc_zero_named_const(parent, size, name) \ _talloc_const_name(parent, size, 1, true, name) #endif /** * Allocate named context. * * Name is formatted via talloc_vasprintf() and allocated * as child of main object. * * @param parent Parent context or NULL. * @param size Size for allocation. * @param fmt printf format for name. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_named(const void *parent, size_t size, const char *fmt, ...); #else #define talloc_named(parent, size, ...) \ _talloc_format_name(parent, size, 1, false, __VA_ARGS__) #endif /** * Allocate new top-level named context. * * Name will be allocaten inside context. * * @param fmt Format string for sprintf. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_init(const char *fmt, ...); #else #define talloc_init(...) \ _talloc_format_name(NULL, 0, 0, false, __VA_ARGS__) #endif /** * @} * * @name Allocate unnamed context. * * The objects will get name based on calling location. * * @{ */ /** * Allocate unnamed context. * * @param parent Parent context or NULL. * @param size Size for allocation. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_size(const void *parent, size_t size); #else #define talloc_size(parent, size) \ _talloc_const_name(parent, size, 1, false, TALLOC_POS("talloc_size")) #endif /** * Allocate unnamed context with zeroed memory. * * @param parent Parent context or NULL. * @param size Size for allocation. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_zero_size(const void *parent, size_t size); #else #define talloc_zero_size(parent, size) \ _talloc_const_name(parent, size, 1, true, TALLOC_POS("talloc_zero_size")) #endif /** * Allocate array of elements of type. * * @param parent Parent context or NULL. * @param size Size for one element. * @param count Number of elements. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_array_size(const void *parent, size_t size, size_t count); #else #define talloc_array_size(parent, size, count) \ _talloc_const_name(parent, size, count, false, TALLOC_POS("talloc_array_size")) #endif /** * Allocate unnamed context from typed pointer. * * sizeof(*(ptr)) will be used as allocation size. * * @param parent Parent context or NULL. * @param ptr Typed pointer * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_ptrtype(const void *parent, type *ptr); #else #define talloc_ptrtype(parent, ptr) \ (__typeof__(ptr))_talloc_const_name(parent, sizeof(*(ptr)), 1, \ false, TALLOC_POS("talloc_ptrtype")) #endif /** * Allocate array of elements of type taken from pointer. * * size = count * sizeof(*ptr); * * @param parent Parent context or NULL. * @param ptr Typed pointer. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_array_ptrtype(const void *parent, type *ptr, size_t count); #else #define talloc_array_ptrtype(parent, ptr, count) \ (typeof(ptr))_talloc_const_name(parent, sizeof(*(ptr)), count, \ false, TALLOC_POS("talloc_array_ptrtype")) #endif /** * Allocate unnamed context as child of parent. * * @param parent Parent context or NULL * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_new(const void *parent); #else #define talloc_new(parent) \ _talloc_const_name(parent, 0, 0, false, TALLOC_POS("talloc_new")) #endif /** * @} * * @name Special contexts. * * Contexts that have unusual behaviour. * * @{ */ /** * Allocate context that will be freed on program exit. * * Objects allocated under this context will be freed * via atexit() handler, unless freed earlier. * This is useful to see leaked memory on program exit. * * @returns New context or NULL on error. */ void *talloc_autofree_context(void); /** * Allocate memory from CxMem. * * Returned pointer and all it's children will be allocated from CxMem; * * Name pointer is used directly, so it should not change. * * @param cx CxMem context to allocate from. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ void *talloc_from_cx(const struct CxMem *cx, size_t size, const char *name); /** * Create CxMem context that uses talloc. * * This makes CxMem-based code work with talloc. * * @param parent Parent context or NULL. * @param name Pointer to static string, will be used directly. * @returns New CxMem context or NULL on error. */ const struct CxMem *talloc_as_cx(const void *parent, const char *name); /** * @} * * @name Free memory. * * @{ */ /** * Set function to be called on final free. */ void talloc_set_destructor(const void *ptr, talloc_destructor_f destructor); #ifndef DOXYGEN #define talloc_set_destructor(ptr, dfn) \ do { int (*_dfn)(__typeof__(ptr)) = (dfn); \ talloc_set_destructor(ptr, (talloc_destructor_f)(_dfn)); \ } while (0) #endif /** * Free allocated context and all it's children. * * This can be called only on context that have one parent. * Use talloc_unlink() if object may have many references. * * @param ptr Pointer to previously allocated area. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_free(const void *ptr); #else #define talloc_free(ptr) \ _talloc_free(ptr, TALLOC_POS("talloc_free")) #endif /** * @} * * @name Reallocate existing context. * * Only context that have only one reference can be reallocated. * * @{ */ /** * Reallocate array. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param #type C type of one element. * @param count Number of elements. * @returns Reallocated context or NULL on error. */ #ifdef DOXYGEN type *talloc_realloc(const void *parent, const void *ptr, #type, size_t count); #else #define talloc_realloc(parent, ptr, type, count) \ (type *)_talloc_realloc(parent, ptr, sizeof(type), count, #type) #endif /** * Reallocate untyped context. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param size New length in bytes. * @returns Reallocated context or NULL on error. */ #ifdef DOXYGEN void *talloc_realloc_size(const void *parent, void *ptr, size_t size); #else #define talloc_realloc_size(parent, ptr, size) \ _talloc_realloc(parent, ptr, size, 1, TALLOC_POS("talloc_realloc_size")) #endif /** * Function version of realloc. * * This is guaranteed to not be macro, * unlike other API calls. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param size New length in bytes. * @returns Reallocated context or NULL on error. */ void *talloc_realloc_fn(const void *parent, void *ptr, size_t size); /** * @} * * @name Custom object name. * * @{ */ /** * Return object name. * * Result will be always non-NULL. */ const char *talloc_get_name(const void *ptr); /** * Set formatted name. * * Format and allocate as child of ptr. * * @param ptr Pointer to be named. * @param fmt Format string. * @returns New name or NULL on error. */ #ifdef DOXYGEN const char *talloc_set_name(const void *ptr, const char *fmt, ...); #else const char *talloc_set_name(const void *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** * Set name pointer directly. * * @param ptr Pointer to be named. * @param name Pointer to string. */ void talloc_set_name_const(const void *ptr, const char *name); /** * Return same pointer only if name matches, NULL otherwise. */ void *talloc_check_name(const void *ptr, const char *name); /** * @} * * @name Type-based names. * * @{ */ /** * Set context name from type. * * @param ptr Pointer to be named. * @param #type C type. */ #ifdef DOXYGEN void talloc_set_type(const void *ptr, #type); #else #define talloc_set_type(ptr, type) \ talloc_set_name_const(ptr, #type) #endif /** * Get typed pointer only if name matches. * * @param ptr Pointer to be checked. * @param #type C type. */ #ifdef DOXYGEN type *talloc_get_type(const void *ptr, #type); #else #define talloc_get_type(ptr, type) \ (type *)talloc_check_name(ptr, #type) #endif /** * Get typed pointed only if name matches, aborting otherwise. * * This is more extreme version of talloc_get_type(). * * @param ptr Pointer to be checked. * @param #type C type. */ #ifdef DOXYGEN type *talloc_get_type_abort(const void *ptr, #type); #else #define talloc_get_type_abort(ptr, type) \ (type *)_talloc_get_type_abort(ptr, #type) #endif /** * @} * * @name Allocated area size. * * @{ */ /** Get length of allocated area. */ size_t talloc_get_size(const void *ptr); /** * Get number of elements in array. */ #ifdef DOXYGEN size_t talloc_array_length(const type *array); #else #define talloc_array_length(array) (talloc_get_size(array) / sizeof(*(array))) #endif /** * @} * * @name Object parent. * * @{ */ /** Get parent object. */ void *talloc_parent(const void *ptr); /** Get parent object's name. */ const char *talloc_parent_name(const void *ptr); /** Find direct parent based on name */ void *talloc_find_parent_byname(const void *ptr, const char *name); /** Find direct parent based on type */ #ifdef DOXYGEN type *talloc_find_parent_bytype(const void *ptr, #type); #else #define talloc_find_parent_bytype(ptr, type) \ (type *)talloc_find_parent_byname(ptr, #type) #endif /** * @} * * @name Reference handling * * Talloc operates on references, not reference counts. * * @{ */ /** * Create another reference from parent to child. * * @param new_parent New parent context or NULL. * @param ptr Target pointer that is referenced. * @returns Original pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_reference(const void *new_parent, const void *ptr); #else #define talloc_reference(new_parent, ptr) \ (__typeof__(ptr))_talloc_reference_named(new_parent, ptr, TALLOC_POS("talloc_reference")) #endif /** * Create another reference from NULL context to child. * * @param ptr Target pointer that is referenced. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_increase_ref_count(const void *ptr); #else #define talloc_increase_ref_count(ptr) \ (_talloc_reference_named(NULL, ptr, \ TALLOC_POS("talloc_increase_ref_count")) ? 0 : -1) #endif /** * Remove parent's reference to child. * * If parent is found, unlinks and returns 0, otherwise -1. * * When removing last reference, the object is freed. * * @param parent Parent context or NULL. * @param ptr Pointer to be unlinked. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_unlink(const void *parent, const void *ptr); #else #define talloc_unlink(parent, ptr) \ _talloc_unlink(parent, ptr, TALLOC_POS("talloc_unlink")) #endif /** * Return number of references context has. */ size_t talloc_reference_count(const void *ptr); /** * @} * * @name Change parent * * @{ */ /** * Find reference from old parent switch it to new parent. */ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr); /** * Change primary parent, set old pointer to NULL. * * Useful when moving ponters between structs. * * Cannot be used on pointers that have multiple parents. * * @param new_parent New parent. * @param ptr_p Location of pointer, will be set to NULL if successful. * @returns Original pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_move(const void *new_parent, void **ptr_p); #else #define talloc_move(new_parent, ptr_p) \ (typeof(*(ptr_p)))_talloc_move(new_parent, (void **)(ptr_p)) #endif /** * Change primary parent. * * Cannot be used on pointers that have multiple parents. * * @param new_parent New parent. * @param ptr Pointer to be moved. * @returns Original pointer or NULL on error. */ void *talloc_steal(const void *new_parent, const void *ptr); /** * @} * * @name String functions. * * @{ */ /** Copy memory */ #ifdef DOXYGEN void *talloc_memdup(const void *parent, const void *p, size_t len); #else void *talloc_memdup(const void *parent, const void *p, size_t len) _MALLOC; #endif /** Copy string */ #ifdef DOXYGEN char *talloc_strdup(const void *parent, const char *s); #else char *talloc_strdup(const void *parent, const char *s) _MALLOC; #endif /** Copy string with size limit */ #ifdef DOXYGEN char *talloc_strndup(const void *parent, const char *s, size_t maxlen); #else char *talloc_strndup(const void *parent, const char *s, size_t maxlen) _MALLOC; #endif /** Format string */ #ifdef DOXYGEN char *talloc_asprintf(const void *parent, const char *fmt, ...); #else char *talloc_asprintf(const void *parent, const char *fmt, ...) _PRINTF(2,3) _MALLOC; #endif /** Format string taking argument from va_list */ #ifdef DOXYGEN char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap); #else char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap) _PRINTF(2,0) _MALLOC; #endif /** * @} * * @name String append functions. * * The *_append() functions use strnlen() to get size of string in buffer. * * @{ */ /** Append string to existing string */ char *talloc_strdup_append(char *ptr, const char *s); /** Append string with limit to existing string */ char *talloc_strndup_append(char *ptr, const char *s, size_t maxlen); /** Append formatted string to existing string */ #ifdef DOXYGEN char *talloc_asprintf_append(char *ptr, const char *fmt, ...); #else char *talloc_asprintf_append(char *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** Append formatted string to existing string */ #ifdef DOXYGEN char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap); #else char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap) _PRINTF(2,0); #endif /** * @} * * @name String append to complete buffer. * * The *_append_buffer() functions assume talloc_get_size() will * give the string length with final NUL byte. * * @{ */ /** Append string to existing buffer */ char *talloc_strdup_append_buffer(char *ptr, const char *str); /** Append string with limit to existing buffer */ char *talloc_strndup_append_buffer(char *ptr, const char *str, size_t maxlen); /** Append formatted string to existing buffer */ #ifdef DOXYGEN char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...); #else char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** Append formatted string to existing buffer */ #ifdef DOXYGEN char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap); #else char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap) _PRINTF(2,0); #endif /** * @} * * @name Debugging * * @{ */ /** * Set log function. */ void talloc_set_log_fn(void (*log_fn)(const char *message)); /** * Restore default function. */ void talloc_set_log_stderr(void); /** * Set function to be called on abort. */ void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); /** Collect all parent==NULL allocations under one context */ void talloc_enable_null_tracking(void); /** Collect all parent==NULL allocations under one context, but not autofree */ void talloc_enable_null_tracking_no_autofree(void); /** Stop collecting all parent==NULL allocations under one context */ void talloc_disable_null_tracking(void); /** On program exit, run talloc_report(NULL, stderr) */ void talloc_enable_leak_report(void); /** On program exit, run talloc_report_full(NULL, stderr) */ void talloc_enable_leak_report_full(void); /** * Return allocated bytes under context. */ size_t talloc_total_size(const void *ptr); /** * Return number of contexts under context. */ size_t talloc_total_blocks(const void *ptr); /** * Walk parents */ bool talloc_is_parent(const void *parent, const void *ptr); /** Print report about context and it's immediate childs */ void talloc_report(const void *ptr, FILE *f); /** Print report about context and it's all childs */ void talloc_report_full(const void *ptr, FILE *f); /** Print full report about context, with customizable depth */ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); /** Run callback on context and it's children */ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *private_data), void *private_data); /** Print parents to file */ void talloc_show_parents(const void *ptr, FILE *file); /** * Activate memory limit for object. * * The limit affects only new children allocated after setting it. * It does not take account object itself and it's current children. * * @param ptr Targer pointer. * @param max_size Limit in bytes. * @returns 0 on success, -1 otherwise. */ int talloc_set_memlimit(const void *ptr, size_t max_size); /** @} */ /* deprecated */ #define talloc_free_children(ptr) _talloc_free_children(ptr, TALLOC_POS("talloc_free_children")) /* custom */ void talloc_set_debug(int level); #endif pgbouncer-1.24.1/lib/usual/daemon.c0000644000175000000000000001106214777762223014016 00000000000000/* * Daemonization & pidfile handling. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include /* * pidfile management. */ static char *g_pidfile; static void remove_pidfile(void) { if (!g_pidfile) return; unlink(g_pidfile); free(g_pidfile); g_pidfile = NULL; } /* * Reads pid from pidfile and sends a signal to it. * * true - signaling was successful. * false - ENOENT / ESRCH * * fatal() otherwise. */ bool signal_pidfile(const char *pidfile, int sig) { char buf[128 + 1]; struct stat st; pid_t pid = 0; int fd, res; if (!pidfile || !pidfile[0]) return false; intr_loop: /* check if pidfile exists */ if (stat(pidfile, &st) < 0) goto fail; /* read old pid */ fd = open(pidfile, O_RDONLY); if (fd < 0) goto fail; res = read(fd, buf, sizeof(buf) - 1); close(fd); if (res <= 0) goto fail; /* parse pid */ buf[res] = 0; errno = 0; pid = strtoul(buf, NULL, 10); if (errno) { /* should we panic, or say no such process exists? */ if (0) errno = ESRCH; goto fail; } /* send the signal */ res = kill(pid, sig); if (res == 0) return true; fail: /* decide error seriousness */ if (errno == EINTR) goto intr_loop; if (errno == ENOENT || errno == ESRCH) return false; fatal_perror("signal_pidfile: unexpected error"); } static void check_pidfile(const char *pidfile) { if (signal_pidfile(pidfile, 0)) die("pidfile exists, another instance running?"); if (errno == ESRCH) { log_info("stale pidfile, removing"); unlink(pidfile); } } static void write_pidfile(const char *pidfile, bool first_write) { char buf[64]; pid_t pid; int res, fd, len; static int atexit_hook = 0; int flags = O_WRONLY | O_CREAT; if (!pidfile || !pidfile[0]) return; free(g_pidfile); g_pidfile = strdup(pidfile); if (!g_pidfile) die("out of memory"); pid = getpid(); snprintf(buf, sizeof(buf), "%u\n", (unsigned)pid); /* don't allow overwrite on first write */ if (first_write) flags |= O_EXCL; fd = open(pidfile, flags, 0644); if (fd < 0) die("could not open pidfile '%s': %s", pidfile, strerror(errno)); len = strlen(buf); loop: res = write(fd, buf, len); if (res < 0) { if (errno == EINTR) goto loop; die("write to pidfile '%s' failed: %s", pidfile, strerror(errno)); } else if (res < len) { len -= res; goto loop; } close(fd); if (!atexit_hook) { /* only remove when we have it actually written */ atexit(remove_pidfile); atexit_hook = 1; } } /* * Function: daemonize * * Handle pidfile and daemonization. * * If pidfile is given, check if old process is running. * * If going background is required, require non-empty pidfile * and logfile. Then fork to background and write pidfile. */ void daemonize(const char *pidfile, bool go_background) { int pid, fd; if (pidfile && pidfile[0]) { check_pidfile(pidfile); /* write pidfile twice, to be able to show problems to user */ write_pidfile(pidfile, true); } else if (go_background) fatal("daemon needs pidfile configured"); if (!go_background) return; if ((!cf_logfile || !cf_logfile[0]) && !cf_syslog) fatal("daemon needs logging configured"); /* send stdin, stdout, stderr to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) die("could not open /dev/null: %s", strerror(errno)); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); /* fork new process */ pid = fork(); if (pid < 0) die("fork failed: %s", strerror(errno)); if (pid > 0) _exit(0); /* create new session */ pid = setsid(); if (pid < 0) die("setsid: %s", strerror(errno)); /* fork again to avoid being session leader */ pid = fork(); if (pid < 0) die("fork failed; %s", strerror(errno)); if (pid > 0) _exit(0); write_pidfile(pidfile, false); } pgbouncer-1.24.1/lib/usual/regex.c0000644000175000000000000006716114777762223013700 00000000000000/* * Small POSIX-only regex engine. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Simple recursive matcher, only features are small size * and POSIX compatibility. * * ERE syntax: . * ^ $ [] [[:cname:]] () {} | + ? * BRE syntax: . * ^ $ [] [[:cname:]] \(\) \{\} \1-9 * * With REG_RELAXED_SYNTAX, following common escapes will be available: * \b\B\d\D\s\S\w\W BRE: \| ERE: \1-9 * * With REG_RELAXED_MATCHING it returns the first match found after applying * leftmost-longest to all elements. It skips the combinatorics to turn it * into guaranteed-longest match. * * Skipped POSIX features: * - collation classes: [[. .]] * - equivalence classes: [[= =]] * - char ranges by locale order: [a-z] (byte order will be used) * - multi-byte chars: UTF-8 */ #include #ifndef USE_SYSTEM_REGEX #include #include #include #include #undef STRICT /* either dynamic or static decision */ #define STRICT (ctx->strict) /* how many regmatch_t can be reported */ #define MAX_GROUPS 128 /* max count we want to store, means 'infinite' for simple atoms */ #define MAX_COUNT 0x7fff /* max count for simple atoms: char, any or class */ #define SIMPLE_MAXCNT(op) (((op)->maxcnt == MAX_COUNT) ? 0x7FFFFFFF : (op)->maxcnt) #define is_word(c) (isalnum(c) || (c) == '_') struct Op; struct ExecCtx; struct GMatch; /* Operation type */ enum OpType { /* ops that take count */ OP_CHAR, OP_ANY, OP_CLASS, OP_GROUP, OP_BREF, /* ops that dont take count */ OP_BOL, OP_EOL, OP_WCHANGE, OP_NWCHANGE, OP_GMATCH, OP_FULLMATCH, }; #define NONCOUNT_OPS_START OP_BOL /* regex_t->internal */ struct RegexInt { struct Op *root; struct Op *glist; struct MemPool *pool; int flags; }; /* match function and its setter */ typedef int (*matcher_f)(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm); static void set_op_type(struct Op *op, enum OpType op_type); /* List of tokens to be AND-ed together */ struct AndList { struct AndList *next; struct Op *op_list; }; /* extra data for group Op */ struct GroupData { struct Op *parent; /* parent group or NULL for first group */ struct AndList *or_list;/* alternative AndLists */ struct Op *glist_prev; /* prev group Op */ bool has_refs; /* if bref references it */ }; /* char class data */ struct ClassData { uint32_t bitmap[256 / 32]; }; /* operation data */ struct Op { struct Op *next; matcher_f matcher; uint16_t mincnt; uint16_t maxcnt; uint8_t type; union { uint8_t grp_no; /* OP_GROUP: group nr, 0-toplevel */ char lit; /* OP_CHAR */ uint8_t bref; /* OP_BREF */ }; union { struct ClassData cdata; struct GroupData gdata; }; }; #define OP_BASE (offsetof(struct Op, cdata)) /* * Operations on ClassData */ static bool class_isset(const struct ClassData *cd, unsigned char c) { return cd->bitmap[c / 32] & (1 << (c % 32)); } static void class_set(struct ClassData *cd, unsigned char c) { cd->bitmap[c / 32] |= (1 << (c % 32)); } static void class_negate(struct ClassData *cd) { int i; class_set(cd, 0); for (i = 0; i < 256/32; i++) cd->bitmap[i] ^= -1; } /* * Parsing code */ /* top-level context */ struct ParseCtx { regex_t *rx; struct RegexInt *rxi; struct Op *last_group; struct AndList *last_andlist; struct Op *last_elem; /* last op in current OR branch */ bool gotcnt; /* count was attached to last op */ bool strict; /* strict syntax */ }; static struct AndList *new_andlist(struct ParseCtx *ctx, struct Op *g) { struct AndList *al = mempool_alloc(&ctx->rxi->pool, sizeof(*al)); if (!al) return NULL; if (ctx->last_andlist) { ctx->last_andlist->next = al; } else { g->gdata.or_list = al; } ctx->last_andlist = al; return al; } static struct Op *new_op(struct ParseCtx *ctx, enum OpType t, int extra) { struct Op *op = mempool_alloc(&ctx->rxi->pool, OP_BASE + extra); if (!op) return NULL; set_op_type(op, t); op->mincnt = op->maxcnt = 1; ctx->gotcnt = false; /* append */ if (ctx->last_elem) { ctx->last_elem->next = op; } else if (ctx->last_andlist) { ctx->last_andlist->op_list = op; } else if (ctx->last_group) { struct AndList *alist; alist = new_andlist(ctx, ctx->last_group); if (!alist) return NULL; alist->op_list = op; } ctx->last_elem = op; if (t == OP_GROUP) { struct Op *parent = ctx->last_group; int gno = ++ctx->rx->re_nsub; op->grp_no = gno; op->gdata.parent = parent; op->gdata.glist_prev = ctx->rxi->glist; ctx->rxi->glist = op; ctx->last_group = op; ctx->last_andlist = NULL; ctx->last_elem = NULL; if (!ctx->rxi->root) ctx->rxi->root = op; } return op; } static int op_char(struct ParseCtx *ctx, unsigned c) { struct Op *op = new_op(ctx, OP_CHAR, 0); if (!op) return REG_ESPACE; op->lit = c; if ((ctx->rxi->flags & REG_ICASE) && isalpha(c)) op->lit = tolower(c); return 0; } static int op_bref(struct ParseCtx *ctx, unsigned c) { struct Op *g, *op; op = new_op(ctx, OP_BREF, 0); if (!op) return REG_ESPACE; op->bref = c - '0'; /* check if valid ref */ for (g = ctx->last_group; g; g = g->gdata.parent) { if (g->grp_no == op->bref) return REG_ESUBREG; } /* tag the group as referenced */ for (g = ctx->rxi->glist; g; g = g->gdata.glist_prev) { if (g->grp_no == op->bref) { g->gdata.has_refs = true; return 0; } } return REG_ESUBREG; } static int op_simple(struct ParseCtx *ctx, enum OpType t) { struct Op *op = new_op(ctx, t, 0); if (!op) return REG_ESPACE; return 0; } static int op_count_simple(struct ParseCtx *ctx, int min, int max) { struct Op *op = ctx->last_elem; if (!op || ctx->gotcnt) return REG_BADRPT; if (op->type >= NONCOUNT_OPS_START) return REG_BADRPT; ctx->gotcnt = true; op->mincnt = min; op->maxcnt = max; return 0; } static int op_count_full(struct ParseCtx *ctx, const char **re) { unsigned a, b; char *end = (char *)*re; bool ext = ctx->rxi->flags & REG_EXTENDED; int err; /* apply sanity check */ err = op_count_simple(ctx, 1, 1); if (err) return err; /* parse */ a = b = strtoul(*re, &end, 10); if (end == *re) return REG_EBRACE; if (*end == ',') { *re = end + 1; end = (char*)*re; b = strtoul(*re, &end, 10); if (end == *re) b = MAX_COUNT; } if (a > b || b > MAX_COUNT || a >= MAX_COUNT) return REG_BADBR; /* check for correct termination */ if (ext && end[0] == '}') { *re = end + 1; goto done; } else if (!ext && end[0] == '\\' && end[1] == '}') { *re = end + 2; goto done; } /* bad fmt, decide between error codes */ return strchr(end, '}') ? REG_BADBR : REG_EBRACE; done: ctx->last_elem->mincnt = a; ctx->last_elem->maxcnt = b; return 0; } static int op_gstart(struct ParseCtx *ctx) { struct Op *op; op = new_op(ctx, OP_GROUP, sizeof(struct GroupData)); if (!op) return REG_ESPACE; if (op->grp_no >= MAX_GROUPS) return REG_BADPAT; return 0; } static int finish_branch(struct ParseCtx *ctx) { int err; /* disallow empty OR fragments, but not empty groups */ if (!ctx->last_elem && ctx->last_andlist && STRICT) return REG_BADPAT; if (ctx->last_group->gdata.parent) err = op_simple(ctx, OP_GMATCH); else err = op_simple(ctx, OP_FULLMATCH); if (err) return err; ctx->last_elem = NULL; return 0; } static int op_gend(struct ParseCtx *ctx) { struct Op *op = ctx->last_group; struct AndList *alist; int err; if (!op) return REG_EPAREN; err = finish_branch(ctx); if (err) return err; ctx->last_group = op->gdata.parent; ctx->last_elem = op; /* recover previous andlist... */ alist = ctx->last_group->gdata.or_list; while (alist && alist->next) alist = alist->next; ctx->last_andlist = alist; return 0; } static int op_or(struct ParseCtx *ctx) { struct Op *gop = ctx->last_group; struct AndList *alist; int err; /* disallow empty OR branches */ if (!ctx->last_elem && STRICT) return REG_BADPAT; /* start new branch */ err = finish_branch(ctx); if (err) return err; alist = new_andlist(ctx, gop); if (!alist) return REG_ESPACE; ctx->last_andlist = alist; ctx->last_elem = NULL; return 0; } /* * Parse bracketed classes. */ static void add_char(struct ClassData *cd, unsigned char c, bool icase) { if (icase && isalpha(c)) { class_set(cd, tolower(c)); class_set(cd, toupper(c)); } else { class_set(cd, c); } } struct NamedClass { const char name[7]; unsigned char name_len; int (*check_func)(int c); }; static const struct NamedClass ctype_list[] = { { "alnum", 5, isalnum }, { "alpha", 5, isalpha }, { "blank", 5, isblank }, { "cntrl", 5, iscntrl }, { "digit", 5, isdigit }, { "graph", 5, isgraph }, { "lower", 5, islower }, { "print", 5, isprint }, { "punct", 5, ispunct }, { "space", 5, isspace }, { "upper", 5, isupper }, { "xdigit", 6, isxdigit }, }; static int fill_class(struct ClassData *cd, const char *name, const char **s_p, bool icase) { unsigned c; const struct NamedClass *cc = ctype_list; for (c = 0; c < ARRAY_NELEM(ctype_list); c++) { cc = ctype_list + c; if (strncmp(name, cc->name, cc->name_len) != 0) continue; name += cc->name_len; if (name[0] == ':' && name[1] == ']') goto found; break; } return *name ? REG_ECTYPE : REG_EBRACK; found: /* fill map */ for (c = 1; c < 256; c++) { if (cc->check_func(c)) add_char(cd, c, icase); } *s_p = name + 2; return 0; } #define MAP_RANGE 0x7FFF0001 #define MAP_END 0x7FFF0002 #define MAP_OTHER 0x7FFF0003 static int get_map_token(struct ParseCtx *ctx, const char **s_p, unsigned *dst_p, bool start, struct ClassData *cd, bool icase) { const char *s = *s_p; unsigned res; if (*s == '-') { if (start || s[1] == ']') res = '-'; else res = MAP_RANGE; s += 1; } else if (*s == ']' && !start) { res = MAP_END; s++; } else if (*s == '[' && (s[1] == '.' || s[1] == ':' || s[1] == '=')) { if (s[1] == ':') { s += 2; *dst_p = MAP_OTHER; return fill_class(cd, s, s_p, icase); } return REG_BADPAT; } else { res = (unsigned char)*s++; } *dst_p = res; *s_p = s; return 0; } static int op_class(struct ParseCtx *ctx, const char **re) { const char *s = *re; struct ClassData *cd; struct Op *op; bool not = false, icase = ctx->rxi->flags & REG_ICASE; const char *start; unsigned tk, c, prevtk = 0; bool is_range = false; int err; if (*s == '^') { s++; not = true; } start = s; op = new_op(ctx, OP_CLASS, sizeof(struct ClassData)); if (!op) return REG_ESPACE; cd = &op->cdata; if (not && (ctx->rxi->flags & REG_NEWLINE)) class_set(cd, '\n'); while (*s) { err = get_map_token(ctx, &s, &tk, s == start, cd, icase); if (err) return err; if (tk == MAP_END) { if (prevtk) add_char(cd, prevtk, icase); goto done; } else if (tk == MAP_OTHER) { if (is_range) return REG_ERANGE; if (prevtk) add_char(cd, prevtk, icase); prevtk = 0; } else if (tk == MAP_RANGE) { if (!prevtk) return REG_ERANGE; is_range = true; } else if (is_range) { if (tk < prevtk) return REG_ERANGE; for (c = prevtk; c <= tk; c++) add_char(cd, c, icase); is_range = false; prevtk = 0; } else { if (prevtk) add_char(cd, prevtk, icase); prevtk = tk; } } return REG_EBRACK; done: *re = s; if (not) class_negate(cd); return 0; } static int op_class_const(struct ParseCtx *ctx, const char *def) { const char *p = def + 1; return op_class(ctx, &p); } /* * Top-level syntax */ static int parse_relaxed_escapes(struct ParseCtx *ctx, char c) { if (STRICT) return REG_BADPAT; switch (c) { case 'b': return op_simple(ctx, OP_WCHANGE); case 'B': return op_simple(ctx, OP_NWCHANGE); case 'w': return op_class_const(ctx, "[_[:alnum:]]"); case 'W': return op_class_const(ctx, "[^_[:alnum:]]"); case 'd': return op_class_const(ctx, "[[:digit:]]"); case 'D': return op_class_const(ctx, "[^[:digit:]]"); case 's': return op_class_const(ctx, "[[:space:]]"); case 'S': return op_class_const(ctx, "[^[:space:]]"); } return REG_BADPAT; } static int parse_posix_ext(struct ParseCtx *ctx, const char *re) { int err = 0; unsigned c; int glevel = 0; loop: if (err) return err; c = *re++; switch (c) { case 0: return (glevel == 0) ? 0 : REG_EPAREN; case '(': glevel++; err = op_gstart(ctx); break; case ')': if (glevel > 0) { glevel--; err = op_gend(ctx); } else { err = op_char(ctx, c); /* POSIX bug */ } break; case '|': err = op_or(ctx); break; case '*': err = op_count_simple(ctx, 0, MAX_COUNT); break; case '?': err = op_count_simple(ctx, 0, 1); break; case '+': err = op_count_simple(ctx, 1, MAX_COUNT); break; case '[': err = op_class(ctx, &re); break; case '{': err = op_count_full(ctx, &re); break; case '.': err = op_simple(ctx, OP_ANY); break; case '^': err = op_simple(ctx, OP_BOL); break; case '$': err = op_simple(ctx, OP_EOL); break; case '\\': goto escaped; default: err = op_char(ctx, c); } goto loop; escaped: c = *re++; if (c == 0) err = REG_EESCAPE; else if (c >= '0' && c <= '9') err = STRICT ? REG_BADPAT : op_bref(ctx, c); else if (isalpha(c)) err = parse_relaxed_escapes(ctx, c); else err = op_char(ctx, c); goto loop; } static int parse_posix_basic(struct ParseCtx *ctx, const char *re) { int err = 0; unsigned c; int glevel = 0; loop: if (err) return err; c = *re++; switch (c) { case 0: return (glevel == 0) ? 0 : REG_EPAREN; case '*': if (ctx->last_elem && ctx->last_elem->type != OP_BOL) err = op_count_simple(ctx, 0, MAX_COUNT); else err = op_char(ctx, '*'); break; case '.': err = op_simple(ctx, OP_ANY); break; case '[': err = op_class(ctx, &re); break; case '^': if (!ctx->last_elem) err = op_simple(ctx, OP_BOL); else err = op_char(ctx, c); break; case '$': if (!*re || (re[0] == '\\' && re[1] == ')')) err = op_simple(ctx, OP_EOL); else err = op_char(ctx, c); break; case '\\': goto escaped; default: err = op_char(ctx, c); } goto loop; escaped: c = *re++; switch (c) { case 0: return REG_EESCAPE; case '(': glevel++; err = op_gstart(ctx); break; case ')': glevel--; if (glevel < 0) return REG_EPAREN; err = op_gend(ctx); break; case '{': err = op_count_full(ctx, &re); break; case '.': case '^': case '$': case '*': case '[': case ']': case '\\': err = op_char(ctx, c); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': err = op_bref(ctx, c); break; case '|': err = STRICT ? REG_BADPAT : op_or(ctx); break; default: err = parse_relaxed_escapes(ctx, c); } goto loop; } /* * Public compiling API. */ int regcomp(regex_t *rx, const char *re, int flags) { struct ParseCtx ctx; struct RegexInt *rxi; int err; struct MemPool *pool = NULL; /* do it first, to allow regfree() */ memset(rx, 0, sizeof(*rx)); if (flags & ~(REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE | REG_RELAXED)) return REG_BADPAT; if (!*re) return REG_BADPAT; rxi = mempool_alloc(&pool, sizeof(*rxi)); if (!rxi) return REG_ESPACE; rx->internal = rxi; rxi->pool = pool; /* initialize rx and local context */ memset(&ctx, 0, sizeof(ctx)); ctx.rx = rx; ctx.rxi = rxi; ctx.strict = !(flags & REG_RELAXED_SYNTAX); rxi->flags = flags; /* setup group #0 */ rx->re_nsub = -1; err = op_gstart(&ctx); if (err) goto failed; /* launch proper parser */ if (flags & REG_EXTENDED) err = parse_posix_ext(&ctx, re); else err = parse_posix_basic(&ctx, re); /* finalize group #0 */ if (!err) err = finish_branch(&ctx); /* relax if details are not needed */ if (flags & REG_NOSUB) { rxi->flags |= REG_RELAXED_MATCHING; rx->re_nsub = 0; } failed: /* clean up if problems */ if (err) regfree(rx); return err; } /* * Matching code */ /* historical best match */ struct HMatch { const char *hist_start; const char *hist_end; int rep_len; /* if repeated seq, full len thus far */ }; /* per-group-match context */ struct GMatch { struct GMatch *parent; /* parent group */ const struct Op *owner; /* Op for this group */ const char *start; /* match start */ const char *end; /* match end, NULL if no match */ struct GMatch *prevgm; /* older stack entry */ struct HMatch hm_next; /* best match for following stack entry */ int count; /* match nr in repeated seq */ }; /* top context */ struct ExecCtx { const regex_t *rx; const struct RegexInt *rxi; const char *str_start; regmatch_t *pmatch; int nmatch; int flags; bool strict; const char *last_endpos; struct HMatch hm_first[MAX_GROUPS]; struct GMatch *gm_stack[MAX_GROUPS]; struct GMatch *gm_cache[MAX_GROUPS]; }; static void push_gm(struct ExecCtx *ctx, struct GMatch *gm) { int gno = gm->owner->grp_no; gm->prevgm = ctx->gm_stack[gno]; ctx->gm_stack[gno] = gm; } static void pop_gm(struct ExecCtx *ctx, struct GMatch *gm) { int gno = gm->owner->grp_no; ctx->gm_stack[gno] = gm->prevgm; } static inline int do_match(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { return op->matcher(ctx, op, str, gm); } static int scan_next(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm, int curcnt, int alen) { int err = REG_NOMATCH; bool gotmatch = false; if (curcnt == op->mincnt) return do_match(ctx, op->next, str, gm); for (; curcnt >= op->mincnt; curcnt--) { err = do_match(ctx, op->next, str, gm); if (STRICT && err == 0) gotmatch = true; else if (err != REG_NOMATCH) break; str -= alen; } if (err == REG_NOMATCH && gotmatch) err = 0; return err; } static int match_char(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool icase = (ctx->flags & REG_ICASE); int c, i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt) && str[i]; i++) { c = icase ? tolower((unsigned char)str[i]) : str[i]; if (c != op->lit) break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_any(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool nl = (ctx->flags & REG_NEWLINE); int i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt) && str[i]; i++) { if (nl && str[i] == '\n') break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_class(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { int i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt); i++) { if (!class_isset(&op->cdata, str[i])) break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_bol(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { if (str == ctx->str_start && !(ctx->flags & REG_NOTBOL)) return do_match(ctx, op->next, str, gm); else if (str != ctx->str_start && str[-1] == '\n' && (ctx->flags & REG_NEWLINE)) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_eol(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { if (*str == '\n' && (ctx->flags & REG_NEWLINE)) return do_match(ctx, op->next, str, gm); else if (*str == 0 && !(ctx->flags & REG_NOTEOL)) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_wchange(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool prevw = (str == ctx->str_start) ? false : is_word(str[-1]); bool curw = is_word(str[0]); bool ischange = prevw ^ curw; if ((op->type == OP_WCHANGE) ? ischange : !ischange) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_bref(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool icase = ctx->flags & REG_ICASE; int i; struct GMatch *bgm = ctx->gm_stack[op->bref]; int blen = (bgm && bgm->end) ? (bgm->end - bgm->start) : -1; /* handle no-match, zero-len, zero-count */ if (blen < 0 && op->mincnt > 0) return REG_NOMATCH; if (blen <= 0 || op->maxcnt == 0) return do_match(ctx, op->next, str, gm); /* find max matches */ for (i = 0; (i < op->maxcnt) && *str; i++) { if (icase && strncasecmp(str, bgm->start, blen) != 0) break; else if (!icase && strncmp(str, bgm->start, blen) != 0) break; str += blen; } return scan_next(ctx, op, str, gm, i, blen); } static int match_group(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { int err = REG_NOMATCH; bool gotmatch = false; struct GMatch gthis; /* per-group-match context */ memset(>his, 0, sizeof(gthis)); gthis.owner = op; gthis.start = str; gthis.parent = gm; if (gm && gm->owner == op) { gthis.parent = gm->parent; gthis.count = gm->count + 1; } gm = >his; push_gm(ctx, gm); if (op->maxcnt > 0) { struct AndList *alist = op->gdata.or_list; /* check all branches, unless relaxed matching */ while (alist) { err = do_match(ctx, alist->op_list, str, gm); if (err == 0 && STRICT) { gm->end = NULL; gotmatch = true; } else if (err != REG_NOMATCH) break; alist = alist->next; } } /* is no-match allowed? */ if ((op->mincnt == 0) && (gm->count == 0) && (err == REG_NOMATCH || (err == 0 && STRICT))) { gm->end = NULL; err = do_match(ctx, op->next, str, gm->parent); } pop_gm(ctx, gm); return gotmatch ? 0 : err; } static int match_gend(struct ExecCtx *ctx, const struct Op *f_op, const char *str, struct GMatch *gm) { int err = REG_NOMATCH; const struct Op *op = gm->owner; bool zeromatch = (str == gm->start); bool gotmatch = false; /* ignore follow-up empty matches, unless it has backrefs */ if (zeromatch && gm->count > 0 && gm->count >= op->mincnt && !gm->owner->gdata.has_refs) return REG_NOMATCH; /* tag as matched */ gm->end = str; /* try more repeats, stop if count full or last match was zero-length */ if (gm->count + 1 < op->maxcnt && !zeromatch) { err = match_group(ctx, op, str, gm); if (err == 0 && STRICT) gotmatch = true; else if (err != REG_NOMATCH) return err; } /* fail if not enough repeats */ if (!zeromatch && gm->count + 1 < op->mincnt) return err; /* continue with parent branch */ err = do_match(ctx, op->next, str, gm->parent); if (err == REG_NOMATCH && gotmatch) err = 0; return err; } /* * The juice of POSIX - match weighting. */ static int gmatch_hist_cmp(struct ExecCtx *ctx, int gno, struct GMatch *gm, int replen) { struct HMatch *hm = (gm->prevgm) ? &gm->prevgm->hm_next : &ctx->hm_first[gno]; int gmlen = (gm->end) ? (gm->end - gm->start) : -1; int hmlen = (hm->hist_end) ? (hm->hist_end - hm->hist_start) : -1; int gmreplen = (gmlen >= 0) ? (gmlen + replen) : replen; int hmreplen = ((hmlen >= 0) ? hmlen : 0) + hm->rep_len; int gmofs = (gm->end) ? (gm->start - ctx->str_start) : -1; int hmofs = (hm->hist_start) ? (hm->hist_start - ctx->str_start) : -1; /* prefer rightmost match, to allow preceding elements match more */ int res = (gmofs - hmofs); /* prefer longer repeated match */ if (res == 0 && gm->count == 0) res = (gmreplen - hmreplen); /* prefer longer single match */ if (res == 0) res = (gmlen - hmlen); return res; } static int cmp_gmatches(struct ExecCtx *ctx, int gno, struct GMatch *gm, int replen) { int cmp = 0, gmlen; if (gm) { /* need to compare preceding groups first */ gmlen = gm->end ? gm->end - gm->start : 0; cmp = cmp_gmatches(ctx, gno, gm->prevgm, (gm->count == 0) ? 0 : (replen + gmlen)); /* actual comparision */ if (!cmp) cmp = gmatch_hist_cmp(ctx, gno, gm, replen); } return cmp; } static int gm_resolve_tie(struct ExecCtx *ctx, int gno) { struct GMatch *gm = ctx->gm_stack[gno]; if (!gm) /* 0-count match is better than no match */ return ctx->hm_first[gno].hist_start ? -1 : 0; return cmp_gmatches(ctx, gno, gm, 0); } static void fill_history(struct ExecCtx *ctx, int gno) { struct HMatch *hm; int gmlen, rep_len = 0; struct GMatch *gm = ctx->gm_stack[gno]; while (STRICT && gm) { hm = (gm->prevgm) ? &gm->prevgm->hm_next : &ctx->hm_first[gno]; hm->hist_start = gm->start; hm->hist_end = gm->end; hm->rep_len = rep_len; gmlen = gm->end ? (gm->end - gm->start) : 0; rep_len += gmlen; if (gm->count == 0) rep_len = 0; gm = gm->prevgm; } } static void publish_gm(struct ExecCtx *ctx, int gno) { struct GMatch *gm = ctx->gm_stack[gno]; regmatch_t *rm = ctx->pmatch + gno; /* ignore non-matches */ while (gm && !gm->end) gm = gm->prevgm; /* require it to be inside reported parent */ if (gm && gm->parent) { int pno = gm->parent->owner->grp_no; if (gm->parent != ctx->gm_cache[pno]) gm = NULL; } ctx->gm_cache[gno] = gm; /* publish new match */ if (gm) { rm->rm_so = gm->start - ctx->str_start; rm->rm_eo = gm->end - ctx->str_start; } else { rm->rm_so = -1; rm->rm_eo = -1; } } /* compare and publish */ static int got_full_match(struct ExecCtx *ctx, const struct Op *f_op, const char *str, struct GMatch *gm) { int gno, cmp; /* tag group as matched */ gm->end = str; /* ignore shorter matches */ if (ctx->last_endpos && str < ctx->last_endpos) return 0; /* longer or equal length */ if (str > ctx->last_endpos) { ctx->last_endpos = str; goto better_match; } else if (STRICT && ctx->nmatch > 1) { for (gno = 0; gno < ctx->nmatch; gno++) { cmp = gm_resolve_tie(ctx, gno); if (cmp < 0) break; if (cmp > 0) goto better_match; } } return 0; better_match: for (gno = 0; gno < ctx->nmatch; gno++) { publish_gm(ctx, gno); fill_history(ctx, gno); } return 0; } /* fill in proper matcher */ static void set_op_type(struct Op *op, enum OpType op_type) { static const matcher_f mlist[] = { match_char, match_any, match_class, match_group, match_bref, match_bol, match_eol, match_wchange, match_wchange, match_gend, got_full_match }; op->matcher = mlist[op_type]; op->type = op_type; } /* * Public matching API */ int regexec(const regex_t *rx, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags) { int err; struct ExecCtx ctx; if (eflags & ~(REG_NOTBOL | REG_NOTEOL)) return REG_BADPAT; /* init local context */ memset(&ctx, 0, sizeof(ctx)); ctx.pmatch = pmatch; ctx.nmatch = nmatch; ctx.str_start = str; ctx.rx = rx; ctx.rxi = rx->internal; ctx.flags = ctx.rxi->flags | eflags; /* reset pmatch area */ if (!(ctx.flags & REG_NOSUB)) memset(pmatch, -1, nmatch * sizeof(regmatch_t)); /* decide pmatch area that will be used */ if (!pmatch || (ctx.flags & REG_NOSUB)) ctx.nmatch = 0; else if (nmatch > (size_t)rx->re_nsub + 1) ctx.nmatch = rx->re_nsub + 1; ctx.strict = !(ctx.flags & REG_RELAXED_MATCHING) && (ctx.nmatch > 0); /* execute search */ str--; do { str++; err = do_match(&ctx, ctx.rxi->root, str, NULL); } while ((err == REG_NOMATCH) && *str); return err; } /* * Free parse tree */ void regfree(regex_t *rx) { struct RegexInt *rxi; if (rx) { rxi = rx->internal; if (rxi) mempool_destroy(&rxi->pool); memset(rx, 0, sizeof(*rx)); } } /* * Error strings */ size_t regerror(int err, const regex_t *rx, char *dst, size_t dstlen) { static const char errlist[][9] = { "NOERROR", /* 0 */ "NOMATCH", /* 1 */ "BADBR", /* 2 */ "BADPAT", /* 3 */ "BADRPT", /* 4 */ "EBRACE", /* 5 */ "EBRACK", /* 6 */ "ECOLLATE", /* 7 */ "ECTYPE", /* 8 */ "EESCAPE", /* 9 */ "EPAREN", /* 10 */ "ERANGE", /* 11 */ "ESPACE", /* 12 */ "ESUBREG", /* 13 */ }; const char *s = "EUNKNOWN"; if ((size_t)err < ARRAY_NELEM(errlist)) s = errlist[err]; return snprintf(dst, dstlen, "%s", s); } #endif /* !USE_SYSTEM_REGEX */ pgbouncer-1.24.1/lib/usual/logging.c0000644000175000000000000001617114777762223014207 00000000000000/* * Logging for unix service. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_SYSLOG_H #include #endif #ifdef USE_SYSTEMD #define SD_JOURNAL_SUPPRESS_LOCATION #include #endif #ifdef WIN32 #define LOG_EMERG 0 #define LOG_ALERT 1 #define LOG_CRIT 2 #define LOG_ERR 3 #define LOG_WARNING 4 #define LOG_NOTICE 5 #define LOG_INFO 6 #define LOG_DEBUG 7 #define LOG_PID 0 #define LOG_DAEMON 0 static inline void openlog(const char *ident, int opt, int fac) {} #define syslog win32_eventlog #define closelog() static void win32_eventlog(int level, const char *fmt, ...) _PRINTF(2, 3); #endif int cf_quiet = 0; int cf_verbose = 0; const char *cf_logfile = NULL; int cf_syslog = 0; const char *cf_syslog_ident = NULL; const char *cf_syslog_facility = NULL; enum LogLevel cf_syslog_level = LG_INFO; enum LogLevel cf_logfile_level = LG_NOISE; enum LogLevel cf_stderr_level = LG_NOISE; /* optional function to fill prefix */ logging_prefix_fn_t logging_prefix_cb; static FILE *log_file = NULL; static bool syslog_started = false; struct LevelInfo { const char *tag; int syslog_prio; }; static const struct LevelInfo log_level_list[] = { { "FATAL", LOG_CRIT }, /* LG_FATAL */ { "ERROR", LOG_ERR }, /* LG_ERROR */ { "WARNING", LOG_WARNING },/* LG_WARNING */ { "LOG", LOG_INFO }, /* LG_STATS*/ { "LOG", LOG_INFO }, /* LG_INFO */ { "DEBUG", LOG_DEBUG }, /* LG_DEBUG */ { "NOISE", LOG_DEBUG }, /* LG_NOISE */ }; struct FacName { const char *name; int code; }; static const struct FacName facility_names [] = { #ifndef WIN32 { "auth", LOG_AUTH }, #ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV }, #endif { "daemon", LOG_DAEMON }, { "user", LOG_USER }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, #endif { NULL }, }; void reset_logging(void) { if (log_file) { fclose(log_file); log_file = NULL; } if (syslog_started) { closelog(); syslog_started = 0; } } static void start_syslog(void) { const struct FacName *f; int fac = LOG_DAEMON; const char *ident = cf_syslog_ident; if (!cf_syslog) return; if (cf_syslog_facility) { for (f = facility_names; f->name; f++) { if (strcmp(f->name, cf_syslog_facility) == 0) { fac = f->code; break; } } } if (!ident) { ident = getprogname(); if (!ident) ident = "unnamed"; } openlog(ident, LOG_PID, fac); syslog_started = 1; } void log_generic(enum LogLevel level, void *ctx, const char *fmt, ...) { char buf[2048], buf2[2048]; char ebuf[256]; char timebuf[64]; const struct LevelInfo *lev = &log_level_list[level]; unsigned pid = getpid(); va_list ap; int pfxlen = 0; int old_errno = errno; char *msg = buf; if (logging_prefix_cb) { pfxlen = logging_prefix_cb(level, ctx, buf, sizeof(buf)); if (pfxlen < 0) goto done; if (pfxlen >= (int)sizeof(buf)) pfxlen = sizeof(buf) - 1; } va_start(ap, fmt); vsnprintf(buf + pfxlen, sizeof(buf) - pfxlen, fmt, ap); va_end(ap); /* replace '\n' in message with '\n\t', strip trailing whitespace */ if (strchr(msg, '\n')) { char *dst = buf2; for (; *msg && dst - buf2 < (int)sizeof(buf2) - 2; msg++) { *dst++ = *msg; if (*msg == '\n') *dst++ = '\t'; } while (dst > buf2 && isspace(dst[-1])) dst--; *dst = 0; msg = buf2; } format_time_ms(0, timebuf, sizeof(timebuf)); if (!log_file && cf_logfile && cf_logfile[0]) { log_file = fopen(cf_logfile, "a"); if (log_file) { /* Got the file, disable buffering */ setvbuf(log_file, NULL, _IONBF, 0); } else { /* Unable to open, complain and fail */ fprintf(stderr, "%s %u %s Cannot open logfile: '%s': %s\n", timebuf, pid, log_level_list[0].tag, cf_logfile, strerror_r(errno, ebuf, sizeof(ebuf))); exit(1); } } if (!cf_quiet && level <= cf_stderr_level) { #ifdef USE_SYSTEMD static bool journal_stream_checked = false; static bool use_systemd_journal = false; if (!journal_stream_checked) { if (getenv("JOURNAL_STREAM")) { long long unsigned int f1, f2; if (sscanf(getenv("JOURNAL_STREAM"), "%llu:%llu", &f1, &f2) == 2) { struct stat st; dev_t js_dev = f1; ino_t js_ino = f2; if (fstat(fileno(stderr), &st) >= 0) if (js_dev == st.st_dev && js_ino == st.st_ino) use_systemd_journal = true; } } journal_stream_checked = true; } if (use_systemd_journal) sd_journal_print(lev->syslog_prio, "%s", msg); else #endif fprintf(stderr, "%s [%u] %s %s\n", timebuf, pid, lev->tag, msg); } if (log_file && level <= cf_logfile_level) fprintf(log_file, "%s [%u] %s %s\n", timebuf, pid, lev->tag, msg); if (cf_syslog && level <= cf_syslog_level) { if (!syslog_started) start_syslog(); syslog(lev->syslog_prio, "%s", msg); } done: if (old_errno != errno) errno = old_errno; } void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *fmt, ...) { char buf[2048], ebuf[256]; const char *estr = NULL; int old_errno = 0; va_list ap; if (show_perror) { old_errno = errno; estr = strerror_r(errno, ebuf, sizeof(ebuf)); } va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (show_perror) { log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s: %s [%d]", file, line, func, buf, estr, old_errno); } else { log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s", file, line, func, buf); } } #ifdef WIN32 static void win32_eventlog(int level, const char *fmt, ...) { static HANDLE evtHandle = INVALID_HANDLE_VALUE; int elevel; char buf[1024]; const char *strlist[1] = { buf }; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); switch (level) { case LOG_CRIT: case LOG_ERR: elevel = EVENTLOG_ERROR_TYPE; break; case LOG_WARNING: elevel = EVENTLOG_WARNING_TYPE; break; default: elevel = EVENTLOG_INFORMATION_TYPE; } if (evtHandle == INVALID_HANDLE_VALUE) { evtHandle = RegisterEventSource(NULL, cf_syslog_ident); if (evtHandle == NULL || evtHandle == INVALID_HANDLE_VALUE) { evtHandle = INVALID_HANDLE_VALUE; return; } } ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, strlist, NULL); } #endif pgbouncer-1.24.1/lib/usual/cxalloc.h0000644000175000000000000000764014777762223014214 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Context-based Memory Allocator. * * The idea is that each data structure is given a context to allocate from, * and it can create subcontext for that which can be specific allocation * pattern that matches the data structure. * * It is slightly more work to use than palloc (PostgreSQL) or talloc (Samba), * but it avoids the need to have big fully-featured framework that does * everything at once. * * Instead you have small task-specific allocators, and you can always fall * back to raw malloc if you want to valgrind the code. * * Potential variants: * - fully-featured pooled * - randomly failing * - logging * - locking * - guard signatures * - palloc / talloc like API */ #ifndef _USUAL_CXALLOC_H_ #define _USUAL_CXALLOC_H_ #include /** * Ops for allocator that takes context. * * NB! - they are not equivalent to cx_* API. The cx_* * functions do additional sanitizing. */ struct CxOps { /** * Allocate memory. * len will not be 0. */ void *(*c_alloc)(void *ctx, size_t len); /** * Resize existing allocation. * Both p and len will not be 0 */ void *(*c_realloc)(void *ctx, void *p, size_t len); /** * Free existing allocation. * p will not be 0 */ void (*c_free)(void *ctx, void *p); /** * Release all memory in context. * This is not supported by all allocators. */ void (*c_destroy)(void *ctx); }; /** * Memory allocation context. */ struct CxMem { const struct CxOps *ops; void *ctx; }; /** Shortcut to const CxMem */ typedef const struct CxMem CxMem; /* * Basic operations on allocation context. */ /** * Allocate memory from context. * * Returns NULL if no memory or len == 0. */ void *cx_alloc(CxMem *cx, size_t len) _MALLOC; /** * Change existing allocation. * * If ptr is NULL it creates new allocation. * If len is 0 it frees the memory. */ void *cx_realloc(CxMem *cx, void *ptr, size_t len); /** * Free existing allocation. * * Does nothing if ptr is NULL. */ void cx_free(CxMem *cx, void *ptr); /** * Release all memory allocated in context. * * Should be called only on contexts that support it. */ void cx_destroy(CxMem *cx); /** Allocate and zero-fill memory */ void *cx_alloc0(CxMem *cx, size_t len) _MALLOC; /** Allocate and copy */ void *cx_memdup(CxMem *cx, const void *src, size_t len) _MALLOC; /** Allocate and copy string */ void *cx_strdup(CxMem *cx, const char *str) _MALLOC; /** Print to allocated string, return length or -1 on error. */ int cx_asprintf(CxMem *cx, char **dst_p, const char *fmt, ...) _PRINTF(3, 4); /** Print to allocated string, return length or -1 on error */ int cx_vasprintf(CxMem *cx, char **dst_p, const char *fmt, va_list ap) _PRINTF(3, 0); /** Print to allocated string, return new string or NULL on error */ char *cx_sprintf(CxMem *cx, const char *fmt, ...) _PRINTF(2, 3); /** Print to allocated string, return new string or NULL on error */ char *cx_vsprintf(CxMem *cx, const char *fmt, va_list ap) _PRINTF(2, 0); /** Allocator that uses libc malloc/realloc/free */ extern CxMem cx_libc_allocator; /** Default allocator */ #ifndef USUAL_ALLOC #define USUAL_ALLOC (&cx_libc_allocator) #endif #endif pgbouncer-1.24.1/lib/usual/slab.h0000644000175000000000000000446614777762223013513 00000000000000/* * Primitive slab allocator. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Slab allocator for same-size objects. * * Basic behaviour: * - On each alloc initializer is called. * - if init func is not given, memset() is done * - init func gets either zeroed obj or old obj from _free(). * 'struct List' on obj start is non-zero. * * ATM custom 'align' larger than malloc() alignment does not work. */ #ifndef _USUAL_SLAB_H_ #define _USUAL_SLAB_H_ #include /** Reference to main */ struct Slab; /** Signature for object init function */ typedef void (*slab_init_fn)(void *obj); /** Create new slab context for specific size */ struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx); /** Free whole context */ void slab_destroy(struct Slab *slab); /** Allocate single object from slab cache */ void *slab_alloc(struct Slab *slab) _MALLOC _MUSTCHECK; /** Release single object back */ void slab_free(struct Slab *slab, void *obj); /** Return sum of free and used objects */ int slab_total_count(const struct Slab *slab); /** Return number of free objects in cache */ int slab_free_count(const struct Slab *slab); /** Return number of used objects */ int slab_active_count(const struct Slab *slab); /** Signature for stat info callback */ typedef void (*slab_stat_fn)(void *arg, const char *slab_name, unsigned size, unsigned free, unsigned total); /** Run stat info callback on all slabs */ void slab_stats(slab_stat_fn cb_func, void *cb_arg); #endif pgbouncer-1.24.1/lib/usual/pgutil.c0000644000175000000000000001141014777762223014054 00000000000000/* * Some utility functions for Postgres. * * - Literal & ident quoting. * - Array parsing */ #include #include /* str -> E'str' */ bool pg_quote_literal(char *_dst, const char *_src, int dstlen) { char *dst = _dst; char *end = _dst + dstlen - 2; const char *src = _src; bool stdquote = true; if (dstlen < 3) return false; if (_src == NULL) { if (dstlen < 5) return false; memcpy(_dst, "NULL", 5); return true; } retry: *dst++ = '\''; while (*src && dst < end) { if (*src == '\'') *dst++ = '\''; else if (*src == '\\') { if (stdquote) goto retry_ext; *dst++ = '\\'; } *dst++ = *src++; } if (*src || dst > end) return false; *dst++ = '\''; *dst = 0; return true; retry_ext: /* string contains '\\', retry as E'' string */ dst = _dst; src = _src; *dst++ = 'E'; stdquote = false; goto retry; } static inline bool id_start(unsigned char c) { return (c >= 'a' && c <= 'z') || c == '_'; } static inline bool id_body(unsigned char c) { return id_start(c) || (c >= '0' && c <= '9'); } /* ident -> "ident" */ bool pg_quote_ident(char *_dst, const char *_src, int dstlen) { char *dst = _dst; char *end = _dst + dstlen - 1; const char *src = _src; if (dstlen < 1) return false; if (!id_start(*src)) goto needs_quoting; while (*src && dst < end) { if (!id_body(*src)) goto needs_quoting; *dst++ = *src++; } if (*src) return false; *dst = 0; if (!pg_is_reserved_word(_dst)) return true; needs_quoting: dst = _dst; src = _src; end = _dst + dstlen - 2; if (dstlen < 3) return false; *dst++ = '"'; while (*src && dst < end) { if (*src == '"') *dst++ = *src; *dst++ = *src++; } if (*src) return false; *dst++ = '"'; *dst = 0; return true; } /* schema.name -> "schema"."name" */ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen) { const char *dot = strchr(_src, '.'); char scmbuf[128]; const char *scm; int scmlen; if (dot) { scmlen = dot - _src; if (scmlen >= (int)sizeof(scmbuf)) return false; memcpy(scmbuf, _src, scmlen); scmbuf[scmlen] = 0; scm = scmbuf; _src = dot + 1; } else { scm = "public"; } if (!pg_quote_ident(_dst, scm, dstlen)) return false; scmlen = strlen(_dst); _dst[scmlen] = '.'; _dst += scmlen + 1; dstlen -= scmlen + 1; if (!pg_quote_ident(_dst, _src, dstlen)) return false; return true; } /* * pgarray parsing */ static bool parse_value(struct StrList *arr, const char *val, const char *vend, CxMem *cx) { int len; const char *s; char *str, *p; unsigned c; while (val < vend && isspace(*val)) val++; while (vend > val && isspace(vend[-1])) vend--; if (val == vend) return false; s = val; len = vend - val; if (len == 4 && !strncasecmp(val, "null", len)) { return strlist_append_ref(arr, NULL); } p = str = cx_alloc(cx, len + 1); if (!str) return false; /* unquote & copy */ while (s < vend) { c = *s++; if (c == '"') { while (1) { c = *s++; if (c == '"') break; else if (c == '\\') *p++ = *s++; else *p++ = c; } } else if (c == '\\') { *p++ = *s++; } else *p++ = c; } *p++ = 0; if (!strlist_append_ref(arr, str)) { cx_free(cx, str); return false; } return true; } struct StrList *pg_parse_array(const char *pgarr, CxMem *cx) { const char *s = pgarr; struct StrList *lst; const char *val = NULL; unsigned c; /* skip dimension def "[x,y]={..}" */ if (*s == '[') { s = strchr(s, ']'); if (!s || s[1] != '=') return NULL; s += 2; } if (*s++ != '{') return NULL; lst = strlist_new(cx); if (!lst) return NULL; while (*s) { /* array end */ if (s[0] == '}') { if (s[1] != 0) { goto failed; } if (val) { if (!parse_value(lst, val, s, cx)) goto failed; } return lst; } /* cannot init earlier to support empty arrays */ if (!val) val = s; /* val done? */ if (*s == ',') { if (!parse_value(lst, val, s, cx)) goto failed; val = ++s; continue; } /* scan value */ c = *s++; if (c == '"') { while (1) { c = *s++; if (c == '"') break; else if (c == '\\') { if (!*s) goto failed; s++; } else if (!*s) goto failed; } } else if (c == '\\') { if (!*s) goto failed; s++; } } if (s[-1] != '}') goto failed; return lst; failed: strlist_free(lst); return NULL; } /* * Postgres keyword lookup. */ /* gperf tries ot inline a non-static function. */ #undef inline #undef __inline #undef __attribute__ #define inline #define __inline #define __attribute__(x) #define long uintptr_t /* include gperf code */ const char *pg_keyword_lookup_real(const char *str, size_t len); #include bool pg_is_reserved_word(const char *str) { const char *kw = pg_keyword_lookup_real(str, strlen(str)); return kw != NULL; } pgbouncer-1.24.1/lib/usual/slab.c0000644000175000000000000001471414777762223013503 00000000000000/* * Primitive slab allocator. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifndef USUAL_FAKE_SLAB /* * Store for pre-initialized objects of one type. */ struct Slab { struct List head; struct StatList freelist; struct StatList fraglist; char name[32]; unsigned final_size; unsigned total_count; slab_init_fn init_func; CxMem *cx; }; /* * Header for each slab. */ struct SlabFrag { struct List head; }; /* keep track of all active slabs */ static STATLIST(slab_list); static void slab_list_append(struct Slab *slab) { #ifndef _REENTRANT statlist_append(&slab_list, &slab->head); #endif } static void slab_list_remove(struct Slab *slab) { #ifndef _REENTRANT statlist_remove(&slab_list, &slab->head); #endif } /* fill struct contents */ static void init_slab(struct Slab *slab, const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { unsigned slen = strlen(name); list_init(&slab->head); statlist_init(&slab->freelist, name); statlist_init(&slab->fraglist, name); slab->total_count = 0; slab->init_func = init_func; slab->cx = cx; if (slen >= sizeof(slab->name)) slen = sizeof(slab->name) - 1; memcpy(slab->name, name, slen); slab->name[slen] = 0; /* don't allow too small align, as we want to put pointers into area */ if (align < sizeof(long)) align = 0; /* actual area for one object */ if (align == 0) slab->final_size = ALIGN(obj_size); else slab->final_size = CUSTOM_ALIGN(obj_size, align); /* allow small structs */ if (slab->final_size < sizeof(struct List)) slab->final_size = sizeof(struct List); slab_list_append(slab); } /* make new slab */ struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { struct Slab *slab; /* new slab object */ slab = cx_alloc0(cx, sizeof(*slab)); if (slab) init_slab(slab, name, obj_size, align, init_func, cx); return slab; } /* free all storage associated by slab */ void slab_destroy(struct Slab *slab) { struct List *item, *tmp; struct SlabFrag *frag; if (!slab) return; slab_list_remove(slab); statlist_for_each_safe(item, &slab->fraglist, tmp) { frag = container_of(item, struct SlabFrag, head); cx_free(slab->cx, frag); } cx_free(slab->cx, slab); } /* add new block of objects to slab */ static void grow(struct Slab *slab) { unsigned count, i, size; char *area; struct SlabFrag *frag; /* calc new slab size */ count = slab->total_count; if (count < 50) count = 16 * 1024 / slab->final_size; if (count < 50) count = 50; size = count * slab->final_size; /* allocate & init */ frag = cx_alloc0(slab->cx, size + sizeof(struct SlabFrag)); if (!frag) return; list_init(&frag->head); area = (char *)frag + sizeof(struct SlabFrag); /* init objects */ for (i = 0; i < count; i++) { void *obj = area + i * slab->final_size; struct List *head = (struct List *)obj; list_init(head); statlist_append(&slab->freelist, head); } /* register to slab */ slab->total_count += count; statlist_append(&slab->fraglist, &frag->head); } /* get free object from slab */ void *slab_alloc(struct Slab *slab) { struct List *item = statlist_pop(&slab->freelist); if (!item) { grow(slab); item = statlist_pop(&slab->freelist); } if (item) { if (slab->init_func) slab->init_func(item); else memset(item, 0, slab->final_size); } return item; } /* put object back to slab */ void slab_free(struct Slab *slab, void *obj) { struct List *item = obj; list_init(item); statlist_prepend(&slab->freelist, item); } /* total number of objects allocated from slab */ int slab_total_count(const struct Slab *slab) { return slab->total_count; } /* free objects in slab */ int slab_free_count(const struct Slab *slab) { return statlist_count(&slab->freelist); } /* number of objects in use */ int slab_active_count(const struct Slab *slab) { return slab_total_count(slab) - slab_free_count(slab); } static void run_slab_stats(struct Slab *slab, slab_stat_fn cb_func, void *cb_arg) { unsigned free = statlist_count(&slab->freelist); cb_func(cb_arg, slab->name, slab->final_size, free, slab->total_count); } /* call a function for all active slabs */ void slab_stats(slab_stat_fn cb_func, void *cb_arg) { struct Slab *slab; struct List *item; statlist_for_each(item, &slab_list) { slab = container_of(item, struct Slab, head); run_slab_stats(slab, cb_func, cb_arg); } } #else struct Slab { int size; struct StatList obj_list; slab_init_fn init_func; CxMem *cx; }; struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { struct Slab *s = cx_alloc(cx, sizeof(*s)); if (s) { s->size = obj_size; s->init_func = init_func; s->cx = cx; statlist_init(&s->obj_list, "obj_list"); } return s; } void slab_destroy(struct Slab *slab) { struct List *el, *tmp; statlist_for_each_safe(el, &slab->obj_list, tmp) { statlist_remove(&slab->obj_list, el); cx_free(slab->cx, el); } cx_free(slab->cx, slab); } void *slab_alloc(struct Slab *slab) { struct List *o; void *res; o = cx_alloc(slab->cx, sizeof(struct List) + slab->size); if (!o) return NULL; list_init(o); statlist_append(&slab->obj_list, o); res = (void *)(o + 1); if (slab->init_func) slab->init_func(res); return res; } void slab_free(struct Slab *slab, void *obj) { if (obj) { struct List *el = obj; statlist_remove(&slab->obj_list, el - 1); cx_free(slab->cx, el - 1); } } int slab_total_count(const struct Slab *slab) { return 0; } int slab_free_count(const struct Slab *slab) { return 0; } int slab_active_count(const struct Slab *slab) { return 0; } void slab_stats(slab_stat_fn cb_func, void *cb_arg) {} #endif pgbouncer-1.24.1/lib/usual/dlfcn.c0000644000175000000000000000240514777762223013642 00000000000000/* * Dynamic library loading. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef _WIN32 #include /* * win32: Minimal dlopen, dlsym, dlclose, dlerror compat. */ void *dlopen(const char *fn, int flag) { HMODULE h = LoadLibraryEx(fn, NULL, 0); return h; } void *dlsym(void *hptr, const char *fname) { HMODULE h = hptr; FARPROC f = GetProcAddress(h, fname); return f; } int dlclose(void *hptr) { HMODULE h = hptr; return FreeLibrary(h) ? 0 : -1; } const char *dlerror(void) { return strerror(GetLastError()); } #endif pgbouncer-1.24.1/lib/usual/cfparser.h0000644000175000000000000001412714777762223014372 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Config file parser. */ #ifndef _USUAL_CFPARSER_H_ #define _USUAL_CFPARSER_H_ #include /** * @name Simple line-by-line parser * @{ */ /** Callback signarure for @ref parse_ini_file() */ typedef bool (*cf_handler_f)(void *arg, bool is_sect, const char *key, const char *val); /** * Simple parser, launches callback for each line */ bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) _MUSTCHECK; /* @} */ /** * @name Complex parser with variable setting. * @{ */ /** @name Per-key flags * @{ */ /** The pointer is absolute */ #define CF_VAL_ABS 0 /** The pointer is relative to base */ #define CF_VAL_REL (1<<1) /** Value must not be changed on reload */ #define CF_NO_RELOAD (1<<2) /** Value can only be read */ #define CF_READONLY (1<<3) /** @} */ /** * Helper structure for passing key info to CfOps */ struct CfValue { void *value_p; const void *extra; const char *key_name; char *buf; int buflen; }; /** * Callbacks for setting and getting a variable value. * * Getter requires temp buf, returns string pointer, which * may or may not point to temp buf. Getter is optional. */ struct CfOps { bool (*setter)(struct CfValue *cv, const char *value); const char *(*getter)(struct CfValue *cv); const void *op_extra; }; /** * Parameter description */ struct CfKey { /** Parameter name */ const char *key_name; /** Type-specific functions, called with absolute pointer */ struct CfOps op; /** Flags: CF_VAL_ABS, CF_VAL_REL */ int flags; /** Absolute or relative offset */ uintptr_t key_ofs; /** Default value as string */ const char *def_value; }; /** * Section description */ struct CfSect { /** Section name */ const char *sect_name; /** Key list */ const struct CfKey *key_list; /** Get base pointer to dynamic sections (optional) */ void *(*base_lookup)(void *top_base, const char *sect_name); /** Set dynamic keys (optional) */ bool (*set_key)(void *base, const char *key, const char *val); /** Get dynamic keys (optional) */ const char *(*get_key)(void *base, const char *key, char *buf, int buflen); /** New section callback (optional) */ bool (*section_start)(void *top_base, const char *sect_name); }; /** * Top-level config information */ struct CfContext { /** Section list */ const struct CfSect *sect_list; /** Top-level base pointer, needed for relative addressing */ void *base; /** If set, then CF_NO_RELOAD keys cannot be changed anymore */ bool loaded; }; /** * @name Type-specific helpers * @{ */ /** Setter for string */ bool cf_set_str(struct CfValue *cv, const char *value); /** Setter for filename */ bool cf_set_filename(struct CfValue *cv, const char *value); /** Setter for int */ bool cf_set_int(struct CfValue *cv, const char *value); /** Setter for unsigned int */ bool cf_set_uint(struct CfValue *cv, const char *value); /** Setter for time-usec */ bool cf_set_time_usec(struct CfValue *cv, const char *value); /** Setter for time-double */ bool cf_set_time_double(struct CfValue *cv, const char *value); /** Setter for lookup */ bool cf_set_lookup(struct CfValue *cv, const char *value); /** Getter for string */ const char *cf_get_str(struct CfValue *cv); /** Getter for int */ const char *cf_get_int(struct CfValue *cv); /** Getter for unsigned int */ const char *cf_get_uint(struct CfValue *cv); /** Getter for time-usec */ const char *cf_get_time_usec(struct CfValue *cv); /** Getter for time-double */ const char *cf_get_time_double(struct CfValue *cv); /** Getter for int lookup */ const char *cf_get_lookup(struct CfValue *cv); /** @} */ /** * @name Shortcut CfOps for well-known types * @{ */ /** Ops for string */ #define CF_STR { cf_set_str, cf_get_str } /** Ops for filename */ #define CF_FILE { cf_set_filename, cf_get_str } /** Ops for integer */ #define CF_INT { cf_set_int, cf_get_int } /** Ops for unsigned integer */ #define CF_UINT { cf_set_uint, cf_get_uint } /** Ops for boolean */ #define CF_BOOL { cf_set_int, cf_get_int } /** Ops for time as usec */ #define CF_TIME_USEC { cf_set_time_usec, cf_get_time_usec } /** Ops for time as double */ #define CF_TIME_DOUBLE { cf_set_time_double, cf_get_time_double } /** Ops for lookup, takes table as argument */ #define CF_LOOKUP(t) { cf_set_lookup, cf_get_lookup, t } /** @} */ /** * Lookup entry for CF_LOOKUP table. */ struct CfLookup { const char *name; int value; }; /** * Helper to describe CfKey with absolute addressing */ #define CF_ABS(name, ops, var, flags, def) \ { name, ops, flags | CF_VAL_ABS, (uintptr_t)&(var), def } /** * Helper to describe CfKey with relative addressing. * * Before using it defined CF_REL_BASE to base struct. * * The var should be field in that struct. * * @code * struct Foo { * char *foo_name; * }; * #define CF_REL_BASE struct Foo * ... * CF_REL("name", CF_STR, foo_name, 0, NULL) * ... * #undef CF_REL_BASE * @endcode */ #define CF_REL(name, ops, var, flags, def) \ { name, ops, flags | CF_VAL_REL, offsetof(CF_REL_BASE, var), def } /** * Load config from file. */ bool cf_load_file(const struct CfContext *cf, const char *fn) _MUSTCHECK; /** * Get single value. */ const char *cf_get(const struct CfContext *cf, const char *sect, const char *var, char *buf, int buflen); /** * Set single value. */ bool cf_set(const struct CfContext *cf, const char *sect, const char *var, const char *val); /* @} */ #endif pgbouncer-1.24.1/lib/usual/mdict.h0000644000175000000000000000555614777762223013673 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Minimal dict. */ #ifndef _USUAL_MDICT_H_ #define _USUAL_MDICT_H_ #include #include /** Dict reference */ struct MDict; /** Create new emtpy dict */ struct MDict *mdict_new(CxMem *cx); /** Free dict */ void mdict_free(struct MDict *dict); /** Get value as MBuf from string */ const struct MBuf *mdict_get_buf(struct MDict *dict, const char *key, unsigned klen); /** Get value from dict */ const char *mdict_get_str(struct MDict *dict, const char *key, unsigned klen); /** Put string to dict */ bool mdict_put_str(struct MDict *dict, const char *key, unsigned klen, const char *val, unsigned vlen); /** Remove a key from dict */ bool mdict_del_key(struct MDict *dict, const char *key, unsigned klen); /** Signature for walker callback */ typedef bool (*mdict_walker_f)(void *arg, const struct MBuf *k, const struct MBuf *v); /** Walk over dict */ bool mdict_walk(struct MDict *dict, mdict_walker_f cb_func, void *cb_arg); /* * Simple API that calculates strlen inline. */ /** Get value from dict */ static inline const char *mdict_get(struct MDict *dict, const char *key) { return mdict_get_str(dict, key, strlen(key)); } /** Put zero-terminated key and value to dict */ static inline bool mdict_put(struct MDict *dict, const char *key, const char *val) { unsigned klen = strlen(key); unsigned vlen = val ? strlen(val) : 0; return mdict_put_str(dict, key, klen, val, vlen); } /** Put MBuf to dict */ static inline bool mdict_put_buf(struct MDict *dict, const char *key, const struct MBuf *buf) { unsigned klen = strlen(key); const char *val = buf ? mbuf_data(buf) : NULL; unsigned vlen = buf ? mbuf_written(buf) : 0; return mdict_put_str(dict, key, klen, val, vlen); } /** Remove value from dict */ static inline bool mdict_del(struct MDict *dict, const char *key) { return mdict_del_key(dict, key, strlen(key)); } /** Urldecode string and add keys with values to dict */ bool mdict_urldecode(struct MDict *dict, const char *str, unsigned len); /** Urlencode dict to string */ bool mdict_urlencode(struct MDict *dict, struct MBuf *dst); #endif pgbouncer-1.24.1/lib/usual/misc.h0000644000175000000000000000307614777762223013521 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Random stuff that does not fit elsewhere. */ #ifndef _USUAL_MISC_H_ #define _USUAL_MISC_H_ #include #ifdef WORDS_BIGENDIAN #define FOURCC(a,b,c,d) \ ( ((unsigned int)(unsigned char)(a) << 24) \ | ((unsigned int)(unsigned char)(b) << 16) \ | ((unsigned int)(unsigned char)(c) << 8) \ | ((unsigned int)(unsigned char)(d))) #else /** Four-byte identifier as integer */ #define FOURCC(a,b,c,d) \ ( ((unsigned int)(unsigned char)(a)) \ | ((unsigned int)(unsigned char)(b) << 8) \ | ((unsigned int)(unsigned char)(c) << 16) \ | ((unsigned int)(unsigned char)(d) << 24)) #endif #if defined(__i386__) || defined(__x86_64__) #define mb() asm volatile("mfence":::"memory") #define rmb() asm volatile("lfence":::"memory") #define wmb() asm volatile("sfence":::"memory") #endif #endif pgbouncer-1.24.1/lib/usual/pgsocket.h0000644000175000000000000000572314777762223014406 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Async Postgres connection framework. */ #ifndef _USUAL_PGSOCKET_H_ #define _USUAL_PGSOCKET_H_ #include #include /** * Event types reported to user handler function. */ enum PgEvent { /** Connection establishing finished */ PGS_CONNECT_OK, /** Connection establishing failed */ PGS_CONNECT_FAILED, /** Got result from query either resultset or DB error */ PGS_RESULT_OK, /** Query execution failed */ PGS_RESULT_BAD, /** Wakeup from timed sleep */ PGS_TIMEOUT, }; struct PgSocket; struct event_base; typedef void (*pgs_handler_f)(struct PgSocket *pgs, void *arg, enum PgEvent dbev, PGresult *res); /** Create PgSocket. * * It does not launch connection yet, use \ref pgs_connect() for that. * * @param connstr libpq connect string * @param fn callback function for event handling * @param arg extra context for callback * @return Initialized PgSocket structure */ struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *arg); /** Release PgSocket */ void pgs_free(struct PgSocket *db); /** Change the event base for PgSocket */ void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base); /** Set connection lifetime (in seconds) */ void pgs_set_lifetime(struct PgSocket *pgs, double lifetime); /** Launch connection */ void pgs_connect(struct PgSocket *db); /** Drop connection */ void pgs_disconnect(struct PgSocket *db); /** Send simple query */ void pgs_send_query_simple(struct PgSocket *db, const char *query); /** Send extended query, args from varargs */ void pgs_send_query_params(struct PgSocket *db, const char *query, int nargs, ...); /** Send extended query, args from list */ void pgs_send_query_params_list(struct PgSocket *db, const char *query, int nargs, const char *argv[]); /** Ignore the connection for specified time */ void pgs_sleep(struct PgSocket *db, double timeout); /** Disconnect, sleep, reconnect */ void pgs_reconnect(struct PgSocket *db, double timeout); /** Does PgSocket have established connection */ int pgs_connection_valid(struct PgSocket *db); /** Return underlying Postgres connection */ PGconn *pgs_get_connection(struct PgSocket *db); bool pgs_waiting_for_reply(struct PgSocket *db); #endif pgbouncer-1.24.1/lib/usual/psrandom.h0000644000175000000000000000347414777762223014413 00000000000000/* * Pseudo-random bytes. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * Pseudo-random number generator for non-cryptographic purposes. * * By default it's seeded with csrandom so returns unpredictable * values (but not cryptographically stong). But when * pseudo_random_seed() is used, all following values * are determined completely by seed. */ #ifndef _USUAL_PSRANDOM_H_ #define _USUAL_PSRANDOM_H_ #include /** * Return value with uniform probability over whole 32-bit range. */ uint32_t pseudo_random(void); /** * Return with with uniform probability over specific range. */ uint32_t pseudo_random_range(uint32_t upper_bound); /** * Fill buffer with random bytes. */ void pseudo_random_bytes(void *dst, size_t count); /** * Set 128-bit seed. Following values will be * fully deterministic based on seed. */ void pseudo_random_seed(uint64_t a, uint64_t b); /* 128-bit state. Period: 2**128 - 1 */ uint64_t xorshift128plus(uint64_t *s0, uint64_t *s1); /* 1024-bit state. Period: 2**1024 - 1 */ uint64_t xorshift1024plus(uint64_t state[16], unsigned int counter); #endif pgbouncer-1.24.1/lib/usual/config.h.in0000664000175000017500000003240714777762315014461 00000000000000/* lib/usual/config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to enable assert checking */ #undef CASSERT /* Define to 1 if you have the `arc4random_buf' function. */ #undef HAVE_ARC4RANDOM_BUF /* Define to 1 if you have the header file. */ #undef HAVE_ARES_NAMESER_H /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the `asn1_time_parse' function. */ #undef HAVE_ASN1_TIME_PARSE /* Define to 1 if you have the `asprintf' function. */ #undef HAVE_ASPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #undef HAVE_DECL_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define if *enc & *dec functions are available */ #undef HAVE_ENCDEC_FUNCS /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H /* Define to 1 if you have the `err' function. */ #undef HAVE_ERR /* Define to 1 if you have the `errx' function. */ #undef HAVE_ERRX /* Define to 1 if you have the header file. */ #undef HAVE_ERR_H /* Define to 1 if you have the `explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO /* Define to 1 if you have the `ffs' function. */ #undef HAVE_FFS /* Define to 1 if you have the `ffsl' function. */ #undef HAVE_FFSL /* Define to 1 if you have the `ffsll' function. */ #undef HAVE_FFSLL /* Define to 1 if you have the `fls' function. */ #undef HAVE_FLS /* Define to 1 if you have the `flsl' function. */ #undef HAVE_FLSL /* Define to 1 if you have the `flsll' function. */ #undef HAVE_FLSLL /* Define to 1 if you have the `fnmatch' function. */ #undef HAVE_FNMATCH /* Define to 1 if you have the header file. */ #undef HAVE_FNMATCH_H /* Define to 1 if your compiler understands __func__. */ #undef HAVE_FUNCNAME__FUNC /* Define to 1 if you have the getaddrinfo_a() function. */ #undef HAVE_GETADDRINFO_A /* Define to 1 if you have the `getentropy' function. */ #undef HAVE_GETENTROPY /* Define to 1 if you have the `getline' function. */ #undef HAVE_GETLINE /* Define to 1 if you have the `getopt' function. */ #undef HAVE_GETOPT /* Define to 1 if you have the header file. */ #undef HAVE_GETOPT_H /* Define to 1 if you have the `getopt_long' function. */ #undef HAVE_GETOPT_LONG /* Define to 1 if you have the `getopt_long_only' function. */ #undef HAVE_GETOPT_LONG_ONLY /* Define to 1 if you have the `getpeereid' function. */ #undef HAVE_GETPEEREID /* Define to 1 if you have the `getpeerucred' function. */ #undef HAVE_GETPEERUCRED /* Define to 1 if you have the `getprogname' function. */ #undef HAVE_GETPROGNAME /* Define to 1 if you have the `getrandom' function. */ #undef HAVE_GETRANDOM /* Define to 1 if you have the `getrusage' function. */ #undef HAVE_GETRUSAGE /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_GRP_H /* Define to 1 if you have the `inet_ntop' function. */ #undef HAVE_INET_NTOP /* Define to 1 if you have the `inet_pton' function. */ #undef HAVE_INET_PTON /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LANGINFO_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_RANDOM_H /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if you have the `lstat' function. */ #undef HAVE_LSTAT /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if you have the `mbsnrtowcs' function. */ #undef HAVE_MBSNRTOWCS /* Define to 1 if you have the `memalign' function. */ #undef HAVE_MEMALIGN /* Define to 1 if you have the `memmem' function. */ #undef HAVE_MEMMEM /* Define to 1 if you have the `mempcpy' function. */ #undef HAVE_MEMPCPY /* Define to 1 if you have the `memrchr' function. */ #undef HAVE_MEMRCHR /* Define to 1 if you have the `memset_s' function. */ #undef HAVE_MEMSET_S /* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_TCP_H /* Define to 1 if you have the `nl_langinfo' function. */ #undef HAVE_NL_LANGINFO /* PAM support */ #undef HAVE_PAM /* Define to 1 if you have the `poll' function. */ #undef HAVE_POLL /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H /* Define to 1 if you have the `posix_memalign' function. */ #undef HAVE_POSIX_MEMALIGN /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the header file. */ #undef HAVE_PWD_H /* Define to 1 if you have the `reallocarray' function. */ #undef HAVE_REALLOCARRAY /* Define to 1 if you have the `recvmsg' function. */ #undef HAVE_RECVMSG /* Define to 1 if you have the `regcomp' function. */ #undef HAVE_REGCOMP /* Define to 1 if you have the header file. */ #undef HAVE_REGEX_H /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H /* Define to 1 if you have the `sendmsg' function. */ #undef HAVE_SENDMSG /* Define to 1 if you have the `setprogname' function. */ #undef HAVE_SETPROGNAME /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* Define to 1 if you have the `sigqueue' function. */ #undef HAVE_SIGQUEUE /* Define to 1 if you have the `SSL_CTX_load_verify_mem' function. */ #undef HAVE_SSL_CTX_LOAD_VERIFY_MEM /* Define to 1 if you have the `SSL_CTX_use_certificate_chain_mem' function. */ #undef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM /* Define to 1 if you have the header file. */ #undef HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define if you have `strerror_r'. */ #undef HAVE_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the `strnlen' function. */ #undef HAVE_STRNLEN /* Define to 1 if you have the `strsep' function. */ #undef HAVE_STRSEP /* Define to 1 if you have the `strtod_l' function. */ #undef HAVE_STRTOD_L /* Define to 1 if you have the `strtonum' function. */ #undef HAVE_STRTONUM /* Define to 1 if you have the `syslog' function. */ #undef HAVE_SYSLOG /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_ENDIAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MMAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_RESOURCE_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UCRED_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the `timegm' function. */ #undef HAVE_TIMEGM /* Define to 1 if you have the header file. */ #undef HAVE_UCRED_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `usleep' function. */ #undef HAVE_USLEEP /* Define to 1 if you have the `valloc' function. */ #undef HAVE_VALLOC /* Define to 1 if you have the `vasprintf' function. */ #undef HAVE_VASPRINTF /* Define to 1 if you have the `warn' function. */ #undef HAVE_WARN /* Define to 1 if you have the `warnx' function. */ #undef HAVE_WARNX /* Define to 1 if you have the header file. */ #undef HAVE_XLOCALE_H /* Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions. */ #undef OPENSSL_API_COMPAT /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to the version of this package for Windows resource file (1,2,3,4). */ #undef PACKAGE_VERSION_4B /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Define to 1 if strerror_r returns char *. */ #undef STRERROR_R_CHAR_P /* Use c-ares for name resolution. */ #undef USE_CARES /* Use libevent for DNS lookups. */ #undef USE_EVDNS /* Define to build with systemd support. (--with-systemd) */ #undef USE_SYSTEMD /* Use libssl for TLS. */ #undef USUAL_LIBSSL_FOR_TLS /* Path to root CA certs. */ #undef USUAL_TLS_CA_FILE /* Define to request cleaner win32 headers. */ #undef WIN32_LEAN_AND_MEAN /* Define to max win32 API version (0x0600=Vista). */ #undef WINVER /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define to get some GNU functions in headers. */ #undef _GNU_SOURCE /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT64_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define to `int' if doesn't define. */ #undef gid_t /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define as a signed integer type capable of holding a process identifier. */ #undef pid_t /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported only directly. */ #undef restrict /* Work around a bug in older versions of Sun C++, which did not #define __restrict__ or support _Restrict or __restrict__ even though the corresponding Sun C compiler ended up with "#define restrict _Restrict" or "#define restrict __restrict__" in the previous line. This workaround can be removed once we assume Oracle Developer Studio 12.5 (2016) or later. */ #if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__ # define _Restrict # define __restrict__ #endif /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to `int' if doesn't define. */ #undef uid_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef uint64_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t pgbouncer-1.24.1/lib/usual/aatree.c0000644000175000000000000001643314777762223014023 00000000000000/* * AA-Tree - Binary tree with embeddable nodes. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Self-balancing binary tree. * * Here is an implementation of AA-tree (Arne Andersson tree) * which is simplification of Red-Black tree. * * Red-black tree has following properties that must be kept: * 1. A node is either red or black. * 2. The root is black. * 3. All leaves (NIL nodes) are black. * 4. Both childen of red node are black. * 5. Every path from root to leaf contains same number of black nodes. * * AA-tree adds additional property: * 6. Red node can exist only as a right node. * * Red-black tree properties quarantee that the longest path is max 2x longer * than shortest path (B-R-B-R-B-R-B vs. B-B-B-B) thus the tree will be roughly * balanced. Also it has good worst-case guarantees for insertion and deletion, * which makes it good tool for real-time applications. * * AA-tree removes most special cases from RB-tree, thus making resulting * code lot simpler. It requires slightly more rotations when inserting * and deleting but also keeps the tree more balanced. */ #include #include /* for NULL */ typedef struct AATree Tree; typedef struct AANode Node; /* * NIL node */ #define NIL ((struct AANode *)&_nil) static const struct AANode _nil = { NIL, NIL, 0 }; /* * Rebalancing. AA-tree needs only 2 operations * to keep the tree balanced. */ /* * Fix red on left. * * X Y * / --> \ * Y X * \ / * a a */ static inline Node * skew(Node *x) { Node *y = x->left; if (x->level == y->level && x != NIL) { x->left = y->right; y->right = x; return y; } return x; } /* * Fix 2 reds on right. * * X Y * \ / \ * Y --> X Z * / \ \ * a Z a */ static inline Node * split(Node *x) { Node *y = x->right; if (x->level == y->right->level && x != NIL) { x->right = y->left; y->left = x; y->level++; return y; } return x; } /* insert is easy */ static Node *rebalance_on_insert(Node *current) { return split(skew(current)); } /* remove is bit more tricky */ static Node *rebalance_on_remove(Node *current) { /* * Removal can create a gap in levels, * fix it by lowering current->level. */ if (current->left->level < current->level - 1 || current->right->level < current->level - 1) { current->level--; /* if ->right is red, change it's level too */ if (current->right->level > current->level) current->right->level = current->level; /* reshape, ask Arne about those */ current = skew(current); current->right = skew(current->right); current->right->right = skew(current->right->right); current = split(current); current->right = split(current->right); } return current; } /* * Recursive insertion */ static Node * insert_sub(Tree *tree, Node *current, uintptr_t value, Node *node) { int cmp; if (current == NIL) { /* * Init node as late as possible, to avoid corrupting * the tree in case it is already added. */ node->left = node->right = NIL; node->level = 1; tree->count++; return node; } /* recursive insert */ cmp = tree->node_cmp(value, current); if (cmp > 0) current->right = insert_sub(tree, current->right, value, node); else if (cmp < 0) current->left = insert_sub(tree, current->left, value, node); else /* already exists? */ return current; return rebalance_on_insert(current); } void aatree_insert(Tree *tree, uintptr_t value, Node *node) { tree->root = insert_sub(tree, tree->root, value, node); } /* * Recursive removal */ /* remove_sub could be used for that, but want to avoid comparisions */ static Node *steal_leftmost(Tree *tree, Node *current, Node **save_p) { if (current->left == NIL) { *save_p = current; return current->right; } current->left = steal_leftmost(tree, current->left, save_p); return rebalance_on_remove(current); } /* drop this node from tree */ static Node *drop_this_node(Tree *tree, Node *old) { Node *new = NIL; if (old->left == NIL) new = old->right; else if (old->right == NIL) new = old->left; else { /* * Picking nearest from right is better than from left, * due to asymmetry of the AA-tree. It will result in * less tree operations in the long run, */ old->right = steal_leftmost(tree, old->right, &new); /* take old node's place */ *new = *old; } /* cleanup for old node */ if (tree->release_cb) tree->release_cb(old, tree); tree->count--; return new; } static Node *remove_sub(Tree *tree, Node *current, uintptr_t value) { int cmp; /* not found? */ if (current == NIL) return current; cmp = tree->node_cmp(value, current); if (cmp > 0) current->right = remove_sub(tree, current->right, value); else if (cmp < 0) current->left = remove_sub(tree, current->left, value); else current = drop_this_node(tree, current); return rebalance_on_remove(current); } void aatree_remove(Tree *tree, uintptr_t value) { tree->root = remove_sub(tree, tree->root, value); } /* * Walking all nodes */ static void walk_sub(Node *current, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg) { if (current == NIL) return; switch (wtype) { case AA_WALK_IN_ORDER: walk_sub(current->left, wtype, walker, arg); walker(current, arg); walk_sub(current->right, wtype, walker, arg); break; case AA_WALK_POST_ORDER: walk_sub(current->left, wtype, walker, arg); walk_sub(current->right, wtype, walker, arg); walker(current, arg); break; case AA_WALK_PRE_ORDER: walker(current, arg); walk_sub(current->left, wtype, walker, arg); walk_sub(current->right, wtype, walker, arg); break; } } /* walk tree in correct order */ void aatree_walk(Tree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg) { walk_sub(tree->root, wtype, walker, arg); } /* walk tree in bottom-up order, so that walker can destroy the nodes */ void aatree_destroy(Tree *tree) { walk_sub(tree->root, AA_WALK_POST_ORDER, tree->release_cb, tree); /* reset tree */ tree->root = NIL; tree->count = 0; } /* prepare tree */ void aatree_init(Tree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb) { tree->root = NIL; tree->count = 0; tree->node_cmp = cmpfn; tree->release_cb = release_cb; } /* * search function */ Node *aatree_search(Tree *tree, uintptr_t value) { Node *current = tree->root; while (current != NIL) { int cmp = tree->node_cmp(value, current); if (cmp > 0) current = current->right; else if (cmp < 0) current = current->left; else return current; } return NULL; } pgbouncer-1.24.1/lib/usual/pgutil.h0000644000175000000000000000276514777762223014076 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Utility functions for PostgreSQL data formats. */ #ifndef _USUAL_PGUTIL_H_ #define _USUAL_PGUTIL_H_ #include /** Check if string is reserver word for PostgreSQL. */ bool pg_is_reserved_word(const char *str); /** Quote value as string for PostgreSQL */ bool pg_quote_literal(char *_dst, const char *_src, int dstlen); /** Quote value as ident for PostgreSQL */ bool pg_quote_ident(char *_dst, const char *_src, int dstlen); /** Quote fully-qualified ident for PostgreSQL */ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen); /** Parse PostgreSQL array. */ struct StrList *pg_parse_array(const char *pgarr, CxMem *cx); #endif pgbouncer-1.24.1/lib/usual/cfparser.c0000644000175000000000000003060714777762223014366 00000000000000/* * Config file parser. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_PWD_H #include #endif #include #include #include #include #include #define MAX_INCLUDE 10 /* * INI file parser. */ static int count_lines(const char *s, const char *end) { int lineno = 1; for (; s < end; s++) { if (*s == '\n') lineno++; } return lineno; } static bool parse_ini_file_internal(const char *fn, cf_handler_f user_handler, void *arg, int inclevel) { char *buf; char *p, *key, *val; int klen, vlen; char o1, o2; bool ok; buf = load_file(fn, NULL); if (buf == NULL) { log_error("could not load file \"%s\": %s", fn, strerror(errno)); return false; } p = buf; while (*p) { /* space at the start of line - including empty lines */ while (*p && isspace(*p)) p++; if (strncmp(p, "%include", 8) == 0 && p[8] != 0 && isblank(p[8])) { if (inclevel >= MAX_INCLUDE) { log_error("include nesting level too deep (%s:%d), stopping loading", fn, count_lines(buf, p)); goto failed; } p += 8; while (*p && isblank(*p)) p++; /* now read value */ val = p; while (*p && (*p != '\n')) p++; vlen = p - val; /* eat space at end */ while (vlen > 0 && isspace(val[vlen - 1])) vlen--; /* * val now has the name of the file to be included. * Process it recursively. */ o1 = val[vlen]; val[vlen] = 0; log_debug("processing include: %s", val); ok = parse_ini_file_internal(val, user_handler, arg, inclevel + 1); val[vlen] = o1; if (!ok) { log_error("error processing include file in configuration (%s:%d), stopping loading", fn, count_lines(buf, p)); goto failed; } log_debug("returned to processing file %s", fn); continue; } /* skip comment lines */ if (*p == '#' || *p == ';') { while (*p && *p != '\n') p++; continue; } /* got new section */ if (*p == '[') { key = ++p; while (*p && *p != ']' && *p != '\n') p++; if (*p != ']') goto syntax_error; o1 = *p; *p = 0; log_debug("parse_ini_file: [%s]", key); ok = user_handler(arg, true, key, NULL); if (!ok) { log_error("invalid section \"%s\" in configuration (%s:%d)", key, fn, count_lines(buf, p)); goto failed; } *p++ = o1; continue; } /* done? */ if (*p == 0) break; /* read key val */ key = p; while (*p && (isalnum(*p) || strchr("_.-*", *p))) p++; klen = p - key; /* expect '=', skip it */ while (*p && (*p == ' ' || *p == '\t')) p++; if (*p != '=') { goto syntax_error; } else p++; while (*p && (*p == ' ' || *p == '\t')) p++; /* now read value */ val = p; while (*p && (*p != '\n')) p++; vlen = p - val; /* eat space at end */ while (vlen > 0 && isspace(val[vlen - 1])) vlen--; /* skip junk */ while (*p && isspace(*p)) p++; /* our buf is r/w, so take it easy */ o1 = key[klen]; o2 = val[vlen]; key[klen] = 0; val[vlen] = 0; log_debug("parse_ini_file: '%s' = '%s'", key, val); ok = user_handler(arg, false, key, val); log_debug("parse_ini_file: '%s' = '%s' ok:%d", key, val, ok); if (!ok) log_error("invalid value \"%s\" for parameter %s in configuration (%s:%d)", val, key, fn, count_lines(buf, p)); /* restore data, to keep count_lines() working */ key[klen] = o1; val[vlen] = o2; if (!ok) goto failed; } free(buf); return true; syntax_error: log_error("syntax error in configuration (%s:%d), stopping loading", fn, count_lines(buf, p)); failed: free(buf); return false; } bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) { return parse_ini_file_internal(fn, user_handler, arg, 0); } /* * Config framework. */ static void *get_dest(void *base, const struct CfKey *k) { char *dst; if (k->flags & CF_VAL_REL) { /* relative address requires base */ if (!base) return NULL; dst = (char *)base + k->key_ofs; } else dst = (char *)k->key_ofs; return dst; } static const struct CfSect *find_sect(const struct CfContext *cf, const char *name) { const struct CfSect *s; for (s = cf->sect_list; s->sect_name; s++) { if (strcmp(s->sect_name, name) == 0) return s; if (strcmp(s->sect_name, "*") == 0) return s; } return NULL; } static const struct CfKey *find_key(const struct CfSect *s, const char *key) { const struct CfKey *k; for (k = s->key_list; k->key_name; k++) { if (strcmp(k->key_name, key) == 0) return k; } return NULL; } const char *cf_get(const struct CfContext *cf, const char *sect, const char *key, char *buf, int buflen) { const struct CfSect *s; const struct CfKey *k; void *base, *p; struct CfValue cv; /* find section */ s = find_sect(cf, sect); if (!s) return NULL; /* find section base */ base = cf->base; if (s->base_lookup) base = s->base_lookup(base, sect); /* handle dynamic keys */ if (s->set_key) { if (!s->get_key) return NULL; return s->get_key(base, key, buf, buflen); } /* get fixed key */ k = find_key(s, key); if (!k || !k->op.getter) return NULL; p = get_dest(base, k); if (!p) return NULL; cv.key_name = k->key_name; cv.extra = k->op.op_extra; cv.value_p = p; cv.buf = buf; cv.buflen = buflen; return k->op.getter(&cv); } bool cf_set(const struct CfContext *cf, const char *sect, const char *key, const char *val) { const struct CfSect *s; const struct CfKey *k; void *base, *p; struct CfValue cv; /* find section */ s = find_sect(cf, sect); if (!s) { log_error("unknown section: %s", sect); return false; } /* find section base */ base = cf->base; if (s->base_lookup) base = s->base_lookup(base, sect); /* handle dynamic keys */ if (s->set_key) return s->set_key(base, key, val); /* set fixed key */ k = find_key(s, key); if (!k) { log_error("unknown parameter: %s/%s", sect, key); return false; } if (!k->op.setter || (k->flags & CF_READONLY)) { /* silently ignore */ return true; } if ((k->flags & CF_NO_RELOAD) && cf->loaded) { /* silently ignore */ return true; } p = get_dest(base, k); if (!p) { log_error("bug - no base for relative key: %s/%s", sect, key); return false; } cv.key_name = k->key_name; cv.extra = k->op.op_extra; cv.value_p = p; cv.buf = NULL; cv.buflen = 0; return k->op.setter(&cv, val); } /* * File loader */ struct LoaderCtx { const struct CfContext *cf; char *cur_sect; void *top_base; bool got_main_sect; }; static bool fill_defaults(struct LoaderCtx *ctx) { const struct CfKey *k; const struct CfSect *s; s = find_sect(ctx->cf, ctx->cur_sect); if (!s) goto fail; if (s == ctx->cf->sect_list) ctx->got_main_sect = true; if (s->section_start) { if (!s->section_start(ctx->top_base, ctx->cur_sect)) return false; } if (s->set_key) return true; for (k = s->key_list; k->key_name; k++) { if (!k->def_value || (k->flags & CF_READONLY)) continue; if ((k->flags & CF_NO_RELOAD) && ctx->cf->loaded) continue; if (!cf_set(ctx->cf, ctx->cur_sect, k->key_name, k->def_value)) goto fail; } return true; fail: log_error("fill_defaults fail"); return false; } static bool load_handler(void *arg, bool is_sect, const char *key, const char *val) { struct LoaderCtx *ctx = arg; if (is_sect) { free(ctx->cur_sect); ctx->cur_sect = strdup(key); if (!ctx->cur_sect) return false; return fill_defaults(ctx); } else if (!ctx->cur_sect) { log_error("load_init_file: value without section: %s", key); return false; } else { return cf_set(ctx->cf, ctx->cur_sect, key, val); } } bool cf_load_file(const struct CfContext *cf, const char *fn) { struct LoaderCtx ctx; bool ok; memset(&ctx, 0, sizeof(ctx)); ctx.cf = cf; ok = parse_ini_file(fn, load_handler, &ctx); free(ctx.cur_sect); if (ok && !ctx.got_main_sect) { log_error("load_init_file: main section missing from config file"); return false; } return ok; } /* * Various value parsers. */ bool cf_set_int(struct CfValue *cv, const char *value) { int *ptr = cv->value_p; char *end; long val; errno = 0; val = strtol(value, &end, 0); if (end == value || *end != 0) { /* reject partial parse */ if (!errno) errno = EINVAL; return false; } *ptr = val; return true; } bool cf_set_uint(struct CfValue *cv, const char *value) { unsigned int *ptr = cv->value_p; char *end; unsigned long val; errno = 0; val = strtoul(value, &end, 0); if (end == value || *end != 0) { /* reject partial parse */ if (!errno) errno = EINVAL; return false; } *ptr = val; return true; } bool cf_set_str(struct CfValue *cv, const char *value) { char **dst_p = cv->value_p; char *tmp = strdup(value); if (!tmp) { log_error("cf_set_str: no mem"); return false; } free(*dst_p); *dst_p = tmp; return true; } bool cf_set_filename(struct CfValue *cv, const char *value) { char **dst_p = cv->value_p; char *tmp, *home, *p; int v_len, usr_len, home_len; struct passwd *pw; /* do we need to do tilde expansion */ if (value[0] != '~') return cf_set_str(cv, value); /* find username end */ v_len = strlen(value); if ((p = memchr(value, '/', v_len)) == NULL) usr_len = v_len - 1; else usr_len = (p - value) - 1; if (usr_len) { p = malloc(usr_len + 1); if (!p) return false; memcpy(p, value + 1, usr_len); p[usr_len] = 0; pw = getpwnam(p); free(p); if (!pw) goto fail; home = pw->pw_dir; } else { home = getenv("HOME"); if (!home) { pw = getpwuid(getuid()); if (!pw) goto fail; home = pw->pw_dir; } } if (!home) goto fail; home_len = strlen(home); tmp = malloc(v_len - usr_len + home_len); if (!tmp) return false; memcpy(tmp, home, home_len); memcpy(tmp + home_len, value + usr_len + 1, v_len - usr_len - 1); tmp[v_len - 1 - usr_len + home_len] = 0; log_debug("expanded '%s' -> '%s'", value, tmp); free(*dst_p); *dst_p = tmp; return true; fail: log_error("cannot to expand filename: %s", value); return false; } /* parse float with error checking. returns -1 if failed */ static double parse_time(const char *value) { double v; char *endp = NULL; errno = 0; v = strtod_dot(value, &endp); if (errno) return -1; if (*endp || endp == value || v < 0) { errno = EINVAL; return -1; } return v; } bool cf_set_time_usec(struct CfValue *cv, const char *value) { usec_t *ptr = cv->value_p; double v = parse_time(value); if (v < 0) return false; *ptr = (usec_t)(USEC * v); return true; } bool cf_set_time_double(struct CfValue *cv, const char *value) { double *ptr = cv->value_p; double v = parse_time(value); if (v < 0) return false; *ptr = v; return true; } /* * Various value formatters. */ const char *cf_get_str(struct CfValue *cv) { char **p = cv->value_p; return *p; } const char *cf_get_int(struct CfValue *cv) { int *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%d", *p); return cv->buf; } const char *cf_get_uint(struct CfValue *cv) { unsigned int *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%u", *p); return cv->buf; } const char *cf_get_time_double(struct CfValue *cv) { double *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%g", *p); return cv->buf; } const char *cf_get_time_usec(struct CfValue *cv) { struct CfValue tmp = *cv; usec_t *p = cv->value_p; double d = (double)(*p) / USEC; tmp.value_p = &d; return cf_get_time_double(&tmp); } /* * str->int mapping */ const char *cf_get_lookup(struct CfValue *cv) { int *p = cv->value_p; const struct CfLookup *lk = cv->extra; for (; lk->name; lk++) { if (lk->value == *p) return lk->name; } return "INVALID"; } bool cf_set_lookup(struct CfValue *cv, const char *value) { int *p = cv->value_p; const struct CfLookup *lk = cv->extra; for (; lk->name; lk++) { if (strcasecmp(lk->name, value) == 0) { *p = lk->value; return true; } } return false; } pgbouncer-1.24.1/lib/usual/talloc.c0000644000175000000000000010165114777762223014035 00000000000000/* * talloc.c - implementation of "talloc" API from Samba. * * Copyright (c) 2013 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #ifndef HAVE_STRNLEN #include /* needed for compat strnlen prototype */ #endif #include #define MAGIC_USED 0xF100F7 /* allocated block */ #define MAGIC_FREE 0x8600CB /* freed block */ #define MAGIC_MASK 0xFFFFFF /* keep only magic */ #define FLAG_PENDING (1 << 24) /* partially freed */ #define FLAG_USE_MEMLIMIT (1 << 25) /* some parent has memlimit */ #define FLAG_HAS_MEMLIMIT (1 << 26) /* current node has TLimit child */ /* flags parent passes to children */ #define INHERIT_FLAGS (FLAG_USE_MEMLIMIT) /* recursion limit */ #define TALLOC_MAX_DEPTH 10000 /* Don't deal with extreme areas */ #define TALLOC_MAXLEN 0x10000000 /* 256MB */ /* header size that is prepended to each pointer */ #define THSIZE (sizeof(struct THeader)) /* * Prefix on each allocated chunk. * * child_list - internal chunks are put into start of list, * use allocations at the end. freeing happens * from start. this makes sure refs are freed * before other objects. */ struct THeader { uint32_t th_flags; /* flags & magic */ uint32_t size; /* requested size */ CxMem *cx; /* low-level allocation context */ struct THeader *parent; /* parent node, may be NULL */ struct List node; /* node in parent->child_list */ struct List child_list; /* contains child->node */ struct List ref_list; /* contains TRef->ref_node */ const char *name; /* pointer to name string */ talloc_destructor_f destructor; /* function to be called on free */ }; /* * Per-reference struct, attached as child to non-primary parent. */ struct TRef { struct List ref_node; /* node in ->ref_list */ struct TRef *paired_ref; /* track paired helper ref */ }; /* * Track memory limits. Attached as child to * the node limits were set on. FLAG_USE_MEMLIMIT says * if it needs to be checked. */ struct TLimit { ssize_t max_size; ssize_t cur_size; }; /* * Internal helper functions. */ static void log_to_stderr(const char *message); static void do_abort(const char *fmt, ...) _NORETURN; static void do_log(const char *fmt, ...); static void do_dbg(const char *fmt, ...); static int ref_destructor(void *ptr); static bool apply_memlimit(struct THeader *parent, ssize_t delta, bool force); static void move_memlimit(struct THeader *t, struct THeader *newparent, struct THeader *oldparent); static void *find_ptr_from_ref(const struct TRef *ref); /* * Global variables. */ /* log callbacks */ static void (*_log_cb)(const char *message) = log_to_stderr; static void (*_abort_cb)(const char *reason); /* context for parent==NULL */ static void *null_context; /* autofree context */ static void *autofree_ctx; /* names for internal allocations */ static const char MEMLIMIT_NAME[] = ".memlimit"; static const char REF_NAME[] = ".ref"; static const char NULL_NAME[] = ".null-context"; static const char AUTOFREE_NAME[] = ".autofree"; static const char UNNAMED_NAME[] = "UNNAMED"; /* flags to atexit callback */ static int leak_report; static int debug_level; void talloc_set_debug(int level) { debug_level = level; } /* * Internal utils. */ static inline bool has_flags(const struct THeader *t, uint32_t flags) { return (t->th_flags & flags) > 0; } static inline void set_flags(struct THeader *t, uint32_t flags) { t->th_flags |= flags; } static inline void clear_flags(struct THeader *t, uint32_t flags) { t->th_flags &= ~flags; } static inline bool hdr_is_ref(const struct THeader *t) { if (t->destructor == ref_destructor) return true; return false; } static inline void check_magic(const struct THeader *t, const char *pos) { uint32_t magic = t->th_flags & MAGIC_MASK; if (magic != MAGIC_USED) { if (magic == MAGIC_FREE) do_abort("Use after free - %s", pos); else do_abort("Invalid magic - %s", pos); } } static inline void *hdr2ptr(const struct THeader *t) { if (!t) return NULL; check_magic(t, "hdr2ptr"); return (void *)(t + 1); } static inline struct THeader *ptr2hdr(const void *ptr) { struct THeader *t; if (!ptr) return NULL; t = ((struct THeader *)ptr) - 1; check_magic(t, "ptr2hdr"); return t; } static inline size_t total_size(size_t alloc) { return ALIGN(alloc) + THSIZE; } /* if FLAG_CXOWNER is set, this->cx is for children */ static CxMem *get_owner_cx(struct THeader *t) { return t->cx; } /* add refs to start, others to end */ static void add_child(struct THeader *parent, struct THeader *child) { if (parent) { if (hdr_is_ref(child)) list_prepend(&parent->child_list, &child->node); else list_append(&parent->child_list, &child->node); } } /* * actual alloc */ static struct THeader *hdr_alloc_cx(CxMem *cx, struct THeader *parent, size_t len, bool prepend) { struct THeader *t; if (len > TALLOC_MAXLEN) return NULL; if (!parent) parent = ptr2hdr(null_context); if (!apply_memlimit(parent, total_size(len), false)) return NULL; t = cx_alloc(cx, total_size(len)); if (!t) { apply_memlimit(parent, -total_size(len), false); return NULL; } t->th_flags = MAGIC_USED; t->size = len; t->cx = cx; t->parent = parent; list_init(&t->node); list_init(&t->child_list); list_init(&t->ref_list); t->name = NULL; t->destructor = NULL; if (parent) { set_flags(t, parent->th_flags & INHERIT_FLAGS); if (prepend) list_prepend(&parent->child_list, &t->node); else list_append(&parent->child_list, &t->node); } return t; } /* * Allocate */ void *talloc_from_cx(CxMem *cx, size_t len, const char *name) { struct THeader *t; t = hdr_alloc_cx(cx, NULL, len, false); if (!t) return NULL; t->name = name; return hdr2ptr(t); } void *_talloc_const_name(const void *parent, size_t elem_size, size_t count, bool zerofill, const char *name) { struct THeader *t; void *res; size_t size; struct THeader *tparent; CxMem *cx; if (!safe_mul_size(&size, elem_size, count)) return NULL; tparent = ptr2hdr(parent); cx = tparent ? tparent->cx : NULL; t = hdr_alloc_cx(cx, tparent, size, false); if (!t) return NULL; res = hdr2ptr(t); if (zerofill) memset(res, 0, size); t->name = name; return res; } void *_talloc_format_name(const void *parent, size_t elem_size, size_t count, bool zerofill, const char *fmt, ...) { void *res; va_list ap; void *name; res = _talloc_const_name(parent, elem_size, count, zerofill, NULL); if (res) { va_start(ap, fmt); name = talloc_vasprintf(res, fmt, ap); va_end(ap); if (!name) { talloc_free(res); return NULL; } talloc_set_name_const(res, name); } return res; } /* alloc node and put into start of child_list */ static void *internal_alloc_prepend(const void *parent, size_t size, const char *name) { struct THeader *t; void *res; struct THeader *tparent; CxMem *cx; tparent = ptr2hdr(parent); cx = tparent ? tparent->cx : NULL; t = hdr_alloc_cx(cx, tparent, size, true); if (!t) return NULL; res = hdr2ptr(t); t->name = name; return res; } /* * Freeing */ #undef talloc_set_destructor void talloc_set_destructor(const void *ptr, talloc_destructor_f destructor) { struct THeader *t; t = ptr2hdr(ptr); if (t) t->destructor = destructor; } /* attach undying child to live parent */ static void throw_child(struct THeader *t) { struct THeader *parent = t->parent; while (parent && has_flags(parent, FLAG_PENDING)) parent = parent->parent; talloc_reparent(hdr2ptr(t->parent), hdr2ptr(parent), hdr2ptr(t)); } static void free_children(const void *ptr, bool free_name, const char *source_pos) { struct List *el, *tmp; struct THeader *tchild; struct THeader *t; void *child; t = ptr2hdr(ptr); if (!t) return; list_for_each_safe(el, &t->child_list, tmp) { tchild = container_of(el, struct THeader, node); child = hdr2ptr(tchild); if (free_name) { if (child == t->name) t->name = NULL; } else if (child == t->name) { continue; } else if (tchild->name == MEMLIMIT_NAME) { continue; } if (talloc_unlink(ptr, child) != 0) { //do_dbg("DBG: free_children: unlink failed: %s", talloc_get_name(child)); throw_child(tchild); } } } void _talloc_free_children(const void *ptr, const char *source_pos) { free_children(ptr, false, source_pos); } /* what happens when refs are present */ static int free_with_refs(struct THeader *t, const char *source_pos) { struct List *el; struct TRef *ref = NULL; struct THeader *tref; if (t->parent == NULL || hdr2ptr(t->parent) == null_context) { return _talloc_unlink(NULL, hdr2ptr(t->parent), source_pos); } /* check if refs have same parent */ list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent != t->parent) { do_dbg("free_with_refs: parent fail"); return -1; } } /* always same parent, drop one ref */ return _talloc_free(ref, source_pos); } int _talloc_free(const void *ptr, const char *source_pos) { CxMem *cx; struct THeader *t; struct THeader *tparent; size_t orig_size; do_dbg("DBG: talloc_free(%p) (%s)", ptr, talloc_get_name(ptr)); if (!ptr) return -1; t = ptr2hdr(ptr); /* handle multi-parent free */ if (!list_empty(&t->ref_list)) return free_with_refs(t, source_pos); /* set pending flag */ if (has_flags(t, FLAG_PENDING)) return 0; set_flags(t, FLAG_PENDING); /* run destructor */ if (t->destructor && t->destructor((void *)ptr) < 0) { do_dbg("DBG: talloc_free(%s) - destructor failed", talloc_get_name(ptr)); clear_flags(t, FLAG_PENDING); return -1; } list_del(&t->node); free_children(ptr, true, source_pos); tparent = t->parent; orig_size = t->size; cx = t->cx; /* clear & free */ memset(t, 0, THSIZE); t->size = orig_size; t->th_flags = MAGIC_FREE; t->name = source_pos; cx_free(cx, t); apply_memlimit(tparent, -total_size(orig_size), false); return 0; } /* * Refs */ static struct THeader *find_ref_by_parent(struct THeader *t, struct THeader *tparent) { struct List *el; struct THeader *tref; struct TRef *ref; list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent == tparent) return tref; } return NULL; } /* remove TRef from ->ref_list */ static int ref_destructor(void *ptr) { struct TRef *ref = ptr; list_del(&ref->ref_node); if (ref->paired_ref) { ref->paired_ref->paired_ref = NULL; ref->paired_ref = NULL; } return 0; } static struct TRef *new_ref(const void *parent, const char *name) { struct TRef *ref; ref = internal_alloc_prepend(parent, sizeof(struct TRef), name ? name : REF_NAME); if (!ref) return NULL; ref->paired_ref = NULL; list_init(&ref->ref_node); talloc_set_destructor(ref, ref_destructor); return ref; } void *_talloc_reference_named(const void *new_parent, const void *ptr, const char *name) { struct TRef *ref; struct THeader *t; t = ptr2hdr(ptr); if (!t) return NULL; ref = new_ref(new_parent, name); if (!ref) return NULL; list_append(&t->ref_list, &ref->ref_node); return (void *)ptr; } int _talloc_unlink(const void *parent, const void *ptr, const char *source_pos) { struct TRef *ref; struct THeader *tref = NULL; struct THeader *tparent; struct THeader *t; int err; t = ptr2hdr(ptr); if (!t) { do_dbg("_talloc_unlink err: no ptr"); return -1; } tparent = ptr2hdr(parent ? parent : null_context); if (t->parent != tparent) { /* ref is not primary */ tref = find_ref_by_parent(t, tparent); if (tref) { err = _talloc_free(hdr2ptr(tref), source_pos); } else { do_dbg("_talloc_unlink err: find_ref_by_parent failed"); err = -1; } } else if (list_empty(&t->ref_list)) { /* ref is primary and there are no other refs */ err = _talloc_free(ptr, source_pos); } else { /* main parent but refs, move to new parent */ /* use first ref to get new parent */ ref = list_pop_type(&t->ref_list, struct TRef, ref_node); tref = ptr2hdr(ref); list_del(&t->node); /* move */ t->parent = tref->parent; add_child(t->parent, t); /* free ref */ err = _talloc_free(ref, source_pos); } return err; } /* * Parent change */ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr) { struct THeader *tnew; struct THeader *told; struct THeader *t; CxMem *cxnew; t = ptr2hdr(ptr); if (!t) return NULL; tnew = ptr2hdr(new_parent ? new_parent : null_context); told = ptr2hdr(old_parent ? old_parent : null_context); if (tnew == t || tnew == told) return (void *)ptr; cxnew = tnew ? tnew->cx : NULL; /* find ref to change parent of */ if (told != t->parent) { t = find_ref_by_parent(t, told); if (!t) { do_log("talloc_reparent failed: did not find old parent\n"); return NULL; } } /* check cx change */ if (t->cx != cxnew) { return NULL; } /* change parent */ list_del(&t->node); add_child(tnew, t); t->parent = tnew; move_memlimit(t, tnew, told); return (void *)ptr; } void *talloc_steal(const void *new_parent, const void *ptr) { struct THeader *t; t = ptr2hdr(ptr); if (!t) return NULL; /* disallow steal when refs are present */ if (!list_empty(&t->ref_list)) return NULL; return talloc_reparent(hdr2ptr(t->parent), new_parent, ptr); } void *_talloc_move(const void *new_parent, void **ptr_p) { void *ptr; ptr = talloc_steal(new_parent, *ptr_p); if (ptr) *ptr_p = NULL; return ptr; } /* * Realloc */ /* node address has moved */ static void fix_list(struct List *node, struct List *oldnode) { if (node->next == oldnode) { list_init(node); } else { node->next->prev = node; node->prev->next = node; } } void *_talloc_realloc(const void *parent, void *ptr, size_t elem_size, size_t count, const char *name) { struct THeader *t1, *t2; struct List *el; CxMem *this_cx; uint32_t old_flags; ssize_t delta; size_t size; /* calc total size */ if (!safe_mul_size(&size, elem_size, count)) return NULL; if (size > TALLOC_MAXLEN) return NULL; /* posix realloc behaviour */ if (!ptr) { if (size == 0) return NULL; return talloc_named_const(parent, size, name); } else if (size == 0) { if (talloc_unlink(parent, ptr) != 0) if (0) do_log("realloc(size=0): unlink failed\n"); return NULL; } t1 = ptr2hdr(ptr); /* disallow realloc when refs are present */ if (!list_empty(&t1->ref_list)) return NULL; /* size difference */ delta = size - t1->size; if (delta == 0) return ptr; /* check limits */ if (!apply_memlimit(t1->parent, delta, false)) return NULL; /* actual realloc of memory */ this_cx = get_owner_cx(t1); old_flags = t1->th_flags; t1->th_flags = MAGIC_FREE; t2 = cx_realloc(this_cx, t1, total_size(size)); if (!t2) { apply_memlimit(t1->parent, -delta, false); t1->th_flags = old_flags; return NULL; } /* fix header after realloc */ t2->th_flags = old_flags; t2->size = size; t2->name = name; /* was memory moved? */ if (t1 == t2) return ptr; /* fix lists after move */ fix_list(&t2->node, &t1->node); fix_list(&t2->child_list, &t1->child_list); fix_list(&t2->ref_list, &t1->ref_list); list_for_each(el, &t2->child_list) { struct THeader *tchild; tchild = container_of(el, struct THeader, node); tchild->parent = t2; } return hdr2ptr(t2); } void *talloc_realloc_fn(const void *parent, void *ptr, size_t size) { return _talloc_realloc(parent, ptr, 1, size, "talloc_realloc_fn"); } /* * memlimit */ /* apply delta to single context */ static bool apply_memlimit_marked(struct THeader *t, ssize_t delta, bool force) { struct List *el; struct THeader *tlim; struct TLimit *lim = NULL; /* find memlimit struct */ list_for_each(el, &t->child_list) { tlim = container_of(el, struct THeader, node); if (tlim->name == MEMLIMIT_NAME) { lim = hdr2ptr(tlim); goto apply; } } return true; apply: /* check limit */ if (delta > 0 && !force) { if (lim->cur_size + delta > lim->max_size) return false; } /* update parent first */ if (!apply_memlimit(t->parent, delta, force)) return false; /* parent is ok, safe to update current struct */ lim->cur_size += delta; if (lim->cur_size < 0) lim->cur_size = 0; return true; } /* apply delta recursively */ static bool apply_memlimit(struct THeader *parent, ssize_t delta, bool force) { struct THeader *t = parent; while (t && has_flags(t, FLAG_USE_MEMLIMIT)) { if (has_flags(t, FLAG_HAS_MEMLIMIT)) return apply_memlimit_marked(t, delta, force); t = t->parent; } return true; } enum { OP_NONE = 0, OP_SET_MEMLIMIT = 1, OP_CLEAR_MEMLIMIT = 2, }; /* count allocated memory and sync flags */ static size_t memlimit_walk(struct THeader *t, int depth, int op) { struct List *el; struct THeader *tchild; size_t size = 0; if (has_flags(t, FLAG_PENDING)) return 0; /* sync memlimit flags */ if (op == OP_SET_MEMLIMIT) { set_flags(t, FLAG_USE_MEMLIMIT); } else if (op == OP_CLEAR_MEMLIMIT) { if (has_flags(t, FLAG_HAS_MEMLIMIT)) op = OP_NONE; else clear_flags(t, FLAG_USE_MEMLIMIT); } /* avoid too deep recursion */ if (depth > TALLOC_MAX_DEPTH) return t->size; /* recurse info child_list */ set_flags(t, FLAG_PENDING); list_for_each(el, &t->child_list) { tchild = container_of(el, struct THeader, node); size += memlimit_walk(tchild, depth + 1, op); } clear_flags(t, FLAG_PENDING); return size + t->size; } static void move_memlimit(struct THeader *t, struct THeader *new_parent, struct THeader *old_parent) { bool oldlim, newlim; ssize_t delta; int op = OP_NONE; /* is memlimit in use? */ newlim = new_parent && has_flags(new_parent, FLAG_USE_MEMLIMIT); oldlim = old_parent && has_flags(old_parent, FLAG_USE_MEMLIMIT); if (!oldlim && !newlim) return; if (oldlim && !newlim) op = OP_CLEAR_MEMLIMIT; else if (newlim && !oldlim) op = OP_SET_MEMLIMIT; /* yes, calc memory size */ delta = memlimit_walk(t, 0, op); /* subtract from old parent */ if (oldlim) apply_memlimit(old_parent, -delta, true); if (newlim) { /* add to new parent */ apply_memlimit(new_parent, delta, true); set_flags(t, FLAG_USE_MEMLIMIT); } else if (!has_flags(t, FLAG_HAS_MEMLIMIT)) { /* drop flag to avoid unnecessary walks */ clear_flags(t, FLAG_USE_MEMLIMIT); } } /* configure memory limits */ int talloc_set_memlimit(const void *ptr, size_t max_size) { struct TLimit *lim = NULL; struct THeader *t, *tmp; struct List *el; if (!ptr) return -1; t = ptr2hdr(ptr); /* find TLimit struct */ if (has_flags(t, FLAG_HAS_MEMLIMIT)) { list_for_each(el, &t->child_list) { tmp = container_of(el, struct THeader, node); if (tmp->name == MEMLIMIT_NAME) { lim = hdr2ptr(tmp); break; } } } /* disable memlimit */ if (max_size == 0) { clear_flags(t, FLAG_HAS_MEMLIMIT); if (lim) talloc_free(lim); return 0; } /* allocate new object */ if (!lim) { lim = internal_alloc_prepend(ptr, sizeof(struct TLimit), MEMLIMIT_NAME); if (!lim) return -1; } /* configure */ lim->max_size = max_size; lim->cur_size = 0; set_flags(t, FLAG_USE_MEMLIMIT | FLAG_HAS_MEMLIMIT); return 0; } /* * Name handling */ const char *talloc_get_name(const void *ptr) { struct THeader *t = ptr2hdr(ptr); return (t && t->name) ? t->name : UNNAMED_NAME; } const char *talloc_set_name(const void *ptr, const char *fmt, ...) { va_list ap; struct THeader *t; t = ptr2hdr(ptr); if (t) { va_start(ap, fmt); t->name = talloc_vasprintf(ptr, fmt, ap); va_end(ap); return t->name; } return NULL; } void talloc_set_name_const(const void *ptr, const char *name) { struct THeader *t; t = ptr2hdr(ptr); if (t) t->name = name; } void *talloc_check_name(const void *ptr, const char *name) { const char *curname; curname = talloc_get_name(ptr); if (curname && name && strcmp(name, curname) == 0) return (void *)ptr; return NULL; } void *_talloc_get_type_abort(const void *ptr, const char *name) { void *res; res = talloc_check_name(ptr, name); if (res) return (void *)ptr; do_abort("wrong type"); } /* * Info */ size_t talloc_get_size(const void *ptr) { struct THeader *t; if (!ptr) return 0; t = ptr2hdr(ptr); return t->size; } bool talloc_is_parent(const void *parent, const void *ptr) { struct THeader *tc; struct THeader *tp; int count = 0; if (!ptr || !parent) return false; tp = ptr2hdr(parent); tc = ptr2hdr(ptr); while (tc) { if (tc->parent == tp) return true; tc = tc->parent; /* dont bother too much */ if (++count >= TALLOC_MAX_DEPTH) break; } return false; } size_t talloc_reference_count(const void *ptr) { struct List *el; size_t cnt = 0; struct THeader *t; t = ptr2hdr(ptr); if (t) { list_for_each(el, &t->ref_list) cnt++; } return cnt; } struct BytesAndCount { size_t bytes; size_t count; }; static void calc_bytes_and_count(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { struct BytesAndCount *state = cb_arg; state->count++; if (!is_ref) { state->bytes += talloc_get_size(ptr); } } size_t talloc_total_size(const void *ptr) { struct BytesAndCount state; memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); return state.bytes; } size_t talloc_total_blocks(const void *ptr) { struct BytesAndCount state; memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); return state.count; } void *talloc_parent(const void *ptr) { struct THeader *t; t = ptr2hdr(ptr); if (t && t->parent) return hdr2ptr(t->parent); return NULL; } const char *talloc_parent_name(const void *ptr) { return talloc_get_name(talloc_parent(ptr)); } void *talloc_find_parent_byname(const void *ptr, const char *name) { struct List *el; struct THeader *tref; struct TRef *ref; struct THeader *t; t = ptr2hdr(ptr); if (!t || !name) return NULL; if (t->parent && !strcmp(name, t->parent->name)) return hdr2ptr(t->parent); list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent && !strcmp(name, tref->parent->name)) return hdr2ptr(tref->parent); } return NULL; } /* * String copy */ void *talloc_memdup(const void *parent, const void *src, size_t len) { void *res = NULL; if (src) { res = talloc_named_const(parent, len, "talloc_memdup"); if (res) memcpy(res, src, len); } return res; } char *talloc_strdup(const void *parent, const char *s) { return talloc_strndup(parent, s, TALLOC_MAXLEN); } char *talloc_strndup(const void *parent, const char *s, size_t maxlen) { size_t len; char *res; if (!s) return NULL; len = strnlen(s, maxlen); res = talloc_named_const(parent, len + 1, NULL); if (!res) return NULL; memcpy(res, s, len); res[len] = 0; talloc_set_name_const(res, res); return res; } /* * string append */ static size_t buffer_strlen(const void *ptr) { size_t len = talloc_get_size(ptr); return len ? len - 1 : 0; } static char *_concat(char *ptr, bool isbuf, const char *s, size_t maxlen) { size_t plen; size_t slen; /* simple cases */ if (!ptr) return talloc_strndup(ptr, s, maxlen); if (!s) return ptr; /* get lengths */ if (isbuf) { plen = buffer_strlen(ptr); } else { plen = strnlen(ptr, talloc_get_size(ptr)); } slen = strnlen(s, maxlen); /* resize and copy */ ptr = talloc_realloc_fn(ptr, ptr, plen + slen + 1); if (!ptr) return NULL; memcpy(ptr + plen, s, slen + 1); talloc_set_name_const(ptr, ptr); return ptr; } char *talloc_strdup_append(char *ptr, const char *s) { return _concat(ptr, false, s, TALLOC_MAXLEN); } char *talloc_strdup_append_buffer(char *ptr, const char *s) { return _concat(ptr, true, s, TALLOC_MAXLEN); } char *talloc_strndup_append(char *ptr, const char *s, size_t maxlen) { return _concat(ptr, false, s, maxlen); } char *talloc_strndup_append_buffer(char *ptr, const char *s, size_t maxlen) { return _concat(ptr, true, s, maxlen); } /* * printfs */ _PRINTF(4,0) static char *_tprintf(const void *parent, char *ptr, size_t plen, const char *fmt, va_list ap) { char buf[128]; ssize_t len; va_list ap2; char *res; /* print into temp buffer */ va_copy(ap2, ap); len = vsnprintf(buf, sizeof(buf), fmt, ap2); va_end(ap2); if (len < 0) return NULL; /* reserve room */ res = talloc_realloc_fn(parent, ptr, plen + len + 1); if (!res) return NULL; /* fill with string */ if (len < (int)sizeof(buf)) { memcpy(res + plen, buf, len + 1); } else { va_copy(ap2, ap); vsnprintf(res + plen, len + 1, fmt, ap2); va_end(ap2); } talloc_set_name_const(res, res); return res; } char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap) { return _tprintf(parent, NULL, 0, fmt, ap); } char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap) { size_t plen = strnlen(ptr, talloc_get_size(ptr)); return _tprintf(NULL, ptr, plen, fmt, ap); } char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap) { size_t plen = buffer_strlen(ptr); return _tprintf(NULL, ptr, plen, fmt, ap); } char *talloc_asprintf(const void *parent, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf(parent, fmt, ap); va_end(ap); return res; } char *talloc_asprintf_append(char *ptr, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf_append(ptr, fmt, ap); va_end(ap); return res; } char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf_append_buffer(ptr, fmt, ap); va_end(ap); return res; } /* * Autofree */ /* run on program exit */ static void autofree_handler(void) { TALLOC_FREE(autofree_ctx); } static int autofree_destructor(void *ptr) { autofree_ctx = NULL; return 0; } /* create context, register handler with atexit */ void *talloc_autofree_context(void) { static int atexit_ok; /* register atexit handler */ if (!atexit_ok) { if (atexit(autofree_handler) != 0) return NULL; atexit_ok = 1; } /* initialize autofree top-level context */ if (!autofree_ctx) { autofree_ctx = talloc_named_const(NULL, 0, AUTOFREE_NAME); if (!autofree_ctx) return NULL; talloc_set_destructor(autofree_ctx, autofree_destructor); } return autofree_ctx; } /* * Logging */ static void log_to_stderr(const char *message) { fprintf(stderr, "%s\n", message); } _PRINTF(1, 2) static void do_log(const char *fmt, ...) { char buf[128]; va_list ap; if (!_log_cb) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); _log_cb(buf); } _PRINTF(1, 2) static void do_dbg(const char *fmt, ...) { char buf[128]; va_list ap; if (!_log_cb || debug_level == 0) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); _log_cb(buf); } _PRINTF(1, 0) static void do_abort(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (_abort_cb) _abort_cb(buf); else if (_log_cb) _log_cb(buf); abort(); } void talloc_set_log_fn(void (*log_fn)(const char *message)) { _log_cb = log_fn; } void talloc_set_log_stderr(void) { _log_cb = log_to_stderr; } void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) { _abort_cb = abort_fn; } /* * Tracking. */ static void atexit_leak_report(void) { if (leak_report == 2) talloc_report_full(NULL, stderr); else talloc_report(NULL, stderr); } /* activate null context as child of autofree context */ void talloc_enable_null_tracking(void) { void *ctx; if (!null_context) { ctx = talloc_named_const(NULL, 0, NULL_NAME); if (ctx && autofree_ctx) talloc_reparent(NULL, ctx, autofree_ctx); null_context = ctx; } } /* activate null context */ void talloc_enable_null_tracking_no_autofree(void) { if (!null_context) null_context = talloc_named_const(NULL, 0, NULL_NAME); } /* move childs away from null context */ void talloc_disable_null_tracking(void) { struct THeader *t, *tchild; struct List *el, *tmp; if (!null_context) return; t = ptr2hdr(null_context); list_for_each_safe(el, &t->child_list, tmp) { tchild = container_of(el, struct THeader, node); list_del(&tchild->node); tchild->parent = NULL; } TALLOC_FREE(null_context); } void talloc_enable_leak_report(void) { if (!leak_report) atexit(atexit_leak_report); leak_report = 1; } void talloc_enable_leak_report_full(void) { if (!leak_report) atexit(atexit_leak_report); leak_report = 2; } /* * Reporting */ static void *find_ptr_from_ref(const struct TRef *ref) { struct List *el; struct TRef *ref2; struct THeader *tref2; struct THeader *t; list_for_each(el, &ref->ref_node) { /* * Actual struct is not known here - THeader * must have both ->destructor & ->ref_list * accessible locations. */ ref2 = container_of(el, struct TRef, ref_node); /* this check must work it out */ tref2 = ((struct THeader *)ref2) - 1; if (hdr_is_ref(tref2)) continue; /* it is not TRef, so it must be parent's THeader */ t = container_of(el, struct THeader, ref_list); return hdr2ptr(t); } return NULL; } void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*cb_func)(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg), void *cb_arg) { struct List *el; struct THeader *tchild; struct THeader *t; t = ptr2hdr(ptr); if (!t) t = ptr2hdr(null_context); if (!t) return; if (has_flags(t, FLAG_PENDING)) return; /* run callback */ if (hdr_is_ref(t)) { void *ptr2 = find_ptr_from_ref(ptr); if (ptr2) cb_func(ptr2, depth, max_depth, true, cb_arg); return; } cb_func(ptr, depth, max_depth, false, cb_arg); /* check depth */ depth++; if (depth > max_depth) return; /* loop over childs */ set_flags(t, FLAG_PENDING); list_for_each(el, &t->child_list) { tchild = container_of(el, struct THeader, node); talloc_report_depth_cb(hdr2ptr(tchild), depth, max_depth, cb_func, cb_arg); } clear_flags(t, FLAG_PENDING); } static void report_cb(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { FILE *f = cb_arg; struct BytesAndCount state; const char *name; int indent; char limitbuf[128]; struct THeader *t; indent = depth * 2; t = ptr2hdr(ptr); name = talloc_get_name(ptr); limitbuf[0] = 0; if (name == MEMLIMIT_NAME) { struct TLimit *lim = hdr2ptr(t); snprintf(limitbuf, sizeof(limitbuf), "%s [cur=%zu max=%zu]", name, lim->cur_size, lim->max_size); name = limitbuf; } memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); if (depth == 0) { fprintf(f, "talloc report on '%s' (total %zu bytes in %zu blocks)%s\n", name, state.bytes, state.count, limitbuf); return; } if (is_ref) { fprintf(f, "%*sreference to %s\n", indent, " ", name); return; } fprintf(f, "%*s%-*s contains %6zu bytes in %6zu blocks%s\n", indent, " ", indent < 40 ? 40 - indent : 0, name, state.bytes, state.count, limitbuf); } void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) { talloc_report_depth_cb(ptr, depth, max_depth, report_cb, f); } void talloc_report(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, 1, f); } void talloc_report_full(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, TALLOC_MAX_DEPTH, f); } void talloc_show_parents(const void *ptr, FILE *file) { struct THeader *t, *tref; struct TRef *ref; struct List *el; if (!ptr) { fprintf(file, "No parents for NULL\n"); return; } fprintf(file, "Parents for '%s'\n", talloc_get_name(ptr)); t = ptr2hdr(ptr); if (t->parent) { fprintf(file, "\t%s\n", talloc_get_name(hdr2ptr(t->parent))); } else { fprintf(file, "\tNULL context\n"); } list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); fprintf(file, "\t%s\n", talloc_get_name(hdr2ptr(tref->parent))); } } /* * Talloc-backed CxMem. * * Makes CxMem modules work with talloc. */ static void *cxt_alloc(void *ctx, size_t size) { return talloc_size(ctx, size); } static void cxt_free(void *ctx, void *ptr) { if (talloc_unlink(ctx, ptr) != 0) do_log("cxt_free: talloc_unlink failed\n"); } static void *cxt_realloc(void *ctx, void *ptr, size_t len) { return talloc_realloc_size(ctx, ptr, len); } static void cxt_destroy(void *ctx) { if (talloc_free(ctx) != 0) do_log("cxt_destroy: talloc_free failed\n"); } static const struct CxOps cxt_ops = { cxt_alloc, cxt_realloc, cxt_free, cxt_destroy, }; CxMem *talloc_as_cx(const void *parent, const char *name) { struct CxMem *cx; if (!name) name = ".cxmem"; cx = talloc_named_const(parent, sizeof(struct CxMem), name); if (!cx) return NULL; cx->ops = &cxt_ops; cx->ctx = cx; return cx; } pgbouncer-1.24.1/lib/usual/aatree.h0000644000175000000000000000533314777762223014025 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * AA-Tree - Binary tree with embeddable nodes. * * AA-Tree (Arne Andersson tree) is a simplified Red-Black tree. */ #ifndef _USUAL_AATREE_H_ #define _USUAL_AATREE_H_ #include struct AATree; struct AANode; /** Callback for node comparision against value */ typedef int (*aatree_cmp_f)(uintptr_t, struct AANode *node); /** Callback for walking the tree */ typedef void (*aatree_walker_f)(struct AANode *n, void *arg); /** * Tree header, for storing helper functions. */ struct AATree { struct AANode *root; int count; aatree_cmp_f node_cmp; aatree_walker_f release_cb; }; /** * Tree node. Embeddable, parent structure should be taken * with container_of(). * * Techinally, the full level is not needed and 2-lowest * bits of either ->left or ->right would be enough * to keep track of structure. Currently this is not * done to keep code simple. */ struct AANode { struct AANode *left; /**< smaller values */ struct AANode *right; /**< larger values */ int level; /**< number of black nodes to leaf */ }; /** * Walk order types. */ enum AATreeWalkType { AA_WALK_IN_ORDER = 0, /* left->self->right */ AA_WALK_PRE_ORDER = 1, /* self->left->right */ AA_WALK_POST_ORDER = 2, /* left->right->self */ }; /** Initialize structure */ void aatree_init(struct AATree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb); /** Search for node */ struct AANode *aatree_search(struct AATree *tree, uintptr_t value); /** Insert new node */ void aatree_insert(struct AATree *tree, uintptr_t value, struct AANode *node); /** Remote node */ void aatree_remove(struct AATree *tree, uintptr_t value); /** Walk over all nodes */ void aatree_walk(struct AATree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg); /** Free */ void aatree_destroy(struct AATree *tree); /** Check if terminal node. */ static inline int aatree_is_nil_node(const struct AANode *node) { return (node->left == node); } #endif pgbouncer-1.24.1/lib/usual/cxalloc.c0000644000175000000000000000625514777762223014210 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * Utility routines for cx_* API. */ void *cx_alloc(CxMem *cx, size_t len) { if (!len) return NULL; if (!cx) cx = USUAL_ALLOC; return cx->ops->c_alloc(cx->ctx, len); } void *cx_realloc(CxMem *cx, void *ptr, size_t len) { if (!cx) cx = USUAL_ALLOC; if (!ptr) return cx_alloc(cx, len); if (!len) { cx_free(cx, ptr); return NULL; } return cx->ops->c_realloc(cx->ctx, ptr, len); } void cx_free(CxMem *cx, void *ptr) { if (!cx) cx = USUAL_ALLOC; if (ptr) cx->ops->c_free(cx->ctx, ptr); } void cx_destroy(CxMem *cx) { if (!cx) return; if (!cx->ops->c_destroy) abort(); cx->ops->c_destroy(cx->ctx); } void *cx_alloc0(CxMem *cx, size_t len) { void *p = cx_alloc(cx, len); if (p) memset(p, 0, len); return p; } void *cx_memdup(CxMem *cx, const void *src, size_t len) { void *p = cx_alloc(cx, len); if (p) memcpy(p, src, len); return p; } void *cx_strdup(CxMem *cx, const char *s) { return cx_memdup(cx, s, strlen(s) + 1); } char *cx_sprintf(CxMem *cx, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = cx_vsprintf(cx, fmt, ap); va_end(ap); return res; } char *cx_vsprintf(CxMem *cx, const char *fmt, va_list ap) { char *res; cx_vasprintf(cx, &res, fmt, ap); return res; } int cx_asprintf(CxMem *cx, char **dst_p, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = cx_vasprintf(cx, dst_p, fmt, ap); va_end(ap); return res; } int cx_vasprintf(CxMem *cx, char **dst_p, const char *fmt, va_list ap) { char buf[128], *dst; int res, res2; *dst_p = NULL; res = vsnprintf(buf, sizeof buf, fmt, ap); if (res < 0) return -1; dst = cx_alloc(cx, res + 1); if (!dst) return -1; if ((size_t)res < sizeof buf) { memcpy(dst, buf, res+1); } else { res2 = vsnprintf(dst, res+1, fmt, ap); if (res2 != res) { cx_free(cx, dst); return -1; } } *dst_p = dst; return res; } /* * Base allocator that uses libc routines. */ static void *libc_alloc(void *ctx, size_t len) { return malloc(len); } static void *libc_realloc(void *ctx, void *ptr, size_t len) { return realloc(ptr, len); } static void libc_free(void *ctx, void *ptr) { free(ptr); } static const struct CxOps libc_alloc_ops = { libc_alloc, libc_realloc, libc_free, }; const struct CxMem cx_libc_allocator = { &libc_alloc_ops, NULL, }; pgbouncer-1.24.1/lib/usual/socket.c0000644000175000000000000002523614777762223014053 00000000000000/* * Socket helpers and compat. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifdef HAVE_UCRED_H #include #endif #ifdef HAVE_SYS_UCRED_H #include #endif /* toggle non-blocking flag */ bool socket_set_nonblocking(int fd, bool non_block) { int flags; /* get old flags */ flags = fcntl(fd, F_GETFL, 0); if (flags < 0) return false; /* flip O_NONBLOCK */ if (non_block) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; /* set new flags */ if (fcntl(fd, F_SETFL, flags) < 0) return false; return true; } /* initial socket setup */ bool socket_setup(int sock, bool non_block) { int res; #ifdef SO_NOSIGPIPE /* disallow SIGPIPE, if possible */ int val = 1; res = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)); if (res < 0) return false; #endif /* close fd on exec */ res = fcntl(sock, F_SETFD, FD_CLOEXEC); if (res < 0) return false; /* when no data available, return EAGAIN instead blocking */ if (!socket_set_nonblocking(sock, non_block)) return false; return true; } bool socket_set_keepalive(int fd, int onoff, int keepidle, int keepintvl, int keepcnt) { int val, res; if (!onoff) { /* turn keepalive off */ val = 0; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); return (res == 0); } /* turn keepalive on */ val = 1; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); if (res < 0) return false; /* Darwin */ #ifdef TCP_KEEPALIVE if (keepidle) { val = keepidle; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif /* Linux, NetBSD */ #ifdef TCP_KEEPIDLE if (keepidle) { val = keepidle; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif #ifdef TCP_KEEPINTVL if (keepintvl) { val = keepintvl; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif #ifdef TCP_KEEPCNT if (keepcnt > 0) { val = keepcnt; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif /* Windows */ #ifdef SIO_KEEPALIVE_VALS if (keepidle || keepintvl) { struct tcp_keepalive vals; DWORD outlen = 0; if (!keepidle) keepidle = 5 * 60; if (!keepintvl) keepintvl = 15; vals.onoff = 1; vals.keepalivetime = keepidle * 1000; vals.keepaliveinterval = keepintvl * 1000; res = WSAIoctl(fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0, &outlen, NULL, NULL); if (res != 0) return false; } #endif return true; } /* * Convert sockaddr to string. Supports ipv4, ipv6 and unix sockets. */ const char *sa2str(const struct sockaddr *sa, char *dst, size_t dstlen) { const struct sockaddr_in *in; const struct sockaddr_in6 *in6; const struct sockaddr_un *un; const char *tmp; char buf[128]; switch (sa->sa_family) { case AF_INET: in = (struct sockaddr_in *)sa; tmp = inet_ntop(AF_INET, &in->sin_addr, buf, sizeof(buf)); if (!tmp) return NULL; snprintf(dst, dstlen, "%s:%d", tmp, ntohs(in->sin_port)); break; case AF_INET6: in6 = (struct sockaddr_in6 *)sa; tmp = inet_ntop(AF_INET6, &in6->sin6_addr, buf, sizeof(buf)); if (!tmp) return NULL; snprintf(dst, dstlen, "[%s]:%d", tmp, ntohs(in6->sin6_port)); break; case AF_UNIX: un = (struct sockaddr_un *)sa; if (un->sun_path[0] == '\0' && un->sun_path[1] != '\0') snprintf(dst, dstlen, "unix:@%s", un->sun_path + 1); else snprintf(dst, dstlen, "unix:%s", un->sun_path); break; default: snprintf(dst, dstlen, "sa2str(%d): unknown proto", sa->sa_family); break; } return dst; } #ifndef HAVE_GETPEEREID /* * Get other side's uid and gid for UNIX socket. */ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p) { pid_t pid; return getpeercreds(fd, uid_p, gid_p, &pid); } #endif /* * Get uid, gid and pid of unix socket peer. * * Pid may not be availalbe on some OSes. * It's set to 0 then. */ int getpeercreds(int fd, uid_t *uid_p, gid_t *gid_p, pid_t *pid_p) { /* What a mess */ #if defined(SO_PEERCRED) /* linux and others */ #if defined(HAVE_SYS_UCRED_H) struct sockpeercred cred; /* openbsd */ #else struct ucred cred; /* linux */ #endif socklen_t len = sizeof(cred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0) { *uid_p = cred.uid; *gid_p = cred.gid; *pid_p = cred.pid; return 0; } return -1; #elif defined(HAVE_GETPEERUCRED) /* solaris */ ucred_t *cred = NULL; if (getpeerucred(fd, &cred) == 0) { *uid_p = ucred_geteuid(cred); *gid_p = ucred_getegid(cred); *pid_p = ucred_getpid(cred); ucred_free(cred); if ((int)*uid_p == -1 || (int)*gid_p == -1) return -1; return 0; } return -1; #elif defined(LOCAL_PEEREID) /* netbsd */ struct unpcbid cred; socklen_t len = sizeof(cred); if (getsockopt(fd, 0, LOCAL_PEEREID, &cred, &len) != 0) return -1; *uid_p = cred.unp_euid; *gid_p = cred.unp_egid; *pid_p = cred.unp_pid; return 0; #elif defined(HAVE_GETPEEREID) /* generic bsd; no pid */ *pid_p = 0; return getpeereid(fd, uid_p, gid_p) == 0 ? 0 : -1; #elif defined(LOCAL_PEERCRED) /* freebsd, osx, dfly; no pid */ struct xucred cred; socklen_t len = sizeof(cred); if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) != 0) return -1; if (cred.cr_version != XUCRED_VERSION) { errno = EINVAL; return -1; } *uid_p = cred.cr_uid; *gid_p = cred.cr_gid; *pid_p = 0; return 0; #else /* no implementation */ errno = ENOSYS; return -1; #endif } #ifndef HAVE_POLL /* * Emulate poll() with select() */ #ifdef HAVE_SYS_SELECT_H #include #endif /* * dynamic buffer for fd_set to avoid depending on FD_SETSIZE */ struct fd_buf { fd_set *set; int alloc_bytes; }; static void fdbuf_zero(struct fd_buf *buf) { if (buf->set) memset(buf->set, 0, buf->alloc_bytes); } static bool fdbuf_resize(struct fd_buf *buf, int fd) { int need_bytes; unsigned char *ptr; /* default allocation */ int alloc = sizeof(fd_set); #ifdef WIN32 int cnt = buf->set ? buf->set->fd_count : 0; /* win32 fd_set is array of handles, +8 for count&padding */ need_bytes = (cnt + 1) * sizeof(buf->set->fd_array[0]) + 8; #else /* otherwise, fd_set is bitmap, +8 for int/long alignment */ need_bytes = fd / 8 + 8; #endif if (buf->alloc_bytes < need_bytes) { while (alloc < need_bytes) alloc *= 2; if (!buf->set) ptr = malloc(alloc); else ptr = realloc(buf->set, alloc); if (!ptr) return false; /* clean new area */ memset(ptr + buf->alloc_bytes, 0, alloc - buf->alloc_bytes); buf->set = (fd_set *)ptr; buf->alloc_bytes = alloc; } return true; } /* win32: make macros ignore FD_SETSIZE */ #undef FD_SETSIZE #define FD_SETSIZE (1 << 30) int poll(struct pollfd *fds, nfds_t nfds, int timeout_ms) { static struct fd_buf readfds = { NULL, 0 }; static struct fd_buf writefds = { NULL, 0 }; struct pollfd *pf; int res, fd_max = 0; struct timeval *tv = NULL; struct timeval tvreal; unsigned i; /* convert timeout_ms to timeval */ if (timeout_ms >= 0) { tvreal.tv_sec = timeout_ms / 1000; tvreal.tv_usec = (timeout_ms % 1000) * 1000; tv = &tvreal; } else if (timeout_ms < -1) goto err_inval; /* * Convert pollfds to fd sets. */ fdbuf_zero(&readfds); fdbuf_zero(&writefds); for (i = 0; i < nfds; i++) { pf = fds + i; if (pf->fd < 0) goto err_badf; /* sets must be equal size */ if (!fdbuf_resize(&readfds, pf->fd)) goto err_nomem; if (!fdbuf_resize(&writefds, pf->fd)) goto err_nomem; if (pf->events & POLLIN) FD_SET((unsigned)pf->fd, readfds.set); if (pf->events & POLLOUT) FD_SET((unsigned)pf->fd, writefds.set); if (pf->fd > fd_max) fd_max = pf->fd; } res = select(fd_max + 1, readfds.set, writefds.set, NULL, tv); if (res <= 0) return res; /* * select() and poll() count fd-s differently, * need to recount them here. */ res = 0; for (i = 0; i < nfds; i++) { pf = fds + i; pf->revents = 0; if ((pf->events & POLLIN) && FD_ISSET(pf->fd, readfds.set)) pf->revents |= POLLIN; if ((pf->events & POLLOUT) && FD_ISSET(pf->fd, writefds.set)) pf->revents |= POLLOUT; if (pf->revents) res += 1; } return res; err_nomem: errno = ENOMEM; return -1; err_badf: errno = EBADF; return -1; err_inval: errno = EINVAL; return -1; } #endif /* PLPROXY_POLL_COMPAT */ #ifdef WIN32 /* create local TCP socket, idea from libevent/Tor */ int win32_socketpair(int d, int typ, int proto, int sv[2]) { int list = -1, s1 = -1, s2 = -1; struct sockaddr_in sa1, sa2; socklen_t slen = sizeof(sa1); int res; if (d != AF_INET && d != AF_UNIX) goto err_inval; if (proto || !sv) goto err_inval; /* prepare sockaddr for bind */ memset(&sa1, 0, sizeof(sa1)); sa1.sin_family = AF_INET; sa1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa1.sin_port = htons(0); /* create listen socket */ list = socket(AF_INET, typ, 0); if (list == -1) return -1; res = bind(list, (struct sockaddr *)&sa1, sizeof(sa1)); if (res == -1) goto failed; res = listen(list, 1); if (res == -1) goto failed; /* read listen port */ res = getsockname(list, (struct sockaddr *)&sa1, &slen); if (res == -1 || slen != sizeof(sa1)) goto failed; /* connect to it */ s1 = socket(AF_INET, typ, 0); if (s1 == -1) goto failed; res = connect(s1, (struct sockaddr *)&sa1, sizeof(sa1)); if (res == -1) goto failed; /* and accept from other end */ s2 = accept(list, (struct sockaddr *)&sa2, &slen); if (s2 == -1 || slen != sizeof(sa2)) goto failed; /* sanity check */ res = getsockname(s1, (struct sockaddr *)&sa1, &slen); if (res == -1 || slen != sizeof(sa1)) goto failed; if (sa1.sin_port != sa2.sin_port) goto failed; closesocket(list); sv[0] = s1; sv[1] = s2; return 0; failed: errno = (res == -1) ? WSAGetLastError() : EFAULT; if (list != -1) closesocket(list); if (s1 != -1) closesocket(s1); if (s2 != -1) closesocket(s2); return -1; err_inval: errno = EINVAL; return -1; } #endif pgbouncer-1.24.1/lib/usual/string.h0000644000175000000000000001466014777762223014075 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file * Theme include for strings. */ #ifndef _USUAL_STRING_H_ #define _USUAL_STRING_H_ #include #include /** * @name List of strings. * @{ */ /** Callback signature */ typedef bool (*str_cb)(void *arg, const char *s); struct StrList; /** Allocate new string list */ struct StrList *strlist_new(CxMem *ca); /** Free string string */ void strlist_free(struct StrList *slist); /** Check if empty */ bool strlist_empty(struct StrList *slist); /** Append copy of string. */ bool strlist_append(struct StrList *slist, const char *str); /** Append reference, strlist now owns it. */ bool strlist_append_ref(struct StrList *slist, char *str); /** Call function on each element */ bool strlist_foreach(const struct StrList *slist, str_cb cb_func, void *cb_arg); /** Remove and return first element */ char *strlist_pop(struct StrList *slist); /* @} */ /** Parse comma-separated elements from string and launch callback for each of them. */ bool parse_word_list(const char *s, str_cb cb_func, void *cb_arg); #ifndef HAVE_STRNLEN #undef strnlen #define strnlen(a,b) usual_strnlen(a,b) /** Compat: determine the length of a fixed-size string */ size_t strnlen(const char *string, size_t maxlen); #endif #ifndef HAVE_STRLCPY #undef strlcpy #define strlcpy(a,b,c) usual_strlcpy(a,b,c) /** Compat: Safely copy string to fixed-length buffer. */ size_t strlcpy(char *dst, const char *src, size_t n); #endif #ifndef HAVE_STRLCAT #undef strlcat #define strlcat(a,b,c) usual_strlcat(a,b,c) /** Compat: Safely append string to fixed-length buffer. */ size_t strlcat(char *dst, const char *src, size_t n); #endif #undef strpcpy #define strpcpy(a,b,c) usual_strpcpy(a,b,c) /** * Safe string copy. * * Returns pointer to end of string in dst or NULL * if truncation occured. Destination will be * zero-terminated unless dstlen is 0. */ char *strpcpy(char *dst, const char *src, size_t dstlen); #undef strpcat #define strpcat(a,b,c) usual_strpcat(a,b,c) /** * Safe string concat. * * Returns pointer to end of string in dst or NULL if truncation occured. * Destination will be zero-terminated, unless dstlen is 0 or existing * contents were not zero-terminated. */ char *strpcat(char *dst, const char *src, size_t dstlen); #ifndef HAVE_MEMRCHR #undef memrchr #define memrchr(a,b,c) usual_memrchr(a,b,c) /** Compat: find byte in reverse direction */ void *memrchr(const void *s, int c, size_t n); #endif #ifndef HAVE_MEMMEM #undef memmem #define memmem(a,b,c,d) usual_memmem(a,b,c,d) /** Compat: find memory area */ void *memmem(const void *s, size_t slen, const void *q, size_t qlen); #endif #ifndef HAVE_MEMPCPY #undef mempcpy #define mempcpy(a,b,c) usual_mempcpy(a,b,c) /** Copy memory, return pointer to end. */ void *mempcpy(void *dst, const void *src, size_t len); #endif /** Return position to first byte that is in 'find'. */ void *mempbrk(const void *data, size_t dlen, const void *find, size_t flen); /** Return number of bytes where none are in reject. */ size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen); /** Return number of bytes where all are in accept. */ size_t memspn(const void *data, size_t dlen, const void *accept, size_t alen); #ifndef HAVE_BASENAME #undef basename #define basename(a) usual_basename(a) /** Compat: Return pointer to last non-path element. Never modifies path, returns either pointer inside path or static buffer. */ const char *basename(const char *path); #endif #ifndef HAVE_DIRNAME #undef dirname #define dirname(a) usual_dirname(a) /** Compat: Return directory part of pathname. Never modifies path, returns either pointer inside path or static buffer. */ const char *dirname(const char *path); #endif #ifndef HAVE_EXPLICIT_BZERO #undef explicit_bzero #define explicit_bzero(a,b) usual_explicit_bzero(a,b) /** Definitely clear memory */ void explicit_bzero(void *buf, size_t len); #endif /* * strerror, strerror_r */ #ifdef WIN32 const char *win32_strerror(int e); /** Compat: strerror() for win32 */ #define strerror(x) win32_strerror(x) #endif const char *usual_strerror_r(int e, char *dst, size_t dstlen); /** Compat: Provide GNU-style API: const char *strerror_r(int e, char *dst, size_t dstlen) */ #define strerror_r(a,b,c) usual_strerror_r(a,b,c) /** strtod() that uses '.' as decimal separator */ double strtod_dot(const char *s, char **tokend); /** Convert double to string with '.' as decimal separator */ ssize_t dtostr_dot(char *buf, size_t buflen, double val); #ifndef HAVE_STRTONUM #undef strtonum #define strtonum(a,b,c,d) usual_strtonum(a,b,c,d) /** * Convert string to integer, check limits. * * Accepts only decimal numbers, no octal or hex. Allows leading whitespace, * but not tailing. * * On success, returns value that is minval <= res <= maxval. If errstr_p is given * it stores NULL there. Keeps old errno value. * * On error, returns 0, sets errno, and if errstr_p is given, stores error reason there. */ long long strtonum(const char *s, long long minval, long long maxval, const char **errstr_p); #endif #ifndef HAVE_STRSEP #undef strsep #define strsep(a,b) usual_strsep(a,b) /** * Return next token from string. * * Tokens are separated by delim chars * Modifies string in-place. */ char *strsep(char **stringp, const char *delim); #endif #ifndef HAVE_ASPRINTF #undef asprintf #define asprintf(dst_p, fmt, ...) usual_asprintf(dst_p, fmt, __VA_ARGS__) int asprintf(char **dst_p, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_VASPRINTF #undef vasprintf #define vasprintf(dst_p, fmt, ap) usual_vasprintf(dst_p, fmt, ap) int vasprintf(char **dst_p, const char *fmt, va_list ap) _PRINTF(2, 0); #endif bool strcmpeq(const char *str_left, const char *str_right); #endif pgbouncer-1.24.1/lib/usual/bytemap.h0000644000175000000000000000637314777762223014232 00000000000000/* * byte map * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Map 256 byte values to bit or int. */ #ifndef _USUAL_BYTEMAP_H_ #define _USUAL_BYTEMAP_H_ #define BITMAP256_SHIFT 5 #define BITMAP256_MASK ((1 << BITMAP256_SHIFT) - 1) /** * Bitmap of 256 bits. */ struct Bitmap256 { uint32_t bmap[256 / 32]; }; /** * Clear bitmap. */ static inline void bitmap256_init(struct Bitmap256 *bmap) { memset(bmap, 0, sizeof(*bmap)); } /** * Set one bit. */ static inline void bitmap256_set(struct Bitmap256 *bmap, uint8_t byte) { bmap->bmap[byte >> BITMAP256_SHIFT] |= 1 << (byte & BITMAP256_MASK); } /** * Check if bit is set. */ static inline bool bitmap256_is_set(const struct Bitmap256 *bmap, uint8_t byte) { return bmap->bmap[byte >> BITMAP256_SHIFT] & (1 << (byte & BITMAP256_MASK)); } /* * Declare const value of bytemap */ /** * Use C preprocessor to fill Bitmap256. * * Usage: * @code * #define check_isdigit(c) ((c) >= '0' && (c) <= '9') * static const struct Bitmap256 map_isdigit = BITMAP256_CONST(check_isdigit); * @endcode */ #define BITMAP256_CONST(check) {{ \ _BMAP256_V32(check,0), _BMAP256_V32(check,32), _BMAP256_V32(check,64), _BMAP256_V32(check,96), \ _BMAP256_V32(check,128), _BMAP256_V32(check,160), _BMAP256_V32(check,192), _BMAP256_V32(check,224) }} #define _BMAP256_V32(ck,p) \ _BMAP256_V8(ck,(p)+0) | _BMAP256_V8(ck,(p)+8) | _BMAP256_V8(ck,(p)+16) | _BMAP256_V8(ck,(p)+24) #define _BMAP256_V8(ck,p) \ _BMAP256_BIT(ck,(p)+0) | _BMAP256_BIT(ck,(p)+1) | _BMAP256_BIT(ck,(p)+2) | _BMAP256_BIT(ck,(p)+3) | \ _BMAP256_BIT(ck,(p)+4) | _BMAP256_BIT(ck,(p)+5) | _BMAP256_BIT(ck,(p)+6) | _BMAP256_BIT(ck,(p)+7) #define _BMAP256_BIT(ck,p) (ck(p) ? (1 << ((p) & BMAP256_MASK)) : 0) /** * Use C preprocessor to generate array of 256 values. * * Usage: * @code * #define my_hexval(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : ( \ * ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : ( \ * ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : -1 ))) * static const int map_hexval[] = INTMAP256_CONST(my_hexval); * @endcode */ #define INTMAP256_CONST(map_value) { _INTMAP_V128(map_value,0), _INTMAP_V128(map_value,128) } #define _INTMAP_V128(mf,n) _INTMAP_V32(mf,(n)+0*32), _INTMAP_V32(mf,(n)+1*32), _INTMAP_V32(mf,(n)+2*32), _INTMAP_V32(mf,(n)+3*32) #define _INTMAP_V32(mf,n) _INTMAP_V8(mf,(n)+0*8), _INTMAP_V8(mf,(n)+1*8), _INTMAP_V8(mf,(n)+2*8), _INTMAP_V8(mf,(n)+3*8) #define _INTMAP_V8(mf,n) mf((n)+0), mf((n)+1), mf((n)+2), mf((n)+3), mf((n)+4), mf((n)+5), mf((n)+6), mf((n)+7) #endif pgbouncer-1.24.1/lib/usual/heap.h0000644000175000000000000000542214777762223013500 00000000000000/* * Binary Heap. * * Copyright (c) 2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Binary heap. * * Binary heap is sort of binary tree held inside array, * with following 2 properties: * - heap property: each node is "better" than it's childs. * - shape property: binary tree is complete, meaning all levels * except the last one are fully filled. * * Instead of "min"- or "max"-heap, this is "best"-heap, * as it operates with user-defined heap_is_better() functions, * which is used to bubble elements on top. */ #ifndef _USUAL_HEAP_H_ #define _USUAL_HEAP_H_ #include /** * Object comparision function. * * Should return true if a needs to reach top before b, * false if not or equal. */ typedef bool (*heap_is_better_f)(const void *a, const void *b); /** * Heap position storage. * * If user wants to delete elements from the middle of heap, * this function should be used to keep track where the element * is located. */ typedef void (*heap_save_pos_f)(void *a, unsigned pos); /** * Heap object. */ struct Heap; /** * Create new heap object. * * @param is_better_cb Callback to decide priority. * @param save_pos_cb Callback to store current index. * @param cx Allocation context. */ struct Heap *heap_create( heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx); /** Release memory allocated by heap */ void heap_destroy(struct Heap *h); /** Put new object into heap */ bool heap_push(struct Heap *h, void *ptr); /** Remove and return topmost object from heap */ void *heap_pop(struct Heap *h); /** Return topmost object in heap */ void *heap_top(struct Heap *h); /** Remove and return any object from heap by index */ void *heap_remove(struct Heap *h, unsigned pos); /** * Reserve room for more elements. * * Returns false if allocation failed. */ bool heap_reserve(struct Heap *h, unsigned extra); /** Return number of objects in heap */ unsigned heap_size(struct Heap *h); /* Return object by index, for testing */ void *heap_get_obj(struct Heap *h, unsigned pos); #endif pgbouncer-1.24.1/lib/usual/tls/0000755000000000000000000000000014777762567013267 500000000000000pgbouncer-1.24.1/lib/usual/tls/tls.c0000644000175000000000000004311714777762223014165 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include #include #include #include "tls_internal.h" static struct tls_config *tls_config_default; static int tls_initialised = 0; int tls_init(void) { if (tls_initialised) return (0); #ifdef USE_LIBSSL_OLD SSL_load_error_strings(); SSL_library_init(); if (BIO_sock_init() != 1) return (-1); #endif if ((tls_config_default = tls_config_new()) == NULL) return (-1); tls_initialised = 1; return (0); } void tls_deinit(void) { if (tls_initialised) { tls_compat_cleanup(); tls_config_free(tls_config_default); tls_config_default = NULL; #ifdef USE_LIBSSL_OLD EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); BIO_sock_cleanup(); ERR_clear_error(); ERR_remove_thread_state(NULL); ERR_free_strings(); #else OPENSSL_cleanup(); #endif tls_initialised = 0; } } const char * tls_error(struct tls *ctx) { return ctx->error.msg; } _PRINTF(3,0) static int tls_error_vset(struct tls_error *error, int errnum, const char *fmt, va_list ap) { char *errmsg = NULL; int rv = -1; free(error->msg); error->msg = NULL; error->num = errnum; if (vasprintf(&errmsg, fmt, ap) == -1) { errmsg = NULL; goto err; } if (errnum == -1) { error->msg = errmsg; return (0); } if (asprintf(&error->msg, "%s: %s", errmsg, strerror(errnum)) == -1) { error->msg = NULL; goto err; } rv = 0; err: free(errmsg); return (rv); } int tls_error_set(struct tls_error *error, const char *fmt, ...) { va_list ap; int errnum, rv; errnum = errno; va_start(ap, fmt); rv = tls_error_vset(error, errnum, fmt, ap); va_end(ap); return (rv); } int tls_error_setx(struct tls_error *error, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_error_vset(error, -1, fmt, ap); va_end(ap); return (rv); } int tls_config_set_error(struct tls_config *config, const char *fmt, ...) { va_list ap; int errnum, rv; errnum = errno; va_start(ap, fmt); rv = tls_error_vset(&config->error, errnum, fmt, ap); va_end(ap); return (rv); } int tls_config_set_errorx(struct tls_config *config, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_error_vset(&config->error, -1, fmt, ap); va_end(ap); return (rv); } int tls_set_error(struct tls *ctx, const char *fmt, ...) { va_list ap; int errnum, rv; errnum = errno; va_start(ap, fmt); rv = tls_error_vset(&ctx->error, errnum, fmt, ap); va_end(ap); return (rv); } int tls_set_errorx(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_error_vset(&ctx->error, -1, fmt, ap); va_end(ap); return (rv); } int tls_set_error_libssl(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; const char *msg = NULL; char *old; int err; err = ERR_peek_error(); if (err != 0) msg = ERR_reason_error_string(err); va_start(ap, fmt); rv = tls_error_vset(&ctx->error, -1, fmt, ap); va_end(ap); if (rv != 0 || msg == NULL) return rv; old = ctx->error.msg; ctx->error.msg = NULL; if (asprintf(&ctx->error.msg, "%s: %s", old, msg) == -1) { ctx->error.msg = old; } else { free(old); } return 0; } struct tls * tls_new(void) { struct tls *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return (NULL); ctx->config = tls_config_default; tls_reset(ctx); return (ctx); } int tls_configure(struct tls *ctx, struct tls_config *config) { if (config == NULL) config = tls_config_default; ctx->config = config; if ((ctx->flags & TLS_SERVER) != 0) return (tls_configure_server(ctx)); return (0); } int tls_configure_keypair(struct tls *ctx, SSL_CTX *ssl_ctx, struct tls_keypair *keypair, int required) { EVP_PKEY *pkey = NULL; X509 *cert = NULL; BIO *bio = NULL; if (!required && keypair->cert_mem == NULL && keypair->key_mem == NULL && keypair->cert_file == NULL && keypair->key_file == NULL) return(0); if (keypair->cert_mem != NULL) { if (keypair->cert_len > INT_MAX) { tls_set_errorx(ctx, "certificate too long"); goto err; } if (SSL_CTX_use_certificate_chain_mem(ssl_ctx, keypair->cert_mem, keypair->cert_len) != 1) { tls_set_errorx(ctx, "failed to load certificate"); goto err; } cert = NULL; } if (keypair->key_mem != NULL) { if (keypair->key_len > INT_MAX) { tls_set_errorx(ctx, "key too long"); goto err; } if ((bio = BIO_new_mem_buf(keypair->key_mem, keypair->key_len)) == NULL) { tls_set_errorx(ctx, "failed to create buffer"); goto err; } if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL)) == NULL) { tls_set_errorx(ctx, "failed to read private key"); goto err; } if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) { tls_set_errorx(ctx, "failed to load private key"); goto err; } BIO_free(bio); bio = NULL; EVP_PKEY_free(pkey); pkey = NULL; } if (keypair->cert_file != NULL) { if (SSL_CTX_use_certificate_chain_file(ssl_ctx, keypair->cert_file) != 1) { const char *errstr = "unknown error"; unsigned long err; if ((err = ERR_peek_error()) != 0) errstr = ERR_reason_error_string(err); tls_set_errorx(ctx, "failed to load certificate file \"%s\": %s", keypair->cert_file, errstr); goto err; } } if (keypair->key_file != NULL) { if (SSL_CTX_use_PrivateKey_file(ssl_ctx, keypair->key_file, SSL_FILETYPE_PEM) != 1) { const char *errstr = "unknown error"; unsigned long err; if ((err = ERR_peek_error()) != 0) errstr = ERR_reason_error_string(err); tls_set_errorx(ctx, "failed to load private key file \"%s\": %s", keypair->key_file, errstr); goto err; } } if (SSL_CTX_check_private_key(ssl_ctx) != 1) { tls_set_errorx(ctx, "private/public key mismatch"); goto err; } return (0); err: EVP_PKEY_free(pkey); X509_free(cert); BIO_free(bio); return (1); } static void tls_info_callback(const SSL *ssl, int where, int rc) { struct tls *ctx = SSL_get_app_data(ssl); #ifdef USE_LIBSSL_INTERNALS if (!(ctx->state & TLS_HANDSHAKE_COMPLETE) && ssl->s3) { /* steal info about used DH key */ if (ssl->s3->tmp.dh && !ctx->used_dh_bits) { ctx->used_dh_bits = DH_size(ssl->s3->tmp.dh) * 8; } else if (ssl->s3->tmp.ecdh && !ctx->used_ecdh_nid) { ctx->used_ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ssl->s3->tmp.ecdh)); } } #endif /* * Detect renegotation on established connection. With * TLSv1.3 this is no longer applicable, and the code below * would erroneously abort with OpenSSL 1.1.1 and 1.1.1a if * using TLSv1.3, so skip it altogether in that case. */ if (SSL_version(ssl) < TLS1_3_VERSION) { if (where & SSL_CB_HANDSHAKE_START) { if (ctx->state & TLS_HANDSHAKE_COMPLETE) ctx->state |= TLS_DO_ABORT; } } } static int tls_do_abort(struct tls *ctx) { int ssl_ret, rv; ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) return (rv); } tls_set_errorx(ctx, "unexpected handshake, closing connection"); return -1; } int tls_configure_ssl(struct tls *ctx) { SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_3); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_3) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_3); /* * obsolete outdated keywords, turn them to default. * For default, don't call SSL_CTX_set_cipher_list() * so we inherit openssl's default, which can also * include a system-wide policy, such as on rhel/fedora * https://docs.fedoraproject.org/en-US/packaging-guidelines/CryptoPolicies/ */ if (ctx->config->ciphers != NULL) { if (strcasecmp(ctx->config->ciphers, "default") != 0 && strcasecmp(ctx->config->ciphers, "secure") != 0 && strcasecmp(ctx->config->ciphers, "normal") != 0 && strcasecmp(ctx->config->ciphers, "fast") != 0) { if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config->ciphers) != 1) { tls_set_errorx(ctx, "failed to set ciphers"); goto err; } } } SSL_CTX_set_info_callback(ctx->ssl_ctx, tls_info_callback); #ifdef X509_V_FLAG_NO_CHECK_TIME if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM *vfp = SSL_CTX_get0_param(ctx->ssl_ctx); X509_VERIFY_PARAM_set_flags(vfp, X509_V_FLAG_NO_CHECK_TIME); } #endif return (0); err: return (-1); } int tls_configure_ssl_verify(struct tls *ctx, int verify) { SSL_CTX_set_verify(ctx->ssl_ctx, verify, NULL); if (ctx->config->ca_mem != NULL) { /* XXX do this in set. */ if (ctx->config->ca_len > INT_MAX) { tls_set_errorx(ctx, "ca too long"); goto err; } if (SSL_CTX_load_verify_mem(ctx->ssl_ctx, ctx->config->ca_mem, ctx->config->ca_len) != 1) { tls_set_errorx(ctx, "ssl verify memory setup failure"); goto err; } } else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ctx->config->ca_file, ctx->config->ca_path) != 1) { const char *errstr = "unknown error"; unsigned long err; if ((err = ERR_peek_error()) != 0) errstr = ERR_reason_error_string(err); tls_set_errorx(ctx, "failed to load CA: %s", errstr); goto err; } if (ctx->config->verify_depth >= 0) SSL_CTX_set_verify_depth(ctx->ssl_ctx, ctx->config->verify_depth); return (0); err: return (-1); } void usual_tls_free(struct tls *ctx) { if (ctx == NULL) return; tls_reset(ctx); free(ctx); } void tls_reset(struct tls *ctx) { SSL_CTX_free(ctx->ssl_ctx); SSL_free(ctx->ssl_conn); X509_free(ctx->ssl_peer_cert); ctx->ssl_conn = NULL; ctx->ssl_ctx = NULL; ctx->ssl_peer_cert = NULL; ctx->socket = -1; ctx->state = 0; free(ctx->servername); ctx->servername = NULL; free(ctx->error.msg); ctx->error.msg = NULL; ctx->error.num = -1; tls_free_conninfo(ctx->conninfo); free(ctx->conninfo); ctx->conninfo = NULL; ctx->used_dh_bits = 0; ctx->used_ecdh_nid = 0; tls_ocsp_info_free(ctx->ocsp_info); ctx->ocsp_info = NULL; ctx->ocsp_result = NULL; if (ctx->flags & TLS_OCSP_CLIENT) tls_ocsp_client_free(ctx); } int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix) { const char *errstr = "unknown error"; unsigned long err; int ssl_err; ssl_err = SSL_get_error(ssl_conn, ssl_ret); switch (ssl_err) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: return (0); case SSL_ERROR_WANT_READ: return (TLS_WANT_POLLIN); case SSL_ERROR_WANT_WRITE: return (TLS_WANT_POLLOUT); case SSL_ERROR_SYSCALL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } else if (ssl_ret == 0) { if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) { ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY; return (0); } errstr = "unexpected EOF"; } else if (ssl_ret == -1) { errstr = strerror(errno); } tls_set_errorx(ctx, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_SSL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } tls_set_errorx(ctx, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: default: tls_set_errorx(ctx, "%s failed (%i)", prefix, ssl_err); return (-1); } } int tls_handshake(struct tls *ctx) { int rv = -1; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); goto out; } if (ctx->conninfo == NULL && (ctx->conninfo = calloc(1, sizeof(*ctx->conninfo))) == NULL) goto out; if ((ctx->flags & TLS_CLIENT) != 0) rv = tls_handshake_client(ctx); else if ((ctx->flags & TLS_SERVER_CONN) != 0) rv = tls_handshake_server(ctx); if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); if (tls_get_conninfo(ctx) == -1) rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_read(struct tls *ctx, void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; if (ctx->state & TLS_DO_ABORT) { rv = tls_do_abort(ctx); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "read"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; if (ctx->state & TLS_DO_ABORT) { rv = tls_do_abort(ctx); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } int tls_close(struct tls *ctx) { int ssl_ret; int rv = 0; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); rv = -1; goto out; } if (ctx->ssl_conn != NULL) { ERR_clear_error(); ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) goto out; } } if (ctx->socket != -1) { if (shutdown(ctx->socket, SHUT_RDWR) != 0) { if (rv == 0 && errno != ENOTCONN && errno != ECONNRESET) { tls_set_error(ctx, "shutdown"); rv = -1; } } if (close(ctx->socket) != 0) { if (rv == 0) { tls_set_error(ctx, "close"); rv = -1; } } ctx->socket = -1; } if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) { tls_set_errorx(ctx, "EOF without close notify"); rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } static bool tls_mem_equal(char *mem1, char *mem2, size_t len1, size_t len2) { if (len1 != len2) return false; if (mem1 && mem2 && memcmp(mem1, mem2, len1) != 0) return false; return true; } static bool tls_keypair_equal(struct tls_keypair *tkp1, struct tls_keypair *tkp2) { if (!strcmpeq(tkp1->cert_file, tkp2->cert_file)) return false; if (!tls_mem_equal(tkp1->cert_mem, tkp2->cert_mem, tkp1->cert_len, tkp2->cert_len)) return false; if (!strcmpeq(tkp1->key_file, tkp2->key_file)) return false; if (!tls_mem_equal(tkp1->key_mem, tkp2->key_mem, tkp1->key_len, tkp2->key_len)) return false; return true; } bool tls_keypair_list_equal(struct tls_keypair *tkp1, struct tls_keypair *tkp2) { for (; tkp1 != NULL && tkp2 != NULL; tkp1 = tkp1->next, tkp2 = tkp2->next) { if (!tls_keypair_equal(tkp1, tkp2)) return false; } return tkp1 == NULL && tkp2 == NULL; } bool tls_config_equal(struct tls_config *tc1, struct tls_config *tc2) { if (!strcmpeq(tc1->ca_file, tc2->ca_file)) return false; if (!strcmpeq(tc1->ca_path, tc2->ca_path)) return false; if (!tls_mem_equal(tc1->ca_mem, tc2->ca_mem, tc1->ca_len, tc2->ca_len)) return false; if (!strcmpeq(tc1->ciphers, tc2->ciphers)) return false; if (tc1->ciphers_server != tc2->ciphers_server) return false; if (tc1->dheparams != tc2->dheparams) return false; if (tc1->ecdhecurve != tc2->ecdhecurve) return false; if (!tls_keypair_list_equal(tc1->keypair, tc2->keypair)) return false; if (!strcmpeq(tc1->ocsp_file, tc2->ocsp_file)) return false; if (!tls_mem_equal(tc1->ocsp_mem, tc2->ocsp_mem, tc1->ocsp_len, tc2->ocsp_len)) return false; if (tc1->protocols != tc2->protocols) return false; if (tc1->verify_cert != tc2->verify_cert) return false; if (tc1->verify_client != tc2->verify_client) return false; if (tc1->verify_depth != tc2->verify_depth) return false; if (tc1->verify_name != tc2->verify_name) return false; if (tc1->verify_time != tc2->verify_time) return false; return true; } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_conninfo.c0000644000175000000000000001170714777762223016056 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int tls_hex_string(const unsigned char *in, size_t inlen, char **out, size_t *outlen) { static const char hex[] = "0123456789abcdef"; size_t i, len; char *p; if (outlen != NULL) *outlen = 0; if (inlen >= SIZE_MAX) return (-1); if ((*out = reallocarray(NULL, inlen + 1, 2)) == NULL) return (-1); p = *out; len = 0; for (i = 0; i < inlen; i++) { p[len++] = hex[(in[i] >> 4) & 0x0f]; p[len++] = hex[in[i] & 0x0f]; } p[len++] = 0; if (outlen != NULL) *outlen = len; return (0); } static int tls_get_peer_cert_hash(struct tls *ctx, char **hash) { unsigned char d[EVP_MAX_MD_SIZE]; char *dhex = NULL; unsigned int dlen; int rv = -1; *hash = NULL; if (ctx->ssl_peer_cert == NULL) return (0); if (X509_digest(ctx->ssl_peer_cert, EVP_sha256(), d, &dlen) != 1) { tls_set_errorx(ctx, "digest failed"); goto err; } if (tls_hex_string(d, dlen, &dhex, NULL) != 0) { tls_set_errorx(ctx, "digest hex string failed"); goto err; } if (asprintf(hash, "SHA256:%s", dhex) == -1) { tls_set_errorx(ctx, "out of memory"); *hash = NULL; goto err; } rv = 0; err: free(dhex); return (rv); } static int tls_get_peer_cert_issuer(struct tls *ctx, char **issuer) { X509_NAME *name = NULL; *issuer = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_issuer_name(ctx->ssl_peer_cert)) == NULL) return (-1); *issuer = X509_NAME_oneline(name, 0, 0); if (*issuer == NULL) return (-1); return (0); } static int tls_get_peer_cert_subject(struct tls *ctx, char **subject) { X509_NAME *name = NULL; *subject = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_subject_name(ctx->ssl_peer_cert)) == NULL) return (-1); *subject = X509_NAME_oneline(name, 0, 0); if (*subject == NULL) return (-1); return (0); } static int tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore, time_t *notafter) { struct tm before_tm, after_tm; ASN1_TIME *before, *after; int rv = -1; memset(&before_tm, 0, sizeof(before_tm)); memset(&after_tm, 0, sizeof(after_tm)); if (ctx->ssl_peer_cert != NULL) { if ((before = X509_get_notBefore(ctx->ssl_peer_cert)) == NULL) goto err; if ((after = X509_get_notAfter(ctx->ssl_peer_cert)) == NULL) goto err; if (asn1_time_parse((char*)before->data, before->length, &before_tm, 0) == -1) goto err; if (asn1_time_parse((char*)after->data, after->length, &after_tm, 0) == -1) goto err; if ((*notbefore = timegm(&before_tm)) == -1) goto err; if ((*notafter = timegm(&after_tm)) == -1) goto err; } rv = 0; err: return (rv); } int tls_get_conninfo(struct tls *ctx) { const char * tmp; tls_free_conninfo(ctx->conninfo); if (ctx->ssl_peer_cert != NULL) { if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1) goto err; if (tls_get_peer_cert_subject(ctx, &ctx->conninfo->subject) == -1) goto err; if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1) goto err; if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore, &ctx->conninfo->notafter) == -1) goto err; } if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL) goto err; ctx->conninfo->version = strdup(tmp); if (ctx->conninfo->version == NULL) goto err; if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL) goto err; ctx->conninfo->cipher = strdup(tmp); if (ctx->conninfo->cipher == NULL) goto err; return (0); err: tls_free_conninfo(ctx->conninfo); return (-1); } void tls_free_conninfo(struct tls_conninfo *conninfo) { if (conninfo != NULL) { free(conninfo->hash); conninfo->hash = NULL; OPENSSL_free(conninfo->subject); conninfo->subject = NULL; OPENSSL_free(conninfo->issuer); conninfo->issuer = NULL; free(conninfo->version); conninfo->version = NULL; free(conninfo->cipher); conninfo->cipher = NULL; } } const char * tls_conn_cipher(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->cipher); } const char * tls_conn_version(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->version); } #endif pgbouncer-1.24.1/lib/usual/tls/tls_server.c0000644000175000000000000001125714777762223015553 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include "tls_internal.h" struct tls * tls_server(void) { struct tls *ctx; if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_SERVER; return (ctx); } struct tls * tls_server_conn(struct tls *ctx) { struct tls *conn_ctx; if ((conn_ctx = tls_new()) == NULL) return (NULL); conn_ctx->flags |= TLS_SERVER_CONN; return (conn_ctx); } int tls_configure_server(struct tls *ctx) { EC_KEY *ecdh_key; STACK_OF(X509_NAME) * cert_stack; unsigned char sid[SSL_MAX_SSL_SESSION_ID_LENGTH]; if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { tls_set_errorx(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (tls_configure_keypair(ctx, ctx->ssl_ctx, ctx->config->keypair, 1) != 0) goto err; if (ctx->config->verify_client != 0) { int verify = SSL_VERIFY_PEER; if (ctx->config->verify_client == 1) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if (tls_configure_ssl_verify(ctx, verify) == -1) goto err; } if (ctx->config->dheparams == -1) SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1); if (ctx->config->ecdhecurve == -1) { SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1); } else if (ctx->config->ecdhecurve != NID_undef) { if ((ecdh_key = EC_KEY_new_by_curve_name( ctx->config->ecdhecurve)) == NULL) { tls_set_errorx(ctx, "failed to set ECDHE curve"); goto err; } SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key); EC_KEY_free(ecdh_key); } if (ctx->config->ciphers_server == 1) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_stapling_callback) != 1) { tls_set_errorx(ctx, "ssl OCSP stapling setup failure"); goto err; } /* * Set session ID context to a random value. We don't support * persistent caching of sessions so it is OK to set a temporary * session ID context that is valid during run time. */ if (!RAND_bytes(sid, sizeof(sid))) { tls_set_errorx(ctx, "failed to generate session id"); goto err; } if (!SSL_CTX_set_session_id_context(ctx->ssl_ctx, sid, sizeof(sid))) { tls_set_errorx(ctx, "failed to set session id context"); goto err; } cert_stack = SSL_load_client_CA_file(ctx->config->ca_file); SSL_CTX_set_client_CA_list(ctx->ssl_ctx, cert_stack); return (0); err: return (-1); } int tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) { return (tls_accept_fds(ctx, cctx, socket, socket)); } int tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) { struct tls *conn_ctx = NULL; if ((ctx->flags & TLS_SERVER) == 0) { tls_set_errorx(ctx, "not a server context"); goto err; } if ((conn_ctx = tls_server_conn(ctx)) == NULL) { tls_set_errorx(ctx, "connection context failure"); goto err; } if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl failure"); goto err; } if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } *cctx = conn_ctx; return (0); err: usual_tls_free(conn_ctx); *cctx = NULL; return (-1); } int tls_handshake_server(struct tls *ctx) { int ssl_ret; int rv = -1; if ((ctx->flags & TLS_SERVER_CONN) == 0) { tls_set_errorx(ctx, "not a server connection context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_client.c0000644000175000000000000001533014777762223015517 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" struct tls * tls_client(void) { struct tls *ctx; if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_CLIENT; return (ctx); } int tls_connect(struct tls *ctx, const char *host, const char *port) { return tls_connect_servername(ctx, host, port, NULL); } int tls_connect_servername(struct tls *ctx, const char *host, const char *port, const char *servername) { struct addrinfo hints, *res, *res0; const char *h = NULL, *p = NULL; char *hs = NULL, *ps = NULL; int rv = -1, s = -1, ret; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if (host == NULL) { tls_set_errorx(ctx, "host not specified"); goto err; } /* * If port is NULL try to extract a port from the specified host, * otherwise use the default. */ if (port == NULL) { ret = tls_host_port(host, &hs, &ps); if (ret == -1) { tls_set_errorx(ctx, "memory allocation failure"); goto err; } if (ret != 0) { tls_set_errorx(ctx, "no port provided"); goto err; } } h = (hs != NULL) ? hs : host; p = (ps != NULL) ? ps : port; /* * First check if the host is specified as a numeric IP address, * either IPv4 or IPv6, before trying to resolve the host. * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6 * records if it is not configured on an interface; not considering * loopback addresses. Checking the numeric addresses first makes * sure that connection attempts to numeric addresses and especially * 127.0.0.1 or ::1 loopback addresses are always possible. */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; /* try as an IPv4 literal */ hints.ai_family = AF_INET; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* try again as an IPv6 literal */ hints.ai_family = AF_INET6; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* last try, with name resolution and save the error */ hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) { tls_set_error(ctx, "%s", gai_strerror(s)); goto err; } } } /* It was resolved somehow; now try connecting to what we got */ s = -1; for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { tls_set_error(ctx, "socket"); continue; } if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { tls_set_error(ctx, "connect"); close(s); s = -1; continue; } break; /* Connected. */ } freeaddrinfo(res0); if (s == -1) goto err; if (servername == NULL) servername = h; if (tls_connect_socket(ctx, s, servername) != 0) { close(s); goto err; } ctx->socket = s; rv = 0; err: free(hs); free(ps); return (rv); } int tls_connect_socket(struct tls *ctx, int s, const char *servername) { return tls_connect_fds(ctx, s, s, servername); } int tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, const char *servername) { union tls_addr addrbuf; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if (fd_read < 0 || fd_write < 0) { tls_set_errorx(ctx, "invalid file descriptors"); goto err; } if (servername != NULL) { if ((ctx->servername = strdup(servername)) == NULL) { tls_set_errorx(ctx, "out of memory"); goto err; } } if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { tls_set_errorx(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (tls_configure_keypair(ctx, ctx->ssl_ctx, ctx->config->keypair, 0) != 0) goto err; if (ctx->config->verify_name) { if (servername == NULL) { tls_set_errorx(ctx, "server name not specified"); goto err; } } if (ctx->config->verify_cert && (tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1)) goto err; if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_callback) != 1) { tls_set_errorx(ctx, "ssl OCSP verification setup failure"); goto err; } if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl connection failure"); goto err; } if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) { tls_set_errorx(ctx, "ssl OCSP extension setup failure"); goto err; } /* * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not * permitted in "HostName". */ if (servername != NULL && inet_pton(AF_INET, servername, &addrbuf) != 1 && inet_pton(AF_INET6, servername, &addrbuf) != 1) { if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) { tls_set_errorx(ctx, "server name indication failure"); goto err; } } rv = 0; err: return (rv); } int tls_handshake_client(struct tls *ctx) { X509 *cert = NULL; int ssl_ret; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_errorx(ctx, "no server certificate"); goto err; } if ((rv = tls_check_name(ctx, cert, ctx->servername)) != 0) { if (rv != -2) tls_set_errorx(ctx, "name `%s' not present in" " server certificate", ctx->servername); goto err; } } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: X509_free(cert); return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_ocsp.c0000644000175000000000000005323614777762223015214 00000000000000/* * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" #ifndef OPENSSL_NO_OCSP #include #define MAXAGE_SEC (14*24*60*60) #define JITTER_SEC (60) #define USE_NONCE 0 #if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10002000 #define BUGGY_VERIFY #endif /* * State for request. */ struct tls_ocsp_query { /* responder location */ char *ocsp_url; /* request blob */ uint8_t *request_data; size_t request_size; /* network state */ BIO *bio; SSL_CTX *ssl_ctx; OCSP_REQ_CTX *http_req; /* cert data, this struct does not own these */ X509 *main_cert; STACK_OF(X509) *extra_certs; SSL_CTX *cert_ssl_ctx; }; /* * Extract OCSP response info. */ static int tls_ocsp_fill_info(struct tls *ctx, int response_status, int cert_status, int crl_reason, ASN1_GENERALIZEDTIME *revtime, ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd) { struct tls_ocsp_info *info; int res; info = calloc(1, sizeof (struct tls_ocsp_info)); if (!info) { tls_set_error(ctx, "calloc"); return -1; } info->response_status = response_status; info->cert_status = cert_status; info->crl_reason = crl_reason; res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time); if (res == 0) res = tls_asn1_parse_time(ctx, thisupd, &info->this_update); if (res == 0) res = tls_asn1_parse_time(ctx, nextupd, &info->next_update); if (res == 0) { ctx->ocsp_info = info; } else { tls_ocsp_info_free(info); } return res; } static void tls_ocsp_fill_result(struct tls *ctx, int res) { struct tls_ocsp_info *info = ctx->ocsp_info; if (res < 0) { ctx->ocsp_result = "error"; } else if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { ctx->ocsp_result = OCSP_response_status_str(info->response_status); } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) { ctx->ocsp_result = OCSP_cert_status_str(info->cert_status); } else { ctx->ocsp_result = OCSP_crl_reason_str(info->crl_reason); } } void tls_ocsp_info_free(struct tls_ocsp_info *info) { free(info); } int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { static const struct tls_ocsp_info no_ocsp = { -1, -1, -1, 0, 0, 0 }; const struct tls_ocsp_info *info = ctx->ocsp_info; const char *ocsp_result = ctx->ocsp_result; int ret = 0; if (!info) { info = &no_ocsp; ret = -1; } if (!ocsp_result) { ret = TLS_NO_OCSP; ocsp_result = "no-ocsp"; } if (response_status) *response_status = info->response_status; if (cert_status) *cert_status = info->cert_status; if (crl_reason) *crl_reason = info->crl_reason; if (this_update) *this_update = info->this_update; if (next_update) *next_update = info->next_update; if (revoction_time) *revoction_time = info->revocation_time; if (result_text) *result_text = ocsp_result; return ret; } /* * Verify stapled response */ static OCSP_CERTID * tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx) { X509_NAME *issuer_name; X509 *issuer; X509_STORE_CTX *storectx = NULL; X509_OBJECT *tmpobj; OCSP_CERTID *cid = NULL; X509_STORE *store; int ok; issuer_name = X509_get_issuer_name(main_cert); if (!issuer_name) return NULL; if (extra_certs) { issuer = X509_find_by_subject(extra_certs, issuer_name); if (issuer) return OCSP_cert_to_id(NULL, main_cert, issuer); } store = SSL_CTX_get_cert_store(ssl_ctx); if (!store) goto error; storectx = X509_STORE_CTX_new(); if (!storectx) goto error; ok = X509_STORE_CTX_init(storectx, store, main_cert, extra_certs); if (ok != 1) goto error; tmpobj = X509_STORE_CTX_get_obj_by_subject(storectx, X509_LU_X509, issuer_name); if (!tmpobj) goto error; cid = OCSP_cert_to_id(NULL, main_cert, X509_OBJECT_get0_X509(tmpobj)); X509_OBJECT_free(tmpobj); X509_STORE_CTX_free(storectx); return cid; error: if (storectx) { X509_STORE_CTX_free(storectx); } return NULL; } static int tls_ocsp_verify_response(struct tls *ctx, X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx, OCSP_RESPONSE *resp) { OCSP_BASICRESP *br = NULL; STACK_OF(X509) *ocsp_chain = NULL; ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; OCSP_CERTID *cid = NULL; STACK_OF(X509) *combined = NULL; int response_status=0, cert_status=0, crl_reason=0; int ssl_res, ret = -1; unsigned long flags; #ifdef BUGGY_VERIFY STACK_OF(X509) *tmpchain = NULL; #endif br = OCSP_response_get1_basic(resp); if (!br) { tls_set_errorx(ctx, "ocsp error: cannot load"); goto error; } #ifdef BUGGY_VERIFY /* * There may be OCSP-subCA in OCSP response that chains to subCA * in main TLS headers. Need to present both chains to verify. * * OCSP_basic_verify() bugs: * - Does not use br->certs when building chain. * - Does not use main_certs when validating br->certs. */ if (br->certs && extra_certs) { int i; combined = sk_X509_new_null(); if (!combined) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } for (i = 0; i < sk_X509_num(br->certs); i++) { ssl_res = sk_X509_push(combined, sk_X509_value(br->certs, i)); if (ssl_res == 0) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } } for (i = 0; i < sk_X509_num(extra_certs); i++) { ssl_res = sk_X509_push(combined, sk_X509_value(extra_certs, i)); if (ssl_res == 0) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } } /* OCSP_TRUSTOTHER would skip br->certs checks */ flags = 0; flags = OCSP_TRUSTOTHER; ocsp_chain = combined; /* Bug: does not use main_certs when validating br->certs */ if ((flags & OCSP_TRUSTOTHER) == 0) { tmpchain = br->certs; br->certs = combined; } } else if (br->certs && !extra_certs) { /* can this happen? */ flags = 0; ocsp_chain = br->certs; } else #endif { /* * Skip validation of 'extra_certs' as this should be done * already as part of main handshake. */ flags = OCSP_TRUSTOTHER; ocsp_chain = extra_certs; } /* now verify */ ssl_res = OCSP_basic_verify(br, ocsp_chain, SSL_CTX_get_cert_store(ssl_ctx), flags); #ifdef BUGGY_VERIFY /* replace back */ if (tmpchain) { br->certs = tmpchain; tmpchain = NULL; } #endif if (ssl_res != 1) { tls_set_error_libssl(ctx, "ocsp verify failed"); goto error; } /* signature OK, look inside */ response_status = OCSP_response_status(resp); if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { tls_set_errorx(ctx, "ocsp verify failed: unsuccessful response - %s", OCSP_response_status_str(response_status)); goto error; } cid = tls_ocsp_get_certid(main_cert, extra_certs, ssl_ctx); if (!cid) { tls_set_errorx(ctx, "ocsp verify failed: no issuer cert"); goto error; } ssl_res = OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, &revtime, &thisupd, &nextupd); if (ssl_res != 1) { tls_set_errorx(ctx, "ocsp verify failed: no result for cert"); goto error; } ssl_res = OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC); if (ssl_res != 1) { tls_set_errorx(ctx, "ocsp verify failed: bad age"); goto error; } ssl_res = tls_ocsp_fill_info(ctx, response_status, cert_status, crl_reason, revtime, thisupd, nextupd); if (ssl_res != 0) goto error; /* finally can look at status */ if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status != V_OCSP_CERTSTATUS_UNKNOWN) { tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s", OCSP_crl_reason_str(crl_reason)); goto error; } ret = 0; error: sk_X509_free(combined); OCSP_CERTID_free(cid); OCSP_BASICRESP_free(br); return ret; } /* * Same callback on client-side has different error proto: * 1=OK, 0=bad, -1=internal error */ int tls_ocsp_verify_callback(SSL *ssl, void *arg) { OCSP_RESPONSE *resp = NULL; STACK_OF(X509) *extra_certs = NULL; X509 *peer = NULL; const unsigned char *raw = NULL; int size, res = -1; struct tls *ctx; ctx = SSL_get_app_data(ssl); if (!ctx) return -1; size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw); if (size <= 0) return 1; peer = SSL_get_peer_certificate(ssl); if (!peer) { tls_set_errorx(ctx, "ocsp verify failed: no peer cert"); goto error; } resp = d2i_OCSP_RESPONSE(NULL, &raw, size); if (!resp) { tls_set_errorx(ctx, "ocsp verify failed: parse failed"); goto error; } extra_certs = SSL_get_peer_cert_chain(ssl); res = tls_ocsp_verify_response(ctx, peer, extra_certs, ctx->ssl_ctx, resp); error: tls_ocsp_fill_result(ctx, res); OCSP_RESPONSE_free(resp); X509_free(peer); return (res == 0) ? 1 : 0; } /* * Staple OCSP response to server handshake. */ int tls_ocsp_stapling_callback(SSL *ssl, void *arg) { struct tls *ctx; char *mem, *fmem = NULL; uint8_t *xmem; size_t len; int ret = SSL_TLSEXT_ERR_ALERT_FATAL; ctx = SSL_get_app_data(ssl); if (!ctx) return SSL_TLSEXT_ERR_NOACK; if (ctx->config->ocsp_file) { fmem = mem = (char*)tls_load_file(ctx->config->ocsp_file, &len, NULL); if (!mem) goto err; } else { mem = ctx->config->ocsp_mem; len = ctx->config->ocsp_len; if (!mem) return SSL_TLSEXT_ERR_NOACK; } xmem = OPENSSL_malloc(len); if (xmem) { memcpy(xmem, mem, len); if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, xmem, len) != 1) { OPENSSL_free(xmem); goto err; } ret = SSL_TLSEXT_ERR_OK; } err: free(fmem); return ret; } /* * Query OCSP responder over HTTP(S). */ void tls_ocsp_client_free(struct tls *ctx) { struct tls_ocsp_query *q; if (!ctx) return; q = ctx->ocsp_query; if (q) { if (q->http_req) OCSP_REQ_CTX_free(q->http_req); BIO_free_all(q->bio); SSL_CTX_free(q->ssl_ctx); free(q->ocsp_url); free(q->request_data); free(q); ctx->ocsp_query = NULL; } } static struct tls * tls_ocsp_client_new(void) { struct tls *ctx; ctx = tls_new(); if (!ctx) return NULL; ctx->flags = TLS_OCSP_CLIENT; ctx->ocsp_query = calloc(1, sizeof (struct tls_ocsp_query)); if (!ctx->ocsp_query) { usual_tls_free(ctx); return NULL; } return ctx; } static int tls_build_ocsp_request(struct tls *ctx) { struct tls_ocsp_query *q; int ok, ret = -1; OCSP_REQUEST *req = NULL; OCSP_CERTID *cid = NULL; OCSP_ONEREQ *onereq = NULL; BIO *mem = NULL; void *data; q = ctx->ocsp_query; cid = tls_ocsp_get_certid(q->main_cert, q->extra_certs, q->cert_ssl_ctx); if (!cid) { tls_set_errorx(ctx, "Cannot create cert-id"); goto failed; } req = OCSP_REQUEST_new(); if (!req) { tls_set_error_libssl(ctx, "Cannot create request"); goto failed; } onereq = OCSP_request_add0_id(req, cid); if (!onereq) { tls_set_error_libssl(ctx, "Cannot add cert-id to request"); goto failed; } cid = NULL; /* * Now render it. */ mem = BIO_new(BIO_s_mem()); if (!mem) { tls_set_errorx(ctx, "BIO_new"); goto failed; } ok = i2d_OCSP_REQUEST_bio(mem, req); if (!ok) { tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio"); goto failed; } q->request_size = BIO_get_mem_data(mem, &data); q->request_data = malloc(q->request_size); if (!q->request_data) { tls_set_error(ctx, "Failed to allocate request data"); goto failed; } memcpy(q->request_data, data, q->request_size); req = NULL; ret = 0; failed: OCSP_CERTID_free(cid); OCSP_REQUEST_free(req); BIO_free(mem); return ret; } static int tls_ocsp_setup(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target) { struct tls *ctx; struct tls_ocsp_query *q; int ret = -1; STACK_OF(OPENSSL_STRING) *ocsp_urls; ctx = tls_ocsp_client_new(); if (!ctx) return -1; *ocsp_ctx_p = ctx; q = ctx->ocsp_query; if (config) { /* create ctx->ssl_ctx */ ctx->flags = TLS_SERVER; ret = tls_configure(ctx, config); ctx->flags = TLS_OCSP_CLIENT; if (ret != 0) return ret; q->main_cert = SSL_get_certificate(ctx->ssl_conn); q->cert_ssl_ctx = ctx->ssl_ctx; SSL_CTX_get_extra_chain_certs(ctx->ssl_ctx, &q->extra_certs); } else { /* steal state from target struct */ q->main_cert = SSL_get_peer_certificate(target->ssl_conn); q->extra_certs = SSL_get_peer_cert_chain(target->ssl_conn); q->cert_ssl_ctx = target->ssl_ctx; X509_free(q->main_cert); /* unref */ } if (!q->main_cert) { tls_set_errorx(ctx, "No cert"); return -1; } ocsp_urls = X509_get1_ocsp(q->main_cert); if (!ocsp_urls) return TLS_NO_OCSP; q->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0)); if (!q->ocsp_url) { tls_set_errorx(ctx, "Cannot copy URL"); goto failed; } ret = tls_build_ocsp_request(ctx); if (ret != 0) goto failed; *ocsp_ctx_p = ctx; failed: X509_email_free(ocsp_urls); return ret; } static int tls_ocsp_process_response_parsed(struct tls *ctx, struct tls_config *config, OCSP_RESPONSE *resp) { struct tls_ocsp_query *q = ctx->ocsp_query; BIO *mem = NULL; size_t len; unsigned char *data; int ret = -1, ok, res; res = tls_ocsp_verify_response(ctx, q->main_cert, q->extra_certs, q->cert_ssl_ctx, resp); if (res < 0) goto failed; /* Update blob in config */ if (config) { mem = BIO_new(BIO_s_mem()); if (!mem) { tls_set_error_libssl(ctx, "BIO_new"); goto failed; } ok = i2d_OCSP_RESPONSE_bio(mem, resp); if (!ok) { tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio"); goto failed; } len = BIO_get_mem_data(mem, &data); res = tls_config_set_ocsp_stapling_mem(config, data, len); if (res < 0) goto failed; } ret = 0; failed: BIO_free(mem); tls_ocsp_fill_result(ctx, ret); return ret; } static int tls_ocsp_create_request(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { int res; struct tls_ocsp_query *q; res = tls_ocsp_setup(ocsp_ctx_p, config, target); if (res != 0) return res; q = (*ocsp_ctx_p)->ocsp_query; *ocsp_url = q->ocsp_url; *request_blob = q->request_data; *request_size = q->request_size; return 0; } /* * Public API for request blobs. */ int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { return tls_ocsp_create_request(ocsp_ctx_p, NULL, target, ocsp_url, request_blob, request_size); } int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size) { return tls_ocsp_create_request(ocsp_ctx_p, config, NULL, ocsp_url, request_blob, request_size); } int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size) { int ret; OCSP_RESPONSE *resp; const unsigned char *raw = response_blob; resp = d2i_OCSP_RESPONSE(NULL, &raw, size); if (!resp) { ctx->ocsp_result = "parse-failed"; tls_set_error_libssl(ctx, "parse failed"); return -1; } ret = tls_ocsp_process_response_parsed(ctx, ctx->config, resp); OCSP_RESPONSE_free(resp); return ret; } /* * Network processing */ static int tls_ocsp_build_http_req(struct tls *ctx) { struct tls_ocsp_query *q = ctx->ocsp_query; int ok; OCSP_REQUEST *req; OCSP_REQ_CTX *sreq; const unsigned char *data; int ret=-1, https=0; char *host=NULL, *port=NULL, *path=NULL; ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https); if (ok != 1) { tls_set_error_libssl(ctx, "Cannot parse URL"); goto failed; } sreq = OCSP_sendreq_new(q->bio, path, NULL, -1); if (!sreq) { tls_set_error_libssl(ctx, "OCSP HTTP request setup failed"); goto failed; } q->http_req = sreq; ok = OCSP_REQ_CTX_add1_header(sreq, "Host", host); /* add payload after headers */ if (ok) { data = q->request_data; req = d2i_OCSP_REQUEST(NULL, &data, q->request_size); if (req) ok = OCSP_REQ_CTX_set1_req(sreq, req); else ok = false; OCSP_REQUEST_free(req); } if (ok) ret = 0; failed: free(host); free(port); free(path); return ret; } static int tls_ocsp_connection_setup(struct tls *ctx) { SSL *ssl = NULL; int ret = -1, ok, https=0; struct tls_ocsp_query *q = ctx->ocsp_query; union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; char *host=NULL, *port=NULL, *path=NULL; ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https); if (ok != 1) { tls_set_error_libssl(ctx, "Cannot parse URL"); goto failed; } if (https) { q->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (!q->ssl_ctx) { tls_set_error_libssl(ctx, "Cannot init SSL"); goto failed; } SSL_CTX_set_options(q->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_clear_options(q->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3); SSL_CTX_set_mode(q->ssl_ctx, SSL_MODE_AUTO_RETRY); q->bio = BIO_new_ssl_connect(q->ssl_ctx); if (q->bio) { if (inet_pton(AF_INET, host, &addrbuf) != 1 && inet_pton(AF_INET6, host, &addrbuf) != 1) { if (!BIO_get_ssl(q->bio, &ssl)) { tls_set_errorx(ctx, "cannot get ssl struct"); goto failed; } if (SSL_set_tlsext_host_name(ssl, host) == 0) { tls_set_errorx(ctx, "server name indication failure"); goto failed; } } } } else { q->bio = BIO_new(BIO_s_connect()); } if (!q->bio) { tls_set_error_libssl(ctx, "Cannot connect"); goto failed; } BIO_set_conn_hostname(q->bio, host); BIO_set_conn_port(q->bio, port); BIO_set_nbio(q->bio, 1); ret = 0; failed: free(host); free(port); free(path); return ret; } static int tls_ocsp_evloop(struct tls *ctx, int *fd_p, struct tls_config *config) { struct tls_ocsp_query *q = ctx->ocsp_query; OCSP_RESPONSE *ocsp_resp = NULL; int ret, ok; if (q->http_req == NULL) { ok = BIO_do_connect(q->bio); if (ok != 1 && !BIO_should_retry(q->bio)) { tls_set_error_libssl(ctx, "Connection failure"); goto error; } *fd_p = BIO_get_fd(q->bio, NULL); if (*fd_p < 0) { tls_set_error_libssl(ctx, "Cannot get FD"); goto error; } if (ok != 1) return TLS_WANT_POLLOUT; ret = tls_ocsp_build_http_req(ctx); if (ret != 0) goto error; } ok = OCSP_sendreq_nbio(&ocsp_resp, q->http_req); if (ok == 1) { ret = tls_ocsp_process_response_parsed(ctx, config, ocsp_resp); return ret; } else if (ok == 0) { tls_set_error_libssl(ctx, "OCSP request failed"); goto error; } else if (BIO_should_read(q->bio)) { return TLS_WANT_POLLIN; } else if (BIO_should_write(q->bio)) { return TLS_WANT_POLLOUT; } tls_set_error_libssl(ctx, "Unexpected request error"); error: tls_ocsp_fill_result(ctx, -1); return -1; } static int tls_ocsp_do_poll(struct tls *ctx, int errcode, int fd) { struct pollfd pfd; int res; memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; if (errcode == TLS_WANT_POLLIN) { pfd.events = POLLIN; } else if (errcode == TLS_WANT_POLLOUT) { pfd.events = POLLOUT; } else { tls_set_error(ctx, "bad code to poll"); return -1; } res = poll(&pfd, 1, -1); if (res == 1) { return 0; } else if (res == 0) { tls_set_errorx(ctx, "poll timed out"); return -1; } tls_set_error(ctx, "poll error"); return -1; } static int tls_ocsp_query_async(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target) { struct tls *ctx = *ocsp_ctx_p; int ret; if (!ctx) { ret = tls_ocsp_setup(&ctx, config, target); if (ret != 0) goto failed; ret = tls_ocsp_connection_setup(ctx); if (ret != 0) goto failed; *ocsp_ctx_p = ctx; } return tls_ocsp_evloop(ctx, fd_p, config); failed: usual_tls_free(ctx); return -1; } static int tls_ocsp_common_query(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target) { struct tls *ctx = NULL; int ret, fd = -1; /* async path */ if (fd_p) return tls_ocsp_query_async(ocsp_ctx_p, fd_p, config, target); /* sync path */ while (1) { ret = tls_ocsp_query_async(&ctx, &fd, config, target); if (ret != TLS_WANT_POLLIN && ret != TLS_WANT_POLLOUT) break; ret = tls_ocsp_do_poll(ctx, ret, fd); if (ret != 0) break; } *ocsp_ctx_p = ctx; return ret; } /* * Public API. */ int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target) { return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, NULL, target); } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, config, NULL); } #else /* No OCSP */ void tls_ocsp_info_free(struct tls_ocsp_info *info) {} void tls_ocsp_client_free(struct tls *ctx) {} int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { if (response_status) *response_status = -1; if (cert_status) *cert_status = -1; if (crl_reason) *crl_reason = -1; if (result_text) *result_text = "OCSP not supported"; if (this_update) *this_update = 0; if (next_update) *next_update = 0; if (revoction_time) *revoction_time = 0; return TLS_NO_OCSP; } int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target) { *ocsp_ctx_p = NULL; return TLS_NO_OCSP; } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { *ocsp_ctx_p = NULL; return TLS_NO_OCSP; } #endif /* OPENSSL_NO_OCSP */ #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_config.c0000644000175000000000000002401614777762223015507 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int set_string(const char **dest, const char *src) { free((char *)*dest); *dest = NULL; if (src != NULL) if ((*dest = strdup(src)) == NULL) return -1; return 0; } static void * memdup(const void *in, size_t len) { void *out; if ((out = malloc(len)) == NULL) return NULL; memcpy(out, in, len); return out; } static int set_mem(char **dest, size_t *destlen, const void *src, size_t srclen) { free(*dest); *dest = NULL; *destlen = 0; if (src != NULL) if ((*dest = memdup(src, srclen)) == NULL) return -1; *destlen = srclen; return 0; } struct tls_keypair * tls_keypair_new(void) { return calloc(1, sizeof(struct tls_keypair)); } int tls_keypair_set_cert_file(struct tls_keypair *keypair, const char *cert_file) { return set_string(&keypair->cert_file, cert_file); } static int tls_keypair_set_cert_mem(struct tls_keypair *keypair, const uint8_t *cert, size_t len) { return set_mem(&keypair->cert_mem, &keypair->cert_len, cert, len); } static int tls_keypair_set_key_file(struct tls_keypair *keypair, const char *key_file) { return set_string(&keypair->key_file, key_file); } static int tls_keypair_set_key_mem(struct tls_keypair *keypair, const uint8_t *key, size_t len) { if (keypair->key_mem != NULL) explicit_bzero(keypair->key_mem, keypair->key_len); return set_mem(&keypair->key_mem, &keypair->key_len, key, len); } static void tls_keypair_clear(struct tls_keypair *keypair) { tls_keypair_set_cert_mem(keypair, NULL, 0); tls_keypair_set_key_mem(keypair, NULL, 0); } static void tls_keypair_free(struct tls_keypair *keypair) { if (keypair == NULL) return; tls_keypair_clear(keypair); free((char *)keypair->cert_file); free(keypair->cert_mem); free((char *)keypair->key_file); free(keypair->key_mem); free(keypair); } struct tls_config * tls_config_new(void) { struct tls_config *config; if ((config = calloc(1, sizeof(*config))) == NULL) return (NULL); if ((config->keypair = tls_keypair_new()) == NULL) goto err; /* * Default configuration. */ if (tls_config_set_ca_file(config, _PATH_SSL_CA_FILE) != 0) goto err; if (tls_config_set_dheparams(config, "none") != 0) goto err; if (tls_config_set_ecdhecurve(config, "auto") != 0) goto err; if (tls_config_set_ciphers(config, "secure") != 0) goto err; tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT); tls_config_set_verify_depth(config, 6); tls_config_prefer_ciphers_server(config); tls_config_verify(config); return (config); err: tls_config_free(config); return (NULL); } void tls_config_free(struct tls_config *config) { struct tls_keypair *kp, *nkp; if (config == NULL) return; for (kp = config->keypair; kp != NULL; kp = nkp) { nkp = kp->next; tls_keypair_free(kp); } free(config->error.msg); free((char *)config->ca_file); free((char *)config->ca_mem); free((char *)config->ca_path); free((char *)config->ciphers); free(config); } const char * tls_config_error(struct tls_config *config) { return config->error.msg; } void tls_config_clear_keys(struct tls_config *config) { struct tls_keypair *kp; for (kp = config->keypair; kp != NULL; kp = kp->next) tls_keypair_clear(kp); tls_config_set_ca_mem(config, NULL, 0); } int tls_config_parse_protocols(uint32_t *protocols, const char *protostr) { uint32_t proto, protos = 0; char *s, *p, *q; int negate; if ((s = strdup(protostr)) == NULL) return (-1); q = s; while ((p = strsep(&q, ",:")) != NULL) { while (*p == ' ' || *p == '\t') p++; negate = 0; if (*p == '!') { negate = 1; p++; } if (negate && protos == 0) protos = TLS_PROTOCOLS_ALL; proto = 0; if (strcasecmp(p, "all") == 0) proto = TLS_PROTOCOLS_ALL; else if (strcasecmp(p, "default") == 0 || strcasecmp(p, "secure") == 0) proto = TLS_PROTOCOLS_DEFAULT; if (strcasecmp(p, "tlsv1") == 0) proto = TLS_PROTOCOL_TLSv1; else if (strcasecmp(p, "tlsv1.0") == 0) proto = TLS_PROTOCOL_TLSv1_0; else if (strcasecmp(p, "tlsv1.1") == 0) proto = TLS_PROTOCOL_TLSv1_1; else if (strcasecmp(p, "tlsv1.2") == 0) proto = TLS_PROTOCOL_TLSv1_2; else if (strcasecmp(p, "tlsv1.3") == 0) proto = TLS_PROTOCOL_TLSv1_3; if (proto == 0) { free(s); return (-1); } if (negate) protos &= ~proto; else protos |= proto; } *protocols = protos; free(s); return (0); } int tls_config_set_ca_file(struct tls_config *config, const char *ca_file) { return set_string(&config->ca_file, ca_file); } int tls_config_set_ca_path(struct tls_config *config, const char *ca_path) { return set_string(&config->ca_path, ca_path); } int tls_config_set_ca_mem(struct tls_config *config, const uint8_t *ca, size_t len) { return set_mem(&config->ca_mem, &config->ca_len, ca, len); } int tls_config_set_cert_file(struct tls_config *config, const char *cert_file) { return tls_keypair_set_cert_file(config->keypair, cert_file); } int tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert, size_t len) { return tls_keypair_set_cert_mem(config->keypair, cert, len); } int tls_config_set_ciphers(struct tls_config *config, const char *ciphers) { SSL_CTX *ssl_ctx = NULL; /* * obsolete outdated keywords, turn them to default. * For default, don't call SSL_CTX_set_cipher_list() * so we inherit openssl's default, which can also * include a system-wide policy, such as on rhel/fedora * https://docs.fedoraproject.org/en-US/packaging-guidelines/CryptoPolicies/ */ if (ciphers == NULL || strcasecmp(ciphers, "default") == 0 || strcasecmp(ciphers, "secure") == 0 || strcasecmp(ciphers, "normal") == 0 || strcasecmp(ciphers, "fast") == 0) { return set_string(&config->ciphers, ciphers); } /* * At this point, these are custom supplied openssl compatible * cipher strings. */ if ((ssl_ctx = SSL_CTX_new(SSLv23_method())) == NULL) { tls_config_set_errorx(config, "out of memory"); goto fail; } if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { tls_config_set_errorx(config, "no ciphers for '%s'", ciphers); goto fail; } SSL_CTX_free(ssl_ctx); return set_string(&config->ciphers, ciphers); fail: SSL_CTX_free(ssl_ctx); return -1; } int tls_config_set_dheparams(struct tls_config *config, const char *params) { int keylen; if (params == NULL || strcasecmp(params, "none") == 0) keylen = 0; else if (strcasecmp(params, "auto") == 0) keylen = -1; else { tls_config_set_errorx(config, "invalid dhe param '%s'", params); return (-1); } config->dheparams = keylen; return (0); } int tls_config_set_ecdhecurve(struct tls_config *config, const char *name) { int nid; if (name == NULL || strcasecmp(name, "none") == 0) nid = NID_undef; else if (strcasecmp(name, "auto") == 0) nid = -1; else if ((nid = OBJ_txt2nid(name)) == NID_undef) { tls_config_set_errorx(config, "invalid ecdhe curve '%s'", name); return (-1); } config->ecdhecurve = nid; return (0); } int tls_config_set_key_file(struct tls_config *config, const char *key_file) { return tls_keypair_set_key_file(config->keypair, key_file); } int tls_config_set_key_mem(struct tls_config *config, const uint8_t *key, size_t len) { return tls_keypair_set_key_mem(config->keypair, key, len); } int tls_config_set_keypair_file(struct tls_config *config, const char *cert_file, const char *key_file) { if (tls_config_set_cert_file(config, cert_file) != 0) return (-1); if (tls_config_set_key_file(config, key_file) != 0) return (-1); return (0); } int tls_config_set_keypair_mem(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len) { if (tls_config_set_cert_mem(config, cert, cert_len) != 0) return (-1); if (tls_config_set_key_mem(config, key, key_len) != 0) return (-1); return (0); } int tls_config_set_ocsp_stapling_file(struct tls_config *config, const char *blob_file) { if (blob_file != NULL) tls_config_set_ocsp_stapling_mem(config, NULL, 0); return set_string(&config->ocsp_file, blob_file); } int tls_config_set_ocsp_stapling_mem(struct tls_config *config, const uint8_t *blob, size_t len) { if (blob != NULL) tls_config_set_ocsp_stapling_file(config, NULL); return set_mem(&config->ocsp_mem, &config->ocsp_len, blob, len); } void tls_config_set_protocols(struct tls_config *config, uint32_t protocols) { config->protocols = protocols; } void tls_config_set_verify_depth(struct tls_config *config, int verify_depth) { config->verify_depth = verify_depth; } void tls_config_prefer_ciphers_client(struct tls_config *config) { config->ciphers_server = 0; } void tls_config_prefer_ciphers_server(struct tls_config *config) { config->ciphers_server = 1; } void tls_config_insecure_noverifycert(struct tls_config *config) { config->verify_cert = 0; } void tls_config_insecure_noverifyname(struct tls_config *config) { config->verify_name = 0; } void tls_config_insecure_noverifytime(struct tls_config *config) { config->verify_time = 0; } void tls_config_verify(struct tls_config *config) { config->verify_cert = 1; config->verify_name = 1; config->verify_time = 1; } void tls_config_verify_client(struct tls_config *config) { config->verify_client = 1; } void tls_config_verify_client_optional(struct tls_config *config) { config->verify_client = 2; } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_cert.c0000644000175000000000000004100414777762223015173 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" #define TLS_CERT_INTERNAL_FUNCS #include "tls_cert.h" /* * Load cert data from X509 cert. */ /* Upper bounds */ #define UB_COMMON_NAME 255 #define UB_COUNTRY_NAME 255 #define UB_STATE_NAME 255 #define UB_LOCALITY_NAME 255 #define UB_STREET_ADDRESS 255 #define UB_ORGANIZATION_NAME 255 #define UB_ORGANIZATIONAL_UNIT_NAME 255 #define UB_GNAME_DNS 255 #define UB_GNAME_EMAIL 255 #define UB_GNAME_URI 255 /* Convert ASN1_INTEGER to decimal string string */ static int tls_parse_bigint(struct tls *ctx, const ASN1_INTEGER *asn1int, const char **dst_p) { long small; BIGNUM *big; char *tmp, buf[64]; *dst_p = NULL; small = ASN1_INTEGER_get(asn1int); if (small < 0) { big = ASN1_INTEGER_to_BN(asn1int, NULL); if (big) { tmp = BN_bn2dec(big); if (tmp) *dst_p = strdup(tmp); OPENSSL_free(tmp); } BN_free(big); } else { snprintf(buf, sizeof buf, "%lu", small); *dst_p = strdup(buf); } if (*dst_p) return 0; tls_set_errorx(ctx, "cannot parse serial"); return -1; } /* * Decode all string types used in RFC5280. * * OpenSSL used (before Jun 1 2014 commit) to pick between PrintableString, * T61String, BMPString and UTF8String, depending on data. This code * tries to match that. * * Disallow any ancient ASN.1 escape sequences. */ static int check_invalid_bytes(struct tls *ctx, const unsigned char *data, unsigned int len, int ascii_only, const char *desc) { unsigned int i, c; /* data is utf8 string, check for crap */ for (i = 0; i < len; i++) { c = data[i]; if (ascii_only && (c & 0x80) != 0) { tls_set_errorx(ctx, "invalid %s: contains non-ascii in ascii string", desc); goto failed; } else if (c < 0x20) { /* ascii control chars, including NUL */ if (c != '\t' && c != '\n' && c != '\r') { tls_set_errorx(ctx, "invalid %s: contains C0 control char", desc); goto failed; } } else if (c == 0xC2 && (i + 1) < len) { /* C1 control chars in UTF-8: \xc2\x80 - \xc2\x9f */ c = data[i + 1]; if (c >= 0x80 && c <= 0x9F) { tls_set_errorx(ctx, "invalid %s: contains C1 control char", desc); goto failed; } } else if (c == 0x7F) { tls_set_errorx(ctx, "invalid %s: contains DEL char", desc); goto failed; } } return 0; failed: return -1; } static int tls_parse_asn1string(struct tls *ctx, ASN1_STRING *a1str, const char **dst_p, int minchars, int maxchars, const char *desc) { int format, len, ret = -1; const unsigned char *data; ASN1_STRING *a1utf = NULL; int ascii_only = 0; char *cstr = NULL; int mbres, mbconvert = -1; *dst_p = NULL; format = ASN1_STRING_type(a1str); data = ASN1_STRING_get0_data(a1str); len = ASN1_STRING_length(a1str); if (len < minchars) { tls_set_errorx(ctx, "invalid %s: string too short", desc); goto failed; } switch (format) { case V_ASN1_NUMERICSTRING: case V_ASN1_VISIBLESTRING: case V_ASN1_PRINTABLESTRING: case V_ASN1_IA5STRING: /* Ascii */ if (len > maxchars) { tls_set_errorx(ctx, "invalid %s: string too long", desc); goto failed; } ascii_only = 1; break; case V_ASN1_T61STRING: /* Latin1 */ mbconvert = MBSTRING_ASC; break; case V_ASN1_BMPSTRING: /* UCS-2 big-endian */ mbconvert = MBSTRING_BMP; break; case V_ASN1_UNIVERSALSTRING: /* UCS-4 big-endian */ mbconvert = MBSTRING_UNIV; break; case V_ASN1_UTF8STRING: /* * UTF-8 - could be used directly if OpenSSL has already * validated the data. ATM be safe and validate here. */ mbconvert = MBSTRING_UTF8; break; default: tls_set_errorx(ctx, "invalid %s: unexpected string type", desc); goto failed; } /* Convert to UTF-8 */ if (mbconvert != -1) { mbres = ASN1_mbstring_ncopy(&a1utf, data, len, mbconvert, B_ASN1_UTF8STRING, minchars, maxchars); if (mbres < 0) { tls_set_error_libssl(ctx, "invalid %s", desc); goto failed; } if (mbres != V_ASN1_UTF8STRING) { tls_set_errorx(ctx, "multibyte conversion failed: expected UTF8 result"); goto failed; } data = ASN1_STRING_get0_data(a1utf); len = ASN1_STRING_length(a1utf); } /* must not allow \0 */ if (memchr(data, 0, len) != NULL) { tls_set_errorx(ctx, "invalid %s: contains NUL", desc); goto failed; } /* no escape codes please */ if (check_invalid_bytes(ctx, data, len, ascii_only, desc) < 0) goto failed; /* copy to new string */ cstr = malloc(len + 1); if (!cstr) { tls_set_error(ctx, "malloc"); goto failed; } memcpy(cstr, data, len); cstr[len] = 0; *dst_p = cstr; ret = len; failed: ASN1_STRING_free(a1utf); return ret; } static int tls_cert_get_dname_string(struct tls *ctx, X509_NAME *name, int nid, const char **str_p, int minchars, int maxchars, const char *desc) { int loc, len; X509_NAME_ENTRY *ne; ASN1_STRING *a1str; *str_p = NULL; loc = X509_NAME_get_index_by_NID(name, nid, -1); if (loc < 0) return 0; ne = X509_NAME_get_entry(name, loc); if (!ne) return 0; a1str = X509_NAME_ENTRY_get_data(ne); if (!a1str) return 0; len = tls_parse_asn1string(ctx, a1str, str_p, minchars, maxchars, desc); if (len < 0) return -1; return 0; } static int tls_load_alt_ia5string(struct tls *ctx, ASN1_IA5STRING *ia5str, struct tls_cert *cert, int slot_type, int minchars, int maxchars, const char *desc) { struct tls_cert_general_name *slot; const char *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = tls_parse_asn1string(ctx, ia5str, &data, minchars, maxchars, desc); if (len < 0) return 0; /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (len == 1 && data[0] == ' ') { tls_set_errorx(ctx, "invalid %s: single space", desc); return -1; } slot->name_value = data; slot->name_type = slot_type; cert->subject_alt_name_count++; return 0; } static int tls_load_alt_ipaddr(struct tls *ctx, ASN1_OCTET_STRING *bin, struct tls_cert *cert) { struct tls_cert_general_name *slot; const void *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = ASN1_STRING_length(bin); data = ASN1_STRING_get0_data(bin); if (len < 0) { tls_set_errorx(ctx, "negative length for ipaddress"); return -1; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (len == 4) { slot->name_type = TLS_CERT_GNAME_IPv4; } else if (len == 16) { slot->name_type = TLS_CERT_GNAME_IPv6; } else { tls_set_errorx(ctx, "invalid length for ipaddress"); return -1; } slot->name_value = malloc(len); if (slot->name_value == NULL) { tls_set_error(ctx, "malloc"); return -1; } memcpy((void *)slot->name_value, data, len); cert->subject_alt_name_count++; return 0; } /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_cert_get_altnames(struct tls *ctx, struct tls_cert *cert, X509 *x509_cert) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; GENERAL_NAME *altname; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return 0; count = sk_GENERAL_NAME_num(altname_stack); if (count == 0) { rv = 0; goto out; } cert->subject_alt_names = calloc(count, sizeof(struct tls_cert_general_name)); if (cert->subject_alt_names == NULL) { tls_set_error(ctx, "calloc"); goto out; } for (i = 0; i < count; i++) { altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type == GEN_DNS) { rv = tls_load_alt_ia5string(ctx, altname->d.dNSName, cert, TLS_CERT_GNAME_DNS, 1, UB_GNAME_DNS, "dns"); } else if (altname->type == GEN_EMAIL) { rv = tls_load_alt_ia5string(ctx, altname->d.rfc822Name, cert, TLS_CERT_GNAME_EMAIL, 1, UB_GNAME_EMAIL, "email"); } else if (altname->type == GEN_URI) { rv = tls_load_alt_ia5string(ctx, altname->d.uniformResourceIdentifier, cert, TLS_CERT_GNAME_URI, 1, UB_GNAME_URI, "uri"); } else if (altname->type == GEN_IPADD) { rv = tls_load_alt_ipaddr(ctx, altname->d.iPAddress, cert); } else { /* ignore unknown types */ rv = 0; } if (rv < 0) goto out; } rv = 0; out: sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_get_dname(struct tls *ctx, X509_NAME *name, struct tls_cert_dname *dname) { int ret; ret = tls_cert_get_dname_string(ctx, name, NID_commonName, &dname->common_name, 0, UB_COMMON_NAME, "commonName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_countryName, &dname->country_name, 0, UB_COUNTRY_NAME, "countryName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_stateOrProvinceName, &dname->state_or_province_name, 0, UB_STATE_NAME, "stateName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_localityName, &dname->locality_name, 0, UB_LOCALITY_NAME, "localityName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_streetAddress, &dname->street_address, 0, UB_STREET_ADDRESS, "streetAddress"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_organizationName, &dname->organization_name, 0, UB_ORGANIZATION_NAME, "organizationName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_organizationalUnitName, &dname->organizational_unit_name, 0, UB_ORGANIZATIONAL_UNIT_NAME, "organizationalUnitName"); return ret; } static int tls_get_basic_constraints(struct tls *ctx, struct tls_cert *cert, X509 *x509) { BASIC_CONSTRAINTS *bc; int crit; int ret = -1; bc = X509_get_ext_d2i(x509, NID_basic_constraints, &crit, NULL); if (!bc) return 0; cert->ext_set |= TLS_EXT_BASIC; if (crit) cert->ext_crit |= TLS_EXT_BASIC; cert->basic_constraints_ca = bc->ca ? 1 : 0; if (bc->pathlen) { cert->basic_constraints_pathlen = ASN1_INTEGER_get(bc->pathlen); if (cert->basic_constraints_pathlen < 0) { tls_set_error(ctx, "BasicConstraints has invalid pathlen"); goto failed; } } else { cert->basic_constraints_pathlen = -1; } ret = 0; failed: BASIC_CONSTRAINTS_free(bc); return ret; } static uint32_t map_bits(const uint32_t map[][2], uint32_t input) { uint32_t i, out = 0; for (i = 0; map[i][0]; i++) { if (map[i][0] & input) out |= map[i][1]; } return out; } static int tls_get_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509) { static const uint32_t ku_map[][2] = { {KU_DIGITAL_SIGNATURE, KU_DIGITAL_SIGNATURE}, {KU_NON_REPUDIATION, KU_NON_REPUDIATION}, {KU_KEY_ENCIPHERMENT, KU_KEY_ENCIPHERMENT}, {KU_DATA_ENCIPHERMENT, KU_DATA_ENCIPHERMENT}, {KU_KEY_AGREEMENT, KU_KEY_AGREEMENT}, {KU_KEY_CERT_SIGN, KU_KEY_CERT_SIGN}, {KU_CRL_SIGN, KU_CRL_SIGN}, {KU_ENCIPHER_ONLY, KU_ENCIPHER_ONLY}, {KU_DECIPHER_ONLY, KU_DECIPHER_ONLY}, {0, 0}, }; ASN1_BIT_STRING *ku; int crit; ku = X509_get_ext_d2i(x509, NID_key_usage, &crit, NULL); if (!ku) return 0; cert->ext_set |= TLS_EXT_KEY_USAGE; if (crit) cert->ext_crit |= TLS_EXT_KEY_USAGE; ASN1_BIT_STRING_free(ku); cert->key_usage_flags = map_bits(ku_map, X509_get_key_usage(x509)); return 0; } static int tls_get_ext_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509) { static const uint32_t xku_map[][2] = { {XKU_SSL_SERVER, TLS_XKU_SSL_SERVER}, {XKU_SSL_CLIENT, TLS_XKU_SSL_CLIENT}, {XKU_SMIME, TLS_XKU_SMIME}, {XKU_CODE_SIGN, TLS_XKU_CODE_SIGN}, {XKU_SGC, TLS_XKU_SGC}, {XKU_OCSP_SIGN, TLS_XKU_OCSP_SIGN}, {XKU_TIMESTAMP, TLS_XKU_TIMESTAMP}, {XKU_DVCS, TLS_XKU_DVCS}, {0, 0}, }; EXTENDED_KEY_USAGE *xku; int crit; xku = X509_get_ext_d2i(x509, NID_ext_key_usage, &crit, NULL); if (!xku) return 0; sk_ASN1_OBJECT_pop_free(xku, ASN1_OBJECT_free); cert->ext_set |= TLS_EXT_EXTENDED_KEY_USAGE; if (crit) cert->ext_crit |= TLS_EXT_EXTENDED_KEY_USAGE; cert->extended_key_usage_flags = map_bits(xku_map, X509_get_extended_key_usage(x509)); return 0; } static int tls_load_extensions(struct tls *ctx, struct tls_cert *cert, X509 *x509) { int ret; /* * Force libssl to fill extension fields under X509 struct. * Then libtls does not need to parse raw data. */ X509_check_ca(x509); ret = tls_get_basic_constraints(ctx, cert, x509); if (ret == 0) ret = tls_get_key_usage(ctx, cert, x509); if (ret == 0) ret = tls_get_ext_key_usage(ctx, cert, x509); if (ret == 0) ret = tls_cert_get_altnames(ctx, cert, x509); return ret; } static void * tls_calc_fingerprint(struct tls *ctx, X509 *x509, const char *algo, size_t *outlen) { const EVP_MD *md; void *res; int ret; unsigned int tmplen, mdlen; if (outlen) *outlen = 0; if (strcasecmp(algo, "sha1") == 0) { md = EVP_sha1(); } else if (strcasecmp(algo, "sha256") == 0) { md = EVP_sha256(); } else { tls_set_errorx(ctx, "invalid fingerprint algorithm"); return NULL; } mdlen = EVP_MD_size(md); res = malloc(mdlen); if (!res) { tls_set_error(ctx, "malloc"); return NULL; } ret = X509_digest(x509, md, res, &tmplen); if (ret != 1 || tmplen != mdlen) { free(res); tls_set_errorx(ctx, "X509_digest failed"); return NULL; } if (outlen) *outlen = mdlen; return res; } static void check_verify_error(struct tls *ctx, struct tls_cert *cert) { long vres = SSL_get_verify_result(ctx->ssl_conn); if (vres == X509_V_OK) { cert->successful_verify = 1; } else { cert->successful_verify = 0; } } int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509) { struct tls_cert *cert = NULL; X509_NAME *subject, *issuer; int ret = -1; long version; *cert_p = NULL; version = X509_get_version(x509); if (version < 0) { tls_set_errorx(ctx, "invalid version"); return -1; } subject = X509_get_subject_name(x509); if (!subject) { tls_set_errorx(ctx, "cert does not have subject"); return -1; } issuer = X509_get_issuer_name(x509); if (!issuer) { tls_set_errorx(ctx, "cert does not have issuer"); return -1; } cert = calloc(1, sizeof(*cert)); if (!cert) { tls_set_error(ctx, "calloc"); goto failed; } cert->version = version; if (fingerprint_algo) { cert->fingerprint = tls_calc_fingerprint(ctx, x509, fingerprint_algo, &cert->fingerprint_size); if (!cert->fingerprint) goto failed; } ret = tls_get_dname(ctx, subject, &cert->subject); if (ret == 0) ret = tls_get_dname(ctx, issuer, &cert->issuer); if (ret == 0) ret = tls_asn1_parse_time(ctx, X509_get_notBefore(x509), &cert->not_before); if (ret == 0) ret = tls_asn1_parse_time(ctx, X509_get_notAfter(x509), &cert->not_after); if (ret == 0) ret = tls_parse_bigint(ctx, X509_get_serialNumber(x509), &cert->serial); if (ret == 0) ret = tls_load_extensions(ctx, cert, x509); if (ret == 0) { *cert_p = cert; return 0; } failed: tls_cert_free(cert); return ret; } int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo) { X509 *peer = ctx->ssl_peer_cert; int res; *cert_p = NULL; if (!peer) { tls_set_errorx(ctx, "peer does not have cert"); return TLS_NO_CERT; } ERR_clear_error(); res = tls_parse_cert(ctx, cert_p, fingerprint_algo, peer); if (res == 0) check_verify_error(ctx, *cert_p); ERR_clear_error(); return res; } static void tls_cert_free_dname(struct tls_cert_dname *dname) { free((void*)dname->common_name); free((void*)dname->country_name); free((void*)dname->state_or_province_name); free((void*)dname->locality_name); free((void*)dname->street_address); free((void*)dname->organization_name); free((void*)dname->organizational_unit_name); } void tls_cert_free(struct tls_cert *cert) { int i; if (!cert) return; tls_cert_free_dname(&cert->issuer); tls_cert_free_dname(&cert->subject); if (cert->subject_alt_name_count) { for (i = 0; i < cert->subject_alt_name_count; i++) free((void*)cert->subject_alt_names[i].name_value); } free(cert->subject_alt_names); free((void*)cert->serial); free((void*)cert->fingerprint); free(cert); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_peer.c0000644000175000000000000000373514777762223015202 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include "tls_internal.h" const char * tls_peer_cert_hash(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->hash); return NULL; } const char * tls_peer_cert_issuer(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->issuer); return NULL; } const char * tls_peer_cert_subject(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->subject); return NULL; } int tls_peer_cert_provided(struct tls *ctx) { return (ctx->ssl_peer_cert != NULL); } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { if (ctx->ssl_peer_cert == NULL) return (0); return (tls_check_name(ctx, ctx->ssl_peer_cert, name) == 0); } time_t tls_peer_cert_notbefore(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notbefore); } time_t tls_peer_cert_notafter(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notafter); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_internal.h0000644000175000000000000001137314777762223016065 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HEADER_TLS_INTERNAL_H #define HEADER_TLS_INTERNAL_H #include #include #define _PATH_SSL_CA_FILE USUAL_TLS_CA_FILE union tls_addr { struct in_addr ip4; struct in6_addr ip6; }; struct tls_error { char *msg; int num; }; struct tls_keypair { struct tls_keypair *next; const char *cert_file; char *cert_mem; size_t cert_len; const char *key_file; char *key_mem; size_t key_len; }; struct tls_config { struct tls_error error; const char *ca_file; const char *ca_path; char *ca_mem; size_t ca_len; const char *ciphers; int ciphers_server; int dheparams; int ecdhecurve; struct tls_keypair *keypair; const char *ocsp_file; char *ocsp_mem; size_t ocsp_len; uint32_t protocols; int verify_cert; int verify_client; int verify_depth; int verify_name; int verify_time; }; struct tls_conninfo { char *issuer; char *subject; char *hash; char *serial; char *fingerprint; char *version; char *cipher; time_t notbefore; time_t notafter; }; #define TLS_CLIENT (1 << 0) #define TLS_SERVER (1 << 1) #define TLS_SERVER_CONN (1 << 2) #define TLS_OCSP_CLIENT (1 << 3) #define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0) #define TLS_HANDSHAKE_COMPLETE (1 << 1) #define TLS_DO_ABORT (1 << 8) struct tls_ocsp_query; struct tls_ocsp_info; struct tls { struct tls_config *config; struct tls_error error; uint32_t flags; uint32_t state; char *servername; int socket; SSL *ssl_conn; SSL_CTX *ssl_ctx; X509 *ssl_peer_cert; struct tls_conninfo *conninfo; int used_dh_bits; int used_ecdh_nid; const char *ocsp_result; struct tls_ocsp_info *ocsp_info; struct tls_ocsp_query *ocsp_query; }; struct tls_ocsp_info { int response_status; int cert_status; int crl_reason; time_t this_update; time_t next_update; time_t revocation_time; }; struct tls *tls_new(void); struct tls *tls_server_conn(struct tls *ctx); int tls_check_name(struct tls *ctx, X509 *cert, const char *servername); int tls_configure_keypair(struct tls *ctx, SSL_CTX *ssl_ctx, struct tls_keypair *keypair, int required); int tls_configure_server(struct tls *ctx); int tls_configure_ssl(struct tls *ctx); int tls_configure_ssl_verify(struct tls *ctx, int verify); int tls_handshake_client(struct tls *ctx); int tls_handshake_server(struct tls *ctx); int tls_host_port(const char *hostport, char **host, char **port); int tls_error_set(struct tls_error *error, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_error_setx(struct tls_error *error, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_config_set_error(struct tls_config *cfg, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_config_set_errorx(struct tls_config *cfg, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_set_error(struct tls *ctx, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_set_errorx(struct tls *ctx, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_set_error_libssl(struct tls *ctx, const char *fmt, ...) _PRINTF(2, 3) __attribute__((__nonnull__ (2))); int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix); int tls_get_conninfo(struct tls *ctx); void tls_free_conninfo(struct tls_conninfo *conninfo); int tls_ocsp_verify_callback(SSL *ssl, void *arg); int tls_ocsp_stapling_callback(SSL *ssl, void *arg); void tls_ocsp_client_free(struct tls *ctx); void tls_ocsp_info_free(struct tls_ocsp_info *info); int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst); int asn1_time_parse(const char *, size_t, struct tm *, int); struct tls_keypair * tls_keypair_new(void); int tls_keypair_set_cert_file(struct tls_keypair *keypair, const char *cert_file); bool tls_keypair_list_equal(struct tls_keypair *tkp1, struct tls_keypair *tkp2); #endif /* HEADER_TLS_INTERNAL_H */ pgbouncer-1.24.1/lib/usual/tls/tls_verify.c0000644000175000000000000001423214777762223015545 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int tls_match_name(const char *cert_name, const char *name); static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name); static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name); static int tls_match_name(const char *cert_name, const char *name) { const char *cert_domain, *domain, *next_dot; if (strcasecmp(cert_name, name) == 0) return 0; /* Wildcard match? */ if (cert_name[0] == '*') { /* * Valid wildcards: * - "*.domain.tld" * - "*.sub.domain.tld" * - etc. * Reject "*.tld". * No attempt to prevent the use of eg. "*.co.uk". */ cert_domain = &cert_name[1]; /* Disallow "*" */ if (cert_domain[0] == '\0') return -1; /* Disallow "*foo" */ if (cert_domain[0] != '.') return -1; /* Disallow "*.." */ if (cert_domain[1] == '.') return -1; next_dot = strchr(&cert_domain[1], '.'); /* Disallow "*.bar" */ if (next_dot == NULL) return -1; /* Disallow "*.bar.." */ if (next_dot[1] == '.') return -1; domain = strchr(name, '.'); /* No wildcard match against a name with no host part. */ if (name[0] == '.') return -1; /* No wildcard match against a name with no domain part. */ if (domain == NULL || strlen(domain) == 1) return -1; if (strcasecmp(cert_domain, domain) == 0) return 0; } return -1; } /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; union tls_addr addrbuf; int addrlen, type; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return -1; if (inet_pton(AF_INET, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 4; } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 16; } else { type = GEN_DNS; addrlen = 0; } count = sk_GENERAL_NAME_num(altname_stack); for (i = 0; i < count; i++) { GENERAL_NAME *altname; altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type != type) continue; if (type == GEN_DNS) { const void *data; int format, len; format = ASN1_STRING_type(altname->d.dNSName); if (format == V_ASN1_IA5STRING) { data = ASN1_STRING_get0_data(altname->d.dNSName); len = ASN1_STRING_length(altname->d.dNSName); if (len < 0 || len != (int)strlen(data)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in subjectAltName, " "probably a malicious certificate", name); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (strcmp(data, " ") == 0) { tls_set_error(ctx, "error verifying name '%s': " "a dNSName of \" \" must not be " "used", name); rv = -2; break; } if (tls_match_name(data, name) == 0) { rv = 0; break; } } else { #ifdef DEBUG fprintf(stdout, "%s: unhandled subjectAltName " "dNSName encoding (%d)\n", getprogname(), format); #endif } } else if (type == GEN_IPADD) { const unsigned char *data; int datalen; datalen = ASN1_STRING_length(altname->d.iPAddress); data = ASN1_STRING_get0_data(altname->d.iPAddress); if (datalen < 0) { tls_set_errorx(ctx, "Unexpected negative length for an " "IP address: %d", datalen); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (datalen == addrlen && memcmp(data, &addrbuf, addrlen) == 0) { rv = 0; break; } } } sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name) { X509_NAME *subject_name; char *common_name = NULL; union tls_addr addrbuf; int common_name_len; int rv = -1; subject_name = X509_get_subject_name(cert); if (subject_name == NULL) goto out; common_name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, NULL, 0); if (common_name_len < 0) goto out; common_name = calloc(common_name_len + 1, 1); if (common_name == NULL) goto out; X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name, common_name_len + 1); /* NUL bytes in CN? */ if (common_name_len != (int)strlen(common_name)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in Common Name field, " "probably a malicious certificate", name); rv = -2; goto out; } if (inet_pton(AF_INET, name, &addrbuf) == 1 || inet_pton(AF_INET6, name, &addrbuf) == 1) { /* * We don't want to attempt wildcard matching against IP * addresses, so perform a simple comparison here. */ if (strcmp(common_name, name) == 0) rv = 0; else rv = -1; goto out; } if (tls_match_name(common_name, name) == 0) rv = 0; out: free(common_name); return rv; } int tls_check_name(struct tls *ctx, X509 *cert, const char *name) { int rv; rv = tls_check_subject_altname(ctx, cert, name); if (rv == 0 || rv == -2) return rv; return tls_check_common_name(ctx, cert, name); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_compat.c0000644000175000000000000003540614777762223015532 00000000000000/* * Compatibility with various libssl implementations. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #ifndef SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE #undef SSLerr #undef X509err #endif #ifndef SSLerr #define SSLerr(a,b) do {} while (0) #define X509err(a,b) do {} while (0) #endif #ifndef SSL_CTX_set_dh_auto #define DH_CLEANUP /* * SKIP primes, used by OpenSSL and PostgreSQL. * * https://tools.ietf.org/html/draft-ietf-ipsec-skip-06 */ static const char file_dh2048[] = "-----BEGIN DH PARAMETERS-----\n" "MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n" "89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n" "T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n" "zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n" "Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n" "CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n" "-----END DH PARAMETERS-----\n"; static const char file_dh4096[] = "-----BEGIN DH PARAMETERS-----\n" "MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n" "l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n" "Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n" "Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n" "VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n" "alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n" "sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n" "ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n" "OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n" "AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n" "KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n" "-----END DH PARAMETERS-----\n"; static DH *dh2048, *dh4096; static DH *load_dh_buffer(struct tls *ctx, DH **dhp, const char *buf) { BIO *bio; DH *dh = *dhp; if (dh == NULL) { bio = BIO_new_mem_buf((char *)buf, strlen(buf)); if (bio) { dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); } *dhp = dh; } if (ctx) ctx->used_dh_bits = DH_size(dh) * 8; return dh; } static DH *dh_auto_cb(SSL *s, int is_export, int keylength) { EVP_PKEY *pk; int bits; struct tls *ctx = SSL_get_app_data(s); pk = SSL_get_privatekey(s); if (!pk) return load_dh_buffer(ctx, &dh2048, file_dh2048); bits = EVP_PKEY_bits(pk); if (bits >= 3072) return load_dh_buffer(ctx, &dh4096, file_dh4096); return load_dh_buffer(ctx, &dh2048, file_dh2048); } long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff) { if (onoff == 0) { return 1; } else { SSL_CTX_set_tmp_dh_callback(ctx, dh_auto_cb); } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); return 1; } #endif #ifndef SSL_CTX_set_ecdh_auto #define ECDH_CLEANUP /* * Use same curve as EC key, fallback to NIST P-256. */ static EC_KEY *ecdh_cache; #ifdef USE_LIBSSL_OLD static EC_KEY *ecdh_auto_cb(SSL *ssl, int is_export, int keylength) { int last_nid; int nid = 0; EVP_PKEY *pk; EC_KEY *ec; struct tls *ctx = SSL_get_app_data(ssl); /* pick curve from EC key */ pk = SSL_get_privatekey(ssl); if (pk && EVP_PKEY_id(pk) == EVP_PKEY_EC) { ec = EVP_PKEY_get1_EC_KEY(pk); if (ec) { nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); } } /* ssl->tlsext_ellipticcurvelist is empty, nothing else to do... */ if (nid == 0) nid = NID_X9_62_prime256v1; if (ctx) ctx->used_ecdh_nid = nid; if (ecdh_cache) { last_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ecdh_cache)); if (last_nid == nid) return ecdh_cache; EC_KEY_free(ecdh_cache); ecdh_cache = NULL; } ecdh_cache = EC_KEY_new_by_curve_name(nid); return ecdh_cache; } #endif long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff) { if (onoff) { SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); #ifdef USE_LIBSSL_OLD SSL_CTX_set_tmp_ecdh_callback(ctx, ecdh_auto_cb); #endif } return 1; } #endif void tls_compat_cleanup(void) { #ifdef DH_CLEANUP if (dh2048) { DH_free(dh2048); dh2048 = NULL; } if (dh4096) { DH_free(dh4096); dh4096 = NULL; } #endif #ifdef ECDH_CLEANUP if (ecdh_cache) { EC_KEY_free(ecdh_cache); ecdh_cache = NULL; } #endif } #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM /* * Load certs for public key from memory. */ int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *data, int data_len) { pem_password_cb *psw_fn = NULL; void *psw_arg = NULL; X509 *cert; BIO *bio = NULL; int ok; #ifdef USE_LIBSSL_OLD psw_fn = ctx->default_passwd_callback; psw_arg = ctx->default_passwd_callback_userdata; #endif ERR_clear_error(); /* Read from memory */ bio = BIO_new_mem_buf(data, data_len); if (!bio) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB); goto failed; } /* Load primary cert */ cert = PEM_read_bio_X509_AUX(bio, NULL, psw_fn, psw_arg); if (!cert) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB); goto failed; } /* Increments refcount */ ok = SSL_CTX_use_certificate(ctx, cert); X509_free(cert); if (!ok || ERR_peek_error()) goto failed; /* Load extra certs */ ok = SSL_CTX_clear_extra_chain_certs(ctx); while (ok) { cert = PEM_read_bio_X509(bio, NULL, psw_fn, psw_arg); if (!cert) { /* Is it EOF? */ unsigned long err = ERR_peek_last_error(); if (ERR_GET_LIB(err) != ERR_LIB_PEM) break; if (ERR_GET_REASON(err) != PEM_R_NO_START_LINE) break; /* On EOF do successful exit */ BIO_free(bio); ERR_clear_error(); return 1; } /* Does not increment refcount */ ok = SSL_CTX_add_extra_chain_cert(ctx, cert); if (!ok) X509_free(cert); } failed: if (bio) BIO_free(bio); return 0; } #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM /* * Load CA certs for verification from memory. */ int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *data, int data_len) { STACK_OF(X509_INFO) *stack = NULL; X509_STORE *store; X509_INFO *info; int nstack, i, ret = 0, got = 0; BIO *bio; /* Read from memory */ bio = BIO_new_mem_buf(data, data_len); if (!bio) goto failed; /* Parse X509_INFO records */ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); if (!stack) goto failed; /* Loop over stack, add certs and revocation records to store */ store = SSL_CTX_get_cert_store(ctx); nstack = sk_X509_INFO_num(stack); for (i = 0; i < nstack; i++) { info = sk_X509_INFO_value(stack, i); if (info->x509 && !X509_STORE_add_cert(store, info->x509)) goto failed; if (info->crl && !X509_STORE_add_crl(store, info->crl)) goto failed; if (info->x509 || info->crl) got = 1; } ret = got; failed: if (bio) BIO_free(bio); if (stack) sk_X509_INFO_pop_free(stack, X509_INFO_free); if (!ret) X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB); return ret; } #endif #ifndef HAVE_ASN1_TIME_PARSE static int parse2num(const char **str_p, int min, int max) { const char *s = *str_p; if (s && s[0] >= '0' && s[0] <= '9' && s[1] >= '0' && s[1] <= '9') { int val = (s[0] - '0') * 10 + (s[1] - '0'); if (val >= min && val <= max) { *str_p += 2; return val; } } *str_p = NULL; return 0; } int asn1_time_parse(const char *src, size_t len, struct tm *tm, int mode) { char buf[16]; const char *s = buf; int utctime; int year; memset(tm, 0, sizeof *tm); if (mode != 0) return -1; /* * gentime: YYYYMMDDHHMMSSZ * utctime: YYMMDDHHMM(SS)(Z) */ if (len == 15) { utctime = 0; } else if (len > 8 && len < 15) { utctime = 1; } else { return -1; } memcpy(buf, src, len); buf[len] = '\0'; year = parse2num(&s, 0, 99); if (utctime) { if (year < 50) year = 2000 + year; else year = 1900 + year; } else { year = year*100 + parse2num(&s, 0, 99); } tm->tm_year = year - 1900; tm->tm_mon = parse2num(&s, 1, 12) - 1; tm->tm_mday = parse2num(&s, 1, 31); tm->tm_hour = parse2num(&s, 0, 23); tm->tm_min = parse2num(&s, 0, 59); if (utctime) { if (s && s[0] != 'Z' && s[0] != '\0') tm->tm_sec = parse2num(&s, 0, 61); } else { tm->tm_sec = parse2num(&s, 0, 61); } if (s) { if (s[0] == '\0') goto good; if (s[0] == 'Z' && s[1] == '\0') goto good; } return -1; good: return utctime ? V_ASN1_UTCTIME : V_ASN1_GENERALIZEDTIME; } #endif /* HAVE_ASN1_TIME_PARSE */ int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst) { struct tm tm; int res; time_t tval; *dst = 0; if (!asn1time) return 0; if (asn1time->type != V_ASN1_GENERALIZEDTIME && asn1time->type != V_ASN1_UTCTIME) { tls_set_errorx(ctx, "Invalid time object type: %d", asn1time->type); return -1; } res = asn1_time_parse((char*)asn1time->data, asn1time->length, &tm, 0); if (res == -1) { tls_set_errorx(ctx, "Invalid asn1 time"); return -1; } tval = timegm(&tm); if (tval == (time_t)-1) { tls_set_error(ctx, "Cannot convert asn1 time"); return -1; } *dst = tval; return 0; } #else /* !USUAL_LIBSSL_FOR_TLS */ #include /* * Install empty functions when openssl is not available. */ int tls_init(void) { return -1; } void tls_deinit(void) { } const char *tls_backend_version(void) { return "unsupported"; } const char *tls_error(struct tls *_ctx) { return "No TLS support"; } struct tls_config *tls_config_new(void) { return NULL; } void tls_config_free(struct tls_config *_config) {} int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file) { return -1; } int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path) { return -1; } int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len) { return -1; } int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file) { return -1; } int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len) { return -1; } int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers) { return -1; } int tls_config_set_dheparams(struct tls_config *_config, const char *_params) { return -1; } int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name) { return -1; } int tls_config_set_key_file(struct tls_config *_config, const char *_key_file) { return -1; } int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len) { return -1; } int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file) { return -1; } int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len) { return -1; } void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols) {} void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth) {} void tls_config_prefer_ciphers_client(struct tls_config *_config) {} void tls_config_prefer_ciphers_server(struct tls_config *_config) {} void tls_config_insecure_noverifycert(struct tls_config *_config) {} void tls_config_insecure_noverifyname(struct tls_config *_config) {} void tls_config_insecure_noverifytime(struct tls_config *_config) {} void tls_config_verify(struct tls_config *_config) {} void tls_config_verify_client(struct tls_config *_config) {} void tls_config_verify_client_optional(struct tls_config *_config) {} void tls_config_clear_keys(struct tls_config *_config) {} int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr) { return -1; } struct tls *tls_client(void) { return NULL; } struct tls *tls_server(void) { return NULL; } int tls_configure(struct tls *_ctx, struct tls_config *_config) { return -1; } void tls_reset(struct tls *_ctx) {} void usual_tls_free(struct tls *_ctx) {} int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write) { return -1; } int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket) { return -1; } int tls_connect(struct tls *_ctx, const char *_host, const char *_port) { return -1; } int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername) { return -1; } int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername) { return -1; } int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername) { return -1; } int tls_handshake(struct tls *_ctx) { return -1; } ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen) { return -1; } ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen) { return -1; } int tls_close(struct tls *_ctx) { return -1; } int tls_peer_cert_provided(struct tls *ctx) { return 0; } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { return 0; } const char *tls_peer_cert_hash(struct tls *_ctx) { return NULL; } const char *tls_peer_cert_issuer(struct tls *ctx) { return NULL; } const char *tls_peer_cert_subject(struct tls *ctx) { return NULL; } time_t tls_peer_cert_notbefore(struct tls *ctx) { return (time_t)-1; } time_t tls_peer_cert_notafter(struct tls *ctx) { return (time_t)-1; } const char *tls_conn_version(struct tls *ctx) { return "n/a"; } const char *tls_conn_cipher(struct tls *ctx) { return "n/a"; } uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password) { return NULL; } ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { return -1; } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { return -1; } int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client) { return -1; } int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { return -1; } int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { return -1; } int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size) { return -1; } int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *algo) { *cert_p = NULL; return -1; } void tls_cert_free(struct tls_cert *cert) {} #endif /* !USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls.h0000644000175000000000000001571714777762223014177 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_HEADER_TLS_H_ #define _USUAL_HEADER_TLS_H_ #ifdef __cplusplus extern "C" { #endif #include #define TLS_API 20141031 #define TLS_PROTOCOL_TLSv1_0 (1 << 1) #define TLS_PROTOCOL_TLSv1_1 (1 << 2) #define TLS_PROTOCOL_TLSv1_2 (1 << 3) #define TLS_PROTOCOL_TLSv1_3 (1 << 4) #define TLS_PROTOCOL_TLSv1 \ (TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3) #define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1 #define TLS_PROTOCOLS_DEFAULT (TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3) #define TLS_WANT_POLLIN -2 #define TLS_WANT_POLLOUT -3 #define TLS_NO_OCSP -4 #define TLS_NO_CERT -5 #define TLS_OCSP_RESPONSE_SUCCESSFUL 0 #define TLS_OCSP_RESPONSE_MALFORMED 1 #define TLS_OCSP_RESPONSE_INTERNALERR 2 #define TLS_OCSP_RESPONSE_TRYLATER 3 #define TLS_OCSP_RESPONSE_SIGREQUIRED 5 #define TLS_OCSP_RESPONSE_UNAUTHORIZED 6 #define TLS_OCSP_CERT_GOOD 0 #define TLS_OCSP_CERT_REVOKED 1 #define TLS_OCSP_CERT_UNKNOWN 2 #define TLS_CRL_REASON_UNPSECIFIED 0 #define TLS_CRL_REASON_KEY_COMPROMISE 1 #define TLS_CRL_REASON_CA_COMPROMISE 2 #define TLS_CRL_REASON_AFFILIATION_CHANGED 3 #define TLS_CRL_REASON_SUPERSEDED 4 #define TLS_CRL_REASON_CESSATION_OF_OPERATION 5 #define TLS_CRL_REASON_CERTIFICATE_HOLD 6 #define TLS_CRL_REASON_REMOVE_FROM_CRL 8 #define TLS_CRL_REASON_PRIVILEGE_WITH_DRAWN 9 #define TLS_CRL_REASON_AA_COMPROMISE 10 struct tls; struct tls_config; int tls_init(void); void tls_deinit(void); const char *tls_backend_version(void); const char *tls_config_error(struct tls_config *_config); const char *tls_error(struct tls *_ctx); struct tls_config *tls_config_new(void); void tls_config_free(struct tls_config *_config); int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file); int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path); int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len); int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file); int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len); int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers); int tls_config_set_dheparams(struct tls_config *_config, const char *_params); int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name); int tls_config_set_key_file(struct tls_config *_config, const char *_key_file); int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len); int tls_config_set_keypair_file(struct tls_config *_config, const char *_cert_file, const char *_key_file); int tls_config_set_keypair_mem(struct tls_config *_config, const uint8_t *_cert, size_t _cert_len, const uint8_t *_key, size_t _key_len); int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file); int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len); void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols); void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth); void tls_config_prefer_ciphers_client(struct tls_config *_config); void tls_config_prefer_ciphers_server(struct tls_config *_config); void tls_config_insecure_noverifycert(struct tls_config *_config); void tls_config_insecure_noverifyname(struct tls_config *_config); void tls_config_insecure_noverifytime(struct tls_config *_config); void tls_config_verify(struct tls_config *_config); void tls_config_verify_client(struct tls_config *_config); void tls_config_verify_client_optional(struct tls_config *_config); void tls_config_clear_keys(struct tls_config *_config); int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr); struct tls *tls_client(void); struct tls *tls_server(void); int tls_configure(struct tls *_ctx, struct tls_config *_config); void tls_reset(struct tls *_ctx); void usual_tls_free(struct tls *_ctx); int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write); int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket); int tls_connect(struct tls *_ctx, const char *_host, const char *_port); int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername); int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername); int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); int tls_handshake(struct tls *_ctx); ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen); ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen); int tls_close(struct tls *_ctx); int tls_peer_cert_provided(struct tls *_ctx); int tls_peer_cert_contains_name(struct tls *_ctx, const char *_name); const char *tls_peer_cert_hash(struct tls *_ctx); const char *tls_peer_cert_issuer(struct tls *_ctx); const char *tls_peer_cert_subject(struct tls *_ctx); time_t tls_peer_cert_notbefore(struct tls *_ctx); time_t tls_peer_cert_notafter(struct tls *_ctx); const char *tls_conn_version(struct tls *_ctx); const char *tls_conn_cipher(struct tls *_ctx); uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen); int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config); int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client); int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text); int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size); int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size); int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size); bool tls_config_equal(struct tls_config *server_connect_conf_left, struct tls_config *server_connect_conf_right); #ifdef __cplusplus } #endif #endif /* HEADER_TLS_H */ pgbouncer-1.24.1/lib/usual/tls/tls_util.c0000644000175000000000000001267314777762223015225 00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2014 Joel Sing * Copyright (c) 2015 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" const char * tls_backend_version(void) { return OpenSSL_version(OPENSSL_VERSION); } /* * Extract the host and port from a colon separated value. For a literal IPv6 * address the address must be contained with square braces. If a host and * port are successfully extracted, the function will return 0 and the * caller is responsible for freeing the host and port. If no port is found * then the function will return 1, with both host and port being NULL. * On memory allocation failure -1 will be returned. */ int tls_host_port(const char *hostport, char **host, char **port) { char *h, *p, *s; int rv = 1; *host = NULL; *port = NULL; if ((s = strdup(hostport)) == NULL) goto fail; h = p = s; /* See if this is an IPv6 literal with square braces. */ if (p[0] == '[') { h++; if ((p = strchr(s, ']')) == NULL) goto done; *p++ = '\0'; } /* Find the port seperator. */ if ((p = strchr(p, ':')) == NULL) goto done; /* If there is another separator then we have issues. */ if (strchr(p + 1, ':') != NULL) goto done; *p++ = '\0'; if (asprintf(host, "%s", h) == -1) goto fail; if (asprintf(port, "%s", p) == -1) goto fail; rv = 0; goto done; fail: free(*host); *host = NULL; free(*port); *port = NULL; rv = -1; done: free(s); return (rv); } static int tls_password_cb(char *buf, int size, int rwflag, void *u) { size_t len; if (u == NULL) { memset(buf, 0, size); return (0); } if ((len = strlcpy(buf, u, size)) >= (size_t)size) return (0); return (len); } uint8_t * tls_load_file(const char *name, size_t *len, char *password) { FILE *fp; EVP_PKEY *key = NULL; BIO *bio = NULL; uint8_t *buf = NULL; char *data; struct stat st; size_t size; int fd = -1; *len = 0; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); /* Just load the file into memory without decryption */ if (password == NULL) { if (fstat(fd, &st) != 0) goto fail; size = (size_t)st.st_size; if ((buf = calloc(1, size + 1)) == NULL) goto fail; if (read(fd, buf, size) != (ssize_t)size) goto fail; close(fd); goto done; } /* Or read the (possibly) encrypted key from file */ if ((fp = fdopen(fd, "r")) == NULL) goto fail; fd = -1; key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password); fclose(fp); if (key == NULL) goto fail; /* Write unencrypted key to memory buffer */ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto fail; if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) goto fail; if ((size = BIO_get_mem_data(bio, &data)) <= 0) goto fail; if ((buf = calloc(1, size)) == NULL) goto fail; memcpy(buf, data, size); BIO_free_all(bio); EVP_PKEY_free(key); done: *len = size; return (buf); fail: free(buf); if (fd != -1) close(fd); if (bio != NULL) BIO_free_all(bio); if (key != NULL) EVP_PKEY_free(key); return (NULL); } ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { SSL *conn = ctx->ssl_conn; const char *ocsp_pfx = "", *ocsp_info = ""; const char *proto = "-", *cipher = "-"; char dh[64]; int used_dh_bits = ctx->used_dh_bits, used_ecdh_nid = ctx->used_ecdh_nid; const SSL_CIPHER *ciph_obj = NULL; dh[0] = 0; if (conn != NULL) { proto = SSL_get_version(conn); cipher = SSL_get_cipher(conn); ciph_obj = SSL_get_current_cipher(conn); #ifdef SSL_get_server_tmp_key if (ctx->flags & TLS_CLIENT) { EVP_PKEY *pk = NULL; int ok = SSL_get_server_tmp_key(conn, &pk); if (ok) { int pk_type = EVP_PKEY_id(pk); if (pk_type == EVP_PKEY_DH) { const DH *dh = EVP_PKEY_get0_DH(pk); used_dh_bits = DH_size(dh) * 8; } else if (pk_type == EVP_PKEY_EC) { const EC_KEY *ecdh = EVP_PKEY_get0_EC_KEY(pk); const EC_GROUP *eg = EC_KEY_get0_group(ecdh); used_ecdh_nid = EC_GROUP_get_curve_name(eg); } EVP_PKEY_free(pk); } } else #endif if (ciph_obj && !used_ecdh_nid && !used_dh_bits) { #ifdef SSL_get_shared_curve int kx = SSL_CIPHER_get_kx_nid(ciph_obj); if (kx == NID_kx_ecdhe) { used_ecdh_nid = SSL_get_shared_curve(conn, 0); } else if (kx == NID_kx_dhe) { snprintf(dh, sizeof dh, "/DH=?"); } #endif } } if (used_dh_bits) { snprintf(dh, sizeof dh, "/DH=%d", used_dh_bits); } else if (used_ecdh_nid) { snprintf(dh, sizeof dh, "/ECDH=%s", OBJ_nid2sn(used_ecdh_nid)); } if (ctx->ocsp_result) { ocsp_info = ctx->ocsp_result; ocsp_pfx = "/OCSP="; } return snprintf(buf, buflen, "%s/%s%s%s%s", proto, cipher, dh, ocsp_pfx, ocsp_info); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.24.1/lib/usual/tls/tls_compat.h0000644000175000000000000000656714777762223015545 00000000000000 #ifndef _USUAL_TLS_COMPAT_H_ #define _USUAL_TLS_COMPAT_H_ #include #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include #include /* OpenSSL 1.1+ has hidden struct fields */ #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) /* * USE_LIBSSL_OLD - old openssl 1.0 api * USE_LIBSSL_INTERNALS - can access old openssl 1.0 structs * otherwise - new openssl 1.1 api */ /* libressl has old api but unstable structs */ #define USE_LIBSSL_OLD #ifndef LIBRESSL_VERSION_NUMBER #define USE_LIBSSL_INTERNALS #endif #define NID_kx_ecdhe (-90) #define NID_kx_dhe (-91) #define SSL_CIPHER_get_kx_nid(ciph) ( 0 ) #define X509_get_key_usage(x509) ((x509)->ex_kusage) #define X509_get_extended_key_usage(x509) ((x509)->ex_xkusage) #define SSL_CTX_get0_param(ssl_ctx) ((ssl_ctx)->param) #define ASN1_STRING_get0_data(x) ((const unsigned char*)ASN1_STRING_data(x)) #define X509_OBJECT_get0_X509(x) ((x)->data.x509) #ifndef OPENSSL_VERSION #define OPENSSL_VERSION SSLEAY_VERSION #define OpenSSL_version(x) SSLeay_version(x) #endif static inline X509_OBJECT *X509_OBJECT_new(void) { X509_OBJECT *obj = OPENSSL_malloc(sizeof(*obj)); if (obj) { memset(obj, 0, sizeof(*obj)); } else { X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); } return obj; } static inline void X509_OBJECT_free(X509_OBJECT *obj) { if (obj) { if (obj->type == X509_LU_X509) { X509_free(obj->data.x509); } else if (obj->type == X509_LU_CRL) { X509_CRL_free(obj->data.crl); } OPENSSL_free(obj); } } static inline X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *ctx, int lookup, X509_NAME *name) { X509_OBJECT *obj = X509_OBJECT_new(); if (obj) { if (X509_STORE_get_by_subject(ctx, lookup, name, obj)) { return obj; } X509_OBJECT_free(obj); } return NULL; } /* * We need these specific functions for OpenSSL 3.0.0 because the * generic function no longer works. But the new ones only exist in * 1.1.0, so in older versions we still use the older one. */ #define EVP_PKEY_get0_DH(pkey) EVP_PKEY_get0(pkey) #define EVP_PKEY_get0_EC_KEY(pkey) EVP_PKEY_get0(pkey) #endif /* OpenSSL <1.1 */ /* ecdh_auto is broken - ignores main EC key */ #undef SSL_CTX_set_ecdh_auto /* dh_auto seems fine, but use ours to get DH info */ #undef SSL_CTX_set_dh_auto #ifndef SSL_CTX_set_dh_auto long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff); #endif #ifndef SSL_CTX_set_ecdh_auto long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff); #endif #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len); #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *buf, int len); #endif /* BoringSSL has no OCSP support */ #ifdef OPENSSL_IS_BORINGSSL #define SSL_CTX_set_tlsext_status_cb(a,b) (1) #define SSL_set_tlsext_status_type(a,b) (1) #endif /* AWS-LC does not currently have OCSP support */ #if defined(OPENSSL_IS_AWSLC) && defined(OPENSSL_NO_OCSP) #define SSL_CTX_set_tlsext_status_cb(a,b) (1) #define SSL_set_tlsext_status_type(a,b) (1) #endif void tls_compat_cleanup(void); #ifndef SSL_OP_NO_TLSv1_3 #define SSL_OP_NO_TLSv1_3 0 #endif #ifndef TLS1_3_VERSION #define TLS1_3_VERSION 0x0304 #endif #endif /* USUAL_LIBSSL_FOR_TLS */ #endif /* _USUAL_TLS_COMPAT_H_ */ pgbouncer-1.24.1/lib/usual/tls/tls_cert.h0000644000175000000000000000477414777762223015215 00000000000000#ifndef _USUAL_TLS_TLS_CERT_H_ #define _USUAL_TLS_TLS_CERT_H_ #define TLS_CERT_GNAME_DNS 1 #define TLS_CERT_GNAME_IPv4 2 #define TLS_CERT_GNAME_IPv6 3 #define TLS_CERT_GNAME_EMAIL 4 #define TLS_CERT_GNAME_URI 5 #define TLS_KU_DIGITAL_SIGNATURE (1 << 0) #define TLS_KU_NON_REPUDIATION (1 << 1) #define TLS_KU_KEY_ENCIPHERMENT (1 << 2) #define TLS_KU_DATA_ENCIPHERMENT (1 << 3) #define TLS_KU_KEY_AGREEMENT (1 << 4) #define TLS_KU_KEY_CERT_SIGN (1 << 5) #define TLS_KU_CRL_SIGN (1 << 6) #define TLS_KU_ENCIPHER_ONLY (1 << 7) #define TLS_KU_DECIPHER_ONLY (1 << 8) #define TLS_XKU_SSL_SERVER (1 << 0) #define TLS_XKU_SSL_CLIENT (1 << 1) #define TLS_XKU_SMIME (1 << 2) #define TLS_XKU_CODE_SIGN (1 << 3) #define TLS_XKU_OCSP_SIGN (1 << 4) #define TLS_XKU_SGC (1 << 5) #define TLS_XKU_TIMESTAMP (1 << 6) #define TLS_XKU_DVCS (1 << 7) #define TLS_EXT_BASIC (1 << 0) #define TLS_EXT_KEY_USAGE (1 << 1) #define TLS_EXT_EXTENDED_KEY_USAGE (1 << 2) #define TLS_EXT_SUBJECT_ALT_NAME (1 << 3) /* * GeneralName */ struct tls_cert_general_name { const void *name_value; int name_type; }; /* * DistinguishedName */ struct tls_cert_dname { const char *common_name; const char *country_name; const char *state_or_province_name; const char *locality_name; const char *street_address; const char *organization_name; const char *organizational_unit_name; }; struct tls_cert { /* Version number from cert: 0:v1, 1:v2, 2:v3 */ int version; /* did it pass verify? useful when noverifycert is on. */ int successful_verify; /* DistringuishedName for subject */ struct tls_cert_dname subject; /* DistringuishedName for issuer */ struct tls_cert_dname issuer; /* decimal number */ const char *serial; /* Validity times */ time_t not_before; time_t not_after; uint32_t ext_set; uint32_t ext_crit; /* BasicConstraints extension */ int basic_constraints_ca; int basic_constraints_pathlen; /* KeyUsage extension */ uint32_t key_usage_flags; /* ExtendedKeyUsage extension */ uint32_t extended_key_usage_flags; /* SubjectAltName extension */ struct tls_cert_general_name *subject_alt_names; int subject_alt_name_count; /* Fingerprint as raw hash */ const unsigned char *fingerprint; size_t fingerprint_size; }; int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo); void tls_cert_free(struct tls_cert *cert); #ifdef TLS_CERT_INTERNAL_FUNCS int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509); #endif #endif pgbouncer-1.24.1/lib/usual/fnmatch.h0000644000175000000000000000330514777762223014201 00000000000000/* * fnmatch.h * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file * Theme include for strings. */ #ifndef _USUAL_FNMATCH_H_ #define _USUAL_FNMATCH_H_ #include #if defined(HAVE_FNMATCH_H) && defined(FNM_CASEFOLD) #include #else /* fnmatch missing or incomplete */ #define NEED_USUAL_FNMATCH #endif #ifdef NEED_USUAL_FNMATCH #define fnmatch(p,s,f) usual_fnmatch(p,s,f) /** Do not allow wildcard to match '/' */ #define FNM_PATHNAME 1 /** Treat '\\' as literal value */ #define FNM_NOESCAPE 2 /** Do not allow wildcard to match leading '.' */ #define FNM_PERIOD 4 /** (GNU) Match case-insensitively */ #define FNM_CASEFOLD 8 /** (GNU) Match leading directory in path */ #define FNM_LEADING_DIR 16 /* (GNU) random alias */ #define FNM_FILE_NAME FNM_PATHNAME /** Returned on no match */ #define FNM_NOMATCH 1 /** * Compat: fnmatch() */ int fnmatch(const char *pat, const char *str, int flags); #endif /* NEED_USUAL_FNMATCH */ #endif /* !_USUAL_FNMATCH_H_ */ pgbouncer-1.24.1/lib/usual/utf8.c0000644000175000000000000001162614777762223013447 00000000000000/* * Low-level UTF8 handling. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define u8head(c, mask) (((c) & (mask | (mask >> 1))) == mask) #define u8tail(c) u8head(c, 0x80) /* * conservative utf8 decoder * * if invalid char, advance src pointer by one and return * negative byte value. this can be ignored or replaced. */ int utf8_get_char(const char **src_p, const char *_srcend) { uint32_t c; const uint8_t *srcend = (uint8_t *)_srcend; const uint8_t *p = (uint8_t *)(*src_p); /* * 0xxx xxxx -> len=1 * 10xx xxxx -> tail byte * 110x xxxx -> len=2 * 1110 xxxx -> len=3 * 1111 0xxx -> len=4 */ if (p[0] < 0x80) { c = *p++; } else if (u8head(p[0], 0xC0)) { if (p + 2 > srcend) goto eos; if (!u8tail(p[1])) goto bad_enc; c = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); if (c < 0x80) goto bad_enc; p += 2; } else if (u8head(p[0], 0xE0)) { if (p + 3 > srcend) goto eos; if (!u8tail(p[1]) || !u8tail(p[2])) goto bad_enc; c = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); if ((c < 0x800) || ((c & 0xF800) == 0xD800)) goto bad_enc; p += 3; } else if (u8head(p[0], 0xF0)) { if (p + 4 > srcend) goto eos; if (!u8tail(p[1]) || !u8tail(p[2]) || !u8tail(p[3])) goto bad_enc; c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); if (c < 0x10000 || c > 0x10FFFF) goto bad_enc; p += 4; } else { goto bad_enc; } *src_p = (char *)p; return c; bad_enc: eos: c = p[0]; *src_p = (char *)p + 1; return -(int)c; } /* encode one char - skip invalid ones */ bool utf8_put_char(unsigned int c, char **dst_p, const char *dstend) { char *dst = *dst_p; if (c < 0x80) { if (dst + 1 > dstend) goto no_room; *dst++ = c; } else if (c < 0x800) { if (dst + 2 > dstend) goto no_room; *dst++ = 0xC0 | (c >> 6); *dst++ = 0x80 | (c & 0x3F); } else if (c < 0x10000) { if (dst + 3 > dstend) goto no_room; if (c < 0xD800 || c > 0xDFFF) { *dst++ = 0xE0 | (c >> 12); *dst++ = 0x80 | ((c >> 6) & 0x3F); *dst++ = 0x80 | (c & 0x3F); } } else if (c <= 0x10FFFF) { if (dst + 4 > dstend) goto no_room; *dst++ = 0xF0 | (c >> 18); *dst++ = 0x80 | ((c >> 12) & 0x3F); *dst++ = 0x80 | ((c >> 6) & 0x3F); *dst++ = 0x80 | (c & 0x3F); } *dst_p = dst; return true; no_room: return false; } int utf8_char_size(unsigned int c) { if (c < 0x80) return 1; if (c < 0x800) return 2; if (c < 0x10000) return 3; return 4; } int utf8_seq_size(unsigned char b) { if (b < 0x80) return 1; if (b < 0xC2) return 0; if (b < 0xE0) return 2; if (b < 0xF0) return 3; if (b < 0xF5) return 4; return 0; } /* * 7f: c1bf (+1) * 80: c280 * 7ff: dfbf * 7ff: e09fbf (+1) * 800: e0a080 * ffff: efbfbf * ffff: f08fbfbf (+1) * 10000: f0908080 * 10ffff: f48fbfbf */ int utf8_validate_seq(const char *src, const char *srcend) { const unsigned char *u = (unsigned char *)src; const unsigned char *uend = (unsigned char *)srcend; if (u[0] < 0x80) { /* ascii */ if (u[0] == 0) goto invalid; return 1; } else if (u[0] < 0xC2) { /* tail byte as first byte */ goto invalid; } else if (u[0] < 0xE0) { /* 1 tail byte */ if (u + 2 > uend) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; return 2; } else if (u[0] < 0xF0) { /* 2 tail bytes */ if (u + 3 > uend) goto invalid; if (u[0] == 0xE0 && u[1] < 0xA0) goto invalid; if (u[0] == 0xED && u[1] >= 0xA0) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; if ((u[2] & 0xC0) != 0x80) goto invalid; return 3; } else if (u[0] < 0xF5) { /* 3-tail bytes */ if (u + 4 > uend) goto invalid; if (u[0] == 0xF0 && u[1] < 0x90) goto invalid; if (u[0] == 0xF4 && u[1] > 0x8F) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; if ((u[2] & 0xC0) != 0x80) goto invalid; if ((u[3] & 0xC0) != 0x80) goto invalid; return 4; } invalid: return 0; } bool utf8_validate_string(const char *src, const char *end) { unsigned int n; while (src < end) { if (*src & 0x80) { n = utf8_validate_seq(src, end); if (n == 0) return false; src += n; } else if (*src == '\0') { return false; } else { src++; } } return true; } pgbouncer-1.24.1/lib/usual/cxextra.h0000644000175000000000000000326514777762223014244 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Extra allocators for cxalloc. */ #ifndef _USUAL_CXEXTRA_H_ #define _USUAL_CXEXTRA_H_ #include /** Allocator that exits on error. .ctx should be pointer to actual allocator */ extern const struct CxOps cx_nofail_ops; /** nofail for libc */ extern CxMem cx_libc_nofail; /** * Creates allocator that pools all memory together, * without keeping track of single objects, to be * freed all together in one shot. * * realloc(), free() are partially supported for the last * objec only. */ CxMem *cx_new_pool(CxMem *parent, size_t initial_size, unsigned int align); CxMem *cx_new_pool_from_area(CxMem *parent, void *buf, size_t size, bool allow_free, unsigned int align); /** * Creates allocator that remebers all allocations done * under it and allows all of it to be freed together. * * Supports hierarchical trees. */ CxMem *cx_new_tree(CxMem *parent); #endif pgbouncer-1.24.1/lib/usual/safeio.h0000644000175000000000000000324414777762223014031 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * EINTR-safe I/O functions. */ #ifndef _USUAL_SAFEIO_H_ #define _USUAL_SAFEIO_H_ #include /** read */ ssize_t safe_read(int fd, void *buf, size_t len) _MUSTCHECK; /** write */ ssize_t safe_write(int fd, const void *buf, size_t len) _MUSTCHECK; /** recv */ ssize_t safe_recv(int fd, void *buf, size_t len, int flags) _MUSTCHECK; /** send */ ssize_t safe_send(int fd, const void *buf, size_t len, int flags) _MUSTCHECK; /** close */ int safe_close(int fd); /** recvmsg */ ssize_t safe_recvmsg(int fd, struct msghdr *msg, int flags) _MUSTCHECK; /** sendmsg */ ssize_t safe_sendmsg(int fd, const struct msghdr *msg, int flags) _MUSTCHECK; /** connect */ int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) _MUSTCHECK; /** accept */ int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len) _MUSTCHECK; #endif pgbouncer-1.24.1/lib/usual/string.c0000644000175000000000000003131214777762223014061 00000000000000/* * String utilities. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #ifdef HAVE_XLOCALE_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #include #include #include #include /* * Dynamic list of strings. */ struct StrList { struct StatList list; CxMem *ca; }; struct StrItem { struct List node; char *str; }; bool strlist_empty(struct StrList *slist) { return statlist_empty(&slist->list); } bool strlist_append(struct StrList *slist, const char *str) { char *nstr = NULL; bool ok; if (str) { nstr = cx_strdup(slist->ca, str); if (!nstr) return false; } ok = strlist_append_ref(slist, nstr); if (!ok) cx_free(slist->ca, nstr); return ok; } bool strlist_append_ref(struct StrList *slist, char *str) { struct StrItem *item = cx_alloc(slist->ca, sizeof(*item)); if (!item) return false; list_init(&item->node); item->str = str; statlist_append(&slist->list, &item->node); return true; } char *strlist_pop(struct StrList *slist) { struct StrItem *item; struct List *el; char *str; el = statlist_pop(&slist->list); if (!el) return NULL; item = container_of(el, struct StrItem, node); str = item->str; cx_free(slist->ca, item); return str; } struct StrList *strlist_new(CxMem *ca) { struct StrList *slist = cx_alloc0(ca, sizeof(*slist)); if (!slist) return NULL; statlist_init(&slist->list, "strlist"); slist->ca = ca; return slist; } void strlist_free(struct StrList *slist) { char *s; if (!slist) return; while (!strlist_empty(slist)) { s = strlist_pop(slist); if (s) cx_free(slist->ca, s); } cx_free(slist->ca, slist); } bool strlist_foreach(const struct StrList *slist, str_cb func, void *arg) { struct List *el; struct StrItem *item; statlist_for_each(el, &slist->list) { item = container_of(el, struct StrItem, node); if (!func(arg, item->str)) return false; } return true; } /* * Parse comma separated words. */ static inline const char *skip_ws(const char *p) { while (*p && isspace(*p)) p++; return p; } bool parse_word_list(const char *s, str_cb cb_func, void *cb_arg) { struct MBuf buf; const char *p = s; const char *start, *end; mbuf_init_dynamic(&buf); while (*p) { /* parse word */ p = skip_ws(p); start = p; while (*p && *p != ',') p++; end = p; while (end > start && isspace(*(end - 1))) end--; /* parse comma */ if (*p) { if (*p == ',') { p++; } else { goto failed_syntax; } } /* extract string */ if (!mbuf_write(&buf, start, end - start)) goto failed; if (!mbuf_write_byte(&buf, 0)) goto failed; /* launch callback */ if (!cb_func(cb_arg, (const char *)buf.data)) goto failed; /* reset */ mbuf_rewind_writer(&buf); } mbuf_free(&buf); return true; failed_syntax: errno = EINVAL; failed: mbuf_free(&buf); return false; } /* * Minimal spec-conforming implementations of strlcpy(), strlcat(). */ #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t n) { size_t len = strlen(src); if (len < n) { memcpy(dst, src, len + 1); } else if (n > 0) { memcpy(dst, src, n - 1); dst[n - 1] = 0; } return len; } #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t n) { size_t pos = 0; while (pos < n && dst[pos]) pos++; return pos + strlcpy(dst + pos, src, n - pos); } #endif char *strpcpy(char *dst, const char *src, size_t n) { if (n == 0) return NULL; for (; n > 0; n--, dst++, src++) { if ((*dst = *src) == '\0') return dst; } dst[-1] = '\0'; return NULL; } char *strpcat(char *dst, const char *src, size_t n) { size_t dstlen = strnlen(dst, n); if (dstlen < n) return strpcpy(dst + dstlen, src, n - dstlen); return NULL; } #ifndef HAVE_MEMPCPY void *mempcpy(void *dst, const void *src, size_t n) { memcpy(dst, src, n); return (char *)(dst) + n; } #endif #ifndef HAVE_MEMRCHR void *memrchr(const void *s, int c, size_t n) { const uint8_t *p = s; while (n--) { if (p[n] == c) return (void *)(p + n); } return NULL; } #endif #ifndef HAVE_MEMMEM void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen) { const uint8_t *s = haystack; const uint8_t *q = needle; const uint8_t *s2; size_t i; if (nlen == 0) return (void *)haystack; if (nlen > hlen) return NULL; s2 = memchr(haystack, *q, hlen); if (!s2 || nlen == 1) return (void *)s2; for (i = s2 - s; i <= hlen - nlen; i++) { if (s[i] == q[0] && s[i+1] == q[1]) { if (memcmp(s + i + 2, q + 2, nlen - 2) == 0) return (void *)(s + i); } } return NULL; } #endif #ifndef HAVE_EXPLICIT_BZERO #if defined(_WIN32) && defined(SecureZeroMemory) void explicit_bzero(void *buf, size_t len) { SecureZeroMemory(buf, len); } #elif defined(HAVE_MEMSET_S) void explicit_bzero(void *buf, size_t len) { memset_s(buf, len, 0, len); } #else /* non-win32 */ /* avoid link-time optimization */ #if defined(__GNUC__x) || __has_attribute(weak) void __explicit_bzero_hack(void *, size_t); __attribute__((weak)) void __explicit_bzero_hack(void *buf, size_t len) { } #else typedef void (*__explicit_bzero_cb_t)(void *, size_t); static void __explicit_bzero_hack_cb(void *buf, size_t len) { } static volatile __explicit_bzero_cb_t __explicit_bzero_hack = __explicit_bzero_hack_cb; #endif void explicit_bzero(void *buf, size_t len) { memset(buf, 0, len); __explicit_bzero_hack(buf, len); } #endif #endif /* !_WIN32 */ #ifndef HAVE_BASENAME const char *basename(const char *path) { const char *p, *p2; static char buf[256]; unsigned len; if (path == NULL || path[0] == 0) return memcpy(buf, ".", 2); if ((p = strrchr(path, '/')) == NULL) return path; if (p[1]) return p + 1; /* last char is '/' */ for (p2 = p; p2 > path; p2--) { if (p2[-1] != '/') { len = p2 - path; if (len > sizeof(buf) - 1) len = sizeof(buf) - 1; memcpy(buf, p2 - len, len); buf[len] = 0; return basename(buf); } } /* path contains only '/' chars */ return p; } #endif #ifndef HAVE_DIRNAME const char *dirname(const char *path) { const char *p; size_t len; static char buf[1024]; if (path == NULL || path[0] == 0) return memcpy(buf, ".", 2); /* ignore tailing '/' */ len = strlen(path); while (len && path[len - 1] == '/') len--; if (!len) return memcpy(buf, "/", 2); /* find end of dirname, strip '/' */ if ((p = memrchr(path, '/', len)) == NULL) return memcpy(buf, ".", 2); len = p - path; while (len && path[len - 1] == '/') len--; if (!len) return memcpy(buf, "/", 2); /* return it */ if (len > sizeof(buf) - 1) { errno = ENAMETOOLONG; return NULL; } memcpy(buf, path, len); buf[len] = 0; return buf; } #endif #ifdef WIN32 const char *win32_strerror(int e) { static char buf[1024]; return strerror_r(e, buf, sizeof(buf)); } #endif /* restore original strerror_r() */ #undef strerror_r const char *usual_strerror_r(int e, char *dst, size_t dstlen) { #ifdef WIN32 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), dst, dstlen, NULL); #else /* !WIN32 */ #ifdef STRERROR_R_CHAR_P dst = strerror_r(e, dst, dstlen); #else if (strerror_r(e, dst, dstlen) != 0) strlcpy(dst, "ERR", dstlen); #endif #endif /* !WIN32 */ return dst; } void *mempbrk(const void *data, size_t dlen, const void *find, size_t flen) { const uint8_t *s = data; const uint8_t *fb = find; size_t i; struct Bitmap256 bmap; if (flen == 0) return NULL; if (flen == 1) return memchr(data, fb[0], dlen); bitmap256_init(&bmap); for (i = 0; i < flen; i++) bitmap256_set(&bmap, fb[i]); for (i = 0; i < dlen; i++) { if (bitmap256_is_set(&bmap, s[i])) return (void *)(s + i); } return NULL; } size_t memspn(const void *data, size_t dlen, const void *accept, size_t alen) { const uint8_t *s = data; const uint8_t *fb = accept; size_t i; struct Bitmap256 bmap; if (alen == 0) return 0; if (alen == 1) { for (i = 0; i < dlen; i++) if (s[i] != fb[0]) break; return i; } bitmap256_init(&bmap); for (i = 0; i < alen; i++) bitmap256_set(&bmap, fb[i]); for (i = 0; i < dlen; i++) { if (!bitmap256_is_set(&bmap, s[i])) break; } return i; } size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen) { const void *p; p = mempbrk(data, dlen, reject, rlen); if (p != NULL) return (char *)p - (char *)data; return dlen; } double strtod_dot(const char *s, char **tokend) { const char *dp; char buf[128]; char *dst, *tmp, *end, *dot = NULL; unsigned int i, dplen; double res; /* check if locale is sane */ #ifdef HAVE_NL_LANGINFO dp = nl_langinfo(RADIXCHAR); #else dp = localeconv()->decimal_point; #endif if (memcmp(dp, ".", 2) == 0) return strtod(s, tokend); /* try to use proper api */ #ifdef HAVE_STRTOD_L { static locale_t c_locale = NULL; if (!c_locale) c_locale = newlocale(LC_ALL_MASK, "C", NULL); if (c_locale) return strtod_l(s, tokend, c_locale); } #endif while (*s && isspace(*s)) s++; dot = NULL; dst = buf; end = buf + sizeof(buf) - 5; dplen = dp[1] ? strlen(dp) : 1; for (i = 0; s[i]; i++) { if (s[i] >= '0' && s[i] <= '9') { *dst++ = s[i]; } else if (s[i] == '.') { dot = dst; memcpy(dst, dp, dplen); dst += dplen; } else if (s[i] == '-' || s[i] == '+' || s[i] == 'e' || s[i] == 'E') { *dst++ = s[i]; } else { break; } if (dst >= end) { errno = ERANGE; return 0; } } *dst = '\0'; if (!dot) return strtod(s, tokend); tmp = NULL; res = strtod(buf, &tmp); if (tmp && tokend) { *tokend = (char *)s + (tmp - buf); if (dot && tmp > dot && dplen > 1) *tokend -= (dplen - 1); } return res; } ssize_t dtostr_dot(char *buf, size_t buflen, double val) { const char *dp; ssize_t len, dplen; char *p; /* render with max precision */ len = snprintf(buf, buflen, "%.17g", val); if (len >= (ssize_t)buflen || len < 0) return len; /* check if locale is sane */ #ifdef HAVE_NL_LANGINFO dp = nl_langinfo(RADIXCHAR); #else dp = localeconv()->decimal_point; #endif if (memcmp(dp, ".", 2) == 0) return len; dplen = dp[1] ? strlen(dp) : 1; p = memchr(buf, dp[0], len); if (p) { *p = '.'; if (dp[1]) { memmove(p + 1, p + dplen, strlen(p + dplen) + 1); len -= dplen - 1; } } return len; } #ifndef HAVE_STRTONUM long long strtonum(const char *s, long long minval, long long maxval, const char **errstr_p) { char *end = NULL; long long res; int old_errno = errno; if (minval > maxval) goto einval; errno = 0; res = strtoll(s, &end, 10); if (errno == ERANGE) { if (res < 0) goto esmall; else goto elarge; } else if (errno != 0) { goto einval; } else if (*end || end == s) { goto einval; } else if (res < minval) { goto esmall; } else if (res > maxval) { goto elarge; } /* success */ if (errstr_p) *errstr_p = NULL; errno = old_errno; return res; esmall: if (errstr_p) *errstr_p = "too small"; errno = ERANGE; return 0; elarge: if (errstr_p) *errstr_p = "too large"; errno = ERANGE; return 0; einval: if (errstr_p) *errstr_p = "invalid"; errno = EINVAL; return 0; } #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim) { char *end, *start = *stringp; if (start) { end = start + strcspn(start, delim); *stringp = *end ? end + 1 : NULL; *end = 0; } return start; } #endif #ifndef HAVE_ASPRINTF int asprintf(char **dst_p, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = vasprintf(dst_p, fmt, ap); va_end(ap); return res; } #endif #ifndef HAVE_VASPRINTF int vasprintf(char **dst_p, const char *fmt, va_list ap) { return cx_vasprintf(NULL, dst_p, fmt, ap); } #endif #ifndef HAVE_STRNLEN size_t strnlen(const char *string, size_t maxlen) { const char *end = memchr(string, '\0', maxlen); return end ? (size_t)(end - string) : maxlen; } #endif /* * Same as strcmp, but handles NULLs. If both sides are NULL, returns "true". */ bool strcmpeq(const char *str_left, const char *str_right) { if (str_left == NULL && str_right == NULL) return true; if (str_left == NULL || str_right == NULL) return false; return strcmp(str_left, str_right) == 0; } pgbouncer-1.24.1/lib/usual/cbtree.h0000644000175000000000000000440614777762223014030 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Crit-bit tree / binary radix tree. */ #ifndef _USUAL_CBTREE_H_ #define _USUAL_CBTREE_H_ #include /** returns length of the key */ typedef size_t (*cbtree_getkey_func)(void *ctx, void *obj, const void **dst_p); /** walk over tree */ typedef bool (*cbtree_walker_func)(void *ctx, void *obj); /** Handle to tree */ struct CBTree; /** * Create new tree. * * @param obj_key_cb callback to get the key for a object * @param obj_free_cb callback to free the object when tree node is freed (optional) * @param cb_ctx extra pointer passed to callbacks * @param cx memory context where from allocate */ struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb, cbtree_walker_func obj_free_cb, void *cb_ctx, CxMem *cx); /** * frees all resources allocated. * If obj_free_cb is non-NULL, it will be called per each object. */ void cbtree_destroy(struct CBTree *tree); /** Inserts new node to tree */ bool cbtree_insert(struct CBTree *tree, void *obj) _MUSTCHECK; /** Removed node from tree. * If obj_free_cb is non-NULL, it will be called for the object. * * @returns true if key was found, false otherwise. */ bool cbtree_delete(struct CBTree *tree, const void *key, size_t klen); /** * Lookup a key. * * @returns object pointer if found, NULL ohterwise */ void *cbtree_lookup(struct CBTree *tree, const void *key, size_t klen); /** Walk over tree */ bool cbtree_walk(struct CBTree *tree, cbtree_walker_func cb_func, void *cb_arg); #endif pgbouncer-1.24.1/lib/usual/socket_ntop.c0000644000175000000000000001235614777762223015112 00000000000000/* $OpenBSD: inet_ntop.c,v 1.8 2008/12/09 19:38:38 otto Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #include #ifndef HAVE_INET_NTOP #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #define u_char uint8_t #define u_int unsigned int /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static const char *inet_ntop4(const u_char *src, char *dst, int size); static const char *inet_ntop6(const u_char *src, char *dst, int size); /* char * * inet_ntop(af, src, dst, size) * convert a network format address to presentation format. * return: * pointer to presentation format address (`dst'), or NULL (see errno). * author: * Paul Vixie, 1996. */ const char * inet_ntop(int af, const void *src, char *dst, int size) { if (size < 0) { errno = ENOSPC; return NULL; } switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } /* NOTREACHED */ } /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address, more or less like inet_ntoa() * return: * `dst' (as a const) * notes: * (1) uses no statics * (2) takes a u_char* not an in_addr as input * author: * Paul Vixie, 1996. */ static const char * inet_ntop4(const u_char *src, char *dst, int size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; int l; l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || l >= size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, int size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; char *tp, *ep; struct { int base, len; } best, cur; u_int words[IN6ADDRSZ / INT16SZ]; int i; int advance; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = best.len = -1; cur.base = cur.len = -1; for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; ep = tmp + sizeof(tmp); for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) return (NULL); tp += strlen(tp); break; } advance = snprintf(tp, ep - tp, "%x", words[i]); if (advance <= 0 || advance >= ep - tp) return (NULL); tp += advance; } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } if (tp + 1 >= ep) return (NULL); *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((tp - tmp) > size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } #endif pgbouncer-1.24.1/lib/usual/getopt.c0000644000175000000000000003245214777762223014063 00000000000000/* $OpenBSD: getopt_long.c,v 1.24 2010/07/22 19:31:53 blambert Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #ifdef NEED_USUAL_GETOPT #include #include char *optarg; /* argument associated with option */ int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, match; current_argv = place; match = -1; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; int optreset = 0; if (options == NULL) return (-1); /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. */ if (posixly_correct == -1) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; else if (*options == '-') flags |= FLAG_ALLARGS; if (*options == '+' || *options == '-') options++; /* * reset if requested */ if (optind == 0) optind = optreset = 1; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt -- * Parse argc/argv argument vector. */ int getopt(int nargc, char *nargv[], const char *options) { return getopt_internal(nargc, nargv, options, NULL, NULL, FLAG_PERMUTE); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char *nargv[], const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char *nargv[], const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } #endif /* NEED_USUAL_GETOPT */ pgbouncer-1.24.1/lib/usual/wchar.h0000644000175000000000000000242514777762223013667 00000000000000/* * wchar.h - wchar_t utilities. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_WCHAR_H_ #define _USUAL_WCHAR_H_ #include #include #include wchar_t *mbstr_decode(const char *str, int str_len, int *wlen_p, wchar_t *wbuf, int wbuf_len, bool allow_invalid); wctype_t wctype_wcsn(const wchar_t *name, unsigned int namelen); #ifndef HAVE_MBSNRTOWCS #define mbsnrtowcs(a,b,c,d,e) usual_mbsnrtowcs(a,b,c,d,e) size_t mbsnrtowcs(wchar_t *dst, const char **src_p, size_t srclen, size_t dstlen, mbstate_t *ps); #endif #endif pgbouncer-1.24.1/lib/usual/hashtab-impl.h0000644000175000000000000001352014777762223015132 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Simple customizable hashtable implementation. * * - Fixed-size hash table, open-addressed * - Extended by linking several together * - Resizable by copying. * - Can be lockless in multi-reader, one-writer situation if * mempory barrier macros are defined. This also requires that * HashItem must not be split across cachelines. */ #include #include #ifndef HTAB_KEY_T /** Overridable type for key */ #define HTAB_KEY_T unsigned long #endif #ifndef HTAB_VAL_T /** Overridable type for value */ #define HTAB_VAL_T void * #endif #ifndef HTAB_RMB #define HTAB_RMB #endif #ifndef HTAB_WMB #define HTAB_WMB #endif /** Typedef for key */ typedef HTAB_KEY_T htab_key_t; /** Typedef for value */ typedef HTAB_VAL_T htab_val_t; #ifndef HTAB_ITEM #define HTAB_ITEM /** HashTab slot */ struct HashItem { htab_key_t key; htab_val_t value; }; #endif /** Signature for comparision function */ typedef bool (*hash_cmp_fn)(const htab_val_t curval, const void *arg); #ifndef HTAB_MAX_FILL /** Max fill percentage */ #define HTAB_MAX_FILL 75 #endif #define MASK(h) ((h)->size - 1) #define CALC_POS(h, key) ((key) & MASK(h)) #define NEXT_POS(h, pos) (((pos) * 5 + 1) & MASK(h)) #define MAX_USED(h) ((h)->size * HTAB_MAX_FILL / 100) /** Single HashTab segment */ struct HashTab { struct HashTab *next; hash_cmp_fn cmp_fn; CxMem *ca; unsigned size; unsigned used; struct HashItem tab[FLEX_ARRAY]; }; /** Initialize HashTab */ static struct HashTab *hashtab_create(unsigned size, hash_cmp_fn cmp_fn, CxMem *ca) { struct HashTab *h; unsigned len = size * sizeof(struct HashItem) + offsetof(struct HashTab, tab); h = cx_alloc0(ca, len); if (h) { h->size = size; h->cmp_fn = cmp_fn; h->ca = ca; } return h; } /** Free HashTab */ static void hashtab_destroy(struct HashTab *h) { struct HashTab *tmp; while (h) { tmp = h->next; cx_free(h->ca, h); h = tmp; } } /** Element lookup, optionally inserting new slot */ static htab_val_t *hashtab_lookup(struct HashTab *h, htab_key_t key, bool do_insert, const void *arg) { unsigned pos; struct HashItem *i; loop: /* find key, starting from pos */ pos = CALC_POS(h, key); while (h->tab[pos].value) { i = &h->tab[pos]; HTAB_RMB; if (i->key == key) { if (arg && h->cmp_fn(i->value, arg)) return &i->value; } pos = NEXT_POS(h, pos); } /* not found in this one, check chained tables */ if (h->next) { h = h->next; goto loop; } /* just lookup? */ if (!do_insert) return NULL; /* insert */ if (h->used >= MAX_USED(h)) { struct HashTab *tmp; tmp = hashtab_create(h->size, h->cmp_fn, h->ca); if (!tmp) return NULL; h->next = tmp; h = tmp; pos = CALC_POS(h, key); } h->used++; h->tab[pos].key = key; HTAB_WMB; return &h->tab[pos].value; } /* if proper pos is between src and dst, cannot move */ static bool _hashtab_slot_can_move(struct HashTab *h, unsigned dstpos, unsigned srcpos) { htab_key_t key = h->tab[srcpos].key; unsigned pos, kpos = CALC_POS(h, key); if (kpos == srcpos) return false; if (kpos == dstpos) return true; for (pos = NEXT_POS(h, dstpos); pos != srcpos; pos = NEXT_POS(h, pos)) { if (pos == kpos) return false; } return true; } /** Delete an element */ static void hashtab_delete(struct HashTab *h, htab_key_t key, void *arg) { htab_val_t *vptr; struct HashItem *hd; unsigned pos, dstpos; /* find it */ vptr = hashtab_lookup(h, key, false, arg); if (!vptr) return; /* find right tab */ hd = container_of(vptr, struct HashItem, value); while (h && ((hd < h->tab) || (hd >= h->tab + h->size))) h = h->next; /* calculate index */ dstpos = hd - h->tab; loop: /* move slot */ for (pos = NEXT_POS(h, dstpos); h->tab[pos].value; pos = NEXT_POS(h, pos)) { if (_hashtab_slot_can_move(h, dstpos, pos)) { h->tab[dstpos].key = h->tab[pos].key; h->tab[dstpos].value = h->tab[pos].value; dstpos = pos; goto loop; } } h->tab[dstpos].value = 0; HTAB_WMB; h->tab[dstpos].key = 0; h->used--; } /** Count elements and fragments */ static void hashtab_stats(struct HashTab *h, unsigned *nitem_p, unsigned *ntab_p) { unsigned n = 0, l = 0; while (h) { l++; n += h->used; h = h->next; } *nitem_p = n; *ntab_p = l; } /** Copy elements to new hashtab, perhaps with different size */ static struct HashTab *hashtab_copy(struct HashTab *h_old, unsigned newsize) { struct HashTab *h_new; unsigned i; h_new = hashtab_create(newsize, h_old->cmp_fn, h_old->ca); for (; h_old; h_old = h_old->next) { for (i = 0; i < h_old->size; i++) { struct HashItem *s = &h_old->tab[i]; htab_val_t *new_pos; if (s->value) { new_pos = hashtab_lookup(h_new, s->key, true, NULL); if (!new_pos) goto err; *new_pos = s->value; } } } return h_new; err: hashtab_destroy(h_new); return NULL; } /* example, and avoid "unused" warnings */ static inline void _hashtab_example(void) { unsigned nitem, nlink; struct HashTab *h, *h2; h = hashtab_create(1024, NULL, NULL); hashtab_lookup(h, 123, true, NULL); hashtab_stats(h, &nitem, &nlink); h2 = hashtab_copy(h, 2048); hashtab_delete(h, 123, NULL); hashtab_destroy(h); hashtab_destroy(h2); } pgbouncer-1.24.1/lib/usual/pgutil_kwlookup.g0000644000175000000000000000242514777762223016021 00000000000000/* gperf header for kwlookup */ %language=ANSI-C %readonly-tables %pic %enum %define lookup-function-name pg_keyword_lookup_real %define hash-function-name pg_keyword_lookup_hash %define string-pool-name pgkw %% all analyse analyze and any array as asc asymmetric authorization between bigint binary bit boolean both case cast char character check coalesce collate column concurrently constraint create cross current_catalog current_date current_role current_schema current_time current_timestamp current_user dec decimal default deferrable desc distinct do else end except exists extract false fetch float for foreign freeze from full grant greatest group having ilike in initially inner inout int integer intersect interval into is isnull join leading least left like limit localtime localtimestamp national natural nchar new none not notnull null nullif numeric off offset old on only or order out outer over overlaps overlay placing position precision primary real references returning right row select session_user setof similar smallint some substring symmetric table then time timestamp to trailing treat trim true union unique user using values varchar variadic verbose when where window with xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize pgbouncer-1.24.1/lib/usual/time.h0000644000175000000000000000515114777762223013520 00000000000000/* * Theme include for time. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Time-related functionality. */ #ifndef _USUAL_TIME_H_ #define _USUAL_TIME_H_ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef _WIN32 #include #endif #include /** Type to hold microseconds. */ typedef uint64_t usec_t; /** How many microseconds in a second. */ #define USEC ((usec_t)1000000) /** Convert usec timestamp to ISO timestamp with millisecond precision: YYYY-mm-dd hh:mm:ss.SSS TZ */ char *format_time_ms(usec_t time, char *dest, unsigned destlen); /** Convert usec timestamp to ISO timestamp with second precision: YYYY-mm-dd hh:mm:ss TZ */ char *format_time_s(usec_t time, char *dest, unsigned destlen); /** Query system time */ usec_t get_time_usec(void); /** Query cached system time */ usec_t get_cached_time(void); /** Forget cached system time, next call will fill it. */ void reset_time_cache(void); #ifdef WIN32 #ifndef HAVE_GETTIMEOFDAY #define gettimeofday(t,z) usual_gettimeofday(t,z) /** Compat: gettimeofday() */ int gettimeofday(struct timeval * tp, void * tzp); #endif #ifndef HAVE_LOCALTIME_R #define localtime_r(t,r) usual_localtime_r(t,r) /** Compat: localtime_r() */ struct tm *localtime_r(const time_t *tp, struct tm *result); #endif #ifndef HAVE_USLEEP #define usleep(x) usual_usleep(x) /** Compat: usleep() */ static inline void usleep(long usec) { Sleep(usec / 1000); } #endif #ifndef HAVE_GETRUSAGE #define getrusage(w,r) usual_getrusage(w,r) #define RUSAGE_SELF 0 /** Compat: rusage for win32 */ struct rusage { struct timeval ru_utime; struct timeval ru_stime; }; /** Compat: getrusage() for win32 */ int getrusage(int who, struct rusage *r_usage); #endif #endif #ifndef HAVE_TIMEGM #define timegm(tm) usual_timegm(tm) /** Compat: timegm() */ time_t timegm(struct tm *tm); #endif #endif pgbouncer-1.24.1/lib/usual/mempool.c0000644000175000000000000000346214777762223014230 00000000000000/* * Simple memory pool for variable-length allocations. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * Allows allocation of several variable-sized objects, * freeing them all together. * * ToDo: make it more 'obstack'-like (???) * - free_last * - resize_last * - append */ struct MemPool { struct MemPool *prev; unsigned size; unsigned used; }; void *mempool_alloc(struct MemPool **pool, unsigned size) { struct MemPool *cur = *pool; void *ptr; unsigned nsize; size = ALIGN(size); if (cur && cur->used + size <= cur->size) { ptr = (char *)(cur + 1) + cur->used; cur->used += size; return ptr; } else { nsize = cur ? (2 * cur->size) : 512; while (nsize < size) nsize *= 2; cur = calloc(1, sizeof(*cur) + nsize); if (cur == NULL) return NULL; cur->used = size; cur->size = nsize; cur->prev = *pool; *pool = cur; return (char *)(cur + 1); } } void mempool_destroy(struct MemPool **pool) { struct MemPool *cur, *tmp; if (!pool) return; for (cur = *pool, *pool = NULL; cur; ) { tmp = cur->prev; free(cur); cur = tmp; } } pgbouncer-1.24.1/lib/usual/fnmatch.c0000644000175000000000000001373714777762223014206 00000000000000/* * fnmatch.c * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Differences from POSIX: * - ^ can be used in place of ! * - \ is escape in bracket expression, unless FNM_NOESCAPE is given. * - FNM_CASEFOLD * - FNM_LEADING_DIR */ #include #include #include #ifdef NEED_USUAL_FNMATCH /* compare chars with case folding */ static inline bool cmp_fold(wchar_t c1, wchar_t c2, int flags) { if (c1 == c2) return true; if (flags & FNM_CASEFOLD) { if (iswupper(c1) && iswlower(c2)) return c1 == (wchar_t)towupper(c2); else if (iswlower(c1) && iswupper(c2)) return c1 == (wchar_t)towlower(c2); } return false; } /* compare char to range with case folding */ static inline bool range_fold(wchar_t c, wchar_t r1, wchar_t r2, int flags) { if (c >= r1 && c <= r2) return true; if (flags & FNM_CASEFOLD) { /* convert only if it makes sense */ if (iswupper(c) && iswlower(r1) && iswlower(r2)) { c = towlower(c); if (c >= r1 && c <= r2) return true; } else if (iswlower(c) && iswupper(r1) && iswupper(r2)) { c = towupper(c); if (c >= r1 && c <= r2) return true; } } return false; } /* match bracket expression */ static const wchar_t *match_class(const wchar_t *pat, wchar_t c, int flags) { const wchar_t *p = pat; const wchar_t *start; bool neg = false; bool match = false; bool fallback_ok = true; const wchar_t *n1, *n2; wctype_t wct; /* negation */ if (*p == '!' || *p == '^') { neg = true; p++; } start = p; loop: /* named class, equivalence class or collating symbol */ if (p[0] == '[' && (p[1] == ':' || p[1] == '.' || p[1] == '=')) { n1 = p + 2; n2 = wcschr(n1, p[1]); if (!n2 || n2[1] != ']') goto parse_fail; if (p[1] != ':') return NULL; p = n2 + 2; wct = wctype_wcsn(n1, n2-n1); if (wct == (wctype_t)0) return NULL; if (iswctype(c, wct)) match = true; fallback_ok = false; /* skip rest */ goto loop; } parse_fail: /* unexpected pattern end */ if (p[0] == '\0') { /* only open bracket exists, take it as literal */ if (fallback_ok && c == '[') return pat - 1; return NULL; } /* closing bracket */ if (p[0] == ']' && p != start) return (match ^ neg) ? p : NULL; /* escape next char */ if (p[0] == '\\' && !(flags & FNM_NOESCAPE)) { if (p[1] == '\0') return NULL; p++; } /* its either simple range or char */ if (p[1] == '-' && p[2] != ']' && p[2] != '\0') { wchar_t r1 = p[0]; wchar_t r2 = p[2]; if (r2 == '\\' && !(flags & FNM_NOESCAPE)) { p++; r2 = p[2]; if (r2 == '\0') return NULL; } if (range_fold(c, r1, r2, flags)) match = true; p += 3; } else { if (cmp_fold(c, p[0], flags)) match = true; p++; } goto loop; } /* * FNM_PATHNAME disallows wildcard match for '/', * FNM_PERIOD disallows wildcard match for leading '.', * check for string end also. */ static bool disallow_wildcard(const wchar_t *s, const wchar_t *str, int flags) { if (*s == '\0') return true; if (*s == '/') return (flags & FNM_PATHNAME); if (*s == '.' && (flags & FNM_PERIOD)) { if (s == str) return true; if (s[-1] == '/' && (flags & FNM_PATHNAME)) return true; } return false; } /* * Non-recursive fnmatch(), based on globmatch() by */ static int wfnmatch(const wchar_t *pat, const wchar_t *str, int flags) { const wchar_t *p = pat; const wchar_t *s = str; const wchar_t *retry_p = NULL; const wchar_t *skip_s = NULL; loop: switch (*p) { case '*': /* match any number of chars from this position on */ retry_p = p + 1; skip_s = s; /* dot after '*' must not match leading dot */ if (p[1] == '.' && disallow_wildcard(s, str, flags)) return FNM_NOMATCH; break; case '?': /* match any char */ if (disallow_wildcard(s, str, flags)) goto nomatch_retry; s++; break; case '[': /* match character class */ if (disallow_wildcard(s, str, flags)) goto nomatch_retry; p = match_class(p + 1, *s, flags); if (p == NULL) goto nomatch_retry; s++; break; case '\\': /* escape next char */ if (!(flags & FNM_NOESCAPE)) { p++; if (*p == '\0') return FNM_NOMATCH; } /* fallthrough */ default: /* match single char */ if (*s == '/' && *p == '\0' && (flags & FNM_LEADING_DIR)) return 0; if (!cmp_fold(*p, *s, flags)) goto nomatch_retry; if (*s == '\0') return 0; s++; } p++; goto loop; nomatch_retry: /* eat chars with '*', if possible */ if (retry_p == NULL || *s == '\0') return FNM_NOMATCH; s = skip_s++; p = retry_p; if (*s == '\0') return (*p == '\0') ? 0 : FNM_NOMATCH; if (disallow_wildcard(s, str, flags)) return FNM_NOMATCH; s++; goto loop; } /* * Convert locale-specific encoding to wchar_t string */ int fnmatch(const char *pat, const char *str, int flags) { wchar_t *wpat, *wstr; wchar_t pbuf[128]; wchar_t sbuf[128]; int plen = strlen(pat); int slen = strlen(str); int res; /* convert encoding */ wpat = mbstr_decode(pat, plen, NULL, pbuf, sizeof(pbuf) / sizeof(wchar_t), false); if (!wpat) return (errno == EILSEQ) ? FNM_NOMATCH : -1; wstr = mbstr_decode(str, slen, NULL, sbuf, sizeof(sbuf) / sizeof(wchar_t), true); if (!wstr) return -1; /* run actual fnmatch */ res = wfnmatch(wpat, wstr, flags); /* free buffers */ if (wstr != sbuf) free(wstr); if (wpat != pbuf) free(wpat); return res; } #endif pgbouncer-1.24.1/lib/usual/err.h0000644000175000000000000000374714777762223013363 00000000000000/* * Cmdline error reporting. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Error printing for command-line utilities. */ #ifndef _USUAL_ERR_H_ #define _USUAL_ERR_H_ #include #ifdef HAVE_ERR_H #include #endif #ifndef HAVE_ERR /** Print formatted message and strerror(errno) to stderr and exit with given error code */ void err(int e, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_ERRX /** Print formatted message to stderr and exit with given error code */ void errx(int e, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_WARN /** Print formatted message and strerror(errno) to stderr */ void warn(const char *fmt, ...) _PRINTF(1, 2); #endif #ifndef HAVE_WARNX /** Print formatted message to stderr */ void warnx(const char *fmt, ...) _PRINTF(1, 2); #endif #ifndef HAVE_SETPROGNAME /** Set program name to that will printed as prefix to error messages */ void setprogname(const char *s); #endif #ifndef HAVE_GETPROGNAME /** Return program name set with @ref setprogname */ const char *getprogname(void); #endif /** Malloc that exits on failure */ void *xmalloc(size_t len); /** Realloc that exits on failure */ void *xrealloc(void *p, size_t len); /** strdup that exits on failure */ char *xstrdup(const char *s); #endif pgbouncer-1.24.1/lib/usual/netdb.h0000644000175000000000000000362714777762223013664 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2010 Marko Kreen, Skype Technologies * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * DNS lookup. */ #ifndef _USUAL_NETDB_H_ #define _USUAL_NETDB_H_ #include #ifdef HAVE_NETDB_H #include #endif #ifndef HAVE_GETADDRINFO_A /** Async execution */ #ifndef GAI_WAIT #define GAI_WAIT 0 #endif /** Synchronous execution */ #ifndef GAI_NOWAIT #define GAI_NOWAIT 1 #endif /* avoid name conflicts */ #define gaicb usual_gaicb #define getaddrinfo_a(a,b,c,d) usual_getaddrinfo_a(a,b,c,d) /** * Request data for getaddrinfo_a(). * * Fields correspond to getaddrinfo() parameters. */ struct gaicb { /** node name */ const char *ar_name; /** service name */ const char *ar_service; /** hints */ const struct addrinfo *ar_request; /** result */ struct addrinfo *ar_result; /* internal state */ int _state; }; #ifndef EAI_INPROGRESS #define EAI_INPROGRESS -100 #endif #ifndef EAI_SYSTEM #define EAI_SYSTEM -10 #endif #define gai_error(gcb) ((gcb)->_state) /** * Compat: Async DNS lookup. */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp); #endif /* HAVE_GETADDRINFO_A */ #endif /* _USUAL_NETDB_H_ */ pgbouncer-1.24.1/lib/usual/strpool.c0000644000175000000000000000557214777762223014266 00000000000000/* * Pool for shared strings. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * Put all strings into cbtree. */ struct StrPool { CxMem *ca; struct CBTree *tree; int count; }; /* pass key info to cbtree */ static size_t get_key(void *ctx, void *obj, const void **dst_p) { struct PStr *s = obj; *dst_p = s->str; return s->len; } /* free PStr obj */ static bool free_str(void *arg, void *obj) { struct PStr *p = obj; struct StrPool *sp = p->pool; memset(p, 0, offsetof(struct PStr, str) + 1); cx_free(sp->ca, obj); return true; } /* create main structure */ struct StrPool *strpool_create(CxMem *ca) { struct StrPool *sp; sp = cx_alloc(ca, sizeof(*sp)); if (!sp) return NULL; sp->count = 0; sp->ca = ca; sp->tree = cbtree_create(get_key, NULL, NULL, ca); if (!sp->tree) { cx_free(ca, sp); return NULL; } return sp; } /* free main structure */ void strpool_free(struct StrPool *sp) { if (sp) { cbtree_walk(sp->tree, free_str, sp); cbtree_destroy(sp->tree); cx_free(sp->ca, sp); } } /* return total count of strings in pool */ int strpool_total(struct StrPool *sp) { return sp->count; } /* get new reference to str */ struct PStr *strpool_get(struct StrPool *sp, const char *str, ssize_t len) { struct PStr *cstr; bool ok; if (len < 0) len = strlen(str); /* search */ cstr = cbtree_lookup(sp->tree, str, len); if (cstr) { cstr->refcnt++; return cstr; } /* create */ cstr = cx_alloc(sp->ca, sizeof(*cstr) + len + 1); if (!cstr) return NULL; cstr->pool = sp; cstr->refcnt = 1; cstr->len = len; memcpy(cstr->str, str, len + 1); /* insert */ ok = cbtree_insert(sp->tree, cstr); if (!ok) { cx_free(sp->ca, cstr); return NULL; } sp->count++; return cstr; } /* add reference */ void strpool_incref(struct PStr *s) { if (s) s->refcnt++; } /* drop reference, free if none left */ void strpool_decref(struct PStr *s) { struct StrPool *sp; if (!s) return; Assert(s->refcnt > 0); s->refcnt--; if (s->refcnt > 0) return; /* remove */ sp = s->pool; sp->count--; cbtree_delete(sp->tree, s->str, s->len); free_str(NULL, s); } pgbouncer-1.24.1/lib/usual/json.c0000644000175000000000000011306514777762223013532 00000000000000/* * Read and write JSON. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #define TYPE_BITS 3 #define TYPE_MASK ((1 << TYPE_BITS) - 1) #define UNATTACHED ((struct JsonValue *)(1 << TYPE_BITS)) #define JSON_MAX_KEY (1024*1024) #define NUMBER_BUF 100 #define JSON_MAXINT ((1LL << 53) - 1) #define JSON_MININT (-(1LL << 53) + 1) /* * Common struct for all JSON values */ struct JsonValue { /* actual value for simple types */ union { double v_float; /* float */ int64_t v_int; /* int */ bool v_bool; /* bool */ size_t v_size; /* str/list/dict */ } u; /* pointer to next elem and type in low bits */ uintptr_t v_next_and_type; }; /* * List container. */ struct ValueList { struct JsonValue *first; struct JsonValue *last; struct JsonValue **array; }; /* * Extra data for list/dict. */ struct JsonContainer { /* parent container */ struct JsonValue *c_parent; /* main context for child alloc */ struct JsonContext *c_ctx; /* child elements */ union { struct CBTree *c_dict; struct ValueList c_list; } u; }; #define DICT_EXTRA (offsetof(struct JsonContainer, u.c_dict) + sizeof(struct CBTree *)) #define LIST_EXTRA (sizeof(struct JsonContainer)) /* * Allocation context. */ struct JsonContext { CxMem *pool; unsigned int options; /* parse state */ struct JsonValue *parent; struct JsonValue *cur_key; struct JsonValue *top; const char *lasterr; char errbuf[128]; int64_t linenr; }; struct RenderState { struct MBuf *dst; unsigned int options; }; /* * Parser states */ enum ParseState { S_INITIAL_VALUE = 1, S_LIST_VALUE, S_LIST_VALUE_OR_CLOSE, S_LIST_COMMA_OR_CLOSE, S_DICT_KEY, S_DICT_KEY_OR_CLOSE, S_DICT_COLON, S_DICT_VALUE, S_DICT_COMMA_OR_CLOSE, S_PARENT, S_DONE, MAX_STATES, }; /* * Tokens that change state. */ enum TokenTypes { T_STRING, T_OTHER, T_COMMA, T_COLON, T_OPEN_DICT, T_OPEN_LIST, T_CLOSE_DICT, T_CLOSE_LIST, MAX_TOKENS }; /* * 4-byte ints for small string tokens. */ #define C_NULL FOURCC('n','u','l','l') #define C_TRUE FOURCC('t','r','u','e') #define C_ALSE FOURCC('a','l','s','e') /* * Signature for render functions. */ typedef bool (*render_func_t)(struct RenderState *rs, struct JsonValue *jv); static bool render_any(struct RenderState *rs, struct JsonValue *jv); /* * Header manipulation */ static inline enum JsonValueType get_type(struct JsonValue *jv) { return jv->v_next_and_type & TYPE_MASK; } static inline bool has_type(struct JsonValue *jv, enum JsonValueType type) { if (!jv) return false; return get_type(jv) == type; } static inline struct JsonValue *get_next(struct JsonValue *jv) { return (struct JsonValue *)(jv->v_next_and_type & ~(uintptr_t)TYPE_MASK); } static inline void set_next(struct JsonValue *jv, struct JsonValue *next) { jv->v_next_and_type = (uintptr_t)next | get_type(jv); } static inline bool is_unattached(struct JsonValue *jv) { return get_next(jv) == UNATTACHED; } static inline void *get_extra(struct JsonValue *jv) { return (void *)(jv + 1); } static inline char *get_cstring(struct JsonValue *jv) { enum JsonValueType type = get_type(jv); if (type != JSON_STRING) return NULL; return get_extra(jv); } /* * Collection header manipulation. */ static inline struct JsonContainer *get_container(struct JsonValue *jv) { enum JsonValueType type = get_type(jv); if (type != JSON_DICT && type != JSON_LIST) return NULL; return get_extra(jv); } static inline void set_parent(struct JsonValue *jv, struct JsonValue *parent) { struct JsonContainer *c = get_container(jv); if (c) c->c_parent = parent; } static inline struct JsonContext *get_context(struct JsonValue *jv) { struct JsonContainer *c = get_container(jv); return c ? c->c_ctx : NULL; } static inline struct CBTree *get_dict_tree(struct JsonValue *jv) { struct JsonContainer *c; if (has_type(jv, JSON_DICT)) { c = get_container(jv); return c->u.c_dict; } return NULL; } static inline struct ValueList *get_list_vlist(struct JsonValue *jv) { struct JsonContainer *c; if (has_type(jv, JSON_LIST)) { c = get_container(jv); return &c->u.c_list; } return NULL; } /* * Random helpers */ /* copy and return final pointer */ static inline char *plain_copy(char *dst, const char *src, const char *endptr) { if (src < endptr) { memcpy(dst, src, endptr - src); return dst + (endptr - src); } return dst; } /* error message on context */ _PRINTF(2,0) static void format_err(struct JsonContext *ctx, const char *errmsg, va_list ap) { char buf[119]; if (ctx->lasterr) return; vsnprintf(buf, sizeof(buf), errmsg, ap); snprintf(ctx->errbuf, sizeof(ctx->errbuf), "Line #%" PRIi64 ": %s", ctx->linenr, buf); ctx->lasterr = ctx->errbuf; } /* set message and return false */ _PRINTF(2,3) static bool err_false(struct JsonContext *ctx, const char *errmsg, ...) { va_list ap; va_start(ap, errmsg); format_err(ctx, errmsg, ap); va_end(ap); return false; } /* set message and return NULL */ _PRINTF(2,3) static void *err_null(struct JsonContext *ctx, const char *errmsg, ...) { va_list ap; va_start(ap, errmsg); format_err(ctx, errmsg, ap); va_end(ap); return NULL; } /* callback for cbtree, returns key bytes */ static size_t get_key_data_cb(void *dictptr, void *keyptr, const void **dst_p) { struct JsonValue *key = keyptr; *dst_p = get_cstring(key); return key->u.v_size; } /* add elemnt to list */ static void real_list_append(struct JsonValue *list, struct JsonValue *elem) { struct ValueList *vlist; vlist = get_list_vlist(list); if (vlist->last) { set_next(vlist->last, elem); } else { vlist->first = elem; } vlist->last = elem; vlist->array = NULL; list->u.v_size++; } /* add key to tree */ static bool real_dict_add_key(struct JsonContext *ctx, struct JsonValue *dict, struct JsonValue *key) { struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return err_false(ctx, "Expect dict"); if (json_value_size(key) > JSON_MAX_KEY) return err_false(ctx, "Too large key"); dict->u.v_size++; if (!cbtree_insert(tree, key)) return err_false(ctx, "Key insertion failed"); return true; } /* create basic value struct, link to stuctures */ static struct JsonValue *mk_value(struct JsonContext *ctx, enum JsonValueType type, size_t extra, bool attach) { struct JsonValue *val; struct JsonContainer *col = NULL; if (!ctx) return NULL; val = cx_alloc(ctx->pool, sizeof(struct JsonValue) + extra); if (!val) return err_null(ctx, "No memory"); if ((uintptr_t)val & TYPE_MASK) return err_null(ctx, "Unaligned pointer"); /* initial value */ val->v_next_and_type = type; val->u.v_int = 0; if (type == JSON_DICT || type == JSON_LIST) { col = get_container(val); col->c_ctx = ctx; col->c_parent = NULL; if (type == JSON_DICT) { col->u.c_dict = cbtree_create(get_key_data_cb, NULL, val, ctx->pool); if (!col->u.c_dict) return err_null(ctx, "No memory"); } else { memset(&col->u.c_list, 0, sizeof(col->u.c_list)); } } /* independent JsonValue? */ if (!attach) { set_next(val, UNATTACHED); return val; } /* attach to parent */ if (col) col->c_parent = ctx->parent; /* attach to previous value */ if (has_type(ctx->parent, JSON_DICT)) { if (ctx->cur_key) { set_next(ctx->cur_key, val); ctx->cur_key = NULL; } else { ctx->cur_key = val; } } else if (has_type(ctx->parent, JSON_LIST)) { real_list_append(ctx->parent, val); } else if (!ctx->top) { ctx->top = val; } else { return err_null(ctx, "Only one top element is allowed"); } return val; } static void prepare_array(struct JsonValue *list) { struct JsonContainer *c; struct JsonValue *val; struct ValueList *vlist; size_t i; vlist = get_list_vlist(list); if (vlist->array) return; c = get_container(list); vlist->array = cx_alloc(c->c_ctx->pool, list->u.v_size * sizeof(struct JsonValue *)); if (!vlist->array) return; val = vlist->first; for (i = 0; i < list->u.v_size && val; i++) { vlist->array[i] = val; val = get_next(val); } } /* * Parsing code starts */ /* create and change context */ static bool open_container(struct JsonContext *ctx, enum JsonValueType type, unsigned int extra) { struct JsonValue *jv; jv = mk_value(ctx, type, extra, true); if (!jv) return false; ctx->parent = jv; ctx->cur_key = NULL; return true; } /* close and change context */ static enum ParseState close_container(struct JsonContext *ctx, enum ParseState state) { struct JsonContainer *c; if (state != S_PARENT) return (int)err_false(ctx, "close_container bug"); c = get_container(ctx->parent); if (!c) return (int)err_false(ctx, "invalid parent"); ctx->parent = c->c_parent; ctx->cur_key = NULL; if (has_type(ctx->parent, JSON_DICT)) { return S_DICT_COMMA_OR_CLOSE; } else if (has_type(ctx->parent, JSON_LIST)) { return S_LIST_COMMA_OR_CLOSE; } return S_DONE; } /* parse 4-char token */ static bool parse_char4(struct JsonContext *ctx, const char **src_p, const char *end, uint32_t t_exp, enum JsonValueType type, bool val) { const char *src; uint32_t t_got; struct JsonValue *jv; src = *src_p; if (src + 4 > end) return err_false(ctx, "Unexpected end of token"); memcpy(&t_got, src, 4); if (t_exp != t_got) return err_false(ctx, "Invalid token"); jv = mk_value(ctx, type, 0, true); if (!jv) return false; jv->u.v_bool = val; *src_p += 4; return true; } /* parse int or float */ static bool parse_number(struct JsonContext *ctx, const char **src_p, const char *end) { const char *start, *src; enum JsonValueType type = JSON_INT; char *tokend = NULL; char buf[NUMBER_BUF]; size_t len; struct JsonValue *jv; double v_float = 0; int64_t v_int = 0; /* scan & copy */ start = src = *src_p; for (; src < end; src++) { if (*src >= '0' && *src <= '9') { } else if (*src == '+' || *src == '-') { } else if (*src == '.' || *src == 'e' || *src == 'E') { type = JSON_FLOAT; } else { break; } } len = src - start; if (len >= NUMBER_BUF) goto failed; memcpy(buf, start, len); buf[len] = 0; /* now parse */ errno = 0; tokend = buf; if (type == JSON_FLOAT) { v_float = strtod_dot(buf, &tokend); if (*tokend != 0 || errno || !isfinite(v_float)) goto failed; } else if (len < 8) { v_int = strtol(buf, &tokend, 10); if (*tokend != 0 || errno) goto failed; } else { v_int = strtoll(buf, &tokend, 10); if (*tokend != 0 || errno || v_int < JSON_MININT || v_int > JSON_MAXINT) goto failed; } /* create value struct */ jv = mk_value(ctx, type, 0, true); if (!jv) return false; if (type == JSON_FLOAT) { jv->u.v_float = v_float; } else { jv->u.v_int = v_int; } *src_p = src; return true; failed: if (!errno) errno = EINVAL; return err_false(ctx, "Number parse failed"); } /* * String parsing */ static int parse_hex(const char *s, const char *end) { int v = 0, c, i, x; if (s + 4 > end) return -1; for (i = 0; i < 4; i++) { c = s[i]; if (c >= '0' && c <= '9') { x = c - '0'; } else if (c >= 'a' && c <= 'f') { x = c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { x = c - 'A' + 10; } else { return -1; } v = (v << 4) | x; } return v; } /* process \uXXXX escapes, merge surrogates */ static bool parse_uescape(struct JsonContext *ctx, char **dst_p, char *dstend, const char **src_p, const char *end) { int c, c2; const char *src = *src_p; c = parse_hex(src, end); if (c <= 0) return err_false(ctx, "Invalid hex escape"); src += 4; if (c >= 0xD800 && c <= 0xDFFF) { /* first surrogate */ if (c >= 0xDC00) return err_false(ctx, "Invalid UTF16 escape"); if (src + 6 > end) return err_false(ctx, "Invalid UTF16 escape"); /* second surrogate */ if (src[0] != '\\' || src[1] != 'u') return err_false(ctx, "Invalid UTF16 escape"); c2 = parse_hex(src + 2, end); if (c2 < 0xDC00 || c2 > 0xDFFF) return err_false(ctx, "Invalid UTF16 escape"); c = 0x10000 + ((c & 0x3FF) << 10) + (c2 & 0x3FF); src += 6; } /* now write char */ if (!utf8_put_char(c, dst_p, dstend)) return err_false(ctx, "Invalid UTF16 escape"); *src_p = src; return true; } #define meta_string(c) (((c) == '"' || (c) == '\\' || (c) == '\0' || \ (c) == '\n' || ((c) & 0x80) != 0) ? 1 : 0) static const uint8_t string_examine_chars[] = INTMAP256_CONST(meta_string); /* look for string end, validate contents */ static bool scan_string(struct JsonContext *ctx, const char *src, const char *end, const char **str_end_p, bool *hasesc_p, int64_t *nlines_p) { bool hasesc = false; int64_t lines = 0; unsigned int n; bool check_utf8 = true; if (ctx->options & JSON_PARSE_IGNORE_ENCODING) check_utf8 = false; while (src < end) { if (!string_examine_chars[(uint8_t)*src]) { src++; } else if (*src == '"') { /* string end */ *hasesc_p = hasesc; *str_end_p = src; *nlines_p = lines; return true; } else if (*src == '\\') { hasesc = true; src++; if (src < end && (*src == '\\' || *src == '"')) src++; } else if (*src & 0x80) { n = utf8_validate_seq(src, end); if (n) { src += n; } else if (check_utf8) { goto badutf; } else { src++; } } else if (*src == '\n') { lines++; src++; } else { goto badutf; } } return err_false(ctx, "Unexpected end of string"); badutf: return err_false(ctx, "Invalid UTF8 sequence"); } /* string boundaries are known, copy and unescape */ static char *process_escapes(struct JsonContext *ctx, const char *src, const char *end, char *dst, char *dstend) { const char *esc; /* process escapes */ while (src < end) { esc = memchr(src, '\\', end - src); if (!esc) { dst = plain_copy(dst, src, end); break; } dst = plain_copy(dst, src, esc); src = esc + 1; switch (*src++) { case '"': *dst++ = '"'; break; case '\\': *dst++ = '\\'; break; case '/': *dst++ = '/'; break; case 'b': *dst++ = '\b'; break; case 'f': *dst++ = '\f'; break; case 'n': *dst++ = '\n'; break; case 'r': *dst++ = '\r'; break; case 't': *dst++ = '\t'; break; case 'u': if (!parse_uescape(ctx, &dst, dstend, &src, end)) return NULL; break; default: return err_null(ctx, "Invalid escape code"); } } return dst; } /* 2-phase string processing */ static bool parse_string(struct JsonContext *ctx, const char **src_p, const char *end) { const char *start, *strend = NULL; bool hasesc = false; char *dst, *dstend; size_t len; struct JsonValue *jv; int64_t lines = 0; /* find string boundaries, validate */ start = *src_p; if (!scan_string(ctx, start, end, &strend, &hasesc, &lines)) return false; /* create value struct */ len = strend - start; jv = mk_value(ctx, JSON_STRING, len + 1, true); if (!jv) return false; dst = get_cstring(jv); dstend = dst + len; /* copy & process escapes */ if (hasesc) { dst = process_escapes(ctx, start, strend, dst, dstend); if (!dst) return false; } else { dst = plain_copy(dst, start, strend); } *dst = '\0'; jv->u.v_size = dst - get_cstring(jv); ctx->linenr += lines; *src_p = strend + 1; return true; } /* * Helpers for relaxed parsing */ static bool skip_comment(struct JsonContext *ctx, const char **src_p, const char *end) { const char *s; char c; size_t lnr; s = *src_p; if (s >= end) return false; c = *s++; if (c == '/') { s = memchr(s, '\n', end - s); if (s) { ctx->linenr++; *src_p = s + 1; } else { *src_p = end; } return true; } else if (c == '*') { for (lnr = 0; s + 2 <= end; s++) { if (s[0] == '*' && s[1] == '/') { ctx->linenr += lnr; *src_p = s + 2; return true; } else if (s[0] == '\n') { lnr++; } } } return false; } static bool skip_extra_comma(struct JsonContext *ctx, const char **src_p, const char *end, enum ParseState state) { bool skip = false; const char *src = *src_p; while (src < end && isspace(*src)) { if (*src == '\n') ctx->linenr++; src++; } if (src < end) { if (*src == '}') { if (state == S_DICT_COMMA_OR_CLOSE || state == S_DICT_KEY_OR_CLOSE) skip = true; } else if (*src == ']') { if (state == S_LIST_COMMA_OR_CLOSE || state == S_LIST_VALUE_OR_CLOSE) skip = true; } } *src_p = src; return skip; } /* * Main parser */ /* oldstate + token -> newstate */ static const unsigned char STATE_STEPS[MAX_STATES][MAX_TOKENS] = { [S_INITIAL_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_DONE, [T_OTHER] = S_DONE }, [S_LIST_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_LIST_COMMA_OR_CLOSE, [T_OTHER] = S_LIST_COMMA_OR_CLOSE }, [S_LIST_VALUE_OR_CLOSE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_LIST_COMMA_OR_CLOSE, [T_OTHER] = S_LIST_COMMA_OR_CLOSE, [T_CLOSE_LIST] = S_PARENT }, [S_LIST_COMMA_OR_CLOSE] = { [T_COMMA] = S_LIST_VALUE, [T_CLOSE_LIST] = S_PARENT }, [S_DICT_KEY] = { [T_STRING] = S_DICT_COLON }, [S_DICT_KEY_OR_CLOSE] = { [T_STRING] = S_DICT_COLON, [T_CLOSE_DICT] = S_PARENT }, [S_DICT_COLON] = { [T_COLON] = S_DICT_VALUE }, [S_DICT_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_DICT_COMMA_OR_CLOSE, [T_OTHER] = S_DICT_COMMA_OR_CLOSE }, [S_DICT_COMMA_OR_CLOSE] = { [T_COMMA] = S_DICT_KEY, [T_CLOSE_DICT] = S_PARENT }, }; #define MAPSTATE(state, tok) do { \ int newstate = STATE_STEPS[state][tok]; \ if (!newstate) \ return err_false(ctx, "Unexpected symbol: '%c'", c); \ state = newstate; \ } while (0) /* actual parser */ static bool parse_tokens(struct JsonContext *ctx, const char *src, const char *end) { char c; enum ParseState state = S_INITIAL_VALUE; bool relaxed = ctx->options & JSON_PARSE_RELAXED; while (src < end) { c = *src++; switch (c) { case '\n': ctx->linenr++; case ' ': case '\t': case '\r': case '\f': case '\v': /* common case - many spaces */ while (src < end && *src == ' ') src++; break; case '"': MAPSTATE(state, T_STRING); if (!parse_string(ctx, &src, end)) goto failed; break; case 'n': MAPSTATE(state, T_OTHER); src--; if (!parse_char4(ctx, &src, end, C_NULL, JSON_NULL, 0)) goto failed; continue; case 't': MAPSTATE(state, T_OTHER); src--; if (!parse_char4(ctx, &src, end, C_TRUE, JSON_BOOL, 1)) goto failed; break; case 'f': MAPSTATE(state, T_OTHER); if (!parse_char4(ctx, &src, end, C_ALSE, JSON_BOOL, 0)) goto failed; break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': MAPSTATE(state, T_OTHER); src--; if (!parse_number(ctx, &src, end)) goto failed; break; case '[': MAPSTATE(state, T_OPEN_LIST); if (!open_container(ctx, JSON_LIST, LIST_EXTRA)) goto failed; break; case '{': MAPSTATE(state, T_OPEN_DICT); if (!open_container(ctx, JSON_DICT, DICT_EXTRA)) goto failed; break; case ']': MAPSTATE(state, T_CLOSE_LIST); state = close_container(ctx, state); if (!state) goto failed; break; case '}': MAPSTATE(state, T_CLOSE_DICT); state = close_container(ctx, state); if (!state) goto failed; break; case ':': MAPSTATE(state, T_COLON); if (!real_dict_add_key(ctx, ctx->parent, ctx->cur_key)) goto failed; break; case ',': if (relaxed && skip_extra_comma(ctx, &src, end, state)) continue; MAPSTATE(state, T_COMMA); break; case '/': if (relaxed && skip_comment(ctx, &src, end)) continue; /* fallthrough */ default: return err_false(ctx, "Invalid symbol: '%c'", c); } } if (state != S_DONE) return err_false(ctx, "Container still open"); return true; failed: return false; } /* parser public api */ struct JsonValue *json_parse(struct JsonContext *ctx, const char *json, size_t len) { const char *end = json + len; /* reset parser */ ctx->linenr = 1; ctx->parent = NULL; ctx->cur_key = NULL; ctx->lasterr = NULL; ctx->top = NULL; if (!parse_tokens(ctx, json, end)) return NULL; return ctx->top; } /* * Render value as JSON string. */ static bool render_null(struct RenderState *rs, struct JsonValue *jv) { return mbuf_write(rs->dst, "null", 4); } static bool render_bool(struct RenderState *rs, struct JsonValue *jv) { if (jv->u.v_bool) return mbuf_write(rs->dst, "true", 4); return mbuf_write(rs->dst, "false", 5); } static bool render_int(struct RenderState *rs, struct JsonValue *jv) { char buf[NUMBER_BUF]; int len; len = snprintf(buf, sizeof(buf), "%" PRIi64, jv->u.v_int); if (len < 0 || len >= NUMBER_BUF) return false; return mbuf_write(rs->dst, buf, len); } static bool render_float(struct RenderState *rs, struct JsonValue *jv) { char buf[NUMBER_BUF + 2]; int len; len = dtostr_dot(buf, NUMBER_BUF, jv->u.v_float); if (len < 0 || len >= NUMBER_BUF) return false; if (!memchr(buf, '.', len) && !memchr(buf, 'e', len)) { buf[len++] = '.'; buf[len++] = '0'; } return mbuf_write(rs->dst, buf, len); } static bool escape_char(struct MBuf *dst, unsigned int c) { char ec; char buf[10]; /* start escape */ if (!mbuf_write_byte(dst, '\\')) return false; /* escape same char */ if (c == '"' || c == '\\') return mbuf_write_byte(dst, c); /* low-ascii mess */ switch (c) { case '\b': ec = 'b'; break; case '\f': ec = 'f'; break; case '\n': ec = 'n'; break; case '\r': ec = 'r'; break; case '\t': ec = 't'; break; default: snprintf(buf, sizeof(buf), "u%04x", c); return mbuf_write(dst, buf, 5); } return mbuf_write_byte(dst, ec); } static bool render_string(struct RenderState *rs, struct JsonValue *jv) { const char *s, *last; const char *val = get_cstring(jv); size_t len = jv->u.v_size; const char *end = val + len; unsigned int c; /* start quote */ if (!mbuf_write_byte(rs->dst, '"')) return false; for (s = last = val; s < end; s++) { if (*s == '"' || *s == '\\' || (unsigned char)*s < 0x20 || /* Valid in JSON, but not in JS: \u2028 - Line separator \u2029 - Paragraph separator */ ((unsigned char)s[0] == 0xE2 && (unsigned char)s[1] == 0x80 && ((unsigned char)s[2] == 0xA8 || (unsigned char)s[2] == 0xA9))) { /* flush */ if (last < s) { if (!mbuf_write(rs->dst, last, s - last)) return false; } if ((unsigned char)s[0] == 0xE2) { c = 0x2028 + ((unsigned char)s[2] - 0xA8); last = s + 3; } else { c = (unsigned char)*s; last = s + 1; } /* output escaped char */ if (!escape_char(rs->dst, c)) return false; } } /* flush */ if (last < s) { if (!mbuf_write(rs->dst, last, s - last)) return false; } /* final quote */ if (!mbuf_write_byte(rs->dst, '"')) return false; return true; } /* * Render complex values */ struct ElemWriterState { struct RenderState *rs; char sep; }; static bool list_elem_writer(void *arg, struct JsonValue *elem) { struct ElemWriterState *state = arg; if (state->sep && !mbuf_write_byte(state->rs->dst, state->sep)) return false; state->sep = ','; return render_any(state->rs, elem); } static bool render_list(struct RenderState *rs, struct JsonValue *list) { struct ElemWriterState state; state.rs = rs; state.sep = 0; if (!mbuf_write_byte(rs->dst, '[')) return false; if (!json_list_iter(list, list_elem_writer, &state)) return false; if (!mbuf_write_byte(rs->dst, ']')) return false; return true; } static bool dict_elem_writer(void *ctx, struct JsonValue *key, struct JsonValue *val) { struct ElemWriterState *state = ctx; if (state->sep && !mbuf_write_byte(state->rs->dst, state->sep)) return false; state->sep = ','; if (!render_any(state->rs, key)) return false; if (!mbuf_write_byte(state->rs->dst, ':')) return false; return render_any(state->rs, val); } static bool render_dict(struct RenderState *rs, struct JsonValue *dict) { struct ElemWriterState state; state.rs = rs; state.sep = 0; if (!mbuf_write_byte(rs->dst, '{')) return false; if (!json_dict_iter(dict, dict_elem_writer, &state)) return false; if (!mbuf_write_byte(rs->dst, '}')) return false; return true; } static bool render_invalid(struct RenderState *rs, struct JsonValue *jv) { return false; } /* * Public api */ static bool render_any(struct RenderState *rs, struct JsonValue *jv) { static const render_func_t rfunc_map[] = { render_invalid, render_null, render_bool, render_int, render_float, render_string, render_list, render_dict, }; return rfunc_map[get_type(jv)](rs, jv); } bool json_render(struct MBuf *dst, struct JsonValue *jv) { struct RenderState rs; rs.dst = dst; rs.options = 0; return render_any(&rs, jv); } /* * Examine single value */ enum JsonValueType json_value_type(struct JsonValue *jv) { return get_type(jv); } size_t json_value_size(struct JsonValue *jv) { if (has_type(jv, JSON_STRING) || has_type(jv, JSON_LIST) || has_type(jv, JSON_DICT)) return jv->u.v_size; return 0; } bool json_value_as_bool(struct JsonValue *jv, bool *dst_p) { if (!has_type(jv, JSON_BOOL)) return false; *dst_p = jv->u.v_bool; return true; } bool json_value_as_int(struct JsonValue *jv, int64_t *dst_p) { if (!has_type(jv, JSON_INT)) return false; *dst_p = jv->u.v_int; return true; } bool json_value_as_float(struct JsonValue *jv, double *dst_p) { if (!has_type(jv, JSON_FLOAT)) { if (has_type(jv, JSON_INT)) { *dst_p = jv->u.v_int; return true; } return false; } *dst_p = jv->u.v_float; return true; } bool json_value_as_string(struct JsonValue *jv, const char **dst_p, size_t *size_p) { if (!has_type(jv, JSON_STRING)) return false; *dst_p = get_cstring(jv); if (size_p) *size_p = jv->u.v_size; return true; } /* * Load value from dict. */ static int dict_getter(struct JsonValue *dict, const char *key, unsigned int klen, struct JsonValue **val_p, enum JsonValueType req_type, bool req_value) { struct JsonValue *val, *kjv; struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return false; kjv = cbtree_lookup(tree, key, klen); if (!kjv) { if (req_value) return false; *val_p = NULL; return true; } val = get_next(kjv); if (!req_value && json_value_is_null(val)) { *val_p = NULL; return true; } if (!has_type(val, req_type)) return false; *val_p = val; return true; } bool json_dict_get_value(struct JsonValue *dict, const char *key, struct JsonValue **val_p) { struct CBTree *tree; struct JsonValue *kjv; size_t klen; tree = get_dict_tree(dict); if (!tree) return false; klen = strlen(key); kjv = cbtree_lookup(tree, key, klen); if (!kjv) return false; *val_p = get_next(kjv); return true; } bool json_dict_is_null(struct JsonValue *dict, const char *key) { struct JsonValue *val; if (!json_dict_get_value(dict, key, &val)) return true; return has_type(val, JSON_NULL); } bool json_dict_get_bool(struct JsonValue *dict, const char *key, bool *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_BOOL, true)) return false; return json_value_as_bool(val, dst_p); } bool json_dict_get_int(struct JsonValue *dict, const char *key, int64_t *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_INT, true)) return false; return json_value_as_int(val, dst_p); } bool json_dict_get_float(struct JsonValue *dict, const char *key, double *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_FLOAT, true)) return false; return json_value_as_float(val, dst_p); } bool json_dict_get_string(struct JsonValue *dict, const char *key, const char **dst_p, size_t *len_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_STRING, true)) return false; return json_value_as_string(val, dst_p, len_p); } bool json_dict_get_list(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { return dict_getter(dict, key, strlen(key), dst_p, JSON_LIST, true); } bool json_dict_get_dict(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { return dict_getter(dict, key, strlen(key), dst_p, JSON_DICT, true); } /* * Load optional dict element. */ bool json_dict_get_opt_bool(struct JsonValue *dict, const char *key, bool *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_BOOL, false)) return false; return !val || json_value_as_bool(val, dst_p); } bool json_dict_get_opt_int(struct JsonValue *dict, const char *key, int64_t *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_INT, false)) return false; return !val || json_value_as_int(val, dst_p); } bool json_dict_get_opt_float(struct JsonValue *dict, const char *key, double *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_FLOAT, false)) return false; return !val || json_value_as_float(val, dst_p); } bool json_dict_get_opt_string(struct JsonValue *dict, const char *key, const char **dst_p, size_t *len_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_STRING, false)) return false; return !val || json_value_as_string(val, dst_p, len_p); } bool json_dict_get_opt_list(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_LIST, false)) return false; if (val) *dst_p = val; return true; } bool json_dict_get_opt_dict(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_DICT, false)) return false; if (val) *dst_p = val; return true; } /* * Load value from list. */ bool json_list_get_value(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *val; struct ValueList *vlist; size_t i; vlist = get_list_vlist(list); if (!vlist) return false; if (index >= list->u.v_size) return false; if (!vlist->array && list->u.v_size > 10) prepare_array(list); /* direct fetch */ if (vlist->array) { *val_p = vlist->array[index]; return true; } /* walk */ val = vlist->first; for (i = 0; val; i++) { if (i == index) { *val_p = val; return true; } val = get_next(val); } return false; } bool json_list_is_null(struct JsonValue *list, size_t n) { struct JsonValue *jv; if (!json_list_get_value(list, n, &jv)) return true; return has_type(jv, JSON_NULL); } bool json_list_get_bool(struct JsonValue *list, size_t index, bool *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_bool(jv, val_p); } bool json_list_get_int(struct JsonValue *list, size_t index, int64_t *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_int(jv, val_p); } bool json_list_get_float(struct JsonValue *list, size_t index, double *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_float(jv, val_p); } bool json_list_get_string(struct JsonValue *list, size_t index, const char **val_p, size_t *len_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_string(jv, val_p, len_p); } bool json_list_get_list(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; if (!has_type(jv, JSON_LIST)) return false; *val_p = jv; return true; } bool json_list_get_dict(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; if (!has_type(jv, JSON_DICT)) return false; *val_p = jv; return true; } /* * Iterate over list and dict values. */ struct DictIterState { json_dict_iter_callback_f cb_func; void *cb_arg; }; static bool dict_iter_helper(void *arg, void *jv) { struct DictIterState *state = arg; struct JsonValue *key = jv; struct JsonValue *val = get_next(key); return state->cb_func(state->cb_arg, key, val); } bool json_dict_iter(struct JsonValue *dict, json_dict_iter_callback_f cb_func, void *cb_arg) { struct DictIterState state; struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return false; state.cb_func = cb_func; state.cb_arg = cb_arg; return cbtree_walk(tree, dict_iter_helper, &state); } bool json_list_iter(struct JsonValue *list, json_list_iter_callback_f cb_func, void *cb_arg) { struct JsonValue *elem; struct ValueList *vlist; vlist = get_list_vlist(list); if (!vlist) return false; for (elem = vlist->first; elem; elem = get_next(elem)) { if (!cb_func(cb_arg, elem)) return false; } return true; } /* * Create new values. */ struct JsonValue *json_new_null(struct JsonContext *ctx) { return mk_value(ctx, JSON_NULL, 0, false); } struct JsonValue *json_new_bool(struct JsonContext *ctx, bool val) { struct JsonValue *jv; jv = mk_value(ctx, JSON_BOOL, 0, false); if (jv) jv->u.v_bool = val; return jv; } struct JsonValue *json_new_int(struct JsonContext *ctx, int64_t val) { struct JsonValue *jv; if (val < JSON_MININT || val > JSON_MAXINT) { errno = ERANGE; return NULL; } jv = mk_value(ctx, JSON_INT, 0, false); if (jv) jv->u.v_int = val; return jv; } struct JsonValue *json_new_float(struct JsonContext *ctx, double val) { struct JsonValue *jv; /* check if value survives JSON roundtrip */ if (!isfinite(val)) return false; jv = mk_value(ctx, JSON_FLOAT, 0, false); if (jv) jv->u.v_float = val; return jv; } struct JsonValue *json_new_string(struct JsonContext *ctx, const char *val) { struct JsonValue *jv; size_t len; len = strlen(val); if (!utf8_validate_string(val, val + len)) return NULL; jv = mk_value(ctx, JSON_STRING, len + 1, false); if (jv) { memcpy(get_cstring(jv), val, len + 1); jv->u.v_size = len; } return jv; } struct JsonValue *json_new_list(struct JsonContext *ctx) { return mk_value(ctx, JSON_LIST, LIST_EXTRA, false); } struct JsonValue *json_new_dict(struct JsonContext *ctx) { return mk_value(ctx, JSON_DICT, DICT_EXTRA, false); } /* * Add to containers */ bool json_list_append(struct JsonValue *list, struct JsonValue *val) { if (!val) return false; if (!has_type(list, JSON_LIST)) return false; if (!is_unattached(val)) return false; set_parent(val, list); set_next(val, NULL); real_list_append(list, val); return true; } bool json_list_append_null(struct JsonValue *list) { struct JsonValue *v; v = json_new_null(get_context(list)); return json_list_append(list, v); } bool json_list_append_bool(struct JsonValue *list, bool val) { struct JsonValue *v; v = json_new_bool(get_context(list), val); return json_list_append(list, v); } bool json_list_append_int(struct JsonValue *list, int64_t val) { struct JsonValue *v; v = json_new_int(get_context(list), val); return json_list_append(list, v); } bool json_list_append_float(struct JsonValue *list, double val) { struct JsonValue *v; v = json_new_float(get_context(list), val); return json_list_append(list, v); } bool json_list_append_string(struct JsonValue *list, const char *val) { struct JsonValue *v; v = json_new_string(get_context(list), val); return json_list_append(list, v); } bool json_dict_put(struct JsonValue *dict, const char *key, struct JsonValue *val) { struct JsonValue *kjv; struct JsonContainer *c; if (!key || !val) return false; if (!has_type(dict, JSON_DICT)) return false; if (!is_unattached(val)) return false; c = get_container(dict); kjv = json_new_string(c->c_ctx, key); if (!kjv) return false; if (!real_dict_add_key(c->c_ctx, dict, kjv)) return false; set_next(kjv, val); set_next(val, NULL); set_parent(val, dict); return true; } bool json_dict_put_null(struct JsonValue *dict, const char *key) { struct JsonValue *v; v = json_new_null(get_context(dict)); return json_dict_put(dict, key, v); } bool json_dict_put_bool(struct JsonValue *dict, const char *key, bool val) { struct JsonValue *v; v = json_new_bool(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_int(struct JsonValue *dict, const char *key, int64_t val) { struct JsonValue *v; v = json_new_int(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_float(struct JsonValue *dict, const char *key, double val) { struct JsonValue *v; v = json_new_float(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_string(struct JsonValue *dict, const char *key, const char *val) { struct JsonValue *v; v = json_new_string(get_context(dict), val); return json_dict_put(dict, key, v); } /* * Main context management */ struct JsonContext *json_new_context(const void *cx, size_t initial_mem) { struct JsonContext *ctx; CxMem *pool; pool = cx_new_pool(cx, initial_mem, 8); if (!pool) return NULL; ctx = cx_alloc0(pool, sizeof(*ctx)); if (!ctx) { cx_destroy(pool); return NULL; } ctx->pool = pool; return ctx; } void json_free_context(struct JsonContext *ctx) { if (ctx) { CxMem *pool = ctx->pool; memset(ctx, 0, sizeof(*ctx)); cx_destroy(pool); } } const char *json_strerror(struct JsonContext *ctx) { return ctx->lasterr; } void json_set_options(struct JsonContext *ctx, unsigned int options) { ctx->options = options; } pgbouncer-1.24.1/lib/mk/0000755000000000000000000000000014777762567011743 500000000000000pgbouncer-1.24.1/lib/mk/amext-cxx.mk0000644000175000000000000000166614777762223014145 00000000000000# # Support for C++ language # # - extensions: .cc, .cpp, cxx # - CXX, CXXFLAGS # - AM_CXXFLAGS, _CXXFLAGS # # autoconfigurable values ifneq ($(filter-out @%,@CXX@),) CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ endif CXX ?= c++ CXXFLAGS ?= -O -g # fixme: add warning flags to CXXFLAGS CXXFLAGS += $(WFLAGS) # helper variables CXXLD ?= $(CXX) CXXCOMPILE ?= $(CXX) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLINK ?= $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ # full compile command define AM_LANG_CXX_COMPILE $(E) "CXX" $< $(Q) $(LTCOMPILE) $(CXXCOMPILE) $(OBJDEPS) -c -o $@ $< endef # full link command define AM_LANG_CXX_LINK $(E) "CXXLD" $@ $(Q) $(LTLINK) $(CXXLINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) endef # source file extensions for c++ AM_LANG_CXX_SRCEXTS = .cc .cpp cxx # register per-target variable AM_TARGET_VARIABLES += CXXFLAGS # register new language AM_LANGUAGES += CXX pgbouncer-1.24.1/lib/mk/antimake.txt0000644000175000000000000003216214777762223014223 00000000000000= antimake.mk(5) = == NAME == antimake - Minimal Automake syntax on plain GNU Make == DESCRIPTION == Antimake makes possible to use GNU Automake conventions to describe builds in ordinary Makefiles for GNU Make. It's main abstractions are target lists and target variables. Target list describes target type and where to install. Target variables give source files and additional flags for build. == EXAMPLE == ------------------- # target list bin_PROGRAMS = prog # target variables for 'prog' prog_SOURCES = prog.c prog.h prog_LDADD = libutil.a # target list noinst_LIBRARIES = libutil.a # target variables for 'libutil.a' libutil_a_SOURCES = util.c util.h # load Antimake include antimake.mk ------------------- == Terminology == Primary:: target type, describes how to build and install particular type of targets. Target:: a file that needs to be built and/or installed. Distribute:: Include file in source .tar.gz. Non-distributed files are skipped when building .tar.gz and are cleaned during `make distclean`. Source:: Source files are files that appear in `..._SOURCES` per-target variable. They are distributed by default. They may or may not result in object files. It's fine to put both `.h` and `.c` files into _SOURCES. == TARGET LISTS == Target lists are variables that contain file names that need to be built and installed. They are specially named so that the name also describes how they are built, how and where they will be installed. The target list name contains 3 parts, separated with underscore, in following order: 1. Optional flags. Flags are: `nodist`, `dist`, `nobase`, `base`. (Default: `base`, `nodist`) 2. Destination directory name. Destination directory called *bin* actual location is stored in Make variable `$(bindir)`. Some common values: `bin`, `lib`, `include`. There are more and the list can be extended. Special name `noinst` means the target file should not be installed. 3. Target type, also called "primary". This will describe how the target needs to be built. Common values: `PROGRAMS`, `LIBRARIES`, `DATA` For details, what the various values mean, see next sections. .Examples: ---------------- bin_PROGRAMS = prog1 prog2 # flags: base, nodist # dest: $(bindir) # type: PROGRAMS noinst_LIBRARIES = lib1.a lib2.a # flags: base, nodist # dest: noinst # type: LIBRARIES nobase_dist_doc_DATA = docs/README # flags: dist, nobase # dest: $(docdir)/docs # type: DATA ---------------- === Primaries === `PROGRAMS`:: executable programs, linked together from objects built from source files `LIBARIES`:: static libraries, linked together from objects built from source files `LTLIBRARIES`:: dynamic or static libraries, linked together from objects built from source files `HEADERS`:: header files, no default build method, the target files have `dist` flag by default. `MANS`:: man pages, no default build method, installed into manX subdir. `SCRIPTS`:: scripts, executable file, no default build method `DATA`:: data, non-executable file, no default build method === Target list flags === `dist`:: The target should be distributed with other sources. Default for `HEADERS` type, others have `nodist` by default. `nodist`:: Target is not distributed and should be cleaned with distclean. Default for all primaries, except `HEADERS`. `base`:: On install relative path is ignored, all files end up in destination directory. Always default. `nobase`:: On install relative path is kept. Eg: if `includedir=/usr/include` then `nobase_include_HEADERS=mylib/common.h` is installed to `/usr/include/mylib/common.h`. `noinst`:: Target is built as part of build process, but is not installed. `EXTRA`:: Targets in such list are not built, nor installed. Useful to make sure that sources for dynamically configured targets will end up in source tarball. Unlike other target list ariables, `EXTRA_` may contain targets already defined in other target lists, they will be filtered out from this list then. == Target variables == Only big targets take additional variables: `PROGRAMS`/`LIBRARIES`/`LTLIBRARIES`. `_SOURCES`:: All source files, *.c *.h *.cpp *.hpp. `nodist__SOURCES`:: Source files that should not be distributed. `EXTRA__SOURCES`:: In case tgt_SOURCES is dynamic, here is non-dynamic list of sources for distribution. Only dynamic sources need to be listed here. `_DEPENDENCIES`:: Add dependencies that need to be build before target build will start. `_CFLAGS`, `_CPPFLAGS`, `_LDFLAGS`, `_LIBTOOLFLAGS`:: Override corresponging AM_xx variable `_LDADD`:: Add dependencies that are used during linking. For PROGRAMS only. They will be added to linker command line. `_LIBADD`:: Add dependencies that are used during linking. For LIBRARIES/LTLIBRARIES only. They will be added to linker command line. `_AR`:: Overrides $(AR) $(ARFLAGS). For LIBRARIES only. .Example: ------------------- bin_PROGRAMS = prog prog_SOURCE = main.c util.c util.h prog_CFLAGS = $(GTK_CFLAGS) prog_LDADD = $(GTK_LIBS) ------------------- == Global variables == They can be set before `antimake.mk` inclusion to change build behaviour. EXTRA_DIST:: Additional files to include in source archive. CLEANFILES:: Additional files to `make clean`. DISTCLEANFILES:: Additional files to `make distclean`. MAINTAINERCLEANFILES:: Additional files to `make maintainer-clean`. SUBDIRS:: Subdirectories of current directory where Make needs to be recursively launched. If subdirectory `Makefile` is Antimake-base, it should set `SUBLOC`. SUBLOC:: Current diretory location in overall source tree. This can stay unset in top directory. Needed for subdirectiories entered with `SUBDIRS` to find its position in source tree. DIST_SUBDIRS:: Subdirs that only `make dist`, `make distclean` and `make maintainer-clean` will enter. EMBED_SUBDIRS:: Subdirectories that are built non-recursively: they need to contain `Makefile.am` that contains makefile-fragment with Antimake syntax that describes local targets using relative filenames. The fragment is included in main makefile and file and variable names are converted and merged with top-level targets. AM_FEATURES:: List of extensions to load. Extensions are Makefile fragments that are loaded before actual rules are generated, so they can change or add targets. === More details on EMBED_SUBDIRS === It acts like `include $(dir)/Makefile.am` for each directory, except it converts file and variable names. Example: --------------------- Makefile: EMBED_SUBDIRS = src src/Makefile.am: bin_PROGRAMS = hello hello_SOURCES = main.c hello_CPPFLAGS = -I./include --------------------- Conversion results as if top-level `Makefile` had contained following rows: ---------------------- bin_PROGRAMS += src/hello src_hello_SOURCES = src/main.c src_hello_CPPFLAGS = -I./src/include ---------------------- Variables, where file names are converted: * SUBDIRS, DIST_SUBDIRS, EMBED_SUBDIRS * DISTFILES, CLEANFILES, DISTCLEANFILES, MAINTAINERCLEANFILES * target lists * _SOURCES, _LDADD, _LIBADD Variables, where -L and -I flags are converted: * _CFLAGS * _CPPFLAGS * _LDFLAGS Makefile should be written in a way that those conversions would be enough. === Global variables for current location === * srcdir, builddir - relative path to source dir and build dir. * top_srcdir, top_builddir - relative path to top-level source and build dir. * abs_srcdir, abs_builddir - absolute path to source and build dir * abs_top_srcdir, abs_top_builddir - absolute path to top-level source and build dir * nosub_top_srcdir, nosub_top_builddir - relative path from top of builddir to srcdir and builddir. === Global variables that target can override === - AM_CPPFLAGS - AM_CFLAGS - AM_LDFLAGS - AM_LIBTOOLFLAGS - AM_DEFS - AM_MAKEFLAGS === Global variables from autoconf === These variables come usually from autoconf, but also have reasonable defaults: CC, DEFS, CPPFLAGS, CFLAGS, LDFLAGS, LIBS, LIBTOOL, LIBTOOLFLAGS, AR, ARFLAGS, RANLIB, CXX, CXXFLAGS, INSTALL, MKDIR_P, LN_S === Global variables for extending Antimake === AM_DIST_DEFAULT:: Default format(s) for `make dist` target. One or more of: `gzip`, `bzip2`, `xz`, `zip`. Default: `gzip`. AM_DESTINATIONS:: Additional directory names to consider as valid destinations. Expects corresponding `dir`-variable to be set. AM_SMALL_PRIMARIES:: Additional single-file primaries. (Builtin: HEADERS, SCRIPTS, DATA, MANS) AM_BIG_PRIMARIES:: Additional primaries built from objects. (Builtin: PROGRAMS, LIBRARIES, LTLIBRARIES) AM_LANGUAGES:: Additional language names. Antimake expects variables `AM_LANG_$(name)_SRCEXTS`, `AM_LANG_$(name)_COMPILE` and `AM_LANG_$(name)_LINK` to be set. === Variables for command-line usage === DESTDIR:: Relocate installation root. AM_TRACE:: Turns on function-call debug info. Can be set from command-line. === Hacking variables === GNUMAKE380, GNUMAKE381, GNUMAKE382:: If we have at least that version of GNU Make. GNUMAKE380 is always set, others may not be. If Makefile uses features from newer GNU Make it would be good idea to use those flags and error out with clear error message, instead having mysterious failures. === Libtool flags === Useful http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html[Libtool] flags that can be put int tgt_LDFLAGS for a LTLIBRARY: * -export-dynamic * -export-symbols symfile * -export-symbols-regex regex * -module See libtool http://www.gnu.org/software/libtool/manual/html_node/Versioning.html["Versioning"] chapter about those: * -avoid-version * -version-info current[:revision[:age]] * -version-number major[:minor[:revision]] * -release major[:minor[:revision]] == Top-level pseudo-targets == === all === The default target when no other target is given on command-line. Builds all target files. ==== Simple targets ==== These are simple - either the file already exists, or the user needs to give build command. ==== Object-based targets ==== The targets in primaries PROGRAMS, LIBRARIES and LTLIBRARIES consist of multiple source files that need to be compiled into objects. Then the objects need to be linked into final target. The process is roughly following: . Dependencies are built (_LDADD, _LIBADD, _DEPENDENCIES). . Source list is filtered for extensions that can be compiled into object files, object file list is created based on them. The rest of files are used and dependencies for target, but otherwise ignored. . Object files are built. . Linker is picked based on source files - as there can be files in multiple languages, the most advanced language wins (the one that appears later in `AM_LANGUAGES`) . Final executable is linked. === install === Install all targets to their destination directories, which is mentioned in their target list variable name. Eg. `bin_PROGRAMS` will be installed to `$(bindir)`. If destination is named `noinst`, it will not be installed. If the flag `nobase` is given, the relative filename is kept, otherwise basename is taken and it will appear directly under destination directory. .Example: ------ include_HEADERS = func1.h lib/func2.h # Files will end up in: # $(includedir)/func1.h # $(includedir)/func2.h nobase_include_HEADERS = func1.h lib/func2.h # Files will end up in: # $(includedir)/func1.h # $(includedir)/lib/func2.h ------ === clean === - Remove files in `$(CLEANFILES)` - Remove built objects. - Remove target files, unless they are marked as `dist`. (Note: `HEADERS` primary is `dist` by default, all other are `nodist`) === distclean === - Remove files in `$(DISTCLEANFILES)` - Remove sources tagged with `nodist`. All sources as `dist` by default. === maintainer-clean === - Remove files in `$(MAINTAINERCLEANFILES)` === help === Describe top-level targets. === am-test === Regression test for low-level Antimake functions. === am-debug === Show Antimake internal state. == FEATURES == Done: - Big primaries: PROGRAMS, LIBRARIES, LTLIBRARIES - Small primaries: DATA, SCRIPTS, MANS, HEADERS - Flags: base nobase dist nodist noinst EXTRA - Target vars: SOURCES, CPPFLAGS, CFLAGS, LDFLAGS, LDADD/LIBADD - Separate build dir - Per-target objects - Languages: C, CXX - SUBDIRS, DIST_SUBDIRS - EMBED_SUBDIRS Todo: - Improve docs - Standardize and document how to extend - Deps with non-gcc? - Long if-s to support `O=` seems to break GNU Make 3.80. Drop `O=` or drop 3.80? Probably out of scope: - `make uninstall` - `make distcheck` - `make dist` from separate build dir - `install-(exec|data)-hook` - based on dir not primary - Default file list for `EXTRA_DIST`. (Problem: distclean / maintainer-clean) Definitely out of scope: - automake conditionals - automake extras (autoconf macros, ltdl) - automake nanny mode (gnu/gnits) == SEE ALSO == GNU Make Reference: http://www.gnu.org/software/make/manual/make.html#Quick-Reference[] Recursive Make Considered Harmful: http://miller.emu.id.au/pmiller/books/rmch/[] Paul's Rules of Makefiles: http://make.mad-scientist.us/rules.html[] Small BSD-ish build system: https://webkeks.org/hg/buildsys/[] GNU Make Standard Library: http://sourceforge.net/projects/gmsl/[] pgbouncer-1.24.1/lib/mk/antimake.mk0000755000175000000000000012207514777762223014021 00000000000000#! /usr/bin/make -f # # antimake.mk - automake syntax with GNU Make # # Copyright (c) 2011 Marko Kreen # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Goals: # - Clean user Makefiles, by using automake syntax # - Clean output during build # - Optional ties with `autoconf` and `libtool` # - Automatic dependency tracking # - Avoid separate build step for Makefiles # - No extra tools needed except GNU Make # Usage without autoconf: # - copy antimake.mk into source dir, then: include antimake.mk # - copy/link antimake.mk into PATH, then: include $(shell antimake.mk) # # Usage with autoconf: # - Copy to antimake.mk.in at top dir, then process with autoconf # to antimake.mk and include that one in Makefiles. # # - Have config.mak.in that also includes antimake.mk. # Suggestion: the separate file should include antimake.mk # using $(abs_top_srcdir) to support separate build dir. # # - Include config and antimake.mk separately in user Makefiles ## ## Startup hacks ## # detect GNU make version, confuse others $(eval GNUMAKE380=1) GNUMAKE381=$(or ,$(GNUMAKE380)) define GNUMAKE382 = $(GNUMAKE381) endef # give error of too old ifeq ($(GNUMAKE381),) $(error GNU Make 3.81+ required) endif # extra targets if this file is executed directly ifeq ($(words $(MAKEFILE_LIST)), 1) .PHONY: show-location show-config # default: print location. For "include $(shell antimake.mk)"-style usage. show-location: @echo $(MAKEFILE_LIST) # show autoconfigurable variables show-config: @grep '@[^ ]*@$$' $(MAKEFILE_LIST) endif ## ## Allow this file to be processed through autoconf ## # # to extract autoconfigurable values: # $ grep '@[^ ]*@$' antimake.mk > config.mk.in # $ antimake.mk show-config > config.mk.in # ifneq ($(filter-out @%,@PACKAGE_NAME@),) PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PORTNAME = @PORTNAME@ EXEEXT = @EXEEXT@ HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ # C language CC = @CC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ WFLAGS = @WFLAGS@ # linking LD = @LD@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ # static and shared libs AR = @AR@ ARFLAGS = @ARFLAGS@ RANLIB = @RANLIB@ LIBTOOL = @LIBTOOL@ # other tools SHELL = @SHELL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ MKDIR_P = @MKDIR_P@ SED = @SED@ AWK = @AWK@ GREP = @GREP@ EGREP = @EGREP@ STRIP = @STRIP@ # install locations prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datarootdir = @datarootdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ docdir = @docdir@ mandir = @mandir@ libdir = @libdir@ localedir = @localedir@ pkgdatadir = @pkgdatadir@ pkgconfigdir = @pkgconfigdir@ aclocaldir = @aclocaldir@ # autoconf values for top dir abs_top_srcdir ?= @abs_top_srcdir@ abs_top_builddir ?= @abs_top_builddir@ nosub_top_srcdir ?= @top_srcdir@ nosub_top_builddir ?= @top_builddir@ endif # end of @xx@ values ## ## In case of missing autoconf values, provide sane defaults ## PACKAGE_NAME ?= package PACKAGE_TARNAME ?= $(PACKAGE_NAME) PACKAGE_VERSION ?= 0.0 PACKAGE_STRING ?= $(PACKAGE_NAME) $(PACKAGE_VERSION) PACKAGE_URL ?= PACKAGE_BUGREPORT ?= PORTNAME ?= unix EXEEXT ?= HAVE_CC_DEPFLAG ?= yes # C language CC ?= cc CPP ?= cpp CPPFLAGS ?= CFLAGS ?= -O -g DEFS ?= # warning flags are keps separately to allow easy override WFLAGS ?= -Wall # add them to main flags now CFLAGS += $(WFLAGS) # linking LD ?= ld LDFLAGS ?= LIBS ?= # static and shared libs LIBTOOL ?= libtool AR ?= ar ARFLAGS ?= rcs ifeq ($(ARFLAGS),rv) ARFLAGS = rcs endif RANLIB ?= ranlib # other tools SHELL ?= /bin/sh INSTALL ?= install INSTALL_PROGRAM ?= $(INSTALL) INSTALL_SCRIPT ?= $(INSTALL) INSTALL_DATA ?= $(INSTALL) MKDIR_P ?= mkdir -p SED ?= sed AWK ?= awk GREP ?= grep EGREP ?= grep -E STRIP ?= strip # install locations prefix ?= /usr/local exec_prefix ?= ${prefix} bindir ?= ${exec_prefix}/bin includedir ?= ${prefix}/include sbindir ?= ${exec_prefix}/sbin libexecdir ?= ${exec_prefix}/libexec datarootdir ?= ${prefix}/share datadir ?= ${datarootdir} sysconfdir ?= ${prefix}/etc docdir ?= ${datarootdir}/doc/${PACKAGE_TARNAME} mandir ?= ${datarootdir}/man libdir ?= ${exec_prefix}/lib localedir ?= ${datarootdir}/locale pkgdatadir ?= ${datarootdir}/${PACKAGE_TARNAME} pkgconfigdir ?= ${libdir}/pkgconfig aclocaldir ?= ${datarootdir}/aclocal # autoconf values for top dir abs_top_srcdir ?= $(CURDIR) abs_top_builddir ?= $(CURDIR) # make sure nosub vals are not empty ifeq ($(nosub_top_builddir),) nosub_top_builddir = . endif ifeq ($(nosub_top_srcdir),) nosub_top_srcdir = . endif ## ## Variables for user makefiles ## # current subdirectory location from top dir (foo/bar) SUBLOC ?= . # subdirectories in current directory SUBDIRS ?= # extra files for clean targets CLEANFILES ?= DISTCLEANFILES ?= MAINTAINERCLEANFILES ?= # Additional flags for Makefile use, to avoid need # to touch flags coming from autoconf/cmdline AM_DEFS ?= AM_CPPFLAGS ?= AM_CFLAGS ?= AM_LDFLAGS ?= AM_LIBTOOLFLAGS ?= AM_MAKEFLAGS ?= AM_LIBS ?= # libusual sources, for embedded usage USUAL_DIR ?= . # V=1 -> verbose build V ?= 0 # turn on function tracing AM_TRACE ?= # default formats for 'dist' AM_DIST_DEFAULT ?= gzip ## ## Non-user-serviceable area ## # Hacking: # # - Uppercase names are simple (late) variables, lowercase names - targets, # mixedcase - functions that need to be $(call)-ed. # # - Minimal amount of shell should be used here. # # - Minimal amount of := and $(eval) # # - It's useful to indent the expressions for easier understanding. # Later the indendation needs to be removed, as whitespace is significant for Make. # Several functions must not add any extra whitespace. # # GNU Make features in new versions: # # 3.80 - 2002-10-03: base version. $(eval) $(value) $(MAKEFILE_LIST) $(.VARIABLES) $(call fixes) # 3.81 - 2006-04-01: $(or), $(and), $(lastword), $(abspath), $(realpath), $(info), $(flavor) # 3.82 - 2010-07-28: private, undefine, define var := # # This file should use only features from 3.80 ## ## command helpers ## CCLD ?= $(CC) COMPILE ?= $(CC) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LINK ?= $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_AR ?= $(AR) $(ARFLAGS) LIBTOOLCMD ?= $(LIBTOOL) $(LIBTOOLQ) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) RM = rm -f ## ## Internals ## # varables that can be set per-target with target_VAR # they appear as AM_foo. [Not supported: COMPILE] AM_TARGET_VARIABLES += CFLAGS CPPFLAGS LDFLAGS LIBTOOLFLAGS DEFS LIBS # list of language (rather compiler) names AM_LANGUAGES += C AM_BIG_PRIMARIES += LIBRARIES LTLIBRARIES PROGRAMS AM_SMALL_PRIMARIES += HEADERS SCRIPTS DATA MANS # list of destinations per primary AM_DESTINATIONS += bin lib libexec sbin \ data doc include locale man sysconf \ pkgdata pkgconfig aclocal \ noinst EXTRA # primaries where 'dist' is default AM_DIST_PRIMARIES += HEADERS AM_PRIMARIES = $(AM_BIG_PRIMARIES) $(AM_SMALL_PRIMARIES) # distclean does rm -rf on that OBJDIR = .objs # extension for objects OBJEXT = .o # extension for static libraries LIBEXT = .a # files that need to be converted to objects AM_SRCEXTS = $(foreach lang,$(AM_LANGUAGES),$(AM_LANG_$(lang)_SRCEXTS)) # target types - big/small: with/without objects # list of flags, 'noinst' is taken as dest, 'base' is always default AM_FLAGS = base nobase dist nodist ## configure non-defult target params AM_PROGRAMS_InstFunc = ProgInstall AM_LTLIBRARIES_InstFunc = LTLibInstall AM_LTLIBRARIES_OBJEXT = .lo AM_SCRIPTS_InstFunc = ScriptInstall AM_MANS_InstFunc = ManInstall # files to distribute am_DISTFILES := am_FINAL_DISTFILES = $(sort $(am_DISTFILES)) AM_DIST_BASE = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) AM_ALL_TARGETS = ## ## Make dependencies work ## HAVE_CC_DEPFLAG ?= yes ifeq ($(HAVE_CC_DEPFLAG),yes) OBJDEPS = -MD -MP -MT $@ -MF $@.d endif ## ## Quiet by default, 'make V=1' shows commands ## # 1-dir MkDir = $(MKDIR_P) $(1) # 1-fmt, 2-args Printf = printf $(1) $(2) CTX ?= ifeq ($(V), 0) E = @$(call Printf,"%-4s %-8s %s\n","$(CTX)") Q = @ LIBTOOLQ = --silent MAKEFLAGS += --no-print-directory else E = @true Q = LIBTOOLQ = --silent endif ## ## libtool activation ## # libtool activates when detects %.lo / %.la pattern LTCOMPILE = $(if $(filter %.lo,$@),$(LIBTOOLCMD) --mode=compile) LTLINK = $(if $(filter %.la %.lo,$^),$(LIBTOOLCMD) --mode=link) LTCLEAN = $(LIBTOOLCMD) --mode=clean ## ## Default setup for C ## AM_LANG_C_SRCEXTS = .c define AM_LANG_C_COMPILE $(E) "CC" $< $(Q) $(LTCOMPILE) $(COMPILE) $(OBJDEPS) -c -o $@ $< endef define AM_LANG_C_LINK $(E) "CCLD" $@ $(Q) $(LTLINK) $(LINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) endef ## ## Various other shortcuts ## define ar_lib $(E) "AR" $@ $(Q) $(AM_AR) $@ $^ $(E) "RANLIB" $@ $(Q) $(RANLIB) $@ endef # 1 - dir define ProgInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_PROGRAM) $< $(1) endef # 1 - dir define ScriptInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_SCRIPT) $< $(1) endef # 1 - dir define DataInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_DATA) $< $(1) endef # 1 - dir, add manX subdir ManInstall = $(call DataInstall,$(1)/man$(call LastWord,$(subst ., ,$<))) # 1 - dir define LTLibInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(LIBTOOLCMD) --mode=install $(INSTALL) $< $(1) endef ## ## Create .srcext -> .obj mapping for a language ## # 1-tgt, 2-name, 3-srcext define LangObjTarget $(trace3) $$(OBJDIR)/$(1)/%$(OBJEXT): %$(3) @$$(call MkDir,$$(dir $$@)) $$(AM_LANG_$(2)_COMPILE) $$(OBJDIR)/$(1)/%.lo: %$(3) @$$(call MkDir,$$(dir $$@)) $$(AM_LANG_$(2)_COMPILE) endef # 1=tgt, 2=name define LangSetup $(trace2) $(foreach ext,$(AM_LANG_$(2)_SRCEXTS),$(call LangObjTarget,$(1),$(2),$(ext))$(NewLine)) endef ## ## Utility functions ## # for function debugging, put them at the start of body ifdef AM_TRACE trace1=$(warning $0('$1')) trace2=$(warning $0('$1','$2')) trace3=$(warning $0('$1','$2','$3')) trace4=$(warning $0('$1','$2','$3','$4')) trace5=$(warning $0('$1','$2','$3','$4','$5')) trace6=$(warning $0('$1','$2','$3','$4','$5','$6')) trace7=$(warning $0('$1','$2','$3','$4','$5','$6','$7')) trace8=$(warning $0('$1','$2','$3','$4','$5','$6','$7','$8')) trace9=$(warning $0('$1','$2','$3','$4','$5','$6','$7','$8','$9')) endif # for use inside $(eval) IFDEF = ifdef IFEQ = ifeq IFNEQ = ifneq ELSE = else ENDIF = endif # returns 'true' if $1==$2 Eq = $(if $(1)$(2),$(if $(findstring $(1),$(2)),$(if $(findstring $(2),$(1)),true)),true) Not = $(if $(1),,true) Neq = $(call Not,$(call Eq,$(1),$(2))) # replace [-./] with '_' CleanName = $(subst /,_,$(subst -,_,$(subst .,_,$(1)))) # return last word from word list LastWord = $(if $(1),$(word $(words $(1)),$(1))) Empty = Space = $(Empty) $(Empty) # twice to unconfuse syntax hiliters SQuote = ' SQuote = ' define NewLine endef # quote str for shell ShellQuote = '$(subst $(SQuote),'\$(SQuote)',$(1))' # replace extensions # 1-src ext list # 2-target ext # 3-source list ReplaceExts = $(foreach ext,$(1),$(patsubst %$(ext),%$(2),$(filter %$(ext),$(3)))) # objs with objdir from source list (1-cleantgt, 2-src list) SourceObjs = $(trace1)$(call SourceObjsExt,$(1),$(OBJEXT),$(2)) # objs with objdir from source list # 1-cleantgt, 2-objext, 3-srcs list SourceObjsExt = $(addprefix $(call JoinPath,$(OBJDIR),$(1))/, $(call ReplaceExts,$(AM_SRCEXTS),$(2),$(3))) # dependency files from object files, must match OBJDEPS DepFiles = $(sort $(wildcard $(addsuffix .d,$(1)))) # per-target var override, 1=target, 2=varname # if foo_VAR exists, expand to: # build_foo install_foo clean_foo: AM_VAR = $(foo_VAR) # 1-tgt, 2-var, 3-final TgtVar2 = $(3): AM_$(2) = $$($(1)_$(2))$(NewLine) TgtVar = $(if $($(1)_$(2)),$(call TgtVar2,$(1),$(2),$(3))) # loop TgtVar over AM_TARGET_VARIABLES, 1=target, 2-final VarOverride = $(foreach var,$(AM_TARGET_VARIABLES),$(call TgtVar,$(1),$(var),$(2))) # check if actual target (.h, .exe) is nodist based on primary and flags # 1-prim 2-flags TargetNoDist = $(strip $(if $(filter nodist,$(2)), \ true, \ $(if $(filter dist,$(2)), \ , \ $(filter-out $(AM_DIST_PRIMARIES),$(1))))) # return sources that match language # 1-lang # 2-sources LangFiles = $(filter $(addprefix %,$(AM_LANG_$(1)_SRCEXTS)),$(2)) # return list of langs that match sources. # 1-sources LangList = $(strip $(foreach lang,$(AM_LANGUAGES),$(if $(call LangFiles,$(lang),$(1)),$(lang)))) # 1-sources LinkLangList = $(foreach lang,$(call LangList,$(1)),$(if $(AM_LANG_$(lang)_LINK),$(lang))) # pick linker variable based on sources, fallback to C # 1-sources DetectLinkVar = AM_LANG_$(call LastWord,C $(call LinkLangList,$(1)))_LINK # convert 'foo/bar' -> '../..' UpDirStep1 = $(subst /, ,$(1)) UpDirStep2 = $(foreach dir,$(call UpDirStep1,$(1)),../) UpDirStep3 = $(subst / ,/,$(call UpDirStep2,$(1))) UpDirStep4 = $(patsubst %/,%,$(call UpDirStep3,$(1))) UpDir = $(if $(filter-out .,$(1)),$(call UpDirStep4,$(1)),.) # # AntiMake requires that joining clean names must result in clean names. # # Thus: # JoinPath(.,foo) -> foo # JoinPath(foo,/abs) -> /abs # JoinPath(a/b,../c) -> a/c # JoinPath(a,../../b/c) -> ../b/c # # 1-path, 2-last name : foo => . | /foo => / | foo/bar => foo CutLastName = $(if $(filter $(2),$(1)),.,$(if $(filter /$(2),$(1)),/,$(patsubst %/$(2),%,$(1)))) # 1-path component, remove last elem : CutLast = $(call CutLastName,$(1),$(lastword $(subst /, ,$(1)))) # 1/2 : actual place where / is put JoinPathFinal = $(if $(filter /,$(1)),$(1)$(2),$(1)/$(2)) # 1/2 : second starts with ../, remove it and last component of $(1) JoinPath5 = $(call JoinPath,$(call CutLast,$(1)),$(patsubst ../%,%,$(2))) # 1/2: check if first ends with .. JoinPath4 = $(if $(filter .. %/..,$(1)),$(call JoinPathFinal,$(1),$(2)),$(call JoinPath5,$(1),$(2))) # 1/2 : check if second starts with ..; otherwise join JoinPath3 = $(if $(filter ../%,$(2)),$(call JoinPath4,$(1),$(2)),$(call JoinPathFinal,$(1),$(2))) # 1/2 : skips component if '.' JoinPath2 = $(if $(filter-out .,$(1)),$(if $(filter-out .,$(2)),$(call JoinPath3,$(1),$(2)),$(1)),$(2)) # 1/2 : check if b is absolute, otherwise fix minor problems JoinPath = $(trace2)$(if $(filter /%,$(2)),$(2),$(call JoinPath2,$(if $(filter /,$(1)),$(1),$(patsubst %/,%,$(1))),$(patsubst ./%,%,$(2)))) ## ## Parse target list variables ## ## pick out components from name, call function # 1-varname, 2-words, 3-func, 4-func arg # func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg ParseName = $(call $(3),$(1),$(filter $(AM_PRIMARIES),$(2)),$(filter $(AM_DESTINATIONS),$(2)),$(filter $(AM_FLAGS),$(2)),$(4)) ForEachList = $(foreach var,$(2),$(call ParseName,$(var),$(subst _, ,$(var)),$(1),$(3))) ## try reconstruct name, if fails, its a random variable # 1-var, 2-prim,3-dest,4-flags CheckName = $(if $(call Eq,$(subst _, ,$(1)),$(strip $(4) $(call LastWord,$(3)) $(call LastWord,$(2)))),$(1)) ## also check if variable is filled # 1-var, 2-prim,3-dest,4-flags CheckNameFull = $(if $(call CheckName,$(1),$(2),$(3),$(4)),$(if $($(1)),$(1))) ## ## Loop over targets in list variables ## ## call function on parsed target # 1-var, 2-prim, 3-dest, 4-flags, 5-func # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags ForEachTarget2 = $(foreach tgt,$($(1)),$(call $(5),$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) ## ForEachTarget: call function on all targets in lists # 1-func, 2- var list # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags ForEachTarget = $(call ForEachList,ForEachTarget2,$(2),$(1)) ## EMBED_SUBDIRS relocations ## add subdir to files # 1-subdir, 2-file list RelocFiles = $(foreach f,$(2),$(if $(filter -%,$(f)),$(f),$(call JoinPath,$(1),$(f)))) # 1-dir, 2-pfx, 3-full RelocOneFlag2 = $(2)$(call JoinPath,$(1),$(patsubst $(2)%,%,$(3))) # 1-dir, 2-flag RelocOneFlag = $(if $(filter -L%,$(2)), \ $(call RelocOneFlag2,$(1),-L,$(2)), \ $(if $(filter -I%,$(2)), \ $(call RelocOneFlag2,$(1),-I,$(2)), \ $(2))) ## Relocate relative files, relative -I/-L, ignore -* # 1-dir, 2- flaglist RelocFlags = $(strip $(if $(filter-out .,$(1)), \ $(foreach flg,$(2),$(call RelocOneFlag,$(1),$(flg))), \ $(2))) ## Separate build dir relocation ## non-local source dir: -Isrc/include -> -Isrc/include -I$(srcdir)/src/include # 1-srcdir, 2-flag list FixIncludes = $(strip $(if $(filter-out .,$(1)), \ $(foreach flg,$(2),$(call FixIncludes2,$(1),$(flg))), \ $(2))) # 1-dir, 2-flg FixIncludes2 = $(if $(filter -I%,$(2)), \ $(call FixIncludes3,$(1),$(patsubst -I%,%,$(2))), \ $(2)) # 1-dir, 2-orig dir FixIncludes3 = -I$(2) -I$(call JoinPath,$(srcdir),$(2)) ## ## Makefile fragments ## ### fill values # abs_top_srcdir, abs_top_builddir # nosub_top_builddir, nosub_top_srcdir # 1 - subdir define SetDirs abs_builddir := $$(call JoinPath,$$(abs_top_builddir),$(1)) abs_srcdir := $$(call JoinPath,$$(abs_top_srcdir),$(1)) top_builddir := $$(call UpDir,$(1)) top_srcdir := $$(call JoinPath,$$(top_builddir),$$(nosub_top_srcdir)) builddir := . $(IFEQ) ($$(nosub_top_srcdir),$$(nosub_top_builddir)) srcdir := . $(ELSE) srcdir := $$(call JoinPath,$$(top_srcdir),$(1)) $(ENDIF) endef ## ## Embedded subdirs ## # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags define RelocBigTarget $(trace5) # move vars: $(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$$(am_PFX)_$(1)_$(var) := $$($(1)_$(var))) # move and relocate EXTRA_$$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$(EXTRA_$(1)_SOURCES)) $$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$($(1)_SOURCES)) $$(am_PFX)_$(1)_DEPENDENCIES := $$(call RelocFiles,$$(am_DIR),$$($(1)_DEPENDENCIES)) $$(am_PFX)_$(1)_LDADD := $$(call RelocFiles,$$(am_DIR),$$($(1)_LDADD)) $$(am_PFX)_$(1)_LIBADD := $$(call RelocFiles,$$(am_DIR),$$($(1)_LIBADD)) $$(am_PFX)_$(1)_CFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CFLAGS)) $$(am_PFX)_$(1)_CPPFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CPPFLAGS)) $$(am_PFX)_$(1)_LDFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_LDFLAGS)) # clean vars $(1)_SOURCES = $(1)_LDADD = $(1)_LIBADD = $(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$(1)_$(var) = ) endef ## pick actual func # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags define RelocTarget $(trace5) $(if $(filter $(AM_BIG_PRIMARIES),$(3)),$(call RelocBigTarget,$(1),$(2),$(3),$(4),$(5))) endef ## relocate target list # func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg define RelocTList $(trace5) # detect top and subdir target conflict - it's easier to detect # and error out than to work around the rare case $(IFNEQ) (,$$(filter $(2),$$(AM_BIG_PRIMARIES))) $(IFEQ) (.,$$(am_DIR)) am_TOP_NAMES += $$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))) $(ELSE) $(IFNEQ) (,$$(filter $$(am_TOP_NAMES),$$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))))) $$(error $$(NewLine)$$(NewLine)\ *** Target names used in top Makefile cannot be re-used in embedded Makefiles. $$(NewLine)\ *** The target variables (eg. _SOURCES) conflict is not handled yet) $(ENDIF) $(ENDIF) $(ENDIF) # move value under real_% $(IFEQ) ($(real_$(1)),) real_$(1) := $(ENDIF) real_$(1) += $$(call RelocFiles,$$(am_DIR),$$($(1))) $(1) = # remember in proper list $(IFEQ) ($(3),EXTRA) am_EXTRA_TARGETLISTS += real_$(1) $(ELSE) am_TARGETLISTS += real_$(1) $(ENDIF) endef ## process included values # 1-dir, 2-pfx, 3-tlist define EmbedProcess $(trace3) $(IFNEQ) ($$(filter $(1),$$(am_EMBED_DONE)),) $$(error Double entry in EMBED_SUBDIRS: $(1)) $(ENDIF) # init local vars am_DIR := $(1) am_LOC := $$(call JoinPath,$$(SUBLOC),$(1)) am_PFX := $(2) am_EMBED_DONE += $(1) # reloc & save vars am_DISTFILES += $$(call RelocFiles,$$(am_DIR),$$(EXTRA_DIST)) am_CLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(CLEANFILES)) am_DISTCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(DISTCLEANFILES)) am_MAINTAINERCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(MAINTAINERCLEANFILES)) am_EMBED_TODO += $$(call RelocFiles,$$(am_DIR),$$(EMBED_SUBDIRS)) am_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(SUBDIRS)) am_DIST_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(DIST_SUBDIRS)) # clean vars for new dir EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = EMBED_SUBDIRS = SUBDIRS = DIST_SUBDIRS = $(call SetDirs,$(call JoinPath,$(SUBLOC),$(1))) $(call ForEachTarget,RelocTarget,$(3)) $(call ForEachList,RelocTList,$(3)) endef ## read Makefile.am, process it # 1 - dir DoEmbed = $(trace1)$(strip \ $(if $(wildcard $(am_srcdir)/$(1)/Makefile.am), \ $(eval include $(am_srcdir)/$(1)/Makefile.am $(NewLine)) \ $(eval $(call EmbedProcess,$(1),$(call CleanName,$(1)),$(AM_NONEXTRA_TLISTS) $(AM_EXTRA_TLISTS))), \ $(error $(SUBLOC)/Makefile failure: $(call JoinPath,$(SUBLOC),$(1)/Makefile.am) not found.))) ## ## Fragments that build targets ## # Note that variable initialization order is important here # as some of them will be used immediately. ## ## Install target object ## # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define InstallTarget $(trace5) $(1)_DEST := $$(if $$($(4)dir),$$($(4)dir),$$(error '$(4)dir' empty))$(if $(filter nobase,$(5)),/$(dir $(2))) $(1)_InstFunc := $$(if $$(AM_$(3)_InstFunc),$$(AM_$(3)_InstFunc),DataInstall) # actual installation .PHONY: install_$(1) install: install_$(1) install_$(1): $(2) $$(call $$($(1)_InstFunc),$$(DESTDIR)$$($(1)_DEST)) # hack to pass -rpath to LTLIBRARIES on build time (1) $(2): AM_DEST = $$($(1)_DEST) # simple uninstall - just remove files .PHONY: uninstall_$(1) uninstall: uninstall_$(1) uninstall_$(1): $$(RM) $$(DESTDIR)$$($(1)_DEST)/$$(notdir $(2)) endef # hack to pass -rpath to LTLIBRARIES on build time (2) %.la: AM_LT_RPATH = $(if $(AM_DEST),-rpath $(AM_DEST)) ## ## Rules for big target ## # 1-varname, 2-ifset, 3-ifnotset IfSet = $(if $(filter-out undefined,$(flavor $(1))),$(2),$(3)) # 1-clean, 2-raw, 3-prim PROGRAMS_Final = $(if $($(1)_EXT),$(2)$($(1)_EXT),$(2)$(EXEEXT)) # 1-clean, 2-raw, 3-prim LIBRARIES_Final = $(if $($(1)_EXT),$(2)$($(1)_EXT),$(patsubst %.a,%$(LIBEXT),$(2))) # calculate target file name # 1-clean, 2-raw, 3-prim FinalTargetFile = $(call IfSet,$(3)_Final,$(call $(3)_Final,$(1),$(2),$(3)),$(2)$($(1)_EXT)) # 1-objs FixObjs = $(patsubst %.a,%$(LIBEXT),$(1)) # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define BigTargetBuild $(trace5) AM_ALL_TARGETS += $(1) $(1)_ALLSRCS := $$($(1)_SOURCES) $$(EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) # calculate OBJS from SOURCES $(1)_OBJEXT := $$(if $$(AM_$(3)_OBJEXT),$$(AM_$(3)_OBJEXT),$$(OBJEXT)) $(1)_OBJS := $$(call SourceObjsExt,$(1),$$($(1)_OBJEXT), \ $$($(1)_SOURCES) $$(nodist_$(1)_SOURCES)) $(1)_OBJS_CLEAN := $$($(1)_OBJS) # include additional objects, move flags to _LIBS $(IFEQ) ($(3),PROGRAMS) $(1)_OBJS += $$(filter-out -%,$$($(1)_LDADD)) $(1)_LIBS += $$(filter -%,$$($(1)_LDADD)) $(ELSE) $(1)_OBJS += $$(filter-out -%,$$($(1)_LIBADD)) $(1)_LIBS += $$(filter -%,$$($(1)_LIBADD)) $(ENDIF) # autodetect linker, unless given $(IFEQ) ($($(1)_LINK),) $(1)_LINKVAR := $$(call DetectLinkVar,$$($(1)_ALLSRCS)) $(ELSE) $(1)_LINKVAR := $(1)_LINK $(ENDIF) # calculate target file name $(1)_FINAL = $(call FinalTargetFile,$(1),$(2),$(3)) # hook libtool into LTLIBRARIES cleanup $(IFEQ) ($(3),LTLIBRARIES) $(1)_RM = $$(LTCLEAN) $$(RM) $(ELSE) $(1)_RM = $$(RM) $(ENDIF) # fix includes in case of separate build dir $(1)_CPPFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CPPFLAGS)) $(1)_CFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CFLAGS)) # load dependencies -include .dummy. $$(call DepFiles, $$($(1)_OBJS)) # actual build, clean & install targets .PHONY: build_$(1) clean_$(1) # allow target-specific variables $$(eval $$(call VarOverride,$(1),$(call FinalTargetFile,$(1),$(2),$(3)))) # build and clean by default, unless flagged EXTRA $(IFNEQ) ($(4),EXTRA) all: build_$(1) $(ENDIF) clean: clean_$(1) # _DEPENDENCIES and nodist_SOURCES must exist before build starts. $$(call FixObjs,$$($(1)_OBJS)): $$($(1)_DEPENDENCIES) $$(nodist_$(1)_SOURCES) build_$(1): $$($(1)_FINAL) $$($(1)_FINAL): $$(call FixObjs,$$($(1)_OBJS)) @$$(call MkDir,$$(dir $$@)) $$($(if $(filter LIBRARIES,$(3)),ar_lib,$$($(1)_LINKVAR))) clean_$(1): $$(E) "CLEAN" "$$($(1)_FINAL)" $$(Q) $$($(1)_RM) -- $$($(1)_OBJS_CLEAN) $(if $(call TargetNoDist,$(3),$(5)),$$($(1)_FINAL)) DISTCLEANFILES += $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) $(foreach lang,$(AM_LANGUAGES),$(call LangSetup,$(1),$(lang))) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define BigTargetDist am_DISTFILES += $$(filter-out $$(nodist_EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES),$$($(1)_SOURCES) \ $$(EXTRA_$(1)_SOURCES)) $(if $(call TargetNoDist,$(3),$(5)),,$$($(1)_FINAL)) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define MakeBigTarget $(trace5) # build if first time $(IFEQ) ($(filter $(1),$(AM_ALL_TARGETS)),) $(call BigTargetBuild,$(1),$(2),$(3),$(4),$(5)) $(call BigTargetDist,$(1),$(2),$(3),$(4),$(5)) $(ELSE) # allow only EXTRA be double $(IFNEQ) ($(4),EXTRA) $$(error Target '$2' described listed several times) $(ENDIF) $(ENDIF) # call InstallTarget, for dest != (EXTRA, noinst) $(IFEQ) ($(filter EXTRA noinst,$(4)),) $(call InstallTarget,$(1),$$($(1)_FINAL),$(3),$(4),$(5)) $(ENDIF) endef ## ## Rules for small target ## # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define MakeSmallTarget $(trace5) AM_ALL_TARGETS += $(1) # should the target file be distributed or cleaned? $(IFEQ) ($(call TargetNoDist,$(3),$(5)),) am_DISTFILES += $(2) $(ELSE) CLEANFILES += $(2) $(ENDIF) # build if not EXTRA $(IFNEQ) ($(4),EXTRA) all: $(2) # install if not EXTRA or noinst $(IFNEQ) ($(4),noinst) $(call InstallTarget,$(1),$(2),$(3),$(4),$(5)) $(ENDIF) $(ENDIF) endef ## ## Fill GNU-style vars for subdir ## # preferred to top_srcdir/top_builddir topdir = $(top_builddir) ifneq ($(nosub_top_builddir),.) $(error Non-local builddir not supported) endif # initial locaton vars $(eval $(call SetDirs,$(SUBLOC))) ifneq ($(nosub_top_srcdir),$(nosub_top_builddir)) # use VPATH to find non-local sources VPATH += $(srcdir) # fix includes AM_CPPFLAGS := $(call FixIncludes,$(srcdir),$(AM_CPPFLAGS)) AM_CFLAGS := $(call FixIncludes,$(srcdir),$(AM_CFLAGS)) endif ## ## O= ## if given, create wrapper makefiles in target dir ## that include makefiles from source dir, then run ## make from target dir. ## ifneq ($(O),) # 1-makefile define WrapMakeFileCmd @$(call MkDir,$(dir $(O)/$(1))) @$(call Printf,'%s\n%s\n%s\n%s\n%s\n', \ 'abs_top_srcdir = $(CURDIR)' \ 'abs_top_builddir = $(call JoinPath,$(CURDIR),$(O))' \ 'nosub_top_srcdir = $(call UpDir,$(O))' \ 'nosub_top_builddir = .' \ 'include $(abs_top_srcdir)/$(1)') \ > $(O)/$(1) endef # 1-makefile WrapMakeFile = $(if $(wildcard $(O)/$(1)),,$(call WrapMakeFileCmd,$(1))$(NewLine)) # redirect whatever rule was given .PHONY: all $(MAKECMDGOALS) all $(filter-out all,$(MAKECMDGOALS)): $(if $(wildcard $(O)),,$(error O=$(O): Directory '$(O)' does not exist)) $(foreach mk,$(filter-out /%,$(MAKEFILE_LIST)),$(call WrapMakeFile,$(mk))) $(Q) $(MAKE) O= -C $(O) $(MAKECMDGOALS) # O=empty, this is main makefile else ## ## main targets, tie them with subdir and local targets ## # disable random rules .SUFFIXES: all: sub-all all-local clean: sub-clean clean-local install: sub-install install-local uninstall: sub-uninstall uninstall-local distclean: sub-distclean distclean-local maintainer-clean: sub-maintainer-clean maintainer-clean-local .PHONY: all clean install dist distclean maintainer-clean # -local are empty targets by default .PHONY: all-local clean-local install-local uninstall-local distclean-local maintainer-clean-local all-local clean-local install-local uninstall-local distclean-local maintainer-clean-local: ## ## Actual embedding starts ## AM_ALL_TLISTS2 = $(filter $(addprefix %,$(AM_PRIMARIES)),$(.VARIABLES)) AM_ALL_TLISTS = $(call ForEachList,CheckName,$(AM_ALL_TLISTS2)) AM_NONEXTRA_TLISTS = $(filter-out EXTRA_%,$(AM_ALL_TLISTS)) AM_EXTRA_TLISTS = $(filter EXTRA_%,$(AM_ALL_TLISTS)) am_srcdir := $(srcdir) am_DIR := . am_PFX := am_TARGETLISTS := am_EXTRA_TARGETLISTS := am_TOP_NAMES := # move top-level targets away $(eval $(call ForEachList,RelocTList,$(AM_NONEXTRA_TLISTS))) $(eval $(call ForEachList,RelocTList,$(AM_EXTRA_TLISTS))) am_SUBDIRS := $(SUBDIRS) am_DIST_SUBDIRS := $(DIST_SUBDIRS) am_DISTFILES := $(EXTRA_DIST) am_CLEANFILES := $(CLEANFILES) am_DISTCLEANFILES := $(DISTCLEANFILES) am_MAINTAINERCLEANFILES := $(MAINTAINERCLEANFILES) am_EMBED_NOW := $(EMBED_SUBDIRS) am_EMBED_DONE := am_EMBED_TODO := EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = SUBDIRS = DIST_SUBDIRS = EMBED_SUBDIRS = $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(if $(am_EMBED_NOW),$(error EMBED_SUBDIRS recursion limit reached...)) # embedding done, move variables back $(eval $(call SetDirs,$(SUBLOC))) CLEANFILES := $(am_CLEANFILES) DISTCLEANFILES := $(am_DISTCLEANFILES) MAINTAINERCLEANFILES := $(am_MAINTAINERCLEANFILES) SUBDIRS := $(am_SUBDIRS) DIST_SUBDIRS := $(am_DIST_SUBDIRS) EMBED_SUBDIRS := $(am_EMBED_DONE) am_CLEANFILES = am_DISTCLEANFILES = am_MAINTAINERCLEANFILES = am_DIST_SUBDIRS = am_SUBDIRS = am_EMBED_DONE = am_TARGETLISTS := $(sort $(am_TARGETLISTS)) am_EXTRA_TARGETLISTS := $(sort $(am_EXTRA_TARGETLISTS)) # avoid duplicate entries with am_TARGETLISTS am_EXTRA_TARGETLISTS := $(filter-out $(am_TARGETLISTS),$(am_EXTRA_TARGETLISTS)) # allow seeing moved lists AM_FLAGS += real ## EMBED_SUBDIRS end ## ## Launch target hooks ## amdir = $(dir $(realpath $(filter %/antimake.mk antimake.mk,$(MAKEFILE_LIST)))) # 1-feat name FeatFile = $(call JoinPath,$(amdir),amext-$(1).mk) # 1- fname LoadFeature = $(if $(wildcard $(call FeatFile,$(1))),$(eval include $(call FeatFile,$(1))),$(error Feature "$(call FeatFile,$(1))" is not available.)) $(foreach f,$(AM_FEATURES),$(call LoadFeature,$(f))) $(eval $(foreach hook,$(AM_TARGET_HOOKS),$(call ForEachTarget,$(hook),$(am_TARGETLISTS)))) $(eval $(foreach hook,$(AM_TARGET_HOOKS),$(call ForEachTarget,$(hook),$(am_EXTRA_TARGETLISTS)))) ## ## Now generate the rules ## ## check which target func to call # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags MakeTarget = $(call $(if $(filter $(AM_BIG_PRIMARIES),$(3)),MakeBigTarget,MakeSmallTarget),$(1),$(2),$(3),$(4),$(5)) ## process all targets in one list # 1-list, 2-prim,3-dest,4-flags MakeTargetList = $(foreach tgt,$($(1)),$(call MakeTarget,$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) ## process all target lists # 1=list names ProcessTargets = $(call ForEachTarget,MakeTarget,$(1)) # process non-EXTRA targets $(eval $(call ProcessTargets,$(am_TARGETLISTS))) # process EXTRA_* last, they may already have been processed $(eval $(call ProcessTargets,$(am_EXTRA_TARGETLISTS))) ## ## clean targets ## clean: ifdef CLEANFILES $(E) "CLEAN" $@ $(Q) $(RM) -- $(CLEANFILES) endif distclean: clean $(E) "DISTCLEAN" $@ $(Q) $(RM) -r -- $(OBJDIR) ifdef DISTCLEANFILES $(Q) $(RM) -- $(DISTCLEANFILES) endif maintainer-clean: clean $(E) "MAINTAINERCLEAN" $@ $(Q) $(RM) -r -- $(OBJDIR) ifdef DISTCLEANFILES $(Q) $(RM) -- $(DISTCLEANFILES) endif ifdef MAINTAINERCLEANFILES $(Q) $(RM) -- $(MAINTAINERCLEANFILES) endif ## ## actual subdir targets ## # 1-dir define MakeSubDir $(trace1) $(E) "MKDIR" "Create $(call JoinPath,$(SUBLOC),$(1))" $(Q) $(call MkDir,$(1)) $(Q) $(call Printf,"include $(call UpDir,$(1))/$(srcdir)/$(1)/Makefile\n") \ > $(1)/Makefile endef # 1-dir, 2-tgt define SubTarget $(trace2) $(if $(wildcard $(1)/Makefile),,$(call MakeSubDir,$(1))) $(E) "-->" "$(call JoinPath,$(SUBLOC),$(1))" $(Q) $(MAKE) -C $(1) $(2) $(E) "<--" "$(call JoinPath,$(SUBLOC),$(1))" endef sub-all sub-install sub-uninstall sub-clean: $(foreach dir,$(SUBDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) # Avoid double dirs in DIST_SUBDIRS, without changing order am_DISTDIRS = $(SUBDIRS) $(foreach dir,$(DIST_SUBDIRS),$(if $(filter $(dir),$(SUBDIRS)),,$(dir))) sub-dist sub-distclean sub-maintainer-clean: $(foreach dir,$(am_DISTDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) .PHONY: sub-all sub-clean sub-install sub-dist sub-distclean sub-maintainer-clean ## ## actual dist targets ## DistTarget = $(foreach fmt,$(1),dist-$(fmt)) AM_DIST_ALL ?= gzip bzip2 xz zip AM_DIST_ALL_TGTS = $(call DistTarget,$(AM_DIST_ALL)) AM_DIST_DEF_TGTS = $(call DistTarget,$(AM_DIST_DEFAULT)) AM_FORMAT_gzip_EXT = tar.gz AM_FORMAT_gzip_CMD = tar chof - $(AM_DIST_BASE) | gzip > $(AM_DIST_BASE).$(AM_FORMAT_gzip_EXT) AM_FORMAT_bzip2_EXT = tar.bz2 AM_FORMAT_bzip2_CMD = tar chof - $(AM_DIST_BASE) | bzip2 > $(AM_DIST_BASE).$(AM_FORMAT_bzip2_EXT) AM_FORMAT_xz_EXT = tar.xz AM_FORMAT_xz_CMD = tar chof - $(AM_DIST_BASE) | xz > $(AM_DIST_BASE).$(AM_FORMAT_xz_EXT) AM_FORMAT_zip_EXT = zip AM_FORMAT_zip_CMD = zip -rq $(AM_DIST_BASE).$(AM_FORMAT_zip_EXT) $(AM_DIST_BASE) # 1-name define MakeDist $(E) "CHECK" $@ $(Q) $(MAKE) -s am-check-distfiles $(E) "MKDIR" $(AM_DIST_BASE) $(Q) $(RM) -r -- $(AM_DIST_BASE) $(AM_DIST_BASE).$(AM_FORMAT_$(1)_EXT) $(Q) $(call MkDir,$(AM_DIST_BASE)) $(E) "COPY" $(AM_DIST_BASE) $(Q) $(MAKE) -s am-show-distfiles | cpio -pmduL --quiet $(AM_DIST_BASE) $(E) "PACK" $(AM_DIST_BASE).$(AM_FORMAT_$(1)_EXT) $(Q) $(AM_FORMAT_$(1)_CMD) $(Q) $(RM) -r -- $(AM_DIST_BASE) endef .PHONY: dist $(AM_DIST_ALL_TGTS) dist: $(AM_DIST_DEF_TGTS) dist-all: $(AM_DIST_ALL_TGTS) $(AM_DIST_ALL_TGTS): $(call MakeDist,$(subst dist-,,$@)) # show list of files that need to be in final archive .PHONY: am-show-distfiles am-show-distfiles: $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) --no-print-directory -C $(dir) $@ $(NewLine)) $(foreach file,$(am_FINAL_DISTFILES),@$(call Printf,"$(call JoinPath,$(SUBLOC),$(file))\n") $(NewLine)) # do dependencies as separate step, in case building outputs anything .PHONY: am-check-distfiles am-check-distfiles: $(am_FINAL_DISTFILES) $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) -C $(dir) $@ $(NewLine)) ## ## debug target ## # 1=var define AmDebugShow $(if $($(1)),@$(call Printf,"$(1) = $($(1))\n")) $(NewLine) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define AmDebugTarget $(trace5) $(foreach var,$(AM_DEBUG_TARGET_VARS),$(call AmDebugShow,$(1)_$(var))) @$(call Printf,"\n") endef # func args: 1-var, 2-prim, 3-dest, 4-flags CollectDests = $(filter-out noinst EXTRA,$(3)) AM_USED_DESTS = $(sort $(call ForEachList,CollectDests,$(am_TARGETLISTS))) AM_DEBUG_VARS = GNUMAKE380 GNUMAKE381 GNUMAKE382 MAKEFILE_LIST \ AM_LANGUAGES AM_FLAGS AM_DESTINATIONS \ AM_ALL_TARGETS EXEEXT am_FINAL_DISTFILES \ nosub_top_builddir nosub_top_srcdir \ abs_top_srcdir abs_top_builddir \ srcdir builddir top_srcdir top_builddir \ SUBDIRS EMBED_SUBDIRS DIST_SUBDIRS \ DISTFILES CLEANFILES DISTCLEANFILES MAINTAINERCLEANFILES AM_DEBUG_TARGET_VARS = SOURCES OBJS LINKVAR DEST USUAL_OBJS USUAL_SRCS EXT FINAL \ $(AM_TARGET_VARIABLES) AM_DEBUG_LANG_VARS = SRCEXTS am-debug: @$(call Printf,"\n==== Global Variables ====\n") $(foreach var,$(AM_DEBUG_VARS),$(call AmDebugShow,$(var))) @$(call Printf,"\n==== Per-language Variables ====\n") $(foreach lg,$(AM_LANGUAGES),$(foreach var,$(AM_DEBUG_LANG_VARS),$(call AmDebugShow,AM_LANG_$(lg)_$(var)))) @$(call Printf,"\n==== Per-target Variables ====\n") $(call ForEachTarget,AmDebugTarget,$(am_TARGETLISTS) $(am_EXTRA_TARGETLISTS)) @$(call Printf,"\n==== Active install directories ====\n") $(foreach dst,$(AM_USED_DESTS),@$(call Printf," $(dst)dir = $($(dst)dir)\n" $(NewLine))) ## ## regtests for basic tools ## AM_TESTS = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 AM_TEST_1 = $(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a) AM_TEST_1_RES = true,true,, AM_TEST_2 = $(call Neq,a,aa),$(call Neq,a,a) AM_TEST_2_RES = true, AM_TEST_3 = $(call CleanName,obj/foo-baz.x) AM_TEST_3_RES = obj_foo_baz_x AM_TEST_4 = $(call LastWord,a),$(call LastWord,a b c),$(call LastWord,) AM_TEST_4_RES = a,c, AM_TEST_5 = $(call ReplaceExts,.c .cpp X.foo,.o,s1.c s2.cpp s3X.foo s4.h) AM_TEST_5_RES = s1.o s2.o s3.o AM_TEST_5 = $(call LangList,foo.c c.foo),$(call LangList,foo.c c.foo f.cpp) AM_TEST_5_RES = C,C CXX AM_TEST_6 = $(call DetectLinkVar,foo.c c.foo),$(call DetectLinkVar,foo.c c.foo x.cpp),$(call DetectLinkVar,foo),$(call DetectLinkVar,) AM_TEST_6_RES = AM_LANG_C_LINK,AM_LANG_CXX_LINK,AM_LANG_C_LINK,AM_LANG_C_LINK AM_TEST_7 = $(call UpDir,foo)|$(call UpDir,)|$(call UpDir,.)|$(call UpDir,foo/bar)|$(call UpDir,a/b/c)| AM_TEST_7_RES = ..|.|.|../..|../../..| AM_TEST_8 = $(call JoinPath,.,.)|$(call JoinPath,,)|$(call JoinPath,a,.)|$(call JoinPath,.,b)|$(call JoinPath,a,b)|$(call JoinPath,a/b,../c)|$(call JoinPath,a/b,../../../c) AM_TEST_8_RES = .||a|b|a/b|a/c|../c define AM_TEST_9_EVAL $(IFEQ) ($$(AM_TEST_9_RES),OK) AM_TEST_9 = OK $(ELSE) AM_TEST_9 = fail $(ENDIF) endef AM_TEST_9_RES = OK $(eval $(AM_TEST_9_EVAL)) AM_TEST_10 = $(call CheckName,nobase_bin_PROGRAMS,PROGRAMS,bin,nobase)|$(call CheckName,a,a,,)|$(call CheckName,bin_bin_DATA,,bin bin,DATA) AM_TEST_10_RES = nobase_bin_PROGRAMS|a| AM_TEST_11_Show = $(4)-$(3)-$(2) AM_TEST_11 = $(call ForEachList,AM_TEST_11_Show,bin_PROGRAMS foo_DATA baz_foo base_nobase_dist_nodist_DATA_PROGRAMS) AM_TEST_11_RES = -bin-PROGRAMS --DATA -- base nobase dist nodist--DATA PROGRAMS AM_TEST_12 = $(call RelocFlags,sub/dir,-I. -I./foo -Lfoo/bar -I/inc -L/lib -lfoo) AM_TEST_12_RES = -Isub/dir -Isub/dir/foo -Lsub/dir/foo/bar -I/inc -L/lib -lfoo AM_TEST_13 = $(call TargetNoDist,HEADERS,)|$(call TargetNoDist,HEADERS,nodist)|$(call TargetNoDist,PROGRAMS,)|$(call TargetNoDist,PROGRAMS,dist) AM_TEST_13_RES = |true|PROGRAMS| AM_TEST_14 = $(call ShellQuote,foo'bar\')|$(call ShellQuote,as!d' \\ $$foo) AM_TEST_14_RES = 'foo'\''bar\'\'''|'as!d'\'' \\ $$foo' AM_TEST_15 = $(call JoinPath,sub/dir,../foo) , \ $(call JoinPath,sub/dir,../../foo) , \ $(call JoinPath,sub/dir,../../../foo) , \ $(call JoinPath,sub/dir/,../foo) , \ $(call JoinPath,/,./foo) , \ $(call JoinPath,..,../foo) , \ $(call JoinPath,/foo,../baz) , \ $(call JoinPath,/foo,../../baz) , \ $(call JoinPath,foo/..,./foo) AM_TEST_15_RES = sub/foo , foo , ../foo , sub/foo , /foo , ../../foo , /baz , /baz , foo/../foo AM_TEST_16_EXT = .foo AM_TEST_16 = $(call FinalTargetFile,prog,prog,PROGRAMS) | $(call FinalTargetFile,AM_TEST_16,AM_TEST_16,PROGRAMS) AM_TEST_16_RES = prog$(EXEEXT) | AM_TEST_16.foo AmTest = $(if $(call Eq,$($(1)),$($(2))),@$(call Printf,"$(1): OK\n"),@$(call Printf,"$(subst ",',$(1): FAIL: $($(1)) != $($(2))\n)"))$(NewLine) am-test: $(Q) test "$(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a)" = "true,true,," $(foreach nr,$(AM_TESTS),$(call AmTest,AM_TEST_$(nr),AM_TEST_$(nr)_RES)) ## ## help target ## AmHelpNames = targets standalone internal config dests .PHONY: help $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) $(foreach n,$(AmHelpNames),help-$(n)-local): help: $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) # 1-var, 2-desc AmConf = @$(call Printf," %-27s %s=%s\n" $(call ShellQuote,$(2)) $(call ShellQuote,$(1)) $(call ShellQuote,$($(1)))) help-targets: @$(call Printf,"\n") @$(call Printf,"Main targets:\n") @$(call Printf," all Build all targets (default)\n") @$(call Printf," install Install files\n") @$(call Printf," dist Create source archive\n") @$(call Printf," clean Clean built files\n") @$(call Printf," distclean Clean configured files\n") @$(call Printf," maintainer-clean Delete anything that can be generated\n") help-standalone: @$(call Printf,"\n") @$(call Printf,"Standalone targets: (make -f antimake.mk)\n") @$(call Printf," show-location Prints full path to antimake.mk (default)\n") @$(call Printf," show-config Prints template config.mak.in\n") help-internal: @$(call Printf,"\n") @$(call Printf,"Internal targets:\n") @$(call Printf," am-show-distfiles Shows files that go into source archive\n") @$(call Printf," am-debug Shows variables that affect the build\n") @$(call Printf," am-test Regtest for internal functions\n") help-config: @$(call Printf,"\n") @$(call Printf,"Config variables and their current values:\n") $(call AmConf,CC,C compiler) $(call AmConf,CFLAGS,C compiler flags) $(call AmConf,CPPFLAGS,C pre-processor flags) $(call AmConf,LDFLAGS,Linker flags) help-dests: @$(call Printf,"\n") @$(call Printf,"Destinations for install [ prefix=$(prefix) ]:\n") $(foreach dst,$(AM_USED_DESTS),@$(call Printf," $(dst)dir = $($(dst)dir)\n") $(NewLine)) endif # O=empty pgbouncer-1.24.1/lib/mk/amext-libusual.mk0000644000175000000000000000274414777762223015161 00000000000000# # Merge libusual sources with target sources # # Usage: # USUAL_DIR = # # _EMBED_LIBUSUAL = 1 # # It adds module sources into _SOURCES # and -I$(USUAL_DIR) to _CPPFLAGS. # ## ## Utility functions for libusual link ## _USUAL_DIR = $(call JoinPath,$(srcdir),$(USUAL_DIR)) _USUAL_DIR2 = $(call JoinPath,$(_USUAL_DIR),usual) # module names from sources (plus headers) UsualMods = $(trace1)$(shell $(_USUAL_DIR)/find_modules.sh $(_USUAL_DIR) $(sort $(wildcard $(addprefix $(srcdir)/,$(1))))) # full-path sources based on module list UsualSrcsFull = $(trace1)$(sort $(wildcard $(addprefix $(_USUAL_DIR2)/,$(addsuffix *.[ch],$(1))))) # remove USUAL_DIR UsualStrip = $(trace1)$(subst $(_USUAL_DIR)/,,$(1)) # simple-path sources based on module list UsualSrcs = $(call UsualStrip,$(call UsualSrcsFull,$(1))) # usual sources from user source file list UsualSources = $(if $(1),$(call UsualSrcsFull,$(call UsualMods,$(1)))) # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define EmbedLibUsual $(trace5) # embed libusual objects directly $(IFEQ) ($$($(1)_EMBED_LIBUSUAL),1) $(1)_SOURCES := $$($(1)_SOURCES) $$(call UsualSources, $$($(1)_SOURCES)) EXTRA_$(1)_SOURCES := $$(EXTRA_$(1)_SOURCES) \ $$(call UsualSources, \ $$(EXTRA_$(1)_SOURCES) \ $$(nodist_$(1)_SOURCES) \ $$(nodist_EXTRA_$(1)_SOURCES)) $(1)_CPPFLAGS += -I$$(USUAL_DIR) $(ENDIF) endef AM_TARGET_HOOKS += EmbedLibUsual EXTRA_DIST += $(_USUAL_DIR)/find_modules.sh $(_USUAL_DIR)/usual/config.h.in pgbouncer-1.24.1/lib/mk/amext-modes.mk0000644000175000000000000000445714777762223014453 00000000000000# # Custom compilation modes # Compile one target several times with different # configuration variables. # # Sample: # CFLAGS = -O2 # bin_PROGRAM = prog # prog_SOURCES = prog.c # # AM_MODES = debug # CFLAGS_debug = -O0 -g # # Result: # prog - compiled with -O2 # prog-debug - compiled with -O0 -g # AM_MODES ?= # Variables that can be overrided with $(var)_$(mode) AM_MODE_OVERRIDE += CC CXX CFLAGS CPPFLAGS DEFS LDFLAGS LIBS ## add "-MODE" string before file extension # 1-mode, 2-filename ModeName = $(basename $(2))-$(1)$(suffix $(2)) ## add mode suffix to all plain filenames # 1-mode, 2-file names, options ModeFilter = $(foreach f,$(2),$(if $(filter /% -%,$(f)),$(f),$(call ModeName,$(1),$(f)))) ## set per-target var # 1-dbgvar, 2-var, 3-final ModeVarX = $(3): $(2) = $$($(1))$(NewLine) # 1-mode, 2-var, 3-final ModeVarOverride = $(if $($(2)_$(1)),$(call ModeVarX,$(2)_$(1),$(2),$(3))) # 1-mode, 2-final ModeVarOverrideAll = $(foreach v,$(AM_MODE_OVERRIDE),$(call ModeVarOverride,$(1),$(v),$(2))) ## copy target, replace vars # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags,6=mode,7-newtgt,8-cleantgt,9-list define AddModes4 $(trace8) $(IFEQ) ($$(filter $(9),$$(am_TARGETLISTS)),) am_TARGETLISTS += $(9) $(ENDIF) # add new target to old list $(9) += $(7) # copy details, change library names $(8)_SOURCES := $$($(1)_SOURCES) nodist_$$(8)_SOURCES := $$(nodist_$(1)_SOURCES) $(8)_CPPFLAGS := $$($(1)_CPPFLAGS) $(8)_CFLAGS := $$($(1)_CFLAGS) $(8)_LDFLAGS := $$($(1)_LDFLAGS) $(8)_LIBADD := $$(call ModeFilter,$(6),$$($(1)_LIBADD)) $(8)_LDADD := $$(call ModeFilter,$(6),$$($(1)_LDADD)) # add variable replacements $(call ModeVarOverrideAll,$(6),$(call FinalTargetFile,$(8),$(7),$(3))) endef ## add clean name, list name # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags,6-mode,7-raw tgt AddModes3 = $(call AddModes4,$(1),$(2),$(3),$(4),$(5),$(6),$(7),$(call CleanName,$(7)),$(subst $(Space),_,$(5)_$(4)_$(3))) ## loop over modes # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags AddModes2 = $(trace5)$(foreach m,$(AM_MODES),$(call AddModes3,$(1),$(2),$(3),$(4),$(5),$(m),$(call ModeName,$(m),$(2)))) ## ignore small primaries # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags AddModes = $(trace5)$(if $(filter $(3),$(AM_BIG_PRIMARIES)),$(call AddModes2,$(1),$(2),$(3),$(4),$(5))) # Install hook AM_TARGET_HOOKS += AddModes pgbouncer-1.24.1/lib/mk/install-sh0000755000175000000000000003253714777762223013703 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2009-04-28.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: pgbouncer-1.24.1/lib/mk/std-autogen.sh0000755000175000000000000000454414777762223014465 00000000000000#! /bin/sh # autogen for non-automake trees # # - it installs files: config.sub, config.guess, install-sh # - it installs ltmain.sh, if LT_INIT or *LIBTOOL macro is used # set -e USUAL_DIR="$1" test -n "${USUAL_DIR}" || USUAL_DIR="." test -f "${USUAL_DIR}/m4/usual.m4" || { echo usage: $0 USUAL_DIR exit 1 } # default programs ACLOCAL=${ACLOCAL:-aclocal} AUTOCONF=${AUTOCONF:-autoconf} AUTOHEADER=${AUTOHEADER:-autoheader} # If neither ACLOCAL/AUTOCONF/AUTOHEADER and # AUTOCONF_VERSION/AUTOMAKE_VERSION are configured, # pick any modern version to avoid pointless errors. if test "$AUTOCONF_VERSION" = ""; then if test "$AUTOCONF" = "autoconf"; then for ac in 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59; do ac="2.$ac" if which autoconf-$ac > /dev/null 2>&1; then AUTOCONF_VERSION="$ac" echo "Using autoconf: $AUTOCONF_VERSION" break fi if which autoconf$ac > /dev/null 2>&1; then AUTOCONF_VERSION="$ac" echo "Using autoconf: $AUTOCONF_VERSION" break fi done fi fi if test "$AUTOMAKE_VERSION" = ""; then if test "$ACLOCAL" = "aclocal"; then for am in 1.19 1.18 1.17 1.16 1.15 1.14 1.13 1.12 1.11 1.10 1.9; do if which aclocal-$am > /dev/null 2>&1; then AUTOMAKE_VERSION="$am" echo "Using aclocal: $AUTOMAKE_VERSION" break fi if which aclocal$am > /dev/null 2>&1; then AUTOMAKE_VERSION="$am" echo "Using aclocal: $AUTOMAKE_VERSION" break fi done fi fi export AUTOCONF_VERSION AUTOMAKE_VERSION # detect first glibtoolize then libtoolize if test "x$LIBTOOLIZE" = "x"; then LIBTOOLIZE=glibtoolize which $LIBTOOLIZE >/dev/null 2>&1 \ || LIBTOOLIZE=libtoolize fi # # Workarounds for libtoolize randomness - it does not update # the files if they exist, except it requires install-sh. # rm -f config.guess config.sub install-sh ltmain.sh libtool cp -p ${USUAL_DIR}/mk/install-sh . if ${LIBTOOLIZE} --help | grep "[-][-]install" > /dev/null; then ${LIBTOOLIZE} -i -f -q -c else ${LIBTOOLIZE} -c fi # drop ltmain.sh if libtool is not used grep -E 'LT_INIT|LIBTOOL' configure.ac > /dev/null \ || rm -f ltmain.sh # Now generate configure & config.h ${ACLOCAL} -I ${USUAL_DIR}/m4 grep AC_CONFIG_HEADER configure.ac > /dev/null \ && ${AUTOHEADER} ${AUTOCONF} # clean junk rm -rf autom4te.* aclocal* pgbouncer-1.24.1/lib/mk/amext-msvc.mk0000644000175000000000000000211014777762223014274 00000000000000# # Support for MSVC toolchain. # # Usage: # 1. Install coreutils (printf, tail) and make from gnuwin32. # 2. Make sure VC env variables are loaded (PATH) # SHELL = cmd.exe ShellQuote = "$(subst $$, \$$, $(subst ",\",$(subst \,\\,$(1))))" EXEEXT = .exe LIBEXT = .lib OBJEXT = .obj CC = cl -nologo CFLAGS = -O2 $(WFLAGS) WFLAGS = -W2 -w24013 CPP = $(CC) -E LDFLAGS = LIBS = -lws2_32 -ladvapi32 AR = lib ARFLAGS = -nologo LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Fe$(call vcFixPath,$@) Printf = printf $(subst %,%%,$(1)) $(2) MKDIR_P = md MkDir = if not exist $(call vcFixPath,$(1)) $(MKDIR_P) $(call vcFixPath,$(1)) vcFixPath = $(subst /,\,$(1)) vcFixLibs = $(patsubst %.a,%.lib,$(patsubst -l%,%.lib,$(1))) vcFixAll = $(call vcFixPath,$(call vcFixLibs,$(1))) define AM_LANG_C_COMPILE $(E) "CC" $< $(Q) $(COMPILE) -c -Fo$(call vcFixPath,$@) $< | tail -n+2 endef define AM_LANG_C_LINK $(E) "CCLD" $@ $(Q) $(LINK) $(call vcFixAll,$^ $(AM_LIBS) $(LIBS)) $(AM_LT_RPATH) endef define ar_lib $(E) "LIB" $@ $(Q) $(AR) $(ARFLAGS) -out:$(call vcFixPath,$@) $^ endef pgbouncer-1.24.1/lib/README0000644000175000000000000000011114777762223012127 00000000000000= libusual = Collection of various code useful for writing server code. pgbouncer-1.24.1/NEWS.md0000644000175000000000000023130214777762222011606 00000000000000PgBouncer changelog =================== PgBouncer 1.24.x ---------------- **2025-04-16 - PgBouncer 1.24.1 - "CVE-2025-2291 VALID UNTIL yesterday"** - Security * Fix CVE-2025-2291: Previously PgBouncer did not take into account the VALID UNTIL of a user password when querying for password hashes using its auth_query. So if PgBouncer is used as a transparent proxy in front of Postgres it could allow passwords that had already expired. To solve this issue the default auth_query and the examples of custom auth_query functions in the documentation have been changed to take VALID UNTIL into account. If you are using a custom auth_query you should update that accordingly. If you are using the default auth_query, you can either update to PgBouncer 1.24.1 or change your config to use the new default auth_query on a previous release of PgBouncer. - Fixes * Fix PAM support by reverting `pam` authentication support in HBA file. ([#1291]) (bug introduced in 1.24.0) * Fix bug when decrementing user connection count. This was included in the tag of 1.24.0 on GitHub, but the release tarball did not contain this fix. ([#1238]) (bug introduced in 1.24.0) * Add `test_load_balance_hosts.py` to the tarball. ([#1282]) * Fix issues with tests to allow them to be run by Debian packagers. ([#1266], [#1250]) - Docs * Update `auth_query` example to set a safe `search_path`. ([#1245]) [#1238]: https://github.com/pgbouncer/pgbouncer/pull/1238 [#1291]: https://github.com/pgbouncer/pgbouncer/pull/1291 [#1282]: https://github.com/pgbouncer/pgbouncer/pull/1282 [#1266]: https://github.com/pgbouncer/pgbouncer/pull/1266 [#1250]: https://github.com/pgbouncer/pgbouncer/pull/1250 [#1245]: https://github.com/pgbouncer/pgbouncer/pull/1245 **2025-01-10 - PgBouncer 1.24.0 - "New year, new bouncer"** - Features * Add support for `Type=notify-reload` for systemd. This requires systemd version 253 or later. ([#1148]) * Add `KILL_CLIENT` command to the admin console. This allows terminating a client connection by force. ([#1147]) * Add `max_user_client_connections` setting, both globally and at the user level. ([#1137]) * Add `max_db_client_connections` setting, both globally and at the database level. ([#1138]) * Add `current_client_connections` counter to `SHOW USERS` and `SHOW DATABASES` output. ([#1137], [#1138]) * Add `load_balance_hosts` parameter, to support **not** load balancing between hosts. ([#736]) * Expose prepared statement usage counters in `SHOW STATS`. ([#1192]) * Add `client_idle_timeout` setting. ([#1189]) * Add user level `query_timeout` and `reserve_pool_size`. ([#1180], [#1228]) * Enable `pam` authentication support in HBA file. ([#326]) - Changes * Don't recycle connections on RELOAD if TLS config is unchanged. Previously if you had TLS connections they would all be recycled on RELOAD, which could cause a temporary but serious performance degradation. Now this only happens when the TLS settings are actually changed. ([#1157]) * Enable prepared statement support by default, `max_prepared_statements` is now set to 200 by default. This change in defaultls should only impact clients that actually use prepared statements. If you do use prepared statements it's recommended to read about the limitations of the prepared statement support in [our documentation][prepared-docs] ([#1144]) * Sockets/clients/servers can now be identified by a unique ID in the admin output. Previously they could be identified by their pointer, but these would often be reused by new clients after disconnect. ([#1172]) * Clearer error for empty pidfile. ([#1195]) * Return original error to client in case of `server_login_retry` failure. ([#1152]) * Log original server error in case of error from `auth_query`. ([#1187]) * Setting `default_pool_size` to 0 means unlimited size. ([#1227]) * Change the name of the `reserve_pool` setting for databases, to `reserve_pool_size`. The previous name is still an alias for the new name. ([#1232]) - Fixes * Handle various unlikely error cases better, such as OOM errors. These could previously cause crashes or memory leaks. ([#1108], [#1101], [#1099], [#1169], [#1202]) * Correct default value for `server_tls_sslmode` in sample config file. ([#1133]) * Remove mention in docs of invalid alias for `server_tls_protocols`. ([#1155]) * Fix bug when using `auth_query` and replication connections together. This bug would cause connection failures in such setups. ([#1166]) * Ignore client cancel requests while PgBouncer is configuring server setting. ([#298]) [prepared-docs]: https://www.pgbouncer.org/config.html#max_prepared_statements [#1148]: https://github.com/pgbouncer/pgbouncer/pull/1148 [#1147]: https://github.com/pgbouncer/pgbouncer/pull/1147 [#1137]: https://github.com/pgbouncer/pgbouncer/pull/1137 [#1138]: https://github.com/pgbouncer/pgbouncer/pull/1138 [#736]: https://github.com/pgbouncer/pgbouncer/pull/736 [#1192]: https://github.com/pgbouncer/pgbouncer/pull/1192 [#1189]: https://github.com/pgbouncer/pgbouncer/pull/1189 [#1180]: https://github.com/pgbouncer/pgbouncer/pull/1180 [#1228]: https://github.com/pgbouncer/pgbouncer/pull/1228 [#326]: https://github.com/pgbouncer/pgbouncer/pull/326 [#1157]: https://github.com/pgbouncer/pgbouncer/pull/1157 [#1144]: https://github.com/pgbouncer/pgbouncer/pull/1144 [#1172]: https://github.com/pgbouncer/pgbouncer/pull/1172 [#1195]: https://github.com/pgbouncer/pgbouncer/pull/1195 [#1152]: https://github.com/pgbouncer/pgbouncer/pull/1152 [#1187]: https://github.com/pgbouncer/pgbouncer/pull/1187 [#1227]: https://github.com/pgbouncer/pgbouncer/pull/1227 [#1232]: https://github.com/pgbouncer/pgbouncer/pull/1232 [#1108]: https://github.com/pgbouncer/pgbouncer/pull/1108 [#1101]: https://github.com/pgbouncer/pgbouncer/pull/1101 [#1099]: https://github.com/pgbouncer/pgbouncer/pull/1099 [#1169]: https://github.com/pgbouncer/pgbouncer/pull/1169 [#1202]: https://github.com/pgbouncer/pgbouncer/pull/1202 [#1133]: https://github.com/pgbouncer/pgbouncer/pull/1133 [#1155]: https://github.com/pgbouncer/pgbouncer/pull/1155 [#1166]: https://github.com/pgbouncer/pgbouncer/pull/1166 [#298]: https://github.com/pgbouncer/pgbouncer/pull/298 PgBouncer 1.23.x ---------------- **2024-08-02 - PgBouncer 1.23.1 - "Everything is put back in order"** - Fixes * Fix a possible segmentation fault after PgBouncer reloads its configuration. ([#1105]) (bug introduced in 1.23.0) * Fix all known put_in_order crashes. ([#1120]) (new crashes were introduced in 1.23.0) * Add missing files to release tarball that are required for testing. ([#1124]) (missing files were introduced in 1.23.0) [#1120]: https://github.com/pgbouncer/pgbouncer/pull/1120 [#1105]: https://github.com/pgbouncer/pgbouncer/pull/1105 [#1124]: https://github.com/pgbouncer/pgbouncer/pull/1124 **2024-07-03 - PgBouncer 1.23.0 - "Into the new beginnings"** - Features * Add support for rolling restarts. SIGTERM doesn't cause immediate shutdown of the PgBouncer process anymore. It now does a "super safe shutdown": waiting for all clients to disconnect before shutting down. The new SIGTERM behaviour allows rolling restarts of multiple PgBouncer processes behind a load balancer, or listening on the same port using `so_reuseport`. This is a **minor breaking change**. If you relied on the old behaviour of SIGTERM in your Dockerfile or Systemd service file you should now use SIGQUIT. ([#902]) * Add support for user name maps for `cert` and `peer` authentication methods. This feature provides the flexibility that the user initiating the connection does not have to be the database user. PgBouncer support for user name maps works very similar to the postgres with the exceptions listed in the docs. ([#996]) * Add support for replication connections through PgBouncer. ([#876]) - Changes * Improve `SHOW USERS` output listing the connections. ([#1040]) * Allow `pool_size` configuration per user. ([#1049]) * Allow `server_lifetime` configuration per database. ([#1057]) * Add support for listing dynamically created users in the output of `SHOW USERS`. ([#1052]) * Add support for `all` address type in hba configuration. ([#1078]) * Add support for automatically restarting when using systemd. ([#1080]) * Increase c-ares minimum version requirement to 1.9.0 ([#1076]) - Fixes * Fix issues handling large and partial startup packets. ([#1058]) * Add support for `--config=value` format in options startup parameter. ([#1064]) * Fix `avg_wait_time` metric calculation. ([#727]) * Add support for negotiating the postgres protocol version with the client. ([#1007]) * Add outstanding request for `auth_query`. ([#1034]) * Multiple documentation and CI improvements. [#996]: https://github.com/pgbouncer/pgbouncer/pull/996 [#1040]: https://github.com/pgbouncer/pgbouncer/pull/1040 [#1049]: https://github.com/pgbouncer/pgbouncer/pull/1049 [#1057]: https://github.com/pgbouncer/pgbouncer/pull/1057 [#1052]: https://github.com/pgbouncer/pgbouncer/pull/1052 [#1058]: https://github.com/pgbouncer/pgbouncer/pull/1058 [#1007]: https://github.com/pgbouncer/pgbouncer/pull/1007 [#876]: https://github.com/pgbouncer/pgbouncer/pull/876 [#902]: https://github.com/pgbouncer/pgbouncer/pull/902 [#1064]: https://github.com/pgbouncer/pgbouncer/pull/1064 [#1078]: https://github.com/pgbouncer/pgbouncer/pull/1078 [#1080]: https://github.com/pgbouncer/pgbouncer/pull/1080 [#727]: https://github.com/pgbouncer/pgbouncer/pull/727 [#1076]: https://github.com/pgbouncer/pgbouncer/pull/1076 [#1034]: https://github.com/pgbouncer/pgbouncer/pull/1034 PgBouncer 1.22.x ---------------- **2024-03-04 - PgBouncer 1.22.1 - "It's summer in Bangalore"** - Fixes * Fix issues caused by some clients using `COPY FROM STDIN` queries. Such queries could introduce memory leaks, performance regressions and prepared statement misbehavior. ([#1025]) (bug introduced in 1.21.0) * Add missing tests to release tarball ([#1026]) (missing tests were introduced in 1.19.0 & 1.21.0) [#1025]: https://github.com/pgbouncer/pgbouncer/pull/1025 [#1026]: https://github.com/pgbouncer/pgbouncer/pull/1026 **2024-01-31 - PgBouncer 1.22.0 - "DEALLOCATE ALL"** - Features * Adds support for `DEALLOCATE ALL` and `DISCARD ALL` when `max_prepared_statements` is set to a non-zero value (normal `DEALLOCATE` is still unsupported) ([#972]) * Support configuring `auth_query` per database ([#979]) - Changes * Improve settings in the recommended systemd unit file ([#983]) * Make fail fast logic handle all scenarios where no working connections to the database exist anymore and none can be established ([#998]) * Multiple documentation improvements - Fixes * Fix issue in PG14+ where PgBouncer would send `SET DateStyle='ISO'` for every transaction ([#879]) * Fix handling of empty `application_name` ([#999]) * Fix building on Windows with OpenSSL 3.2.0 ([#1009]) [#972]: https://github.com/pgbouncer/pgbouncer/pull/972 [#979]: https://github.com/pgbouncer/pgbouncer/pull/979 [#983]: https://github.com/pgbouncer/pgbouncer/pull/983 [#998]: https://github.com/pgbouncer/pgbouncer/pull/998 [#879]: https://github.com/pgbouncer/pgbouncer/pull/879 [#999]: https://github.com/pgbouncer/pgbouncer/pull/999 [#1009]: https://github.com/pgbouncer/pgbouncer/pull/1009 PgBouncer 1.21.x ---------------- **2023-10-16 - PgBouncer 1.21.0 - "The one with prepared statements"** - Features * Add support for protocol-level named prepared statements! This is probably one of the most requested features for PgBouncer. Using prepared statements together with PgBouncer can reduce the CPU load on your system a lot (both at the PgBouncer side and the PostgreSQL side). In synthetic benchmarks this feature was able to increase query throughput anywhere from 15% to 250%, depending on the workload. To benefit from this new feature you need to change the new `max_prepared_statements` setting to a non-zero value (the exact value depends on your workload, but 100 is probably reasonable). See [the docs on `max_prepared_statements`](https://pgbouncer.org/config.html#max_prepared_statements) for details on how the feature works, its limitations, and how to tune the value. After doing that you need to make sure your client library actually uses prepared statements. How to do that differs for each client, so you should look at the docs for the client you're using. This feature has been tested very well before releasing, but performance issues or bugs might very well exist due to the complexity of the feature. If you find those, please report them. ([#845]) - Changes * Improve security of OpenSSL settings, the defaults used were VERY outdated. With this release the defaults are now the same as the OpenSSL defaults of the system that runs PgBouncer. ([#948] & [libusual/#41]) * PgBouncer now uses OpenSSL to calculate MD5 hashes when possible. This is necessary to use PgBouncer in a FIPS compliant way. ([#949]) * Maintain `min_pool_size` for pools with a forced user even if no clients are connected to PgBouncer ([#947]) * The way a `peer_id` is encoded in the cancellation token by PgBouncer has changed, this means that peering between different PgBouncer versions will not work if not all of them are on the same side of the v1.21.0 version boundary. ([#945]) - Fixes * Fix crash with error message: "FATAL in function client\_proto(): bad client state: 6/7" ([#928]) (bug introduced in 1.18.0) * Fix crash with error message: "FATAL in function server\_proto(): server in bad state: 11" ([#927]) (bug introduced in 1.18.0) * Reduce cancellation sending log level ([#903]) * Fix slog log prefix for peers ([#922]) * Fix typos in docs ([#932]) * Fix errors pointed out by static analyzer ([#943]) * Don't kill all waiting clients on temporary FATAL errors during login ([#946]) * Use auto-database when database in `auth_dbname` is not explicitly configured ([#921]) - Cleanup * Remove support for udns ([#938]) [#845]: https://github.com/pgbouncer/pgbouncer/pull/845 [#948]: https://github.com/pgbouncer/pgbouncer/pull/948 [#949]: https://github.com/pgbouncer/pgbouncer/pull/949 [#947]: https://github.com/pgbouncer/pgbouncer/pull/947 [#945]: https://github.com/pgbouncer/pgbouncer/pull/945 [#903]: https://github.com/pgbouncer/pgbouncer/pull/903 [#922]: https://github.com/pgbouncer/pgbouncer/pull/922 [#932]: https://github.com/pgbouncer/pgbouncer/pull/932 [#928]: https://github.com/pgbouncer/pgbouncer/pull/928 [#927]: https://github.com/pgbouncer/pgbouncer/pull/927 [#943]: https://github.com/pgbouncer/pgbouncer/pull/943 [#946]: https://github.com/pgbouncer/pgbouncer/pull/946 [#921]: https://github.com/pgbouncer/pgbouncer/pull/921 [#938]: https://github.com/pgbouncer/pgbouncer/pull/938 [libusual/#41]: https://github.com/libusual/libusual/pull/41 PgBouncer 1.20.x ---------------- **2023-08-09 - PgBouncer 1.20.1 - "Optional options"** - Fixes * Fix regression where putting `options` inside `ignore_startup_parameters` would not ignore unknown parameters inside the `options` startup parameter anymore. ([#908]) (regression was introduced in 1.20.0) * Fix confusing typo in the docs ([#917]) [#908]: https://github.com/pgbouncer/pgbouncer/pull/908 [#917]: https://github.com/pgbouncer/pgbouncer/pull/917 **2023-07-20 - PgBouncer 1.20.0 - "A funny name goes here"** - Deprecations * Online restart option is now considered deprecated. The feature has received very little love in recent years. There are multiple known issues with it and newly added features often don't support it. The recommended method to do online restarts these days is using the `so_reuseport` and `peers` feature. That way you can have multiple different PgBouncer processes running on the same port. Then by restarting those processes one-by-one, you can make sure there's always a PgBouncer process listening on the desired port. ([#894]) - Features * Introduce the `track_extra_parameters` which allows tracking of more parameters in transaction pooling mode. Previously, PgBouncer only tracked `application_name`, `DateStyle`, `TimeZone` and `standard_conforming_strings`. Now PgBouncer also tracks `IntervalStyle` by default. And by changing `track_extra_parameters` you can track even more settings, but only [ones that PostgreSQL reports back to the client][guc_report]. If you're using Citus 12.0+, then Citus will make sure that PostgreSQL also reports `search_path` back to the client. So if you use Citus you can add `search_path` to the `track_extra_parameters` setting. ([#867]) * Forward SQLSTATE in authentication phase. This allows the detection of database not existing, which is done by Npgsql (a .NET data provider for PostgreSQL). ([#814]) * Change default `server_tls_sslmode` to `prefer`. ([#866]) * Add support for the `options` startup parameter. This allows usage of [the `PGOPTIONS` environment variable that `psql` and `libpq` know about][pgoptions]. Using this variable you can set any PostgreSQL parameter at startup. This only works for PostgreSQL parameters that PgBouncer tracks through `track_extra_parameters`. ([#878]) - Fixes * Don't crash when the `pgbouncer` admin database is used as auth_dbname. It's still not supported, but this now gives a clear error instead of crashing. ([#817]) * Fix name of `peer_cache` in `SHOW MEM`. It was incorrectly showing up as `db_cache` before. ([#864]) * Fix src/dst confusion in log. PgBouncer was logging a source IP when it meant to log the destination IP. ([#880]) * Only log admin connections over unix sockets when `log_connections` is set to `1`. ([#883]) [guc_report]: https://www.postgresql.org/docs/15/protocol-flow.html#PROTOCOL-ASYNC [pgoptions]: https://www.postgresql.org/docs/current/config-setting.html#id-1.6.7.4.5 [#867]: https://github.com/pgbouncer/pgbouncer/pull/867 [#814]: https://github.com/pgbouncer/pgbouncer/pull/814 [#866]: https://github.com/pgbouncer/pgbouncer/pull/866 [#878]: https://github.com/pgbouncer/pgbouncer/pull/878 [#817]: https://github.com/pgbouncer/pgbouncer/pull/817 [#864]: https://github.com/pgbouncer/pgbouncer/pull/864 [#880]: https://github.com/pgbouncer/pgbouncer/pull/880 [#883]: https://github.com/pgbouncer/pgbouncer/pull/883 [#894]: https://github.com/pgbouncer/pgbouncer/pull/894 PgBouncer 1.19.x ---------------- **2023-05-31 - PgBouncer 1.19.1 - "Sunny Spring"** This is a minor release that fixes a few recently introduced bugs: - Fixes * Fix: FATAL in function disconnect_client(): bad client state: 0 ([#846]) (bug introduced in 1.18.0) * Fix: FATAL in function server_proto(): server in bad state: 14 ([#849]) (bug introduced in 1.18.0) * Add files required to run python based tests to release tarball ([#852]) (new tests introduced in 1.19.0) [#846]: https://github.com/pgbouncer/pgbouncer/pull/846 [#849]: https://github.com/pgbouncer/pgbouncer/pull/849 [#852]: https://github.com/pgbouncer/pgbouncer/pull/852 **2023-05-04 - PgBouncer 1.19.0 - "The old-fashioned, human-generated kind"** - Features * Add `auth_dbname` option, which specifies against which database to run the `auth_query`. ([#764]) * Add the `SHOW STATE` command, which shows if PgBouncer is active, paused or suspended. ([#528]) * Add support for peering between PgBouncer processes. This allows configuring PgBouncer such that cancellation requests continue to work when multiple different PgBouncer processes are behind a single load balancer. ([#666]) * Add a dedicated `cancel_wait_timeout` setting, which determines after how long to give up on forwarding a cancel request. Default is 10 seconds. ([#833]) * New testing framework ([#792]) - Fixes * Fix possible memory leak on TLS handshake failure. ([#796]) * Give more accurate error messages for unsupported command-line options on Windows. ([#620]) * Fix calling `disconnect_server` on a server in `BEING_CANCELED` state. ([#815]) (introduced in 1.18.0) * Don't exit with a non-zero status when a `SIGTERM` is received. ([#834]) * Fail hard during startup when a socket could not be created in `unix_socket_dir`. ([#830]) * Fail hard during startup when none of the addresses in `listen_addr` could be listened on. ([#838]) * Give more warning messages with more information when `sbuf_connect` fails. This is especially useful when failing to create Unix sockets. ([#837]) - Cleanups * Various CI updates for better performance * Removed AppVeyor [#528]: https://github.com/pgbouncer/pgbouncer/issues/528 [#620]: https://github.com/pgbouncer/pgbouncer/pull/620 [#666]: https://github.com/pgbouncer/pgbouncer/pull/666 [#764]: https://github.com/pgbouncer/pgbouncer/pull/764 [#792]: https://github.com/pgbouncer/pgbouncer/pull/792 [#796]: https://github.com/pgbouncer/pgbouncer/pull/796 [#815]: https://github.com/pgbouncer/pgbouncer/pull/815 [#830]: https://github.com/pgbouncer/pgbouncer/pull/830 [#833]: https://github.com/pgbouncer/pgbouncer/pull/833 [#834]: https://github.com/pgbouncer/pgbouncer/pull/834 [#837]: https://github.com/pgbouncer/pgbouncer/pull/837 [#838]: https://github.com/pgbouncer/pgbouncer/pull/838 PgBouncer 1.18.x ---------------- **2022-12-12 - PgBouncer 1.18.0 - "No real mystery"** - Features * Add `application_name` to `SHOW CLIENTS`/`SERVERS`/`SOCKETS` output ([#449](https://github.com/pgbouncer/pgbouncer/pull/449)) * Add information about cancel requests to `SHOW CLIENTS`/`SERVERS`/`POOLS` output ([#782](https://github.com/pgbouncer/pgbouncer/pull/782)) - Fixes * Fail `sbuf_send_pending` operation if destination socket is closed ([#652](https://github.com/pgbouncer/pgbouncer/pull/652)) * Fix a few possible crashes ([#700](https://github.com/pgbouncer/pgbouncer/pull/700), [#730](https://github.com/pgbouncer/pgbouncer/pull/730)) * Fix for overflow bug in comma-separated host list feature, causing connection to get re-routed to Unix socket ([#747](https://github.com/pgbouncer/pgbouncer/pull/747)) * Don't evict connections to achieve `min_pool_size` ([#648](https://github.com/pgbouncer/pgbouncer/pull/648)) * Fix `SHOW HELP` with PostgreSQL 15 ([#769](https://github.com/pgbouncer/pgbouncer/issues/769)) * Fix race condition in query cancellation handling. It was possible that a query cancellation for one client canceled a query for another one. This could happen when a cancel request was received by PgBouncer when the query it was meant to cancel already completed by itself. ([#717](https://github.com/pgbouncer/pgbouncer/pull/717)) - Cleanups * Various CI updates PgBouncer 1.17.x ---------------- **2022-03-23 - PgBouncer 1.17.0 - "A line has been drawn"** - Features * A database definition can specify a comma-separated host list. The hosts will be connected to in a round-robin manner. * When connecting to a non-existing database, the error ("no such database") is now reported after authentication. This prevents unauthenticated clients from probing what databases exist. (This is similar to the change in version 1.15.0 to report missing users after authentication.) * Don't send server disconnect errors to the client before login. This could reveal not-quite-public information, such as configuration details, to a client that is not logged in yet. * Increase maximum password length again. Apparently, the last increase wasn't enough for long enough. * Remove automatic `auth_file` reload. The `auth_file` is now reread only on configuration file reload, no longer automatically as soon as it is changed. * The Windows build now includes a version-information resource file. * The Windows builds created on CI are now statically linked, so they can be used directly without requiring any dependencies. - Fixes * OpenSSL 3 support has been fixed. Previous releases would crash. * Don't apply fast-fail at connect time. This is part of the above-mentioned change to not report server errors before authentication. It also fixes a particular situation with SCRAM pass-through authentication, where we need to allow the client-side authentication exchange in order to be able to fix the server-side connection by re-authenticating. The fast-fail mechanism still applies right after authentication, so the effective observed behavior will be the same in most situations. * Change `auth_type` in sample `pgbouncer.ini` to `md5` to match the built-in default. Some deploy this file as the default configuration file, so check if this changed configuration still makes sense for you. * Fix crash at exit in assert-enabled builds. * Improve `tcp_defer_accept` documentation and behavior. The documentation was incorrect and misleading about the default. In some cases the wrong value was showing in "show config". Also, if it's set but not supported, give an error instead of ignoring, similar to how other platform-specific socket options are handled. * Fix build with c-ares on Windows. c-ares >=1.18.0 is now required on Windows. - Cleanups * Most deprecation warnings from Autoconf >=2.70 have been cleaned up. Older Autoconf versions are still supported. * Cirrus CI use has been expanded to more platforms. * Travis CI support has been removed. * Update locations to search for default root CA file, to cover more platforms, such as Fedora/RHEL/CentOS. * Python scripts now all use `python3` by default. Python 2 compatibility is no longer maintained. * The test suite scripts use `command -v` instead of `which`, which is deprecated. * Several error messages have been reworded to make it clearer which command or configuration setting they relate to. * The test suite scripts no longer require GNU sed. * `make check` now works on Windows (but not the SSL test suite yet). * Document that the admin console only supports the simple query protocol, and give better error messages about this. PgBouncer 1.16.x ---------------- **2021-11-11 - PgBouncer 1.16.1 - "Test of depth against quiet efficiency"** This is a minor release with a security fix. * Make PgBouncer acting as a server reject extraneous data after an SSL or GSS encryption handshake. A man-in-the-middle with the ability to inject data into the TCP connection could stuff some cleartext data into the start of a supposedly encryption-protected database session. This could be abused to send faked SQL commands to the server, although that would only work if PgBouncer did not demand any authentication data. (However, a PgBouncer setup relying on SSL certificate authentication might well not do so.) (CVE-2021-3935) **2021-08-09 - PgBouncer 1.16.0 - "Fended off a jaguar"** - Features * Support hot reloading of TLS settings. When the configuration file is reloaded, changed TLS settings automatically take effect. * Add support for abstract Unix-domain sockets. Prefix a Unix-domain socket path with `@` to use a socket in the abstract namespace. This matches the corresponding PostgreSQL 14 feature. * The maximum lengths of passwords and user names have been increased to 996 and 128, respectively. Various cloud services require this. * The minimum pool size can now be set per database, similar to the regular pool size and the reserve pool size. * The number of pending query cancellations is shown in `SHOW POOLS`. - Fixes * Configuration parsing now has tighter error handling in many places. Where previously it might have logged an error and proceeded, those configuration errors would now result in startup failures. This is what always should have happened, but some code didn't do this right. Some users might discover that their configurations have been faulty all along and will not work anymore. * Query cancel handling has been fixed. Under some circumstances, cancel requests would seemingly get stuck for a long time. This should no longer happen. In fact, cancel requests can now exceed the pool size by a factor of two, so they really shouldn't get stuck anymore. ([#542](https://github.com/pgbouncer/pgbouncer/pull/542), [#543](https://github.com/pgbouncer/pgbouncer/pull/543)) * Mixed use of md5 and scram via hba has been fixed. * The build with c-ares on Windows has been fixed. * The dreaded "FIXME: query end, but query_start == 0" messages have been fixed. We now know why they happen, and you shouldn't see them anymore. ([#565](https://github.com/pgbouncer/pgbouncer/pull/565)) * Fix reloading of `default_pool_size`, `min_pool_size`, and `res_pool_size`. Reloading these settings previously didn't work. - Cleanups * Cirrus CI is now [used](https://cirrus-ci.com/github/pgbouncer/pgbouncer) instead of Travis CI. * As usual, many tests have been added. * The "unclean server" log message has been clarified a bit. It now says "client disconnect while server was not ready" or "client disconnect before everything was sent to the server". The former can happen if the client connection is closed when the server has a transaction block open, which confused some users. * You can no longer use "pgbouncer" as a database name. This name is reserved for the admin console, and using it as a normal database name never really worked right. This is now explicitly prohibited. * Errors sent to clients before the connection is closed are now labeled as FATAL instead of just ERROR. Some clients were confused otherwise. ([#564](https://github.com/pgbouncer/pgbouncer/pull/564)) * Fix compiler warnings with GCC 11. ([#623](https://github.com/pgbouncer/pgbouncer/issues/623)) PgBouncer 1.15.x ---------------- **2020-11-19 - PgBouncer 1.15.0 - "Ich hab noch einen Koffer in Berlin"** - Features * Improve authentication failure reporting. The authentication failure messages sent to the client now only state that authentication failed but give no further details. Details are available in the PgBouncer log. Also, if the requested user does not exist, the authentication is still processed to the end and will result in the same generic failure message. All this prevents clients from probing the PgBouncer instance for user names and other authentication-related insights. This is similar to how PostgreSQL behaves. * Don't log anything if client disconnects immediately. This avoids log spam when monitoring systems just open a TCP/IP connection but don't send anything before disconnecting. * Use systemd journal for logging when in use. When we detect that stderr is going to the systemd journal, we use systemd native functions for log output. This avoids printing duplicate timestamp and pid, thus making the log a bit cleaner. Also, this adds metadata such as the severity to the logs, so that if the journal gets sent on to syslog, the messages have useful metadata attached. * A subset of the test suite can now be run under Windows. * `SHOW CONFIG` now also shows the default values of the settings. - Fixes * Fix the `so_reuseport` option on FreeBSD. The original code in PgBouncer 1.12.0 didn't actually work on FreeBSD. ([#504](https://github.com/pgbouncer/pgbouncer/pull/504)) * Repair compilation on systems with older systemd versions. This was broken in 1.14.0. ([#505](https://github.com/pgbouncer/pgbouncer/issues/505)) * The makefile target to build Windows binary zip packages has been repaired. * Long command-line options now also work on Windows. * Fix the behavior of the global `auth_user` setting. The old behavior was confusing and fragile as it depended on the order in the configuration file. This is no longer the case. ([#391](https://github.com/pgbouncer/pgbouncer/issues/391), [#393](https://github.com/pgbouncer/pgbouncer/issues/393)) - Cleanups * Improve test stability and portability. * Modernize Autoconf-related code. * Disable deprecation compiler warnings from OpenSSL 3.0.0. PgBouncer 1.14.x ---------------- **2020-06-11 - PgBouncer 1.14.0 - "La ritrovata magia"** - Features * Add SCRAM authentication pass-through. This allows using encrypted SCRAM secrets in PgBouncer (either in `userlist.txt` or from `auth_query`) for logging into servers. * Add support for systemd socket activation. This is especially useful to let systemd handle the creation of the Unix-domain sockets on systems where access to `/var/run/postgresql` is restricted. * Add support for Unix-domain sockets on Windows. - Cleanups * Add an alternative smaller sample configuration file `pgbouncer-minimal.ini` for testing or deployment. PgBouncer 1.13.x ---------------- **2020-04-27 - PgBouncer 1.13.0 - "My favourite game"** - Features * Add configuration setting `tcp_user_timeout`, to set the corresponding socket option. * `client_tls_protocols` and `server_tls_protocols` now default to `secure`, which means only TLS 1.2 and TLS 1.3 are enabled. Older versions are still supported, they are just not turned on by default. * Add support for systemd service notifications. Right now, this allows using `Type=notify` service units. More integration is planned for future versions. - Fixes * Fix multiline log messages ([libusual #24](https://github.com/libusual/libusual/pull/24)) * Handle null user names returned from `auth_query` properly ([#340](https://github.com/pgbouncer/pgbouncer/pull/340)) - Cleanups * The Debian packaging files under `debian` have been removed. It is recommended to use the packages from https://apt.postgresql.org/. * Numerous fixes and improvements in the test suite * The tests no longer try to use sudo by default. This can now be activated explicitly by setting the environment variable `USE_SUDO`. * The libevent API use was updated to use version 2 style interfaces and to no longer use deprecated interfaces from version 1. PgBouncer 1.12.x ---------------- **2019-10-17 - PgBouncer 1.12.0 - "It's about learning and getting better"** This release contains a variety of minor enhancements and fixes. - Features * Add a setting to turn on the `SO_REUSEPORT` socket option. On some operating systems, this allows running multiple PgBouncer instances on the same host listening on the same port and having the kernel distribute the connections automatically. * Add a setting to use a `resolv.conf` file separate from the operating system. This allows setting custom DNS servers and perhaps other DNS options. * Send the output of `SHOW VERSION` as a normal result row instead of a NOTICE message. This makes it easier to consume and is consistent with other `SHOW` commands. - Fixes * Send statistics columns as `numeric` instead of `bigint`. This avoids some client libraries failing on values that overflow the `bigint` range. ([#360](https://github.com/pgbouncer/pgbouncer/pull/360), [#401](https://github.com/pgbouncer/pgbouncer/pull/401)) * Fix issue with PAM users losing their password. ([#285](https://github.com/pgbouncer/pgbouncer/issues/285)) * Accept SCRAM channel binding enabled clients. Previously, a client supporting channel binding (that is, PostgreSQL 11+) would get a connection failure when connecting to PgBouncer in certain situations. (PgBouncer does not support channel binding. This change just fixes support for clients that offer it.) * Fix compilation with newer versions of musl-libc (used by Alpine Linux). - Cleanups * Add `make check` target. This allows running all the tests from a single command. * Remove references to the PostgreSQL wiki. All information is now either in the PgBouncer documentation or on the web site. * Remove support for Libevent version 1.x. Libevent 2.x is now required. Libevent is now detected using pkg-config. * Fix compiler warnings on macOS and Windows. The build on these platforms should now be free of warnings. * Fix some warnings from LLVM scan-build. PgBouncer 1.11.x ---------------- **2019-08-27 - PgBouncer 1.11.0 - "Instinct for Greatness"** - Features * Add support for SCRAM authentication for clients and servers. A new authentication type `scram-sha-256` is added. * Handle `auth_type=password` when the stored password is md5, like a PostgreSQL server would. ([#129](https://github.com/pgbouncer/pgbouncer/pull/129)) * Add option `log_stats` to disable printing stats to log. ([#287](https://github.com/pgbouncer/pgbouncer/pull/287)) * Add time zone to log timestamps. * Put PID into [brackets] in log prefix. - Fixes * Fix OpenSSL configure test when running against newer OpenSSL with `-Werror`. * Fix wait time computation with `auth_user`. This would either crash or report garbage values for wait time. ([#393](https://github.com/pgbouncer/pgbouncer/pull/393)) * Handle GSSENCRequest packet, added in PostgreSQL 12. It doesn't do anything right now, but it avoids confusing error messages about "bad packet header". - Cleanups * Many improvements in the test suite and several new tests * Fix several compiler warnings on Windows. * Expand documentation of the `[users]` section and add to example config file. ([#330](https://github.com/pgbouncer/pgbouncer/pull/330)) PgBouncer 1.10.x ---------------- **2019-07-01 - PgBouncer 1.10.0 - "Afraid of the World"** - Features * Add support for enabling and disabling TLS 1.3. (TLS 1.3 was already supported, depending on the OpenSSL library, but now the configuration settings to pick the TLS protocol versions also support it.) - Fixes * Fix TLS 1.3 support. This was broken with OpenSSL 1.1.1 and 1.1.1a (but not before or after). * Fix a rare crash in `SHOW FDS` ([#311](https://github.com/pgbouncer/pgbouncer/issues/311)). * Fix an issue that could lead to prolonged downtime if many cancel requests arrive ([#329](https://github.com/pgbouncer/pgbouncer/issues/329)). * Avoid "unexpected response from login query" after a postgres reload ([#220](https://github.com/pgbouncer/pgbouncer/issues/220)). * Fix `idle_transaction_timeout` calculation ([#125](https://github.com/pgbouncer/pgbouncer/issues/125)). The bug would lead to premature timeouts in specific situations. - Cleanups * Make various log and error messages more precise. * Fix issues found by Coverity (none had a significant impact in practice). * Improve and document all test scripts. * Add additional SHOW commands to the documentation. * Convert the documentation from rst to Markdown. * Python scripts in the source tree are all compatible with Python 3 now. PgBouncer 1.9.x --------------- **2018-08-13 - PgBouncer 1.9.0 - "Chaos Survival"** - Features * RECONNECT command * WAIT_CLOSE command * Fast close - Disconnect a server in session pool mode immediately if it is in "close_needed" (reconnect) mode. * Add close_needed column to SHOW SERVERS - Fixes * Avoid double-free in parse_filename * Avoid NULL pointer deref in parse_line - Cleanups * Port mkauth.py to Python 3 * Improve signals documentation * Improve quick start documentation * Document SET command * Correct list of required software * Fix -Wimplicit-fallthrough warnings * Add missing documentation for various SHOW fields * Document reconnect behavior on reload and DNS change * Document that KILL requires RESUME afterwards * Clarify documentation of server_lifetime * Typos and capitalization fixes in messages and docs * Fix psql invocation in tests * Various other test setup improvements PgBouncer 1.8.x --------------- **2017-12-20 - PgBouncer 1.8.1 - "Ground-and-pound Mentality"** - Fixes * Include file `include/pam.h` into distribution tarball. This prevented the 1.8 tarball from building at all. **2017-12-19 - PgBouncer 1.8 - "Confident at the Helm"** - Features * Support PAM authentication. (Enable with `--with-pam`.) * Add `paused` and `disabled` fields to `SHOW DATABASES` output. * Add `maxwait_us` field to `SHOW POOLS` output. * Add `wait` and `wait_us` fields to `SHOW` commands output. * Add new commands `SHOW STATS_TOTALS` and `SHOW STATS_AVERAGES`. * Track queries and transactions separately in `SHOW STATS`. The fields `total_requests`, `avg_req`, and `avg_query` have been replaced by new fields. * Add `wait_time` to `SHOW STATS`. - Fixes * Updated libusual supports OpenSSL 1.1. * Do not attempt to use TLS on Unix sockets. * When parsing `pg_hba.conf`, keep parsing after erroneous lines instead of rejecting the whole file. ([#118](https://github.com/pgbouncer/pgbouncer/issues/118)) * Several other hba parsing fixes. * Fix race condition when canceling query. ([#141](https://github.com/pgbouncer/pgbouncer/issues/141)) - Cleanups * `auth_user` setting is now also allowed globally, not only per database. ([#142](https://github.com/pgbouncer/pgbouncer/issues/142)) * Set console client and server encoding to `UTF8`. PgBouncer 1.7.x --------------- **2016-02-26 - PgBouncer 1.7.2 - "Finally Airborne"** - Fixes * Fix crash on stale pidfile removal. Problem introduced in 1.7.1. * Disable cleanup - it breaks takeover and is not useful for production loads. Problem introduced in 1.7.1. * After takeover, wait until pidfile is gone before booting. Slow shutdown due to memory cleanup exposed existing race. ([#113](https://github.com/pgbouncer/pgbouncer/issues/113)) - Cleanups * Make build reproducible by dropping DBGVER handling. ([#112](https://github.com/pgbouncer/pgbouncer/issues/112)) * Antimake: Sort file list from $(wildcard), newer gmake does not sort it anymore. ([#111](https://github.com/pgbouncer/pgbouncer/issues/111)) * Show libssl version in log. * deb: Turn on full hardening. **2016-02-18 - PgBouncer 1.7.1 - "Forward To Five Friends Or Else"** WARNING: Since version 1.7, `server_reset_query` is not executed when database is in transaction-pooling mode. Seems this was not highlighted enough in 1.7 announcement. If your apps depend on that happening, use `server_reset_query_always` to restore previous behaviour. Otherwise main work of this release was to track down TLS-related memory leak, which turned out to not exist. Instead there is libssl build in Debian/wheezy which has 600k overhead per connection (without leaking) instead expected 20-30k. Something to keep an eye on when using TLS. - Fixes * TLS: Rename sslmode "disabled" to "disable" as that is what PostgreSQL uses. * TLS: `client_tls_sslmode=verify-ca/-full` now reject connections without client certificate. ([#104](https://github.com/pgbouncer/pgbouncer/issues/104)) * TLS: `client_tls_sslmode=allow/require` do validate client certificate if sent. Previously they left cert validation unconfigured so connections with client cert failed. ([#105](https://github.com/pgbouncer/pgbouncer/issues/105)) * Fix memleak when freeing database. * Fix potential memleak in tls_handshake(). * Fix EOF handling in tls_handshake(). * Fix too small memset in asn1_time_parse compat. * Fix non-TLS (`--without-openssl`) build. ([#101](https://github.com/pgbouncer/pgbouncer/issues/101)) * Fix various issues with Windows build. ([#100](https://github.com/pgbouncer/pgbouncer/issues/100)) - Cleanups * TLS: Use SSL_MODE_RELEASE_BUFFERS to decrease memory usage of inactive connections. * Clean allocated memory on exit. Helps to run memory-leak checkers. * Improve `server_reset_query` documentation. ([#110](https://github.com/pgbouncer/pgbouncer/issues/110)) * Add TLS options to sample config. **2015-12-18 - PgBouncer 1.7 - "Colors Vary After Resurrection"** - Features * Support TLS connections. OpenSSL/LibreSSL is used as backend implementation. * Support authentication via TLS client certificate. * Support "peer" authentication on Unix sockets. * Support Host Based Access control file, like [pg_hba.conf](http://www.postgresql.org/docs/9.4/static/auth-pg-hba-conf.html) in Postgres. This allows to configure TLS for network connections and "peer" authentication for local connections. - Cleanups * Set `query_wait_timeout` to 120s by default. Current default (0) causes infinite queueing, which is not useful. That means if client has pending query and has not been assigned to server connection, the client connection will be dropped. * Disable `server_reset_query_always` by default. Now reset query is used only in pools that are in session mode. * Increase pkt_buf to 4096 bytes. Improves performance with TLS. The behaviour is probably load-specific, but it should be safe to do as since v1.2 the packet buffers are split from connections and used lazily from pool. * Support pipelining count expected ReadyForQuery packets. This avoids releasing server too early. Fixes [#52](https://github.com/pgbouncer/pgbouncer/issues/52). * Improved sbuf_loopcnt logic - socket is guarateed to be reprocessed even if there are no event from socket. Required for TLS as it has it's own buffering. * Adapt system tests to work with modern BSD and MacOS. (Eric Radman) * Remove **crypt** auth. It's obsolete and not supported by PostgreSQL since 8.4. * Fix plain "--with-cares" configure option - without argument it was broken. PgBouncer 1.6.x --------------- **2015-09-03 - PgBouncer 1.6.1 - "Studio Audience Approves"** - Features * New setting: `server_reset_query_always`. When set, disables `server_reset_query` use on non-session pools. PgBouncer introduces per-pool pool_mode, but session-pooling and transaction-pooling should not use same reset query. In fact, transaction-pooling should not use any reset query. It is set in 1.6.x, but will be disabled in 1.7. - Fixes * [SECURITY] Remove invalid assignment of `auth_user`. (#69) When `auth_user` is set and client asks non-existing username, client will log in as `auth_user`. Not good. [CVE-2015-6817](https://access.redhat.com/security/cve/cve-2015-6817) * Skip NoticeResponse in handle_auth_response. Otherwise verbose log levels on server cause login failures. * console: Fill `auth_user` when auth_type=any. Otherwise logging can crash (#67). * Various portability fixes (OpenBSD, Solaris, OSX). **2015-08-01 - PgBouncer 1.6 - "Zombies of the future"** - Features * Load user password hash from postgres database. New parameters: auth_user user to use for connecting same db and fetching user info. Can be set per-database too. auth_query SQL query to run under auth_user. Default: "SELECT usename, passwd FROM pg_shadow WHERE usename=$1" (Cody Cutrer) * Pooling mode can be configured both per-database and per-user. (Cody Cutrer) * Per-database and per-user connection limits: max_db_connections and max_user_connections. (Cody Cutrer / Pavel Stehule) * Add DISABLE/ENABLE commands to prevent new connections. (William Grant) * New DNS backend: c-ares. Only DNS backend that supports all interesting features: /etc/hosts with refresh, SOA lookup, large replies (via TCP/EDNS+UDP), IPv6. It is the preferred backend now, and probably will be **only** backend in the future, as it's pointless to support zoo of inadequate libraries. SNAFU: c-ares versions <= 1.10 have bug which breaks CNAME-s support when IPv6 has been enabled. (Fixed upstream.) As a workaround, c-ares <= 1.10 is used IPv4-only. So PgBouncer will drop other backends only when c-ares >1.10 (still unreleased) has been out some time... * Show remote_pid in SHOW CLIENTS/SERVERS. Available for clients that connect over unix sockets and both tcp and unix socket server. In case of tcp-server, the pid is taken from cancel key. * Add separate config param (dns_nxdomain_ttl) for controlling negative dns caching. (Cody Cutrer) * Add the client host IP address and port to application_name. This is enabled by a config parameter application_name_add_host which defaults to 'off'. (Andrew Dunstan) * Config files have '%include FILENAME' directive to allow configuration to be split into several files. (Andrew Dunstan) - Cleanups * log: wrap ipv6 address with [] * log: On connect to server, show local ip and port * win32: use gnu-style for long args: --foo * Allow numbers in hostname, always try to parse with inet_pton * Fix deallocate_all() in FAQ * Fix incorrect keyword in example config file (Magnus Hagander) * Allow comments (with ';') in auth files. (Guillaume Aubert) * Fix spelling mistakes in log messages and comments. (Dmitriy Olshevskiy) - Fixes * fix launching new connections during maintenance (Cody Cutrer) * don't load auth file twice at boot (Cody Cutrer) * Proper invalidation for autodbs * ipv6: Set IPV6_V6ONLY on listen socket. * win32: Don't set SO_REUSEADDR on listen socket. * Fix IPv6 address memcpy * Fix cancellation of of waiting clients. (Mathieu Fenniak) * Small bug fix, must check calloc result (Heikki Linnakangas) * Add newline at the end of the PID file (Peter Eisentraut) * Don't allow new server connections when PAUSE was issued. (Petr Jelinek) * Fix 'bad packet' during login when header is delayed. (Michał Trojnara, Marko Kreen) * Fix errors detected by Coverty. (Euler Taveira) * Disable server_idle_timeout when server count gets below min_pool (#60) (Marko Kreen) PgBouncer 1.5.x --------------- **2015-04-09 - PgBouncer 1.5.5 - "Play Dead To Win"** - Fixes * Fix remote crash - invalid packet order causes lookup of NULL pointer. Not exploitable, just DoS. **2012-11-28 - PgBouncer 1.5.4 - "No Leaks, Potty-Training Successful"** - Fixes * DNS: Fix memory leak in getaddrinfo_a() backend. * DNS: Fix memory leak in udns backend. * DNS: Fix stats calculation. * DNS: Improve error message handling for getaddrinfo_a(). * Fix win32 compile. * Fix compiler dependency support check in configure. * Few documentation fixes. **2012-09-12 - PgBouncer 1.5.3 - "Quantum Toaster"** - Critical fix * Too long database names can lead to crash, which is remotely triggerable if autodbs are enabled. The original checks assumed all names come from config files, thus using fatal() was fine, but when autodbs are enabled - by '*' in [databases] section - the database name can come from network thus making remote shutdown possible. [CVE-2012-4575](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-4575) - Minor Features * max_packet_size - config parameter to tune maximum packet size that is allowed through. Default is kept same: (2G-1), but now it can be made smaller. * In case of unparsable packet header, show it in hex in log and error message. - Fixes * AntiMake: it used $(relpath) and $(abspath) to manipulate pathnames, but the result was build failure when source tree path contained symlinks. The code is now changed to work on plain strings only. * console: now SET can be used to set empty string values. * config.txt: show that all timeouts can be set in floats. This is well-hidden feature introduced in 1.4. **2012-05-29 - PgBouncer 1.5.2 - "Don't Chew, Just Swallow"** - Fixes * Due to mistake, reserve_pool_timeout was taken in microseconds, not seconds, effectively activating reserve pool immediately when pool got full. Now use it as seconds, as was intended. (Noticed by Keyur Govande) **2012-04-17 - PgBouncer 1.5.1 - "Abort, Retry, Ignore?"** - Features * Parameters to tune permissions on unix socket: unix_socket_mode=0777, unix_socket_group=''. - Fixes * Allow empty string for server-side variable - this is needed to get "application_name" properly working, as it's the only parameter that does not have server-side default. * If connect string changes, require refresh of server parameters. Previously PgBouncer continued with old parameters, which breaks in case of Postgres upgrade. * If autodb connect string changes, drop old connections. * cf_setint: Use strtol() instead atoi() to parse integer config parameters. It allows hex, octal and better error detection. * Use sigqueue() to detect union sigval existence - fixes compilation on HPUX. * Remove 'git' command from Makefile, it throws random errors in case of plain-tarball build. * Document stats_period parameter. This tunes the period for stats output. * Require Asciidoc >= 8.4, seems docs are not compatible with earlier versions anymore. * Stop trying to retry on EINTR from close(). **2012-01-05 - PgBouncer 1.5 - "Bouncing Satisfied Clients Since 2007"** If you use more than 8 IPs behind one DNS name, you now need to use EDNS0 protocol to query. Only getaddrinfo_a()/getaddrinfo() and UDNS backends support it, libevent 1.x/2.x does not. To enable it for libc, add 'options edns0' to /etc/resolv.conf. GNU Make 3.81+ is required for building. - Features * Detect DNS reply changes and invalidate connections to IPs no longer present in latest reply. (Petr Jelinek) * DNS zone serial based hostname invalidation. When option dns_zone_check_period is set, all DNS zones will be queried for SOA, and when serial has changed, all hostnames will be queried. This is needed to get deterministic connection invalidation, because invalidation on lookup is useless when no lookups are performed. Works only with new UDNS backend. * New SHOW DNS_HOSTS, SHOW DNS_ZONES commands to examine DNS cache. * New param: min_pool_size - avoids dropping all connections when there is no load. (Filip Rembiałkowski) * idle_in_transaction_timeout - kill transaction if idle too long. Not set by default. * New libudns backend for DNS lookups. More featureful than evdns. Use --with-udns to activate. Does not work with IPv6 yet. * KILL command, to immediately kill all connections for one database. (Michael Tharp) * Move to Antimake build system to have better looking Makefiles. Now GNU Make 3.81+ is required for building. - Fixes * DNS now works with IPv6 hostnames. * Don't change connection state when NOTIFY arrives from server. * Various documentation fixes. (Dan McGee) * Console: Support ident quoting with "". Originally we did not have any commands that took database names, so no quoting was needed. * Console: allow numbers at the start of word regex. Trying to use strict parser makes things too complex here. * Don't expire auto DBs that are paused. (Michael Tharp) * Create auto databases as needed when doing PAUSE. (Michael Tharp) * Fix wrong log message issued by RESUME command. (Peter Eisentraut) * When user= without password= is in database connect string, password will be taken from userlist. * Parse '*' properly in takeover code. * autogen.sh: work with older autoconf/automake. * Fix run-as-service crash on win32 due to bad basename() from mingw/msvc runtime. Now compat basename() is always used. PgBouncer 1.4.x --------------- **2011-06-16 - PgBouncer 1.4.2 - "Strike-First Algorithm"** Affected OS-es: \*BSD, Solaris, Win32. - Portability Fixes * Give CFLAGS to linker. Needed when using pthread-based getaddrinfo_a() fallback. * lib/find_modules.sh: Replace split() with index()+substr(). This should make it work with older AWKs. * : Ignore system htoX/Xtoh defines. There may be only subset of macros defined. * : Separate compat sigval from compat sigevent * : Include to get iovec * : Better function autodetection on win32 * : Remove duplicate sigval/sigevent declaration **2011-04-01 - PgBouncer 1.4.1 - "It Was All An Act"** - Features * Support listening/connect for IPv6 addresses. (Hannu Krosing) * Multiple listen addresses in 'listen_addr'. For each getaddrinfo() is called, so names can also be used. * console: Send PgBouncer version as 'server_version' to client. - Important Fixes * Disable getaddrinfo_a() on glibc < 2.9 as it crashes on older versions. Notable affected OS'es: RHEL/CentOS 5.x (glibc 2.5), Ubuntu 8.04 (glibc 2.7). Also Debian/lenny (glibc 2.7) which has non-crashing getaddrinfo_a() but we have no good way to detect it. Please use libevent 2.x on such OS'es, fallback getaddrinfo_a() is not meant for production systems. And read new 'DNS lookup support' section in README to see how DNS backend is picked. (Hubert Depesz Lubaczewski, Dominique Hermsdorff, David Sommerseth) * Default to --enable-evdns if libevent 2.x is used. * Turn on tcp_keepalive by default, as that's what Postgres also does. (Hubert Depesz Lubaczewski) * Set default server_reset_query to DISCARD ALL to be compatible with Postgres by default. * win32: Fix crashes with NULL unix socket addr. (Hiroshi Saito) * Fix autodb cleanup: old cleanup code was mixing up databases and pools: as soon as one empty pool was found, the database was tagged as 'idle', potentially later killing database with active users. Reported-By: Hubert Depesz Lubaczewski - Fixes * Make compat getaddrinfo_a() non-blocking, by using single parallel thread to do lookups. * Enable pthread compilation if compat getaddrinfo_a is used. * release_server missed setting ->last_lifetime_disconnect on lifetime disconnect. (Emmanuel Courreges) * win32: fix auth file on DOS line endings - load_file() did not take account of file shringage when loading. (Rich Schaaf) * : add autoconf detection for enc/dec functions so it would not create conflicts on BSD. (James Pye) * Don't crash when config file does not exist. (Lou Picciano) * Don't crash on DNS lookup failure when logging on noise level (-v -v). (Hubert Depesz Lubaczewski, Dominique Hermsdorff) * Use backticks instead of $(cmd) in find_modules.sh to make it more portable. (Lou Picciano) * Use 'awk' instead of 'sed' in find_modules.sh to make it more portable. (Giorgio Valoti) * Log active async DNS backend info on startup. * Fix --disable-evdns to mean 'no' instead 'yes'. * Mention in docs that -R requires unix_socket_dir. * Discuss server_reset_query in faq.txt. * Restore lost memset in slab allocator * Various minor portability fixes in libusual. **2011-01-11 - PgBouncer 1.4 - "Gore Code"** - Features * Async DNS lookup - instead of resolving hostnames at reload time, the names are now resolved at connect time, with configurable caching. (See dns_max_ttl parameter.) By default it uses getaddrinfo_a() (glibc) as backend, if it does not exist, then getaddrinfo_a() is emulated via blocking(!) getaddrinfo(). When --enable-evdns argument to configure, libevent's evdns is used as backend. It is not used by default, because libevent 1.3/1.4 contain buggy implementation. Only evdns in libevent 2.0 seems OK. * New config var: syslog_ident, to tune syslog name. * Proper support for `application_name` startup parameter. * Command line long options (Guillaume Lelarge) * Solaris portability fixes (Hubert Depesz Lubaczewski) * New config var: disable_pqexec. Highly-paranoid environments can disable Simple Query Protocol with that. Requires apps that use only Extended Query Protocol. * Postgres compat: if database name is empty in startup packet, use user name as database. - Fixes * DateStyle and TimeZone server params need to use exact case. * Console: send datetime, timezone and stdstr server params to client. - Internal cleanups * Use libusual library for low-level utility functions. * Remove fixed-length limit from server params. PgBouncer 1.3.x --------------- **2010-09-09 - PgBouncer 1.3.4 - "Bouncer is always right"** - Fixes * Apply fast-fail logic at connect time. So if server is failing, the clients get error when connecting. * Don't tag automatically generated databases for checking on reload time, otherwise they get killed, because they don't exist in config. * Ignore application_name parameter by default. This avoids the need for all Postgres 9.0 users to add it into ignore_startup_parameters= themselves. * Correct pg_auth quoting. '\' is not used there. * Better error reporting on console, show incoming query to user. * Support OS'es (OpenBSD) where tv_sec is not time_t. * Avoid too noisy warnings on gcc 4.5. **2010-05-10 - PgBouncer 1.3.3 - "NSFW"** - Improvements * Make listen(2) argument configurable: listen_backlog. This is useful on OS'es, where system max allowed is configurable. * Improve disconnect messages to show what username or dbname caused login to fail. - Fixes * Move fast-fail relaunch logic around. Old one was annoying in case of permanently broken databases or users, by trying to retry even if there is no clients who want to login. * Make logging functions keep old errno, otherwise pgbouncer may act funny on higher loglevels and logging problems. * Increase the size of various startup-related buffers to handle EDB more noisy startup. * Detect V2 protocol startup request and give clear reason for disconnect. **2010-03-15 - PgBouncer 1.3.2 - "Boomerang Bullet"** - Fixes * New config var 'query_wait_timeout'. If client does not get server connection in this many seconds, it will be killed. * If no server connection in pool and last connect failed, then don't put client connections on hold but send error immediately. This together with previous fix avoids unnecessary stalls if a database has gone down. * Track libevent state in sbuf.c to avoid double event_del(). Although it usually is safe, it does not seem to work 100%. Now we should always know whether it has been called or not. * Disable maintenance during SUSPEND. Otherwise with short timeouts the old bouncer could close few connections after sending them over. * Apply client_login_timeout to clients waiting for welcome packet (first server connection). Otherwise they can stay waiting infinitely, unless there is query_timeout set. * win32: Add switch -U/-P to -regservice to let user pick account to run service under. Old automatic choice between Local Service and Local System was not reliable enough. * console: Remove \0 from end of text columns. It was hard to notice, as C clients were fine with it. * Documentation improvements. (Greg Sabino Mullane) * Clarify few login-related log messages. * Change logging level for pooler-sent errors (usually on disconnect) from INFO to WARNING, as they signify problems. * Change log message for query_timeout to "query timeout". **2009-07-06 - PgBouncer 1.3.1 - "Now fully conforming to NSA monitoring requirements"** - Fixes * Fix problem with sbuf_loopcnt which could make connections hang. If query or result length is nearby of multiple of (pktlen*sbuf_loopcnt) [10k by default], it could stay waiting for more data which will not appear. * Make database reconfigure immediate. Currently old connections could be reused after SIGHUP. * Fix SHOW DATABASES which was broken due to column addition. * Console access was disabled when "auth_type=any" as pgbouncer dropped username. Fix: if "auth_type=any", allow any user to console as admin. * Fix bad CUSTOM_ALIGN macro. Luckily it's unused if OS already defines ALIGN macro thus seems the bug has not happened in wild. * win32: call WSAStartup() always, not only in daemon mode as config parsing wants to resolve hosts. * win32: put quotes around config filename in service cmdline to allow spaces in paths. Executable path does not seem to need it due to some win32 magic. * Add STATS to SHOW HELP text. * doc/usage.txt: the time units in console results are in microseconds, not milliseconds. **2009-02-18 - PgBouncer 1.3 - "New Ki-Smash Finishing Move"** - Features * IANA has assigned port 6432 to be official port for PgBouncer. Thus the default port number has changed to 6432. Existing individual users do not need to change, but if you distribute packages of PgBouncer, please change the package default to official port. * Dynamic database creation (David Galoyan) Now you can define database with name "*". If defined, it's connect string will be used for all undefined databases. Useful mostly for test / dev environments. * Windows support (Hiroshi Saito) PgBouncer runs on Windows 2000+ now. Command line usage stays same, except it cannot run as daemon and cannot do online reboot. To run as service, define parameter service_name in config. Then: > pgbouncer.exe config.ini -regservice > net start SERVICE_NAME To stop and unregister: > net stop SERVICE_NAME > pgbouncer.exe config.ini -unregservice To use Windows Event Log, event DLL needs to be registered first: > regsrv32 pgbevent.dll Afterwards you can set "syslog = 1" in config. - Minor features * Database names in config file can now be quoted with standard SQL ident quoting, to allow non-standard characters in db names. * New tunables: 'reserve_pool_size' and 'reserve_pool_timeout'. In case there are clients in pool that have waited more that 'reserve_pool_timeout' seconds, 'reserve_pool_size' specifies the number of connections that can be added to pool. It can also set per-pool with 'reserve_pool' connection variable. * New tunable 'sbuf_loopcnt' to limit time spent on one socket. In some situations - eg SMP server, local Postgres and fast network - pgbouncer can run recv()->send() loop many times without blocking on either side. But that means other connections will stall for a long time. To make processing more fair, limit the times of doing recv()->send() one socket. If count reaches limit, just proceed processing other sockets. The processing for that socket will resume on next event loop. Thanks to Alexander Schöcke for report and testing. * crypt() authentication is now optional, as it was removed from Postgres. If OS does not provide it, pgbouncer works fine without it. * Add milliseconds to log timestamps. * Replace old MD5 implementation with more compact one. * Update ISC licence with the FSF clarification. - Fixes * In case event_del() reports failure, just proceed with cleanup. Previously pgbouncer retried it, in case the failure was due ENOMEM. But this has caused log floods with infinite repeats, so it seems libevent does not like it. Why event_del() report failure first time is still mystery. * --enable-debug now just toggles whether debug info is stripped from binary. It no longer plays with -fomit-frame-pointer as it's dangerous. * Fix include order, as otherwise system includes could come before internal ones. Was problem for new md5.h include file. * Include COPYRIGHT file in .tgz... PgBouncer 1.2.x --------------- **2008-08-08 - PgBouncer 1.2.3 - "Carefully Selected Bytes"** - Fixes * Disable SO_ACCEPTFILTER code for BSDs which did not work. * Include example etc/userlist.txt in tgz. * Use '$(MAKE)' instead 'make' for recursion (Jørgen Austvik) * Define _GNU_SOURCE as glibc is useless otherwise. * Let the libevent 1.1 pass link test so we can later report "1.3b+ needed" * Detect stale pidfile and remove it. Thanks to Devrim GÜNDÜZ and Bjoern Metzdorf for problem reports and testing. **2008-08-06 - PgBouncer 1.2.2 - "Barf-bag Included"** - Fixes * Remove 'drop_on_error', it was a bad idea. It was added as workaround for broken plan cache behaviour in Postgres, but can cause damage in common case when some queries always return error. **2008-08-04 - PgBouncer 1.2.1 - "Waterproof"** - Features * New parameter 'drop_on_error' - if server throws error the connection will not be reused but dropped after client finished with it. This is needed to refresh plan cache. Automatic refresh does not work even in 8.3. Defaults to 1. - Fixes * SHOW SOCKETS/CLIENTS/SERVERS: Don't crash if socket has no buffer. * Fix infinite loop on SUSPEND if suspend_timeout triggers. - Minor cleanups * Use for 'struct iovec'. * Cancel shutdown (from SIGINT) on RESUME/SIGUSR2, otherwise it will trigger on next PAUSE. * Proper log message if console operation is canceled. **2008-07-29 - PgBouncer 1.2 - "Ordinary Magic Flute"** PgBouncer 1.2 now requires libevent version 1.3b or newer. Older libevent versions crash with new restart code. - Features * Command line option (-u) and config parameter (user=) to support user switching at startup. Also now pgbouncer refuses to run as root. (Jacob Coby) * More descriptive usage text (-h). (Jacob Coby) * New database option: connect_query to allow run a query on new connections before they are taken into use. (Teodor Sigaev) * New config var 'ignore_startup_parameters' to allow and ignore extra parameters in startup packet. By default only 'database' and 'user' are allowed, all others raise error. This is needed to tolerate overenthusiastic JDBC wanting to unconditionally set 'extra_float_digits=2' in startup packet. * Logging to syslog: new parameters syslog=0/1 and syslog_facility=daemon/user/local0. * Less scary online restart (-R) - Move FD loading before fork, so it logs to console and can be canceled by ^C - Keep SHUTDOWN after fork, so ^C would be safe - A connect() is attempted to unix socket to see if anyone is listening. Now -R can be used even when no previous process was running. If there is previous process, but -R is not used, startup fails. * New console commands: - SHOW TOTALS that shows stats summary (as goes to log) plus mem usage. - SHOW ACTIVE_SOCKETS - like show sockets; but filter only active ones. - Less visible features * suspend_timeout - drop stalled conns and long logins. This brings additional safety to reboot. * When remote database throws error on logging in, notify clients. * Removing a database from config and reloading works - all connections are killed and the database is removed. * Fake some parameters on console SHOW/SET commands to be more Postgres-like. That was needed to allow psycopg to connect to console. (client_encoding/default_transaction_isolation/datestyle/timezone) * Make server_lifetime=0 disconnect server connection immediately after first use. Previously "0" made PgBouncer ignore server age. As this behavior was undocumented, there should not be any users depending on it. * Internal improvements: - Packet buffers are allocated lazily and reused. This should bring huge decrease in memory usage. This also makes realistic to use big pktbuf with lot of connections. - Lot's of error handling improvements, PgBouncer should now survive OOM situations gracefully. - Use slab allocator for memory management. - Lots of code cleanups. - Fixes * Only single accept() was issued per event loop which could cause connection backlog when having high amount of connection attempts. Now the listening socket is always drained fully, which should fix this. * Handle EINTR from connect(). * Make configure.ac compatible with autoconf 2.59. * Solaris compatibility fixes (Magne Mæhre) PgBouncer 1.1.x --------------- **2007-12-10 - PgBouncer 1.1.2 - "The Hammer"** - Features * Disconnects because of server_lifetime are now separated by (server_lifetime / pool_size) seconds. This avoids pgbouncer causing reconnect floods. - Fixes * Online upgrade 1.0 -> 1.1 problems: - 1.0 does not track server parameters, so they stay NULL but 1.1 did not expect it and crashed. - If server params are unknown, but client ones are set, then issue a SET for them, instead complaining. * Remove temp debug statements that were accidentally left in code on INFO level, so they polluted logs. * Unbroke debian/changelog - Cleanup * reorder struct SBuf fields to get better alignment for buffer. **2007-10-26 - PgBouncer 1.1.1 - "Breakdancing Bee"** - Fixes * Server parameter cache could stay uninitialized, which caused unnecessary SET of them. This caused problem on 8.1 which does not allow touching standard_conforming_strings. (Thanks to Dimitri Fontaine for report & testing.) * Some doc fixes. * Include doc/fixman.py in .tgz. **2007-10-09 - PgBouncer 1.1 - "Mad-Hat Toolbox"** - Features * Keep track of following server parameters: client_encoding datestyle, timezone, standard_conforming_strings * Database connect string enhancements: - Accept hostname in host= - Accept custom unix socket location in host= - Accept quoted values: password=' asd''foo' * New config var: server_reset_query, to be sent immediately after release * New config var: server_round_robin, to switch between LIFO and RR. * Cancel pkt sent for idle connection does not drop it anymore. * Cancel with ^C from psql works for SUSPEND / PAUSE. * Print FD limits on startup. * When suspending, try to hit packet boundary ASAP. * Add 'timezone' to database parameters. * Use longlived logfile fd. Reopened on SIGHUP / RELOAD; * Local connection endpoint info in SHOW SERVERS/CLIENTS/SOCKETS. - Code cleanup * More debug log messages include socket info. * Magic number removal and error message cleanup. (David Fetter) * Wrapper struct for current pkt info. Removes lot of compexity. - Fixes * Detect invalid pkt headers better. * auth_file modification check was broken, which made pgbouncer reload it too often. PgBouncer 1.0.x --------------- **2007-06-18 - PgBouncer 1.0.8 - "Undead Shovel Jutsu"** - Fixes * Fix crash in cancel packet handling. (^C from psql) - Features * PAUSE ; RESUME ; works now. * Cleanup of console command parsing. * Disable expensive in-list assert check. **2007-04-19 - PgBouncer 1.0.7 - "With Vitamin A-Z"** - Fixes * Several error/notice packets with send() blocking between triggered assert. Fix it by removing flushing logic altogether. As pgbouncer does not actively buffer anything, its not needed. It was a remnant from the time when buffering was pushed to kernel with MSG_MORE. * Additionally avoid calling recv() logic when sending unblocks. * List search code for admin_users and stats_users mishandled partial finds. Fix it. * Standardise UNIX socket peer UID finding to getpeereid(). **2007-04-12 - PgBouncer 1.0.6 - "Daily Dose"** - Fixes * The "Disable maintenance during the takeover" fix could disable maintenance altogether. Fix it. * Compilation fix for FreeBSD, requires there. Thanks go to Robert Gogolok for report. **2007-04-11 - PgBouncer 1.0.5 - "Enough for today"** - Fixes * Fix online-restart bugs: - Set ->ready for idle servers. - Remove obsolete code from use_client_socket() - Disable maintenance during the takeover. **2007-04-11 - PgBouncer 1.0.4 - "Last 'last' bug"** - Fixes * Notice from idle server tagged server dirty. release_server() did not expect it. Fix it by dropping them. **2007-04-11 - PgBouncer 1.0.3 - "Fearless Fork"** - Fixes * Some error handling was missing in login path, so dying connection there could trigger asserts. * Cleanup of asserts in sbuf.c to catch problems earlier. * Create core when Assert() triggers. - New stuff * New config vars: log_connections, log_disconnections, log_pooler_errors to turn on/off noise. * Config var: client_login_timeout to kill dead connections in login phase that could stall SUSPEND and thus online restart. **2007-03-28 - PgBouncer 1.0.2 - "Supersonic Spoon"** - Fixes * libevent may report a deleted event inside same loop. Avoid socket reuse for one loop. * release_server() from disconnect_client() didn't look it the packet was actually sent. **2007-03-15 - PgBouncer 1.0.1 - "Alien technology"** - Fixes * Mixed usage of cached and non-cached time, plus unsigned usec_t typedef created spurious query_timeout errors. * Fix rare case when socket woken up from send-wait could stay stalling. * More fair queueing of server connections. Before, a new query could get a server connections before older one. * Delay server release until everything is guaranteed to be sent. - Features * SHOW SOCKETS command to have detailed info about state state. * Put PgSocket ptr to log, to help tracking one connection. * In console, allow SELECT in place of SHOW. * Various code cleanups. **2007-03-13 - PgBouncer 1.0 - "Tuunitud bemm"** - First public release. pgbouncer-1.24.1/config.sub0000755000175000017500000010511614777762315012516 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | convergent* | ncr* | news | 32* | 3600* | 3100* \ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | ultra | tti* | harris | dolphin | highlevel | gould \ | cbm | ns | masscomp | apple | axis | knuth | cray \ | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read kernel os <&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ | linux-musl* | linux-relibc* | linux-uclibc* ) ;; uclinux-uclibc* ) ;; -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; nto-qnx*) ;; os2-emx) ;; *-eabi* | *-gnueabi*) ;; -*) # Blank kernel with real OS is always fine. ;; *-*) echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pgbouncer-1.24.1/Makefile0000644000175000000000000001254314777762222012154 00000000000000 include config.mak bin_PROGRAMS = pgbouncer pgbouncer_SOURCES = \ src/admin.c \ src/client.c \ src/dnslookup.c \ src/hba.c \ src/janitor.c \ src/loader.c \ src/messages.c \ src/main.c \ src/objects.c \ src/pam.c \ src/pktbuf.c \ src/pooler.c \ src/proto.c \ src/prepare.c \ src/sbuf.c \ src/scram.c \ src/server.c \ src/stats.c \ src/system.c \ src/takeover.c \ src/util.c \ src/varcache.c \ src/common/base64.c \ src/common/bool.c \ src/common/pgstrcasecmp.c \ src/common/saslprep.c \ src/common/scram-common.c \ src/common/unicode_norm.c \ src/common/wchar.c \ include/admin.h \ include/bouncer.h \ include/client.h \ include/dnslookup.h \ include/hba.h \ include/iobuf.h \ include/janitor.h \ include/loader.h \ include/messages.h \ include/objects.h \ include/pam.h \ include/pktbuf.h \ include/pooler.h \ include/proto.h \ include/prepare.h \ include/sbuf.h \ include/scram.h \ include/server.h \ include/stats.h \ include/system.h \ include/takeover.h \ include/util.h \ include/varcache.h \ include/common/base64.h \ include/common/builtins.h \ include/common/pg_wchar.h \ include/common/postgres_compat.h \ include/common/protocol.h \ include/common/saslprep.h \ include/common/scram-common.h \ include/common/unicode_combining_table.h \ include/common/unicode_norm.h \ include/common/unicode_norm_table.h \ include/common/uthash_lowercase.h UTHASH = uthash pgbouncer_CPPFLAGS = -Iinclude $(CARES_CFLAGS) $(LIBEVENT_CFLAGS) $(TLS_CPPFLAGS) pgbouncer_CPPFLAGS += -I$(UTHASH)/src # include libusual sources directly AM_FEATURES = libusual pgbouncer_EMBED_LIBUSUAL = 1 # docs to install as-is dist_doc_DATA = README.md NEWS.md \ etc/pgbouncer-minimal.ini \ etc/pgbouncer.ini \ etc/pgbouncer.service \ etc/pgbouncer.socket \ etc/userlist.txt DISTCLEANFILES = config.mak config.status lib/usual/config.h config.log DIST_SUBDIRS = doc test dist_man_MANS = doc/pgbouncer.1 doc/pgbouncer.5 # files in tgz EXTRA_DIST = AUTHORS COPYRIGHT Makefile config.mak.in config.sub config.guess \ pyproject.toml requirements.txt \ install-sh autogen.sh configure configure.ac \ etc/mkauth.py etc/optscan.sh etc/example.debian.init.sh \ win32/Makefile \ $(LIBUSUAL_DIST) \ $(UTHASH_DIST) \ # libusual files (FIXME: list should be provided by libusual...) LIBUSUAL_DIST = $(filter-out %/config.h, $(sort $(wildcard \ lib/usual/*.[chg] \ lib/usual/*/*.[ch] \ lib/m4/*.m4 \ lib/usual/config.h.in \ lib/mk/*.mk \ lib/mk/antimake.mk lib/mk/antimake.txt \ lib/mk/install-sh lib/mk/std-autogen.sh \ lib/README lib/COPYRIGHT \ lib/find_modules.sh ))) UTHASH_DIST = $(UTHASH)/src/uthash.h \ $(UTHASH)/LICENSE pgbouncer_LDFLAGS := $(TLS_LDFLAGS) pgbouncer_LDADD := $(CARES_LIBS) $(LIBEVENT_LIBS) $(TLS_LIBS) $(LIBS) LIBS := # # win32 # EXTRA_pgbouncer_SOURCES = win32/win32support.c win32/win32support.h win32/win32ver.rc EXTRA_PROGRAMS = pgbevent ifeq ($(PORTNAME),win32) pgbouncer_CPPFLAGS += -Iwin32 pgbouncer_SOURCES += $(EXTRA_pgbouncer_SOURCES) bin_PROGRAMS += pgbevent endif pgbevent_SOURCES = win32/pgbevent.c win32/eventmsg.rc \ win32/eventmsg.mc win32/MSG00001.bin pgbevent_EXT = .dll pgbevent_LINK = $(CC) -shared -Wl,--export-all-symbols -Wl,--add-stdcall-alias -o $@ $^ # .rc->.o AM_LANGUAGES = RC AM_LANG_RC_SRCEXTS = .rc AM_LANG_RC_COMPILE = $(WINDRES) $< -o $@ --include-dir=$(srcdir)/win32 --include-dir=lib AM_LANG_RC_LINK = false # # now load antimake # USUAL_DIR = lib abs_top_srcdir ?= $(CURDIR) include $(abs_top_srcdir)/lib/mk/antimake.mk config.mak: @echo "Please run ./configure" @exit 1 PYTEST = $(shell command -v pytest || echo '$(PYTHON) -m pytest') CONCURRENCY = auto check: all etc/optscan.sh if [ $(CONCURRENCY) = 1 ]; then \ PYTHONIOENCODING=utf8 $(PYTEST); \ else \ PYTHONIOENCODING=utf8 $(PYTEST) -n $(CONCURRENCY); \ fi $(MAKE) -C test check w32zip = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-windows-$(host_cpu).zip zip: $(w32zip) $(w32zip): pgbouncer.exe pgbevent.dll etc/pgbouncer.ini etc/userlist.txt README.md COPYRIGHT rm -rf $(basename $@) mkdir $(basename $@) cp $^ $(basename $@) $(STRIP) $(addprefix $(basename $@)/,$(filter %.exe %.dll,$(^F))) zip -MM $@ $(addprefix $(basename $@)/,$(filter %.exe %.dll,$(^F))) # NB: zip -l for text files for end-of-line conversion zip -MM -l $@ $(addprefix $(basename $@)/,$(filter-out %.exe %.dll,$(^F))) .PHONY: tags tags: ctags src/*.c include/*.h lib/usual/*.[ch] lib/usual/*/*.[ch] htmls: for f in *.md doc/*.md; do \ mkdir -p html && $(PANDOC) $$f -o html/`basename $$f`.html; \ done doc/pgbouncer.1 doc/pgbouncer.5: $(MAKE) -C doc $(@F) lint: flake8 format-check: uncrustify black --check --diff . isort --check --diff . ./uncrustify -c uncrustify.cfg --check include/*.h src/*.c -L WARN format: uncrustify $(MAKE) format-c $(MAKE) format-python format-python: uncrustify black . isort . format-c: uncrustify ./uncrustify -c uncrustify.cfg --replace --no-backup include/*.h src/*.c -L WARN UNCRUSTIFY_VERSION=0.77.1 uncrustify: temp=$$(mktemp -d) \ && cd $$temp \ && curl -L https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-$(UNCRUSTIFY_VERSION).tar.gz --output uncrustify.tar.gz \ && tar xzf uncrustify.tar.gz \ && cd uncrustify-uncrustify-$(UNCRUSTIFY_VERSION) \ && mkdir -p build \ && cd build \ && cmake .. \ && $(MAKE) \ && cp uncrustify $(CURDIR)/uncrustify pgbouncer-1.24.1/uthash/0000755000000000000000000000000014777762567012062 500000000000000pgbouncer-1.24.1/uthash/src/0000755000000000000000000000000014777762567012651 500000000000000pgbouncer-1.24.1/uthash/src/uthash.h0000644000175000000000000022027514777762223014250 00000000000000/* Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTHASH_H #define UTHASH_H #define UTHASH_VERSION 2.3.0 #include /* memcmp, memset, strlen */ #include /* ptrdiff_t */ #include /* exit */ #if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT /* This codepath is provided for backward compatibility, but I plan to remove it. */ #warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead" typedef unsigned int uint32_t; typedef unsigned char uint8_t; #elif defined(HASH_NO_STDINT) && HASH_NO_STDINT #else #include /* uint8_t, uint32_t */ #endif /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ #if !defined(DECLTYPE) && !defined(NO_DECLTYPE) #if defined(_MSC_VER) /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define DECLTYPE(x) (decltype(x)) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE #endif #elif defined(__MCST__) /* Elbrus C Compiler */ #define DECLTYPE(x) (__typeof(x)) #elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) #define NO_DECLTYPE #else /* GNU, Sun and other compilers */ #define DECLTYPE(x) (__typeof(x)) #endif #endif #ifdef NO_DECLTYPE #define DECLTYPE(x) #define DECLTYPE_ASSIGN(dst,src) \ do { \ char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ } while (0) #else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ } while (0) #endif #ifndef uthash_malloc #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ #endif #ifndef uthash_free #define uthash_free(ptr,sz) free(ptr) /* free fcn */ #endif #ifndef uthash_bzero #define uthash_bzero(a,n) memset(a,'\0',n) #endif #ifndef uthash_strlen #define uthash_strlen(s) strlen(s) #endif #ifndef HASH_FUNCTION #define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) #endif #ifndef HASH_KEYCMP #define HASH_KEYCMP(a,b,n) memcmp(a,b,n) #endif #ifndef uthash_noexpand_fyi #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ #endif #ifndef uthash_expand_fyi #define uthash_expand_fyi(tbl) /* can be defined to log expands */ #endif #ifndef HASH_NONFATAL_OOM #define HASH_NONFATAL_OOM 0 #endif #if HASH_NONFATAL_OOM /* malloc failures can be recovered from */ #ifndef uthash_nonfatal_oom #define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ #endif #define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) #define IF_HASH_NONFATAL_OOM(x) x #else /* malloc failures result in lost memory, hash tables are unusable */ #ifndef uthash_fatal #define uthash_fatal(msg) exit(-1) /* fatal OOM error */ #endif #define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") #define IF_HASH_NONFATAL_OOM(x) #endif /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ #define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ /* calculate the element whose hash handle address is hhp */ #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) /* calculate the hash handle from element address elp */ #define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) #define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ do { \ struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ unsigned _hd_bkt; \ HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ (head)->hh.tbl->buckets[_hd_bkt].count++; \ _hd_hh_item->hh_next = NULL; \ _hd_hh_item->hh_prev = NULL; \ } while (0) #define HASH_VALUE(keyptr,keylen,hashv) \ do { \ HASH_FUNCTION(keyptr, keylen, hashv); \ } while (0) #define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ do { \ (out) = NULL; \ if (head) { \ unsigned _hf_bkt; \ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) { \ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ } \ } \ } while (0) #define HASH_FIND(hh,head,keyptr,keylen,out) \ do { \ (out) = NULL; \ if (head) { \ unsigned _hf_hashv; \ HASH_VALUE(keyptr, keylen, _hf_hashv); \ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ } \ } while (0) #ifdef HASH_BLOOM #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) #define HASH_BLOOM_MAKE(tbl,oomed) \ do { \ (tbl)->bloom_nbits = HASH_BLOOM; \ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ if (!(tbl)->bloom_bv) { \ HASH_RECORD_OOM(oomed); \ } else { \ uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ } \ } while (0) #define HASH_BLOOM_FREE(tbl) \ do { \ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ } while (0) #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) #define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0) #define HASH_BLOOM_ADD(tbl,hashv) \ HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) #define HASH_BLOOM_TEST(tbl,hashv) \ HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) #else #define HASH_BLOOM_MAKE(tbl,oomed) #define HASH_BLOOM_FREE(tbl) #define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) 1 #define HASH_BLOOM_BYTELEN 0U #endif #define HASH_MAKE_TABLE(hh,head,oomed) \ do { \ (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ if (!(head)->hh.tbl) { \ HASH_RECORD_OOM(oomed); \ } else { \ uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ (head)->hh.tbl->tail = &((head)->hh); \ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ (head)->hh.tbl->signature = HASH_SIGNATURE; \ if (!(head)->hh.tbl->buckets) { \ HASH_RECORD_OOM(oomed); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ } else { \ uthash_bzero((head)->hh.tbl->buckets, \ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ IF_HASH_NONFATAL_OOM( \ if (oomed) { \ uthash_free((head)->hh.tbl->buckets, \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ } \ ) \ } \ } \ } while (0) #define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ do { \ (replaced) = NULL; \ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ if (replaced) { \ HASH_DELETE(hh, head, replaced); \ } \ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ } while (0) #define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ do { \ (replaced) = NULL; \ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ if (replaced) { \ HASH_DELETE(hh, head, replaced); \ } \ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ } while (0) #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ do { \ unsigned _hr_hashv; \ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ } while (0) #define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ do { \ unsigned _hr_hashv; \ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ } while (0) #define HASH_APPEND_LIST(hh, head, add) \ do { \ (add)->hh.next = NULL; \ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ (head)->hh.tbl->tail->next = (add); \ (head)->hh.tbl->tail = &((add)->hh); \ } while (0) #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ do { \ do { \ if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ break; \ } \ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ } while (0) #ifdef NO_DECLTYPE #undef HASH_AKBI_INNER_LOOP #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ do { \ char *_hs_saved_head = (char*)(head); \ do { \ DECLTYPE_ASSIGN(head, _hs_iter); \ if (cmpfcn(head, add) > 0) { \ DECLTYPE_ASSIGN(head, _hs_saved_head); \ break; \ } \ DECLTYPE_ASSIGN(head, _hs_saved_head); \ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ } while (0) #endif #if HASH_NONFATAL_OOM #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ do { \ if (!(oomed)) { \ unsigned _ha_bkt; \ (head)->hh.tbl->num_items++; \ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ if (oomed) { \ HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ HASH_DELETE_HH(hh, head, &(add)->hh); \ (add)->hh.tbl = NULL; \ uthash_nonfatal_oom(add); \ } else { \ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ } \ } else { \ (add)->hh.tbl = NULL; \ uthash_nonfatal_oom(add); \ } \ } while (0) #else #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ do { \ unsigned _ha_bkt; \ (head)->hh.tbl->num_items++; \ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ } while (0) #endif #define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ do { \ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ (add)->hh.hashv = (hashval); \ (add)->hh.key = (char*) (keyptr); \ (add)->hh.keylen = (unsigned) (keylen_in); \ if (!(head)) { \ (add)->hh.next = NULL; \ (add)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh, add, _ha_oomed); \ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ (head) = (add); \ IF_HASH_NONFATAL_OOM( } ) \ } else { \ void *_hs_iter = (head); \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ if (_hs_iter) { \ (add)->hh.next = _hs_iter; \ if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ } else { \ (head) = (add); \ } \ HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ } else { \ HASH_APPEND_LIST(hh, head, add); \ } \ } \ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ } while (0) #define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ do { \ unsigned _hs_hashv; \ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ } while (0) #define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) #define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) #define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ do { \ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ (add)->hh.hashv = (hashval); \ (add)->hh.key = (const void*) (keyptr); \ (add)->hh.keylen = (unsigned) (keylen_in); \ if (!(head)) { \ (add)->hh.next = NULL; \ (add)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh, add, _ha_oomed); \ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ (head) = (add); \ IF_HASH_NONFATAL_OOM( } ) \ } else { \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_APPEND_LIST(hh, head, add); \ } \ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ } while (0) #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_hashv; \ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ } while (0) #define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) #define HASH_TO_BKT(hashv,num_bkts,bkt) \ do { \ bkt = ((hashv) & ((num_bkts) - 1U)); \ } while (0) /* delete "delptr" from the hash table. * "the usual" patch-up process for the app-order doubly-linked-list. * The use of _hd_hh_del below deserves special explanation. * These used to be expressed using (delptr) but that led to a bug * if someone used the same symbol for the head and deletee, like * HASH_DELETE(hh,users,users); * We want that to work, but by changing the head (users) below * we were forfeiting our ability to further refer to the deletee (users) * in the patch-up process. Solution: use scratch space to * copy the deletee pointer, then the latter references are via that * scratch pointer rather than through the repointed (users) symbol. */ #define HASH_DELETE(hh,head,delptr) \ HASH_DELETE_HH(hh, head, &(delptr)->hh) #define HASH_DELETE_HH(hh,head,delptrhh) \ do { \ const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head) = NULL; \ } else { \ unsigned _hd_bkt; \ if (_hd_hh_del == (head)->hh.tbl->tail) { \ (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ } \ if (_hd_hh_del->prev != NULL) { \ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ } else { \ DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ } \ if (_hd_hh_del->next != NULL) { \ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ } \ HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ (head)->hh.tbl->num_items--; \ } \ HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ } while (0) /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ #define HASH_FIND_STR(head,findstr,out) \ do { \ unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ } while (0) #define HASH_ADD_STR(head,strfield,add) \ do { \ unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ } while (0) #define HASH_REPLACE_STR(head,strfield,add,replaced) \ do { \ unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ } while (0) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(int),add) #define HASH_REPLACE_INT(head,intfield,add,replaced) \ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) #define HASH_FIND_PTR(head,findptr,out) \ HASH_FIND(hh,head,findptr,sizeof(void *),out) #define HASH_ADD_PTR(head,ptrfield,add) \ HASH_ADD(hh,head,ptrfield,sizeof(void *),add) #define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) #define HASH_DEL(head,delptr) \ HASH_DELETE(hh,head,delptr) /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. */ #ifdef HASH_DEBUG #include /* fprintf, stderr */ #define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) #define HASH_FSCK(hh,head,where) \ do { \ struct UT_hash_handle *_thh; \ if (head) { \ unsigned _bkt_i; \ unsigned _count = 0; \ char *_prev; \ for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ unsigned _bkt_count = 0; \ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ _prev = NULL; \ while (_thh) { \ if (_prev != (char*)(_thh->hh_prev)) { \ HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ (where), (void*)_thh->hh_prev, (void*)_prev); \ } \ _bkt_count++; \ _prev = (char*)(_thh); \ _thh = _thh->hh_next; \ } \ _count += _bkt_count; \ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ } \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ (where), (head)->hh.tbl->num_items, _count); \ } \ _count = 0; \ _prev = NULL; \ _thh = &(head)->hh; \ while (_thh) { \ _count++; \ if (_prev != (char*)_thh->prev) { \ HASH_OOPS("%s: invalid prev %p, actual %p\n", \ (where), (void*)_thh->prev, (void*)_prev); \ } \ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ (where), (head)->hh.tbl->num_items, _count); \ } \ } \ } while (0) #else #define HASH_FSCK(hh,head,where) #endif /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ do { \ unsigned _klen = fieldlen; \ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ } while (0) #else #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ #define HASH_BER(key,keylen,hashv) \ do { \ unsigned _hb_keylen = (unsigned)keylen; \ const unsigned char *_hb_key = (const unsigned char*)(key); \ (hashv) = 0; \ while (_hb_keylen-- != 0U) { \ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ } \ } while (0) /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx * (archive link: https://archive.is/Ivcan ) */ #define HASH_SAX(key,keylen,hashv) \ do { \ unsigned _sx_i; \ const unsigned char *_hs_key = (const unsigned char*)(key); \ hashv = 0; \ for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ } \ } while (0) /* FNV-1a variation */ #define HASH_FNV(key,keylen,hashv) \ do { \ unsigned _fn_i; \ const unsigned char *_hf_key = (const unsigned char*)(key); \ (hashv) = 2166136261U; \ for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ hashv = hashv ^ _hf_key[_fn_i]; \ hashv = hashv * 16777619U; \ } \ } while (0) #define HASH_OAT(key,keylen,hashv) \ do { \ unsigned _ho_i; \ const unsigned char *_ho_key=(const unsigned char*)(key); \ hashv = 0; \ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ hashv += _ho_key[_ho_i]; \ hashv += (hashv << 10); \ hashv ^= (hashv >> 6); \ } \ hashv += (hashv << 3); \ hashv ^= (hashv >> 11); \ hashv += (hashv << 15); \ } while (0) #define HASH_JEN_MIX(a,b,c) \ do { \ a -= b; a -= c; a ^= ( c >> 13 ); \ b -= c; b -= a; b ^= ( a << 8 ); \ c -= a; c -= b; c ^= ( b >> 13 ); \ a -= b; a -= c; a ^= ( c >> 12 ); \ b -= c; b -= a; b ^= ( a << 16 ); \ c -= a; c -= b; c ^= ( b >> 5 ); \ a -= b; a -= c; a ^= ( c >> 3 ); \ b -= c; b -= a; b ^= ( a << 10 ); \ c -= a; c -= b; c ^= ( b >> 15 ); \ } while (0) #define HASH_JEN(key,keylen,hashv) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ unsigned const char *_hj_key=(unsigned const char*)(key); \ hashv = 0xfeedbeefu; \ _hj_i = _hj_j = 0x9e3779b9u; \ _hj_k = (unsigned)(keylen); \ while (_hj_k >= 12U) { \ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + ( (unsigned)_hj_key[2] << 16 ) \ + ( (unsigned)_hj_key[3] << 24 ) ); \ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + ( (unsigned)_hj_key[6] << 16 ) \ + ( (unsigned)_hj_key[7] << 24 ) ); \ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + ( (unsigned)_hj_key[10] << 16 ) \ + ( (unsigned)_hj_key[11] << 24 ) ); \ \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ \ _hj_key += 12; \ _hj_k -= 12U; \ } \ hashv += (unsigned)(keylen); \ switch ( _hj_k ) { \ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ default: ; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ } while (0) /* The Paul Hsieh hash function */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif #define HASH_SFH(key,keylen,hashv) \ do { \ unsigned const char *_sfh_key=(unsigned const char*)(key); \ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ \ unsigned _sfh_rem = _sfh_len & 3U; \ _sfh_len >>= 2; \ hashv = 0xcafebabeu; \ \ /* Main loop */ \ for (;_sfh_len > 0U; _sfh_len--) { \ hashv += get16bits (_sfh_key); \ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ hashv = (hashv << 16) ^ _sfh_tmp; \ _sfh_key += 2U*sizeof (uint16_t); \ hashv += hashv >> 11; \ } \ \ /* Handle end cases */ \ switch (_sfh_rem) { \ case 3: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 16; \ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ hashv += hashv >> 11; \ break; \ case 2: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 11; \ hashv += hashv >> 17; \ break; \ case 1: hashv += *_sfh_key; \ hashv ^= hashv << 10; \ hashv += hashv >> 1; \ break; \ default: ; \ } \ \ /* Force "avalanching" of final 127 bits */ \ hashv ^= hashv << 3; \ hashv += hashv >> 5; \ hashv ^= hashv << 4; \ hashv += hashv >> 17; \ hashv ^= hashv << 25; \ hashv += hashv >> 6; \ } while (0) /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ do { \ if ((head).hh_head != NULL) { \ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ } else { \ (out) = NULL; \ } \ while ((out) != NULL) { \ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ break; \ } \ } \ if ((out)->hh.hh_next != NULL) { \ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ } else { \ (out) = NULL; \ } \ } \ } while (0) /* add an item to a bucket */ #define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ do { \ UT_hash_bucket *_ha_head = &(head); \ _ha_head->count++; \ (addhh)->hh_next = _ha_head->hh_head; \ (addhh)->hh_prev = NULL; \ if (_ha_head->hh_head != NULL) { \ _ha_head->hh_head->hh_prev = (addhh); \ } \ _ha_head->hh_head = (addhh); \ if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ && !(addhh)->tbl->noexpand) { \ HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ IF_HASH_NONFATAL_OOM( \ if (oomed) { \ HASH_DEL_IN_BKT(head,addhh); \ } \ ) \ } \ } while (0) /* remove an item from a given bucket */ #define HASH_DEL_IN_BKT(head,delhh) \ do { \ UT_hash_bucket *_hd_head = &(head); \ _hd_head->count--; \ if (_hd_head->hh_head == (delhh)) { \ _hd_head->hh_head = (delhh)->hh_next; \ } \ if ((delhh)->hh_prev) { \ (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ } \ if ((delhh)->hh_next) { \ (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ } \ } while (0) /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of * the hash function as it applies to the key domain). * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write * * ceil(n/b) = (n/b) + ((n%b)?1:0) * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) * */ #define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ do { \ unsigned _he_bkt; \ unsigned _he_bkt_i; \ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ if (!_he_new_buckets) { \ HASH_RECORD_OOM(oomed); \ } else { \ uthash_bzero(_he_new_buckets, \ sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ (tbl)->ideal_chain_maxlen = \ ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ (tbl)->nonideal_items = 0; \ for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ while (_he_thh != NULL) { \ _he_hh_nxt = _he_thh->hh_next; \ HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ _he_newbkt = &(_he_new_buckets[_he_bkt]); \ if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ (tbl)->nonideal_items++; \ if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ _he_newbkt->expand_mult++; \ } \ } \ _he_thh->hh_prev = NULL; \ _he_thh->hh_next = _he_newbkt->hh_head; \ if (_he_newbkt->hh_head != NULL) { \ _he_newbkt->hh_head->hh_prev = _he_thh; \ } \ _he_newbkt->hh_head = _he_thh; \ _he_thh = _he_hh_nxt; \ } \ } \ uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ (tbl)->num_buckets *= 2U; \ (tbl)->log2_num_buckets++; \ (tbl)->buckets = _he_new_buckets; \ (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ ((tbl)->ineff_expands+1U) : 0U; \ if ((tbl)->ineff_expands > 1U) { \ (tbl)->noexpand = 1; \ uthash_noexpand_fyi(tbl); \ } \ uthash_expand_fyi(tbl); \ } \ } while (0) /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ /* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ do { \ unsigned _hs_i; \ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ if (head != NULL) { \ _hs_insize = 1; \ _hs_looping = 1; \ _hs_list = &((head)->hh); \ while (_hs_looping != 0U) { \ _hs_p = _hs_list; \ _hs_list = NULL; \ _hs_tail = NULL; \ _hs_nmerges = 0; \ while (_hs_p != NULL) { \ _hs_nmerges++; \ _hs_q = _hs_p; \ _hs_psize = 0; \ for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ _hs_psize++; \ _hs_q = ((_hs_q->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ if (_hs_q == NULL) { \ break; \ } \ } \ _hs_qsize = _hs_insize; \ while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ if (_hs_psize == 0U) { \ _hs_e = _hs_q; \ _hs_q = ((_hs_q->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ _hs_qsize--; \ } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ _hs_e = _hs_p; \ if (_hs_p != NULL) { \ _hs_p = ((_hs_p->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ } \ _hs_psize--; \ } else if ((cmpfcn( \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ )) <= 0) { \ _hs_e = _hs_p; \ if (_hs_p != NULL) { \ _hs_p = ((_hs_p->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ } \ _hs_psize--; \ } else { \ _hs_e = _hs_q; \ _hs_q = ((_hs_q->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ _hs_qsize--; \ } \ if ( _hs_tail != NULL ) { \ _hs_tail->next = ((_hs_e != NULL) ? \ ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ } else { \ _hs_list = _hs_e; \ } \ if (_hs_e != NULL) { \ _hs_e->prev = ((_hs_tail != NULL) ? \ ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ } \ _hs_tail = _hs_e; \ } \ _hs_p = _hs_q; \ } \ if (_hs_tail != NULL) { \ _hs_tail->next = NULL; \ } \ if (_hs_nmerges <= 1U) { \ _hs_looping = 0; \ (head)->hh.tbl->tail = _hs_tail; \ DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ } \ _hs_insize *= 2U; \ } \ HASH_FSCK(hh, head, "HASH_SRT"); \ } \ } while (0) /* This function selects items from one hash into another hash. * The end result is that the selected items have dual presence * in both hashes. There is no copy of the items made; rather * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ unsigned _src_bkt, _dst_bkt; \ void *_last_elt = NULL, *_elt; \ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ if ((src) != NULL) { \ for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ _src_hh != NULL; \ _src_hh = _src_hh->hh_next) { \ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ if (cond(_elt)) { \ IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ _dst_hh->key = _src_hh->key; \ _dst_hh->keylen = _src_hh->keylen; \ _dst_hh->hashv = _src_hh->hashv; \ _dst_hh->prev = _last_elt; \ _dst_hh->next = NULL; \ if (_last_elt_hh != NULL) { \ _last_elt_hh->next = _elt; \ } \ if ((dst) == NULL) { \ DECLTYPE_ASSIGN(dst, _elt); \ HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ IF_HASH_NONFATAL_OOM( \ if (_hs_oomed) { \ uthash_nonfatal_oom(_elt); \ (dst) = NULL; \ continue; \ } \ ) \ } else { \ _dst_hh->tbl = (dst)->hh_dst.tbl; \ } \ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ (dst)->hh_dst.tbl->num_items++; \ IF_HASH_NONFATAL_OOM( \ if (_hs_oomed) { \ HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ _dst_hh->tbl = NULL; \ uthash_nonfatal_oom(_elt); \ continue; \ } \ ) \ HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ _last_elt = _elt; \ _last_elt_hh = _dst_hh; \ } \ } \ } \ } \ HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ } while (0) #define HASH_CLEAR(hh,head) \ do { \ if ((head) != NULL) { \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head) = NULL; \ } \ } while (0) #define HASH_OVERHEAD(hh,head) \ (((head) != NULL) ? ( \ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ sizeof(UT_hash_table) + \ (HASH_BLOOM_BYTELEN))) : 0U) #ifdef NO_DECLTYPE #define HASH_ITER(hh,head,el,tmp) \ for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) #else #define HASH_ITER(hh,head,el,tmp) \ for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) #endif /* obtain a count of items in the hash */ #define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) typedef struct UT_hash_bucket { struct UT_hash_handle *hh_head; unsigned count; /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. * (The multiplier is simply expand_mult+1). The whole idea of this * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; } UT_hash_bucket; /* random signature used only to find hash tables in external analysis */ #define HASH_SIGNATURE 0xa0111fe1u #define HASH_BLOOM_SIGNATURE 0xb12220f2u typedef struct UT_hash_table { UT_hash_bucket *buckets; unsigned num_buckets, log2_num_buckets; unsigned num_items; struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ /* in an ideal situation (all buckets used equally), no bucket would have * more than ceil(#items/#buckets) items. that's the ideal chain length. */ unsigned ideal_chain_maxlen; /* nonideal_items is the number of items in the hash whose chain position * exceeds the ideal chain maxlen. these items pay the penalty for an uneven * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash * function isn't a good fit for the key domain. When expansion is inhibited * the hash will still work, albeit no longer in constant time. */ unsigned ineff_expands, noexpand; uint32_t signature; /* used only to find hash tables in external analysis */ #ifdef HASH_BLOOM uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ uint8_t *bloom_bv; uint8_t bloom_nbits; #endif } UT_hash_table; typedef struct UT_hash_handle { struct UT_hash_table *tbl; void *prev; /* prev element in app order */ void *next; /* next element in app order */ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ struct UT_hash_handle *hh_next; /* next hh in bucket order */ const void *key; /* ptr to enclosing struct's key */ unsigned keylen; /* enclosing struct's key len */ unsigned hashv; /* result of hash-fcn(key) */ } UT_hash_handle; #endif /* UTHASH_H */ pgbouncer-1.24.1/uthash/LICENSE0000644000175000000000000000216314777762223013013 00000000000000Copyright (c) 2005-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pgbouncer-1.24.1/autogen.sh0000755000175000000000000000005214777762222012505 00000000000000#! /bin/sh ./lib/mk/std-autogen.sh ./lib