]> granicus.if.org Git - pgbouncer/commitdiff
win32 support (wip)
authorMarko Kreen <markokr@gmail.com>
Mon, 24 Nov 2008 11:31:46 +0000 (11:31 +0000)
committerMarko Kreen <markokr@gmail.com>
Mon, 24 Nov 2008 11:31:46 +0000 (11:31 +0000)
Original patch by Hiroshi Saito, applied with some cleanup

20 files changed:
Makefile
config.mak.in
configure.ac
doc/config.txt
doc/usage.txt
include/system.h
src/loader.c
src/main.c
src/pooler.c
src/system.c
src/util.c
test/asynctest.c
win32/MSG00001.bin [new file with mode: 0644]
win32/Makefile [new file with mode: 0644]
win32/compat_win32.h [new file with mode: 0644]
win32/eventmsg.mc [new file with mode: 0644]
win32/eventmsg.rc [new file with mode: 0644]
win32/pgbevent.c [new file with mode: 0644]
win32/win32service.c [new file with mode: 0644]
win32/win32service.h [new file with mode: 0644]

index 8091f5cc19b70e589b3d02a60424c964f20e7b09..5aacb6cf4876bc538a08b2a1865c329a58f681e5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,8 @@ DATA = README NEWS AUTHORS etc/pgbouncer.ini etc/userlist.txt Makefile \
        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
@@ -31,7 +32,8 @@ hdrs = $(addprefix $(srcdir)/include/, $(HDRS))
 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
 
@@ -43,6 +45,23 @@ ifeq ($(enable_debug),yes)
 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
@@ -57,10 +76,10 @@ endif
 ## 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)
 
@@ -70,12 +89,20 @@ $(builddir)/lib/%.o: $(srcdir)/src/%.c $(builddir)/config.mak $(hdrs)
        $(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)
@@ -98,7 +125,7 @@ deb: configure
 
 # 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
@@ -157,3 +184,26 @@ $(builddir)/lib/%.s: $(srcdir)/src/%.c config.mak $(hdrs)
 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)
+
index e8e4fb020659f8b0fa77238562ab76b5f7eb7be2..37fb1c9b11ee188610399bb0c1469cfed9c5112a 100644 (file)
@@ -1,6 +1,8 @@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PACKAGE_TARNAME = @PACKAGE_TARNAME@
 
+PORTNAME = @PORTNAME@
+
 DEFS = @DEFS@
 LIBS = @LIBS@
 CC = @CC@
@@ -11,6 +13,11 @@ LDFLAGS = @LDFLAGS@
 
 XMLTO = @XMLTO@
 ASCIIDOC = @ASCIIDOC@
+MC = @MC@
+DLLWRAP = @DLLWRAP@
+DLLTOOL = @DLLTOOL@
+WINDRES = @WINDRES@
+STRIP = @STRIP@
 
 prefix = @prefix@
 exec_prefix = @exec_prefix@
index 515237200acd6666eac92d67ee84013f7a992a68..925bcd39af33725c0c400c6fbd7c3f60ee7cb26f 100644 (file)
@@ -5,6 +5,21 @@ AC_CONFIG_SRCDIR(src/janitor.c)
 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
@@ -54,6 +69,15 @@ if test -n "$ASCIIDOC"; then
   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])
@@ -76,6 +100,8 @@ fi
 
 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], [], [], [
@@ -113,7 +139,8 @@ AC_SEARCH_LIBS(clock_gettime, rt)
 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])
index 7eba7e3a2d335aeb6274f802091348d031d21810..aa8f81e4a2ea5d29a9a0dc6423e2263d9400c4f3 100644 (file)
@@ -27,6 +27,7 @@ appear later in the line.
 
 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.
 
@@ -54,6 +55,7 @@ Default: 6432
 
 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+
 
@@ -61,6 +63,7 @@ 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
 
@@ -125,6 +128,7 @@ The theoretical maximum should be never reached, unless somebody deliberately
 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
 
@@ -160,6 +164,7 @@ Default: empty
 ==== syslog ====
 
 Toggles syslog on/off
+As for windows environment, eventlog is used for substitution.
 
 Default: 0
 
index 9d5104a459cb6fe4935a101e0c05bc3bb92843bb..28ef7f1d46557801b31dcb07193dc1345c700db2 100644 (file)
@@ -9,6 +9,20 @@ pgbouncer - Lightweight connection pooler for PostgreSQL.
   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
@@ -97,11 +111,14 @@ Basic setup and usage as following.
 
 -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.
@@ -350,6 +367,7 @@ Shows list of fds in use. When the connected user has username
 "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.
index 24c75a9f35ac1eb68363ac4cdc047cd62cbe5bf6..993013397f59a1475ef9869142b57df4aa2e9779 100644 (file)
 #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>
@@ -165,9 +184,14 @@ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p) _MUSTCHECK;
 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);
 
-
index 20147bc12cb1a22afa165bf32aa1854a92e739a4..9f95f24840a78945c58bdc43653cb4ac29f0efc1 100644 (file)
@@ -22,7 +22,9 @@
 
 #include "bouncer.h"
 
