configure configure.ac debian/packages debian/changelog doc/Makefile \
test/Makefile test/asynctest.c test/conntest.sh test/ctest6000.ini \
test/ctest7000.ini test/run-conntest.sh test/stress.py test/test.ini \
- test/test.sh test/userlist.txt etc/example.debian.init.sh doc/fixman.py
+ test/test.sh test/userlist.txt etc/example.debian.init.sh doc/fixman.py \
+ win32/wbnmsgevent.mc win32/wbnmsgevent.rc win32/MSG00001.bin
DIRS = doc etc include src debian test
# keep autoconf stuff separate
srcs = $(addprefix $(srcdir)/src/, $(SRCS))
objs = $(addprefix $(builddir)/lib/, $(OBJS))
FULL = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)
-DISTFILES = $(DIRS) $(DATA) $(DOCS) $(srcs) $(hdrs) $(MANPAGES)
+DISTFILES = $(DIRS) $(DATA) $(DOCS) $(srcs) $(hdrs) $(MANPAGES) $(WINPORT)
+exe = $(builddir)/pgbouncer$(EXT)
CPPCFLAGS += -I$(srcdir)/include
CPPCFLAGS += -DDBGVER="\"compiled by <$${USER}@`hostname`> at `date '+%Y-%m-%d %H:%M:%S'`\""
endif
+ifeq ($(PORTNAME),win32)
+
+EXT = .exe
+
+CPPFLAGS += -I$(srcdir)/win32
+WSRCS = win32service.c
+WOBJS = $(WSRCS:.c=.o)
+WHDRS = win32service.h config_win32.h
+srcs += $(addprefix $(srcdir)/win32/, $(WSRCS))
+hdrs += $(addprefix $(srcdir)/win32/, $(WHDRS))
+objs += $(addprefix $(builddir)/lib/, $(WOBJS))
+
+dll = $(builddir)/pgbevent.dll
+dlldef = $(builddir)/lib/pgbevent.def
+dllobjs = $(builddir)/lib/eventmsg.o $(builddir)/lib/pgbevent.o
+
+endif
# Quiet by default, 'make V=1' shows commands
V=0
## actual targets now ##
# default target
-all: $(builddir)/pgbouncer doc-all
+all: $(exe) $(dll) doc-all
# final executable
-$(builddir)/pgbouncer: $(builddir)/config.mak $(objs)
+$(exe): $(builddir)/config.mak $(objs)
$(E) " LD" $@
$(Q) $(CC) -o $@ $(LDFLAGS) $(objs) $(LIBS)
$(E) " CC" $<
$(Q) $(CC) -c -o $@ $< $(DEFS) $(CFLAGS) $(CPPFLAGS)
+$(builddir)/lib/%.o: $(srcdir)/win32/%.c $(builddir)/config.mak $(hdrs)
+ @mkdir -p $(builddir)/lib
+ $(E) " CC" $<
+ $(Q) $(CC) -c -o $@ $< $(DEFS) $(CFLAGS) $(CPPFLAGS)
+
# install binary and other stuff
-install: $(builddir)/pgbouncer doc-install
+install: $(exe) doc-install
mkdir -p $(DESTDIR)$(bindir)
mkdir -p $(DESTDIR)$(docdir)
- $(BININSTALL) -m 755 $(builddir)/pgbouncer $(DESTDIR)$(bindir)
+ $(BININSTALL) -m 755 $(exe) $(DESTDIR)$(bindir)
$(INSTALL) -m 644 $(srcdir)/etc/pgbouncer.ini $(DESTDIR)$(docdir)
+ifeq ($(PORTNAME),win32)
+ $(BININSTALL) -m 755 $(dll) $(DESTDIR)$(bindir)
+endif
# create tarfile
tgz: config.mak $(DISTFILES) $(MANPAGES)
# clean object files
clean: doc-clean
- rm -f $(objs) $(builddir)/pgbouncer
+ rm -f $(objs) $(exe) $(dll) $(dlldef) $(dllobjs)
# clean configure results
distclean: clean doc-distclean
asms = $(objs:.o=.s)
asm: $(asms)
+ifeq ($(PORTNAME),win32)
+
+$(srcdir)/win32/eventmsg.rc: $(srcdir)/win32/eventmsg.mc
+ $(MC) $< -o $@ --include-dir=$(srcdir)/win32
+
+$(builddir)/lib/eventmsg.o: $(srcdir)/win32/eventmsg.rc
+ $(E) " WINDRES" $<
+ $(Q) $(WINDRES) $< -o $@ --include-dir=$(srcdir)/win32
+
+$(dlldef): $(dllobjs)
+ $(E) " DLLTOOL" $@
+ $(Q) $(DLLTOOL) --output-def $@ $(dllobjs)
+
+# final executable
+$(dll): $(builddir)/config.mak $(dllobjs) $(dlldef)
+ $(E) " DLLWRAP" $@
+ $(Q) $(DLLWRAP) --def $(dlldef) -o $@ $(dllobjs)
+
+endif
+
+stripped: $(exe) $(dll)
+ $(STRIP) $(exe) $(dll)
+
PACKAGE_VERSION = @PACKAGE_VERSION@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PORTNAME = @PORTNAME@
+
DEFS = @DEFS@
LIBS = @LIBS@
CC = @CC@
XMLTO = @XMLTO@
ASCIIDOC = @ASCIIDOC@
+MC = @MC@
+DLLWRAP = @DLLWRAP@
+DLLTOOL = @DLLTOOL@
+WINDRES = @WINDRES@
+STRIP = @STRIP@
prefix = @prefix@
exec_prefix = @exec_prefix@
AC_CONFIG_HEADER(include/config.h)
AC_PREREQ([2.59])
+dnl windows port check (Not use AC_CANONICAL_HOST)
+AC_MSG_CHECKING([target host type])
+xhost="$host_alias"
+if test "x$xhost" = "x"; then
+ xhost=`uname -s`
+fi
+case $xhost in
+*cygwin* | *mingw* | *pw32* | *MINGW*)
+ LIBS="$LIBS -lws2_32"
+ PORTNAME=win32;;
+*) PORTNAME=unix ;;
+esac
+AC_SUBST(PORTNAME)
+AC_MSG_RESULT([$PORTNAME])
+
dnl Checks for programs.
AC_PROG_CC
AC_PROG_CPP
AC_CHECK_PROGS(XMLTO, xmlto)
fi
+dnl check for windows tools
+if test "$PORTNAME" = "win32"; then
+ AC_CHECK_TOOL([MC], [mc])
+ AC_CHECK_TOOL([WINDRES], [windres])
+ AC_CHECK_TOOL([DLLWRAP], [dllwrap])
+ AC_CHECK_TOOL([DLLTOOL], [dlltool])
+fi
+AC_CHECK_TOOL([STRIP], [strip])
+
dnl Additional gcc tuning
if test x"$GCC" = xyes; then
AC_MSG_CHECKING([for working warning switches])
dnl Checks for header files.
AC_CHECK_HEADERS([crypt.h sys/param.h sys/socket.h sys/uio.h libgen.h pwd.h grp.h])
+AC_CHECK_HEADERS([sys/resource.h sys/wait.h sys/un.h arpa/inet.h])
+AC_CHECK_HEADERS([netinet/in.h netinet/tcp.h netdb.h regex.h])
dnl ucred.h may have prereqs
AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [
AC_SEARCH_LIBS(getsockname, socket)
AC_SEARCH_LIBS(gethostbyname, nsl)
AC_SEARCH_LIBS(hstrerror, resolv)
-AC_CHECK_FUNCS(crypt)
+AC_SEARCH_LIBS(regcomp, regex, [], AC_MSG_ERROR([regcomp not found]))
+AC_CHECK_FUNCS(crypt inet_ntop lstat)
dnl Find libevent
AC_MSG_CHECKING([for libevent])
Specifies log file. Log file is kept open so after rotation
`kill -HUP` or on console `RELOAD;` should be done.
+Note: The windows environment by a stop and start with a service property.
Default: not set.
Specifies location for Unix sockets. Applies to both listening socket and
server connections. If set to empty string, Unix sockets are disabled.
+Note: Did not support in the windows environment.
Default: +/tmp+
If set, specifies UNIX user to change to. Work only if started as root
or `user` is same as current user.
+Note: Did not support in the windows environment.
Default: not set
crafts special load for it. Still, it means you should give fds liberately.
Search for `ulimit` in your favourite shell man page.
+Note: `ulimit` can't be held in the windows environment for now.
Default: 100
==== syslog ====
Toggles syslog on/off
+As for windows environment, eventlog is used for substitution.
Default: 0
pgbouncer [-d][-R][-v][-u user] <pgbouncer.ini>
pgbouncer -V|-h
+The windows environment serves as the following options.
+
+ pgbouncer.exe [-v][-u user] <pgbouncer.ini>
+ pgbouncer.exe -V|-h
+
+Furthermore, this option is specially offered for windows-service.
+
+ <windows service registration>
+ -regservice [servicename]
+ -unregservice [servicename]
+ -listengines [servicename]
+ -addengine [servicename] config.ini
+ -delengine [servicename] config.ini
+
== DESCRIPTION ==
+pgbouncer+ is a PostgreSQL connection pooler. Any target application
-d::
Run in background. Without it the process will run in foreground.
+ Note: The windows environment should use the special option for service
+ operation.
-R::
Do a online restart. That means connecting to running process, loading
open sockets from it and using them. If there is no active process,
boot normally.
+ Note: The windows environment is not equipped.
-u user::
Switch to user on startup.
"pgbouncer", connects thru unix socket and has same UID as running
process, the actual fds are passed over connection. This mechanism is
used to do online restart.
+Note: The windows environment is not supported.
fd::
File descriptor numeric value.
#include "config.h"
#endif
+#ifdef WIN32
+#include "compat_win32.h"
+#endif
+
/* glibc is useless without it */
#define _GNU_SOURCE
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
-#include <sys/socket.h>
#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <sys/resource.h>
+#endif
+
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
const char *basename(const char *path);
#endif
#ifndef HAVE_CRYPT
-#define crypt(p,s) (NULL)
+static inline char *crypt(const char *p, const char *s) { return NULL; }
+#endif
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
+#endif
+#ifndef HAVE_LSTAT
+static inline int lstat(const char *path, struct stat *st) { return stat(path, st); }
#endif
void change_user(const char *user);
-
#include "bouncer.h"
+#ifdef HAVE_NETDB_H
#include <netdb.h>
+#endif
/*
* ConnString parsing
#include "bouncer.h"
-#include <sys/resource.h>
-
#include <signal.h>
#include <getopt.h>
+#ifdef WIN32
+#include "win32service.h"
+#endif
+
static bool set_mode(ConfElem *elem, const char *val, PgSocket *console);
static const char *get_mode(ConfElem *elem);
static bool set_auth(ConfElem *elem, const char *val, PgSocket *console);
static const char *usage_str =
"Usage: %s [OPTION]... config.ini\n"
+#ifndef WIN32
" -d Run in background (as a daemon)\n"
" -R Do a online restart\n"
" -q Run quietly\n"
+#endif
" -v Increase verbosity\n"
" -u <username> Assume identity of <username>\n"
" -V Show version\n"
-" -h Show this help screen and exit\n";
+" -h Show this help screen and exit\n"
+#ifdef WIN32
+" <windows service registration>\n"
+" -regservice [servicename]\n"
+" -unregservice [servicename]\n"
+" -listengines [servicename]\n"
+" -addengine [servicename] config.ini\n"
+" -delengine [servicename] config.ini\n"
+#endif
+"";
static void usage(int err, char *exe)
{
char *cf_listen_addr = NULL;
int cf_listen_port = 6432;
+#ifndef WIN32
char *cf_unix_socket_dir = "/tmp";
+#else
+char *cf_unix_socket_dir = "";
+#endif
int cf_pool_mode = POOL_SESSION;
{"pidfile", false, CF_STR, &cf_pidfile},
{"listen_addr", false, CF_STR, &cf_listen_addr},
{"listen_port", false, CF_INT, &cf_listen_port},
+#ifndef WIN32
{"unix_socket_dir", false, CF_STR, &cf_unix_socket_dir},
+#endif
{"auth_type", true, {get_auth, set_auth}},
{"auth_file", true, CF_STR, &cf_auth_file},
{"pool_mode", true, {get_mode, set_mode}},
{"default_pool_size", true, CF_INT, &cf_default_pool_size},
{"syslog", true, CF_INT, &cf_syslog},
{"syslog_facility", true, CF_STR, &cf_syslog_facility},
+#ifndef WIN32
{"user", false, CF_STR, &cf_username},
+#endif
{"autodb_idle_timeout", true, CF_TIME, &cf_autodb_idle_timeout},
*/
static struct event ev_sigterm;
static struct event ev_sigint;
-static struct event ev_sigusr1;
-static struct event ev_sigusr2;
-static struct event ev_sighup;
static void handle_sigterm(int sock, short flags, void *arg)
{
cf_shutdown = 1;
}
+#ifndef WIN32
+
+static struct event ev_sigusr1;
+static struct event ev_sigusr2;
+static struct event ev_sighup;
+
static void handle_sigusr1(int sock, short flags, void *arg)
{
if (cf_pause_mode == P_NONE) {
log_info("Got SIGHUP re-reading config");
load_config(true);
}
+#endif
static void signal_setup(void)
{
int err;
+
+#ifndef WIN32
sigset_t set;
/* block SIGPIPE */
/* install handlers */
- signal_set(&ev_sigterm, SIGTERM, handle_sigterm, NULL);
- err = signal_add(&ev_sigterm, NULL);
- if (err < 0)
- fatal_perror("signal_add");
-
- signal_set(&ev_sigint, SIGINT, handle_sigint, NULL);
- err = signal_add(&ev_sigint, NULL);
- if (err < 0)
- fatal_perror("signal_add");
-
signal_set(&ev_sigusr1, SIGUSR1, handle_sigusr1, NULL);
err = signal_add(&ev_sigusr1, NULL);
if (err < 0)
err = signal_add(&ev_sighup, NULL);
if (err < 0)
fatal_perror("signal_add");
+#endif
+ signal_set(&ev_sigterm, SIGTERM, handle_sigterm, NULL);
+ err = signal_add(&ev_sigterm, NULL);
+ if (err < 0)
+ fatal_perror("signal_add");
+
+ signal_set(&ev_sigint, SIGINT, handle_sigint, NULL);
+ err = signal_add(&ev_sigint, NULL);
+ if (err < 0)
+ fatal_perror("signal_add");
}
/*
fatal_perror("fork");
if (pid > 0)
_exit(0);
-
}
/*
event_base_free(evtmp);
}
+#ifdef WIN32
+static void win32_startup(int argc, char *argv[])
+{
+ WSADATA wsaData;
+
+ /* parse cmdline */
+ if (argc >= 2 && !strcmp(argv[1], "-service"))
+ {
+ win32_servicestart();
+ exit(0);
+ }
+ if (argc >= 2 && !strcmp(argv[1], "-subservice"))
+ {
+ cf_quiet = 1;
+ argc--;
+ argv++;
+ }
+ if (argc >= 2 && argc <= 4 && (
+ !strcmp(argv[1], "-regservice") ||
+ !strcmp(argv[1], "-unregservice") ||
+ !strcmp(argv[1], "-addengine") ||
+ !strcmp(argv[1], "-delengine") ||
+ !strcmp(argv[1], "-listengines")))
+ {
+ win32_serviceconfig(argc, argv);
+ exit(0);
+ }
+
+ if (!WSAStartup(MAKEWORD(2,0), &wsaData))
+ fatal("Cannot start the network subsystem");
+}
+#endif
+
/* boot everything */
int main(int argc, char *argv[])
{
int c;
bool did_takeover = false;
char *arg_username = NULL;
+#ifndef WIN32
+ const char *flags = "avhV" "qdRu:";
+#else
+ const char *flags = "avhV";
- /* parse cmdline */
- while ((c = getopt(argc, argv, "avhdVRu:")) != EOF) {
+ win32_startup(argc, argv);
+#endif
+ while ((c = getopt(argc, argv, flags)) != EOF) {
switch (c) {
case 'R':
cf_reboot = 1;
} addr;
socklen_t len = sizeof(addr);
+ if(!(flags & EV_READ)) {
+ log_warning("No EV_READ in pool_accept");
+ return;
+ }
loop:
/* get fd */
fd = safe_accept(sock, &addr.sa, &len);
fatal("setuid() failed to work");
}
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
+{
+ const unsigned char *p = src;
+ if (af != AF_INET)
+ return NULL;
+ snprintf(dst, cnt, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ return dst;
+}
+#endif
+
+
#include "bouncer.h"
+#ifndef WIN32
#include <syslog.h>
+#else
+#include "win32service.h"
+#endif
#include "md5.h"
-static int syslog_started = 0;
static int log_fd = 0;
+static int syslog_started = 0;
struct FacName { const char *name; int code; };
static struct FacName facility_names [] = {
{ "auth", LOG_AUTH },
static void render_time(char *buf, int max)
{
- struct tm tm;
+ struct tm *tm;
struct timeval tv;
gettimeofday(&tv, NULL);
- localtime_r(&tv.tv_sec, &tm);
+ tm = localtime(&tv.tv_sec);
snprintf(buf, max, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
- tm.tm_year, tm.tm_mon, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
(int)(tv.tv_usec / 1000));
}
loop:
/* by manpage, the close() could be interruptable
although it seems that at least in linux it cannot happen */
+#ifndef WIN32
res = close(fd);
+#else
+ /* Pending(this is necessary to wait for FIN of a client.) */
+ log_debug("closesocket(%d)",fd);
+ res = closesocket(fd);
+#endif
if (res < 0 && errno == EINTR)
goto loop;
return res;
static const char *sa2str(const struct sockaddr *sa)
{
static char buf[256];
- if (sa->sa_family == AF_UNIX) {
- struct sockaddr_un *un = (struct sockaddr_un *)sa;
- snprintf(buf, sizeof(buf), "unix:%s", un->sun_path);
- } else if (sa->sa_family == AF_INET) {
+
+ if (sa->sa_family == AF_INET) {
struct sockaddr_in *in = (struct sockaddr_in *)sa;
snprintf(buf, sizeof(buf), "%s:%d", inet_ntoa(in->sin_addr), ntohs(in->sin_port));
+ } if (sa->sa_family == AF_UNIX) {
+ struct sockaddr_un *un = (struct sockaddr_un *)sa;
+ snprintf(buf, sizeof(buf), "unix:%s", un->sun_path);
} else {
snprintf(buf, sizeof(buf), "sa2str: unknown proto");
}
void socket_set_nonblocking(int fd, int val)
{
+#ifndef WIN32
int flags, res;
/* get old flags */
res = fcntl(fd, F_SETFL, flags);
if (res < 0)
fatal_perror("fcntl(F_SETFL)");
+#else
+ ULONG NonBlock = val ? 1 : 0;
+ if (ioctlsocket(fd, FIONBIO, &NonBlock) == SOCKET_ERROR)
+ fatal_perror("ioctlsocket error");
+#endif
}
/* set needed socket options */
int res;
int val;
+#ifndef WIN32
/* close fd on exec */
res = fcntl(sock, F_SETFD, FD_CLOEXEC);
if (res < 0)
fatal_perror("fcntl FD_CLOEXEC");
-
+#else
+ if (!SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0))
+ fatal_perror("SetHandleInformation");
+#endif
/* when no data available, return EAGAIN instead blocking */
socket_set_nonblocking(sock, 1);
unsigned seed = time(NULL) ^ getpid();
char *cstr = NULL;
int numcon = 50;
+#ifdef WIN32
+ WSADATA wsaData;
+#endif
while ((c = getopt(argc, argv, "S:d:n:s:t:hvC:Q:q:")) != EOF) {
switch (c) {
printf(usage_str);
return 1;
}
-
+#ifdef WIN32
+ wsresult = WSAStartup(MAKEWORD(2,0),&wsaData);
+ if (wsresult != 0)
+ {
+ fatal("Cannot start the network subsystem -%d", wsresult);
+ }
+#endif
if (throttle_connects < 0 || throttle_queries < 0 || numcon < 0)
fatal("invalid parameter");
--- /dev/null
+
+# create eventmsg.rc + MSG*.bin
+eventmsg.rc: eventmsg.mc
+ mc.exe eventmsg.mc
+
--- /dev/null
+#ifndef _CONFIG_WIN32_
+#define _CONFIG_WIN32_
+
+#define WIN32_LEAN_AND_MEAN
+
+#include <errno.h>
+#include <stdint.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#define ECONNABORTED WSAECONNABORTED
+#define EINPROGRESS WSAEINPROGRESS
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef EAGAIN
+#define EAGAIN WSAEWOULDBLOCK // WSAEAGAIN
+#define EMSGSIZE WSAEMSGSIZE
+
+#undef errno
+#define errno WSAGetLastError()
+
+/* dummy types / functions */
+#define uid_t int
+#define gid_t int
+#define hstrerror strerror
+#define getuid() (6667)
+#define setsid() getpid()
+#define setgid(x) (-1)
+#define setuid(x) (-1)
+#define fork() (-1)
+#define geteuid() getuid()
+#define setgroups(s, p) (-1)
+
+#define srandom(s) srand(s)
+#define random() rand()
+
+typedef enum
+{
+ LOG_CRIT = -4,
+ LOG_ERR,
+ LOG_WARNING,
+ LOG_INFO,
+ LOG_DEBUG
+} Log_Level;
+
+#define in_addr_t uint32_t
+
+/*
+ * make recvmsg/sendmsg and fd related code compile
+ */
+
+struct iovec {
+ void *iov_base; /* Base address. */
+ size_t iov_len; /* Length. */
+};
+
+struct msghdr {
+ void *msg_name;
+ socklen_t msg_namelen;
+ struct iovec *msg_iov;
+ int msg_iovlen;
+ void *msg_control;
+ socklen_t msg_controllen;
+ int msg_flags;
+};
+
+struct cmsghdr {
+ socklen_t cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+
+#define SCM_RIGHTS 1
+
+#define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
+#define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \
+ & ~(sizeof (size_t) - 1))
+#define CMSG_LEN(len) ((int)(CMSG_ALIGN(sizeof(struct cmsghdr))+(len)))
+#define CMSG_FIRSTHDR(mhdr) \
+ ((mhdr)->msg_controllen >= (int)sizeof(struct cmsghdr) ? \
+ (struct cmsghdr *)(mhdr)->msg_control : \
+ (struct cmsghdr *)NULL)
+#define CMSG_NXTHDR(mhdr, cmsg) \
+ (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \
+ (((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len) \
+ + CMSG_ALIGN(sizeof(struct cmsghdr)) > \
+ (u_char *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \
+ (struct cmsghdr *)NULL : \
+ (struct cmsghdr *)((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len))))
+#define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+CMSG_ALIGN(len))
+
+/* proper signature for setsockopt */
+static inline int w_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
+{
+ return setsockopt(fd, level, optname, optval, optlen);
+}
+#define setsockopt(a,b,c,d,e) w_setsockopt(a,b,c,d,e)
+
+/* gettimeoutday() */
+static inline int win32_gettimeofday(struct timeval * tp, void * tzp)
+{
+ FILETIME file_time;
+ SYSTEMTIME system_time;
+ ULARGE_INTEGER ularge;
+ __int64 epoch = 116444736000000000LL;
+
+ GetSystemTime(&system_time);
+ SystemTimeToFileTime(&system_time, &file_time);
+ ularge.LowPart = file_time.dwLowDateTime;
+ ularge.HighPart = file_time.dwHighDateTime;
+
+ tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
+ tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
+
+ return 0;
+}
+#define gettimeofday win32_gettimeofday
+
+/* make unix socket related code compile */
+struct sockaddr_un {
+ int sun_family;
+ char sun_path[128];
+};
+
+/* getrlimit() */
+#define RLIMIT_NOFILE -1
+struct rlimit {
+ int rlim_cur;
+ int rlim_max;
+};
+static inline int getrlimit(int res, struct rlimit *dst)
+{
+ dst->rlim_cur = dst->rlim_max = -1;
+ return 0;
+}
+
+/* kill is only used to detect if process is running, be always successful */
+static inline int kill(int pid, int sig)
+{
+ return (sig == 0) ? 0 : -1;
+}
+
+/* sendmsg is not used */
+static inline int sendmsg(int s, const struct msghdr *m, int flags)
+{
+ return -1;
+}
+
+/* recvmsg() is, but only with one iov */
+static inline int recvmsg(int s, struct msghdr *m, int flags)
+{
+ if (m->msg_iovlen != 1)
+ return -1;
+ if (m->msg_controllen)
+ m->msg_controllen = 0;
+ return recv(s, m->msg_iov[0].iov_base,
+ m->msg_iov[0].iov_len, flags);
+}
+
+/* dummy getpwnam() */
+struct passwd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+};
+static inline const struct passwd * getpwnam(const char *u) { return NULL; }
+
+#endif /* _CONFIG_WIN32_ */
--- /dev/null
+MessageId=0
+SymbolicName=PGWIN32_EVENTLOG_MSG
+Language=English
+%1
+.
--- /dev/null
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pgbevent.c
+ * Defines the entry point for pgbevent dll.
+ * The DLL defines event source for pgbouncer tools
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define WIN32_LEAN_AND_MEAN
+
+#include <windows.h>
+#include <olectl.h>
+#include <string.h>
+
+#define APP_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\pgbouncer"
+
+/* Global variables */
+HANDLE g_module = NULL; /* hModule of DLL */
+
+/* Prototypes */
+STDAPI DllRegisterServer(void);
+STDAPI DllUnregisterServer(void);
+BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved);
+
+/*
+ * DllRegisterServer --- Instructs DLL to create its registry entries
+ */
+STDAPI DllRegisterServer(void)
+{
+ HKEY key;
+ DWORD data;
+ char buffer[_MAX_PATH];
+
+ /* Set the name of DLL full path name. */
+ if (!GetModuleFileName((HMODULE)g_module, buffer, sizeof(buffer))) {
+ MessageBox(NULL, "Could not retrieve DLL filename", "pgbouncer error", MB_OK | MB_ICONSTOP);
+ return SELFREG_E_TYPELIB;
+ }
+
+ /*
+ * Add our source name as a subkey under the Application key in
+ * the EventLog registry key.
+ */
+ if (RegCreateKey(HKEY_LOCAL_MACHINE, APP_KEY, &key)) {
+ MessageBox(NULL, "Could not create the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP);
+ return SELFREG_E_TYPELIB;
+ }
+
+ /* Add the name to the EventMessageFile subkey. */
+ if (RegSetValueEx(key, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)buffer, strlen(buffer) + 1)) {
+ MessageBox(NULL, "Could not set the event message file.", "pgbouncer error", MB_OK | MB_ICONSTOP);
+ return SELFREG_E_TYPELIB;
+ }
+
+ /* Set the supported event types in the TypesSupported subkey. */
+ data = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
+
+ if (RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD))) {
+ MessageBox(NULL, "Could not set the supported types.", "pgbouncer error", MB_OK | MB_ICONSTOP);
+ return SELFREG_E_TYPELIB;
+ }
+
+ RegCloseKey(key);
+ return S_OK;
+}
+
+/*
+ * DllUnregisterServer --- Instructs DLL to remove only those entries created through DllRegisterServer
+ */
+STDAPI DllUnregisterServer(void)
+{
+ if (RegDeleteKey(HKEY_LOCAL_MACHINE, APP_KEY)) {
+ MessageBox(NULL, "Could not delete the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP);
+ return SELFREG_E_TYPELIB;
+ }
+ return S_OK;
+}
+
+/*
+ * DllMain --- is an optional entry point into a DLL.
+ */
+BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ if (ul_reason_for_call == DLL_PROCESS_ATTACH)
+ g_module = hModule;
+ return TRUE;
+}
+
--- /dev/null
+/*-------------------------------------------------------------------------
+ * win32service.c
+ *
+ * Windows service integration and eventlog
+ *
+ * Copyright (c) 2005, PostgreSQL Global Development Group
+ * Author: Magnus Hagander and Hiroshi Saito
+ *
+ * $Id$
+ *-------------------------------------------------------------------------
+ */
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "bouncer.h"
+
+#include "win32service.h"
+
+/* forward declarations */
+static void WINAPI win32_servicemain(DWORD argc, LPTSTR * argv);
+static void WINAPI win32_servicehandler(DWORD request);
+static void win32_setservicestatus(DWORD state);
+static bool win32_load_child_list(void);
+static HANDLE win32_start_engine(int num);
+
+static void RegisterService(char *servicename);
+static void UnRegisterService(char *servicename);
+static void ListEngines(char *servicename);
+static void AddEngine(char *servicename, char *configfile);
+static void DelEngine(char *servicename, char *configfile);
+
+/* Gobals for service control */
+static SERVICE_STATUS status;
+static SERVICE_STATUS_HANDLE hStatus;
+static HANDLE shutdownEvent;
+
+/* Not used in WIN32_OWN_PROCESS, but has to exist */
+static char *servicename = "pgbouncer";
+
+/* Name of the service as the SCM sees it */
+static char running_servicename[256];
+
+/* Child engines */
+static DWORD childcount;
+static char **children_config_files;
+
+/* Start running as a service */
+void win32_servicestart(void)
+{
+ SERVICE_TABLE_ENTRY st[] = { {servicename, win32_servicemain}, {NULL, NULL} };
+
+ if (StartServiceCtrlDispatcher(st) == 0) {
+ fprintf(stderr, "could not start service control dispatcher: %lu\n", GetLastError());
+ exit(1);
+ }
+}
+
+/*
+ * Entrypoint for actual service work.
+ *
+ * Fork of a normal pgbouncer process with specified commandline. Wait
+ * on them to die (and restart) or the SCM to tell us to shut
+ * down (and stop all engines).
+ */
+static void WINAPI win32_servicemain(DWORD argc, LPTSTR * argv)
+{
+ DWORD ret;
+ HANDLE *waithandles = NULL;
+ DWORD i;
+ DWORD startcount;
+
+ /* fetch our actual service name */
+ safe_strcpy(running_servicename, argv[0], sizeof(running_servicename));
+
+ /* initialize the status structure with static stuff */
+ status.dwWin32ExitCode = 0;
+ status.dwCheckPoint = 0;
+ status.dwWaitHint = 30000;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+ status.dwServiceSpecificExitCode = 0;
+ status.dwCurrentState = SERVICE_START_PENDING;
+
+ /* register control request handler */
+ hStatus = RegisterServiceCtrlHandler(servicename, win32_servicehandler);
+ if (hStatus == 0) {
+ fatal("could not connect to service control handler: %lu", GetLastError());
+ exit(1);
+ }
+
+ /* Tell SCM we are running before we make any API calls */
+ win32_setservicestatus(SERVICE_RUNNING);
+
+ /* create event to handle shutdown */
+ shutdownEvent = CreateEvent(NULL, true, false, NULL);
+ if (shutdownEvent == NULL) {
+ fatal("could not create shutdown event: %lu", GetLastError());
+ exit(1);
+ }
+
+ /* Report we're up and running */
+ log_info("pgbouncer service controller version %s started", PACKAGE_VERSION);
+
+ /* Read our configuration from the registry to determine which
+ enginesto start. Will do it's own error logging. */
+ if (!win32_load_child_list())
+ exit(1);
+
+ /* Set up our array of handles to wait on. First handle is the
+ shutdown handle, then one handle for each child */
+ waithandles = malloc((childcount + 1) * sizeof(HANDLE));
+ if (!waithandles) {
+ fatal("win32_servicemain: out of memory");
+ exit(1);
+ }
+ waithandles[0] = shutdownEvent;
+
+ /* Start the required pgbouncer processes */
+ startcount = 0;
+ for (i = 0; i < childcount; i++) {
+ waithandles[i + 1] = win32_start_engine(i);
+
+ /* If start failed, set to shutdown event to
+ prevent failure in wait code */
+ if (waithandles[i + 1] == INVALID_HANDLE_VALUE)
+ waithandles[i + 1] = shutdownEvent;
+ else
+ startcount++;
+ }
+ log_info("started %i pgbouncer engine(s)", (int)startcount);
+
+ /* Stay in a loop until SCM shuts us down */
+ while (true) {
+ ret = WaitForMultipleObjectsEx(childcount + 1, waithandles, FALSE, INFINITE, FALSE);
+ if (ret == WAIT_FAILED) {
+ fatal("win32_servicemain: could not wait for child handles: %lu", GetLastError());
+ exit(1);
+ }
+ if (ret == WAIT_OBJECT_0) { /* shutdown */
+ win32_setservicestatus(SERVICE_STOP_PENDING);
+ /* Shut down all pgbouncer processes */
+ log_info("received shutdown event, terminating all engines");
+ for (i = 0; i < childcount; i++) {
+ if (waithandles[i + 1] != shutdownEvent && waithandles[i + 1] != INVALID_HANDLE_VALUE) {
+ TerminateProcess(waithandles[i + 1], 0);
+ CloseHandle(waithandles[i + 1]);
+ }
+ }
+ win32_setservicestatus(SERVICE_STOPPED);
+ break;
+ } else if (ret > WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + childcount) {
+ /* a child process died! */
+ int ofs = ret - WAIT_OBJECT_0 - 1;
+
+ log_warning("engine for '%s' terminated, restarting.", children_config_files[ofs]);
+ CloseHandle(waithandles[ofs + 1]);
+ waithandles[ofs + 1] = win32_start_engine(ofs);
+ if (waithandles[ofs + 1] == INVALID_HANDLE_VALUE)
+ waithandles[ofs + 1] = shutdownEvent;
+ }
+ /* else just ignore what happened */
+ }
+}
+
+/* Set the current service status */
+static void win32_setservicestatus(DWORD state)
+{
+ status.dwCurrentState = state;
+ SetServiceStatus(hStatus, (LPSERVICE_STATUS) & status);
+}
+
+/*
+ * Handle any events sent by the service control manager
+ * NOTE! Events are sent on a different thread! And it's
+ * not a pthreads thread, so avoid calling anything that
+ * may use pthreads - like pgbouncer_log()
+ */
+static void WINAPI win32_servicehandler(DWORD request)
+{
+ switch (request) {
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ win32_setservicestatus(SERVICE_STOP_PENDING);
+ SetEvent(shutdownEvent);
+ return;
+ default:
+ break;
+ }
+}
+
+/*
+ * Open the <servicename>\Parameters\Engines registry key, which holds the list
+ * of all the engines associated with this service.
+ */
+static HKEY OpenEnginesKey(DWORD access)
+{
+ char rootkey[1024];
+ HKEY key;
+ int r;
+
+ snprintf(rootkey, sizeof(rootkey),
+ "SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters\\Engines",
+ running_servicename);
+
+ r = RegCreateKeyEx(HKEY_LOCAL_MACHINE, rootkey, 0, NULL, REG_OPTION_NON_VOLATILE, access, NULL, &key, NULL);
+ if (r != ERROR_SUCCESS) {
+ fatal("Failed to open registry key '%s': %d", rootkey, r);
+ return NULL;
+ }
+ return key;
+}
+
+/*
+ * Load the list of pgbouncer engines to start
+ */
+static bool win32_load_child_list(void)
+{
+ char rootkey[1024];
+ HKEY key;
+ char valname[256];
+ char valval[256];
+ DWORD valnamesize = sizeof(valname);
+ DWORD valvalsize = sizeof(valval);
+ DWORD regtype;
+ int r;
+
+ key = OpenEnginesKey(KEY_READ);
+ if (!key)
+ return false;
+
+ childcount = 0;
+ while ((r = RegEnumValue(key, childcount, valname, &valnamesize, NULL, ®type, NULL, NULL)) == ERROR_SUCCESS) {
+ if (regtype != REG_SZ) {
+ fatal("Bad data type in registry key '%s', value '%s': %i", rootkey, valname, (int)regtype);
+ RegCloseKey(key);
+ return false;
+ }
+
+ valnamesize = sizeof(valname);
+ childcount++;
+ }
+ if (r != ERROR_NO_MORE_ITEMS) {
+ fatal("Failed to enumerate registry key '%s': %d", rootkey, r);
+ RegCloseKey(key);
+ return false;
+ }
+
+ children_config_files = malloc(childcount * sizeof(char *));
+ if (!children_config_files) {
+ fatal("Out of memory.");
+ RegCloseKey(key);
+ return false;
+ }
+
+ childcount = 0;
+ valnamesize = sizeof(valname);
+ valvalsize = sizeof(valval);
+ while ((r = RegEnumValue(key, childcount, valname, &valnamesize, NULL,
+ ®type, (unsigned char *)valval, &valvalsize)) == ERROR_SUCCESS) {
+ children_config_files[childcount] = strdup(valval);
+ if (!children_config_files[childcount]) {
+ fatal("Out of memory.");
+ RegCloseKey(key);
+ return false;
+ }
+
+ childcount++;
+ valnamesize = sizeof(valname);
+ valvalsize = sizeof(valval);
+ }
+ if (r != ERROR_NO_MORE_ITEMS) {
+ fatal("Failed to enumerate registry key '%s' a second time: %d", rootkey, r);
+ RegCloseKey(key);
+ return false;
+ }
+ RegCloseKey(key);
+ return true;
+
+}
+
+/* Start engine with config file at offset num in children_config_files */
+static HANDLE win32_start_engine(int num)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ char cmdline[512];
+ static char self_process[512] = { 0 };
+ int r;
+
+ ZeroMemory(&si, sizeof(si));
+ ZeroMemory(&pi, sizeof(pi));
+ si.cb = sizeof(si);
+
+ if (!self_process[0]) {
+ if (!GetModuleFileName(NULL, self_process, sizeof(self_process))) {
+ fatal("Failed to determine own filename: %lu", GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ wsprintf(cmdline, "\"%s\" -subservice \"%s\"", self_process, children_config_files[num]);
+
+ if (!CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
+ log_error("Failed to spawn process for engine at '%s': %lu",
+ children_config_files[num], GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ CloseHandle(pi.hThread);
+
+ /* Give the process five seconds to start up, and see if
+ it's still around. If not, we call it dead on startup. */
+ r = WaitForSingleObject(pi.hProcess, 5000);
+ if (r == WAIT_TIMEOUT) {
+ /* nothing happened, so things seem ok */
+ return pi.hProcess;
+ } else if (r == WAIT_OBJECT_0) {
+ /* process died within one second */
+ log_error("Process for engine at '%s' died on startup.", children_config_files[num]);
+ CloseHandle(pi.hProcess);
+ return INVALID_HANDLE_VALUE;
+ } else {
+ fatal("Failed to wait for newly started process: %lu", GetLastError());
+ TerminateProcess(pi.hProcess, 250);
+ CloseHandle(pi.hProcess);
+ return INVALID_HANDLE_VALUE;
+ }
+ log_info("Started pgbouncer engine for '%s' with pid %lu", children_config_files[num], pi.dwProcessId);
+}
+
+/* Write a log entry to the eventlog */
+static void win32_eventlog(int level, const char *msg)
+{
+ int elevel;
+ static HANDLE evtHandle = INVALID_HANDLE_VALUE;
+
+ switch (level) {
+ case LOG_CRIT:
+ case LOG_ERR:
+ elevel = EVENTLOG_ERROR_TYPE;
+ break;
+ case LOG_WARNING:
+ elevel = EVENTLOG_WARNING_TYPE;
+ break;
+ default:
+ elevel = EVENTLOG_INFORMATION_TYPE;
+ }
+
+ if (evtHandle == INVALID_HANDLE_VALUE) {
+ evtHandle = RegisterEventSource(NULL, "pgbouncer");
+ if (evtHandle == NULL) {
+ evtHandle = INVALID_HANDLE_VALUE;
+ return;
+ }
+ }
+ ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, (const char **)&msg, NULL);
+}
+
+static void usage(int err, char *exe)
+{
+ fprintf(stderr, "Bad usage, see -h for help\n");
+ exit(1);
+}
+
+/* Deal with service and engine registration and unregistration */
+void win32_serviceconfig(int argc, char *const argv[])
+{
+ if (!strcmp(argv[1], "-regservice")) {
+ if (argc != 2 && argc != 3)
+ usage(1, argv[0]);
+ RegisterService((argc == 3) ? argv[2] : "pgbouncer");
+ } else if (!strcmp(argv[1], "-unregservice")) {
+ if (argc != 2 && argc != 3)
+ usage(1, argv[0]);
+ UnRegisterService((argc == 3) ? argv[2] : "pgbouncer");
+ } else if (!strcmp(argv[1], "-listengines")) {
+ if (argc != 2 && argc != 3)
+ usage(1, argv[0]);
+ ListEngines((argc == 3) ? argv[2] : "pgbouncer");
+ } else if (!strcmp(argv[1], "-addengine")) {
+ if (argc != 3 && argc != 4)
+ usage(1, argv[0]);
+ AddEngine((argc == 4) ? argv[2] : "pgbouncer", (argc == 4) ? argv[3] : argv[2]);
+ } else if (!strcmp(argv[1], "-delengine")) {
+ if (argc != 3 && argc != 4)
+ usage(1, argv[0]);
+ DelEngine((argc == 4) ? argv[2] : "pgbouncer", (argc == 4) ? argv[3] : argv[2]);
+ } else
+ usage(1, argv[0]);
+ exit(0);
+}
+
+/* Check windows version against Server 2003 to determine service functionality */
+static bool is_windows2003ornewer(void)
+{
+ OSVERSIONINFO vi;
+
+ vi.dwOSVersionInfoSize = sizeof(vi);
+
+ if (!GetVersionEx(&vi)) {
+ fprintf(stderr, "Failed to determine OS version: %lu\n", GetLastError());
+ exit(1);
+ }
+ if (vi.dwMajorVersion > 5)
+ return true; /* Vista + */
+ if (vi.dwMajorVersion == 5 && vi.dwMinorVersion >= 2)
+ return true; /* Win 2003 */
+ return false;
+}
+
+/* Open Service Control Manager */
+static SC_HANDLE openSCM(void)
+{
+ SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (!manager) {
+ fprintf(stderr, "Failed to open service control manager: %lu\n", GetLastError());
+ exit(1);
+ }
+ return manager;
+}
+
+/* Register a service with the specified name with the local service control manager */
+static void RegisterService(char *servicename)
+{
+ char self[1024];
+ char execpath[1200];
+ SC_HANDLE manager;
+ SC_HANDLE service;
+ char *account = is_windows2003ornewer() ? "NT AUTHORITY\\Local Service" : NULL;
+
+ ZeroMemory(self, sizeof(self));
+
+ if (!GetModuleFileName(NULL, self, sizeof(self))) {
+ fprintf(stderr, "Failed to determine path name: %lu\n", GetLastError());
+ exit(1);
+ }
+ wsprintf(execpath, "%s -service", self);
+
+ manager = openSCM();
+
+ service = CreateService(manager, servicename, servicename, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, execpath, NULL, NULL, "RPCSS\0", account, "");
+ if (!service) {
+ fprintf(stderr, "Failed to create service: %lu\n", GetLastError());
+ exit(1);
+ }
+
+ CloseServiceHandle(service);
+ CloseServiceHandle(manager);
+
+ printf("Service registered.\n");
+ printf("Before you can run pgbouncer, you must also register an engine!\n\n");
+ if (account == NULL) {
+ printf("WARNING! Service is registered to run as Local System. You are\n");
+ printf("encouraged to change this to a low privilege account to increase\n");
+ printf("system security.\n");
+ }
+}
+
+/* Remove a service with the specified name from the local service control manager */
+static void UnRegisterService(char *servicename)
+{
+ SC_HANDLE manager;
+ SC_HANDLE service;
+
+ manager = openSCM();
+
+ service = OpenService(manager, servicename, SC_MANAGER_ALL_ACCESS);
+ if (!service) {
+ fprintf(stderr, "Failed to open service: %lu\n", GetLastError());
+ exit(1);
+ }
+
+ if (!DeleteService(service)) {
+ fprintf(stderr, "Failed to delete service: %lu\n", GetLastError());
+ exit(1);
+ }
+
+ CloseServiceHandle(service);
+ CloseServiceHandle(manager);
+
+ printf("Service removed.\n");
+}
+
+/* Print a list of all engines associated with the specified service */
+static void ListEngines(char *servicename)
+{
+ DWORD i;
+
+ safe_strcpy(running_servicename, servicename, sizeof(running_servicename));
+ if (!win32_load_child_list())
+ exit(1);
+
+ printf("\n%lu engine(s) registered for service '%s'\n", childcount, servicename);
+ for (i = 0; i < childcount; i++) {
+ printf("Engine %lu: %s\n", i + 1, children_config_files[i]);
+ }
+}
+
+/*
+ * Verify that a file exists, and also expand the filename to
+ * an absolute path.
+ */
+static char _vfe_buf[UNIX_PATH_MAX];
+static char *VerifyFileExists(char *filename)
+{
+ DWORD r;
+
+ ZeroMemory(_vfe_buf, sizeof(_vfe_buf));
+ r = GetFullPathName(filename, sizeof(_vfe_buf), _vfe_buf, NULL);
+ if (r == 0 || r > sizeof(_vfe_buf)) {
+ fprintf(stderr, "Failed to get full pathname for '%s': %lu\n", filename, GetLastError());
+ exit(1);
+ }
+
+ if (GetFileAttributes(_vfe_buf) == 0xFFFFFFFF) {
+ fprintf(stderr, "File '%s' could not be opened: %lu\n", _vfe_buf, GetLastError());
+ exit(1);
+ }
+
+ return _vfe_buf;
+}
+
+/* Register a new engine with a specific config file with the specified service */
+static void AddEngine(char *servicename, char *configfile)
+{
+ HKEY key;
+ int r;
+
+ char *full_configfile = VerifyFileExists(configfile);
+
+ safe_strcpy(running_servicename, servicename, sizeof(running_servicename));
+ key = OpenEnginesKey(KEY_ALL_ACCESS);
+ if (!key)
+ exit(1);
+
+ r = RegQueryValueEx(key, full_configfile, 0, NULL, NULL, NULL);
+ if (r == 0) {
+ fprintf(stderr, "Engine '%s' already registered for service '%s'.\n", full_configfile, servicename);
+ exit(1);
+ }
+
+ r = RegSetValueEx(key, full_configfile, 0, REG_SZ,
+ (unsigned char *)full_configfile, strlen(full_configfile) + 1);
+ RegCloseKey(key);
+ if (r == ERROR_SUCCESS) {
+ printf("Engine added.\n");
+ printf("NOTE! You need to restart the pgbouncer service before this takes effect.\n");
+ return;
+ } else
+ fprintf(stderr, "Failed to register engine: %d.\n", r);
+ exit(1);
+}
+
+/* Remove an engine registration from the specified service */
+static void DelEngine(char *servicename, char *configfile)
+{
+ HKEY key;
+ int r;
+
+ safe_strcpy(running_servicename, servicename, sizeof(running_servicename));
+ key = OpenEnginesKey(KEY_ALL_ACCESS);
+ if (!key)
+ exit(1);
+
+ r = RegDeleteValue(key, configfile);
+ RegCloseKey(key);
+ if (r == ERROR_SUCCESS) {
+ printf("Engine removed.\n");
+ printf("NOTE! You need to restart the pgbouncer service before this takes effect.\n");
+ return;
+ } else if (r == 2) {
+ fprintf(stderr, "Engine '%s' not registered for service '%s'.\n", configfile, servicename);
+ } else
+ fprintf(stderr, "Failed to unregister engine: %d\n", r);
+ exit(1);
+}
+
+void openlog(const char *ident, int option, int facility)
+{
+}
+
+void syslog(int prio, const char *fmt, ...)
+{
+ char buf[1024];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ win32_eventlog(prio, buf);
+}
+
+void closelog(void)
+{
+}
--- /dev/null
+#ifndef WIN32SERVICE
+#define WIN32SERVICE
+/*-------------------------------------------------------------------------
+ * win32service.h
+ *
+ * Windows service definitions
+ *
+ * Copyright (c) 2008, PostgreSQL Global Development Group
+ *
+ * $Id$
+ *-------------------------------------------------------------------------
+ */
+
+void win32_servicestart(void);
+void win32_serviceconfig(int argc, char *const argv[]);
+
+#define openlog w_openlog
+#define syslog w_syslog
+#define closelog w_closelog
+
+#define LOG_EMERG 0
+#define LOG_ALERT 1
+#define LOG_CRIT 2
+#define LOG_ERR 3
+#define LOG_WARNING 4
+#define LOG_NOTICE 5
+#define LOG_INFO 6
+#define LOG_DEBUG 7
+
+#define LOG_PID 0
+
+#define LOG_KERN 0
+#define LOG_USER 0
+#define LOG_MAIL 0
+#define LOG_DAEMON 0
+#define LOG_AUTH 0
+#define LOG_SYSLOG 0
+#define LOG_LPR 0
+#define LOG_NEWS 0
+#define LOG_UUCP 0
+#define LOG_CRON 0
+#define LOG_AUTHPRIV 0
+#define LOG_FTP 0
+#define LOG_LOCAL0 0
+#define LOG_LOCAL1 0
+#define LOG_LOCAL2 0
+#define LOG_LOCAL3 0
+#define LOG_LOCAL4 0
+#define LOG_LOCAL5 0
+#define LOG_LOCAL6 0
+#define LOG_LOCAL7 0
+
+
+void openlog(const char *ident, int option, int facility);
+void syslog(int priority, const char *format, ...);
+void closelog(void);
+
+#endif