├── .drone.yml ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── AUTHORS ├── COPYRIGHT ├── Makefile ├── NEWS.rst ├── README.rst ├── autogen.sh ├── config.mak.in ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── copyright ├── pgbouncer.install ├── pgbouncer.service └── rules ├── doc ├── .gitignore ├── Makefile ├── config.rst ├── frag-config-man ├── frag-usage-man ├── todo.rst └── usage.rst ├── etc ├── example.debian.init.sh ├── mkauth.py ├── optscan.sh ├── pgbouncer.ini ├── small.ini ├── smp.ini ├── test.ini ├── test.users └── userlist.txt ├── include ├── admin.h ├── bouncer.h ├── client.h ├── dnslookup.h ├── hba.h ├── iobuf.h ├── janitor.h ├── loader.h ├── objects.h ├── pam.h ├── pktbuf.h ├── pooler.h ├── proto.h ├── sbuf.h ├── server.h ├── smp.h ├── stats.h ├── system.h ├── takeover.h ├── util.h └── varcache.h ├── src ├── admin.c ├── client.c ├── dnslookup.c ├── hba.c ├── janitor.c ├── loader.c ├── main.c ├── objects.c ├── pam.c ├── pktbuf.c ├── pooler.c ├── proto.c ├── sbuf.c ├── server.c ├── smp.c ├── stats.c ├── system.c ├── takeover.c ├── util.c └── varcache.c ├── test ├── Makefile ├── asynctest.c ├── conntest.sh ├── ctest6000.ini ├── ctest7000.ini ├── hba_test.c ├── hba_test.eval ├── hba_test.rules ├── run-conntest.sh ├── ssl │ ├── Makefile │ ├── TestCA1 │ │ ├── ca.crt │ │ ├── ca.key │ │ ├── certs │ │ │ ├── 01.pem │ │ │ ├── 02.pem │ │ │ └── 03.pem │ │ ├── config.ini │ │ ├── index.txt │ │ ├── serial │ │ └── sites │ │ │ ├── 01-localhost.crt │ │ │ ├── 01-localhost.csr │ │ │ ├── 01-localhost.key │ │ │ ├── 02-bouncer.crt │ │ │ ├── 02-bouncer.csr │ │ │ ├── 02-bouncer.key │ │ │ ├── 03-random.crt │ │ │ ├── 03-random.csr │ │ │ └── 03-random.key │ ├── lib.sh │ ├── newca.sh │ ├── newsite.sh │ ├── test.ini │ └── test.sh ├── stress.py ├── test.ini ├── test.sh └── userlist.txt └── win32 ├── MSG00001.bin ├── Makefile ├── eventmsg.mc ├── eventmsg.rc ├── pgbevent.c ├── win32support.c └── win32support.h /.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | build-debian: 3 | image: pexeso/pexeso-build-debian:9.3 4 | commands: 5 | - apt-get update 6 | - apt-get install -y libevent-dev python-docutils devscripts build-essential lintian 7 | - git submodule init 8 | - git submodule update 9 | - ./autogen.sh 10 | - ./configure --with-pmgr 11 | - make 12 | - make deb 13 | - mkdir -p proto/debian/ 14 | - mv ../*.deb proto/debian/ 15 | - ls -lR proto 16 | - dpkg -c proto/debian/*.deb 17 | build-ubuntu: 18 | image: pexeso/pexeso-build-ubuntu:16.04 19 | commands: 20 | - apt-get update 21 | - apt-get install -y libevent-dev python-docutils devscripts build-essential lintian dh-systemd 22 | - git submodule init 23 | - git submodule update 24 | - ./autogen.sh 25 | - ./configure --with-pmgr 26 | - make 27 | - make deb 28 | - mkdir -p proto/ubuntu/ 29 | - mv ../*.deb proto/ubuntu/ 30 | - ls -lR proto 31 | - dpkg -c proto/debian/*.deb 32 | 33 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rst whitespace=-blank-at-eol 2 | doc/frag-*-man whitespace=-blank-at-eof 3 | test/hba_test.rules -whitespace 4 | test/ssl/TestCA1/certs/*.pem whitespace=-blank-at-eol 5 | test/ssl/TestCA1/sites/*.crt whitespace=-blank-at-eol 6 | test/test.sh whitespace=-blank-at-eol 7 | win32/eventmsg.rc whitespace=-blank-at-eol 8 | 9 | # Avoid confusing ASCII underlines with leftover merge conflict markers 10 | *.rst conflict-marker-size=32 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /aclocal.m4 2 | /config.guess 3 | /config.log 4 | /config.mak 5 | /config.status 6 | /config.sub 7 | /configure 8 | /install-sh 9 | /pgbouncer 10 | /.objs 11 | /tags 12 | /test/.objs 13 | /test/hba_test 14 | /test/log 15 | /test/test.log 16 | /test/test.pid 17 | 18 | *.html 19 | *.xml 20 | *.exe 21 | *.gz 22 | *.swp 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib"] 2 | path = lib 3 | url = https://github.com/libusual/libusual.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | addons: 3 | apt: 4 | packages: 5 | - debhelper 6 | - devscripts 7 | - fakeroot 8 | - libc-ares-dev 9 | - libevent-dev 10 | - libudns-dev 11 | - lintian 12 | - python-docutils 13 | compiler: 14 | - clang 15 | - gcc 16 | env: 17 | matrix: 18 | - configure_args='' 19 | - configure_args='--disable-evdns --without-cares --without-udns' 20 | - configure_args='--enable-evdns --without-cares --without-udns' 21 | - configure_args='--disable-evdns --with-cares --without-udns --disable-werror' 22 | - configure_args='--disable-evdns --without-cares --with-udns --disable-werror' 23 | - configure_args='--with-pam' 24 | - configure_args='--with-openssl' 25 | - configure_args='--without-openssl' 26 | script: | 27 | set -e 28 | ./autogen.sh 29 | ./configure --prefix=$HOME/install --enable-cassert --enable-werror --without-cares $configure_args 30 | make 31 | make install 32 | make dist 33 | tar -x -f pgbouncer-*.tar.gz 34 | cd pgbouncer-* 35 | ./configure --prefix=$HOME/install2 --enable-werror --without-cares $configure_args 36 | make 37 | make install 38 | cd .. 39 | make distclean 40 | ./configure 41 | make deb 42 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | 2 | Maintainers 3 | ----------- 4 | Marko Kreen 5 | Petr Jelinek 6 | 7 | Contributors 8 | ------------ 9 | Alexander Schöcke 10 | Andrew Dunstan 11 | Bjoern Metzdorf 12 | Bob Poekert 13 | Christoph Berg 14 | Cody Cutrer 15 | Dan McGee 16 | David Fetter 17 | David Galoyan 18 | David Sommerseth 19 | Devrim GÜNDÜZ 20 | Dimitri Fontaine 21 | Dmitriy Olshevskiy 22 | Dominique Hermsdorff 23 | Emmanuel Courreges 24 | Eric Radman 25 | Euler Taveira 26 | Filip Rembiałkowski 27 | Giorgio Valoti 28 | Guillaume Aubert 29 | Guillaume Lelarge 30 | Greg Sabino Mullane 31 | Hannu Krosing 32 | Heikki Linnakangas 33 | Hiroshi Saito 34 | Hubert Depesz Lubaczewski 35 | Jacob Coby 36 | James Pye 37 | Jørgen Austvik 38 | Lou Picciano 39 | Magne Mæhre 40 | Magnus Hagander 41 | Martin Pihlak 42 | Mathieu Fenniak 43 | Michael Tharp 44 | Michał Trojnara 45 | Pavel Stehule 46 | Peter Eisentraut 47 | Pierre-Emmanuel André 48 | Rich Schaaf 49 | Robert Gogolok 50 | Sam McLeod 51 | Teodor Sigaev 52 | William Grant 53 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | PgBouncer - Lightweight connection pooler for PostgreSQL. 2 | 3 | ISC License 4 | 5 | Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 6 | 7 | Permission to use, copy, modify, and/or distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above 9 | copyright notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | include config.mak 3 | 4 | bin_PROGRAMS = pgbouncer 5 | 6 | pgbouncer_SOURCES = \ 7 | src/admin.c \ 8 | src/client.c \ 9 | src/dnslookup.c \ 10 | src/hba.c \ 11 | src/janitor.c \ 12 | src/loader.c \ 13 | src/main.c \ 14 | src/objects.c \ 15 | src/pam.c \ 16 | src/pktbuf.c \ 17 | src/smp.c \ 18 | src/pooler.c \ 19 | src/proto.c \ 20 | src/sbuf.c \ 21 | src/server.c \ 22 | src/stats.c \ 23 | src/system.c \ 24 | src/takeover.c \ 25 | src/util.c \ 26 | src/varcache.c \ 27 | include/admin.h \ 28 | include/bouncer.h \ 29 | include/client.h \ 30 | include/dnslookup.h \ 31 | include/hba.h \ 32 | include/iobuf.h \ 33 | include/janitor.h \ 34 | include/loader.h \ 35 | include/objects.h \ 36 | include/pam.h \ 37 | include/pktbuf.h \ 38 | include/smp.h \ 39 | include/pooler.h \ 40 | include/proto.h \ 41 | include/sbuf.h \ 42 | include/server.h \ 43 | include/stats.h \ 44 | include/system.h \ 45 | include/takeover.h \ 46 | include/util.h \ 47 | include/varcache.h 48 | 49 | pgbouncer_CPPFLAGS = -Iinclude $(CARES_CFLAGS) $(TLS_CPPFLAGS) 50 | 51 | # include libusual sources directly 52 | AM_FEATURES = libusual 53 | pgbouncer_EMBED_LIBUSUAL = 1 54 | 55 | # docs to install as-is 56 | dist_doc_DATA = README.rst NEWS.rst etc/pgbouncer.ini etc/userlist.txt 57 | 58 | DISTCLEANFILES = config.mak config.status lib/usual/config.h config.log 59 | 60 | DIST_SUBDIRS = doc test 61 | dist_man_MANS = doc/pgbouncer.1 doc/pgbouncer.5 62 | 63 | # files in tgz 64 | EXTRA_DIST = AUTHORS COPYRIGHT Makefile config.mak.in config.sub config.guess \ 65 | install-sh autogen.sh configure configure.ac \ 66 | debian/compat debian/changelog debian/control debian/rules debian/copyright \ 67 | etc/mkauth.py etc/example.debian.init.sh \ 68 | win32/Makefile \ 69 | $(LIBUSUAL_DIST) 70 | 71 | # libusual files (FIXME: list should be provided by libusual...) 72 | LIBUSUAL_DIST = $(filter-out %/config.h, $(sort $(wildcard \ 73 | lib/usual/*.[chg] \ 74 | lib/usual/*/*.[ch] \ 75 | lib/m4/*.m4 \ 76 | lib/usual/config.h.in \ 77 | lib/mk/*.mk \ 78 | lib/mk/antimake.mk lib/mk/antimake.txt \ 79 | lib/mk/install-sh lib/mk/std-autogen.sh \ 80 | lib/README lib/COPYRIGHT \ 81 | lib/find_modules.sh ))) 82 | 83 | # 84 | # win32 85 | # 86 | 87 | pgbouncer_LDFLAGS := $(TLS_LDFLAGS) 88 | pgbouncer_LDADD := $(CARES_LIBS) $(TLS_LIBS) $(LIBS) 89 | LIBS := 90 | 91 | EXTRA_pgbouncer_SOURCES = win32/win32support.c win32/win32support.h 92 | EXTRA_PROGRAMS = pgbevent 93 | ifeq ($(PORTNAME),win32) 94 | pgbouncer_CPPFLAGS += -Iwin32 95 | pgbouncer_SOURCES += $(EXTRA_pgbouncer_SOURCES) 96 | bin_PROGRAMS += pgbevent 97 | endif 98 | 99 | pgbevent_SOURCES = win32/pgbevent.c win32/eventmsg.rc \ 100 | win32/eventmsg.mc win32/MSG00001.bin 101 | pgbevent_EXT = .dll 102 | pgbevent_LINK = $(CC) -shared -Wl,--export-all-symbols -Wl,--add-stdcall-alias -o $@ $^ 103 | 104 | # .rc->.o 105 | AM_LANGUAGES = RC 106 | AM_LANG_RC_SRCEXTS = .rc 107 | AM_LANG_RC_COMPILE = $(WINDRES) $< -o $@ --include-dir=$(srcdir)/win32 108 | AM_LANG_RC_LINK = false 109 | 110 | # 111 | # now load antimake 112 | # 113 | 114 | USUAL_DIR = lib 115 | 116 | abs_top_srcdir ?= $(CURDIR) 117 | include $(abs_top_srcdir)/lib/mk/antimake.mk 118 | 119 | config.mak: 120 | @echo "Please run ./configure" 121 | @exit 1 122 | 123 | deb: 124 | debuild -b -us -uc 125 | 126 | w32arch = i686-w64-mingw32 127 | w32zip = pgbouncer-$(PACKAGE_VERSION)-win32.zip 128 | zip: configure clean 129 | rm -rf buildexe 130 | mkdir buildexe 131 | cd buildexe \ 132 | && ../configure --host=$(w32arch) --disable-debug \ 133 | --without-openssl \ 134 | --without-cares \ 135 | --with-libevent=/opt/apps/win32 --enable-evdns \ 136 | && make \ 137 | && $(w32arch)-strip pgbouncer.exe pgbevent.dll \ 138 | && zip pgbouncer.zip pgbouncer.exe pgbevent.dll doc/*.html 139 | zip -l buildexe/pgbouncer.zip etc/pgbouncer.ini etc/userlist.txt 140 | mv buildexe/pgbouncer.zip $(w32zip) 141 | 142 | zip-up: $(w32zip) 143 | rsync $(w32zip) pgf:web/pgbouncer/htdocs/win32/ 144 | 145 | tgz = pgbouncer-$(PACKAGE_VERSION).tar.gz 146 | tgz-up: $(tgz) 147 | rsync $(tgz) pgf:web/pgbouncer/htdocs/testing/ 148 | 149 | .PHONY: tags 150 | tags: 151 | ctags src/*.c include/*.h lib/usual/*.[ch] lib/usual/*/*.[ch] 152 | 153 | htmls: 154 | for f in *.rst doc/*.rst; do \ 155 | mkdir -p html && rst2html $$f > html/`basename $$f`.html; \ 156 | done 157 | 158 | doc/pgbouncer.1 doc/pgbouncer.5: 159 | $(MAKE) -C doc 160 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | PgBouncer with SMP 3 | =================== 4 | 5 | This is a fork of the pgbouncer that adds support for multiprocessing. 6 | 7 | When enabled pgbouncer will fork itself into a manager process 8 | and multiple worker processes. The manager then waits for new connections on 9 | the specified `listen_addr` and then sends the connected client socket to one of 10 | the workers to handle all the requests. 11 | 12 | `See configuration `_. 13 | 14 | Building 15 | --------- 16 | 17 | SMP is enabled by default, but can be disabled by specifying:: 18 | 19 | $ ./configure --without-smp ... 20 | 21 | PgBouncer depends on few things to get compiled: 22 | 23 | * `GNU Make`_ 3.81+ 24 | * libevent_ 2.0 25 | * (optional) OpenSSL_ 1.0.1 for TLS support. 26 | * (optional) `c-ares`_ as alternative to libevent's evdns. 27 | 28 | .. _GNU Make: https://www.gnu.org/software/make/ 29 | .. _libevent: http://libevent.org/ 30 | .. _OpenSSL: https://www.openssl.org/ 31 | .. _`c-ares`: http://c-ares.haxx.se/ 32 | 33 | When dependencies are installed just run:: 34 | 35 | $ ./configure --prefix=/usr/local --with-libevent=libevent-prefix 36 | $ make 37 | $ make install 38 | 39 | If you are building from git, or are building for Windows, please see 40 | separate build instructions below. 41 | 42 | DNS lookup support 43 | ------------------ 44 | 45 | Starting from PgBouncer 1.4, it does hostname lookups at connect 46 | time instead just once at config load time. This requires proper 47 | async DNS implementation. Following list shows supported backends 48 | and their probing order: 49 | 50 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 51 | | backend | parallel | EDNS0 (1) | /etc/hosts | SOA lookup (2) | note | 52 | +============================+==========+===========+============+================+=======================================+ 53 | | c-ares | yes | yes | yes | yes | ipv6+CNAME buggy in <=1.10 | 54 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 55 | | udns | yes | yes | no | yes | ipv4-only | 56 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 57 | | evdns, libevent 2.x | yes | no | yes | no | does not check /etc/hosts updates | 58 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 59 | | getaddrinfo_a, glibc 2.9+ | yes | yes (3) | yes | no | N/A on non-linux | 60 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 61 | | getaddrinfo, libc | no | yes (3) | yes | no | N/A on win32, requires pthreads | 62 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 63 | | evdns, libevent 1.x | yes | no | no | no | buggy | 64 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 65 | 66 | 1. EDNS0 is required to have more than 8 addresses behind one hostname. 67 | 2. SOA lookup is needed to re-check hostnames on zone serial change 68 | 3. To enable EDNS0, add `options edns0` to /etc/resolv.conf 69 | 70 | `./configure` also has flags `--enable-evdns` and `--disable-evdns` which 71 | turn off automatic probing and force use of either `evdns` or `getaddrinfo_a()`. 72 | 73 | PAM authorization 74 | ----------------- 75 | 76 | To enable PAM authorization `./configure` has a flag `--with-pam` (default value is no). When compiled with 77 | PAM support new global authorization type `pam` appears which can be used to validate users through PAM. 78 | 79 | Building from GIT 80 | ----------------- 81 | 82 | Building PgBouncer from GIT requires that you fetch libusual 83 | submodule and generate the header and config files before 84 | you can run configure:: 85 | 86 | $ git clone https://github.com/pgbouncer/pgbouncer.git 87 | $ cd pgbouncer 88 | $ git submodule init 89 | $ git submodule update 90 | $ ./autogen.sh 91 | $ ./configure ... 92 | $ make 93 | $ make install 94 | 95 | Additional packages required: autoconf, automake, libevent-dev, libtool, 96 | autoconf-archive, python-docutils, and pkg-config. 97 | 98 | Building for WIN32 99 | ------------------ 100 | 101 | At the moment only build env tested is MINGW32 / MSYS. Cygwin 102 | and Visual $ANYTHING are untested. Libevent 2.x is required 103 | for DNS hostname lookup. 104 | 105 | Then do the usual:: 106 | 107 | $ ./configure ... 108 | $ make 109 | 110 | If cross-compiling from Unix:: 111 | 112 | $ ./configure --host=i586-mingw32msvc ... 113 | 114 | Running on WIN32 115 | ---------------- 116 | 117 | Running from command-line goes as usual, except -d (daemonize), 118 | -R (reboot) and -u (switch user) switches will not work. 119 | 120 | To run pgbouncer as a Windows service, you need to configure 121 | `service_name` parameter to set name for service. Then:: 122 | 123 | $ pgbouncer -regservice config.ini 124 | 125 | To uninstall service:: 126 | 127 | $ pgbouncer -unregservice config.ini 128 | 129 | To use Windows Event Log, set "syslog = 1" in config file. 130 | But before you need to register pgbevent.dll:: 131 | 132 | $ regsvr32 pgbevent.dll 133 | 134 | To unregister it, do:: 135 | 136 | $ regsvr32 /u pgbevent.dll 137 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | ./lib/mk/std-autogen.sh ./lib 4 | -------------------------------------------------------------------------------- /config.mak.in: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME = @PACKAGE_NAME@ 2 | PACKAGE_TARNAME = @PACKAGE_TARNAME@ 3 | PACKAGE_VERSION = @PACKAGE_VERSION@ 4 | PACKAGE_STRING = @PACKAGE_STRING@ 5 | PACKAGE_URL = @PACKAGE_URL@ 6 | PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ 7 | PORTNAME = @PORTNAME@ 8 | EXEEXT = @EXEEXT@ 9 | HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ 10 | CC = @CC@ 11 | CPP = @CPP@ 12 | CPPFLAGS = @CPPFLAGS@ 13 | CFLAGS = @CFLAGS@ 14 | DEFS = @DEFS@ 15 | WFLAGS = @WFLAGS@ 16 | CXX = @CXX@ 17 | CXXFLAGS = @CXXFLAGS@ 18 | LD = @LD@ 19 | LDFLAGS = @LDFLAGS@ 20 | LIBS = @LIBS@ 21 | AR = @AR@ 22 | ARFLAGS = @ARFLAGS@ 23 | RANLIB = @RANLIB@ 24 | LIBTOOL = @LIBTOOL@ 25 | INSTALL = @INSTALL@ 26 | INSTALL_PROGRAM = @INSTALL_PROGRAM@ 27 | INSTALL_SCRIPT = @INSTALL_SCRIPT@ 28 | INSTALL_DATA = @INSTALL_DATA@ 29 | MKDIR_P = @MKDIR_P@ 30 | SED = @SED@ 31 | AWK = @AWK@ 32 | GREP = @GREP@ 33 | EGREP = @EGREP@ 34 | STRIP = @STRIP@ 35 | prefix = @prefix@ 36 | exec_prefix = @exec_prefix@ 37 | bindir = @bindir@ 38 | includedir = @includedir@ 39 | sbindir = @sbindir@ 40 | libexecdir = @libexecdir@ 41 | datarootdir = @datarootdir@ 42 | datadir = @datadir@ 43 | sysconfdir = @sysconfdir@ 44 | docdir = @docdir@ 45 | mandir = @mandir@ 46 | libdir = @libdir@ 47 | localedir = @localedir@ 48 | pkgdatadir = @pkgdatadir@ 49 | pkgconfigdir = @pkgconfigdir@ 50 | abs_top_srcdir ?= @abs_top_srcdir@ 51 | abs_top_builddir ?= @abs_top_builddir@ 52 | nosub_top_srcdir ?= @top_srcdir@ 53 | nosub_top_builddir ?= @top_builddir@ 54 | 55 | CARES_CFLAGS = @CARES_CFLAGS@ 56 | CARES_LIBS = @CARES_LIBS@ 57 | 58 | TLS_CPPFLAGS = @TLS_CPPFLAGS@ 59 | TLS_LDFLAGS = @TLS_LDFLAGS@ 60 | TLS_LIBS = @TLS_LIBS@ 61 | 62 | RST2MAN = @RST2MAN@ 63 | DLLWRAP = @DLLWRAP@ 64 | DLLTOOL = @DLLTOOL@ 65 | WINDRES = @WINDRES@ 66 | 67 | enable_debug = @enable_debug@ 68 | have_libevent = @have_libevent@ 69 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script. 2 | 3 | AC_INIT(pgbouncer, 1.8.1) 4 | AC_CONFIG_SRCDIR(src/janitor.c) 5 | AC_CONFIG_HEADER(lib/usual/config.h) 6 | AC_PREREQ([2.59]) 7 | 8 | dnl basic init 9 | AC_USUAL_INIT 10 | 11 | dnl Checks for programs. 12 | AC_USUAL_PROGRAM_CHECK 13 | 14 | PKG_PROG_PKG_CONFIG 15 | 16 | dnl check for rst2man 17 | AC_CHECK_PROGS(RST2MAN, rst2man, rst2man) 18 | 19 | dnl check for windows tools 20 | if test "$PORTNAME" = "win32"; then 21 | AC_CHECK_TOOL([WINDRES], [windres]) 22 | AC_CHECK_TOOL([DLLWRAP], [dllwrap]) 23 | AC_CHECK_TOOL([DLLTOOL], [dlltool]) 24 | fi 25 | AC_CHECK_TOOL([STRIP], [strip]) 26 | 27 | dnl Checks for header files. 28 | AC_USUAL_HEADER_CHECK 29 | AC_CHECK_HEADERS([sys/resource.h sys/wait.h]) 30 | 31 | dnl Checks for typedefs, structures, and compiler characteristics. 32 | AC_USUAL_TYPE_CHECK 33 | 34 | dnl autoconf 2.59 does not have UINT macros nor docdir 35 | m4_ifdef([AC_TYPE_UINT8_T], [ 36 | AC_TYPE_UINT8_T 37 | AC_TYPE_UINT32_T 38 | AC_TYPE_UINT64_T 39 | ], [ 40 | datarootdir='${prefix}/share' 41 | docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' 42 | AC_SUBST(datarootdir) 43 | AC_SUBST(docdir) 44 | ]) 45 | 46 | dnl Checks for library functions. 47 | AC_USUAL_FUNCTION_CHECK 48 | AC_SEARCH_LIBS(clock_gettime, rt) 49 | AC_SEARCH_LIBS(getsockname, socket) 50 | AC_SEARCH_LIBS(gethostbyname, nsl) 51 | AC_SEARCH_LIBS(hstrerror, resolv) 52 | AC_CHECK_FUNCS(lstat) 53 | 54 | dnl Find libevent 55 | AC_USUAL_LIBEVENT 56 | 57 | dnl Check for multiprocessing support 58 | smp_support=yes 59 | AC_ARG_WITH(smp, 60 | AC_HELP_STRING([--with-smp],[Enable multiprocessing]), 61 | [ if test "$withval" == no; then 62 | smp_support=no 63 | fi 64 | ], []) 65 | 66 | if test "$smp_support" = "yes"; then 67 | have_pthreads=no 68 | AC_CHECK_HEADERS(pthread.h, [have_pthreads=yes]) 69 | AC_SEARCH_LIBS(pthread_create, pthread, [], [have_pthreads=no]) 70 | if test x"${have_pthreads}" != xyes; then 71 | AC_MSG_ERROR([pthread library should be available for smp support]) 72 | else 73 | smp_support=yes 74 | AC_DEFINE(HAVE_SMP, 1, [smp support]) 75 | fi 76 | fi 77 | 78 | dnl Check for PAM authorization support 79 | pam_support=no 80 | AC_ARG_WITH(pam, 81 | AC_HELP_STRING([--with-pam],[Enable PAM support]), 82 | [ PAM= 83 | if test "$withval" != no; then 84 | have_pthreads=no 85 | # Look for PAM header and lib 86 | AC_CHECK_HEADERS(security/pam_appl.h, [have_pam_header=t]) 87 | AC_CHECK_HEADERS(pthread.h, [have_pthreads=yes]) 88 | AC_SEARCH_LIBS(pam_start, pam, [have_libpam=t]) 89 | AC_SEARCH_LIBS(pthread_create, pthread, [], [have_pthreads=no]) 90 | if test x"${have_pthreads}" != xyes; then 91 | AC_MSG_ERROR([pthread library should be available for PAM support]) 92 | fi 93 | if test x"${have_pam_header}" != x -a x"${have_libpam}" != x -a x"${have_pthreads}" = xyes; then 94 | pam_support=yes 95 | AC_DEFINE(HAVE_PAM, 1, [PAM support]) 96 | fi 97 | fi 98 | ], []) 99 | 100 | ## 101 | ## DNS backend 102 | ## 103 | 104 | # make sure all vars are set 105 | use_cares=auto 106 | use_udns=no 107 | use_evdns=no 108 | 109 | dnl Find c-ares 110 | AC_MSG_CHECKING([whether to use c-ares for DNS lookups]) 111 | AC_ARG_WITH(cares, 112 | AC_HELP_STRING([--with-cares=prefix],[Specify where c-ares is installed]), 113 | [ if test "$withval" = "no"; then 114 | use_cares=no 115 | elif test "$withval" = "yes"; then 116 | use_cares="$withval" 117 | CARES_CFLAGS="" 118 | CARES_LIBS="-lcares" 119 | elif test "$withval" = "auto"; then 120 | use_cares="$withval" 121 | else 122 | use_cares=yes 123 | CARES_CFLAGS="-I$withval/include" 124 | CARES_LIBS="-L$withval/lib -lcares" 125 | fi 126 | ], []) 127 | AC_MSG_RESULT([$use_cares]) 128 | 129 | if test "$use_cares" = "auto"; then 130 | PKG_CHECK_MODULES(CARES, [libcares >= 1.6.0], [use_cares=yes], [use_cares=no]) 131 | fi 132 | 133 | if test "$use_cares" = "yes"; then 134 | AC_DEFINE(USE_CARES, 1, [Use c-ares for name resolution.]) 135 | 136 | # does it support SOA parse 137 | tmp_CFLAGS="$CFLAGS" 138 | tmp_LIBS="$LIBS" 139 | CFLAGS="$CARES_CFLAGS $CFLAGS" 140 | LIBS="$CARES_LIBS $LIBS" 141 | AC_CHECK_FUNCS(ares_parse_soa_reply) 142 | LIBS="$tmp_LIBS" 143 | CFLAGS="$tmp_CFLAGS" 144 | else 145 | 146 | dnl Find libudns 147 | AC_MSG_CHECKING([whether to use libudns]) 148 | AC_ARG_WITH(udns, 149 | AC_HELP_STRING([--with-udns=prefix],[Specify where udns is installed]), 150 | [ if test "$withval" = "no"; then 151 | use_udns=no 152 | elif test "$withval" = "yes"; then 153 | use_udns=yes 154 | else 155 | use_udns=yes 156 | CPPFLAGS="$CPPFLAGS -I$withval/include" 157 | LDFLAGS="$LDFLAGS -L$withval/lib" 158 | fi 159 | ], []) 160 | AC_MSG_RESULT([$use_udns]) 161 | 162 | if test "$use_udns" = "yes"; then 163 | AC_DEFINE(USE_UDNS, 1, [Use UDNS for name resolution.]) 164 | LIBS="-ludns $LIBS" 165 | AC_MSG_CHECKING([whether libudns is available]) 166 | AC_LINK_IFELSE([AC_LANG_SOURCE([ 167 | #include 168 | #include 169 | #include 170 | #include 171 | int main(void) { 172 | struct dns_ctx *ctx = NULL; 173 | dns_init(ctx, 0); 174 | dns_reset(ctx); 175 | } ])], 176 | [AC_MSG_RESULT([found])], 177 | [AC_MSG_ERROR([not found, cannot proceed])]) 178 | 179 | else # !udns 180 | 181 | dnl On libevent 2.x use evdns by default 182 | if test "$ac_cv_func_evdns_base_new" = "yes"; then 183 | use_evdns=yes 184 | fi 185 | 186 | dnl Allow user to override the decision 187 | AC_ARG_ENABLE(evdns, AC_HELP_STRING([--enable-evdns],[Use libevent for DNS lookups (default on libevent 2.x)]), 188 | [use_evdns=$enableval]) 189 | AC_MSG_CHECKING([whether to use libevent for DNS lookups]) 190 | if test "$use_evdns" = "yes"; then 191 | AC_DEFINE(USE_EVDNS, 1, [Use libevent for DNS lookups.]) 192 | AC_MSG_RESULT([yes]) 193 | else 194 | AC_MSG_RESULT([no]) 195 | fi 196 | 197 | dnl Check if need getaddinfo_a compat 198 | if test "$use_udns.$use_cares.$use_evdns" = "no.no.no"; then 199 | AC_USUAL_GETADDRINFO_A 200 | fi 201 | 202 | fi # !udns 203 | 204 | fi # !cares 205 | 206 | ## end of DNS 207 | 208 | AC_USUAL_TLS 209 | 210 | AC_USUAL_DEBUG 211 | AC_USUAL_CASSERT 212 | AC_USUAL_WERROR 213 | 214 | dnl Output findings 215 | AC_OUTPUT([config.mak]) 216 | 217 | dnl If separate build dir, link Makefile over 218 | test -f Makefile || { 219 | echo "Linking Makefile" 220 | ln -s $srcdir/Makefile 221 | } 222 | 223 | echo "" 224 | echo "Results" 225 | echo " c-ares = $use_cares" 226 | echo " evdns = $use_evdns" 227 | echo " udns = $use_udns" 228 | echo " tls = $tls_support" 229 | echo " PAM = $pam_support" 230 | echo " smp = $smp_support" 231 | echo "" 232 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | pgbouncer (1.8.1-2) unstable; urgency=low 2 | 3 | * v1.8.1-2 4 | 5 | -- Peter Eisentraut Wed, 20 Dec 2017 00:00:00 +0000 6 | pgbouncer (1.8.1-1) unstable; urgency=low 7 | 8 | * v1.8.1 9 | 10 | -- Peter Eisentraut Wed, 20 Dec 2017 00:00:00 +0000 11 | 12 | pgbouncer (1.8-1) unstable; urgency=low 13 | 14 | * v1.8 15 | 16 | -- Peter Eisentraut Tue, 19 Dec 2017 00:00:00 +0000 17 | 18 | pgbouncer (1.7.2-1) unstable; urgency=low 19 | 20 | * v1.7.2 21 | 22 | -- Marko Kreen Fri, 26 Feb 2016 10:29:47 +0200 23 | 24 | pgbouncer (1.7.1-1) unstable; urgency=low 25 | 26 | * v1.7.1 27 | 28 | -- Marko Kreen Thu, 18 Feb 2016 18:34:55 +0200 29 | 30 | pgbouncer (1.7-1) unstable; urgency=low 31 | 32 | * v1.7 33 | 34 | -- Marko Kreen Fri, 18 Dec 2015 19:57:16 +0200 35 | 36 | pgbouncer (1.7~rc1-1) unstable; urgency=low 37 | 38 | * 1.7rc1 39 | 40 | -- Marko Kreen Mon, 02 Nov 2015 18:11:58 +0200 41 | 42 | pgbouncer (1.7~dev-1) unstable; urgency=low 43 | 44 | * v1.7dev 45 | 46 | -- Marko Kreen Mon, 03 Aug 2015 23:47:46 +0300 47 | 48 | pgbouncer (1.6-1) unstable; urgency=low 49 | 50 | * v1.6 51 | 52 | -- Petr Jelinek Sat, 01 Aug 2015 11:53:28 +0200 53 | 54 | pgbouncer (1.6rc1-1) unstable; urgency=low 55 | 56 | * v1.6rc1 57 | 58 | -- Marko Kreen Tue, 30 Dec 2014 16:19:51 +0200 59 | 60 | pgbouncer (1.6~dev-1) unstable; urgency=low 61 | 62 | * v1.6dev 63 | 64 | -- Marko Kreen Tue, 08 Oct 2013 00:37:48 +0300 65 | 66 | pgbouncer (1.5.4-1) unstable; urgency=low 67 | 68 | * v1.5.4 69 | 70 | -- Marko Kreen Wed, 28 Nov 2012 12:44:39 +0200 71 | 72 | pgbouncer (1.5.3-1) unstable; urgency=low 73 | 74 | * v1.5.3 75 | 76 | -- Marko Kreen Wed, 12 Sep 2012 13:36:51 +0300 77 | 78 | pgbouncer (1.5.2-1) unstable; urgency=low 79 | 80 | * v1.5.2 81 | 82 | -- Marko Kreen Tue, 29 May 2012 11:02:48 +0300 83 | 84 | pgbouncer (1.5.1-1) unstable; urgency=low 85 | 86 | * v1.5.1 87 | 88 | -- Marko Kreen Tue, 17 Apr 2012 16:04:13 +0300 89 | 90 | pgbouncer (1.5.1rc1-1) unstable; urgency=low 91 | 92 | * v1.5.1rc1 93 | 94 | -- Marko Kreen Wed, 14 Mar 2012 14:31:54 +0200 95 | 96 | pgbouncer (1.5-1) unstable; urgency=low 97 | 98 | * v1.5 99 | 100 | -- Marko Kreen Thu, 05 Jan 2012 14:59:43 +0200 101 | 102 | pgbouncer (1.5rc1-1) unstable; urgency=low 103 | 104 | * v1.5rc1 105 | 106 | -- Marko Kreen Tue, 13 Dec 2011 00:28:43 +0200 107 | 108 | pgbouncer (1.4.3dev1-1) unstable; urgency=low 109 | 110 | * v1.4.3dev1 111 | 112 | -- Marko Kreen Thu, 01 Sep 2011 11:49:12 +0300 113 | 114 | pgbouncer (1.4.2rc1-1) unstable; urgency=low 115 | 116 | * v1.4.2rc1 117 | 118 | -- Marko Kreen Thu, 02 Jun 2011 18:36:27 +0300 119 | 120 | pgbouncer (1.4.1-1) unstable; urgency=low 121 | 122 | * v1.4.1 123 | 124 | -- Marko Kreen Fri, 01 Apr 2011 21:51:54 +0300 125 | 126 | pgbouncer (1.4.1rc5-1) unstable; urgency=low 127 | 128 | * v1.4.1rc5 129 | 130 | -- Marko Kreen Tue, 29 Mar 2011 17:00:55 +0300 131 | 132 | pgbouncer (1.4.1rc3-1) unstable; urgency=low 133 | 134 | * v1.4.1rc4 135 | 136 | -- Marko Kreen Sat, 26 Mar 2011 00:40:50 +0200 137 | 138 | pgbouncer (1.4.1rc3-1) unstable; urgency=low 139 | 140 | * v1.4.1rc3 141 | 142 | -- Marko Kreen Wed, 23 Mar 2011 18:06:01 +0200 143 | 144 | pgbouncer (1.4.1rc2-1) unstable; urgency=low 145 | 146 | * v1.4.1rc2 147 | 148 | -- Marko Kreen Mon, 21 Mar 2011 23:24:00 +0200 149 | 150 | pgbouncer (1.4.1rc1-1) unstable; urgency=low 151 | 152 | * v1.4.1rc1 153 | 154 | -- Marko Kreen Mon, 28 Feb 2011 23:11:27 +0200 155 | 156 | pgbouncer (1.4-1) unstable; urgency=low 157 | 158 | * v1.4 159 | 160 | -- Marko Kreen Wed, 08 Dec 2010 17:30:06 +0200 161 | 162 | pgbouncer (1.4-0rc4) unstable; urgency=low 163 | 164 | * v1.4rc4 165 | 166 | -- Marko Kreen Tue, 30 Nov 2010 11:54:55 +0200 167 | 168 | pgbouncer (1.4-0rc3) unstable; urgency=low 169 | 170 | * v1.4rc3 171 | 172 | -- Marko Kreen Mon, 29 Nov 2010 13:44:46 +0200 173 | 174 | pgbouncer (1.4-0rc2.1) unstable; urgency=low 175 | 176 | * v1.4rc2.1 177 | 178 | -- Marko Kreen Thu, 18 Nov 2010 10:26:25 +0200 179 | 180 | pgbouncer (1.4-0rc2) unstable; urgency=low 181 | 182 | * v1.4rc2 183 | 184 | -- Marko Kreen Wed, 17 Nov 2010 15:11:13 +0200 185 | 186 | pgbouncer (1.4-0rc1.1) unstable; urgency=low 187 | 188 | * v1.4rc1.1 189 | 190 | -- Marko Kreen Wed, 17 Nov 2010 14:10:25 +0200 191 | 192 | pgbouncer (1.4-0rc1) unstable; urgency=low 193 | 194 | * v1.4rc1 195 | 196 | -- Marko Kreen Mon, 08 Nov 2010 11:02:57 +0200 197 | 198 | pgbouncer (1.4-0rc0) unstable; urgency=low 199 | 200 | * v1.4rc0 201 | 202 | -- Marko Kreen Wed, 03 Nov 2010 15:14:10 +0200 203 | 204 | pgbouncer (1.4-0alpha1) unstable; urgency=low 205 | 206 | * v1.4 alpha 1 207 | 208 | -- Marko Kreen Mon, 20 Sep 2010 13:37:15 -0700 209 | 210 | pgbouncer (1.3.3-0rc1) unstable; urgency=low 211 | 212 | * v1.3.3rc1 213 | 214 | -- Marko Kreen Fri, 23 Apr 2010 17:33:07 +0300 215 | 216 | pgbouncer (1.3.2-1) unstable; urgency=low 217 | 218 | * v1.3.2 219 | 220 | -- Marko Kreen Mon, 15 Mar 2010 16:09:16 +0200 221 | 222 | pgbouncer (1.3.2rc1-1) unstable; urgency=low 223 | 224 | * v1.3.2rc1 225 | 226 | -- Marko Kreen Fri, 04 Dec 2009 13:18:26 +0200 227 | 228 | pgbouncer (1.3.1-1) unstable; urgency=low 229 | 230 | * v1.3.1 231 | 232 | -- Marko Kreen Mon, 06 Jul 2009 16:12:53 +0300 233 | 234 | pgbouncer (1.3.1rc1-1) unstable; urgency=low 235 | 236 | * v1.3.1rc1 237 | 238 | -- Marko Kreen Fri, 26 Jun 2009 14:25:42 +0300 239 | 240 | pgbouncer (1.3-1) unstable; urgency=low 241 | 242 | * v1.3 243 | 244 | -- Marko Kreen Wed, 18 Feb 2009 14:11:41 +0200 245 | 246 | pgbouncer (1.3rc1-0) unstable; urgency=low 247 | 248 | * v1.3rc1 249 | 250 | -- Marko Kreen Fri, 16 jan 2009 15:28:49 +0200 251 | 252 | pgbouncer (1.2.3-1) unstable; urgency=low 253 | 254 | * v.1.2.3 255 | 256 | -- Marko Kreen Fri, 08 Aug 2008 14:35:27 +0300 257 | 258 | pgbouncer (1.2.2-1) unstable; urgency=low 259 | 260 | * v1.2.2 261 | 262 | -- Marko Kreen Wed, 06 Aug 2008 09:44:15 +0300 263 | 264 | pgbouncer (1.2.1-1) unstable; urgency=low 265 | 266 | * v1.2.1 267 | 268 | -- Marko Kreen Fri, 01 Aug 2008 13:00:30 +0300 269 | 270 | pgbouncer (1.2-1) unstable; urgency=low 271 | 272 | * v1.2 273 | 274 | -- Marko Kreen Tue, 29 Jul 2008 14:18:59 +0300 275 | 276 | pgbouncer (1.2-0rc1) unstable; urgency=low 277 | 278 | * v1.2rc1 279 | 280 | -- Marko Kreen Wed, 25 Jun 2008 21:02:08 +0300 281 | 282 | pgbouncer (1.1.1-1) unstable; urgency=low 283 | 284 | * PgBouncer 1.1.1 285 | 286 | -- Marko Kreen Mon, 15 Oct 2007 17:41:05 +0300 287 | 288 | pgbouncer (1.0.8-1) unstable; urgency=low 289 | 290 | * Fix crash with ^C from psql. 291 | * PAUSE db; RESUME db; 292 | 293 | -- Marko Kreen Mon, 18 Jun 2007 15:23:32 +0300 294 | 295 | pgbouncer (1.0.7-1) unstable; urgency=low 296 | 297 | * Fix send() blocking on flush packets. 298 | 299 | -- Marko Kreen Wed, 18 Apr 2007 16:43:45 +0300 300 | 301 | pgbouncer (1.0.6-1) unstable; urgency=low 302 | 303 | * previous fix could broke maint. 304 | 305 | -- Marko Kreen Thu, 12 Apr 2007 11:30:53 +0300 306 | 307 | pgbouncer (1.0.5-1) unstable; urgency=low 308 | 309 | * fix online restart bugs. 310 | 311 | -- Marko Kreen Wed, 11 Apr 2007 13:50:50 +0300 312 | 313 | pgbouncer (1.0.4-1) unstable; urgency=low 314 | 315 | * last bug, honestly. 316 | 317 | -- Marko Kreen Wed, 11 Apr 2007 12:03:52 +0300 318 | 319 | pgbouncer (1.0.3-1) unstable; urgency=low 320 | 321 | * more error handling fixes. 322 | 323 | -- Marko Kreen Tue, 10 Apr 2007 17:22:49 +0300 324 | 325 | pgbouncer (1.0.2-1) unstable; urgency=low 326 | 327 | * 2 more bugs. 328 | 329 | -- Marko Kreen Wed, 28 Mar 2007 12:04:39 +0300 330 | 331 | pgbouncer (1.0.1) unstable; urgency=low 332 | 333 | * Couple hotfixes. 334 | 335 | -- Marko Kreen Wed, 14 Mar 2007 21:30:44 +0200 336 | 337 | pgbouncer (1.0) unstable; urgency=low 338 | 339 | * Public release. 340 | 341 | -- Marko Kreen Tue, 13 Mar 2007 17:30:02 +0200 342 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: pgbouncer 2 | Section: database 3 | Priority: extra 4 | Maintainer: Marko Kreen 5 | Standards-Version: 3.6.2 6 | Build-Depends: debhelper (>= 9), make (>= 3.81), python-docutils, 7 | libevent-dev (>= 2.0), libssl-dev (>= 1.0.1), libc-ares-dev (>= 1.7.0) 8 | 9 | Package: pgbouncer 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends}, adduser 12 | Description: Lightweight connection pooler for PostgreSQL 13 | PgBouncer is a PostgreSQL connection pooler. Any target application can be 14 | connected to pgbouncer as if it were a PostgreSQL server, and pgbouncer will 15 | create a connection to the actual server, or it will reuse one of its existing 16 | connections. 17 | . 18 | The aim of pgbouncer is to lower the performance impact of opening 19 | new connections to PostgreSQL. 20 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Copyright (C) 2007-2011 Marko Kreen, Skype Technologies 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /debian/pgbouncer.install: -------------------------------------------------------------------------------- 1 | etc/pgbouncer.ini etc/ 2 | -------------------------------------------------------------------------------- /debian/pgbouncer.service: -------------------------------------------------------------------------------- 1 | Unit] 2 | Description=PGbouncer 1 3 | After=multi-user.target 4 | 5 | [Service] 6 | Type=simple 7 | WorkingDirectory=/etc/pgbouncer 8 | ExecStart=/usr/local/bin/pgbouncer /etc/pgbouncer/pgbouncer.ini 9 | Restart=always 10 | User=postgres 11 | StandardOutput=syslog 12 | StandardError=syslog 13 | LimitNOFILE=49152 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #! /usr/bin/make -f 2 | 3 | export V=1 4 | export DH_VERBOSE = 1 5 | export DEB_BUILD_OPTIONS = nostrip 6 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 7 | 8 | %: 9 | dh $@ 10 | 11 | override_dh_auto_clean: 12 | if test -f config.mak; then $(MAKE) clean; fi 13 | 14 | override_dh_auto_configure: 15 | dh_auto_configure -- \ 16 | --with-smp 17 | 18 | override_dh_installinit: 19 | dh_systemd_enable -ppgbouncer --name=pgbouncer pgbouncer.service 20 | dh_systemd_start -ppgbouncer --no-restart-on-upgrade 21 | 22 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | pgbouncer.1 2 | pgbouncer.5 3 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | -include ../config.mak 2 | 3 | manpages = pgbouncer.1 pgbouncer.5 4 | 5 | dist_man_MANS = $(manpages) 6 | 7 | EXTRA_DIST = config.rst usage.rst Makefile $(manpages) \ 8 | frag-config-man frag-usage-man 9 | 10 | # make maintainer-clean removes those 11 | MAINTAINERCLEANFILES = $(manpages) 12 | 13 | SUBLOC = doc 14 | abs_top_srcdir ?= $(CURDIR)/.. 15 | include $(abs_top_srcdir)/lib/mk/antimake.mk 16 | 17 | 18 | pgbouncer.1: usage.rst frag-usage-man 19 | $(SED) -e '4r frag-usage-man' usage.rst | $(RST2MAN) --exit-status=2 > $@ 20 | 21 | pgbouncer.5: config.rst frag-config-man 22 | $(SED) -e '4r frag-config-man' config.rst | $(RST2MAN) --exit-status=2 > $@ 23 | 24 | web: 25 | make -C ../../pgbouncer.github.io 26 | -------------------------------------------------------------------------------- /doc/frag-config-man: -------------------------------------------------------------------------------- 1 | -------------------------------- 2 | configuration file for pgbouncer 3 | -------------------------------- 4 | 5 | :Date: 2017-12-20 6 | :Version: 1.8.1 7 | :Manual section: 5 8 | :Manual group: Databases 9 | 10 | -------------------------------------------------------------------------------- /doc/frag-usage-man: -------------------------------------------------------------------------------- 1 | -------------------------------------------- 2 | lightweight connection pooler for PostgreSQL 3 | -------------------------------------------- 4 | 5 | :Date: 2017-12-20 6 | :Version: 1.8.1 7 | :Manual section: 1 8 | :Manual group: Databases 9 | 10 | -------------------------------------------------------------------------------- /doc/todo.rst: -------------------------------------------------------------------------------- 1 | PgBouncer TODO list 2 | =================== 3 | 4 | Highly visible missing features 5 | ------------------------------- 6 | 7 | Significant amount of users feel the need for those. 8 | 9 | * Protocol-level plan cache. 10 | 11 | * LISTEN/NOTIFY. Requires strict SQL format. 12 | 13 | Waiting for contributors... 14 | 15 | Problems / cleanups 16 | ------------------- 17 | 18 | * Bad naming in data strctures: 19 | * PgSocket->auth_user [vs. PgDatabase->auth_user] 20 | * PgSocket->db [vs. PgPool->db] 21 | 22 | * other per-user settings 23 | 24 | * Maintenance order vs. lifetime_kill_gap: 25 | http://lists.pgfoundry.org/pipermail/pgbouncer-general/2011-February/000679.html 26 | 27 | * per_loop_maint/per_loop_activate take too much time in case 28 | of moderate load and lots of pools. Perhaps active_pool_list 29 | would help, which contains only pools touched in current loop. 30 | 31 | * new states for clients: idle and in-query. That allows to apply 32 | client_idle_timeout and query_timeout without walking all clients 33 | on maintenance time. 34 | 35 | * check if SQL error codes are correct 36 | 37 | * removing user should work - kill connections 38 | 39 | * keep stats about error counts 40 | 41 | * cleanup of logging levels, to make log more useful 42 | 43 | * to test: 44 | - signal flood 45 | - no mem / no fds handling 46 | 47 | * fix high-freq maintenance timer - it's only needed when 48 | PAUSE/RESUME/shutdown is issued. 49 | 50 | * Get rid of SBUF_SMALL_PKT logic - it makes processing code complex. 51 | Needs a new sbuf_prepare_*() to notify sbuf about short data. 52 | [Plain 'false' from handler postpones processing to next event loop.] 53 | 54 | * units for config parameters. 55 | 56 | Dubious/complicated features 57 | ---------------------------- 58 | 59 | * Load-balancing / failover. Both are already solved via DNS. 60 | Adding load-balancing config in pgbouncer might be good idea. 61 | Adding failover decision-making is not... 62 | 63 | * User-based route. Simplest would be to move db info to pool 64 | and fill username into dns. 65 | 66 | * some preliminary notification that fd limit is full 67 | 68 | * Move all "look-at-full-packet" situations to SBUF_EV_PKT_CALLBACK 69 | 70 | * `pool_mode = plproxy` - use postgres in full-duplex mode for autocommit 71 | queries, multiplexing several queries into one connection. Should result 72 | in more efficient CPU usage of server. 73 | 74 | * SMP: spread sockets over per-cpu threads. Needs confirmation that 75 | single-threadedness can be problem. It can also be that only 76 | accept() + login handling of short connection is problem 77 | that could be solved by just having threads for login handling, 78 | which would be lot simpler or just deciding that it is not 79 | worth fixing. 80 | -------------------------------------------------------------------------------- /etc/example.debian.init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # pgbouncer Start the PgBouncer PostgreSQL pooler. 4 | # 5 | # The variables below are NOT to be changed. They are there to make the 6 | # script more readable. 7 | 8 | NAME=pgbouncer 9 | DAEMON=/usr/bin/$NAME 10 | PIDFILE=/var/run/$NAME.pid 11 | CONF=/etc/$NAME.ini 12 | OPTS="-d $CONF" 13 | # note: SSD is required only at startup of the daemon. 14 | SSD=`which start-stop-daemon` 15 | ENV="env -i LANG=C PATH=/bin:/usr/bin:/usr/local/bin" 16 | 17 | trap "" 1 18 | 19 | # Check if configuration exists 20 | test -f $CONF || exit 0 21 | 22 | case "$1" in 23 | start) 24 | echo -n "Starting server: $NAME" 25 | $ENV $SSD --start --pidfile $PIDFILE --exec $DAEMON -- $OPTS > /dev/null 26 | ;; 27 | 28 | stop) 29 | echo -n "Stopping server: $NAME" 30 | start-stop-daemon --stop --pidfile $PIDFILE 31 | ;; 32 | 33 | reload | force-reload) 34 | echo -n "Reloading $NAME configuration" 35 | start-stop-daemon --stop --pidfile $PIDFILE --signal HUP 36 | ;; 37 | 38 | restart) 39 | $0 stop 40 | $0 start 41 | ;; 42 | 43 | *) 44 | echo "Usage: /etc/init.d/$NAME {start|stop|reload|restart}" 45 | exit 1 46 | ;; 47 | esac 48 | 49 | if [ $? -eq 0 ]; then 50 | echo . 51 | exit 0 52 | else 53 | echo " failed" 54 | exit 1 55 | fi 56 | -------------------------------------------------------------------------------- /etc/mkauth.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import sys, os, tempfile, psycopg2 4 | 5 | if len(sys.argv) != 3: 6 | print('usage: mkauth DSTFN CONNSTR') 7 | sys.exit(1) 8 | 9 | # read old file 10 | fn = sys.argv[1] 11 | try: 12 | old = open(fn, 'r').read() 13 | except IOError: 14 | old = '' 15 | 16 | # create new file data 17 | db = psycopg2.connect(sys.argv[2]) 18 | curs = db.cursor() 19 | curs.execute("select usename, passwd from pg_shadow order by 1") 20 | lines = [] 21 | for user, psw in curs.fetchall(): 22 | user = user.replace('"', '""') 23 | if not psw: psw = '' 24 | psw = psw.replace('"', '""') 25 | lines.append('"%s" "%s" ""\n' % (user, psw)) 26 | db.commit() 27 | cur = "".join(lines) 28 | 29 | # if changed, replace data securely 30 | if old != cur: 31 | fd, tmpfn = tempfile.mkstemp(dir = os.path.split(fn)[0]) 32 | f = os.fdopen(fd, 'w') 33 | f.write(cur) 34 | f.close() 35 | os.rename(tmpfn, fn) 36 | -------------------------------------------------------------------------------- /etc/optscan.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Check if all options in main.c are defined in sample ini and docs 4 | 5 | sources="src/main.c" 6 | targets="doc/config.rst etc/pgbouncer.ini" 7 | 8 | for opt in `grep CF_ABS "$sources" | sed -r 's/^[^"]*"([^"]*)".*/\1/'`; do 9 | for conf in $targets; do 10 | if ! grep -q "$opt" "$conf"; then 11 | echo "$opt is missing in $conf" 12 | fi 13 | done 14 | done 15 | -------------------------------------------------------------------------------- /etc/pgbouncer.ini: -------------------------------------------------------------------------------- 1 | ;; database name = connect string 2 | ;; 3 | ;; connect string params: 4 | ;; dbname= host= port= user= password= 5 | ;; client_encoding= datestyle= timezone= 6 | ;; pool_size= connect_query= 7 | ;; auth_user= 8 | [databases] 9 | 10 | ; foodb over Unix socket 11 | ;foodb = 12 | 13 | ; redirect bardb to bazdb on localhost 14 | ;bardb = host=localhost dbname=bazdb 15 | 16 | ; access to dest database will go with single user 17 | ;forcedb = host=127.0.0.1 port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO connect_query='SELECT 1' 18 | 19 | ; use custom pool sizes 20 | ;nondefaultdb = pool_size=50 reserve_pool_size=10 21 | 22 | ; use auth_user with auth_query if user not present in auth_file 23 | ; auth_user must exist in auth_file 24 | ; foodb = auth_user=bar 25 | 26 | ; fallback connect string 27 | ;* = host=testserver 28 | 29 | ;; Configuration section 30 | [pgbouncer] 31 | 32 | ;;; 33 | ;;; Administrative settings 34 | ;;; 35 | 36 | logfile = /var/log/pgbouncer/pgbouncer.log 37 | pidfile = /var/run/pgbouncer/pgbouncer.pid 38 | 39 | ;;; 40 | ;;; Where to wait for clients 41 | ;;; 42 | 43 | ; IP address or * which means all IPs 44 | listen_addr = 127.0.0.1 45 | listen_port = 6432 46 | 47 | ; Unix socket is also used for -R. 48 | ; On Debian it should be /var/run/postgresql 49 | ;unix_socket_dir = /tmp 50 | ;unix_socket_mode = 0777 51 | ;unix_socket_group = 52 | 53 | ;;; 54 | ;;; TLS settings for accepting clients 55 | ;;; 56 | 57 | ;; disable, allow, require, verify-ca, verify-full 58 | ;client_tls_sslmode = disable 59 | 60 | ;; Path to file that contains trusted CA certs 61 | ;client_tls_ca_file = 62 | 63 | ;; Private key and cert to present to clients. 64 | ;; Required for accepting TLS connections from clients. 65 | ;client_tls_key_file = 66 | ;client_tls_cert_file = 67 | 68 | ;; fast, normal, secure, legacy, 69 | ;client_tls_ciphers = fast 70 | 71 | ;; all, secure, tlsv1.0, tlsv1.1, tlsv1.2 72 | ;client_tls_protocols = all 73 | 74 | ;; none, auto, legacy 75 | ;client_tls_dheparams = auto 76 | 77 | ;; none, auto, 78 | ;client_tls_ecdhcurve = auto 79 | 80 | ;;; 81 | ;;; TLS settings for connecting to backend databases 82 | ;;; 83 | 84 | ;; disable, allow, require, verify-ca, verify-full 85 | ;server_tls_sslmode = disable 86 | 87 | ;; Path to that contains trusted CA certs 88 | ;server_tls_ca_file = 89 | 90 | ;; Private key and cert to present to backend. 91 | ;; Needed only if backend server require client cert. 92 | ;server_tls_key_file = 93 | ;server_tls_cert_file = 94 | 95 | ;; all, secure, tlsv1.0, tlsv1.1, tlsv1.2 96 | ;server_tls_protocols = all 97 | 98 | ;; fast, normal, secure, legacy, 99 | ;server_tls_ciphers = fast 100 | 101 | ;;; 102 | ;;; Authentication settings 103 | ;;; 104 | 105 | ; any, trust, plain, crypt, md5, cert, hba, pam 106 | auth_type = trust 107 | ;auth_file = /8.0/main/global/pg_auth 108 | auth_file = /etc/pgbouncer/userlist.txt 109 | 110 | ;; Path to HBA-style auth config 111 | ;auth_hba_file = 112 | 113 | ;; Query to use to fetch password from database. Result 114 | ;; must have 2 columns - username and password hash. 115 | ;auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1 116 | 117 | ;;; 118 | ;;; Users allowed into database 'pgbouncer' 119 | ;;; 120 | 121 | ; comma-separated list of users, who are allowed to change settings 122 | ;admin_users = user2, someadmin, otheradmin 123 | 124 | ; comma-separated list of users who are just allowed to use SHOW command 125 | ;stats_users = stats, root 126 | 127 | ;;; 128 | ;;; Pooler personality questions 129 | ;;; 130 | 131 | ; When server connection is released back to pool: 132 | ; session - after client disconnects 133 | ; transaction - after transaction finishes 134 | ; statement - after statement finishes 135 | pool_mode = session 136 | 137 | ; 138 | ; Query for cleaning connection immediately after releasing from client. 139 | ; No need to put ROLLBACK here, pgbouncer does not reuse connections 140 | ; where transaction is left open. 141 | ; 142 | ; Query for 8.3+: 143 | ; DISCARD ALL; 144 | ; 145 | ; Older versions: 146 | ; RESET ALL; SET SESSION AUTHORIZATION DEFAULT 147 | ; 148 | ; Empty if transaction pooling is in use. 149 | ; 150 | server_reset_query = DISCARD ALL 151 | 152 | 153 | ; Whether server_reset_query should run in all pooling modes. 154 | ; If it is off, server_reset_query is used only for session-pooling. 155 | ;server_reset_query_always = 0 156 | 157 | ; 158 | ; Comma-separated list of parameters to ignore when given 159 | ; in startup packet. Newer JDBC versions require the 160 | ; extra_float_digits here. 161 | ; 162 | ;ignore_startup_parameters = extra_float_digits 163 | 164 | ; 165 | ; When taking idle server into use, this query is ran first. 166 | ; SELECT 1 167 | ; 168 | ;server_check_query = select 1 169 | 170 | ; If server was used more recently that this many seconds ago, 171 | ; skip the check query. Value 0 may or may not run in immediately. 172 | ;server_check_delay = 30 173 | 174 | ;; Use as application_name on server. 175 | ;application_name_add_host = 0 176 | 177 | ;;; 178 | ;;; Connection limits 179 | ;;; 180 | 181 | ; total number of clients that can connect 182 | max_client_conn = 100 183 | 184 | ; default pool size. 20 is good number when transaction pooling 185 | ; is in use, in session pooling it needs to be the number of 186 | ; max clients you want to handle at any moment 187 | default_pool_size = 20 188 | 189 | ;; Minimum number of server connections to keep in pool. 190 | ;min_pool_size = 0 191 | 192 | ; how many additional connection to allow in case of trouble 193 | ;reserve_pool_size = 0 194 | 195 | ; if a clients needs to wait more than this many seconds, use reserve pool 196 | ;reserve_pool_timeout = 5 197 | 198 | ; how many total connections to a single database to allow from all pools 199 | ;max_db_connections = 0 200 | ;max_user_connections = 0 201 | 202 | ; If off, then server connections are reused in LIFO manner 203 | ;server_round_robin = 0 204 | 205 | ;;; 206 | ;;; Logging 207 | ;;; 208 | 209 | ;; Syslog settings 210 | ;syslog = 0 211 | ;syslog_facility = daemon 212 | ;syslog_ident = pgbouncer 213 | 214 | ; log if client connects or server connection is made 215 | ;log_connections = 1 216 | 217 | ; log if and why connection was closed 218 | ;log_disconnections = 1 219 | 220 | ; log error messages pooler sends to clients 221 | ;log_pooler_errors = 1 222 | 223 | ;; Period for writing aggregated stats into log. 224 | ;stats_period = 60 225 | 226 | ;; Logging verbosity. Same as -v switch on command line. 227 | ;verbose = 0 228 | 229 | ;;; 230 | ;;; Timeouts 231 | ;;; 232 | 233 | ;; Close server connection if its been connected longer. 234 | ;server_lifetime = 3600 235 | 236 | ;; Close server connection if its not been used in this time. 237 | ;; Allows to clean unnecessary connections from pool after peak. 238 | ;server_idle_timeout = 600 239 | 240 | ;; Cancel connection attempt if server does not answer takes longer. 241 | ;server_connect_timeout = 15 242 | 243 | ;; If server login failed (server_connect_timeout or auth failure) 244 | ;; then wait this many second. 245 | ;server_login_retry = 15 246 | 247 | ;; Dangerous. Server connection is closed if query does not return 248 | ;; in this time. Should be used to survive network problems, 249 | ;; _not_ as statement_timeout. (default: 0) 250 | ;query_timeout = 0 251 | 252 | ;; Dangerous. Client connection is closed if the query is not assigned 253 | ;; to a server in this time. Should be used to limit the number of queued 254 | ;; queries in case of a database or network failure. (default: 120) 255 | ;query_wait_timeout = 120 256 | 257 | ;; Dangerous. Client connection is closed if no activity in this time. 258 | ;; Should be used to survive network problems. (default: 0) 259 | ;client_idle_timeout = 0 260 | 261 | ;; Disconnect clients who have not managed to log in after connecting 262 | ;; in this many seconds. 263 | ;client_login_timeout = 60 264 | 265 | ;; Clean automatically created database entries (via "*") if they 266 | ;; stay unused in this many seconds. 267 | ; autodb_idle_timeout = 3600 268 | 269 | ;; How long SUSPEND/-R waits for buffer flush before closing connection. 270 | ;suspend_timeout = 10 271 | 272 | ;; Close connections which are in "IDLE in transaction" state longer than 273 | ;; this many seconds. 274 | ;idle_transaction_timeout = 0 275 | 276 | ;;; 277 | ;;; Low-level tuning options 278 | ;;; 279 | 280 | ;; buffer for streaming packets 281 | ;pkt_buf = 4096 282 | 283 | ;; man 2 listen 284 | ;listen_backlog = 128 285 | 286 | ;; Max number pkt_buf to process in one event loop. 287 | ;sbuf_loopcnt = 5 288 | 289 | ;; Maximum PostgreSQL protocol packet size. 290 | ;max_packet_size = 2147483647 291 | 292 | ;; networking options, for info: man 7 tcp 293 | 294 | ;; Linux: notify program about new connection only if there 295 | ;; is also data received. (Seconds to wait.) 296 | ;; On Linux the default is 45, on other OS'es 0. 297 | ;tcp_defer_accept = 0 298 | 299 | ;; In-kernel buffer size (Linux default: 4096) 300 | ;tcp_socket_buffer = 0 301 | 302 | ;; whether tcp keepalive should be turned on (0/1) 303 | ;tcp_keepalive = 1 304 | 305 | ;; The following options are Linux-specific. 306 | ;; They also require tcp_keepalive=1. 307 | 308 | ;; count of keepalive packets 309 | ;tcp_keepcnt = 0 310 | 311 | ;; how long the connection can be idle, 312 | ;; before sending keepalive packets 313 | ;tcp_keepidle = 0 314 | 315 | ;; The time between individual keepalive probes. 316 | ;tcp_keepintvl = 0 317 | 318 | ;; DNS lookup caching time 319 | ;dns_max_ttl = 15 320 | 321 | ;; DNS zone SOA lookup period 322 | ;dns_zone_check_period = 0 323 | 324 | ;; DNS negative result caching time 325 | ;dns_nxdomain_ttl = 15 326 | 327 | ;;; 328 | ;;; Random stuff 329 | ;;; 330 | 331 | ;; Hackish security feature. Helps against SQL-injection - when PQexec is disabled, 332 | ;; multi-statement cannot be made. 333 | ;disable_pqexec = 0 334 | 335 | ;; Config file to use for next RELOAD/SIGHUP. 336 | ;; By default contains config file from command line. 337 | ;conffile 338 | 339 | ;; Win32 service name to register as. job_name is alias for service_name, 340 | ;; used by some Skytools scripts. 341 | ;service_name = pgbouncer 342 | ;job_name = pgbouncer 343 | 344 | ;; Read additional config from the /etc/pgbouncer/pgbouncer-other.ini file 345 | ;%include /etc/pgbouncer/pgbouncer-other.ini 346 | -------------------------------------------------------------------------------- /etc/small.ini: -------------------------------------------------------------------------------- 1 | 2 | [databases] 3 | evtest = host=127.0.0.1 4 | provider = host=127.0.0.1 5 | postgres = host=127.0.0.1 6 | orderdb = host=127.0.0.1 7 | forcedb = host=127.0.0.1 port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO 8 | marko = host=127.0.0.1 port=5432 pool_size=5 9 | orderdb_test = host=192.168.125.155 10 | test_part = host=127.0.0.1 11 | 12 | [pgbouncer] 13 | logfile = pgbouncer.log 14 | ;pidfile = pgbouncer.pid 15 | 16 | listen_addr = 127.0.0.1 17 | listen_port = 6432 18 | unix_socket_dir = /tmp 19 | 20 | ; any, trust, plain, crypt, md5 21 | auth_type = trust 22 | #auth_file = 8.0/main/global/pg_auth 23 | auth_file = etc/userlist.txt 24 | 25 | ; session, transaction, statement 26 | pool_mode = session 27 | 28 | max_client_conn = 100 29 | default_pool_size = 20 30 | -------------------------------------------------------------------------------- /etc/smp.ini: -------------------------------------------------------------------------------- 1 | 2 | [databases] 3 | postgres = host=127.0.0.1 4 | 5 | [pgbouncer] 6 | logfile = pgbouncer.log 7 | ;pidfile = pgbouncer.pid 8 | 9 | listen_addr = 127.0.0.1 10 | listen_port = 6432 11 | 12 | ; unix_socket_dir must be configured when using SMP 13 | unix_socket_dir = /tmp 14 | 15 | ; any, trust, plain, crypt, md5 16 | auth_type = trust 17 | #auth_file = 8.0/main/global/pg_auth 18 | auth_file = etc/userlist.txt 19 | 20 | ; session, transaction, statement 21 | pool_mode = session 22 | 23 | max_client_conn = 100 24 | default_pool_size = 20 25 | 26 | [smp] 27 | 28 | ; Turns on process manager 29 | ; 30 | ; Default: 1 31 | enabled = 1 32 | 33 | ; How many workers will process manager create. When 0 is specified, the number 34 | ; of worker processes will be equal to the number of available CPUs. If unable 35 | ; to determine number of CPUs only one worker will be used. 36 | ; 37 | ; Default: 0 38 | workers = 4 39 | 40 | ; Each worker is assigned a port starting at this value. 41 | ; 42 | ; For example, if the value is 33333 and there are two workers, 43 | ; first worker will use port 33333 and second worker will use port 33334. 44 | ; 45 | ; The port does not represent TCP port, it is only used in unix socket filename. 46 | ; 47 | ; Default: 33333 48 | port_start = 33333 49 | -------------------------------------------------------------------------------- /etc/test.ini: -------------------------------------------------------------------------------- 1 | [databases] 2 | marko = host=127.0.0.1 port=7000 3 | 4 | [pgbouncer] 5 | logfile = lib/pgbouncer.log 6 | pidfile = lib/pgbouncer.pid 7 | 8 | listen_addr = 127.0.0.1 9 | listen_port = 6432 10 | unix_socket_dir = /tmp 11 | 12 | ; any, trust, plain, crypt, md5 13 | auth_type = md5 14 | auth_file = etc/test.users 15 | 16 | ; When server connection is released back to pool: 17 | ; session - after client disconnects 18 | ; transaction - after transaction finishes 19 | ; statement - after statement finishes 20 | pool_mode = transaction 21 | 22 | server_check_query = select 1 23 | server_check_delay = 5 24 | max_client_conn = 100 25 | default_pool_size = 30 26 | 27 | admin_users = plproxy 28 | stats_users = marko 29 | 30 | stats_period = 60 31 | 32 | log_connections = 0 33 | log_disconnections = 0 34 | log_pooler_errors = 0 35 | 36 | ; short timeouts 37 | server_lifetime = 5 38 | server_idle_timeout = 3 39 | server_connect_timeout = 1 40 | server_login_retry = 1 41 | query_timeout = 10 42 | client_idle_timeout = 10 43 | 44 | client_login_timeout = 3 45 | -------------------------------------------------------------------------------- /etc/test.users: -------------------------------------------------------------------------------- 1 | "admin" "" "" 2 | "backoffice" "" "" 3 | "info" "" "" 4 | "martinp" "md55c06ac8c93212495f8eaf6a7ffd688dd" "" 5 | "plproxy" "md5a704fc5c9a4bf2f745acc6f7a7ec2f2f" "" 6 | "postgres" "md5264abda62970ba635b133f545ce12132" "" 7 | "priitk" "md55c08f2e34592ddb13972db7eaadc1232" "" 8 | "replicator" "" "" 9 | "webstore" "" "" 10 | "wypbe" "md57e17e9c6cfde1c1f6f9155071d7d18a8" "" 11 | "wypfe" "md5e3b7c35f688032d97ab066210a33184b" "" 12 | "marko" "kama" 13 | -------------------------------------------------------------------------------- /etc/userlist.txt: -------------------------------------------------------------------------------- 1 | "marko" "asdasd" 2 | "postgres" "asdasd" 3 | "pgbouncer" "fake" 4 | -------------------------------------------------------------------------------- /include/admin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | bool admin_handle_client(PgSocket *client, PktHdr *pkt) _MUSTCHECK; 19 | bool admin_pre_login(PgSocket *client, const char *username) _MUSTCHECK; 20 | bool admin_post_login(PgSocket *client) _MUSTCHECK; 21 | void admin_setup(void); 22 | bool admin_error(PgSocket *console, const char *fmt, ...) _PRINTF(2, 3) /* _MUSTCHECK */; 23 | void admin_pause_done(void); 24 | bool admin_flush(PgSocket *admin, PktBuf *buf, const char *desc) /* _MUSTCHECK */; 25 | bool admin_ready(PgSocket *admin, const char *desc) _MUSTCHECK; 26 | void admin_handle_cancel(PgSocket *client); 27 | void admin_cleanup(void); 28 | -------------------------------------------------------------------------------- /include/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; 20 | bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) _MUSTCHECK; 21 | bool handle_auth_response(PgSocket *client, PktHdr *pkt); 22 | -------------------------------------------------------------------------------- /include/dnslookup.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | struct DNSContext; 20 | struct DNSToken; 21 | struct addrinfo; 22 | 23 | typedef void (*adns_callback_f)(void *arg, const struct sockaddr *sa, int salen); 24 | 25 | struct DNSContext *adns_create_context(void); 26 | void adns_reload(struct DNSContext *ctx); 27 | void adns_free_context(struct DNSContext *ctx); 28 | 29 | struct DNSToken *adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *arg); 30 | 31 | void adns_cancel(struct DNSContext *ctx, struct DNSToken *tk); 32 | 33 | const char *adns_get_backend(void); 34 | 35 | void adns_zone_cache_maint(struct DNSContext *ctx); 36 | 37 | void adns_info(struct DNSContext *ctx, int *names, int *zones, int *queries, int *pending); 38 | 39 | typedef void (*adns_walk_name_f)(void *arg, const char *name, const struct addrinfo *ai, usec_t ttl); 40 | typedef void (*adns_walk_zone_f)(void *arg, const char *name, uint32_t serial, int nhosts); 41 | 42 | void adns_walk_names(struct DNSContext *ctx, adns_walk_name_f cb, void *arg); 43 | void adns_walk_zones(struct DNSContext *ctx, adns_walk_zone_f cb, void *arg); 44 | 45 | void adns_per_loop(struct DNSContext *ctx); 46 | -------------------------------------------------------------------------------- /include/hba.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Host-Based-Access-control file support. 3 | * 4 | * Copyright (c) 2015 Marko Kreen 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | struct HBA; 20 | 21 | struct HBA *hba_load_rules(const char *fn); 22 | void hba_free(struct HBA *hba); 23 | int hba_eval(struct HBA *hba, PgAddr *addr, bool is_tls, const char *dbname, const char *username); 24 | -------------------------------------------------------------------------------- /include/iobuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Temporary buffer for single i/o. 21 | * 22 | * Pattern: 23 | * 24 | * iobuf_get_and_reset() 25 | * start: 26 | * iobuf_recv() 27 | * loop: 28 | * if (new_pkt) 29 | * iobuf_parse() 30 | * 31 | * if (send) { 32 | * iobuf_tag_send() 33 | * } else { 34 | * send_pending() 35 | * iobuf_tag_skip() 36 | * } 37 | * if (more-unparsed) 38 | * goto loop; 39 | * send_pending(); 40 | */ 41 | 42 | /* 43 | * 0 .. done_pos -- sent 44 | * done_pos .. parse_pos -- parsed, to send 45 | * parse_pos .. recv_pos -- received, to parse 46 | */ 47 | struct iobuf { 48 | unsigned done_pos; 49 | unsigned parse_pos; 50 | unsigned recv_pos; 51 | uint8_t buf[FLEX_ARRAY]; 52 | }; 53 | typedef struct iobuf IOBuf; 54 | 55 | static inline bool iobuf_sane(const IOBuf *io) 56 | { 57 | return (io == NULL) || 58 | ( io->parse_pos >= io->done_pos 59 | && io->recv_pos >= io->parse_pos 60 | && (unsigned)cf_sbuf_len >= io->recv_pos); 61 | } 62 | 63 | static inline bool iobuf_empty(const IOBuf *io) 64 | { 65 | return io == NULL || io->done_pos == io->recv_pos; 66 | } 67 | 68 | /* unsent amount */ 69 | static inline unsigned iobuf_amount_pending(const IOBuf *buf) 70 | { 71 | return buf->parse_pos - buf->done_pos; 72 | } 73 | 74 | /* max possible to parse (tag_send/tag_skip) */ 75 | static inline unsigned iobuf_amount_parse(const IOBuf *buf) 76 | { 77 | return buf->recv_pos - buf->parse_pos; 78 | } 79 | 80 | /* max possible to recv */ 81 | static inline unsigned iobuf_amount_recv(const IOBuf *buf) 82 | { 83 | return cf_sbuf_len - buf->recv_pos; 84 | } 85 | 86 | /* put all unparsed to mbuf */ 87 | static inline unsigned iobuf_parse_all(const IOBuf *buf, struct MBuf *mbuf) 88 | { 89 | unsigned avail = iobuf_amount_parse(buf); 90 | const uint8_t *pos = buf->buf + buf->parse_pos; 91 | mbuf_init_fixed_reader(mbuf, pos, avail); 92 | return avail; 93 | } 94 | 95 | /* put all unparsed to mbuf, with size limit */ 96 | static inline unsigned iobuf_parse_limit(const IOBuf *buf, struct MBuf *mbuf, unsigned limit) 97 | { 98 | unsigned avail = iobuf_amount_parse(buf); 99 | const uint8_t *pos = buf->buf + buf->parse_pos; 100 | if (avail > limit) 101 | avail = limit; 102 | mbuf_init_fixed_reader(mbuf, pos, avail); 103 | return avail; 104 | } 105 | 106 | static inline void iobuf_tag_send(IOBuf *io, unsigned len) 107 | { 108 | Assert(len > 0 && len <= iobuf_amount_parse(io)); 109 | 110 | io->parse_pos += len; 111 | } 112 | 113 | static inline void iobuf_tag_skip(IOBuf *io, unsigned len) 114 | { 115 | Assert(io->parse_pos == io->done_pos); /* no send pending */ 116 | Assert(len > 0 && len <= iobuf_amount_parse(io)); 117 | 118 | io->parse_pos += len; 119 | io->done_pos = io->parse_pos; 120 | } 121 | 122 | static inline void iobuf_try_resync(IOBuf *io, unsigned small_pkt) 123 | { 124 | unsigned avail = io->recv_pos - io->done_pos; 125 | if (avail == 0) { 126 | if (io->recv_pos > 0) 127 | io->recv_pos = io->parse_pos = io->done_pos = 0; 128 | } else if (avail <= small_pkt && io->done_pos > 0) { 129 | memmove(io->buf, io->buf + io->done_pos, avail); 130 | io->parse_pos -= io->done_pos; 131 | io->recv_pos = avail; 132 | io->done_pos = 0; 133 | } 134 | } 135 | 136 | static inline void iobuf_reset(IOBuf *io) 137 | { 138 | io->recv_pos = io->parse_pos = io->done_pos = 0; 139 | } 140 | -------------------------------------------------------------------------------- /include/janitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | void janitor_setup(void); 20 | void config_postprocess(void); 21 | void resume_all(void); 22 | void per_loop_maint(void); 23 | bool suspend_socket(PgSocket *sk, bool force) _MUSTCHECK; 24 | void kill_pool(PgPool *pool); 25 | void kill_database(PgDatabase *db); 26 | -------------------------------------------------------------------------------- /include/loader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* connstring parsing */ 20 | bool parse_database(void *base, const char *name, const char *connstr); 21 | 22 | bool parse_user(void *base, const char *name, const char *params); 23 | 24 | /* user file parsing */ 25 | bool load_auth_file(const char *fn) /* _MUSTCHECK */; 26 | bool loader_users_check(void) /* _MUSTCHECK */; 27 | -------------------------------------------------------------------------------- /include/objects.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | extern struct StatList user_list; 20 | extern struct AATree user_tree; 21 | extern struct StatList pool_list; 22 | extern struct StatList database_list; 23 | extern struct StatList autodatabase_idle_list; 24 | extern struct StatList login_client_list; 25 | extern struct Slab *client_cache; 26 | extern struct Slab *server_cache; 27 | extern struct Slab *db_cache; 28 | extern struct Slab *pool_cache; 29 | extern struct Slab *user_cache; 30 | extern struct Slab *iobuf_cache; 31 | 32 | PgDatabase *find_database(const char *name); 33 | PgUser *find_user(const char *name); 34 | PgPool *get_pool(PgDatabase *, PgUser *); 35 | PgSocket *compare_connections_by_time(PgSocket *lhs, PgSocket *rhs); 36 | bool evict_connection(PgDatabase *db) _MUSTCHECK; 37 | bool evict_user_connection(PgUser *user) _MUSTCHECK; 38 | bool find_server(PgSocket *client) _MUSTCHECK; 39 | bool release_server(PgSocket *server) /* _MUSTCHECK */; 40 | bool finish_client_login(PgSocket *client) _MUSTCHECK; 41 | bool check_fast_fail(PgSocket *client) _MUSTCHECK; 42 | 43 | PgSocket *accept_client(int sock, bool is_unix) _MUSTCHECK; 44 | void disconnect_server(PgSocket *server, bool notify, const char *reason, ...) _PRINTF(3, 4); 45 | void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) _PRINTF(3, 4); 46 | 47 | PgDatabase * add_database(const char *name) _MUSTCHECK; 48 | PgDatabase *register_auto_database(const char *name); 49 | PgUser * add_user(const char *name, const char *passwd) _MUSTCHECK; 50 | PgUser * add_db_user(PgDatabase *db, const char *name, const char *passwd) _MUSTCHECK; 51 | PgUser * force_user(PgDatabase *db, const char *username, const char *passwd) _MUSTCHECK; 52 | 53 | PgUser * add_pam_user(const char *name, const char *passwd) _MUSTCHECK; 54 | 55 | void accept_cancel_request(PgSocket *req); 56 | void forward_cancel_request(PgSocket *server); 57 | 58 | void launch_new_connection(PgPool *pool); 59 | 60 | bool use_client_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, 61 | const char *client_end, const char *std_string, const char *datestyle, const char *timezone, 62 | const char *password) 63 | _MUSTCHECK; 64 | bool use_server_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, 65 | const char *client_end, const char *std_string, const char *datestyle, const char *timezone, 66 | const char *password) 67 | _MUSTCHECK; 68 | 69 | void activate_client(PgSocket *client); 70 | 71 | void change_client_state(PgSocket *client, SocketState newstate); 72 | void change_server_state(PgSocket *server, SocketState newstate); 73 | 74 | int get_active_client_count(void); 75 | int get_active_server_count(void); 76 | 77 | void tag_database_dirty(PgDatabase *db); 78 | void tag_autodb_dirty(void); 79 | void tag_host_addr_dirty(const char *host, const struct sockaddr *sa); 80 | void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)); 81 | 82 | void reuse_just_freed_objects(void); 83 | 84 | void init_objects(void); 85 | 86 | void init_caches(void); 87 | 88 | void objects_cleanup(void); 89 | -------------------------------------------------------------------------------- /include/pam.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * PAM support. 21 | */ 22 | 23 | /* Name of the service to be passed to PAM */ 24 | #define PGBOUNCER_PAM_SERVICE "pgbouncer" 25 | 26 | /* 27 | * Defines how many authorization requests can be placed to the waiting queue. 28 | * When the queue is full calls to pam_auth_begin() will block until there is 29 | * free space in the queue. 30 | */ 31 | #define PAM_REQUEST_QUEUE_SIZE 20 32 | 33 | void pam_init(void); 34 | void pam_auth_begin(PgSocket *client, const char *passwd); 35 | int pam_poll(void); 36 | -------------------------------------------------------------------------------- /include/pktbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Safe & easy creation of PostgreSQL packets. 21 | */ 22 | 23 | typedef struct PktBuf PktBuf; 24 | struct PktBuf { 25 | uint8_t *buf; 26 | int buf_len; 27 | int write_pos; 28 | int pktlen_pos; 29 | 30 | int send_pos; 31 | struct event *ev; 32 | PgSocket *queued_dst; 33 | 34 | unsigned failed:1; 35 | unsigned sending:1; 36 | unsigned fixed_buf:1; 37 | }; 38 | 39 | /* 40 | * pktbuf creation 41 | */ 42 | PktBuf *pktbuf_dynamic(int start_len) _MUSTCHECK; 43 | void pktbuf_static(PktBuf *buf, uint8_t *data, int len); 44 | 45 | void pktbuf_free(PktBuf *buf); 46 | 47 | void pktbuf_reset(struct PktBuf *pkt); 48 | struct PktBuf *pktbuf_temp(void); 49 | 50 | 51 | /* 52 | * sending 53 | */ 54 | bool pktbuf_send_immediate(PktBuf *buf, PgSocket *sk) _MUSTCHECK; 55 | bool pktbuf_send_queued(PktBuf *buf, PgSocket *sk) _MUSTCHECK; 56 | 57 | /* 58 | * low-level ops 59 | */ 60 | void pktbuf_start_packet(PktBuf *buf, int type); 61 | void pktbuf_put_char(PktBuf *buf, char val); 62 | void pktbuf_put_uint16(PktBuf *buf, uint16_t val); 63 | void pktbuf_put_uint32(PktBuf *buf, uint32_t val); 64 | void pktbuf_put_uint64(PktBuf *buf, uint64_t val); 65 | void pktbuf_put_string(PktBuf *buf, const char *str); 66 | void pktbuf_put_bytes(PktBuf *buf, const void *data, int len); 67 | void pktbuf_finish_packet(PktBuf *buf); 68 | #define pktbuf_written(buf) ((buf)->write_pos) 69 | 70 | 71 | /* 72 | * Packet writing 73 | */ 74 | void pktbuf_write_generic(PktBuf *buf, int type, const char *fmt, ...); 75 | void pktbuf_write_RowDescription(PktBuf *buf, const char *tupdesc, ...); 76 | void pktbuf_write_DataRow(PktBuf *buf, const char *tupdesc, ...); 77 | void pktbuf_write_ExtQuery(PktBuf *buf, const char *query, int nargs, ...); 78 | 79 | /* 80 | * Shortcuts for actual packets. 81 | */ 82 | #define pktbuf_write_ParameterStatus(buf, key, val) \ 83 | pktbuf_write_generic(buf, 'S', "ss", key, val) 84 | 85 | #define pktbuf_write_AuthenticationOk(buf) \ 86 | pktbuf_write_generic(buf, 'R', "i", 0) 87 | 88 | #define pktbuf_write_ReadyForQuery(buf) \ 89 | pktbuf_write_generic(buf, 'Z', "c", 'I') 90 | 91 | #define pktbuf_write_CommandComplete(buf, desc) \ 92 | pktbuf_write_generic(buf, 'C', "s", desc) 93 | 94 | #define pktbuf_write_BackendKeyData(buf, key) \ 95 | pktbuf_write_generic(buf, 'K', "b", key, 8) 96 | 97 | #define pktbuf_write_CancelRequest(buf, key) \ 98 | pktbuf_write_generic(buf, PKT_CANCEL, "b", key, 8) 99 | 100 | #define pktbuf_write_StartupMessage(buf, user, parms, parms_len) \ 101 | pktbuf_write_generic(buf, PKT_STARTUP, "bsss", parms, parms_len, "user", user, "") 102 | 103 | #define pktbuf_write_PasswordMessage(buf, psw) \ 104 | pktbuf_write_generic(buf, 'p', "s", psw) 105 | 106 | #define pktbuf_write_Notice(buf, msg) \ 107 | pktbuf_write_generic(buf, 'N', "sscss", "SNOTICE", "C00000", 'M', msg, ""); 108 | 109 | #define pktbuf_write_SSLRequest(buf) \ 110 | pktbuf_write_generic(buf, PKT_SSLREQ, "") 111 | 112 | /* 113 | * Shortcut for creating DataRow in memory. 114 | */ 115 | 116 | #define BUILD_DataRow(reslen, dst, dstlen, args...) do { \ 117 | PktBuf _buf; \ 118 | pktbuf_static(&_buf, dst, dstlen); \ 119 | pktbuf_write_DataRow(&_buf, ## args); \ 120 | reslen = _buf.failed ? -1 : _buf.write_pos; \ 121 | } while (0) 122 | 123 | /* 124 | * Shortcuts for immediate send of one packet. 125 | */ 126 | 127 | #define SEND_wrap(buflen, pktfn, res, sk, args...) do { \ 128 | uint8_t _data[buflen]; PktBuf _buf; \ 129 | pktbuf_static(&_buf, _data, sizeof(_data)); \ 130 | pktfn(&_buf, ## args); \ 131 | res = pktbuf_send_immediate(&_buf, sk); \ 132 | } while (0) 133 | 134 | #define SEND_RowDescription(res, sk, args...) \ 135 | SEND_wrap(512, pktbuf_write_RowDescription, res, sk, ## args) 136 | 137 | #define SEND_generic(res, sk, args...) \ 138 | SEND_wrap(512, pktbuf_write_generic, res, sk, ## args) 139 | 140 | #define SEND_ReadyForQuery(res, sk) \ 141 | SEND_wrap(8, pktbuf_write_ReadyForQuery, res, sk) 142 | 143 | #define SEND_CancelRequest(res, sk, key) \ 144 | SEND_wrap(16, pktbuf_write_CancelRequest, res, sk, key) 145 | 146 | #define SEND_PasswordMessage(res, sk, psw) \ 147 | SEND_wrap(512, pktbuf_write_PasswordMessage, res, sk, psw) 148 | 149 | void pktbuf_cleanup(void); 150 | -------------------------------------------------------------------------------- /include/pooler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | void pooler_setup(void); 20 | bool use_pooler_socket(int fd, bool is_unix) _MUSTCHECK; 21 | void resume_pooler(void); 22 | void suspend_pooler(void); 23 | void per_loop_pooler_maint(void); 24 | void pooler_tune_accept(bool on); 25 | 26 | typedef bool (*pooler_cb)(void *arg, int fd, const PgAddr *addr); 27 | bool for_each_pooler_fd(pooler_cb cb, void *arg); 28 | -------------------------------------------------------------------------------- /include/proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* old style V2 header: len:4b code:4b */ 20 | #define OLD_HEADER_LEN 8 21 | /* new style V3 packet header len - type:1b, len:4b */ 22 | #define NEW_HEADER_LEN 5 23 | 24 | /* 25 | * parsed packet header, plus whatever data is 26 | * available in SBuf for this packet. 27 | * 28 | * if (pkt->len == mbuf_avail(&pkt->data)) 29 | * packet is fully in buffer 30 | * 31 | * get_header() points pkt->data.pos after header. 32 | * to packet body. 33 | */ 34 | struct PktHdr { 35 | unsigned type; 36 | unsigned len; 37 | struct MBuf data; 38 | }; 39 | 40 | bool get_header(struct MBuf *data, PktHdr *pkt) _MUSTCHECK; 41 | 42 | bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg) /*_MUSTCHECK*/; 43 | void log_server_error(const char *note, PktHdr *pkt); 44 | void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p); 45 | 46 | bool add_welcome_parameter(PgPool *pool, const char *key, const char *val) _MUSTCHECK; 47 | void finish_welcome_msg(PgSocket *server); 48 | bool welcome_client(PgSocket *client) _MUSTCHECK; 49 | 50 | bool answer_authreq(PgSocket *server, PktHdr *pkt) _MUSTCHECK; 51 | 52 | bool send_startup_packet(PgSocket *server) _MUSTCHECK; 53 | bool send_sslreq_packet(PgSocket *server) _MUSTCHECK; 54 | 55 | int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...) _MUSTCHECK; 56 | 57 | /* is packet completely in our buffer */ 58 | static inline bool incomplete_pkt(const PktHdr *pkt) 59 | { 60 | return mbuf_written(&pkt->data) != pkt->len; 61 | } 62 | 63 | /* is packet header completely in buffer */ 64 | static inline bool incomplete_header(const struct MBuf *data) { 65 | uint32_t avail = mbuf_avail_for_read(data); 66 | if (avail >= OLD_HEADER_LEN) 67 | return false; 68 | if (avail < NEW_HEADER_LEN) 69 | return true; 70 | /* is it old V2 header? */ 71 | return data->data[data->read_pos] == 0; 72 | } 73 | 74 | /* one char desc */ 75 | static inline char pkt_desc(const PktHdr *pkt) 76 | { 77 | return pkt->type > 256 ? '!' : pkt->type; 78 | } 79 | -------------------------------------------------------------------------------- /include/sbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * event types for protocol handler 21 | */ 22 | typedef enum { 23 | SBUF_EV_READ, /* got new packet */ 24 | SBUF_EV_RECV_FAILED, /* error */ 25 | SBUF_EV_SEND_FAILED, /* error */ 26 | SBUF_EV_CONNECT_FAILED, /* error */ 27 | SBUF_EV_CONNECT_OK, /* got connection */ 28 | SBUF_EV_FLUSH, /* data is sent, buffer empty */ 29 | SBUF_EV_PKT_CALLBACK, /* next part of pkt data */ 30 | SBUF_EV_TLS_READY /* TLS was established */ 31 | } SBufEvent; 32 | 33 | /* 34 | * If less than this amount of data is pending, then 35 | * prefer to merge it with next recv(). 36 | * 37 | * It needs to be larger than data handler wants 38 | * to see completely. Generally just header, 39 | * but currently also ServerParam pkt. 40 | */ 41 | #define SBUF_SMALL_PKT 64 42 | 43 | struct tls; 44 | 45 | /* fwd def */ 46 | typedef struct SBuf SBuf; 47 | typedef struct SBufIO SBufIO; 48 | 49 | /* callback should return true if it used one of sbuf_prepare_* on sbuf, 50 | false if it used sbuf_pause(), sbuf_close() or simply wants to wait for 51 | next event loop (eg. too few data available). */ 52 | typedef bool (*sbuf_cb_t)(SBuf *sbuf, 53 | SBufEvent evtype, 54 | struct MBuf *mbuf); 55 | 56 | /* for some reason, libevent has no typedef for callback */ 57 | typedef void (*sbuf_libevent_cb)(int, short, void *); 58 | 59 | struct SBufIO { 60 | int (*sbufio_recv)(SBuf *sbuf, void *buf, unsigned int len); 61 | int (*sbufio_send)(SBuf *sbuf, const void *data, unsigned int len); 62 | int (*sbufio_close)(SBuf *sbuf); 63 | }; 64 | 65 | /* 66 | * Stream Buffer. 67 | * 68 | * Stream is divided to packets. On each packet start 69 | * protocol handler is called that decides what to do. 70 | */ 71 | struct SBuf { 72 | struct event ev; /* libevent handle */ 73 | 74 | uint8_t wait_type; /* track wait state */ 75 | uint8_t pkt_action; /* method for handling current pkt */ 76 | uint8_t tls_state; /* progress of tls */ 77 | 78 | int sock; /* fd for this socket */ 79 | 80 | unsigned pkt_remain; /* total packet length remaining */ 81 | 82 | sbuf_cb_t proto_cb; /* protocol callback */ 83 | 84 | SBuf *dst; /* target SBuf for current packet */ 85 | 86 | IOBuf *io; /* data buffer, lazily allocated */ 87 | 88 | const SBufIO *ops; /* normal vs. TLS */ 89 | struct tls *tls; /* TLS context */ 90 | const char *tls_host; /* target hostname */ 91 | }; 92 | 93 | #define sbuf_socket(sbuf) ((sbuf)->sock) 94 | 95 | void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn); 96 | bool sbuf_accept(SBuf *sbuf, int read_sock, bool is_unix) _MUSTCHECK; 97 | bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec) _MUSTCHECK; 98 | 99 | void sbuf_tls_setup(void); 100 | bool sbuf_tls_accept(SBuf *sbuf) _MUSTCHECK; 101 | bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) _MUSTCHECK; 102 | 103 | bool sbuf_pause(SBuf *sbuf) _MUSTCHECK; 104 | void sbuf_continue(SBuf *sbuf); 105 | bool sbuf_close(SBuf *sbuf) _MUSTCHECK; 106 | 107 | /* proto_fn can use those functions to order behaviour */ 108 | void sbuf_prepare_send(SBuf *sbuf, SBuf *dst, unsigned amount); 109 | void sbuf_prepare_skip(SBuf *sbuf, unsigned amount); 110 | void sbuf_prepare_fetch(SBuf *sbuf, unsigned amount); 111 | 112 | bool sbuf_answer(SBuf *sbuf, const void *buf, unsigned len) _MUSTCHECK; 113 | 114 | bool sbuf_continue_with_callback(SBuf *sbuf, sbuf_libevent_cb cb) _MUSTCHECK; 115 | bool sbuf_use_callback_once(SBuf *sbuf, short ev, sbuf_libevent_cb user_cb) _MUSTCHECK; 116 | 117 | /* 118 | * Returns true if SBuf is has no data buffered 119 | * and is not in a middle of a packet. 120 | */ 121 | static inline bool sbuf_is_empty(SBuf *sbuf) 122 | { 123 | return iobuf_empty(sbuf->io) && sbuf->pkt_remain == 0; 124 | } 125 | 126 | static inline bool sbuf_is_closed(SBuf *sbuf) 127 | { 128 | return sbuf->sock == 0; 129 | } 130 | 131 | /* 132 | * Lowlevel operations. 133 | */ 134 | 135 | static inline int sbuf_op_recv(SBuf *sbuf, void *buf, unsigned int len) 136 | { 137 | return sbuf->ops->sbufio_recv(sbuf, buf, len); 138 | } 139 | 140 | static inline int sbuf_op_send(SBuf *sbuf, const void *buf, unsigned int len) 141 | { 142 | return sbuf->ops->sbufio_send(sbuf, buf, len); 143 | } 144 | 145 | static inline int sbuf_op_close(SBuf *sbuf) 146 | { 147 | return sbuf->ops->sbufio_close(sbuf); 148 | } 149 | 150 | void sbuf_cleanup(void); 151 | -------------------------------------------------------------------------------- /include/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; 20 | int pool_pool_mode(PgPool *pool) _MUSTCHECK; 21 | int database_max_connections(PgDatabase *db) _MUSTCHECK; 22 | int user_max_connections(PgUser *user) _MUSTCHECK; 23 | -------------------------------------------------------------------------------- /include/smp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Multiprocessing. 21 | */ 22 | 23 | void smp_run(void); 24 | void smp_worker_setup(void); 25 | -------------------------------------------------------------------------------- /include/stats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | void stats_setup(void); 20 | 21 | bool admin_database_stats(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; 22 | bool admin_database_stats_totals(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; 23 | bool admin_database_stats_averages(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; 24 | bool show_stat_totals(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; 25 | -------------------------------------------------------------------------------- /include/system.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Required system headers 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #ifdef WIN32 27 | #include "win32support.h" 28 | #endif 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | #ifdef HAVE_LIBGEN_H 38 | #include 39 | #endif 40 | #ifdef HAVE_SYS_UIO_H 41 | #include 42 | #endif 43 | 44 | #ifndef UNIX_PATH_MAX 45 | #define UNIX_PATH_MAX 128 /* actual sizeof() will be applied later anyway */ 46 | #endif 47 | 48 | /* 49 | * PostgreSQL type OIDs for resultsets. 50 | */ 51 | 52 | #define INT8OID 20 53 | #define INT4OID 23 54 | #define TEXTOID 25 55 | 56 | /* 57 | * libc compat functions. 58 | */ 59 | 60 | #ifndef HAVE_LSTAT 61 | static inline int lstat(const char *path, struct stat *st) { return stat(path, st); } 62 | #endif 63 | 64 | bool check_unix_peer_name(int fd, const char *username); 65 | 66 | void change_user(const char *user); 67 | 68 | void change_file_mode(const char *fn, mode_t mode, const char *user, const char *group); 69 | -------------------------------------------------------------------------------- /include/takeover.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | void takeover_init(void); 20 | bool takeover_login(PgSocket *bouncer) _MUSTCHECK; 21 | void takeover_login_failed(void); 22 | void takeover_finish(void); 23 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * logging about specific socket 21 | */ 22 | int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen); 23 | 24 | #define slog_error(sk, args...) log_generic(LG_ERROR, sk, ## args) 25 | #define slog_warning(sk, args...) log_generic(LG_WARNING, sk, ## args) 26 | #define slog_info(sk, args...) log_generic(LG_INFO, sk, ## args) 27 | #define slog_debug(sk, args...) do { \ 28 | if (unlikely(cf_verbose > 0)) \ 29 | log_generic(LG_DEBUG, sk, ## args); \ 30 | } while (0) 31 | #define slog_noise(sk, args...) do { \ 32 | if (unlikely(cf_verbose > 1)) \ 33 | log_generic(LG_NOISE, sk, ## args); \ 34 | } while (0) 35 | 36 | /* 37 | * password tools 38 | */ 39 | #define MD5_PASSWD_LEN 35 40 | #define isMD5(passwd) (memcmp(passwd, "md5", 3) == 0 \ 41 | && strlen(passwd) == MD5_PASSWD_LEN) 42 | void pg_md5_encrypt(const char *part1, const char *part2, size_t p2len, char *dest); 43 | void get_random_bytes(uint8_t *dest, int len); 44 | 45 | const char *bin2hex(const uint8_t *src, unsigned srclen, char *dst, unsigned dstlen); 46 | 47 | bool tune_socket(int sock, bool is_unix) _MUSTCHECK; 48 | void tune_accept(int sock, bool on); 49 | 50 | void create_sockets(struct StatList *sock_list); 51 | void cleanup_sockets(struct StatList *sock_list); 52 | 53 | bool strlist_contains(const char *liststr, const char *str); 54 | 55 | void fill_remote_addr(PgSocket *sk, int fd, bool is_unix); 56 | void fill_local_addr(PgSocket *sk, int fd, bool is_unix); 57 | 58 | 59 | void rescue_timers(void); 60 | void safe_evtimer_add(struct event *ev, struct timeval *tv); 61 | 62 | /* log truncated strings */ 63 | #define safe_strcpy(dst, src, dstlen) do { \ 64 | size_t needed = strlcpy(dst, src, dstlen); \ 65 | if (unlikely(needed >= (dstlen))) \ 66 | log_warning("bug in %s:%d - string truncated", __FILE__, __LINE__); \ 67 | } while (0) 68 | -------------------------------------------------------------------------------- /include/varcache.h: -------------------------------------------------------------------------------- 1 | enum VarCacheIdx { 2 | VDateStyle = 0, 3 | VClientEncoding, 4 | VTimeZone, 5 | VStdStr, 6 | VAppName, 7 | NumVars 8 | }; 9 | 10 | typedef struct VarCache VarCache; 11 | 12 | struct VarCache { 13 | struct PStr *var_list[NumVars]; 14 | }; 15 | 16 | bool varcache_set(VarCache *cache, const char *key, const char *value) /* _MUSTCHECK */; 17 | bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p) _MUSTCHECK; 18 | void varcache_fill_unset(VarCache *src, PgSocket *dst); 19 | void varcache_clean(VarCache *cache); 20 | void varcache_add_params(PktBuf *pkt, VarCache *vars); 21 | void varcache_deinit(void); 22 | -------------------------------------------------------------------------------- /src/pktbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Packet writing and sending. 21 | */ 22 | 23 | #include "bouncer.h" 24 | 25 | void pktbuf_free(PktBuf *buf) 26 | { 27 | if (!buf || buf->fixed_buf) 28 | return; 29 | 30 | log_debug("pktbuf_free(%p)", buf); 31 | if (buf->buf) 32 | free(buf->buf); 33 | if (buf->ev) 34 | free(buf->ev); 35 | free(buf); 36 | } 37 | 38 | PktBuf *pktbuf_dynamic(int start_len) 39 | { 40 | PktBuf *buf = zmalloc(sizeof(PktBuf)); 41 | log_debug("pktbuf_dynamic(%d): %p", start_len, buf); 42 | if (!buf) 43 | return NULL; 44 | 45 | buf->ev = zmalloc(sizeof(*buf->ev)); 46 | if (!buf->ev) { 47 | pktbuf_free(buf); 48 | return NULL; 49 | } 50 | buf->buf = malloc(start_len); 51 | if (!buf->buf) { 52 | pktbuf_free(buf); 53 | return NULL; 54 | } 55 | buf->buf_len = start_len; 56 | return buf; 57 | } 58 | 59 | void pktbuf_reset(struct PktBuf *pkt) 60 | { 61 | pkt->failed = 0; 62 | pkt->write_pos = 0; 63 | pkt->pktlen_pos = 0; 64 | pkt->send_pos = 0; 65 | pkt->sending = 0; 66 | } 67 | 68 | void pktbuf_static(PktBuf *buf, uint8_t *data, int len) 69 | { 70 | memset(buf, 0, sizeof(*buf)); 71 | buf->buf = data; 72 | buf->buf_len = len; 73 | buf->fixed_buf = 1; 74 | } 75 | 76 | static PktBuf *temp_pktbuf; 77 | 78 | struct PktBuf *pktbuf_temp(void) 79 | { 80 | if (!temp_pktbuf) 81 | temp_pktbuf = pktbuf_dynamic(512); 82 | if (!temp_pktbuf) 83 | fatal("failed to create temp pktbuf"); 84 | pktbuf_reset(temp_pktbuf); 85 | return temp_pktbuf; 86 | } 87 | 88 | void pktbuf_cleanup(void) 89 | { 90 | pktbuf_free(temp_pktbuf); 91 | temp_pktbuf = NULL; 92 | } 93 | 94 | bool pktbuf_send_immediate(PktBuf *buf, PgSocket *sk) 95 | { 96 | uint8_t *pos = buf->buf + buf->send_pos; 97 | int amount = buf->write_pos - buf->send_pos; 98 | int res; 99 | 100 | if (buf->failed) 101 | return false; 102 | res = sbuf_op_send(&sk->sbuf, pos, amount); 103 | if (res < 0) { 104 | log_debug("pktbuf_send_immediate: %s", strerror(errno)); 105 | } 106 | return res == amount; 107 | } 108 | 109 | static void pktbuf_send_func(int fd, short flags, void *arg) 110 | { 111 | PktBuf *buf = arg; 112 | SBuf *sbuf = &buf->queued_dst->sbuf; 113 | int amount, res; 114 | 115 | log_debug("pktbuf_send_func(%d, %d, %p)", fd, (int)flags, buf); 116 | 117 | if (buf->failed) 118 | return; 119 | 120 | amount = buf->write_pos - buf->send_pos; 121 | res = sbuf_op_send(sbuf, buf->buf + buf->send_pos, amount); 122 | if (res < 0) { 123 | if (errno == EAGAIN) { 124 | res = 0; 125 | } else { 126 | log_error("pktbuf_send_func: %s", strerror(errno)); 127 | pktbuf_free(buf); 128 | return; 129 | } 130 | } 131 | buf->send_pos += res; 132 | 133 | if (buf->send_pos < buf->write_pos) { 134 | event_set(buf->ev, fd, EV_WRITE, pktbuf_send_func, buf); 135 | res = event_add(buf->ev, NULL); 136 | if (res < 0) { 137 | log_error("pktbuf_send_func: %s", strerror(errno)); 138 | pktbuf_free(buf); 139 | } 140 | } else { 141 | pktbuf_free(buf); 142 | } 143 | } 144 | 145 | bool pktbuf_send_queued(PktBuf *buf, PgSocket *sk) 146 | { 147 | Assert(!buf->sending); 148 | Assert(!buf->fixed_buf); 149 | 150 | if (buf->failed) { 151 | pktbuf_free(buf); 152 | return send_pooler_error(sk, true, "result prepare failed"); 153 | } else { 154 | buf->sending = 1; 155 | buf->queued_dst = sk; 156 | pktbuf_send_func(sk->sbuf.sock, EV_WRITE, buf); 157 | return true; 158 | } 159 | } 160 | 161 | static void make_room(PktBuf *buf, int len) 162 | { 163 | int newlen = buf->buf_len; 164 | int need = buf->write_pos + len; 165 | void *ptr; 166 | 167 | if (newlen >= need) 168 | return; 169 | 170 | if (buf->failed) 171 | return; 172 | 173 | if (buf->fixed_buf) { 174 | buf->failed = 1; 175 | return; 176 | } 177 | 178 | while (newlen < need) 179 | newlen = newlen * 2; 180 | 181 | log_debug("make_room(%p, %d): realloc newlen=%d", 182 | buf, len, newlen); 183 | ptr = realloc(buf->buf, newlen); 184 | if (!ptr) { 185 | buf->failed = 1; 186 | } else { 187 | buf->buf = ptr; 188 | buf->buf_len = newlen; 189 | } 190 | } 191 | 192 | void pktbuf_put_char(PktBuf *buf, char val) 193 | { 194 | make_room(buf, 1); 195 | if (buf->failed) 196 | return; 197 | 198 | buf->buf[buf->write_pos++] = val; 199 | } 200 | 201 | void pktbuf_put_uint16(PktBuf *buf, uint16_t val) 202 | { 203 | make_room(buf, 4); 204 | if (buf->failed) 205 | return; 206 | 207 | buf->buf[buf->write_pos++] = (val >> 8) & 255; 208 | buf->buf[buf->write_pos++] = val & 255; 209 | } 210 | 211 | void pktbuf_put_uint32(PktBuf *buf, uint32_t val) 212 | { 213 | uint8_t *pos; 214 | 215 | make_room(buf, 4); 216 | if (buf->failed) 217 | return; 218 | 219 | pos = buf->buf + buf->write_pos; 220 | pos[0] = (val >> 24) & 255; 221 | pos[1] = (val >> 16) & 255; 222 | pos[2] = (val >> 8) & 255; 223 | pos[3] = val & 255; 224 | buf->write_pos += 4; 225 | } 226 | 227 | void pktbuf_put_uint64(PktBuf *buf, uint64_t val) 228 | { 229 | pktbuf_put_uint32(buf, val >> 32); 230 | pktbuf_put_uint32(buf, (uint32_t)val); 231 | } 232 | 233 | void pktbuf_put_bytes(PktBuf *buf, const void *data, int len) 234 | { 235 | make_room(buf, len); 236 | if (buf->failed) 237 | return; 238 | memcpy(buf->buf + buf->write_pos, data, len); 239 | buf->write_pos += len; 240 | } 241 | 242 | void pktbuf_put_string(PktBuf *buf, const char *str) 243 | { 244 | int len = strlen(str); 245 | pktbuf_put_bytes(buf, str, len + 1); 246 | } 247 | 248 | /* 249 | * write header, remember pos to write length later. 250 | */ 251 | void pktbuf_start_packet(PktBuf *buf, int type) 252 | { 253 | if (buf->failed) 254 | return; 255 | 256 | if (type < 256) { 257 | /* new-style packet */ 258 | pktbuf_put_char(buf, type); 259 | buf->pktlen_pos = buf->write_pos; 260 | pktbuf_put_uint32(buf, 0); 261 | } else { 262 | /* old-style packet */ 263 | buf->pktlen_pos = buf->write_pos; 264 | pktbuf_put_uint32(buf, 0); 265 | pktbuf_put_uint32(buf, type); 266 | } 267 | } 268 | 269 | void pktbuf_finish_packet(PktBuf *buf) 270 | { 271 | uint8_t *pos; 272 | unsigned len; 273 | 274 | if (buf->failed) 275 | return; 276 | 277 | len = buf->write_pos - buf->pktlen_pos; 278 | pos = buf->buf + buf->pktlen_pos; 279 | buf->pktlen_pos = 0; 280 | 281 | *pos++ = (len >> 24) & 255; 282 | *pos++ = (len >> 16) & 255; 283 | *pos++ = (len >> 8) & 255; 284 | *pos++ = len & 255; 285 | } 286 | 287 | /* types: 288 | * c - char/byte 289 | * h - uint16 290 | * i - uint32 291 | * q - uint64 292 | * s - Cstring 293 | * b - bytes 294 | */ 295 | void pktbuf_write_generic(PktBuf *buf, int type, const char *pktdesc, ...) 296 | { 297 | va_list ap; 298 | int len; 299 | const char *adesc = pktdesc; 300 | uint8_t *bin; 301 | 302 | pktbuf_start_packet(buf, type); 303 | 304 | va_start(ap, pktdesc); 305 | while (*adesc) { 306 | switch (*adesc) { 307 | case 'c': 308 | pktbuf_put_char(buf, va_arg(ap, int)); 309 | break; 310 | case 'h': 311 | pktbuf_put_uint16(buf, va_arg(ap, int)); 312 | break; 313 | case 'i': 314 | pktbuf_put_uint32(buf, va_arg(ap, int)); 315 | break; 316 | case 'q': 317 | pktbuf_put_uint64(buf, va_arg(ap, uint64_t)); 318 | break; 319 | case 's': 320 | pktbuf_put_string(buf, va_arg(ap, char *)); 321 | break; 322 | case 'b': 323 | bin = va_arg(ap, uint8_t *); 324 | len = va_arg(ap, int); 325 | pktbuf_put_bytes(buf, bin, len); 326 | break; 327 | default: 328 | fatal("bad pktdesc: %s", pktdesc); 329 | } 330 | adesc++; 331 | } 332 | va_end(ap); 333 | 334 | /* set correct length */ 335 | pktbuf_finish_packet(buf); 336 | } 337 | 338 | 339 | /* send resultset column info 340 | * tupdesc keys: 341 | * 'i' - int4 342 | * 'q' - int8 343 | * 's' - string 344 | * 'T' - usec_t to date 345 | */ 346 | void pktbuf_write_RowDescription(PktBuf *buf, const char *tupdesc, ...) 347 | { 348 | va_list ap; 349 | char *name; 350 | int i, ncol = strlen(tupdesc); 351 | 352 | log_noise("write RowDescription"); 353 | 354 | pktbuf_start_packet(buf, 'T'); 355 | 356 | pktbuf_put_uint16(buf, ncol); 357 | 358 | va_start(ap, tupdesc); 359 | for (i = 0; i < ncol; i++) { 360 | name = va_arg(ap, char *); 361 | 362 | /* Fields: name, reloid, colnr, oid, typsize, typmod, fmt */ 363 | pktbuf_put_string(buf, name); 364 | pktbuf_put_uint32(buf, 0); 365 | pktbuf_put_uint16(buf, 0); 366 | if (tupdesc[i] == 's') { 367 | pktbuf_put_uint32(buf, TEXTOID); 368 | pktbuf_put_uint16(buf, -1); 369 | } else if (tupdesc[i] == 'i') { 370 | pktbuf_put_uint32(buf, INT4OID); 371 | pktbuf_put_uint16(buf, 4); 372 | } else if (tupdesc[i] == 'q') { 373 | pktbuf_put_uint32(buf, INT8OID); 374 | pktbuf_put_uint16(buf, 8); 375 | } else if (tupdesc[i] == 'T') { 376 | pktbuf_put_uint32(buf, TEXTOID); 377 | pktbuf_put_uint16(buf, -1); 378 | } else { 379 | fatal("bad tupdesc"); 380 | } 381 | pktbuf_put_uint32(buf, 0); 382 | pktbuf_put_uint16(buf, 0); 383 | } 384 | va_end(ap); 385 | 386 | /* set correct length */ 387 | pktbuf_finish_packet(buf); 388 | } 389 | 390 | /* 391 | * send DataRow. 392 | * 393 | * tupdesc keys: 394 | * 'i' - int4 395 | * 'q' - int8 396 | * 's' - string 397 | * 'T' - usec_t to date 398 | */ 399 | void pktbuf_write_DataRow(PktBuf *buf, const char *tupdesc, ...) 400 | { 401 | char tmp[32]; 402 | const char *val = NULL; 403 | int i, len, ncol = strlen(tupdesc); 404 | va_list ap; 405 | 406 | pktbuf_start_packet(buf, 'D'); 407 | pktbuf_put_uint16(buf, ncol); 408 | 409 | va_start(ap, tupdesc); 410 | for (i = 0; i < ncol; i++) { 411 | if (tupdesc[i] == 'i') { 412 | snprintf(tmp, sizeof(tmp), "%d", va_arg(ap, int)); 413 | val = tmp; 414 | } else if (tupdesc[i] == 'q') { 415 | snprintf(tmp, sizeof(tmp), "%" PRIu64, va_arg(ap, uint64_t)); 416 | val = tmp; 417 | } else if (tupdesc[i] == 's') { 418 | val = va_arg(ap, char *); 419 | } else if (tupdesc[i] == 'T') { 420 | usec_t time = va_arg(ap, usec_t); 421 | val = format_time_s(time, tmp, sizeof(tmp)); 422 | } else { 423 | fatal("bad tupdesc: %s", tupdesc); 424 | } 425 | 426 | if (val) { 427 | len = strlen(val); 428 | pktbuf_put_uint32(buf, len); 429 | pktbuf_put_bytes(buf, val, len); 430 | } else { 431 | /* NULL */ 432 | pktbuf_put_uint32(buf, -1); 433 | } 434 | } 435 | va_end(ap); 436 | 437 | pktbuf_finish_packet(buf); 438 | } 439 | 440 | /* 441 | * Send Parse+Bind+Execute with string parameters. 442 | */ 443 | void pktbuf_write_ExtQuery(PktBuf *buf, const char *query, int nargs, ...) 444 | { 445 | va_list ap; 446 | const char *val; 447 | int len, i; 448 | 449 | /* Parse */ 450 | pktbuf_write_generic(buf, 'P', "csh", 0, query, 0); 451 | 452 | /* Bind */ 453 | pktbuf_start_packet(buf, 'B'); 454 | pktbuf_put_char(buf, 0); /* portal name */ 455 | pktbuf_put_char(buf, 0); /* query name */ 456 | pktbuf_put_uint16(buf, 0); /* number of parameter format codes */ 457 | pktbuf_put_uint16(buf, nargs); /* number of parameter values */ 458 | 459 | va_start(ap, nargs); 460 | for (i = 0; i < nargs; i++) { 461 | val = va_arg(ap, char *); 462 | len = strlen(val); 463 | pktbuf_put_uint32(buf, len); 464 | pktbuf_put_bytes(buf, val, len); 465 | } 466 | va_end(ap); 467 | 468 | pktbuf_put_uint16(buf, 0); /* number of result-column format codes */ 469 | pktbuf_finish_packet(buf); 470 | 471 | /* Describe */ 472 | pktbuf_write_generic(buf, 'D', "cc", 'P', 0); 473 | 474 | /* Execute */ 475 | pktbuf_write_generic(buf, 'E', "ci", 0, 0); 476 | 477 | /* Sync */ 478 | pktbuf_write_generic(buf, 'S', ""); 479 | } 480 | -------------------------------------------------------------------------------- /src/pooler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Handling of pooler listening sockets 21 | */ 22 | 23 | #include "bouncer.h" 24 | 25 | #include 26 | 27 | 28 | 29 | static STATLIST(sock_list); 30 | 31 | /* should listening sockets be active or suspended? */ 32 | static bool need_active = false; 33 | /* is it actually active or suspended? */ 34 | static bool pooler_active = false; 35 | 36 | /* on accept() failure sleep 5 seconds */ 37 | static struct event ev_err; 38 | static struct timeval err_timeout = {5, 0}; 39 | 40 | void pooler_tune_accept(bool on) 41 | { 42 | struct List *el; 43 | struct ListenSocket *ls; 44 | statlist_for_each(el, &sock_list) { 45 | ls = container_of(el, struct ListenSocket, node); 46 | if (!pga_is_unix(&ls->addr)) 47 | tune_accept(ls->fd, on); 48 | } 49 | } 50 | 51 | static void atexit_cb(void) 52 | { 53 | cleanup_sockets(&sock_list); 54 | } 55 | 56 | static void err_wait_func(int sock, short flags, void *arg) 57 | { 58 | if (cf_pause_mode != P_SUSPEND) 59 | resume_pooler(); 60 | } 61 | 62 | static const char *addrpair(const PgAddr *src, const PgAddr *dst) 63 | { 64 | static char ip1buf[PGADDR_BUF], ip2buf[PGADDR_BUF], 65 | buf[2*PGADDR_BUF + 16]; 66 | const char *ip1, *ip2; 67 | if (pga_is_unix(src)) 68 | return "unix->unix"; 69 | 70 | ip1 = pga_ntop(src, ip1buf, sizeof(ip1buf)); 71 | ip2 = pga_ntop(src, ip2buf, sizeof(ip2buf)); 72 | snprintf(buf, sizeof(buf), "%s:%d -> %s:%d", 73 | ip1, pga_port(src), ip2, pga_port(dst)); 74 | return buf; 75 | } 76 | 77 | static const char *conninfo(const PgSocket *sk) 78 | { 79 | if (is_server_socket(sk)) { 80 | return addrpair(&sk->local_addr, &sk->remote_addr); 81 | } else { 82 | return addrpair(&sk->remote_addr, &sk->local_addr); 83 | } 84 | } 85 | 86 | /* got new connection, associate it with client struct */ 87 | static void pool_accept(int sock, short flags, void *arg) 88 | { 89 | struct ListenSocket *ls = arg; 90 | int fd; 91 | PgSocket *client; 92 | union { 93 | struct sockaddr_in in; 94 | struct sockaddr_in6 in6; 95 | struct sockaddr_un un; 96 | struct sockaddr sa; 97 | } raddr; 98 | socklen_t len = sizeof(raddr); 99 | bool is_unix = pga_is_unix(&ls->addr); 100 | 101 | if(!(flags & EV_READ)) { 102 | log_warning("No EV_READ in pool_accept"); 103 | return; 104 | } 105 | loop: 106 | /* get fd */ 107 | fd = safe_accept(sock, &raddr.sa, &len); 108 | if (fd < 0) { 109 | if (errno == EAGAIN) 110 | return; 111 | else if (errno == ECONNABORTED) 112 | return; 113 | 114 | /* 115 | * probably fd limit, pointless to try often 116 | * wait a bit, hope that admin resolves somehow 117 | */ 118 | log_error("accept() failed: %s", strerror(errno)); 119 | evtimer_set(&ev_err, err_wait_func, NULL); 120 | safe_evtimer_add(&ev_err, &err_timeout); 121 | suspend_pooler(); 122 | return; 123 | } 124 | 125 | log_noise("new fd from accept=%d", fd); 126 | if (is_unix) { 127 | client = accept_client(fd, true); 128 | } else { 129 | client = accept_client(fd, false); 130 | } 131 | 132 | if (client) 133 | slog_debug(client, "P: got connection: %s", conninfo(client)); 134 | 135 | /* 136 | * there may be several clients waiting, 137 | * avoid context switch by looping 138 | */ 139 | goto loop; 140 | } 141 | 142 | bool use_pooler_socket(int sock, bool is_unix) 143 | { 144 | struct ListenSocket *ls; 145 | int res; 146 | char buf[PGADDR_BUF]; 147 | 148 | if (!tune_socket(sock, is_unix)) 149 | return false; 150 | 151 | ls = calloc(1, sizeof(*ls)); 152 | if (!ls) 153 | return false; 154 | ls->fd = sock; 155 | if (is_unix) { 156 | pga_set(&ls->addr, AF_UNIX, cf_listen_port); 157 | } else { 158 | struct sockaddr_storage ss; 159 | socklen_t len = sizeof(ss); 160 | res = getsockname(sock, (struct sockaddr *)&ss, &len); 161 | if (res < 0) { 162 | log_error("getsockname failed"); 163 | free(ls); 164 | return false; 165 | } 166 | pga_copy(&ls->addr, (struct sockaddr *)&ss); 167 | } 168 | log_info("got pooler socket: %s", pga_str(&ls->addr, buf, sizeof(buf))); 169 | statlist_append(&sock_list, &ls->node); 170 | return true; 171 | } 172 | 173 | void suspend_pooler(void) 174 | { 175 | struct List *el; 176 | struct ListenSocket *ls; 177 | 178 | need_active = false; 179 | statlist_for_each(el, &sock_list) { 180 | ls = container_of(el, struct ListenSocket, node); 181 | if (!ls->active) 182 | continue; 183 | if (event_del(&ls->ev) < 0) { 184 | log_warning("suspend_pooler, event_del: %s", strerror(errno)); 185 | return; 186 | } 187 | ls->active = false; 188 | } 189 | pooler_active = false; 190 | } 191 | 192 | void resume_pooler(void) 193 | { 194 | struct List *el; 195 | struct ListenSocket *ls; 196 | 197 | need_active = true; 198 | statlist_for_each(el, &sock_list) { 199 | ls = container_of(el, struct ListenSocket, node); 200 | if (ls->active) 201 | continue; 202 | event_set(&ls->ev, ls->fd, EV_READ | EV_PERSIST, pool_accept, ls); 203 | if (event_add(&ls->ev, NULL) < 0) { 204 | log_warning("event_add failed: %s", strerror(errno)); 205 | return; 206 | } 207 | ls->active = true; 208 | } 209 | pooler_active = true; 210 | } 211 | 212 | /* retry previously failed suspend_pooler() / resume_pooler() */ 213 | void per_loop_pooler_maint(void) 214 | { 215 | if (need_active && !pooler_active) 216 | resume_pooler(); 217 | else if (!need_active && pooler_active) 218 | suspend_pooler(); 219 | } 220 | 221 | /* listen on socket - should happen after all other initializations */ 222 | void pooler_setup(void) 223 | { 224 | static int init_done = 0; 225 | 226 | if (!init_done) { 227 | /* remove socket on shutdown */ 228 | atexit(atexit_cb); 229 | init_done = 1; 230 | } 231 | 232 | create_sockets(&sock_list); 233 | if (!statlist_count(&sock_list)) 234 | fatal("nowhere to listen on"); 235 | 236 | resume_pooler(); 237 | } 238 | 239 | bool for_each_pooler_fd(pooler_cb cbfunc, void *arg) 240 | { 241 | struct List *el; 242 | struct ListenSocket *ls; 243 | bool ok; 244 | 245 | statlist_for_each(el, &sock_list) { 246 | ls = container_of(el, struct ListenSocket, node); 247 | ok = cbfunc(arg, ls->fd, &ls->addr); 248 | if (!ok) 249 | return false; 250 | } 251 | return true; 252 | } 253 | -------------------------------------------------------------------------------- /src/proto.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Pieces that need to have detailed info about protocol. 21 | */ 22 | 23 | #include "bouncer.h" 24 | 25 | /* 26 | * parse protocol header from struct MBuf 27 | */ 28 | 29 | /* parses pkt header from buffer, returns false if failed */ 30 | bool get_header(struct MBuf *data, PktHdr *pkt) 31 | { 32 | unsigned type; 33 | uint32_t len; 34 | unsigned got; 35 | unsigned avail; 36 | uint16_t len16; 37 | uint8_t type8; 38 | uint32_t code; 39 | struct MBuf hdr; 40 | const uint8_t *ptr; 41 | 42 | mbuf_copy(data, &hdr); 43 | 44 | if (mbuf_avail_for_read(&hdr) < NEW_HEADER_LEN) { 45 | log_noise("get_header: less than 5 bytes available"); 46 | return false; 47 | } 48 | if (!mbuf_get_byte(&hdr, &type8)) 49 | return false; 50 | type = type8; 51 | if (type != 0) { 52 | /* wire length does not include type byte */ 53 | if (!mbuf_get_uint32be(&hdr, &len)) 54 | return false; 55 | len++; 56 | got = NEW_HEADER_LEN; 57 | } else { 58 | if (!mbuf_get_byte(&hdr, &type8)) 59 | return false; 60 | if (type8 != 0) { 61 | log_noise("get_header: unknown special pkt"); 62 | return false; 63 | } 64 | /* don't tolerate partial pkt */ 65 | if (mbuf_avail_for_read(&hdr) < OLD_HEADER_LEN - 2) { 66 | log_noise("get_header: less than 8 bytes for special pkt"); 67 | return false; 68 | } 69 | if (!mbuf_get_uint16be(&hdr, &len16)) 70 | return false; 71 | len = len16; 72 | if (!mbuf_get_uint32be(&hdr, &code)) 73 | return false; 74 | if (code == PKT_CANCEL) { 75 | type = PKT_CANCEL; 76 | } else if (code == PKT_SSLREQ) { 77 | type = PKT_SSLREQ; 78 | } else if ((code >> 16) == 3 && (code & 0xFFFF) < 2) { 79 | type = PKT_STARTUP; 80 | } else if (code == PKT_STARTUP_V2) { 81 | type = PKT_STARTUP_V2; 82 | } else { 83 | log_noise("get_header: unknown special pkt: len=%u code=%u", len, code); 84 | return false; 85 | } 86 | got = OLD_HEADER_LEN; 87 | } 88 | 89 | /* don't believe nonsense */ 90 | if (len < got || len > cf_max_packet_size) 91 | return false; 92 | 93 | /* store pkt info */ 94 | pkt->type = type; 95 | pkt->len = len; 96 | 97 | /* fill pkt with only data for this packet */ 98 | if (len > mbuf_avail_for_read(data)) { 99 | avail = mbuf_avail_for_read(data); 100 | } else { 101 | avail = len; 102 | } 103 | if (!mbuf_slice(data, avail, &pkt->data)) 104 | return false; 105 | 106 | /* tag header as read */ 107 | return mbuf_get_bytes(&pkt->data, got, &ptr); 108 | } 109 | 110 | 111 | /* 112 | * Send error message packet to client. 113 | */ 114 | 115 | bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg) 116 | { 117 | uint8_t tmpbuf[512]; 118 | PktBuf buf; 119 | 120 | if (cf_log_pooler_errors) 121 | slog_warning(client, "Pooler Error: %s", msg); 122 | 123 | pktbuf_static(&buf, tmpbuf, sizeof(tmpbuf)); 124 | pktbuf_write_generic(&buf, 'E', "cscscsc", 125 | 'S', "ERROR", 'C', "08P01", 'M', msg, 0); 126 | if (send_ready) 127 | pktbuf_write_ReadyForQuery(&buf); 128 | return pktbuf_send_immediate(&buf, client); 129 | } 130 | 131 | /* 132 | * Parse server error message and log it. 133 | */ 134 | void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p) 135 | { 136 | const char *level = NULL, *msg = NULL, *val; 137 | uint8_t type; 138 | while (mbuf_avail_for_read(&pkt->data)) { 139 | if (!mbuf_get_byte(&pkt->data, &type)) 140 | break; 141 | if (type == 0) 142 | break; 143 | if (!mbuf_get_string(&pkt->data, &val)) 144 | break; 145 | if (type == 'S') { 146 | level = val; 147 | } else if (type == 'M') { 148 | msg = val; 149 | } 150 | } 151 | *level_p = level; 152 | *msg_p = msg; 153 | } 154 | 155 | void log_server_error(const char *note, PktHdr *pkt) 156 | { 157 | const char *level = NULL, *msg = NULL; 158 | 159 | parse_server_error(pkt, &level, &msg); 160 | 161 | if (!msg || !level) { 162 | log_error("%s: partial error message, cannot log", note); 163 | } else { 164 | log_error("%s: %s: %s", note, level, msg); 165 | } 166 | } 167 | 168 | 169 | /* 170 | * Preparation of welcome message for client connection. 171 | */ 172 | 173 | /* add another server parameter packet to cache */ 174 | bool add_welcome_parameter(PgPool *pool, const char *key, const char *val) 175 | { 176 | PktBuf *msg = pool->welcome_msg; 177 | 178 | if (pool->welcome_msg_ready) 179 | return true; 180 | 181 | if (!msg) { 182 | msg = pktbuf_dynamic(128); 183 | if (!msg) 184 | return false; 185 | pool->welcome_msg = msg; 186 | } 187 | 188 | /* first packet must be AuthOk */ 189 | if (msg->write_pos == 0) 190 | pktbuf_write_AuthenticationOk(msg); 191 | 192 | /* if not stored in ->orig_vars, write full packet */ 193 | if (!varcache_set(&pool->orig_vars, key, val)) 194 | pktbuf_write_ParameterStatus(msg, key, val); 195 | 196 | return !msg->failed; 197 | } 198 | 199 | /* all parameters processed */ 200 | void finish_welcome_msg(PgSocket *server) 201 | { 202 | PgPool *pool = server->pool; 203 | if (pool->welcome_msg_ready) 204 | return; 205 | pool->welcome_msg_ready = 1; 206 | } 207 | 208 | bool welcome_client(PgSocket *client) 209 | { 210 | int res; 211 | PgPool *pool = client->pool; 212 | const PktBuf *pmsg = pool->welcome_msg; 213 | PktBuf *msg; 214 | 215 | slog_noise(client, "P: welcome_client"); 216 | 217 | /* copy prepared stuff around */ 218 | msg = pktbuf_temp(); 219 | pktbuf_put_bytes(msg, pmsg->buf, pmsg->write_pos); 220 | 221 | /* fill vars */ 222 | varcache_fill_unset(&pool->orig_vars, client); 223 | varcache_add_params(msg, &client->vars); 224 | 225 | /* give each client its own cancel key */ 226 | get_random_bytes(client->cancel_key, 8); 227 | pktbuf_write_BackendKeyData(msg, client->cancel_key); 228 | 229 | /* finish */ 230 | pktbuf_write_ReadyForQuery(msg); 231 | if (msg->failed) { 232 | disconnect_client(client, true, "failed to prepare welcome message"); 233 | return false; 234 | } 235 | 236 | /* send all together */ 237 | res = pktbuf_send_immediate(msg, client); 238 | if (!res) { 239 | disconnect_client(client, true, "failed to send welcome message"); 240 | return false; 241 | } 242 | return true; 243 | } 244 | 245 | /* 246 | * Password authentication for server 247 | */ 248 | 249 | static PgUser *get_srv_psw(PgSocket *server) 250 | { 251 | PgDatabase *db = server->pool->db; 252 | PgUser *user = server->pool->user; 253 | 254 | /* if forced user without password, use userlist psw */ 255 | if (!user->passwd[0] && db->forced_user) { 256 | PgUser *u2 = find_user(user->name); 257 | if (u2) 258 | return u2; 259 | } 260 | return user; 261 | } 262 | 263 | /* actual packet send */ 264 | static bool send_password(PgSocket *server, const char *enc_psw) 265 | { 266 | bool res; 267 | SEND_PasswordMessage(res, server, enc_psw); 268 | return res; 269 | } 270 | 271 | static bool login_clear_psw(PgSocket *server) 272 | { 273 | PgUser *user = get_srv_psw(server); 274 | slog_debug(server, "P: send clear password"); 275 | return send_password(server, user->passwd); 276 | } 277 | 278 | static bool login_md5_psw(PgSocket *server, const uint8_t *salt) 279 | { 280 | char txt[MD5_PASSWD_LEN + 1], *src; 281 | PgUser *user = get_srv_psw(server); 282 | 283 | slog_debug(server, "P: send md5 password"); 284 | if (!isMD5(user->passwd)) { 285 | pg_md5_encrypt(user->passwd, user->name, strlen(user->name), txt); 286 | src = txt + 3; 287 | } else { 288 | src = user->passwd + 3; 289 | } 290 | pg_md5_encrypt(src, (char *)salt, 4, txt); 291 | 292 | return send_password(server, txt); 293 | } 294 | 295 | /* answer server authentication request */ 296 | bool answer_authreq(PgSocket *server, PktHdr *pkt) 297 | { 298 | uint32_t cmd; 299 | const uint8_t *salt; 300 | bool res = false; 301 | 302 | /* authreq body must contain 32bit cmd */ 303 | if (mbuf_avail_for_read(&pkt->data) < 4) 304 | return false; 305 | 306 | if (!mbuf_get_uint32be(&pkt->data, &cmd)) 307 | return false; 308 | switch (cmd) { 309 | case AUTH_OK: 310 | slog_debug(server, "S: auth ok"); 311 | res = true; 312 | break; 313 | case AUTH_PLAIN: 314 | slog_debug(server, "S: req cleartext password"); 315 | res = login_clear_psw(server); 316 | break; 317 | case AUTH_MD5: 318 | slog_debug(server, "S: req md5-crypted psw"); 319 | if (!mbuf_get_bytes(&pkt->data, 4, &salt)) 320 | return false; 321 | res = login_md5_psw(server, salt); 322 | break; 323 | default: 324 | slog_error(server, "unknown/unsupported auth method: %d", cmd); 325 | res = false; 326 | break; 327 | } 328 | return res; 329 | } 330 | 331 | bool send_startup_packet(PgSocket *server) 332 | { 333 | PgDatabase *db = server->pool->db; 334 | const char *username = server->pool->user->name; 335 | PktBuf *pkt; 336 | 337 | pkt = pktbuf_temp(); 338 | pktbuf_write_StartupMessage(pkt, username, 339 | db->startup_params->buf, 340 | db->startup_params->write_pos); 341 | return pktbuf_send_immediate(pkt, server); 342 | } 343 | 344 | bool send_sslreq_packet(PgSocket *server) 345 | { 346 | int res; 347 | SEND_wrap(16, pktbuf_write_SSLRequest, res, server); 348 | return res; 349 | } 350 | 351 | int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...) 352 | { 353 | const char *val = NULL; 354 | uint32_t len; 355 | uint16_t ncol; 356 | unsigned i, asked; 357 | va_list ap; 358 | int *int_p; 359 | uint64_t *long_p; 360 | const char **str_p; 361 | 362 | asked = strlen(tupdesc); 363 | if (!mbuf_get_uint16be(pkt, &ncol)) 364 | return -1; 365 | 366 | va_start(ap, tupdesc); 367 | for (i = 0; i < asked; i++) { 368 | if (i < ncol) { 369 | if (!mbuf_get_uint32be(pkt, &len)) { 370 | va_end(ap); 371 | return -1; 372 | } 373 | if ((int32_t)len < 0) { 374 | val = NULL; 375 | } else { 376 | if (!mbuf_get_chars(pkt, len, &val)) { 377 | va_end(ap); 378 | return -1; 379 | } 380 | } 381 | 382 | /* hack to zero-terminate the result */ 383 | if (val) { 384 | char *xval = (char *)val - 1; 385 | memmove(xval, val, len); 386 | xval[len] = 0; 387 | val = xval; 388 | } 389 | } else { 390 | /* tuple was shorter than requested */ 391 | val = NULL; 392 | } 393 | 394 | switch (tupdesc[i]) { 395 | case 'i': 396 | int_p = va_arg(ap, int *); 397 | *int_p = val ? atoi(val) : 0; 398 | break; 399 | case 'q': 400 | long_p = va_arg(ap, uint64_t *); 401 | *long_p = val ? atoll(val) : 0; 402 | break; 403 | case 's': 404 | str_p = va_arg(ap, const char **); 405 | *str_p = val; 406 | break; 407 | default: 408 | fatal("bad tupdesc: %s", tupdesc); 409 | } 410 | } 411 | va_end(ap); 412 | 413 | return ncol; 414 | } 415 | -------------------------------------------------------------------------------- /src/stats.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "bouncer.h" 20 | 21 | static struct event ev_stats; 22 | static usec_t old_stamp, new_stamp; 23 | 24 | static void reset_stats(PgStats *stat) 25 | { 26 | stat->server_bytes = 0; 27 | stat->client_bytes = 0; 28 | stat->query_count = 0; 29 | stat->query_time = 0; 30 | stat->xact_count = 0; 31 | stat->xact_time = 0; 32 | stat->wait_time = 0; 33 | } 34 | 35 | static void stat_add(PgStats *total, PgStats *stat) 36 | { 37 | total->server_bytes += stat->server_bytes; 38 | total->client_bytes += stat->client_bytes; 39 | total->query_count += stat->query_count; 40 | total->query_time += stat->query_time; 41 | total->xact_count += stat->xact_count; 42 | total->xact_time += stat->xact_time; 43 | total->wait_time += stat->wait_time; 44 | } 45 | 46 | static void calc_average(PgStats *avg, PgStats *cur, PgStats *old) 47 | { 48 | uint64_t query_count; 49 | uint64_t xact_count; 50 | 51 | usec_t dur = get_cached_time() - old_stamp; 52 | 53 | reset_stats(avg); 54 | 55 | if (dur <= 0) 56 | return; 57 | 58 | query_count = cur->query_count - old->query_count; 59 | xact_count = cur->xact_count - old->xact_count; 60 | 61 | avg->query_count = USEC * query_count / dur; 62 | avg->xact_count = USEC * xact_count / dur; 63 | 64 | avg->client_bytes = USEC * (cur->client_bytes - old->client_bytes) / dur; 65 | avg->server_bytes = USEC * (cur->server_bytes - old->server_bytes) / dur; 66 | 67 | if (query_count > 0) 68 | avg->query_time = (cur->query_time - old->query_time) / query_count; 69 | 70 | if (xact_count > 0) 71 | avg->xact_time = (cur->xact_time - old->xact_time) / xact_count; 72 | 73 | avg->wait_time = USEC * (cur->wait_time - old->wait_time) / dur; 74 | } 75 | 76 | static void write_stats(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) 77 | { 78 | PgStats avg; 79 | calc_average(&avg, stat, old); 80 | pktbuf_write_DataRow(buf, "sqqqqqqqqqqqqqq", dbname, 81 | stat->xact_count, stat->query_count, 82 | stat->client_bytes, stat->server_bytes, 83 | stat->xact_time, stat->query_time, 84 | stat->wait_time, 85 | avg.xact_count, avg.query_count, 86 | avg.client_bytes, avg.server_bytes, 87 | avg.xact_time, avg.query_time, 88 | avg.wait_time); 89 | } 90 | 91 | bool admin_database_stats(PgSocket *client, struct StatList *pool_list) 92 | { 93 | PgPool *pool; 94 | struct List *item; 95 | PgDatabase *cur_db = NULL; 96 | PgStats st_total, st_db, old_db, old_total; 97 | int rows = 0; 98 | PktBuf *buf; 99 | 100 | reset_stats(&st_total); 101 | reset_stats(&st_db); 102 | reset_stats(&old_db); 103 | reset_stats(&old_total); 104 | 105 | buf = pktbuf_dynamic(512); 106 | if (!buf) { 107 | admin_error(client, "no mem"); 108 | return true; 109 | } 110 | 111 | pktbuf_write_RowDescription(buf, "sqqqqqqqqqqqqqq", "database", 112 | "total_xact_count", "total_query_count", 113 | "total_received", "total_sent", 114 | "total_xact_time", "total_query_time", 115 | "total_wait_time", 116 | "avg_xact_count", "avg_query_count", 117 | "avg_recv", "avg_sent", 118 | "avg_xact_time", "avg_query_time", 119 | "avg_wait_time"); 120 | statlist_for_each(item, pool_list) { 121 | pool = container_of(item, PgPool, head); 122 | 123 | if (!cur_db) 124 | cur_db = pool->db; 125 | 126 | if (pool->db != cur_db) { 127 | write_stats(buf, &st_db, &old_db, cur_db->name); 128 | 129 | rows ++; 130 | cur_db = pool->db; 131 | stat_add(&st_total, &st_db); 132 | stat_add(&old_total, &old_db); 133 | reset_stats(&st_db); 134 | reset_stats(&old_db); 135 | } 136 | 137 | stat_add(&st_db, &pool->stats); 138 | stat_add(&old_db, &pool->older_stats); 139 | } 140 | if (cur_db) { 141 | write_stats(buf, &st_db, &old_db, cur_db->name); 142 | stat_add(&st_total, &st_db); 143 | stat_add(&old_total, &old_db); 144 | rows ++; 145 | } 146 | admin_flush(client, buf, "SHOW"); 147 | 148 | return true; 149 | } 150 | 151 | static void write_stats_totals(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) 152 | { 153 | PgStats avg; 154 | calc_average(&avg, stat, old); 155 | pktbuf_write_DataRow(buf, "sqqqqqqq", dbname, 156 | stat->xact_count, stat->query_count, 157 | stat->client_bytes, stat->server_bytes, 158 | stat->xact_time, stat->query_time, 159 | stat->wait_time); 160 | } 161 | 162 | bool admin_database_stats_totals(PgSocket *client, struct StatList *pool_list) 163 | { 164 | PgPool *pool; 165 | struct List *item; 166 | PgDatabase *cur_db = NULL; 167 | PgStats st_total, st_db, old_db, old_total; 168 | int rows = 0; 169 | PktBuf *buf; 170 | 171 | reset_stats(&st_total); 172 | reset_stats(&st_db); 173 | reset_stats(&old_db); 174 | reset_stats(&old_total); 175 | 176 | buf = pktbuf_dynamic(512); 177 | if (!buf) { 178 | admin_error(client, "no mem"); 179 | return true; 180 | } 181 | 182 | pktbuf_write_RowDescription(buf, "sqqqqqqq", "database", 183 | "xact_count", "query_count", 184 | "bytes_received", "bytes_sent", 185 | "xact_time", "query_time", 186 | "wait_time"); 187 | statlist_for_each(item, pool_list) { 188 | pool = container_of(item, PgPool, head); 189 | 190 | if (!cur_db) 191 | cur_db = pool->db; 192 | 193 | if (pool->db != cur_db) { 194 | write_stats_totals(buf, &st_db, &old_db, cur_db->name); 195 | 196 | rows ++; 197 | cur_db = pool->db; 198 | stat_add(&st_total, &st_db); 199 | stat_add(&old_total, &old_db); 200 | reset_stats(&st_db); 201 | reset_stats(&old_db); 202 | } 203 | 204 | stat_add(&st_db, &pool->stats); 205 | stat_add(&old_db, &pool->older_stats); 206 | } 207 | if (cur_db) { 208 | write_stats_totals(buf, &st_db, &old_db, cur_db->name); 209 | stat_add(&st_total, &st_db); 210 | stat_add(&old_total, &old_db); 211 | rows ++; 212 | } 213 | admin_flush(client, buf, "SHOW"); 214 | 215 | return true; 216 | } 217 | 218 | static void write_stats_averages(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) 219 | { 220 | PgStats avg; 221 | calc_average(&avg, stat, old); 222 | pktbuf_write_DataRow(buf, "sqqqqqqq", dbname, 223 | avg.xact_count, avg.query_count, 224 | avg.client_bytes, avg.server_bytes, 225 | avg.xact_time, avg.query_time, 226 | avg.wait_time); 227 | } 228 | 229 | bool admin_database_stats_averages(PgSocket *client, struct StatList *pool_list) 230 | { 231 | PgPool *pool; 232 | struct List *item; 233 | PgDatabase *cur_db = NULL; 234 | PgStats st_total, st_db, old_db, old_total; 235 | int rows = 0; 236 | PktBuf *buf; 237 | 238 | reset_stats(&st_total); 239 | reset_stats(&st_db); 240 | reset_stats(&old_db); 241 | reset_stats(&old_total); 242 | 243 | buf = pktbuf_dynamic(512); 244 | if (!buf) { 245 | admin_error(client, "no mem"); 246 | return true; 247 | } 248 | 249 | pktbuf_write_RowDescription(buf, "sqqqqqqq", "database", 250 | "xact_count", "query_count", 251 | "bytes_received", "bytes_sent", 252 | "xact_time", "query_time", 253 | "wait_time"); 254 | statlist_for_each(item, pool_list) { 255 | pool = container_of(item, PgPool, head); 256 | 257 | if (!cur_db) 258 | cur_db = pool->db; 259 | 260 | if (pool->db != cur_db) { 261 | write_stats_averages(buf, &st_db, &old_db, cur_db->name); 262 | 263 | rows ++; 264 | cur_db = pool->db; 265 | stat_add(&st_total, &st_db); 266 | stat_add(&old_total, &old_db); 267 | reset_stats(&st_db); 268 | reset_stats(&old_db); 269 | } 270 | 271 | stat_add(&st_db, &pool->stats); 272 | stat_add(&old_db, &pool->older_stats); 273 | } 274 | if (cur_db) { 275 | write_stats_averages(buf, &st_db, &old_db, cur_db->name); 276 | stat_add(&st_total, &st_db); 277 | stat_add(&old_total, &old_db); 278 | rows ++; 279 | } 280 | admin_flush(client, buf, "SHOW"); 281 | 282 | return true; 283 | } 284 | 285 | bool show_stat_totals(PgSocket *client, struct StatList *pool_list) 286 | { 287 | PgPool *pool; 288 | struct List *item; 289 | PgStats st_total, old_total, avg; 290 | PktBuf *buf; 291 | 292 | reset_stats(&st_total); 293 | reset_stats(&old_total); 294 | 295 | buf = pktbuf_dynamic(512); 296 | if (!buf) { 297 | admin_error(client, "no mem"); 298 | return true; 299 | } 300 | 301 | 302 | statlist_for_each(item, pool_list) { 303 | pool = container_of(item, PgPool, head); 304 | stat_add(&st_total, &pool->stats); 305 | stat_add(&old_total, &pool->older_stats); 306 | } 307 | 308 | calc_average(&avg, &st_total, &old_total); 309 | 310 | pktbuf_write_RowDescription(buf, "sq", "name", "value"); 311 | 312 | #define WTOTAL(name) pktbuf_write_DataRow(buf, "sq", "total_" #name, st_total.name) 313 | #define WAVG(name) pktbuf_write_DataRow(buf, "sq", "avg_" #name, avg.name) 314 | 315 | WTOTAL(xact_count); 316 | WTOTAL(query_count); 317 | WTOTAL(client_bytes); 318 | WTOTAL(server_bytes); 319 | WTOTAL(xact_time); 320 | WTOTAL(query_time); 321 | WTOTAL(wait_time); 322 | WAVG(xact_count); 323 | WAVG(query_count); 324 | WAVG(client_bytes); 325 | WAVG(server_bytes); 326 | WAVG(xact_time); 327 | WAVG(query_time); 328 | WAVG(wait_time); 329 | 330 | admin_flush(client, buf, "SHOW"); 331 | return true; 332 | } 333 | 334 | static void refresh_stats(int s, short flags, void *arg) 335 | { 336 | struct List *item; 337 | PgPool *pool; 338 | struct timeval period = { cf_stats_period, 0 }; 339 | PgStats old_total, cur_total, avg; 340 | 341 | reset_stats(&old_total); 342 | reset_stats(&cur_total); 343 | 344 | old_stamp = new_stamp; 345 | new_stamp = get_cached_time(); 346 | 347 | statlist_for_each(item, &pool_list) { 348 | pool = container_of(item, PgPool, head); 349 | pool->older_stats = pool->newer_stats; 350 | pool->newer_stats = pool->stats; 351 | 352 | stat_add(&cur_total, &pool->stats); 353 | stat_add(&old_total, &pool->older_stats); 354 | } 355 | calc_average(&avg, &cur_total, &old_total); 356 | /* send totals to logfile */ 357 | log_info("Stats: %" PRIu64 " xacts/s," 358 | " %" PRIu64 " queries/s," 359 | " in %" PRIu64 " B/s," 360 | " out %" PRIu64 " B/s," 361 | " xact %" PRIu64 " us," 362 | " query %" PRIu64 " us" 363 | " wait time %" PRIu64 " us", 364 | avg.xact_count, avg.query_count, 365 | avg.client_bytes, avg.server_bytes, 366 | avg.xact_time, avg.query_time, 367 | avg.wait_time); 368 | 369 | safe_evtimer_add(&ev_stats, &period); 370 | } 371 | 372 | void stats_setup(void) 373 | { 374 | struct timeval period = { cf_stats_period, 0 }; 375 | 376 | new_stamp = get_cached_time(); 377 | old_stamp = new_stamp - USEC; 378 | 379 | /* launch stats */ 380 | evtimer_set(&ev_stats, refresh_stats, NULL); 381 | safe_evtimer_add(&ev_stats, &period); 382 | } 383 | -------------------------------------------------------------------------------- /src/system.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Compat functions for OSes where libc does not provide them. 21 | */ 22 | 23 | #include "bouncer.h" 24 | 25 | #ifdef HAVE_SYS_PARAM_H 26 | #include 27 | #endif 28 | #ifdef HAVE_UCRED_H 29 | #include 30 | #endif 31 | #ifdef HAVE_SYS_UCRED_H 32 | #include 33 | #endif 34 | #ifdef HAVE_PWD_H 35 | #include 36 | #endif 37 | #ifdef HAVE_GRP_H 38 | #include 39 | #endif 40 | 41 | void change_user(const char *user) 42 | { 43 | const struct passwd *pw; 44 | gid_t gset[1]; 45 | 46 | /* check for a valid username */ 47 | pw = getpwnam(user); 48 | if (pw == NULL) 49 | fatal("could not find user '%s' to switch to", user); 50 | 51 | gset[0] = pw->pw_gid; 52 | if (getuid() == 0) { 53 | if (setgroups(1, gset) < 0) 54 | fatal_perror("failed to reset groups"); 55 | } 56 | 57 | if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) 58 | fatal_perror("failed to assume identity of user '%s'", user); 59 | 60 | if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) 61 | fatal("setuid() failed to work"); 62 | } 63 | 64 | /* set permissions & mode for file */ 65 | void change_file_mode(const char *fn, mode_t mode, 66 | const char *user_name, 67 | const char *group_name) 68 | { 69 | int res; 70 | uid_t uid = -1; 71 | gid_t gid = -1; 72 | unsigned long val; 73 | char *end; 74 | 75 | /* user lookup */ 76 | if (user_name && user_name[0]) { 77 | const struct passwd *pw; 78 | 79 | val = strtoul(user_name, &end, 0); 80 | if (*end == 0) { 81 | uid = val; 82 | } else { 83 | /* check for a valid username */ 84 | pw = getpwnam(user_name); 85 | if (!pw) 86 | fatal("could not find user '%s': %s", 87 | user_name, strerror(errno)); 88 | uid = pw->pw_uid; 89 | } 90 | } 91 | 92 | /* group lookup */ 93 | if (group_name && group_name[0]) { 94 | struct group *gr; 95 | 96 | val = strtoul(group_name, &end, 0); 97 | if (*end == 0) { 98 | gid = val; 99 | } else { 100 | gr = getgrnam(group_name); 101 | if (!gr) 102 | fatal("could not find group '%s': %s", 103 | group_name, strerror(errno)); 104 | gid = gr->gr_gid; 105 | } 106 | } 107 | 108 | /* change user/group */ 109 | if (uid != (uid_t)-1 || gid != (gid_t)-1) { 110 | res = chown(fn, uid, gid); 111 | if (res != 0) { 112 | fatal("chown(%s, %d, %d) failed: %s", 113 | fn, uid, gid, strerror(errno)); 114 | } 115 | } 116 | 117 | /* change mode */ 118 | res = chmod(fn, mode); 119 | if (res != 0) { 120 | fatal("Failure to chmod(%s, 0%o): %s", 121 | fn, mode, strerror(errno)); 122 | } 123 | } 124 | 125 | /* 126 | * UNIX socket helper. 127 | */ 128 | 129 | bool check_unix_peer_name(int fd, const char *username) 130 | { 131 | int res; 132 | uid_t peer_uid = -1; 133 | gid_t peer_gid = -1; 134 | struct passwd *pw; 135 | 136 | res = getpeereid(fd, &peer_uid, &peer_gid); 137 | if (res < 0) 138 | return false; 139 | pw = getpwuid(peer_uid); 140 | if (!pw) 141 | return false; 142 | return strcmp(pw->pw_name, username) == 0; 143 | } 144 | -------------------------------------------------------------------------------- /src/takeover.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Connect to running bouncer process, load fds from it, shut it down 21 | * and continue with them. 22 | * 23 | * Each row from SHOW FDS will have corresponding fd in ancillary message. 24 | * 25 | * Manpages: unix, sendmsg, recvmsg, cmsg, readv 26 | */ 27 | 28 | #include "bouncer.h" 29 | 30 | /* 31 | * Takeover done, old process shut down, 32 | * kick this one running. 33 | */ 34 | 35 | static PgSocket *old_bouncer = NULL; 36 | 37 | void takeover_finish(void) 38 | { 39 | uint8_t buf[512]; 40 | int fd = sbuf_socket(&old_bouncer->sbuf); 41 | bool res; 42 | int got; 43 | 44 | log_info("sending SHUTDOWN;"); 45 | socket_set_nonblocking(fd, 0); 46 | SEND_generic(res, old_bouncer, 'Q', "s", "SHUTDOWN;"); 47 | if (!res) 48 | fatal("failed to send SHUTDOWN;"); 49 | 50 | while (1) { 51 | got = safe_recv(fd, buf, sizeof(buf), 0); 52 | if (got == 0) 53 | break; 54 | if (got < 0) 55 | fatal_perror("sky is falling - error while waiting result from SHUTDOWN"); 56 | } 57 | 58 | disconnect_server(old_bouncer, false, "disko over"); 59 | old_bouncer = NULL; 60 | 61 | if (cf_pidfile && cf_pidfile[0]) { 62 | log_info("waiting for old pidfile to go away"); 63 | while (1) { 64 | struct stat st; 65 | if (stat(cf_pidfile, &st) < 0) { 66 | if (errno == ENOENT) 67 | break; 68 | } 69 | usleep(USEC/10); 70 | } 71 | } 72 | 73 | log_info("old process killed, resuming work"); 74 | resume_all(); 75 | } 76 | 77 | static void takeover_finish_part1(PgSocket *bouncer) 78 | { 79 | Assert(old_bouncer == NULL); 80 | 81 | /* unregister bouncer from libevent */ 82 | if (!sbuf_pause(&bouncer->sbuf)) 83 | fatal_perror("sbuf_pause failed"); 84 | old_bouncer = bouncer; 85 | cf_reboot = 0; 86 | log_info("disko over, going background"); 87 | } 88 | 89 | /* parse msg for fd and info */ 90 | static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg) 91 | { 92 | int fd; 93 | char *task, *saddr, *user, *db; 94 | char *client_enc, *std_string, *datestyle, *timezone, *password; 95 | int oldfd, port, linkfd; 96 | int got; 97 | uint64_t ckey; 98 | PgAddr addr; 99 | bool res = false; 100 | 101 | memset(&addr, 0, sizeof(addr)); 102 | 103 | if (cmsg->cmsg_level == SOL_SOCKET 104 | && cmsg->cmsg_type == SCM_RIGHTS 105 | && cmsg->cmsg_len >= CMSG_LEN(sizeof(int))) 106 | { 107 | /* get the fd */ 108 | memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); 109 | log_debug("got fd: %d", fd); 110 | } else { 111 | fatal("broken fd packet"); 112 | } 113 | 114 | /* parse row contents */ 115 | got = scan_text_result(pkt, "issssiqisssss", &oldfd, &task, &user, &db, 116 | &saddr, &port, &ckey, &linkfd, 117 | &client_enc, &std_string, &datestyle, &timezone, 118 | &password); 119 | if (got < 0 || task == NULL || saddr == NULL) 120 | fatal("NULL data from old process"); 121 | 122 | log_debug("FD row: fd=%d(%d) linkfd=%d task=%s user=%s db=%s enc=%s", 123 | oldfd, fd, linkfd, task, 124 | user ? user : "NULL", db ? db : "NULL", 125 | client_enc ? client_enc : "NULL"); 126 | 127 | if (!password) 128 | password = ""; 129 | 130 | /* fill address */ 131 | if (strcmp(saddr, "unix") == 0) { 132 | pga_set(&addr, AF_UNIX, cf_listen_port); 133 | } else { 134 | if (!pga_pton(&addr, saddr, port)) 135 | fatal("failed to convert address: %s", saddr); 136 | } 137 | 138 | /* decide what to do with it */ 139 | if (strcmp(task, "client") == 0) { 140 | res = use_client_socket(fd, &addr, db, user, ckey, oldfd, linkfd, 141 | client_enc, std_string, datestyle, timezone, 142 | password); 143 | } else if (strcmp(task, "server") == 0) { 144 | res = use_server_socket(fd, &addr, db, user, ckey, oldfd, linkfd, 145 | client_enc, std_string, datestyle, timezone, 146 | password); 147 | } else if (strcmp(task, "pooler") == 0) { 148 | res = use_pooler_socket(fd, pga_is_unix(&addr)); 149 | } else { 150 | fatal("unknown task: %s", task); 151 | } 152 | 153 | if (!res) 154 | fatal("socket takeover failed - no mem?"); 155 | } 156 | 157 | static void takeover_create_link(PgPool *pool, PgSocket *client) 158 | { 159 | struct List *item; 160 | PgSocket *server; 161 | 162 | statlist_for_each(item, &pool->active_server_list) { 163 | server = container_of(item, PgSocket, head); 164 | if (server->tmp_sk_oldfd == client->tmp_sk_linkfd) { 165 | server->link = client; 166 | client->link = server; 167 | return; 168 | } 169 | } 170 | fatal("takeover_create_link: failed to find pair"); 171 | } 172 | 173 | /* clean the inappropriate places the old fds got stored in */ 174 | static void takeover_clean_socket_list(struct StatList *list) 175 | { 176 | struct List *item; 177 | PgSocket *sk; 178 | statlist_for_each(item, list) { 179 | sk = container_of(item, PgSocket, head); 180 | if (sk->suspended) { 181 | sk->tmp_sk_oldfd = get_cached_time(); 182 | sk->tmp_sk_linkfd = get_cached_time(); 183 | } 184 | } 185 | } 186 | 187 | /* all fds loaded, create links */ 188 | static void takeover_postprocess_fds(void) 189 | { 190 | struct List *item, *item2; 191 | PgSocket *client; 192 | PgPool *pool; 193 | 194 | statlist_for_each(item, &pool_list) { 195 | pool = container_of(item, PgPool, head); 196 | if (pool->db->admin) 197 | continue; 198 | statlist_for_each(item2, &pool->active_client_list) { 199 | client = container_of(item2, PgSocket, head); 200 | if (client->suspended && client->tmp_sk_linkfd) 201 | takeover_create_link(pool, client); 202 | } 203 | } 204 | statlist_for_each(item, &pool_list) { 205 | pool = container_of(item, PgPool, head); 206 | takeover_clean_socket_list(&pool->active_client_list); 207 | takeover_clean_socket_list(&pool->active_server_list); 208 | takeover_clean_socket_list(&pool->idle_server_list); 209 | } 210 | } 211 | 212 | static void next_command(PgSocket *bouncer, struct MBuf *pkt) 213 | { 214 | bool res = true; 215 | const char *cmd; 216 | 217 | if (!mbuf_get_string(pkt, &cmd)) 218 | fatal("bad result pkt"); 219 | 220 | log_debug("takeover_recv_fds: 'C' body: %s", cmd); 221 | if (strcmp(cmd, "SUSPEND") == 0) { 222 | log_info("SUSPEND finished, sending SHOW FDS"); 223 | SEND_generic(res, bouncer, 'Q', "s", "SHOW FDS;"); 224 | } else if (strncmp(cmd, "SHOW", 4) == 0) { 225 | /* all fds loaded, review them */ 226 | takeover_postprocess_fds(); 227 | log_info("SHOW FDS finished"); 228 | 229 | takeover_finish_part1(bouncer); 230 | } else { 231 | fatal("got bad CMD from old bouncer: %s", cmd); 232 | } 233 | 234 | if (!res) 235 | fatal("command send failed"); 236 | } 237 | 238 | static void takeover_parse_data(PgSocket *bouncer, 239 | struct msghdr *msg, struct MBuf *data) 240 | { 241 | struct cmsghdr *cmsg; 242 | PktHdr pkt; 243 | 244 | cmsg = msg->msg_controllen ? CMSG_FIRSTHDR(msg) : NULL; 245 | 246 | while (mbuf_avail_for_read(data) > 0) { 247 | if (!get_header(data, &pkt)) 248 | fatal("cannot parse packet"); 249 | 250 | /* 251 | * There should not be partial reads from UNIX socket. 252 | */ 253 | if (incomplete_pkt(&pkt)) 254 | fatal("unexpected partial packet"); 255 | 256 | switch (pkt.type) { 257 | case 'T': /* RowDescription */ 258 | log_debug("takeover_parse_data: 'T'"); 259 | break; 260 | case 'D': /* DataRow */ 261 | log_debug("takeover_parse_data: 'D'"); 262 | if (cmsg) { 263 | takeover_load_fd(&pkt.data, cmsg); 264 | cmsg = CMSG_NXTHDR(msg, cmsg); 265 | } else 266 | fatal("got row without fd info"); 267 | break; 268 | case 'Z': /* ReadyForQuery */ 269 | log_debug("takeover_parse_data: 'Z'"); 270 | break; 271 | case 'C': /* CommandComplete */ 272 | log_debug("takeover_parse_data: 'C'"); 273 | next_command(bouncer, &pkt.data); 274 | break; 275 | case 'E': /* ErrorMessage */ 276 | log_server_error("old bouncer sent", &pkt); 277 | fatal("something failed"); 278 | default: 279 | fatal("takeover_parse_data: unexpected pkt: '%c'", pkt_desc(&pkt)); 280 | } 281 | } 282 | } 283 | 284 | /* 285 | * listen for data from old bouncer. 286 | * 287 | * use always recvmsg, to keep code simpler 288 | */ 289 | static void takeover_recv_cb(int sock, short flags, void *arg) 290 | { 291 | PgSocket *bouncer = container_of(arg, PgSocket, sbuf); 292 | uint8_t data_buf[STARTUP_BUF * 2]; 293 | uint8_t cnt_buf[128]; 294 | struct msghdr msg; 295 | struct iovec io; 296 | int res; 297 | struct MBuf data; 298 | 299 | memset(&msg, 0, sizeof(msg)); 300 | io.iov_base = data_buf; 301 | io.iov_len = sizeof(data_buf); 302 | msg.msg_iov = &io; 303 | msg.msg_iovlen = 1; 304 | msg.msg_control = cnt_buf; 305 | msg.msg_controllen = sizeof(cnt_buf); 306 | 307 | res = safe_recvmsg(sock, &msg, 0); 308 | if (res > 0) { 309 | mbuf_init_fixed_reader(&data, data_buf, res); 310 | takeover_parse_data(bouncer, &msg, &data); 311 | } else if (res == 0) { 312 | fatal("unexpected EOF"); 313 | } else { 314 | if (errno == EAGAIN) 315 | return; 316 | fatal_perror("safe_recvmsg"); 317 | } 318 | } 319 | 320 | /* 321 | * login finished, send first command, 322 | * replace recv callback with custom recvmsg() based one. 323 | */ 324 | bool takeover_login(PgSocket *bouncer) 325 | { 326 | bool res; 327 | 328 | slog_info(bouncer, "Login OK, sending SUSPEND"); 329 | SEND_generic(res, bouncer, 'Q', "s", "SUSPEND;"); 330 | if (res) { 331 | /* use own callback */ 332 | if (!sbuf_pause(&bouncer->sbuf)) 333 | fatal("sbuf_pause failed"); 334 | res = sbuf_continue_with_callback(&bouncer->sbuf, takeover_recv_cb); 335 | if (!res) 336 | fatal("takeover_login: sbuf_continue_with_callback failed"); 337 | } else { 338 | fatal("takeover_login: failed to send command"); 339 | } 340 | return res; 341 | } 342 | 343 | /* launch connection to running process */ 344 | void takeover_init(void) 345 | { 346 | PgDatabase *db = find_database("pgbouncer"); 347 | PgPool *pool = get_pool(db, db->forced_user); 348 | 349 | if (!pool) 350 | fatal("no admin pool?"); 351 | 352 | log_info("takeover_init: launching connection"); 353 | launch_new_connection(pool); 354 | } 355 | 356 | void takeover_login_failed(void) 357 | { 358 | fatal("login failed"); 359 | } 360 | -------------------------------------------------------------------------------- /src/varcache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PgBouncer - Lightweight connection pooler for PostgreSQL. 3 | * 4 | * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Operations with server config parameters. 21 | */ 22 | 23 | #include "bouncer.h" 24 | 25 | #include 26 | 27 | struct var_lookup { 28 | const char *name; 29 | enum VarCacheIdx idx; 30 | }; 31 | 32 | static const struct var_lookup lookup [] = { 33 | {"client_encoding", VClientEncoding }, 34 | {"DateStyle", VDateStyle }, 35 | {"TimeZone", VTimeZone }, 36 | {"standard_conforming_strings", VStdStr }, 37 | {"application_name", VAppName }, 38 | {NULL}, 39 | }; 40 | 41 | static struct StrPool *vpool; 42 | 43 | static inline struct PStr *get_value(VarCache *cache, const struct var_lookup *lk) 44 | { 45 | return cache->var_list[lk->idx]; 46 | } 47 | 48 | bool varcache_set(VarCache *cache, const char *key, const char *value) 49 | { 50 | const struct var_lookup *lk; 51 | struct PStr *pstr = NULL; 52 | 53 | if (!vpool) { 54 | vpool = strpool_create(USUAL_ALLOC); 55 | if (!vpool) 56 | return false; 57 | } 58 | 59 | for (lk = lookup; lk->name; lk++) { 60 | if (strcasecmp(lk->name, key) == 0) 61 | goto set_value; 62 | } 63 | return false; 64 | 65 | set_value: 66 | /* drop old value */ 67 | strpool_decref(cache->var_list[lk->idx]); 68 | cache->var_list[lk->idx] = NULL; 69 | 70 | /* NULL value? */ 71 | if (!value) 72 | return false; 73 | 74 | /* set new value */ 75 | pstr = strpool_get(vpool, value, strlen(value)); 76 | if (!pstr) 77 | return false; 78 | cache->var_list[lk->idx] = pstr; 79 | return true; 80 | } 81 | 82 | static int apply_var(PktBuf *pkt, const char *key, 83 | const struct PStr *cval, 84 | const struct PStr *sval) 85 | { 86 | char buf[128]; 87 | char qbuf[128]; 88 | unsigned len; 89 | 90 | /* if unset, skip */ 91 | if (!cval || !sval || !*cval->str) 92 | return 0; 93 | 94 | /* if equal, skip */ 95 | if (cval == sval) 96 | return 0; 97 | 98 | /* ignore case difference */ 99 | if (strcasecmp(cval->str, sval->str) == 0) 100 | return 0; 101 | 102 | /* the string may have been taken from startup pkt */ 103 | if (!pg_quote_literal(qbuf, cval->str, sizeof(qbuf))) 104 | return 0; 105 | 106 | /* add SET statement to packet */ 107 | len = snprintf(buf, sizeof(buf), "SET %s=%s;", key, qbuf); 108 | if (len < sizeof(buf)) { 109 | pktbuf_put_bytes(pkt, buf, len); 110 | return 1; 111 | } else { 112 | log_warning("got too long value, skipping"); 113 | return 0; 114 | } 115 | } 116 | 117 | bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p) 118 | { 119 | int changes = 0; 120 | struct PStr *cval, *sval; 121 | const struct var_lookup *lk; 122 | int sql_ofs; 123 | struct PktBuf *pkt = pktbuf_temp(); 124 | 125 | pktbuf_start_packet(pkt, 'Q'); 126 | 127 | /* grab query position inside pkt */ 128 | sql_ofs = pktbuf_written(pkt); 129 | 130 | for (lk = lookup; lk->name; lk++) { 131 | sval = get_value(&server->vars, lk); 132 | cval = get_value(&client->vars, lk); 133 | changes += apply_var(pkt, lk->name, cval, sval); 134 | } 135 | *changes_p = changes > 0; 136 | if (!changes) 137 | return true; 138 | 139 | pktbuf_put_char(pkt, 0); 140 | pktbuf_finish_packet(pkt); 141 | 142 | slog_debug(server, "varcache_apply: %s", pkt->buf + sql_ofs); 143 | return pktbuf_send_immediate(pkt, server); 144 | } 145 | 146 | void varcache_fill_unset(VarCache *src, PgSocket *dst) 147 | { 148 | struct PStr *srcval, *dstval; 149 | const struct var_lookup *lk; 150 | for (lk = lookup; lk->name; lk++) { 151 | srcval = src->var_list[lk->idx]; 152 | dstval = dst->vars.var_list[lk->idx]; 153 | if (!dstval) { 154 | strpool_incref(srcval); 155 | dst->vars.var_list[lk->idx] = srcval; 156 | } 157 | } 158 | } 159 | 160 | void varcache_clean(VarCache *cache) 161 | { 162 | int i; 163 | for (i = 0; i < NumVars; i++) { 164 | strpool_decref(cache->var_list[i]); 165 | cache->var_list[i] = NULL; 166 | } 167 | } 168 | 169 | void varcache_add_params(PktBuf *pkt, VarCache *vars) 170 | { 171 | struct PStr *val; 172 | const struct var_lookup *lk; 173 | for (lk = lookup; lk->name; lk++) { 174 | val = vars->var_list[lk->idx]; 175 | if (val) 176 | pktbuf_write_ParameterStatus(pkt, lk->name, val->str); 177 | } 178 | } 179 | 180 | void varcache_deinit(void) 181 | { 182 | strpool_free(vpool); 183 | vpool = NULL; 184 | } 185 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | PG_CPPFLAGS = -I$(shell pg_config --includedir) 3 | PG_LIBS = -lpq 4 | PG_LDFLAGS = -L$(shell pg_config --libdir) 5 | 6 | USUAL_DIR = ../lib 7 | 8 | SUBLOC = test 9 | DIST_SUBDIRS = ssl 10 | 11 | EXTRA_DIST = conntest.sh ctest6000.ini ctest7000.ini run-conntest.sh \ 12 | hba_test.eval hba_test.rules Makefile \ 13 | test.ini test.sh stress.py userlist.txt 14 | 15 | noinst_PROGRAMS = hba_test 16 | hba_test_CPPFLAGS = -I../include 17 | hba_test_CFLAGS = -O0 18 | hba_test_SOURCES = hba_test.c ../src/hba.c ../src/util.c 19 | hba_test_EMBED_LIBUSUAL = 1 20 | 21 | EXTRA_PROGRAMS = asynctest 22 | asynctest_CPPFLAGS = -I../include $(PG_CPPFLAGS) 23 | asynctest_LDFLAGS = $(PG_LDFLAGS) 24 | asynctest_LDADD = $(PG_LIBS) 25 | asynctest_SOURCES = asynctest.c 26 | asynctest_EMBED_LIBUSUAL = 1 27 | 28 | AM_FEATURES = libusual 29 | 30 | 31 | include ../config.mak 32 | include ../lib/mk/antimake.mk 33 | 34 | all: run_test 35 | 36 | run_test: hba_test 37 | ./hba_test 38 | -------------------------------------------------------------------------------- /test/conntest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | fw_drop_port() { 4 | echo "fw_drop_port" 5 | case `uname` in 6 | Linux) 7 | sudo iptables -A OUTPUT -p tcp --dport $1 -j DROP;; 8 | Darwin) 9 | sudo ipfw add 100 drop tcp from any to 127.0.0.1 dst-port $1;; 10 | *) 11 | echo "Unknown OS";; 12 | esac 13 | } 14 | fw_reject_port() { 15 | echo "fw_reject_port" 16 | case `uname` in 17 | Linux) 18 | sudo iptables -A OUTPUT -p tcp --dport $1 -j REJECT --reject-with tcp-reset;; 19 | Darwin) 20 | sudo ipfw add 100 reset tcp from any to 127.0.0.1 dst-port $1;; 21 | *) 22 | echo "Unknown OS";; 23 | esac 24 | } 25 | 26 | fw_reset() { 27 | echo "fw_reset" 28 | case `uname` in 29 | Linux) 30 | sudo iptables -F;; 31 | Darwin) 32 | sudo ipfw del 100;; 33 | *) 34 | echo "Unknown OS"; exit 1;; 35 | esac 36 | } 37 | 38 | port=5432 39 | port=7000 40 | 41 | fw_reset 42 | 43 | while true; do 44 | fw_drop_port $port 45 | sleep 12 46 | fw_reset 47 | sleep 12 48 | fw_reject_port $port 49 | sleep 3 50 | fw_reset 51 | sleep 6 52 | done 53 | -------------------------------------------------------------------------------- /test/ctest6000.ini: -------------------------------------------------------------------------------- 1 | ;; database name = connect string 2 | [databases] 3 | 4 | ; redirect bardb to bazdb on localhost 5 | conntest = host=127.0.0.1 port=7000 dbname=conntest 6 | 7 | ;; Configuation section 8 | [pgbouncer] 9 | 10 | logfile = ctest6000.log 11 | pidfile = ctest6000.pid 12 | 13 | listen_addr = 127.0.0.1 14 | listen_port = 6000 15 | unix_socket_dir = /tmp 16 | 17 | auth_type = md5 18 | auth_file = userlist.txt 19 | 20 | admin_users = marko 21 | stats_users = stats 22 | 23 | pool_mode = transaction 24 | 25 | server_reset_query = reset all 26 | server_check_query = select 1 27 | server_check_delay = 2 28 | 29 | max_client_conn = 1000 30 | default_pool_size = 20 31 | 32 | log_connections = 0 33 | log_disconnections = 0 34 | log_pooler_errors = 0 35 | 36 | server_lifetime = 30 37 | server_idle_timeout = 3 38 | server_connect_timeout = 2 39 | server_login_retry = 5 40 | query_timeout = 5 41 | 42 | client_idle_timeout = 10 43 | client_login_timeout = 50 44 | -------------------------------------------------------------------------------- /test/ctest7000.ini: -------------------------------------------------------------------------------- 1 | ;; database name = connect string 2 | [databases] 3 | 4 | ; redirect bardb to bazdb on localhost 5 | conntest = host=127.0.0.1 port=5432 dbname=marko password=kama 6 | 7 | ;; Configuation section 8 | [pgbouncer] 9 | 10 | logfile = ctest7000.log 11 | pidfile = ctest7000.pid 12 | 13 | listen_addr = 127.0.0.1 14 | listen_port = 7000 15 | unix_socket_dir = /tmp 16 | 17 | auth_type = md5 18 | auth_file = userlist.txt 19 | 20 | admin_users = marko 21 | stats_users = stats 22 | 23 | pool_mode = transaction 24 | 25 | server_reset_query = reset all 26 | server_check_query = select 1 27 | server_check_delay = 2 28 | 29 | max_client_conn = 5000 30 | default_pool_size = 20 31 | 32 | log_connections = 0 33 | log_disconnections = 0 34 | log_pooler_errors = 0 35 | 36 | server_lifetime = 30 37 | server_idle_timeout = 3 38 | server_connect_timeout = 2 39 | server_login_retry = 5 40 | query_timeout = 5 41 | 42 | client_idle_timeout = 10 43 | client_login_timeout = 50 44 | -------------------------------------------------------------------------------- /test/hba_test.c: -------------------------------------------------------------------------------- 1 | 2 | #include "bouncer.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int cf_tcp_keepcnt; 14 | int cf_tcp_keepintvl; 15 | int cf_tcp_keepidle; 16 | int cf_tcp_keepalive; 17 | int cf_tcp_socket_buffer; 18 | int cf_listen_port; 19 | 20 | static const char *method2string[] = { 21 | "trust", 22 | "x1", 23 | "x2", 24 | "password", 25 | "crypt", 26 | "md5", 27 | "creds", 28 | "cert", 29 | "peer", 30 | "hba", 31 | "reject", 32 | }; 33 | 34 | static char *get_token(char **ln_p) 35 | { 36 | char *ln = *ln_p, *tok, *end; 37 | 38 | while (*ln && *ln == '\t') ln++; 39 | tok = ln; 40 | while (*ln && *ln != '\t') ln++; 41 | end = ln; 42 | while (*ln && *ln == '\t') ln++; 43 | 44 | *ln_p = ln; 45 | if (tok == end) 46 | return NULL; 47 | *end = 0; 48 | return tok; 49 | } 50 | 51 | static int hba_test_eval(struct HBA *hba, char *ln, int linenr) 52 | { 53 | const char *addr=NULL, *user=NULL, *db=NULL, *tls=NULL, *exp=NULL; 54 | PgAddr pgaddr; 55 | int res; 56 | 57 | if (ln[0] == '#') 58 | return 0; 59 | exp = get_token(&ln); 60 | db = get_token(&ln); 61 | user = get_token(&ln); 62 | addr = get_token(&ln); 63 | tls = get_token(&ln); 64 | if (!exp) 65 | return 0; 66 | if (!db || !user) 67 | die("hbatest: invalid line #%d", linenr); 68 | 69 | if (!pga_pton(&pgaddr, addr, 9999)) 70 | die("hbatest: invalid addr on line #%d", linenr); 71 | 72 | res = hba_eval(hba, &pgaddr, !!tls, db, user); 73 | if (strcmp(method2string[res], exp) == 0) { 74 | res = 0; 75 | } else { 76 | log_warning("FAIL on line %d: expected '%s' got '%s' - user=%s db=%s addr=%s", 77 | linenr, exp, method2string[res], user, db, addr); 78 | res = 1; 79 | } 80 | return res; 81 | } 82 | 83 | static void hba_test(void) 84 | { 85 | struct HBA *hba; 86 | FILE *f; 87 | char *ln = NULL; 88 | size_t lnbuf = 0; 89 | ssize_t len; 90 | int linenr; 91 | int nfailed = 0; 92 | 93 | hba = hba_load_rules("hba_test.rules"); 94 | if (!hba) 95 | die("hbatest: did not find config"); 96 | 97 | f = fopen("hba_test.eval", "r"); 98 | if (!f) 99 | die("hbatest: cannot open eval"); 100 | 101 | for (linenr = 1; ; linenr++) { 102 | len = getline(&ln, &lnbuf, f); 103 | if (len < 0) 104 | break; 105 | if (len && ln[len-1] == '\n') 106 | ln[len-1] = 0; 107 | nfailed += hba_test_eval(hba, ln, linenr); 108 | } 109 | free(ln); 110 | fclose(f); 111 | hba_free(hba); 112 | if (nfailed) 113 | errx(1, "HBA test failures: %d", nfailed); 114 | else 115 | printf("HBA test OK\n"); 116 | } 117 | 118 | int main(void) 119 | { 120 | hba_test(); 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /test/hba_test.eval: -------------------------------------------------------------------------------- 1 | # peer 2 | md5 db user unix 3 | peer dbp user unix 4 | password db userp unix 5 | trust dbz userz unix 6 | 7 | # hostssl 8 | cert db user 10.1.1.1 tls 9 | reject db user 10.1.1.1 10 | reject db user 13.1.1.1 11 | 12 | # hostnossl 13 | reject db user 11.1.1.1 tls 14 | md5 db user 11.1.1.1 15 | reject db user 13.1.1.1 16 | 17 | # host 18 | password db user 127.0.0.2 tls 19 | password db user 127.0.0.3 20 | reject db user 127.0.1.4 21 | 22 | # db1 filt 23 | reject db1x user 127.0.1.4 24 | md5 db1 user 127.0.1.4 25 | 26 | # user1 filt 27 | md5 db1z user1 15.0.0.1 28 | reject db1z user2 15.0.0.1 29 | 30 | # someusers 31 | reject db2 user 16.0.0.1 32 | md5 db2 user1 16.0.0.1 33 | md5 db2 user2 16.0.0.1 34 | md5 db2 user3 16.0.0.1 35 | reject db2 user4 16.0.0.1 36 | 37 | # manyusers 38 | md5 db2 u1 17.0.0.1 39 | md5 db2 u2 17.0.0.1 40 | md5 db2 u3 17.0.0.1 41 | md5 db2 u4 17.0.0.1 42 | md5 db2 u5 17.0.0.1 43 | md5 db2 u6 17.0.0.1 44 | md5 db2 u7 17.0.0.1 45 | md5 db2 u8 17.0.0.1 46 | md5 db2 u9 17.0.0.1 47 | md5 db2 u10 17.0.0.1 48 | md5 db2 u11 17.0.0.1 49 | 50 | # manydbs 51 | reject d1 user 18.0.0.2 52 | trust d1 t18user 18.0.0.2 53 | trust d2 t18user 18.0.0.2 54 | trust d3 t18user 18.0.0.2 55 | trust d4 t18user 18.0.0.2 56 | trust d5 t18user 18.0.0.2 57 | trust d6 t18user 18.0.0.2 58 | trust d7 t18user 18.0.0.2 59 | trust d8 t18user 18.0.0.2 60 | trust d9 t18user 18.0.0.2 61 | trust d10 t18user 18.0.0.2 62 | trust d11 t18user 18.0.0.2 63 | 64 | # quoting 65 | reject db t19user 19.0.0.2 66 | cert all all 19.0.0.2 67 | cert q1"q2 a , b 19.0.0.2 68 | 69 | # bitmask 70 | cert mdb muser 199.199.199.199 71 | reject mdb muser 199.199.199.198 72 | reject mdb muser 199.199.199.200 73 | 74 | cert mdb2 muser 254.1.1.1 75 | 76 | # ipv6 77 | md5 mdb muser ff11:2::1 78 | md5 mdb muser ff22:3::1 79 | trust mdb muser ::1 80 | reject mdb muser ::2 81 | -------------------------------------------------------------------------------- /test/hba_test.rules: -------------------------------------------------------------------------------- 1 | # METHOD: trust, reject, md5, password, peer, cert 2 | 3 | # local DATABASE USER METHOD [OPTIONS] 4 | # host DATABASE USER ADDRESS METHOD [OPTIONS] 5 | # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] 6 | # hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] 7 | 8 | # ws 9 | 10 | # z 11 | 12 | # testing 13 | 14 | local dbp all peer 15 | local all userp password 16 | local dbz userz trust 17 | local all all md5 18 | 19 | hostssl all all 10.0.0.0/8 cert 20 | hostnossl all all 11.0.0.0/8 md5 21 | host all all 127.0.0.0 255.255.255.0 password 22 | 23 | host db1 all 0.0.0.0/0 md5 24 | host all user1 15.0.0.0/8 md5 25 | 26 | host tmp1,all user1,user2 , user3 16.0.0.0/8 md5 27 | host tmp2,all u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u2 17.0.0.0/8 md5 28 | host d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11 t18user 18.0.0.0/8 trust # comment 29 | 30 | host "all" "all" 19.0.0.0/8 cert 31 | host "q1""q2" "a , b" 19.0.0.0/8 cert 32 | 33 | # mask 34 | host mdb muser 199.199.199.199/32 cert 35 | 36 | host mdb2 muser 128.0.0.0/9 trust 37 | host mdb2 muser 128.0.0.0/8 md5 38 | host mdb2 muser 128.0.0.0/7 cert 39 | host mdb2 muser 128.0.0.0/6 password 40 | host mdb2 muser 128.0.0.0/5 cert 41 | host mdb2 muser 128.0.0.0/4 trust 42 | host mdb2 muser 128.0.0.0/3 md5 43 | host mdb2 muser 128.0.0.0/2 password 44 | host mdb2 muser 128.0.0.0/1 cert 45 | 46 | # ipv6 47 | host mdb muser ff11::0/16 md5 48 | host mdb muser ff20::/12 md5 49 | host mdb muser ::1/128 trust 50 | -------------------------------------------------------------------------------- /test/run-conntest.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | createdb conntest 4 | 5 | ./pgbouncer -d ctest6000.ini 6 | ./pgbouncer -d ctest7000.ini 7 | 8 | ./asynctest 9 | 10 | # now run conntest.sh on another console 11 | -------------------------------------------------------------------------------- /test/ssl/Makefile: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = lib.sh newca.sh newsite.sh test.ini test.sh Makefile \ 2 | TestCA1/ca.crt TestCA1/ca.key TestCA1/config.ini \ 3 | TestCA1/index.txt TestCA1/serial \ 4 | TestCA1/certs/01.pem TestCA1/certs/02.pem TestCA1/certs/03.pem \ 5 | TestCA1/sites/01-localhost.crt TestCA1/sites/01-localhost.key TestCA1/sites/01-localhost.csr \ 6 | TestCA1/sites/02-bouncer.crt TestCA1/sites/02-bouncer.key TestCA1/sites/02-bouncer.csr \ 7 | TestCA1/sites/03-random.crt TestCA1/sites/03-random.key TestCA1/sites/03-random.csr 8 | 9 | SUBLOC = test/ssl 10 | 11 | include ../../config.mak 12 | include ../../lib/mk/antimake.mk 13 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBSDCB8QIJALlVrWqKaqriMAkGByqGSM49BAEwLjELMAkGA1UEBhMCUVExDTAL 3 | BgNVBAoTBE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcN 4 | NDMxMTE1MTg1MTI5WjAuMQswCQYDVQQGEwJRUTENMAsGA1UEChMET3JnMTEQMA4G 5 | A1UEAxMHVGVzdENBMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIaZ2BgFEJDH 6 | nSROqIHzpXjr74bFc0kqacRFi869KFql+EPz277FkCqF58ePGo9ATIt6bxfp6ibk 7 | BtT7F05i6R8wCQYHKoZIzj0EAQNHADBEAiBcrNnGaoJyb9gc2hH5+h1dGwWTKihe 8 | EFK0PXQ4Wfs8xAIgfYS7sOe48XrB9RkrE2PEiYYWnHoWvHLuVZDa+gSVoQI= 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIFRzmArkwfwzfIUXYTj1fz1kFcr9EG+esqwryTUq62HToAoGCCqGSM49 6 | AwEHoUQDQgAEhpnYGAUQkMedJE6ogfOleOvvhsVzSSppxEWLzr0oWqX4Q/PbvsWQ 7 | KoXnx48aj0BMi3pvF+nqJuQG1PsXTmLpHw== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/certs/01.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: ecdsa-with-SHA256 6 | Issuer: C=QQ, O=Org1, CN=TestCA1 7 | Validity 8 | Not Before: Nov 2 18:51:29 2015 GMT 9 | Not After : Nov 15 18:51:29 2043 GMT 10 | Subject: C=QQ, L=computer, O=Org1, OU=db, CN=localhost 11 | Subject Public Key Info: 12 | Public Key Algorithm: id-ecPublicKey 13 | Public-Key: (256 bit) 14 | pub: 15 | 04:43:67:94:56:08:84:77:1e:ca:2f:00:aa:28:37: 16 | af:a6:73:b2:42:f3:97:78:6f:c7:fb:c8:3f:67:4c: 17 | fe:36:5d:de:fc:4a:75:39:6a:1c:e9:bf:bf:30:ac: 18 | 8b:eb:3c:d1:f2:1a:48:f9:97:e9:3d:75:5e:82:41: 19 | d5:a3:91:56:91 20 | ASN1 OID: prime256v1 21 | Signature Algorithm: ecdsa-with-SHA256 22 | 30:45:02:21:00:bb:bb:d5:e7:e1:2e:30:24:9c:48:48:32:d4: 23 | aa:b4:b0:05:41:28:50:c8:4e:53:3d:56:49:06:f6:09:84:93: 24 | 98:02:20:5c:41:08:61:05:c7:48:18:bb:f5:6e:c5:7a:74:35: 25 | b1:10:cd:cf:75:55:83:43:36:a8:e2:02:7e:44:24:7d:5c 26 | -----BEGIN CERTIFICATE----- 27 | MIIBZjCCAQwCAQEwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT 28 | BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 29 | MTg1MTI5WjBQMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV 30 | BAoTBE9yZzExCzAJBgNVBAsTAmRiMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcq 31 | hkjOPQIBBggqhkjOPQMBBwNCAARDZ5RWCIR3HsovAKooN6+mc7JC85d4b8f7yD9n 32 | TP42Xd78SnU5ahzpv78wrIvrPNHyGkj5l+k9dV6CQdWjkVaRMAoGCCqGSM49BAMC 33 | A0gAMEUCIQC7u9Xn4S4wJJxISDLUqrSwBUEoUMhOUz1WSQb2CYSTmAIgXEEIYQXH 34 | SBi79W7FenQ1sRDNz3VVg0M2qOICfkQkfVw= 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/certs/02.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 2 (0x2) 5 | Signature Algorithm: ecdsa-with-SHA256 6 | Issuer: C=QQ, O=Org1, CN=TestCA1 7 | Validity 8 | Not Before: Nov 2 18:51:29 2015 GMT 9 | Not After : Nov 15 18:51:29 2043 GMT 10 | Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=bouncer 11 | Subject Public Key Info: 12 | Public Key Algorithm: id-ecPublicKey 13 | Public-Key: (256 bit) 14 | pub: 15 | 04:78:24:60:bd:49:d0:a7:cc:cd:4c:fd:5f:05:57: 16 | 85:b2:82:d7:07:e4:09:ef:26:c3:8d:81:45:ed:ab: 17 | 48:e2:49:a7:06:d2:ce:5c:7a:87:9c:29:b4:da:be: 18 | 02:d4:60:90:bc:6f:61:13:9b:95:d9:9a:5d:ae:a5: 19 | bf:42:46:c7:13 20 | ASN1 OID: prime256v1 21 | Signature Algorithm: ecdsa-with-SHA256 22 | 30:44:02:20:5f:94:b0:e5:5f:87:1a:fb:19:ff:44:c6:fa:33: 23 | d1:b8:40:37:a8:8f:1a:a1:ce:fd:5e:c2:e1:0a:ae:de:97:9d: 24 | 02:20:11:3b:5a:be:4a:02:9a:ee:b0:6c:ab:f1:a4:9b:6a:c9: 25 | 03:a8:17:40:3a:9b:44:e6:d8:21:42:f0:4f:c0:fe:4b 26 | -----BEGIN CERTIFICATE----- 27 | MIIBZDCCAQsCAQIwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT 28 | BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 29 | MTg1MTI5WjBPMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV 30 | BAoTBE9yZzExDDAKBgNVBAsTA0RldjEQMA4GA1UEAxMHYm91bmNlcjBZMBMGByqG 31 | SM49AgEGCCqGSM49AwEHA0IABHgkYL1J0KfMzUz9XwVXhbKC1wfkCe8mw42BRe2r 32 | SOJJpwbSzlx6h5wptNq+AtRgkLxvYRObldmaXa6lv0JGxxMwCgYIKoZIzj0EAwID 33 | RwAwRAIgX5Sw5V+HGvsZ/0TG+jPRuEA3qI8aoc79XsLhCq7el50CIBE7Wr5KApru 34 | sGyr8aSbaskDqBdAOptE5tghQvBPwP5L 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/certs/03.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 3 (0x3) 5 | Signature Algorithm: ecdsa-with-SHA256 6 | Issuer: C=QQ, O=Org1, CN=TestCA1 7 | Validity 8 | Not Before: Nov 2 18:51:29 2015 GMT 9 | Not After : Nov 15 18:51:29 2043 GMT 10 | Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=random 11 | Subject Public Key Info: 12 | Public Key Algorithm: id-ecPublicKey 13 | Public-Key: (256 bit) 14 | pub: 15 | 04:61:d8:4f:c8:fd:7a:56:a3:3e:f4:e3:fd:b8:0d: 16 | fc:72:9e:3b:74:72:18:fb:d1:10:ae:f7:44:8c:cd: 17 | 24:55:c3:34:1a:02:3c:d5:26:66:e0:eb:7e:e8:d4: 18 | d0:8b:b8:d7:6b:9d:71:7b:c6:a1:57:fa:e6:87:8f: 19 | c8:f5:63:dc:28 20 | ASN1 OID: prime256v1 21 | Signature Algorithm: ecdsa-with-SHA256 22 | 30:45:02:21:00:91:0b:37:23:2f:2a:c9:c2:60:51:af:ba:33: 23 | 1e:c7:50:6f:f4:fb:2a:ff:2d:f5:c6:0d:fd:f4:e6:7a:3a:9e: 24 | 77:02:20:6e:02:7b:f5:16:c1:7a:78:68:af:c2:8e:8b:86:b6: 25 | ad:84:b1:2e:4e:f6:80:70:5e:c4:13:dc:c4:84:62:61:92 26 | -----BEGIN CERTIFICATE----- 27 | MIIBZDCCAQoCAQMwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT 28 | BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 29 | MTg1MTI5WjBOMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV 30 | BAoTBE9yZzExDDAKBgNVBAsTA0RldjEPMA0GA1UEAxMGcmFuZG9tMFkwEwYHKoZI 31 | zj0CAQYIKoZIzj0DAQcDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdEjM0k 32 | VcM0GgI81SZm4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKDAKBggqhkjOPQQDAgNI 33 | ADBFAiEAkQs3Iy8qycJgUa+6Mx7HUG/0+yr/LfXGDf305no6nncCIG4Ce/UWwXp4 34 | aK/CjouGtq2EsS5O9oBwXsQT3MSEYmGS 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/config.ini: -------------------------------------------------------------------------------- 1 | [ca] 2 | default_ca = test-ca 3 | 4 | [test-ca] 5 | dir = TestCA1 # top dir 6 | database = $dir/index.txt # index file. 7 | new_certs_dir = $dir/certs # new certs dir 8 | certificate = $dir/ca.crt # The CA cert 9 | serial = $dir/serial # serial no file 10 | private_key = $dir/ca.key # CA private key 11 | 12 | default_md = sha256 13 | 14 | policy = pol-user 15 | 16 | [pol-user] 17 | C = supplied 18 | L = supplied 19 | #ST = supplied 20 | O = supplied 21 | OU = supplied 22 | CN = supplied 23 | emailAddress = supplied 24 | 25 | [pol-server] 26 | C = supplied 27 | L = supplied 28 | #ST = supplied 29 | O = supplied 30 | OU = supplied 31 | CN = supplied 32 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/index.txt: -------------------------------------------------------------------------------- 1 | V 431115185129Z 01 unknown /C=QQ/L=computer/O=Org1/OU=db/CN=localhost 2 | V 431115185129Z 02 unknown /C=QQ/L=computer/O=Org1/OU=Dev/CN=bouncer 3 | V 431115185129Z 03 unknown /C=QQ/L=computer/O=Org1/OU=Dev/CN=random 4 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/serial: -------------------------------------------------------------------------------- 1 | 04 2 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/01-localhost.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: ecdsa-with-SHA256 6 | Issuer: C=QQ, O=Org1, CN=TestCA1 7 | Validity 8 | Not Before: Nov 2 18:51:29 2015 GMT 9 | Not After : Nov 15 18:51:29 2043 GMT 10 | Subject: C=QQ, L=computer, O=Org1, OU=db, CN=localhost 11 | Subject Public Key Info: 12 | Public Key Algorithm: id-ecPublicKey 13 | Public-Key: (256 bit) 14 | pub: 15 | 04:43:67:94:56:08:84:77:1e:ca:2f:00:aa:28:37: 16 | af:a6:73:b2:42:f3:97:78:6f:c7:fb:c8:3f:67:4c: 17 | fe:36:5d:de:fc:4a:75:39:6a:1c:e9:bf:bf:30:ac: 18 | 8b:eb:3c:d1:f2:1a:48:f9:97:e9:3d:75:5e:82:41: 19 | d5:a3:91:56:91 20 | ASN1 OID: prime256v1 21 | Signature Algorithm: ecdsa-with-SHA256 22 | 30:45:02:21:00:bb:bb:d5:e7:e1:2e:30:24:9c:48:48:32:d4: 23 | aa:b4:b0:05:41:28:50:c8:4e:53:3d:56:49:06:f6:09:84:93: 24 | 98:02:20:5c:41:08:61:05:c7:48:18:bb:f5:6e:c5:7a:74:35: 25 | b1:10:cd:cf:75:55:83:43:36:a8:e2:02:7e:44:24:7d:5c 26 | -----BEGIN CERTIFICATE----- 27 | MIIBZjCCAQwCAQEwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT 28 | BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 29 | MTg1MTI5WjBQMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV 30 | BAoTBE9yZzExCzAJBgNVBAsTAmRiMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcq 31 | hkjOPQIBBggqhkjOPQMBBwNCAARDZ5RWCIR3HsovAKooN6+mc7JC85d4b8f7yD9n 32 | TP42Xd78SnU5ahzpv78wrIvrPNHyGkj5l+k9dV6CQdWjkVaRMAoGCCqGSM49BAMC 33 | A0gAMEUCIQC7u9Xn4S4wJJxISDLUqrSwBUEoUMhOUz1WSQb2CYSTmAIgXEEIYQXH 34 | SBi79W7FenQ1sRDNz3VVg0M2qOICfkQkfVw= 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/01-localhost.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBCTCBsgIBADBQMRIwEAYDVQQDEwlsb2NhbGhvc3QxCzAJBgNVBAYTAlFRMQ0w 3 | CwYDVQQKEwRPcmcxMREwDwYDVQQHEwhjb21wdXRlcjELMAkGA1UECxMCZGIwWTAT 4 | BgcqhkjOPQIBBggqhkjOPQMBBwNCAARDZ5RWCIR3HsovAKooN6+mc7JC85d4b8f7 5 | yD9nTP42Xd78SnU5ahzpv78wrIvrPNHyGkj5l+k9dV6CQdWjkVaRoAAwCQYHKoZI 6 | zj0EAQNHADBEAiAhY7+n6M4b3SPkqCFD2EZQVaq3Tb2iNeZXjkJRgFcqQAIgEFQt 7 | n/TI3cU+D1K79A3Msc3OTDLiG9csY+oj6hL6jYM= 8 | -----END CERTIFICATE REQUEST----- 9 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/01-localhost.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIKbYtlQ55hDDsDxMJh4jApq+0VlG5dBz3uarAr7rAGC4oAoGCCqGSM49 6 | AwEHoUQDQgAEQ2eUVgiEdx7KLwCqKDevpnOyQvOXeG/H+8g/Z0z+Nl3e/Ep1OWoc 7 | 6b+/MKyL6zzR8hpI+ZfpPXVegkHVo5FWkQ== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/02-bouncer.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 2 (0x2) 5 | Signature Algorithm: ecdsa-with-SHA256 6 | Issuer: C=QQ, O=Org1, CN=TestCA1 7 | Validity 8 | Not Before: Nov 2 18:51:29 2015 GMT 9 | Not After : Nov 15 18:51:29 2043 GMT 10 | Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=bouncer 11 | Subject Public Key Info: 12 | Public Key Algorithm: id-ecPublicKey 13 | Public-Key: (256 bit) 14 | pub: 15 | 04:78:24:60:bd:49:d0:a7:cc:cd:4c:fd:5f:05:57: 16 | 85:b2:82:d7:07:e4:09:ef:26:c3:8d:81:45:ed:ab: 17 | 48:e2:49:a7:06:d2:ce:5c:7a:87:9c:29:b4:da:be: 18 | 02:d4:60:90:bc:6f:61:13:9b:95:d9:9a:5d:ae:a5: 19 | bf:42:46:c7:13 20 | ASN1 OID: prime256v1 21 | Signature Algorithm: ecdsa-with-SHA256 22 | 30:44:02:20:5f:94:b0:e5:5f:87:1a:fb:19:ff:44:c6:fa:33: 23 | d1:b8:40:37:a8:8f:1a:a1:ce:fd:5e:c2:e1:0a:ae:de:97:9d: 24 | 02:20:11:3b:5a:be:4a:02:9a:ee:b0:6c:ab:f1:a4:9b:6a:c9: 25 | 03:a8:17:40:3a:9b:44:e6:d8:21:42:f0:4f:c0:fe:4b 26 | -----BEGIN CERTIFICATE----- 27 | MIIBZDCCAQsCAQIwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT 28 | BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 29 | MTg1MTI5WjBPMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV 30 | BAoTBE9yZzExDDAKBgNVBAsTA0RldjEQMA4GA1UEAxMHYm91bmNlcjBZMBMGByqG 31 | SM49AgEGCCqGSM49AwEHA0IABHgkYL1J0KfMzUz9XwVXhbKC1wfkCe8mw42BRe2r 32 | SOJJpwbSzlx6h5wptNq+AtRgkLxvYRObldmaXa6lv0JGxxMwCgYIKoZIzj0EAwID 33 | RwAwRAIgX5Sw5V+HGvsZ/0TG+jPRuEA3qI8aoc79XsLhCq7el50CIBE7Wr5KApru 34 | sGyr8aSbaskDqBdAOptE5tghQvBPwP5L 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/02-bouncer.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBCDCBsQIBADBPMRAwDgYDVQQDEwdib3VuY2VyMQswCQYDVQQGEwJRUTENMAsG 3 | A1UEChMET3JnMTERMA8GA1UEBxMIY29tcHV0ZXIxDDAKBgNVBAsTA0RldjBZMBMG 4 | ByqGSM49AgEGCCqGSM49AwEHA0IABHgkYL1J0KfMzUz9XwVXhbKC1wfkCe8mw42B 5 | Re2rSOJJpwbSzlx6h5wptNq+AtRgkLxvYRObldmaXa6lv0JGxxOgADAJBgcqhkjO 6 | PQQBA0cAMEQCIFQrYbvpD5MQ5K2cnpJEvV/6YU9yevAwCLow31jFj2RBAiBuhZ5u 7 | 87BL7owf+r/G1KK7V07GhJHI7D+Hb6NNzTvILQ== 8 | -----END CERTIFICATE REQUEST----- 9 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/02-bouncer.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEICB7ePrPDhy/lN9C82KbSZvajGKP2S9w9qcxwxWLlxTloAoGCCqGSM49 6 | AwEHoUQDQgAEeCRgvUnQp8zNTP1fBVeFsoLXB+QJ7ybDjYFF7atI4kmnBtLOXHqH 7 | nCm02r4C1GCQvG9hE5uV2ZpdrqW/QkbHEw== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/03-random.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 3 (0x3) 5 | Signature Algorithm: ecdsa-with-SHA256 6 | Issuer: C=QQ, O=Org1, CN=TestCA1 7 | Validity 8 | Not Before: Nov 2 18:51:29 2015 GMT 9 | Not After : Nov 15 18:51:29 2043 GMT 10 | Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=random 11 | Subject Public Key Info: 12 | Public Key Algorithm: id-ecPublicKey 13 | Public-Key: (256 bit) 14 | pub: 15 | 04:61:d8:4f:c8:fd:7a:56:a3:3e:f4:e3:fd:b8:0d: 16 | fc:72:9e:3b:74:72:18:fb:d1:10:ae:f7:44:8c:cd: 17 | 24:55:c3:34:1a:02:3c:d5:26:66:e0:eb:7e:e8:d4: 18 | d0:8b:b8:d7:6b:9d:71:7b:c6:a1:57:fa:e6:87:8f: 19 | c8:f5:63:dc:28 20 | ASN1 OID: prime256v1 21 | Signature Algorithm: ecdsa-with-SHA256 22 | 30:45:02:21:00:91:0b:37:23:2f:2a:c9:c2:60:51:af:ba:33: 23 | 1e:c7:50:6f:f4:fb:2a:ff:2d:f5:c6:0d:fd:f4:e6:7a:3a:9e: 24 | 77:02:20:6e:02:7b:f5:16:c1:7a:78:68:af:c2:8e:8b:86:b6: 25 | ad:84:b1:2e:4e:f6:80:70:5e:c4:13:dc:c4:84:62:61:92 26 | -----BEGIN CERTIFICATE----- 27 | MIIBZDCCAQoCAQMwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT 28 | BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 29 | MTg1MTI5WjBOMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV 30 | BAoTBE9yZzExDDAKBgNVBAsTA0RldjEPMA0GA1UEAxMGcmFuZG9tMFkwEwYHKoZI 31 | zj0CAQYIKoZIzj0DAQcDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdEjM0k 32 | VcM0GgI81SZm4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKDAKBggqhkjOPQQDAgNI 33 | ADBFAiEAkQs3Iy8qycJgUa+6Mx7HUG/0+yr/LfXGDf305no6nncCIG4Ce/UWwXp4 34 | aK/CjouGtq2EsS5O9oBwXsQT3MSEYmGS 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/03-random.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBCDCBsAIBADBOMQ8wDQYDVQQDEwZyYW5kb20xCzAJBgNVBAYTAlFRMQ0wCwYD 3 | VQQKEwRPcmcxMREwDwYDVQQHEwhjb21wdXRlcjEMMAoGA1UECxMDRGV2MFkwEwYH 4 | KoZIzj0CAQYIKoZIzj0DAQcDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdE 5 | jM0kVcM0GgI81SZm4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKKAAMAkGByqGSM49 6 | BAEDSAAwRQIhAOz1EZLE+5x0dXV8SkcDYYH9OjlhhvEySBixQy9kHA+VAiBcbfGK 7 | l2tVsxuYbu3Qv9KOuda+0t6RKkuPVkP0zDZgNg== 8 | -----END CERTIFICATE REQUEST----- 9 | -------------------------------------------------------------------------------- /test/ssl/TestCA1/sites/03-random.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIIEPwOJ8lZNOWm9zmgj38p0WaL839An9RJNFVbQNltcjoAoGCCqGSM49 6 | AwEHoUQDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdEjM0kVcM0GgI81SZm 7 | 4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKA== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /test/ssl/lib.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # PEM format 4 | 5 | # req fields 6 | # C = Country 7 | # ST = State/Province 8 | # L = Locality 9 | # O = Organization 10 | # OU = Org Unit 11 | # CN = commonName 12 | # ? = emailAddress 13 | 14 | umask 077 15 | 16 | run() { 17 | echo '$' "$@" 18 | "$@" 2>&1 | sed 's/^/ > /' 19 | } 20 | 21 | # key -> csr 22 | run_req() { 23 | tmp="csr.template" 24 | args="" 25 | while test "$1" != '--'; do 26 | args="$args $1" 27 | shift 28 | done 29 | shift 30 | 31 | ( 32 | echo "[req]" 33 | echo "prompt=no" 34 | echo "distinguished_name=req_distinguished_name" 35 | echo "[req_distinguished_name]" 36 | for arg; do echo "$arg"; done 37 | ) > "$tmp" 38 | run openssl req $args -config "$tmp" 39 | rm -f csr.template 40 | } 41 | 42 | run_ca() { 43 | ser=`cat ${CaName}/serial` 44 | run openssl ca -batch -config "${CaName}/config.ini" "$@" 45 | while test "$1" != '-out'; do 46 | shift 47 | done 48 | if test "$1" = '-out'; then 49 | cp "${CaName}/certs/$ser.pem" "$2" 50 | fi 51 | } 52 | -------------------------------------------------------------------------------- /test/ssl/newca.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # create new CA 4 | 5 | set -e 6 | 7 | test -n "$1" || { 8 | echo "usage: $0 CaName [K=V]*" 9 | exit 1 10 | } 11 | 12 | test -d "$1" && { 13 | echo "CA '$1' already exists" 14 | exit 1 15 | } 16 | 17 | name="$1" 18 | shift 19 | 20 | mkdir -p "$name"/certs 21 | mkdir -p "$name"/sites 22 | touch "$name"/index.txt 23 | echo 01 > "$name"/serial 24 | 25 | . ./lib.sh 26 | 27 | days=10240 28 | 29 | #run openssl genrsa -out "$name/ca.key" $ksize 30 | run openssl ecparam -name prime256v1 -genkey -out "$name/ca.key" 31 | 32 | # self-signed cert 33 | run_req -new -x509 -days $days -key "$name/ca.key" -out "$name/ca.crt" -- "$@" 34 | 35 | 36 | cat > "$name"/config.ini < " 7 | exit 1 8 | } 9 | 10 | test -f "$1/ca.key" || { 11 | echo "CA $1 does not exist" 12 | exit 1 13 | } 14 | 15 | days=10240 16 | 17 | . ./lib.sh 18 | 19 | CaName="$1" 20 | DstName="$2" 21 | shift 2 22 | 23 | ser=`cat $CaName/serial` 24 | 25 | pfx=$CaName/sites/${ser}-$DstName 26 | 27 | run openssl ecparam -genkey -name prime256v1 -out $pfx.key 28 | 29 | # cert reqs 30 | run_req -new -key "$pfx.key" -out "$pfx.csr" -- CN="$DstName" "$@" 31 | 32 | # accept certs 33 | run_ca -days $days -policy pol-server -in "$pfx.csr" -out "$pfx.crt" 34 | -------------------------------------------------------------------------------- /test/ssl/test.ini: -------------------------------------------------------------------------------- 1 | ;; database name = connect string 2 | [databases] 3 | 4 | p0 = port=6666 host=localhost dbname=p0 user=bouncer pool_size=2 5 | p1 = port=6666 host=localhost dbname=p1 user=bouncer 6 | p2 = port=6668 host=localhost dbname=p2 user=bouncer 7 | 8 | ;; Configuation section 9 | [pgbouncer] 10 | 11 | ;;; 12 | ;;; Administrative settings 13 | ;;; 14 | 15 | logfile = tmp/test.log 16 | pidfile = tmp/test.pid 17 | 18 | ;;; 19 | ;;; Where to wait for clients 20 | ;;; 21 | 22 | ; ip address or * which means all ip-s 23 | listen_addr = 127.0.0.1 24 | listen_port = 6667 25 | unix_socket_dir = /tmp 26 | 27 | ;;; 28 | ;;; Authentication settings 29 | ;;; 30 | 31 | ; any, trust, plain, crypt, md5 32 | ;auth_type = trust 33 | auth_file = tmp/userlist.txt 34 | 35 | ;;; 36 | ;;; Pooler personality questions 37 | ;;; 38 | 39 | ; When server connection is released back to pool: 40 | ; session - after client disconnects 41 | ; transaction - after transaction finishes 42 | ; statement - after statement finishes 43 | pool_mode = statement 44 | 45 | ; When taking idle server into use, this query is ran first. 46 | ; 47 | ; Query for session pooling: 48 | ; ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT 49 | ; Query for statement/transaction pooling: 50 | ; SELECT 1 51 | ; Empty query disables the functionality 52 | server_check_query = select 1 53 | 54 | ; If server was used more recently that this many seconds ago, 55 | ; skip the check query. If 0, the check query is always ran. 56 | server_check_delay = 10 57 | 58 | ;;; 59 | ;;; Connection limits 60 | ;;; 61 | 62 | ; total number of clients that can connect 63 | max_client_conn = 10 64 | default_pool_size = 5 65 | 66 | ;;; 67 | ;;; Timeouts 68 | ;;; 69 | 70 | ; Close server connection if its been connected longer. 71 | server_lifetime = 120 72 | 73 | ; Close server connection if its not been used in this time. 74 | ; Allows to clean unneccessary connections from pool after peak. 75 | server_idle_timeout = 60 76 | 77 | ; Cancel connection attepmt if server does not answer takes longer. 78 | server_connect_timeout = 15 79 | 80 | ; If server login failed (server_connect_timeout or auth failure) 81 | ; then wait this many second. 82 | server_login_retry = 15 83 | 84 | ; Dangerous. Server connection is closed if query does not return 85 | ; in this time. Should be used to survive network problems, 86 | ; _not_ as statement_timeout. (default: 0) 87 | query_timeout = 0 88 | 89 | ; Dangerous. Client connection is closed if no activity in this time. 90 | ; Should be used to survive network problems. (default: 0) 91 | client_idle_timeout = 0 92 | -------------------------------------------------------------------------------- /test/ssl/test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | rm -rf TestCA1 4 | 5 | ( 6 | ./newca.sh TestCA1 C=QQ O=Org1 CN="TestCA1" 7 | ./newsite.sh TestCA1 localhost C=QQ O=Org1 L=computer OU=db 8 | ./newsite.sh TestCA1 bouncer C=QQ O=Org1 L=computer OU=Dev 9 | ./newsite.sh TestCA1 random C=QQ O=Org1 L=computer OU=Dev 10 | ) > /dev/null 11 | 12 | export PATH=/usr/lib/postgresql/9.4/bin:$PATH 13 | export PGDATA=$PWD/pgdata 14 | export PGHOST=localhost 15 | export PGPORT=6667 16 | export EF_ALLOW_MALLOC_0=1 17 | 18 | mkdir -p tmp 19 | 20 | BOUNCER_LOG=tmp/test.log 21 | BOUNCER_INI=test.ini 22 | BOUNCER_PID=tmp/test.pid 23 | BOUNCER_PORT=`sed -n '/^listen_port/s/listen_port.*=[^0-9]*//p' $BOUNCER_INI` 24 | BOUNCER_EXE="../../pgbouncer" 25 | 26 | LOGDIR=tmp 27 | NC_PORT=6668 28 | PG_PORT=6666 29 | PG_LOG=$LOGDIR/pg.log 30 | 31 | pgctl() { 32 | pg_ctl -o "-p $PG_PORT" -D $PGDATA $@ >>$PG_LOG 2>&1 33 | } 34 | 35 | rm -f core 36 | ulimit -c unlimited 37 | 38 | for f in pgdata/postmaster.pid tmp/test.pid; do 39 | test -f $f && { kill `cat $f` || true; } 40 | done 41 | 42 | mkdir -p $LOGDIR 43 | rm -fr $BOUNCER_LOG $PG_LOG 44 | rm -rr $PGDATA 45 | 46 | if [ ! -d $PGDATA ]; then 47 | echo "initdb" 48 | mkdir $PGDATA 49 | initdb --nosync >> $PG_LOG 2>&1 50 | sed -r -i "/unix_socket_director/s:.*(unix_socket_director.*=).*:\\1 '/tmp':" pgdata/postgresql.conf 51 | echo "port = $PG_PORT" >> pgdata/postgresql.conf 52 | echo "log_connections = on" >> pgdata/postgresql.conf 53 | echo "log_disconnections = on" >> pgdata/postgresql.conf 54 | cp pgdata/postgresql.conf pgdata/postgresql.conf.orig 55 | cp pgdata/pg_hba.conf pgdata/pg_hba.conf.orig 56 | cp pgdata/pg_ident.conf pgdata/pg_ident.conf.orig 57 | 58 | cp -p TestCA1/sites/01-localhost.crt pgdata/server.crt 59 | cp -p TestCA1/sites/01-localhost.key pgdata/server.key 60 | cp -p TestCA1/ca.crt pgdata/root.crt 61 | 62 | echo '"bouncer" "zzz"' > tmp/userlist.txt 63 | 64 | chmod 600 pgdata/server.key 65 | chmod 600 tmp/userlist.txt 66 | fi 67 | 68 | pgctl start 69 | sleep 5 70 | 71 | echo "createdb" 72 | psql -p $PG_PORT -l | grep p0 > /dev/null || { 73 | psql -p $PG_PORT -c "create user bouncer" template1 74 | createdb -p $PG_PORT p0 75 | createdb -p $PG_PORT p1 76 | } 77 | 78 | $BOUNCER_EXE -d $BOUNCER_INI 79 | sleep 1 80 | 81 | reconf_bouncer() { 82 | cp test.ini tmp/test.ini 83 | for ln in "$@"; do 84 | echo "$ln" >> tmp/test.ini 85 | done 86 | test -f tmp/test.pid && kill `cat tmp/test.pid` 87 | sleep 1 88 | $BOUNCER_EXE -v -v -v -d tmp/test.ini 89 | } 90 | 91 | reconf_pgsql() { 92 | cp pgdata/postgresql.conf.orig pgdata/postgresql.conf 93 | for ln in "$@"; do 94 | echo "$ln" >> pgdata/postgresql.conf 95 | done 96 | pgctl stop 97 | pgctl start 98 | sleep 1 99 | } 100 | 101 | 102 | # 103 | # fw hacks 104 | # 105 | 106 | # 107 | # util functions 108 | # 109 | 110 | complete() { 111 | test -f $BOUNCER_PID && kill `cat $BOUNCER_PID` >/dev/null 2>&1 112 | pgctl -m fast stop 113 | rm -f $BOUNCER_PID 114 | } 115 | 116 | die() { 117 | echo $@ 118 | complete 119 | exit 1 120 | } 121 | 122 | admin() { 123 | psql -h /tmp -U pgbouncer pgbouncer -c "$@;" || die "Cannot contact bouncer!" 124 | } 125 | 126 | runtest() { 127 | echo -n "`date` running $1 ... " 128 | eval $1 >$LOGDIR/$1.log 2>&1 129 | if [ $? -eq 0 ]; then 130 | echo "ok" 131 | else 132 | echo "FAILED" 133 | fi 134 | date >> $LOGDIR/$1.log 135 | 136 | # allow background processing to complete 137 | wait 138 | # start with fresh config 139 | kill -HUP `cat $BOUNCER_PID` 140 | } 141 | 142 | psql_pg() { 143 | psql -U bouncer -h 127.0.0.1 -p $PG_PORT "$@" 144 | } 145 | 146 | psql_bouncer() { 147 | PGUSER=bouncer psql "$@" 148 | } 149 | 150 | # server_lifetime 151 | test_server_ssl() { 152 | reconf_bouncer "auth_type = trust" "server_tls_sslmode = require" 153 | echo "hostssl all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf 154 | reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" 155 | psql_bouncer -q -d p0 -c "select 'ssl-connect'" | tee tmp/test.tmp0 156 | grep -q "ssl-connect" tmp/test.tmp0 157 | rc=$? 158 | return $rc 159 | } 160 | 161 | test_server_ssl_verify() { 162 | reconf_bouncer "auth_type = trust" \ 163 | "server_tls_sslmode = verify-full" \ 164 | "server_tls_ca_file = TestCA1/ca.crt" 165 | 166 | echo "hostssl all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf 167 | reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" 168 | psql_bouncer -q -d p0 -c "select 'ssl-full-connect'" | tee tmp/test.tmp1 169 | grep -q "ssl-full-connect" tmp/test.tmp1 170 | rc=$? 171 | return $rc 172 | } 173 | 174 | test_server_ssl_pg_auth() { 175 | reconf_bouncer "auth_type = trust" \ 176 | "server_tls_sslmode = verify-full" \ 177 | "server_tls_ca_file = TestCA1/ca.crt" \ 178 | "server_tls_key_file = TestCA1/sites/02-bouncer.key" \ 179 | "server_tls_cert_file = TestCA1/sites/02-bouncer.crt" 180 | 181 | echo "hostssl all all 127.0.0.1/32 cert" > pgdata/pg_hba.conf 182 | reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" 183 | psql_bouncer -q -d p0 -c "select 'ssl-cert-connect'" | tee tmp/test.tmp2 184 | grep "ssl-cert-connect" tmp/test.tmp2 185 | rc=$? 186 | return $rc 187 | } 188 | 189 | test_client_ssl() { 190 | reconf_bouncer "auth_type = trust" "server_tls_sslmode = prefer" \ 191 | "client_tls_sslmode = require" \ 192 | "client_tls_key_file = TestCA1/sites/01-localhost.key" \ 193 | "client_tls_cert_file = TestCA1/sites/01-localhost.crt" 194 | echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf 195 | reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" 196 | psql_bouncer -q -d "dbname=p0 sslmode=require" -c "select 'client-ssl-connect'" | tee tmp/test.tmp 197 | grep -q "client-ssl-connect" tmp/test.tmp 198 | rc=$? 199 | return $rc 200 | } 201 | 202 | test_client_ssl() { 203 | reconf_bouncer "auth_type = trust" "server_tls_sslmode = prefer" \ 204 | "client_tls_sslmode = require" \ 205 | "client_tls_key_file = TestCA1/sites/01-localhost.key" \ 206 | "client_tls_cert_file = TestCA1/sites/01-localhost.crt" 207 | echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf 208 | reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" 209 | psql_bouncer -q -d "dbname=p0 sslmode=verify-full sslrootcert=TestCA1/ca.crt" -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1 210 | grep -q "client-ssl-connect" tmp/test.tmp 211 | rc=$? 212 | return $rc 213 | } 214 | 215 | test_client_ssl_auth() { 216 | reconf_bouncer "auth_type = cert" "server_tls_sslmode = prefer" \ 217 | "client_tls_sslmode = verify-full" \ 218 | "client_tls_ca_file = TestCA1/ca.crt" \ 219 | "client_tls_key_file = TestCA1/sites/01-localhost.key" \ 220 | "client_tls_cert_file = TestCA1/sites/01-localhost.crt" 221 | echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf 222 | reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" 223 | psql_bouncer -q -d "dbname=p0 sslmode=require sslkey=TestCA1/sites/02-bouncer.key sslcert=TestCA1/sites/02-bouncer.crt" \ 224 | -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1 225 | grep -q "client-ssl-connect" tmp/test.tmp 226 | rc=$? 227 | return $rc 228 | } 229 | 230 | testlist=" 231 | test_server_ssl 232 | test_server_ssl_verify 233 | test_server_ssl_pg_auth 234 | test_client_ssl 235 | test_client_ssl_auth 236 | " 237 | if [ $# -gt 0 ]; then 238 | testlist="$*" 239 | fi 240 | 241 | for test in $testlist 242 | do 243 | runtest $test 244 | done 245 | 246 | complete 247 | 248 | # vim: sts=0 sw=8 noet nosmarttab: 249 | -------------------------------------------------------------------------------- /test/stress.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import sys, os, re, time, psycopg 4 | import threading, thread, random 5 | 6 | n_thread = 100 7 | longtx = 0 8 | tx_sleep = 0 9 | tx_sleep = 8 10 | 11 | conn_data = { 12 | 'dbname': 'marko', 13 | #'host': '127.0.0.1', 14 | 'host': '/tmp', 15 | 'port': '6000', 16 | 'user': 'marko', 17 | #'password': '', 18 | 'connect_timeout': '5', 19 | } 20 | 21 | def get_connstr(): 22 | tmp = [] 23 | for k, v in conn_data.items(): 24 | tmp.append(k+'='+v) 25 | return " ".join(tmp) 26 | 27 | class WorkThread(threading.Thread): 28 | def __init__(self): 29 | threading.Thread.__init__(self) 30 | self.setDaemon(True) 31 | self.stat_lock = threading.Lock() 32 | self.query_cnt = 0 33 | 34 | def inc_cnt(self): 35 | self.stat_lock.acquire() 36 | self.query_cnt += 1 37 | self.stat_lock.release() 38 | 39 | def fetch_cnt(self): 40 | self.stat_lock.acquire() 41 | val = self.query_cnt 42 | self.query_cnt = 0 43 | self.stat_lock.release() 44 | return val 45 | 46 | def run(self): 47 | try: 48 | time.sleep(random.random() * 10.0) 49 | except: pass 50 | while 1: 51 | try: 52 | self.main_loop() 53 | except KeyboardInterrupt: 54 | break 55 | except SystemExit: 56 | break 57 | except Exception, d: 58 | print d 59 | try: 60 | time.sleep(5) 61 | except: pass 62 | 63 | def main_loop(self): 64 | db = psycopg.connect(get_connstr()) 65 | if not longtx: 66 | db.autocommit(1) 67 | n = 0 68 | while n < 10: 69 | self.do_work(db) 70 | self.inc_cnt() 71 | n += 1 72 | 73 | def do_work(self, db): 74 | curs = db.cursor() 75 | q = "select pg_sleep(%.02f)" % (random.random() * 1) 76 | curs.execute(q) 77 | time.sleep(tx_sleep * random.random() + 1) 78 | if longtx: 79 | db.commit() 80 | 81 | def main(): 82 | print "connstr", get_connstr() 83 | 84 | thread_list = [] 85 | while len(thread_list) < n_thread: 86 | t = WorkThread() 87 | t.start() 88 | thread_list.append(t) 89 | 90 | print "started %d threads" % len(thread_list) 91 | 92 | last = time.time() 93 | while 1: 94 | time.sleep(1) 95 | now = time.time() 96 | dur = now - last 97 | if dur >= 5: 98 | last = now 99 | cnt = 0 100 | for t in thread_list: 101 | cnt += t.fetch_cnt() 102 | avg = cnt / dur 103 | print "avg", avg 104 | 105 | if __name__ == '__main__': 106 | try: 107 | main() 108 | except SystemExit: 109 | pass 110 | except KeyboardInterrupt: 111 | pass 112 | #except Exception, d: 113 | # print d 114 | -------------------------------------------------------------------------------- /test/test.ini: -------------------------------------------------------------------------------- 1 | ;; database name = connect string 2 | [databases] 3 | 4 | p0 = port=6666 host=127.0.0.1 dbname=p0 user=bouncer pool_size=2 5 | p1 = port=6666 host=127.0.0.1 dbname=p1 user=bouncer 6 | p2 = port=6668 host=127.0.0.1 dbname=p2 user=bouncer 7 | 8 | authdb = port=6666 host=127.0.0.1 dbname=p1 auth_user=pswcheck 9 | 10 | ;; Configuation section 11 | [pgbouncer] 12 | 13 | ;;; 14 | ;;; Administrative settings 15 | ;;; 16 | 17 | logfile = test.log 18 | pidfile = test.pid 19 | 20 | ;;; 21 | ;;; Where to wait for clients 22 | ;;; 23 | 24 | ; ip address or * which means all ip-s 25 | listen_addr = 127.0.0.1 26 | listen_port = 6667 27 | unix_socket_dir = /tmp 28 | 29 | ;;; 30 | ;;; Authentication settings 31 | ;;; 32 | 33 | ; any, trust, plain, crypt, md5 34 | auth_type = trust 35 | #auth_file = 8.0/main/global/pg_auth 36 | auth_file = userlist.txt 37 | 38 | ;;; 39 | ;;; Pooler personality questions 40 | ;;; 41 | 42 | ; When server connection is released back to pool: 43 | ; session - after client disconnects 44 | ; transaction - after transaction finishes 45 | ; statement - after statement finishes 46 | pool_mode = statement 47 | 48 | ; When taking idle server into use, this query is ran first. 49 | ; 50 | ; Query for session pooling: 51 | ; ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT 52 | ; Query for statement/transaction pooling: 53 | ; SELECT 1 54 | ; Empty query disables the functionality 55 | server_check_query = select 1 56 | 57 | ; If server was used more recently that this many seconds ago, 58 | ; skip the check query. If 0, the check query is always ran. 59 | server_check_delay = 10 60 | 61 | ;;; 62 | ;;; Connection limits 63 | ;;; 64 | 65 | ; total number of clients that can connect 66 | max_client_conn = 10 67 | default_pool_size = 5 68 | 69 | ;;; 70 | ;;; Timeouts 71 | ;;; 72 | 73 | ; Close server connection if its been connected longer. 74 | server_lifetime = 120 75 | 76 | ; Close server connection if its not been used in this time. 77 | ; Allows to clean unneccessary connections from pool after peak. 78 | server_idle_timeout = 60 79 | 80 | ; Cancel connection attepmt if server does not answer takes longer. 81 | server_connect_timeout = 15 82 | 83 | ; If server login failed (server_connect_timeout or auth failure) 84 | ; then wait this many second. 85 | server_login_retry = 15 86 | 87 | ; Dangerous. Server connection is closed if query does not return 88 | ; in this time. Should be used to survive network problems, 89 | ; _not_ as statement_timeout. (default: 0) 90 | query_timeout = 0 91 | 92 | ; Dangerous. Client connection is closed if no activity in this time. 93 | ; Should be used to survive network problems. (default: 0) 94 | client_idle_timeout = 0 95 | 96 | 97 | ;;; 98 | ;;; Low-level tuning options 99 | ;;; 100 | 101 | ; buffer for streaming packets 102 | ;pkt_buf = 4096 103 | 104 | ;;; 105 | ;;; networking options, for info: man 7 tcp 106 | ;;; 107 | 108 | ; linux: notify program about new connection only if there 109 | ; is also data received. (Seconds to wait.) 110 | tcp_defer_accept = 0 111 | 112 | ;; following options are reloadable, but apply only to 113 | ;; new connections. 114 | 115 | ; in-kernel buffer size (linux default: 4096) 116 | tcp_socket_buffer = 0 117 | 118 | ; whether tcp keepalive should be turned on (0/1) 119 | tcp_keepalive = 0 120 | 121 | ;; following options are linux-specific. 122 | ;; they also require tcp_keepalive=1 123 | 124 | ; count of keepaliva packets 125 | tcp_keepcnt = 0 126 | 127 | ; how long the connection can be idle, 128 | ; before sending keepalive packets 129 | tcp_keepidle = 0 130 | 131 | ; The time between individual keepalive probes. 132 | tcp_keepintvl = 0 133 | -------------------------------------------------------------------------------- /test/userlist.txt: -------------------------------------------------------------------------------- 1 | "marko" "kama" 2 | "postgres" "asdasd" 3 | ;Commented out line should be ignored. 4 | "pgbouncer" "fake" 5 | "pswcheck" "pgbouncer-check" 6 | -------------------------------------------------------------------------------- /win32/MSG00001.bin: -------------------------------------------------------------------------------- 1 | %1 2 | -------------------------------------------------------------------------------- /win32/Makefile: -------------------------------------------------------------------------------- 1 | # create eventmsg.rc + MSG*.bin 2 | eventmsg.rc: eventmsg.mc 3 | mc.exe eventmsg.mc 4 | -------------------------------------------------------------------------------- /win32/eventmsg.mc: -------------------------------------------------------------------------------- 1 | MessageId=0 2 | SymbolicName=MSG_PGBOUNCER 3 | Language=English 4 | %1 5 | . 6 | -------------------------------------------------------------------------------- /win32/eventmsg.rc: -------------------------------------------------------------------------------- 1 | LANGUAGE 0x9,0x1 2 | 1 11 "MSG00001.bin" 3 | -------------------------------------------------------------------------------- /win32/pgbevent.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * pgbevent.c 3 | * Defines the entry point for pgbevent dll. 4 | * The DLL defines event source for pgbouncer tools 5 | *------------------------------------------------------------------------- 6 | */ 7 | 8 | #define WIN32_LEAN_AND_MEAN 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define APP_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\pgbouncer" 15 | 16 | /* Global variables */ 17 | static HANDLE g_module = NULL; /* hModule of DLL */ 18 | 19 | /* Prototypes */ 20 | STDAPI DllRegisterServer(void); 21 | STDAPI DllUnregisterServer(void); 22 | BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); 23 | 24 | /* 25 | * DllRegisterServer --- Instructs DLL to create its registry entries 26 | */ 27 | STDAPI DllRegisterServer(void) 28 | { 29 | HKEY key; 30 | DWORD data; 31 | char buffer[_MAX_PATH]; 32 | 33 | /* Set the name of DLL full path name. */ 34 | if (!GetModuleFileName((HMODULE)g_module, buffer, sizeof(buffer))) { 35 | MessageBox(NULL, "Could not retrieve DLL filename", "pgbouncer error", MB_OK | MB_ICONSTOP); 36 | return SELFREG_E_TYPELIB; 37 | } 38 | 39 | /* 40 | * Add our source name as a subkey under the Application key in 41 | * the EventLog registry key. 42 | */ 43 | if (RegCreateKey(HKEY_LOCAL_MACHINE, APP_KEY, &key)) { 44 | MessageBox(NULL, "Could not create the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP); 45 | return SELFREG_E_TYPELIB; 46 | } 47 | 48 | /* Add the name to the EventMessageFile subkey. */ 49 | if (RegSetValueEx(key, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)buffer, strlen(buffer) + 1)) { 50 | MessageBox(NULL, "Could not set the event message file.", "pgbouncer error", MB_OK | MB_ICONSTOP); 51 | return SELFREG_E_TYPELIB; 52 | } 53 | 54 | /* Set the supported event types in the TypesSupported subkey. */ 55 | data = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; 56 | 57 | if (RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD))) { 58 | MessageBox(NULL, "Could not set the supported types.", "pgbouncer error", MB_OK | MB_ICONSTOP); 59 | return SELFREG_E_TYPELIB; 60 | } 61 | 62 | RegCloseKey(key); 63 | return S_OK; 64 | } 65 | 66 | /* 67 | * DllUnregisterServer --- Instructs DLL to remove only those entries created through DllRegisterServer 68 | */ 69 | STDAPI DllUnregisterServer(void) 70 | { 71 | if (RegDeleteKey(HKEY_LOCAL_MACHINE, APP_KEY)) { 72 | MessageBox(NULL, "Could not delete the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP); 73 | return SELFREG_E_TYPELIB; 74 | } 75 | return S_OK; 76 | } 77 | 78 | /* 79 | * DllMain --- is an optional entry point into a DLL. 80 | */ 81 | BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 82 | { 83 | if (ul_reason_for_call == DLL_PROCESS_ATTACH) 84 | g_module = hModule; 85 | return TRUE; 86 | } 87 | -------------------------------------------------------------------------------- /win32/win32support.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * win32service.c 3 | * 4 | * Windows service integration and eventlog 5 | * 6 | * Copyright (c) 2005, PostgreSQL Global Development Group 7 | * Authors: Magnus Hagander, Hiroshi Saito, Marko Kreen 8 | *------------------------------------------------------------------------- 9 | */ 10 | 11 | #include "bouncer.h" 12 | 13 | #if defined(UNICODE) || defined(_UNICODE) 14 | #error This code does not support wide characters. 15 | #endif 16 | 17 | /* Globals for service control */ 18 | static SERVICE_STATUS_HANDLE hStatus = 0; 19 | static SERVICE_STATUS svcStatus = { 20 | .dwServiceType = SERVICE_WIN32_OWN_PROCESS, 21 | .dwControlsAccepted = 0, 22 | .dwWin32ExitCode = NO_ERROR, 23 | .dwCheckPoint = 0, 24 | .dwWaitHint = 0, 25 | .dwCurrentState = SERVICE_START_PENDING, 26 | }; 27 | 28 | /* Event source name for ReportEvent. 29 | * 30 | * Also used as placeholder for service handling API's, but it is ignored 31 | * because our service is defined as WIN32_OWN_PROCESS. 32 | */ 33 | static char *servicename = "pgbouncer"; 34 | 35 | static char *service_username = NULL; 36 | static char *service_password = NULL; 37 | 38 | static char *serviceDescription = "Lightweight connection pooler for PostgreSQL."; 39 | 40 | /* custom help string for win32 exe */ 41 | static const char usage_str[] = 42 | "Usage: %s [OPTION]... config.ini\n" 43 | " -q No console messages\n" 44 | " -v Increase verbosity\n" 45 | " -V Show version\n" 46 | " -h Show this help screen and exit\n" 47 | "Windows service registration:\n" 48 | " --regservice config.ini [-U username [-P password]]\n" 49 | " --unregservice config.ini\n" 50 | ""; 51 | 52 | static void usage(int err, char *exe) 53 | { 54 | printf(usage_str, basename(exe)); 55 | exit(err); 56 | } 57 | 58 | static int exec_real_main(int argc, char *argv[]) 59 | { 60 | int i, j; 61 | 62 | /* win32 stdio seems to be fully buffered by default */ 63 | setvbuf(stdout, NULL, _IONBF, 0); 64 | setvbuf(stderr, NULL, _IONBF, 0); 65 | 66 | /* check if regular arguments are in allowed list */ 67 | for (i = 1; i < argc; i++) { 68 | char *p = argv[i]; 69 | if (p[0] != '-') 70 | continue; 71 | for (j = 1; p[j]; j++) { 72 | if (!strchr("qvhV", p[j])) 73 | usage(1, argv[0]); 74 | if (p[j] == 'h') 75 | usage(0, argv[0]); 76 | } 77 | } 78 | 79 | /* call actual main() */ 80 | return real_main(argc, argv); 81 | } 82 | 83 | /* Set the current service status */ 84 | static void win32_setservicestatus(DWORD state) 85 | { 86 | svcStatus.dwCurrentState = state; 87 | switch (state) { 88 | case SERVICE_START_PENDING: 89 | case SERVICE_STOP_PENDING: 90 | svcStatus.dwControlsAccepted = 0; 91 | svcStatus.dwWaitHint = 5000; 92 | break; 93 | default: 94 | svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 95 | svcStatus.dwWaitHint = 0; 96 | } 97 | 98 | SetServiceStatus(hStatus, &svcStatus); 99 | } 100 | 101 | /* 102 | * Handle any events sent by the service control manager 103 | * NOTE! Events are sent on a different thread! And it's 104 | * not a pthreads thread, so avoid calling anything that 105 | * may use pthreads - like pgbouncer_log() 106 | */ 107 | static void WINAPI win32_servicehandler(DWORD request) 108 | { 109 | switch (request) { 110 | case SERVICE_CONTROL_STOP: 111 | case SERVICE_CONTROL_SHUTDOWN: 112 | win32_setservicestatus(SERVICE_STOP_PENDING); 113 | cf_shutdown = 2; 114 | break; 115 | case SERVICE_CONTROL_INTERROGATE: 116 | SetServiceStatus(hStatus, &svcStatus); 117 | break; 118 | } 119 | } 120 | 121 | /* notify control thread about stop */ 122 | static void win32_service_cleanup(void) 123 | { 124 | if (hStatus) 125 | win32_setservicestatus(SERVICE_STOPPED); 126 | hStatus = 0; /* may get called twice from atexit */ 127 | } 128 | 129 | /* 130 | * Entrypoint for actual service work. 131 | * 132 | * Service is set-up and then actual main() is called. 133 | */ 134 | static void WINAPI win32_servicemain(DWORD argc, LPSTR *argv) 135 | { 136 | int new_argc = 2; 137 | char *new_argv[] = { servicename, cf_config_file, NULL }; 138 | 139 | /* register control request handler */ 140 | hStatus = RegisterServiceCtrlHandler(servicename, win32_servicehandler); 141 | if (hStatus == 0) { 142 | fatal("could not connect to service control handler: %s", strerror(GetLastError())); 143 | exit(1); 144 | } 145 | 146 | /* Tell SCM we are running before we make any API calls */ 147 | win32_setservicestatus(SERVICE_RUNNING); 148 | 149 | /* register with system atexit(), in case somebody calls exit() */ 150 | atexit(win32_service_cleanup); 151 | 152 | /* Execute actual main() */ 153 | exec_real_main(new_argc, new_argv); 154 | 155 | win32_service_cleanup(); 156 | } 157 | 158 | /* Start running as a service */ 159 | static void win32_servicestart(void) 160 | { 161 | SERVICE_TABLE_ENTRY st[] = { {servicename, win32_servicemain}, {NULL, NULL} }; 162 | 163 | if (StartServiceCtrlDispatcher(st) == 0) { 164 | fprintf(stderr, "could not start service control dispatcher: %s\n", 165 | strerror(GetLastError())); 166 | exit(1); 167 | } 168 | } 169 | 170 | /* Open Service Control Manager */ 171 | static SC_HANDLE openSCM(void) 172 | { 173 | SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 174 | if (!manager) { 175 | fprintf(stderr, "Failed to open service control manager: %s\n", strerror(GetLastError())); 176 | exit(1); 177 | } 178 | return manager; 179 | } 180 | 181 | /* Full path to current config file. */ 182 | static const char *get_config_fullpath(void) 183 | { 184 | DWORD r; 185 | static char buf[PATH_MAX]; 186 | 187 | r = GetFullPathName(cf_config_file, sizeof(buf), buf, NULL); 188 | if (r == 0 || r >= sizeof(buf)) { 189 | fprintf(stderr, "Failed to get full pathname for '%s': %s\n", 190 | cf_config_file, strerror(GetLastError())); 191 | exit(1); 192 | } 193 | return buf; 194 | } 195 | 196 | /* Register a service with the specified name with the local service control manager */ 197 | static void RegisterService(void) 198 | { 199 | char self[1024]; 200 | char cmdline[2048]; 201 | const char *config_fn = get_config_fullpath(); 202 | SC_HANDLE manager; 203 | SC_HANDLE service; 204 | SERVICE_DESCRIPTION sd; 205 | DWORD r; 206 | 207 | r = GetModuleFileName(NULL, self, sizeof(self)); 208 | if (!r || r >= sizeof(self)) { 209 | fprintf(stderr, "Failed to determine path name: %s\n", strerror(GetLastError())); 210 | exit(1); 211 | } 212 | snprintf(cmdline, sizeof(cmdline), "%s --service \"%s\"", self, config_fn); 213 | 214 | manager = openSCM(); 215 | service = CreateService(manager, cf_jobname, cf_jobname, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, 216 | SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdline, NULL, NULL, "RPCSS\0", 217 | service_username, service_password); 218 | if (!service) { 219 | fprintf(stderr, "Failed to create service: %s\n", strerror(GetLastError())); 220 | exit(1); 221 | } 222 | 223 | /* explain the service purpose */ 224 | sd.lpDescription = serviceDescription; 225 | ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd); 226 | 227 | CloseServiceHandle(service); 228 | CloseServiceHandle(manager); 229 | 230 | printf("Service registered.\n"); 231 | if (service_username == NULL) { 232 | printf("\nWARNING! Service is registered to run as Local System. You are\n"); 233 | printf("encouraged to change this to a low privilege account to increase\n"); 234 | printf("system security. (Eg. NT AUTHORITY\\Local Service)\n"); 235 | } 236 | } 237 | 238 | /* Remove a service with the specified name from the local service control manager */ 239 | static void UnRegisterService(void) 240 | { 241 | SC_HANDLE manager; 242 | SC_HANDLE service; 243 | 244 | manager = openSCM(); 245 | service = OpenService(manager, cf_jobname, SC_MANAGER_ALL_ACCESS); 246 | if (!service) { 247 | fprintf(stderr, "Failed to open service: %s\n", strerror(GetLastError())); 248 | exit(1); 249 | } 250 | 251 | if (!DeleteService(service)) { 252 | fprintf(stderr, "Failed to delete service: %s\n", strerror(GetLastError())); 253 | exit(1); 254 | } 255 | 256 | CloseServiceHandle(service); 257 | CloseServiceHandle(manager); 258 | 259 | printf("Service removed.\n"); 260 | } 261 | 262 | /* config loader for service register/unregister */ 263 | static void win32_load_config(char *conf) 264 | { 265 | cf_config_file = conf; 266 | init_objects(); 267 | load_config(); 268 | } 269 | 270 | /* 271 | * Wrapper around actual main() that handles win32 hacks. 272 | */ 273 | 274 | #undef main 275 | int main(int argc, char *argv[]) 276 | { 277 | WSADATA wsaData; 278 | 279 | /* initialize socket subsystem */ 280 | if (WSAStartup(MAKEWORD(2,0), &wsaData)) 281 | fatal("Cannot start the network subsystem"); 282 | 283 | /* service cmdline */ 284 | if (argc >= 3) { 285 | if (!strcmp(argv[1], "--service") || !strcmp(argv[1], "-service")) { 286 | cf_quiet = 1; 287 | cf_config_file = argv[2]; 288 | win32_servicestart(); 289 | return 0; 290 | } 291 | 292 | if (!strcmp(argv[1], "--regservice") || !strcmp(argv[1], "-regservice")) { 293 | int i; 294 | win32_load_config(argv[2]); 295 | for (i = 3; i < argc; i++) { 296 | if (!strcmp(argv[i], "-U") && i + 1 < argc) { 297 | service_username = argv[++i]; 298 | } else if (!strcmp(argv[i], "-P") && i + 1 < argc) { 299 | service_password = argv[++i]; 300 | } else { 301 | printf("unknown arg: %s\n", argv[i]); 302 | usage(1, argv[0]); 303 | } 304 | } 305 | RegisterService(); 306 | return 0; 307 | } 308 | 309 | if (!strcmp(argv[1], "--unregservice") || !strcmp(argv[1], "-unregservice")) { 310 | win32_load_config(argv[2]); 311 | UnRegisterService(); 312 | return 0; 313 | } 314 | } 315 | 316 | return exec_real_main(argc, argv); 317 | } 318 | -------------------------------------------------------------------------------- /win32/win32support.h: -------------------------------------------------------------------------------- 1 | /* redirect main() */ 2 | #define main(a,b) real_main(a,b) 3 | int real_main(int argc, char *argv[]); 4 | --------------------------------------------------------------------------------