+#ifdef HAVE_NETDB_H
 #include <netdb.h>
+#endif
 
 /*
  * ConnString parsing
index 7e31b3b6cd554371a4f449bf9fa221e10d77b493..293333d1bd325d7f535979aa5c4ba6e8e628b283 100644 (file)
 
 #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);
@@ -35,13 +37,24 @@ static bool set_defer_accept(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)
 {
@@ -66,7 +79,11 @@ static char *cf_config_file;
 
 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;
 
@@ -133,7 +150,9 @@ ConfElem bouncer_params[] = {
 {"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}},
@@ -141,7 +160,9 @@ ConfElem bouncer_params[] = {
 {"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},
 
@@ -302,9 +323,6 @@ void load_config(bool reload)
  */
 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)
 {
@@ -324,6 +342,12 @@ static void handle_sigint(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) {
@@ -362,10 +386,13 @@ static void handle_sighup(int sock, short flags, void *arg)
        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 */
@@ -377,16 +404,6 @@ static void signal_setup(void)
 
        /* 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)
@@ -401,6 +418,16 @@ static void signal_setup(void)
        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");
 }
 
 /*
@@ -444,7 +471,6 @@ static void go_daemon(void)
                fatal_perror("fork");
        if (pid > 0)
                _exit(0);
-
 }
 
 /*
@@ -620,15 +646,53 @@ static void takeover_part1(void)
        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;
index 3db572832f4b3b922e09c38e0b88a836611ed666..722ccf80417f960369e269d6910ee9878c0c42da 100644 (file)
@@ -249,6 +249,10 @@ static void pool_accept(int sock, short flags, void *is_unix)
        } 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);
index d9a815932273369df70608132fc8f90b7c5932c4..b5a05f7ad3cec6d459118f0a9b30babaa41f7ebd 100644 (file)
@@ -133,3 +133,15 @@ void change_user(const char *user)
                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
+
+
index 0ba6e56d75682fc4b9d7a8b063c462ab893fe400..c6de1a6c92b1d815a98f57e5a68de7f82a7a1936 100644 (file)
 
 #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 },
@@ -63,13 +67,13 @@ void *zmalloc(size_t len)
 
 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));
 }
 
@@ -297,7 +301,13 @@ int safe_close(int fd)
 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;
@@ -349,12 +359,13 @@ loop:
 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");
        }
@@ -501,6 +512,7 @@ void reset_time_cache(void)
 
 void socket_set_nonblocking(int fd, int val)
 {
+#ifndef WIN32
        int flags, res;
 
        /* get old flags */
@@ -518,6 +530,11 @@ void socket_set_nonblocking(int fd, int val)
        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 */
@@ -526,11 +543,15 @@ void tune_socket(int sock, bool is_unix)
        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);
 
index ae74c28123ab5740d82f02b69bc379efd326042b..794218dbc51574622e8c93730404021f2fe63a71 100644 (file)
@@ -512,6 +512,9 @@ int main(int argc, char *argv[])
        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) {
@@ -559,7 +562,13 @@ int main(int argc, char *argv[])
                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");
 
diff --git a/win32/MSG00001.bin b/win32/MSG00001.bin
new file mode 100644 (file)
index 0000000..6ac08e5
Binary files /dev/null and b/win32/MSG00001.bin differ
diff --git a/win32/Makefile b/win32/Makefile
new file mode 100644 (file)
index 0000000..cadde74
--- /dev/null
@@ -0,0 +1,5 @@
+
+# create eventmsg.rc + MSG*.bin
+eventmsg.rc: eventmsg.mc
+       mc.exe eventmsg.mc
+
diff --git a/win32/compat_win32.h b/win32/compat_win32.h
new file mode 100644 (file)
index 0000000..ca469c3
--- /dev/null
@@ -0,0 +1,169 @@
+#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_ */
diff --git a/win32/eventmsg.mc b/win32/eventmsg.mc
new file mode 100644 (file)
index 0000000..5d16e3e
--- /dev/null
@@ -0,0 +1,5 @@
+MessageId=0
+SymbolicName=PGWIN32_EVENTLOG_MSG
+Language=English
+%1
+.
diff --git a/win32/eventmsg.rc b/win32/eventmsg.rc
new file mode 100644 (file)
index 0000000..0885a89
--- /dev/null
@@ -0,0 +1,2 @@
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
diff --git a/win32/pgbevent.c b/win32/pgbevent.c
new file mode 100644 (file)
index 0000000..123e245
--- /dev/null
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
+
diff --git a/win32/win32service.c b/win32/win32service.c
new file mode 100644 (file)
index 0000000..f375bb6
--- /dev/null
@@ -0,0 +1,598 @@
+/*-------------------------------------------------------------------------
+ * 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, &regtype, 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,
+                                &regtype, (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)
+{
+}
diff --git a/win32/win32service.h b/win32/win32service.h
new file mode 100644 (file)
index 0000000..05c1242
--- /dev/null
@@ -0,0 +1,58 @@
+#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