pgpool2.git / summary / log / commit / refs
commit 24aaeb6ec00669b78a82f03a24e12336961ca305
Author: Muhammad Usama <m.usama@gmail.com>
Date: Fri Apr 04 15:09:40 2014 +0000
Merge branch 'master' into EXCEPTION_MGR
Conflicts:
src/config/pool_config.c
src/libs/pcp/Makefile.am
src/libs/pcp/Makefile.in
src/libs/pcp/pcp.c
src/main/main.c
src/protocol/child.c
src/protocol/pool_process_query.c
src/utils/pcp/pcp_stream.c
doc/pgpool-en.html | 29 +++++-
doc/pgpool-ja.html | 32 ++++++-
pgpool.spec | 2 +-
src/config/pool_config.c | 44 +++++----
src/config/pool_config.l | 44 +++++----
src/context/pool_query_context.c | 24 ++++-
src/include/context/pool_query_context.h | 1 +
src/include/protocol/pool_proto_modules.h | 3 +
src/libs/pcp/Makefile.am | 3 +-
src/libs/pcp/Makefile.in | 13 ++-
src/libs/pcp/pcp.c | 19 ++--
src/main/pgpool_main.c | 5 +
src/protocol/child.c | 22 ++++-
src/protocol/pool_proto_modules.c | 102 +++++++++++++++++++++
src/rewrite/pool_timestamp.c | 48 +++++++---
src/streaming_replication/pool_worker_child.c | 6 ++
src/test/regression/tests/005.jdbc/PgpoolTest.java | 2 +-
.../tests/005.jdbc/PrepareThresholdTest.java | 74 +++++++++++++++
src/test/regression/tests/005.jdbc/RunTest.java | 3 +
src/test/regression/tests/005.jdbc/expected.txt | 2 +
.../tests/005.jdbc/expected/PrepareThreshold | 1 +
src/test/regression/tests/005.jdbc/prepare.sql | 11 ++-
src/test/regression/tests/005.jdbc/test.sh | 7 +-
src/utils/pcp/pcp_stream.c | 2 +-
src/utils/pool_select_walker.c | 6 +-
src/watchdog/wd_if.c | 12 ++-
src/watchdog/wd_ping.c | 2 +-
27 files changed, 436 insertions(+), 83 deletions(-)
diff --cc src/context/pool_query_context.c
index b9179e6c6,47b872063..caa4d3aea
--- a/src/context/pool_query_context.c
+++ b/src/context/pool_query_context.c
@@@ -172,6 -172,14 +173,14 @@@ void pool_clear_node_to_be_sent(POOL_QU
void pool_setall_node_to_be_sent(POOL_QUERY_CONTEXT *query_context)
{
int i;
+ POOL_SESSION_CONTEXT *sc;
+
- sc = pool_get_session_context();
++ sc = pool_get_session_context(true);
+ if (!sc)
+ {
+ pool_error("pool_setall_node_to_be_sent: no session context");
+ return;
+ }
if (!query_context)
{
diff --cc src/libs/pcp/Makefile.am
index 56dff4b55,faa930b24..aaa1b2c87
--- a/src/libs/pcp/Makefile.am
+++ b/src/libs/pcp/Makefile.am
@@@ -1,10 -1,8 +1,11 @@@
-AM_CPPFLAGS = -D_GNU_SOURCE -I @PGSQL_INCLUDE_DIR@
+AM_CPPFLAGS = -D_GNU_SOURCE -DPOOL_PRIVATE -I @PGSQL_INCLUDE_DIR@
lib_LTLIBRARIES = libpcp.la
-libpcp_la_SOURCES = pcp.c pcp_stream.c pcp_error.c $(top_srcdir)/src/auth/md5.c \
- $(top_srcdir)/src/utils/strlcpy.c
+libpcp_la_SOURCES = pcp.c $(top_srcdir)/src/utils/pcp/pcp_stream.c \
+ $(top_srcdir)/src/utils/pcp/pcp_error.c \
+ $(top_srcdir)/src/utils/pcp/pcp_timeout.c \
- $(top_srcdir)/src/auth/md5.c
++ $(top_srcdir)/src/auth/md5.c \
++ $(top_srcdir)/src/utils/strlcpy.c
include_HEADERS = $(top_srcdir)/src/include/pcp/pcp.h \
$(top_srcdir)/src/include/pcp/libpcp_ext.h \
diff --cc src/libs/pcp/Makefile.in
index 1863ab0fe,88d80ce0e..b836dafda
--- a/src/libs/pcp/Makefile.in
+++ b/src/libs/pcp/Makefile.in
@@@ -127,25 -76,9 +127,25 @@@ am__uninstall_files_from_dir = {
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libpcp_la_LIBADD =
-am_libpcp_la_OBJECTS = pcp.lo pcp_stream.lo pcp_error.lo md5.lo \
- strlcpy.lo
+am_libpcp_la_OBJECTS = pcp.lo pcp_stream.lo pcp_error.lo \
- pcp_timeout.lo md5.lo
++ pcp_timeout.lo md5.lo strlcpy.lo
libpcp_la_OBJECTS = $(am_libpcp_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src/include
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@@ -322,12 -223,10 +322,13 @@@ target_alias = @target_alias
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-AM_CPPFLAGS = -D_GNU_SOURCE -I @PGSQL_INCLUDE_DIR@
+AM_CPPFLAGS = -D_GNU_SOURCE -DPOOL_PRIVATE -I @PGSQL_INCLUDE_DIR@
lib_LTLIBRARIES = libpcp.la
-libpcp_la_SOURCES = pcp.c pcp_stream.c pcp_error.c $(top_srcdir)/src/auth/md5.c \
- $(top_srcdir)/src/utils/strlcpy.c
+libpcp_la_SOURCES = pcp.c $(top_srcdir)/src/utils/pcp/pcp_stream.c \
+ $(top_srcdir)/src/utils/pcp/pcp_error.c \
+ $(top_srcdir)/src/utils/pcp/pcp_timeout.c \
- $(top_srcdir)/src/auth/md5.c
++ $(top_srcdir)/src/auth/md5.c \
++ $(top_srcdir)/src/utils/strlcpy.c
include_HEADERS = $(top_srcdir)/src/include/pcp/pcp.h \
$(top_srcdir)/src/include/pcp/libpcp_ext.h \
@@@ -417,57 -311,43 +418,65 @@@ distclean-compile
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_error.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_stream.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_timeout.Plo@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+pcp_stream.lo: $(top_srcdir)/src/utils/pcp/pcp_stream.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pcp_stream.lo -MD -MP -MF $(DEPDIR)/pcp_stream.Tpo -c -o pcp_stream.lo `test -f '$(top_srcdir)/src/utils/pcp/pcp_stream.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/pcp/pcp_stream.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/pcp_stream.Tpo $(DEPDIR)/pcp_stream.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/utils/pcp/pcp_stream.c' object='pcp_stream.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pcp_stream.lo `test -f '$(top_srcdir)/src/utils/pcp/pcp_stream.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/pcp/pcp_stream.c
+
+pcp_error.lo: $(top_srcdir)/src/utils/pcp/pcp_error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pcp_error.lo -MD -MP -MF $(DEPDIR)/pcp_error.Tpo -c -o pcp_error.lo `test -f '$(top_srcdir)/src/utils/pcp/pcp_error.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/pcp/pcp_error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/pcp_error.Tpo $(DEPDIR)/pcp_error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/utils/pcp/pcp_error.c' object='pcp_error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pcp_error.lo `test -f '$(top_srcdir)/src/utils/pcp/pcp_error.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/pcp/pcp_error.c
+
+pcp_timeout.lo: $(top_srcdir)/src/utils/pcp/pcp_timeout.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pcp_timeout.lo -MD -MP -MF $(DEPDIR)/pcp_timeout.Tpo -c -o pcp_timeout.lo `test -f '$(top_srcdir)/src/utils/pcp/pcp_timeout.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/pcp/pcp_timeout.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/pcp_timeout.Tpo $(DEPDIR)/pcp_timeout.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/utils/pcp/pcp_timeout.c' object='pcp_timeout.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pcp_timeout.lo `test -f '$(top_srcdir)/src/utils/pcp/pcp_timeout.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/pcp/pcp_timeout.c
md5.lo: $(top_srcdir)/src/auth/md5.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT md5.lo -MD -MP -MF $(DEPDIR)/md5.Tpo -c -o md5.lo `test -f '$(top_srcdir)/src/auth/md5.c' || echo '$(srcdir)/'`$(top_srcdir)/src/auth/md5.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/md5.Tpo $(DEPDIR)/md5.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$(top_srcdir)/src/auth/md5.c' object='md5.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT md5.lo -MD -MP -MF $(DEPDIR)/md5.Tpo -c -o md5.lo `test -f '$(top_srcdir)/src/auth/md5.c' || echo '$(srcdir)/'`$(top_srcdir)/src/auth/md5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/md5.Tpo $(DEPDIR)/md5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/auth/md5.c' object='md5.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o md5.lo `test -f '$(top_srcdir)/src/auth/md5.c' || echo '$(srcdir)/'`$(top_srcdir)/src/auth/md5.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o md5.lo `test -f '$(top_srcdir)/src/auth/md5.c' || echo '$(srcdir)/'`$(top_srcdir)/src/auth/md5.c
+ strlcpy.lo: $(top_srcdir)/src/utils/strlcpy.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT strlcpy.lo -MD -MP -MF $(DEPDIR)/strlcpy.Tpo -c -o strlcpy.lo `test -f '$(top_srcdir)/src/utils/strlcpy.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/strlcpy.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/strlcpy.Tpo $(DEPDIR)/strlcpy.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$(top_srcdir)/src/utils/strlcpy.c' object='strlcpy.lo' libtool=yes @AMDEPBACKSLASH@
++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT strlcpy.lo -MD -MP -MF $(DEPDIR)/strlcpy.Tpo -c -o strlcpy.lo `test -f '$(top_srcdir)/src/utils/strlcpy.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/strlcpy.c
++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/strlcpy.Tpo $(DEPDIR)/strlcpy.Plo
++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/utils/strlcpy.c' object='strlcpy.lo' libtool=yes @AMDEPBACKSLASH@
+ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o strlcpy.lo `test -f '$(top_srcdir)/src/utils/strlcpy.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/strlcpy.c
++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o strlcpy.lo `test -f '$(top_srcdir)/src/utils/strlcpy.c' || echo '$(srcdir)/'`$(top_srcdir)/src/utils/strlcpy.c
+
mostlyclean-libtool:
-rm -f *.lo
diff --cc src/libs/pcp/pcp.c
index 37d04e1b7,165466bf7..02e82c4fd
--- a/src/libs/pcp/pcp.c
+++ b/src/libs/pcp/pcp.c
@@@ -240,9 -247,17 +240,12 @@@ pcp_authorize(char *username, char *pas
if (pcp_read(pc, &rsize, sizeof(int)))
return -1;
rsize = ntohl(rsize);
- buf = (char *)malloc(rsize);
- if (buf == NULL)
- {
- errorcode = NOMEMERR;
- return -1;
- }
+ buf = (char *)palloc(rsize);
if (pcp_read(pc, buf, rsize - sizeof(int)))
+ {
+ free(buf);
return -1;
+ }
if (debug) fprintf(stderr, "DEBUG: recv: tos=\"%c\", len=%d, data=%s\n", tos, rsize, buf);
if (tos == 'e')
@@@ -1011,11 -1068,14 +1014,13 @@@ free_systemdb_info(SystemDBInfo * si
{
int i, j;
+ if (si == NULL)
+ return;
-
- free(si->hostname);
- free(si->user);
- free(si->password);
- free(si->schema_name);
- free(si->database_name);
+ pfree(si->hostname);
+ pfree(si->user);
+ pfree(si->password);
+ pfree(si->schema_name);
+ pfree(si->database_name);
if (si->dist_def_slot != NULL)
{
diff --cc src/main/pgpool_main.c
index 812caf6bf,000000000..4300fae44
mode 100644,000000..100644
--- a/src/main/pgpool_main.c
+++ b/src/main/pgpool_main.c
@@@ -1,2516 -1,0 +1,2521 @@@
+/* -*-pgpool_main-c-*- */
+/*
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2014 PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ */
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <signal.h>
+
+#include <libgen.h>
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "pool.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+#include "pool_config.h"
+#include "context/pool_process_context.h"
+#include "version.h"
+#include "parser/pool_string.h"
+#include "auth/pool_passwd.h"
+#include "query_cache/pool_memqcache.h"
+#include "watchdog/wd_ext.h"
+
+/*
+ * Process pending signal actions.
+ */
+#define CHECK_REQUEST \
+ do { \
+ if (wakeup_request) \
+ { \
+ wakeup_children(); \
+ wakeup_request = 0; \
+ } \
+ if (failover_request) \
+ { \
+ failover(); \
+ failover_request = 0; \
+ } \
+ if (sigchld_request) \
+ { \
+ reaper(); \
+ } \
+ if (reload_config_request) \
+ { \
+ reload_config(); \
+ reload_config_request = 0; \
+ } \
+ } while (0)
+
+#define CLEAR_ALARM \
+ do { \
+ pool_debug("health check: clearing alarm"); \
+ } while (alarm(0) > 0)
+
+
+#define PGPOOLMAXLITSENQUEUELENGTH 10000
+
+static int process_backend_health_check_failure(int health_check_node_id, int retrycnt);
+static bool do_health_check(bool use_template_db, volatile int *health_check_node_id);
+
+static void FileUnlink(int code, Datum path);
+static int write_status_file();
+static pid_t pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file);
+static pid_t fork_a_child(int unix_fd, int inet_fd, int id);
+static pid_t worker_fork_a_child(void);
+static int create_unix_domain_socket(struct sockaddr_un un_addr_tmp);
+static int create_inet_domain_socket(const char *hostname, const int port);
+static void failover(void);
+static void reaper(void);
+static void wakeup_children(void);
+static void reload_config(void);
+static int pool_pause(struct timeval *timeout);
+static void kill_all_children(int sig);
+static int get_next_master_node(void);
+static pid_t fork_follow_child(int old_master, int new_primary, int old_primary);
+static int read_status_file(bool discard_status);
+static RETSIGTYPE exit_handler(int sig);
+static RETSIGTYPE reap_handler(int sig);
+static RETSIGTYPE failover_handler(int sig);
+static RETSIGTYPE reload_config_handler(int sig);
+static RETSIGTYPE health_check_timer_handler(int sig);
+static RETSIGTYPE wakeup_handler(int sig);
+
+static void initialize_shared_mem_objects(void);
+static int trigger_failover_command(int node, const char *command_line,
+ int old_master, int new_master, int old_primary);
+static int find_primary_node(void);
+static int find_primary_node_repeatedly(void);
+static void terminate_all_childrens();
+static void system_will_go_down(int code, Datum arg);
+
+
+static struct sockaddr_un un_addr; /* unix domain socket path */
+static struct sockaddr_un pcp_un_addr; /* unix domain socket path for PCP */
+ProcessInfo *process_info = NULL; /* Per child info table on shmem */
+
+/*
+ * Private copy of backend status
+ */
+BACKEND_STATUS private_backend_status[MAX_NUM_BACKENDS];
+
+/*
+ * shmem connection info table
+ * this is a three dimension array. i.e.:
+ * con_info[pool_config->num_init_children][pool_config->max_pool][MAX_NUM_BACKENDS]
+ */
+ConnectionInfo *con_info;
+
+static int unix_fd; /* unix domain socket fd */
+static int inet_fd; /* inet domain socket fd */
+
+static int pcp_unix_fd; /* unix domain socket fd for PCP (not used) */
+static int pcp_inet_fd; /* inet domain socket fd for PCP */
+extern char pcp_conf_file[POOLMAXPATHLEN+1]; /* path for pcp.conf */
+extern char conf_file[POOLMAXPATHLEN+1];
+extern char hba_file[POOLMAXPATHLEN+1];
+
+static int exiting = 0; /* non 0 if I'm exiting */
+static int switching = 0; /* non 0 if I'm fail overing or degenerating */
+
+POOL_REQUEST_INFO *Req_info; /* request info area in shared memory */
+volatile sig_atomic_t *InRecovery; /* non 0 if recovery is started */
+volatile sig_atomic_t reload_config_request = 0;
+static volatile sig_atomic_t failover_request = 0;
+static volatile sig_atomic_t sigchld_request = 0;
+static volatile sig_atomic_t wakeup_request = 0;
+
+static int pipe_fds[2]; /* for delivering signals */
+
+int my_proc_id;
+
+static BackendStatusRecord backend_rec; /* Backend status record */
+
+static pid_t worker_pid = -1; /* pid of worker process */
+static pid_t follow_pid = -1; /* pid for child process handling follow command */
+static pid_t pcp_pid = -1; /* pid for child process handling PCP */
+
+BACKEND_STATUS* my_backend_status[MAX_NUM_BACKENDS]; /* Backend status buffer */
+int my_master_node_id; /* Master node id buffer */
+
+/*
+* pgpool main program
+*/
+
+int PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
+{
+ int i;
+ volatile int health_check_node_id = 0;
+ volatile bool use_template_db = false;
+ volatile int retrycnt;
+ volatile int sys_retrycnt;
+
+ MemoryContext MainLoopMemoryContext;
+ sigjmp_buf local_sigjmp_buf;
+
+ /* Set the process type variable */
+ processType = PT_MAIN;
+ processState = INITIALIZING;
+
+ /*
+ * Restore previous backend status if possible
+ */
+ read_status_file(discard_status);
+
+ /*
+ * install the call back for preparation of system exit
+ */
+ on_system_exit(system_will_go_down, NULL);
+
+ /* set unix domain socket path for connections to pgpool */
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/.s.PGSQL.%d",
+ pool_config->socket_dir,
+ pool_config->port);
+ /* set unix domain socket path for pgpool PCP communication */
+ snprintf(pcp_un_addr.sun_path, sizeof(pcp_un_addr.sun_path), "%s/.s.PGSQL.%d",
+ pool_config->pcp_socket_dir,
+ pool_config->pcp_port);
+
+ /* set up signal handlers */
+ pool_signal(SIGPIPE, SIG_IGN);
+
+ /* create unix domain socket */
+ unix_fd = create_unix_domain_socket(un_addr);
+ on_proc_exit(FileUnlink, (Datum) un_addr.sun_path);
+
+ /* create inet domain socket if any */
+ if (pool_config->listen_addresses[0])
+ inet_fd = create_inet_domain_socket(pool_config->listen_addresses, pool_config->port);
+
+ initialize_shared_mem_objects();
+
+ /* start watchdog */
+ if (pool_config->use_watchdog )
+ {
+ if (!wd_main(1))
+ {
+ ereport(FATAL,
+ (errmsg("wd_main error")));
+ }
+ }
+
+ /*
+ * We need to block signal here. Otherwise child might send some
+ * signals, for example SIGUSR1(fail over). Children will inherit
+ * signal blocking but they do unblock signals at the very beginning
+ * of process. So this is harmless.
+ */
+ POOL_SETMASK(&BlockSig);
+ /* fork the children */
+ for (i=0;i<pool_config->num_init_children;i++)
+ {
+ process_info[i].pid = fork_a_child(unix_fd, inet_fd, i);
+ process_info[i].start_time = time(NULL);
+ }
+
+ /* set up signal handlers */
+
+ pool_signal(SIGTERM, exit_handler);
+ pool_signal(SIGINT, exit_handler);
+ pool_signal(SIGQUIT, exit_handler);
+ pool_signal(SIGCHLD, reap_handler);
+ pool_signal(SIGUSR1, failover_handler);
+ pool_signal(SIGUSR2, wakeup_handler);
+ pool_signal(SIGHUP, reload_config_handler);
+
+ /* create pipe for delivering event */
+ if (pipe(pipe_fds) < 0)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create pipe")));
+ }
+
+ MemoryContextSwitchTo(TopMemoryContext);
+
+ /* Create per loop iteration memory context */
+ MainLoopMemoryContext = AllocSetContextCreate(TopMemoryContext,
+ "pgpool_main_loop",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ ereport(LOG,
+ (errmsg("%s successfully started. version %s (%s)", PACKAGE, VERSION, PGPOOLVERSION)));
+
+ /* fork a child for PCP handling */
+ pcp_unix_fd = create_unix_domain_socket(pcp_un_addr);
+ /* Add onproc exit to clean up the unix domain socket at exit */
+ on_proc_exit(FileUnlink, (Datum)pcp_un_addr.sun_path);
+
+ /* maybe change "*" to pool_config->pcp_listen_addresses */
+ pcp_inet_fd = create_inet_domain_socket("*", pool_config->pcp_port);
+ pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file);
+
+ /* Fork worker process */
+ worker_pid = worker_fork_a_child();
+
+ retrycnt = 0; /* reset health check retry counter */
+ sys_retrycnt = 0; /* reset SystemDB health check retry counter */
+
+ /* Save primary node id */
+ Req_info->primary_node_id = find_primary_node();
+
+ if (sigsetjmp(local_sigjmp_buf, 1) != 0)
+ {
+ /* Since not using PG_TRY, must reset error stack by hand */
+ error_context_stack = NULL;
+ EmitErrorReport();
+ FlushErrorState();
+ POOL_SETMASK(&BlockSig);
+
+ /* Check if we are failed during health check
+ * perform the necessary actions in case
+ */
+ if(processState == PERFORMING_HEALTH_CHECK)
+ {
+ if(errno != EINTR || health_check_timer_expired)
+ {
+ if(use_template_db == false)
+ {
+ /* Health check was performed on 'postgres' database
+ * lets try to perform health check with template1 db
+ * before marking the health check as failed
+ */
+ use_template_db = true;
+
+ }
+ else
+ {
+ retrycnt++;
+ if(process_backend_health_check_failure(health_check_node_id, retrycnt))
+ {
+ health_check_node_id = 0;
+ use_template_db = false;
+ }
+ }
+ }
+ }
+ else if(processState == PERFORMING_SYSDB_CHECK)
+ {
+ if ( errno != EINTR || health_check_timer_expired)
+ {
+ sys_retrycnt++;
+ pool_signal(SIGALRM, SIG_IGN);
+ CLEAR_ALARM;
+
+ if (sys_retrycnt > NUM_BACKENDS)
+ {
+ ereport(LOG,
+ (errmsg("set SystemDB down status")));
+
+ SYSDB_STATUS = CON_DOWN;
+ sys_retrycnt = 0;
+ }
+ int sleep_time = pool_config->health_check_period/NUM_BACKENDS;
+
+ ereport(DEBUG2,
+ (errmsg("retry sleep time: %d seconds", sleep_time)));
+ pool_sleep(sleep_time);
+ }
+ }
+ }
+ /* We can now handle ereport(ERROR) */
+ PG_exception_stack = &local_sigjmp_buf;
+
+ /* This is the main loop */
+ for (;;)
+ {
+ bool all_nodes_healthy;
+ CHECK_REQUEST;
+
+ /* do we need health checking for PostgreSQL? */
+ if (pool_config->health_check_period > 0)
+ {
+ /* rest per iteration memory context */
+ MemoryContextSwitchTo(MainLoopMemoryContext);
+ MemoryContextResetAndDeleteChildren(MainLoopMemoryContext);
+
+ if (retrycnt == 0 && !use_template_db)
+ ereport(DEBUG1,
+ (errmsg("starting health check")));
+ else if(retrycnt)
+ ereport(LOG,
+ (errmsg("health checking retry count %d", retrycnt)));
+
+ if (pool_config->health_check_timeout > 0)
+ {
+ /*
+ * set health checker timeout. we want to detect
+ * communication path failure much earlier before
+ * TCP/IP stack detects it.
+ */
+ CLEAR_ALARM;
+ pool_signal(SIGALRM, health_check_timer_handler);
+ alarm(pool_config->health_check_timeout);
+ }
+ /*
+ * do actual health check. trying to connect to the backend
+ */
+ errno = 0;
+ health_check_timer_expired = 0;
+
+ if (pool_config->parallel_mode)
+ {
+ processState = PERFORMING_SYSDB_CHECK;
+ system_db_health_check();
+ sys_retrycnt = 0;
+ errno = 0;
+ }
+
+ POOL_SETMASK(&UnBlockSig);
+
+ processState = PERFORMING_HEALTH_CHECK;
+ all_nodes_healthy = do_health_check(use_template_db,&health_check_node_id);
+ POOL_SETMASK(&BlockSig);
+
+ health_check_node_id = 0;
+ use_template_db = false;
+ retrycnt = 0;
+
+ if (all_nodes_healthy && retrycnt)
+ ereport(LOG,
+ (errmsg("after retry %d, All backends are returned to healthy state",retrycnt)));
+
+
+ processState = SLEEPING;
+
+ if (pool_config->health_check_timeout > 0)
+ {
+ /* seems OK. cancel health check timer */
+ pool_signal(SIGALRM, SIG_IGN);
+ CLEAR_ALARM;
+ }
+ pool_sleep(pool_config->health_check_period);
+ }
+ else /* Health Check is not enable and we have not much to do */
+ {
+ processState = SLEEPING;
+ for (;;)
+ {
+ int r;
+ struct timeval t = {3, 0};
+
+ POOL_SETMASK(&UnBlockSig);
+ r = pool_pause(&t);
+ POOL_SETMASK(&BlockSig);
+ if (r > 0)
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Function process the backend node failure captured by the health check
+ * since this function is called from the exception handler so ereport(ERROR)
+ * is not allowed from this function
+ * Function returns non zero if failover is performed and 0 otherwise.
+ */
+static int
+process_backend_health_check_failure(int health_check_node_id, int retrycnt)
+{
+ /*
+ * health check is failed on template1 database as well
+ */
+ int sleep_time = pool_config->parallel_mode ?
+ pool_config->health_check_period/NUM_BACKENDS : pool_config->health_check_retry_delay;
+
+ int health_check_max_retries = pool_config->parallel_mode ?
+ (NUM_BACKENDS - 1) : pool_config->health_check_retry_delay;
+
+ pool_signal(SIGALRM, SIG_IGN); /* Cancel timer */
+ CLEAR_ALARM;
+
+ if (retrycnt <= health_check_max_retries)
+ {
+ /* Keep retrying and sleep a little in between */
+ ereport(DEBUG1,
+ (errmsg("Sleeping for %d seconds from process backend health check failure", sleep_time),
+ errdetail("health check failed retry no is %d while max retries are %d",retrycnt,health_check_max_retries) ));
+
+ pool_sleep(sleep_time);
+ }
+ else
+ {
+ /* No more retries left, proceed to failover if allowed */
+ if ((!pool_config->parallel_mode) &&
+ POOL_DISALLOW_TO_FAILOVER(BACKEND_INFO(health_check_node_id).flag))
+ {
+ ereport(LOG,
+ (errmsg("health check failed on node %d but failover is disallowed for the node", health_check_node_id)));
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("setting backend node %d status to NODE DOWN", health_check_node_id)));
+ Req_info->kind = NODE_DOWN_REQUEST;
+ Req_info->node_id[0] = health_check_node_id;
+ health_check_timer_expired = 0;
+ failover();
+ return 1;
+ /* need to distribute this info to children ??*/
+ }
+ }
+ return 0;
+}
+/*
+ * fork a child for PCP
+ */
+pid_t pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file)
+{
+ pid_t pid;
+
+ pid = fork();
+
+ if (pid == 0)
+ {
+ on_exit_reset();
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+
+ myargv = save_ps_display_args(myargc, myargv);
+ /* Set the process type variable */
+ processType = PT_PCP;
+
+ /* call PCP child main */
+ POOL_SETMASK(&UnBlockSig);
+ health_check_timer_expired = 0;
+ reload_config_request = 0;
+ run_as_pcp_child = true;
+ pcp_do_child(unix_fd, inet_fd, pcp_conf_file);
+ }
+ else if (pid == -1)
+ {
+ ereport(FATAL,
+ (errmsg("fork() failed. reason: %s", strerror(errno))));
+ }
+
+ return pid;
+}
+
+/*
+* fork a child
+*/
+pid_t fork_a_child(int unix_fd, int inet_fd, int id)
+{
+ pid_t pid;
+
+ pid = fork();
+
+ if (pid == 0)
+ {
+ on_exit_reset();
+
+ /* Before we unconditionally closed pipe_fds[0] and pipe_fds[1]
+ * here, which is apparently wrong since in the start up of
+ * pgpool, pipe(2) is not called yet and it mistakenly closes
+ * fd 0. Now we check the fd > 0 before close(), expecting
+ * pipe returns fds greater than 0. Note that we cannot
+ * unconditionally remove close(2) calls since fork_a_child()
+ * may be called *after* pgpool starting up.
+ */
+ if (pipe_fds[0] > 0)
+ {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ }
+
+ myargv = save_ps_display_args(myargc, myargv);
+ /* Set the process type variable */
+ processType = PT_CHILD;
+
+ /* call child main */
+ POOL_SETMASK(&UnBlockSig);
+ health_check_timer_expired = 0;
+ reload_config_request = 0;
+ my_proc_id = id;
+ run_as_pcp_child = false;
+ do_child(unix_fd, inet_fd);
+ }
+ else if (pid == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to fork a child"),
+ errdetail("system call fork() failed with reason: %s", strerror(errno))));
+ }
+
+ return pid;
+}
+
+/*
+* fork worker child process
+*/
+pid_t worker_fork_a_child()
+{
+ pid_t pid;
+
+ pid = fork();
+
+ if (pid == 0)
+ {
+ on_exit_reset();
+
+ /* Before we unconditionally closed pipe_fds[0] and pipe_fds[1]
+ * here, which is apparently wrong since in the start up of
+ * pgpool, pipe(2) is not called yet and it mistakenly closes
+ * fd 0. Now we check the fd > 0 before close(), expecting
+ * pipe returns fds greater than 0. Note that we cannot
+ * unconditionally remove close(2) calls since fork_a_child()
+ * may be called *after* pgpool starting up.
+ */
+ if (pipe_fds[0] > 0)
+ {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ }
+
+ myargv = save_ps_display_args(myargc, myargv);
+ /* Set the process type variable */
+ processType = PT_WORKER;
+
+ /* call child main */
+ POOL_SETMASK(&UnBlockSig);
+ health_check_timer_expired = 0;
+ reload_config_request = 0;
+ do_worker_child();
+ }
+ else if (pid == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to fork a child"),
+ errdetail("system call fork() failed with reason: %s", strerror(errno))));
+ }
+
+ return pid;
+}
+
+/*
+* create inet domain socket
+*/
+static int create_inet_domain_socket(const char *hostname, const int port)
+{
+ struct sockaddr_in addr;
+ int fd;
+ int status;
+ int one = 1;
+ int len;
+ int backlog;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("socket error \"%s\"",strerror(errno))));
+ }
+ if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
+ sizeof(one))) == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("socket error \"%s\"",strerror(errno))));
+ }
+
+ memset((char *) &addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ if (strcmp(hostname, "*")==0)
+ {
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else
+ {
+ struct hostent *hostinfo;
+
+ hostinfo = gethostbyname(hostname);
+ if (!hostinfo)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("could not resolve hostname \"%s\": error \"%s\"",hostname,hstrerror(h_errno))));
+
+ }
+ addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
+ }
+
+ addr.sin_port = htons(port);
+ len = sizeof(struct sockaddr_in);
+ status = bind(fd, (struct sockaddr *)&addr, len);
+ if (status == -1)
+ {
+ char *host = "", *serv = "";
+ char hostname[NI_MAXHOST], servname[NI_MAXSERV];
+ if (getnameinfo((struct sockaddr *) &addr, len, hostname, sizeof(hostname), servname, sizeof(servname), 0) == 0)
+ {
+ host = hostname;
+ serv = servname;
+ }
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("bind on host:\"%s\" server:\"%s\" failed with error \"%s\"",host, serv,strerror(errno))));
+ }
+
+ backlog = pool_config->num_init_children * pool_config->listen_backlog_multiplier;
+
+ if (backlog > PGPOOLMAXLITSENQUEUELENGTH)
+ backlog = PGPOOLMAXLITSENQUEUELENGTH;
+
+ status = listen(fd, backlog);
+ if (status < 0)
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("listen on socket failed with error \"%s\"",strerror(errno))));
+
+ return fd;
+}
+
+/*
+* create UNIX domain socket
+*/
+static int create_unix_domain_socket(struct sockaddr_un un_addr_tmp)
+{
+ struct sockaddr_un addr;
+ int fd;
+ int status;
+ int len;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create a socket"),
+ errdetail("Failed to create UNIX domain socket. reason: %s", strerror(errno))));
+ }
+ memset((char *) &addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", un_addr_tmp.sun_path);
+ len = sizeof(struct sockaddr_un);
+ status = bind(fd, (struct sockaddr *)&addr, len);
+ if (status == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to bind a socket"),
+ errdetail("Failed to bind socket. reason: %s", strerror(errno))));
+ }
+
+ if (chmod(un_addr_tmp.sun_path, 0777) == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to bind a socket"),
+ errdetail("system call chmod failed. reason: %s", strerror(errno))));
+ }
+
+ status = listen(fd, PGPOOLMAXLITSENQUEUELENGTH);
+ if (status < 0)
+ {
+ ereport(FATAL,
+ (errmsg("failed to bind a socket"),
+ errdetail("system call listen() failed. reason: %s", strerror(errno))));
+ }
+ return fd;
+}
+
+/*
+ * function called as shared memory exit call back to kill all childrens
+ */
+static void terminate_all_childrens()
+{
+ /*
+ * This is supposed to be called from main process
+ */
+ if(processType != PT_MAIN)
+ return;
+ POOL_SETMASK(&BlockSig);
+
+ kill_all_children(SIGTERM);
+ if(pcp_pid > 0)
+ kill(pcp_pid, SIGTERM);
+ pcp_pid = 0;
+ if(worker_pid > 0)
+ kill(worker_pid, SIGTERM);
+ worker_pid = 0;
+ if (pool_config->use_watchdog)
+ {
+ wd_kill_watchdog(SIGTERM);
+ }
+ /* wait for all children to exit */
+
+ while (wait(NULL) > 0);
+ if (errno != ECHILD)
+ ereport(LOG,
+ (errmsg("wait() failed. reason:%s", strerror(errno))));
+
+ POOL_SETMASK(&UnBlockSig);
+}
+
+/*
+ * This function is called by the proc_exit in elog.c after clearing
+ * the shared memory, so no shared memory residents should be accessed form
+ * this
+ */
+void system_exit(int code)
+{
+ /* we have nothing much left to do here.*/
+ exit(code);
+}
+
+void notice_backend_error(int node_id)
+{
+ int n = node_id;
+
+ if (getpid() == mypid)
+ {
+ ereport(LOG,
+ (errmsg("notice_backend_error: called from pgpool main. ignored.")));
+ }
+ else
+ {
+ degenerate_backend_set(&n, 1);
+ }
+}
+
+/* notice backend connection error using SIGUSR1 */
+void degenerate_backend_set(int *node_id_set, int count)
+{
+ pid_t parent = getppid();
+ int i;
+ bool need_signal = false;
+#ifdef HAVE_SIGPROCMASK
+ sigset_t oldmask;
+#else
+ int oldmask;
+#endif
+
+ if (pool_config->parallel_mode)
+ {
+ return;
+ }
+
+ POOL_SETMASK2(&BlockSig, &oldmask);
+ pool_semaphore_lock(REQUEST_INFO_SEM);
+ Req_info->kind = NODE_DOWN_REQUEST;
+ for (i = 0; i < count; i++)
+ {
+ if (node_id_set[i] < 0 || node_id_set[i] >= MAX_NUM_BACKENDS ||
+ !VALID_BACKEND(node_id_set[i]))
+ {
+ ereport(LOG,
+ (errmsg("degenerate_backend_set: node %d is not valid backend.", i)));
+ continue;
+ }
+
+ if (POOL_DISALLOW_TO_FAILOVER(BACKEND_INFO(node_id_set[i]).flag))
+ {
+ ereport(LOG,
+ (errmsg("degenerate_backend_set: %d failover request from pid %d is canceled because failover is disallowed", node_id_set[i], getpid())));
+ continue;
+ }
+
+ ereport(LOG,
+ (errmsg("degenerate_backend_set: %d fail over request from pid %d", node_id_set[i], getpid())));
+ Req_info->node_id[i] = node_id_set[i];
+ need_signal = true;
+ }
+
+ if (need_signal)
+ {
+ if (!pool_config->use_watchdog || WD_OK == wd_degenerate_backend_set(node_id_set, count))
+ {
+ kill(parent, SIGUSR1);
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("degenerate_backend_set: failover request from pid %d is canceled by other pgpool", getpid())));
+ memset(Req_info->node_id, -1, sizeof(int) * MAX_NUM_BACKENDS);
+ }
+ }
+
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+ POOL_SETMASK(&oldmask);
+}
+
+/* send promote node request using SIGUSR1 */
+void promote_backend(int node_id)
+{
+ pid_t parent = getppid();
+
+ if (!MASTER_SLAVE || strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP))
+ {
+ return;
+ }
+
+ if (node_id < 0 || node_id >= MAX_NUM_BACKENDS || !VALID_BACKEND(node_id))
+ {
+ pool_error("promote_backend: node %d is not valid backend.", node_id);
+ return;
+ }
+
+ pool_semaphore_lock(REQUEST_INFO_SEM);
+ Req_info->kind = PROMOTE_NODE_REQUEST;
+ Req_info->node_id[0] = node_id;
+ ereport(LOG,
+ (errmsg("promote_backend: %d promote node request from pid %d", node_id, getpid())));
+
+ if (!pool_config->use_watchdog || WD_OK == wd_promote_backend(node_id))
+ {
+ kill(parent, SIGUSR1);
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("promote_backend: promote request from pid %d is canceled by other pgpool", getpid())));
+ Req_info->node_id[0] = -1;
+ }
+
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+}
+
+/* send failback request using SIGUSR1 */
+void send_failback_request(int node_id)
+{
+ pid_t parent = getppid();
+
+ ereport(LOG,
+ (errmsg("send_failback_request: fail back %d th node request from pid %d", node_id, getpid())));
+ Req_info->kind = NODE_UP_REQUEST;
+ Req_info->node_id[0] = node_id;
+
+ if (node_id < 0 || node_id >= MAX_NUM_BACKENDS ||
+ (RAW_MODE && BACKEND_INFO(node_id).backend_status != CON_DOWN && VALID_BACKEND(node_id)))
+ {
+ pool_error("send_failback_request: node %d is alive.", node_id);
+ Req_info->node_id[0] = -1;
+ return;
+ }
+
+ if (pool_config->use_watchdog && WD_OK != wd_send_failback_request(node_id))
+ {
+ ereport(LOG,
+ (errmsg("send_failback_request: failback request from pid %d is canceled by other pgpool", getpid())));
+ Req_info->node_id[0] = -1;
+ return;
+ }
+ kill(parent, SIGUSR1);
+}
+
+static RETSIGTYPE exit_handler(int sig)
+{
+ int i;
+ POOL_SETMASK(&AuthBlockSig);
+
+ /*
+ * this could happen in a child process if a signal has been sent
+ * before resetting signal handler
+ */
+ if (getpid() != mypid)
+ {
+ pool_debug("exit_handler: I am not parent");
+ POOL_SETMASK(&UnBlockSig);
+ proc_exit(0);
+ }
+
+ if (sig == SIGTERM)
+ ereport(LOG,
+ (errmsg("received smart shutdown request")));
+ else if (sig == SIGINT)
+ ereport(LOG,
+ (errmsg("received fast shutdown request")));
+ else if (sig == SIGQUIT)
+ ereport(LOG,
+ (errmsg("received immediate shutdown request")));
+ else
+ {
+ pool_error("exit_handler: unknown signal received %d", sig);
+ POOL_SETMASK(&UnBlockSig);
+ return;
+ }
+ exiting = 1;
+
++ /* Close listen socket */
++ pool_log("pgpool main: close listen socket");
++ close(inet_fd);
++ close(unix_fd);
++
+ for (i = 0; i < pool_config->num_init_children; i++)
+ {
+ pid_t pid = process_info[i].pid;
+ if (pid)
+ {
+ kill(pid, sig);
+ }
+ }
+ if(pcp_pid > 0)
+ kill(pcp_pid, sig);
+ if(worker_pid > 0)
+ kill(worker_pid, sig);
+ if (pool_config->use_watchdog)
+ {
+ wd_kill_watchdog(sig);
+ }
+
+ POOL_SETMASK(&UnBlockSig);
+
+ while (wait(NULL) > 0);
+
+ if (errno != ECHILD)
+ pool_error("wait() failed. reason:%s", strerror(errno));
+
+ process_info = NULL;
+ system_exit(0);
+}
+
+/*
+ * Calculate next valid master node id.
+ * If no valid node found, returns -1.
+ */
+static int get_next_master_node(void)
+{
+ int i;
+
+ for (i=0;i<pool_config->backend_desc->num_backends;i++)
+ {
+ /*
+ * Do not use VALID_BACKEND macro in raw mode.
+ * VALID_BACKEND return true only if the argument is master
+ * node id. In other words, standby nodes are false. So need
+ * to check backend status with VALID_BACKEND_RAW.
+ */
+ if (RAW_MODE)
+ {
+ if (VALID_BACKEND_RAW(i))
+ break;
+ }
+ else
+ {
+ if (VALID_BACKEND(i))
+ break;
+ }
+ }
+
+ if (i == pool_config->backend_desc->num_backends)
+ i = -1;
+
+ return i;
+}
+
+/*
+ * handle SIGUSR1
+ *
+ */
+static RETSIGTYPE failover_handler(int sig)
+{
+ POOL_SETMASK(&BlockSig);
+ failover_request = 1;
+ write(pipe_fds[1], "\0", 1);
+ POOL_SETMASK(&UnBlockSig);
+}
+
+/*
+ * backend connection error, failover/failback request, if possible
+ * failover() must be called under protecting signals.
+ */
+static void failover(void)
+{
+ int i;
+ int node_id;
+ bool by_health_check;
+ int new_master;
+ int new_primary;
+ int nodes[MAX_NUM_BACKENDS];
+ bool need_to_restart_children;
+ int status;
+ int sts;
+
+ pool_debug("failover_handler called");
+
+ memset(nodes, 0, sizeof(int) * MAX_NUM_BACKENDS);
+
+ /*
+ * this could happen in a child process if a signal has been sent
+ * before resetting signal handler
+ */
+ if (getpid() != mypid)
+ {
+ pool_debug("failover_handler: I am not parent");
+ kill(pcp_pid, SIGUSR2);
+ return;
+ }
+
+ /*
+ * processing SIGTERM, SIGINT or SIGQUIT
+ */
+ if (exiting)
+ {
+ pool_debug("failover_handler called while exiting");
+ kill(pcp_pid, SIGUSR2);
+ return;
+ }
+
+ /*
+ * processing fail over or switch over
+ */
+ if (switching)
+ {
+ pool_debug("failover_handler called while switching");
+ kill(pcp_pid, SIGUSR2);
+ return;
+ }
+
+ pool_semaphore_lock(REQUEST_INFO_SEM);
+
+ if (Req_info->kind == CLOSE_IDLE_REQUEST)
+ {
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+ kill_all_children(SIGUSR1);
+ kill(pcp_pid, SIGUSR2);
+ return;
+ }
+
+ /*
+ * if not in replication mode/master slave mode, we treat this a restart request.
+ * otherwise we need to check if we have already failovered.
+ */
+ pool_debug("failover_handler: starting to select new master node");
+ switching = 1;
+ Req_info->switching = true;
+ node_id = Req_info->node_id[0];
+
+ /* start of command inter-lock with watchdog */
+ if (pool_config->use_watchdog)
+ {
+ by_health_check = (!failover_request && Req_info->kind==NODE_DOWN_REQUEST);
+ wd_start_interlock(by_health_check);
+ }
+
+ /* failback request? */
+ if (Req_info->kind == NODE_UP_REQUEST)
+ {
+ if (node_id < 0 || node_id >= MAX_NUM_BACKENDS ||
+ (Req_info->kind == NODE_UP_REQUEST && !(RAW_MODE &&
+ BACKEND_INFO(node_id).backend_status == CON_DOWN) && VALID_BACKEND(node_id)) ||
+ (Req_info->kind == NODE_DOWN_REQUEST && !VALID_BACKEND(node_id)))
+ {
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+
+ if (node_id < 0 || node_id >= MAX_NUM_BACKENDS)
+ pool_error("failover_handler: invalid node_id %d MAX_NUM_BACKENDS: %d", node_id, MAX_NUM_BACKENDS);
+ else
+ pool_error("failover_handler: invalid node_id %d status:%d MAX_NUM_BACKENDS: %d", node_id,
+ BACKEND_INFO(node_id).backend_status, MAX_NUM_BACKENDS);
+ kill(pcp_pid, SIGUSR2);
+ switching = 0;
+ Req_info->switching = false;
+
+ /* end of command inter-lock */
+ if (pool_config->use_watchdog)
+ wd_leave_interlock();
+
+ return;
+ }
+
+ ereport(LOG,
+ (errmsg("starting fail back. reconnect host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ BACKEND_INFO(node_id).backend_status = CON_CONNECT_WAIT; /* unset down status */
+
+ /* wait for failback command lock or to be lock holder */
+ if (pool_config->use_watchdog && !wd_am_I_lock_holder())
+ {
+ wd_wait_for_lock(WD_FAILBACK_COMMAND_LOCK);
+ }
+ /* execute failback command if lock holder */
+ if (!pool_config->use_watchdog || wd_am_I_lock_holder())
+ {
+ trigger_failover_command(node_id, pool_config->failback_command,
+ MASTER_NODE_ID, get_next_master_node(), PRIMARY_NODE_ID);
+
+ /* unlock failback command */
+ if (pool_config->use_watchdog)
+ wd_unlock(WD_FAILBACK_COMMAND_LOCK);
+ }
+ }
+ else if (Req_info->kind == PROMOTE_NODE_REQUEST)
+ {
+ if (node_id != -1 && VALID_BACKEND(node_id))
+ {
+ ereport(LOG,
+ (errmsg("starting promotion. promote host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("failover: no backends are promoted")));
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+ kill(pcp_pid, SIGUSR2);
+ switching = 0;
+ Req_info->switching = false;
+
+ /* end of command inter-lock */
+ if (pool_config->use_watchdog)
+ wd_leave_interlock();
+
+ return;
+ }
+ }
+ else
+ {
+ int cnt = 0;
+
+ for (i = 0; i < MAX_NUM_BACKENDS; i++)
+ {
+ if (Req_info->node_id[i] != -1 &&
+ ((RAW_MODE && VALID_BACKEND_RAW(Req_info->node_id[i])) ||
+ VALID_BACKEND(Req_info->node_id[i])))
+ {
+ ereport(LOG,
+ (errmsg("starting degeneration. shutdown host %s(%d)",
+ BACKEND_INFO(Req_info->node_id[i]).backend_hostname,
+ BACKEND_INFO(Req_info->node_id[i]).backend_port)));
+
+ BACKEND_INFO(Req_info->node_id[i]).backend_status = CON_DOWN; /* set down status */
+ /* save down node */
+ nodes[Req_info->node_id[i]] = 1;
+ cnt++;
+ }
+ }
+
+ if (cnt == 0)
+ {
+ ereport(LOG,
+ (errmsg("failover: no backends are degenerated")));
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+ kill(pcp_pid, SIGUSR2);
+ switching = 0;
+ Req_info->switching = false;
+
+ /* end of command inter-lock */
+ if (pool_config->use_watchdog)
+ wd_leave_interlock();
+
+ return;
+ }
+ }
+
+ new_master = get_next_master_node();
+
+ if (new_master < 0)
+ {
+ pool_error("failover_handler: no valid DB node found");
+ }
+
+/*
+ * Before we tried to minimize restarting pgpool to protect existing
+ * connections from clients to pgpool children. What we did here was,
+ * if children other than master went down, we did not fail over.
+ * This is wrong. Think about following scenario. If someone
+ * accidentally plugs out the network cable, the TCP/IP stack keeps
+ * retrying for long time (typically 2 hours). The only way to stop
+ * the retry is restarting the process. Bottom line is, we need to
+ * restart all children in any case. See pgpool-general list posting
+ * "TCP connections are *not* closed when a backend timeout" on Jul 13
+ * 2008 for more details.
+ */
+#ifdef NOT_USED
+ else
+ {
+ if (Req_info->master_node_id == new_master && *InRecovery == RECOVERY_INIT)
+ {
+ ereport(LOG,
+ (errmsg("failover_handler: do not restart pgpool. same master node %d was selected", new_master)));
+ if (Req_info->kind == NODE_UP_REQUEST)
+ {
+ ereport(LOG,
+ (errmsg("failback done. reconnect host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("failover done. shutdown host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ }
+
+ /* exec failover_command */
+ for (i = 0; i < pool_config->backend_desc->num_backends; i++)
+ {
+ if (nodes[i])
+ trigger_failover_command(i, pool_config->failover_command);
+ }
+
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+ switching = 0;
+ Req_info->switching = false;
+ kill(pcp_pid, SIGUSR2);
+ switching = 0;
+ Req_info->switching = false;
+ return;
+ }
+ }
+#endif
+
+
+ /* On 2011/5/2 Tatsuo Ishii says: if mode is streaming replication
+ * and request is NODE_UP_REQUEST(failback case) we don't need to
+ * restart all children. Existing session will not use newly
+ * attached node, but load balanced node is not changed until this
+ * session ends, so it's harmless anyway.
+ */
+ if (MASTER_SLAVE && !strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP) &&
+ Req_info->kind == NODE_UP_REQUEST)
+ {
+ ereport(LOG,
+ (errmsg("Do not restart children because we are failbacking node id %d host%s port:%d and we are in streaming replication mode", node_id,
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+
+ need_to_restart_children = false;
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("Restart all children")));
+
+ /* kill all children */
+ for (i = 0; i < pool_config->num_init_children; i++)
+ {
+ pid_t pid = process_info[i].pid;
+ if (pid)
+ {
+ kill(pid, SIGQUIT);
+ pool_debug("failover_handler: kill %d", pid);
+ }
+ }
+
+ need_to_restart_children = true;
+ }
+
+ /* wait for failover command lock or to be lock holder*/
+ if (pool_config->use_watchdog && !wd_am_I_lock_holder())
+ {
+ wd_wait_for_lock(WD_FAILOVER_COMMAND_LOCK);
+ }
+
+ /* execute failover command if lock holder */
+ if (!pool_config->use_watchdog || wd_am_I_lock_holder())
+ {
+ /* Exec failover_command if needed */
+ for (i = 0; i < pool_config->backend_desc->num_backends; i++)
+ {
+ if (nodes[i])
+ trigger_failover_command(i, pool_config->failover_command,
+ MASTER_NODE_ID, new_master, PRIMARY_NODE_ID);
+ }
+
+ /* unlock failover command */
+ if (pool_config->use_watchdog)
+ wd_unlock(WD_FAILOVER_COMMAND_LOCK);
+ }
+
+
+/* no need to wait since it will be done in reap_handler */
+#ifdef NOT_USED
+ while (wait(NULL) > 0)
+ ;
+
+ if (errno != ECHILD)
+ pool_error("failover_handler: wait() failed. reason:%s", strerror(errno));
+#endif
+
+ if (Req_info->kind == PROMOTE_NODE_REQUEST && VALID_BACKEND(node_id))
+ new_primary = node_id;
+
+ /*
+ * If the down node was a standby node in streaming replication
+ * mode, we can avoid calling find_primary_node_repeatedly() and
+ * recognize the former primary as the new primary node, which
+ * will reduce the time to process standby down.
+ */
+ else if (MASTER_SLAVE && !strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP) &&
+ Req_info->kind == NODE_DOWN_REQUEST)
+ {
+ if (Req_info->primary_node_id != node_id)
+ new_primary = Req_info->primary_node_id;
+ else
+ new_primary = find_primary_node_repeatedly();
+ }
+ else
+ new_primary = find_primary_node_repeatedly();
+
+ /*
+ * If follow_master_command is provided and in master/slave
+ * streaming replication mode, we start degenerating all backends
+ * as they are not replicated anymore.
+ */
+ int follow_cnt = 0;
+ if (MASTER_SLAVE && !strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP))
+ {
+ if (*pool_config->follow_master_command != '\0' ||
+ Req_info->kind == PROMOTE_NODE_REQUEST)
+ {
+ /* only if the failover is against the current primary */
+ if (((Req_info->kind == NODE_DOWN_REQUEST) &&
+ (nodes[Req_info->primary_node_id])) ||
+ ((Req_info->kind == PROMOTE_NODE_REQUEST) &&
+ (VALID_BACKEND(node_id)))) {
+
+ for (i = 0; i < pool_config->backend_desc->num_backends; i++)
+ {
+ /* do not degenerate the new primary */
+ if ((new_primary >= 0) && (i != new_primary)) {
+ BackendInfo *bkinfo;
+ bkinfo = pool_get_node_info(i);
+ ereport(LOG,
+ (errmsg("starting follow degeneration. shutdown host %s(%d)",
+ bkinfo->backend_hostname,
+ bkinfo->backend_port)));
+ bkinfo->backend_status = CON_DOWN; /* set down status */
+ follow_cnt++;
+ }
+ }
+
+ if (follow_cnt == 0)
+ {
+ ereport(LOG,
+ (errmsg("failover: no follow backends are degenerated")));
+ }
+ else
+ {
+ /* update new master node */
+ new_master = get_next_master_node();
+ ereport(LOG,
+ (errmsg("failover: %d follow backends have been degenerated", follow_cnt)));
+ }
+ }
+ }
+ }
+
+ memset(Req_info->node_id, -1, sizeof(int) * MAX_NUM_BACKENDS);
+ pool_semaphore_unlock(REQUEST_INFO_SEM);
+
+ /* wait for follow_master_command lock or to be lock holder */
+ if (pool_config->use_watchdog && !wd_am_I_lock_holder())
+ {
+ wd_wait_for_lock(WD_FOLLOW_MASTER_COMMAND_LOCK);
+ }
+
+ /* execute follow_master_command */
+ if (!pool_config->use_watchdog || wd_am_I_lock_holder())
+ {
+ if ((follow_cnt > 0) && (*pool_config->follow_master_command != '\0'))
+ {
+ follow_pid = fork_follow_child(Req_info->master_node_id, new_primary,
+ Req_info->primary_node_id);
+ }
+
+ /* unlock follow_master_command */
+ if (pool_config->use_watchdog)
+ wd_unlock(WD_FOLLOW_MASTER_COMMAND_LOCK);
+ }
+
+ /* end of command inter-lock */
+ if (pool_config->use_watchdog)
+ wd_end_interlock();
+
+ /* Save primary node id */
+ Req_info->primary_node_id = new_primary;
+ ereport(LOG,
+ (errmsg("failover: set new primary node: %d", Req_info->primary_node_id)));
+
+ if (new_master >= 0)
+ {
+ Req_info->master_node_id = new_master;
+ ereport(LOG,
+ (errmsg("failover: set new master node: %d", Req_info->master_node_id)));
+ }
+
+
+ /* Fork the children if needed */
+ if (need_to_restart_children)
+ {
+ for (i=0;i<pool_config->num_init_children;i++)
+ {
+
+ /*
+ * Try to kill pgpool child because previous kill signal
+ * may not be received by pgpool child. This could happen
+ * if multiple PostgreSQL are going down (or even starting
+ * pgpool, without starting PostgreSQL can trigger this).
+ * Child calls degenerate_backend() and it tries to aquire
+ * semaphore to write a failover request. In this case the
+ * signal mask is set as well, thus signals are never
+ * received.
+ */
+ kill(process_info[i].pid, SIGQUIT);
+
+ process_info[i].pid = fork_a_child(unix_fd, inet_fd, i);
+ process_info[i].start_time = time(NULL);
+ }
+ }
+ else
+ {
+ /* Set restart request to each child. Children will exit(1)
+ * whenever they are idle to restart.
+ */
+ for (i=0;i<pool_config->num_init_children;i++)
+ {
+ process_info[i].need_to_restart = 1;
+ }
+ }
+
+ /*
+ * Send restart request to worker child.
+ */
+ kill(worker_pid, SIGUSR1);
+
+ if (Req_info->kind == NODE_UP_REQUEST)
+ {
+ ereport(LOG,
+ (errmsg("failback done. reconnect host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ }
+ else if (Req_info->kind == PROMOTE_NODE_REQUEST)
+ {
+ ereport(LOG,
+ (errmsg("promotion done. promoted host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ }
+ else
+ {
+ ereport(LOG,
+ (errmsg("failover done. shutdown host %s(%d)",
+ BACKEND_INFO(node_id).backend_hostname,
+ BACKEND_INFO(node_id).backend_port)));
+ }
+
+ switching = 0;
+ Req_info->switching = false;
+
+ /* kick wakeup_handler in pcp_child to notice that
+ * failover/failback done
+ */
+ kill(pcp_pid, SIGUSR2);
+
+ sleep(1);
+
+ /*
+ * Send restart request to pcp child.
+ */
+ kill(pcp_pid, SIGUSR1);
+ for (;;)
+ {
+ sts = waitpid(pcp_pid, &status, 0);
+ if (sts != -1)
+ break;
+ if (sts == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ {
+ pool_error("failover: waitpid failed. reason: %s", strerror(errno));
+ return;
+ }
+ }
+ }
+ if (WIFSIGNALED(status))
+ ereport(LOG,
+ (errmsg("PCP child %d exits with status %d by signal %d in failover()", pcp_pid, status, WTERMSIG(status))));
+ else
+ ereport(LOG,
+ (errmsg("PCP child %d exits with status %d in failover()", pcp_pid, status)));
+
+ pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file);
+ ereport(LOG,
+ (errmsg("fork a new PCP child pid %d in failover()", pcp_pid)));
+}
+
+/*
+ * health check timer handler
+ */
+static RETSIGTYPE health_check_timer_handler(int sig)
+{
+ POOL_SETMASK(&BlockSig);
+ health_check_timer_expired = 1;
+ POOL_SETMASK(&UnBlockSig);
+}
+
+
+
+/*
+ * do_health_check() performs the health check on all backend nodes.
+ * The inout parameter health_check_node_id is the starting backend
+ * node number for health check and when the function returns or
+ * exits with an error health_check_node_id contains the value
+ * of last backend node number on which health check was performed.
+ *
+ * Function returns false if all backend nodes are down and true if all
+ * backend nodes are in healthy state
+ */
+static bool
+do_health_check(bool use_template_db, volatile int *health_check_node_id)
+{
+ POOL_CONNECTION_POOL_SLOT *slot;
+ BackendInfo *bkinfo;
+ static char *dbname;
+ int i;
+ bool all_nodes_healthy = false;
+
+ /* Do not execute health check during recovery */
+ if (*InRecovery)
+ return false;
+
+ dbname = use_template_db ? "template1" : "postgres";
+ /*
+ * Start checking the backed nodes starting from the
+ * previously failed node
+ */
+ for (i=*health_check_node_id;i<pool_config->backend_desc->num_backends;i++)
+ {
+ *health_check_node_id = i;
+ /*
+ * Make sure that health check timer has not been expired.
+ * Before called health_check(), health_check_timer_expired is
+ * set to 0. However it is possible that while processing DB
+ * nodes health check timer expired.
+ */
+ if (health_check_timer_expired)
+ {
+ ereport(ERROR,
+ (errmsg("health check timer has been already expired before attempting to connect backend node %d", i)));
+ }
+
+ bkinfo = pool_get_node_info(i);
+
+ ereport(DEBUG1,
+ (errmsg("Backend DB node %d status is %d", i, bkinfo->backend_status)));
+
+
+ if (bkinfo->backend_status == CON_UNUSED ||
+ bkinfo->backend_status == CON_DOWN)
+ continue;
+
+ all_nodes_healthy = true;
+ ereport(DEBUG1,
+ (errmsg("Trying to make persistent DB connection to backend node %d having status %d", i, bkinfo->backend_status)));
+
+ slot = make_persistent_db_connection(bkinfo->backend_hostname,
+ bkinfo->backend_port,
+ dbname,
+ pool_config->health_check_user,
+ pool_config->health_check_password, false);
+
+ ereport(DEBUG1,
+ (errmsg("persistent DB connection to backend node %d having status %d is successful", i, bkinfo->backend_status)));
+
+ discard_persistent_db_connection(slot);
+ }
+ return all_nodes_healthy;
+}
+
+/*
+ * handle SIGCHLD
+ */
+static RETSIGTYPE reap_handler(int sig)
+{
+ POOL_SETMASK(&BlockSig);
+ sigchld_request = 1;
+ write(pipe_fds[1], "\0", 1);
+ POOL_SETMASK(&UnBlockSig);
+}
+
+/*
+ * Attach zombie processes and restart child processes.
+ * reaper() must be called protected from signals.
+ */
+static void reaper(void)
+{
+ pid_t pid;
+ int status;
+ int i;
+
+ pool_debug("reap_handler called");
+
+ if (exiting)
+ {
+ pool_debug("reap_handler: exited due to exiting");
+ return;
+ }
+
+ if (switching)
+ {
+ pool_debug("reap_handler: exited due to switching");
+ return;
+ }
+
+ /* clear SIGCHLD request */
+ sigchld_request = 0;
+
+#ifdef HAVE_WAITPID
+ pool_debug("reap_handler: call waitpid");
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+#else
+ pool_debug("reap_handler: call wait3");
+ while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
+#endif
+ {
+ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)
+ {
+ /* Child terminated by segmentation fault. Report it */
+ pool_error("Child process %d was terminated by segmentation fault", pid);
+ }
+
+ /* if exiting child process was PCP handler */
+ if (pid == pcp_pid)
+ {
+ if (WIFSIGNALED(status))
+ ereport(LOG,
+ (errmsg("PCP child %d exits with status %d by signal %d", pid, status, WTERMSIG(status))));
+ else
+ ereport(LOG,
+ (errmsg("PCP child %d exits with status %d", pid, status)));
+
+ pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file);
+ ereport(LOG,
+ (errmsg("fork a new PCP child pid %d", pcp_pid)));
+ }
+
+ /* exiting process was worker process */
+ else if (pid == worker_pid)
+ {
+ if (WIFSIGNALED(status))
+ ereport(LOG,
+ (errmsg("worker child %d exits with status %d by signal %d", pid, status, WTERMSIG(status))));
+ else
+ ereport(LOG,
+ (errmsg("worker child %d exits with status %d", pid, status)));
+
+ if (status)
+ worker_pid = worker_fork_a_child();
+
+ ereport(LOG,
+ (errmsg("fork a new worker child pid %d", worker_pid)));
+ }
+
+ /* exiting process was watchdog process */
+ else if (pool_config->use_watchdog && wd_is_watchdog_pid(pid))
+ {
+ if (!wd_reaper_watchdog(pid, status))
+ {
+ ereport(FATAL,
+ (errmsg("wd_reaper failed")));
+
+ }
+ }
+
+ else
+ {
+ if (WIFSIGNALED(status))
+ pool_debug("child %d exits with status %d by signal %d", pid, status, WTERMSIG(status));
+ else
+ pool_debug("child %d exits with status %d", pid, status);
+
+ /* look for exiting child's pid */
+ for (i=0;i<pool_config->num_init_children;i++)
+ {
+ if (pid == process_info[i].pid)
+ {
+ /* if found, fork a new child */
+ if (!switching && !exiting && status)
+ {
+ process_info[i].pid = fork_a_child(unix_fd, inet_fd, i);
+ process_info[i].start_time = time(NULL);
+ pool_debug("fork a new child pid %d", process_info[i].pid);
+ break;
+ }
+ }
+ }
+ }
+ }
+ pool_debug("reap_handler: normally exited");
+}
+
+/*
+ * get node information specified by node_number
+ */
+BackendInfo *
+pool_get_node_info(int node_number)
+{
+ if (node_number < 0 || node_number >= NUM_BACKENDS)
+ return NULL;
+
+ return &BACKEND_INFO(node_number);
+}
+
+/*
+ * get number of nodes
+ */
+int
+pool_get_node_count(void)
+{
+ return NUM_BACKENDS;
+}
+
+/*
+ * get process ids
+ */
+int *
+pool_get_process_list(int *array_size)
+{
+ int *array;
+ int i;
+
+ *array_size = pool_config->num_init_children;
+ array = palloc0(*array_size * sizeof(int));
+ for (i = 0; i < *array_size; i++)
+ array[i] = process_info[i].pid;
+
+ return array;
+}
+
+/*
+ * get process information specified by pid
+ */
+ProcessInfo *
+pool_get_process_info(pid_t pid)
+{
+ int i;
+
+ for (i = 0; i < pool_config->num_init_children; i++)
+ if (process_info[i].pid == pid)
+ return &process_info[i];
+
+ return NULL;
+}
+
+/*
+ * handle SIGUSR2
+ * Wakeup all processes
+ */
+static void wakeup_children(void)
+{
+ kill_all_children(SIGUSR2);
+}
+
+
+static RETSIGTYPE wakeup_handler(int sig)
+{
+ POOL_SETMASK(&BlockSig);
+ wakeup_request = 1;
+ write(pipe_fds[1], "\0", 1);
+ POOL_SETMASK(&UnBlockSig);
+}
+
+/*
+ * handle SIGHUP
+ *
+ */
+static RETSIGTYPE reload_config_handler(int sig)
+{
+ POOL_SETMASK(&BlockSig);
+ reload_config_request = 1;
+ write(pipe_fds[1], "\0", 1);
+ POOL_SETMASK(&UnBlockSig);
+}
+
+static void kill_all_children(int sig)
+{
+ int i;
+ if(process_info)
+ {
+ /* kill all children */
+ for (i = 0; i < pool_config->num_init_children; i++)
+ {
+ pid_t pid = process_info[i].pid;
+ if (pid)
+ {
+ kill(pid, sig);
+ }
+ }
+ }
+ /* make PCP process reload as well */
+ if (sig == SIGHUP && pcp_pid > 0)
+ kill(pcp_pid, sig);
+}
+
+/*
+ * pause in a period specified by timeout. If any data is coming
+ * through pipe_fds[0], that means one of: failover request(SIGUSR1),
+ * SIGCHLD received, children wake up request(SIGUSR2 used in on line
+ * recovery processing) or config file reload request(SIGHUP) has been
+ * occurred. In this case this function returns 1.
+ * otherwise 0: (no signal event occurred), -1: (error)
+ * XXX: is it OK that select(2) error is ignored here?
+ */
+static int pool_pause(struct timeval *timeout)
+{
+ fd_set rfds;
+ int n;
+ char dummy;
+
+ FD_ZERO(&rfds);
+ FD_SET(pipe_fds[0], &rfds);
+ n = select(pipe_fds[0]+1, &rfds, NULL, NULL, timeout);
+ if (n == 1)
+ read(pipe_fds[0], &dummy, 1);
+ return n;
+}
+
+/*
+ * sleep for seconds specified by "second". Unlike pool_pause(), this
+ * function guarantees that it will sleep for specified seconds. This
+ * function uses pool_pause() internally. If it informs that there is
+ * a pending signal event, they are processed using CHECK_REQUEST
+ * macro. Note that most of these processes are done while all signals
+ * are blocked.
+ */
+void pool_sleep(unsigned int second)
+{
+ struct timeval current_time, sleep_time;
+
+ gettimeofday(¤t_time, NULL);
+ sleep_time.tv_sec = second + current_time.tv_sec;
+ sleep_time.tv_usec = current_time.tv_usec;
+
+ POOL_SETMASK(&UnBlockSig);
+ while (sleep_time.tv_sec > current_time.tv_sec)
+ {
+ struct timeval timeout;
+ int r;
+
+ timeout.tv_sec = sleep_time.tv_sec - current_time.tv_sec;
+ timeout.tv_usec = sleep_time.tv_usec - current_time.tv_usec;
+ if (timeout.tv_usec < 0)
+ {
+ timeout.tv_sec--;
+ timeout.tv_usec += 1000000;
+ }
+
+ r = pool_pause(&timeout);
+ POOL_SETMASK(&BlockSig);
+ if (r > 0)
+ CHECK_REQUEST;
+ POOL_SETMASK(&UnBlockSig);
+ gettimeofday(¤t_time, NULL);
+ }
+ POOL_SETMASK(&BlockSig);
+}
+
+/*
+ * trigger_failover_command: execute specified command at failover.
+ * command_line is null-terminated string.
+ */
+static int trigger_failover_command(int node, const char *command_line,
+ int old_master, int new_master, int old_primary)
+{
+ int r = 0;
+ String *exec_cmd;
+ char port_buf[6];
+ char buf[2];
+ BackendInfo *info;
+ BackendInfo *newmaster;
+ if (command_line == NULL || (strlen(command_line) == 0))
+ return 0;
+
+ /* check failed nodeID */
+ if (node < 0 || node > NUM_BACKENDS)
+ return -1;
+
+ info = pool_get_node_info(node);
+ if (!info)
+ return -1;
+
+ buf[1] = '\0';
+ exec_cmd = init_string("");
+
+ while (*command_line)
+ {
+ if (*command_line == '%')
+ {
+ if (*(command_line + 1))
+ {
+ char val = *(command_line + 1);
+ switch (val)
+ {
+ case 'p': /* failed node port */
+ snprintf(port_buf, sizeof(port_buf), "%d", info->backend_port);
+ string_append_char(exec_cmd, port_buf);
+ break;
+
+ case 'D': /* failed node database directory */
+ string_append_char(exec_cmd, info->backend_data_directory);
+ break;
+
+ case 'd': /* failed node id */
+ snprintf(port_buf, sizeof(port_buf), "%d", node);
+ string_append_char(exec_cmd, port_buf);
+ break;
+
+ case 'h': /* failed host name */
+ string_append_char(exec_cmd, info->backend_hostname);
+ break;
+
+ case 'H': /* new master host name */
+ newmaster = pool_get_node_info(new_master);
+ if (newmaster)
+ string_append_char(exec_cmd, newmaster->backend_hostname);
+ else
+ /* no valid new master */
+ string_append_char(exec_cmd, "");
+ break;
+
+ case 'm': /* new master node id */
+ snprintf(port_buf, sizeof(port_buf), "%d", new_master);
+ string_append_char(exec_cmd, port_buf);
+ break;
+
+ case 'r': /* new master port */
+ newmaster = pool_get_node_info(get_next_master_node());
+ if (newmaster)
+ {
+ snprintf(port_buf, sizeof(port_buf), "%d", newmaster->backend_port);
+ string_append_char(exec_cmd, port_buf);
+ }
+ else
+ /* no valid new master */
+ string_append_char(exec_cmd, "");
+ break;
+
+ case 'R': /* new master database directory */
+ newmaster = pool_get_node_info(get_next_master_node());
+ if (newmaster)
+ string_append_char(exec_cmd, newmaster->backend_data_directory);
+ else
+ /* no valid new master */
+ string_append_char(exec_cmd, "");
+ break;
+
+ case 'M': /* old master node id */
+ snprintf(port_buf, sizeof(port_buf), "%d", old_master);
+ string_append_char(exec_cmd, port_buf);
+ break;
+
+ case 'P': /* old primary node id */
+ snprintf(port_buf, sizeof(port_buf), "%d", old_primary);
+ string_append_char(exec_cmd, port_buf);
+ break;
+
+ case '%': /* escape */
+ string_append_char(exec_cmd, "%");
+ break;
+
+ default: /* ignore */
+ break;
+ }
+ command_line++;
+ }
+ } else {
+ buf[0] = *command_line;
+ string_append_char(exec_cmd, buf);
+ }
+ command_line++;
+ }
+
+ if (strlen(exec_cmd->data) != 0)
+ {
+ ereport(LOG,
+ (errmsg("execute command: %s", exec_cmd->data)));
+ r = system(exec_cmd->data);
+ }
+
+ return r;
+}
+
+/*
+ * Find the primary node (i.e. not standby node) and returns its node
+ * id. If no primary node is found, returns -1.
+ */
+static int find_primary_node(void)
+{
+ BackendInfo *bkinfo;
+ POOL_CONNECTION_POOL_SLOT *s;
+ POOL_CONNECTION *con;
+ POOL_STATUS status;
+ POOL_SELECT_RESULT *res;
+ bool is_standby;
+ int i;
+
+ /* Streaming replication mode? */
+ if (pool_config->master_slave_mode == 0 ||
+ strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP))
+ {
+ /* No point to look for primary node if not in streaming
+ * replication mode.
+ */
+ pool_debug("find_primary_node: not in streaming replication mode");
+ return -1;
+ }
+
+ for(i=0;i<NUM_BACKENDS;i++)
+ {
+ if (!VALID_BACKEND(i))
+ continue;
+
+ /*
+ * Check to see if this is a standby node or not.
+ */
+ is_standby = false;
+ PG_TRY();
+ {
+
+ bkinfo = pool_get_node_info(i);
+ s = make_persistent_db_connection(bkinfo->backend_hostname,
+ bkinfo->backend_port,
+ "postgres",
+ pool_config->sr_check_user,
+ pool_config->sr_check_password, true);
+ if (!s)
+ {
+ pool_error("find_primary_node: make_persistent_connection failed");
+
+ /*
+ * It is possible that a node is down even if
+ * VALID_BACKEND tells it's valid. This could happen
+ * before health checking detects the failure.
+ * Thus we should continue to look for primary node.
+ */
+ continue;
+ }
+ con = s->con;
+ status = do_query(con, "SELECT pg_is_in_recovery()",
+ &res, PROTO_MAJOR_V3);
+ if (res->numrows <= 0)
+ {
+ ereport(LOG,
+ (errmsg("find_primary_node: do_query returns no rows")));
+ }
+ if (res->data[0] == NULL)
+ {
+ ereport(LOG,
+ (errmsg("find_primary_node: do_query returns no data")));
+ }
+ if (res->nullflags[0] == -1)
+ {
+ ereport(LOG,
+ (errmsg("find_primary_node: do_query returns NULL")));
+ }
+ if (res->data[0] && !strcmp(res->data[0], "t"))
+ {
+ is_standby = true;
+ }
+ free_select_result(res);
+ discard_persistent_db_connection(s);
+ }
+ PG_CATCH();
+ {
+ EmitErrorReport();
+ FlushErrorState();
+ continue;
+ }
+ PG_END_TRY();
+
+ /*
+ * If this is a standby, we continue to look for primary node.
+ */
+ if (is_standby)
+ {
+ pool_debug("find_primary_node: %d node is standby", i);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (i == NUM_BACKENDS)
+ {
+ pool_debug("find_primary_node: no primary node found");
+ return -1;
+ }
+
+ ereport(LOG,
+ (errmsg("find_primary_node: primary node id is %d", i)));
+ return i;
+}
+
+static int find_primary_node_repeatedly(void)
+{
+ int sec;
+ int node_id = -1;
+
+ /* Streaming replication mode? */
+ if (pool_config->master_slave_mode == 0 ||
+ strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP))
+ {
+ /* No point to look for primary node if not in streaming
+ * replication mode.
+ */
+ ereport(DEBUG1,
+ (errmsg("find_primary_node: not in streaming replication mode")));
+ return -1;
+ }
+
+ /*
+ * Try to find the new primary node and keep trying for
+ * search_primary_node_timeout seconds.
+ * search_primary_node_timeout = 0 means never timeout and keep searching
+ * indefinitely
+ */
+ ereport(LOG,
+ (errmsg("find_primary_node_repeatedly: waiting for finding a primary node")));
+ for (sec = 0; (pool_config->search_primary_node_timeout == 0 ||
+ sec < pool_config->search_primary_node_timeout); sec++)
+ {
+ node_id = find_primary_node();
+ if (node_id != -1)
+ break;
+ pool_sleep(1);
+ }
+ return node_id;
+}
+
+/*
+* fork a follow child
+*/
+pid_t fork_follow_child(int old_master, int new_primary, int old_primary)
+{
+ pid_t pid;
+ int i;
+
+ pid = fork();
+
+ if (pid == 0)
+ {
+ on_exit_reset();
+ ereport(LOG,
+ (errmsg("start triggering follow command.")));
+ for (i = 0; i < pool_config->backend_desc->num_backends; i++)
+ {
+ BackendInfo *bkinfo;
+ bkinfo = pool_get_node_info(i);
+ if (bkinfo->backend_status == CON_DOWN)
+ trigger_failover_command(i, pool_config->follow_master_command,
+ old_master, new_primary, old_primary);
+ }
+ exit(0);
+ }
+ else if (pid == -1)
+ {
+ pool_error("follow fork() failed. reason: %s", strerror(errno));
+ exit(1);
+ }
+ return pid;
+}
+
+
+static void initialize_shared_mem_objects()
+{
+ int size,i;
+ /*
+ * con_info is a 3 dimension array: i corresponds to pgpool child
+ * process, j corresponds to connection pool in each process and k
+ * corresponds to backends in each connection pool.
+ *
+ * XXX: Before 2010/4/12 this was a 2 dimension array: i
+ * corresponds to pgpool child process, j corresponds to
+ * connection pool in each process. Of course this was wrong.
+ */
+ size = pool_coninfo_size();
+ con_info = pool_shared_memory_create(size);
+ memset(con_info, 0, size);
+
+ size = pool_config->num_init_children * (sizeof(ProcessInfo));
+ process_info = pool_shared_memory_create(size);
+ memset(process_info, 0, size);
+
+ for (i = 0; i < pool_config->num_init_children; i++)
+ {
+ process_info[i].connection_info = pool_coninfo(i,0,0);
+ }
+
+ /* create fail over/switch over event area */
+ Req_info = pool_shared_memory_create(sizeof(POOL_REQUEST_INFO));
+
+ /*
+ * Initialize backend status area.
+ * From now on, VALID_BACKEND macro can be used.
+ * (get_next_master_node() uses VALID_BACKEND)
+ */
+
+ for (i=0;i<MAX_NUM_BACKENDS;i++)
+ {
+ my_backend_status[i] = &(BACKEND_INFO(i).backend_status);
+ }
+
+ /* initialize Req_info */
+ Req_info->kind = NODE_UP_REQUEST;
+ memset(Req_info->node_id, -1, sizeof(int) * MAX_NUM_BACKENDS);
+ Req_info->master_node_id = get_next_master_node();
+ Req_info->conn_counter = 0;
+ Req_info->switching = false;
+
+ InRecovery = pool_shared_memory_create(sizeof(int));
+ *InRecovery = RECOVERY_INIT;
+
+ /*
+ * Initialize shared memory cache
+ */
+ if (pool_config->memory_cache_enabled)
+ {
+ if (pool_is_shmem_cache())
+ {
+ size_t size;
+
+ size = pool_shared_memory_cache_size();
+ pool_init_memory_cache(size);
+
+ size = pool_shared_memory_fsmm_size();
+ if (size == 0)
+ ereport(FATAL,
+ (errmsg("invalid shared memory size"),
+ errdetail("pool_shared_memory_fsmm_size error")));
+
+ pool_init_fsmm(size);
+
+ pool_allocate_fsmm_clock_hand();
+
+ pool_discard_oid_maps();
+
+ ereport(LOG,
+ (errmsg("pool_discard_oid_maps: discarded memqcache oid maps")));
+
+ pool_hash_init(pool_config->memqcache_max_num_cache);
+ }
+
+#ifdef USE_MEMCACHED
+ else
+ {
+ if (clear_memcache_oidmaps)
+ {
+ pool_discard_oid_maps();
+ ereport(LOG,
+ (errmsg("discarded memqcache oid maps")));
+ }
+ else
+ {
+ ereport(DEBUG1,
+ (errmsg("skipped discarding memqcache oid maps")));
+ }
+ }
+#endif
+
+ pool_init_memqcache_stats();
+ }
+}
+
+/*
+* Read the status file
+*/
+static int read_status_file(bool discard_status)
+{
+ FILE *fd;
+ char fnamebuf[POOLMAXPATHLEN];
+ int i;
+ bool someone_wakeup = false;
+
+ snprintf(fnamebuf, sizeof(fnamebuf), "%s/%s", pool_config->logdir, STATUS_FILE_NAME);
+ fd = fopen(fnamebuf, "r");
+ if (!fd)
+ {
+ ereport(LOG,
+ (errmsg("Backend status file %s does not exist", fnamebuf)));
+ return -1;
+ }
+
+ /*
+ * If discard_status is true, unlink pgpool_status and
+ * do not restore previous status.
+ */
+ if (discard_status)
+ {
+ fclose(fd);
+ if (unlink(fnamebuf) == 0)
+ {
+ ereport(LOG,
+ (errmsg("Backend status file %s discarded", fnamebuf)));
+ }
+ else
+ {
+ pool_error("Failed to discard backend status file %s reason:%s", fnamebuf, strerror(errno));
+ }
+ return 0;
+ }
+
+ if (fread(&backend_rec, 1, sizeof(backend_rec), fd) != sizeof(backend_rec))
+ {
+ pool_error("Could not read backend status file as %s. reason: %s",
+ fnamebuf, strerror(errno));
+ fclose(fd);
+ return -1;
+ }
+ fclose(fd);
+
+ for (i=0;i< pool_config->backend_desc->num_backends;i++)
+ {
+ if (backend_rec.status[i] == CON_DOWN)
+ {
+ BACKEND_INFO(i).backend_status = CON_DOWN;
+ ereport(LOG,
+ (errmsg("read_status_file: %d th backend is set to down status", i)));
+ }
+ else
+ {
+ BACKEND_INFO(i).backend_status = CON_CONNECT_WAIT;
+ someone_wakeup = true;
+ }
+ }
+
+ /*
+ * If no one woke up, we regard the status file bogus
+ */
+ if (someone_wakeup == false)
+ {
+ for (i=0;i< pool_config->backend_desc->num_backends;i++)
+ {
+ BACKEND_INFO(i).backend_status = CON_CONNECT_WAIT;
+ }
+ }
+
+ return 0;
+}
+
+/*
+* Write the pid file
+*/
+static int write_status_file()
+{
+ FILE *fd;
+ char fnamebuf[POOLMAXPATHLEN];
+ int i;
+
+ snprintf(fnamebuf, sizeof(fnamebuf), "%s/%s", pool_config->logdir, STATUS_FILE_NAME);
+ fd = fopen(fnamebuf, "w");
+ if (!fd)
+ {
+ pool_error("Could not open status file %s", fnamebuf);
+ return -1;
+ }
+
+ memset(&backend_rec, 0, sizeof(backend_rec));
+ if(pool_config)
+ {
+ for (i=0;i< pool_config->backend_desc->num_backends;i++)
+ {
+ backend_rec.status[i] = BACKEND_INFO(i).backend_status;
+ }
+
+ if (fwrite(&backend_rec, 1, sizeof(backend_rec), fd) != sizeof(backend_rec))
+ {
+ pool_error("Could not write backend status file as %s. reason: %s",
+ fnamebuf, strerror(errno));
+ fclose(fd);
+ return -1;
+ }
+ fclose(fd);
+ }
+ return 0;
+}
+
+
+static void reload_config(void)
+{
+ ereport(LOG,(errmsg("reload config files.")));
+ MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext);
+ pool_get_config(conf_file, RELOAD_CONFIG);
+ MemoryContextSwitchTo(oldContext);
+ if (pool_config->enable_pool_hba)
+ load_hba(hba_file);
+ if (pool_config->parallel_mode)
+ pool_memset_system_db_info(system_db_info->info);
+ kill_all_children(SIGHUP);
+
+ if (worker_pid)
+ kill(worker_pid, SIGHUP);
+}
+
+/* Call back function to unlink the file */
+static void FileUnlink(int code, Datum path)
+{
+ char* filePath = (char*)path;
+ if (unlink(filePath) == 0) return;
+ /*
+ * We are already exiting the system just produce a log entry to report an error
+ */
+ ereport(LOG,
+ (errmsg("unlink failed for file at path \"%s\"", filePath),
+ errdetail("%s", strerror(errno))));
+}
+
+static void system_will_go_down(int code, Datum arg)
+{
+ if(mypid != getpid())
+ {
+ /* should never happen */
+ ereport(LOG,("system_will_go_down called from invalid process"));
+ return;
+ }
+ POOL_SETMASK(&AuthBlockSig);
+ /* Write status file */
+ write_status_file();
+ /* terminate all childrens */
+ terminate_all_childrens();
+ processState = EXITING;
+ POOL_SETMASK(&UnBlockSig);
+
+}
diff --cc src/protocol/child.c
index 1a605570e,f84b46219..968f40597
--- a/src/protocol/child.c
+++ b/src/protocol/child.c
@@@ -100,8 -84,10 +100,12 @@@ static volatile sig_atomic_t alarm_enab
static int idle; /* non 0 means this child is in idle state */
static int accepted = 0;
- fd_set readmask;
- int nsocks;
+
++fd_set readmask;
++int nsocks;
+ static int child_inet_fd = 0;
+ static int child_unix_fd = 0;
+
extern int myargc;
extern char **myargv;
@@@ -152,21 -137,12 +156,25 @@@ void do_child(int unix_fd, int inet_fd
pool_set_nonblock(inet_fd);
}
#endif
- if (inet_fd)
- {
- child_inet_fd = inet_fd;
- }
+ nsocks =Max(unix_fd, inet_fd)+1;
+ FD_ZERO(&readmask);
+ FD_SET(unix_fd, &readmask);
+ if (inet_fd)
++ {
+ FD_SET(inet_fd, &readmask);
++ child_inet_fd = inet_fd;
++ }
+ child_unix_fd = unix_fd;
+ /* Create per loop iteration memory context */
+ ProcessLoopContext = AllocSetContextCreate(TopMemoryContext,
+ "pgpool_child_main_loop",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ MemoryContextSwitchTo(TopMemoryContext);
+
/* Initialize my backend status */
pool_initialize_private_backend_status();
diff --cc src/protocol/pool_proto_modules.c
index e42464dd4,ac89f86cb..df3cab739
--- a/src/protocol/pool_proto_modules.c
+++ b/src/protocol/pool_proto_modules.c
@@@ -2039,6 -2226,109 +2039,104 @@@ POOL_STATUS CommandComplete(POOL_CONNEC
return POOL_CONTINUE;
}
+ POOL_STATUS ParameterDescription(POOL_CONNECTION *frontend,
+ POOL_CONNECTION_POOL *backend)
+ {
+ int len, len1 = 0;
+ char *p = NULL;
+ char *p1 = NULL;
+ int status;
+ int sendlen;
+ int i;
+
+ POOL_SESSION_CONTEXT *session_context;
+ int num_params, send_num_params, num_dmy;
+ char kind = 't';
+
- session_context = pool_get_session_context();
- if (!session_context)
- {
- pool_error("ParameterDescription: cannot get session context");
- return POOL_END;
- }
++ session_context = pool_get_session_context(false);
+
+ /* only in replication mode and rewritten query */
+ if (!REPLICATION || !session_context->query_context->rewritten_query)
+ return SimpleForwardToFrontend('t', frontend, backend);
+
+ /* get number of parameters in original query */
+ num_params = session_context->query_context->num_original_params;
+
+ status = pool_read(MASTER(backend), &len, sizeof(len));
+ if (status < 0)
+ {
+ pool_error("ParameterDescription: error while reading message length");
+ return POOL_END;
+ }
+
+ len = ntohl(len);
+ len -= sizeof(int32);
+ len1 = len;
+
+ /* number of parameters in rewritten query is just discarded */
+ status = pool_read(MASTER(backend), &num_dmy, sizeof(int16));
+ len -= sizeof(int16);
+
+ p = pool_read2(MASTER(backend), len);
+ if (p == NULL)
+ return POOL_END;
+
+ p1 = malloc(len);
+ if (p1 == NULL)
+ {
+ pool_error("ParameterDescription: malloc failed");
+ return POOL_ERROR;
+ }
+ memcpy(p1, p, len);
+
+ for (i=0;i<NUM_BACKENDS;i++)
+ {
+ if (VALID_BACKEND(i) && !IS_MASTER_NODE_ID(i))
+ {
+ status = pool_read(CONNECTION(backend, i), &len, sizeof(len));
+ if (status < 0)
+ {
+ pool_error("ParameterDescription: error while reading message length");
+ return POOL_END;
+ }
+
+ len = ntohl(len);
+ len -= sizeof(int32);
+
+ p = pool_read2(CONNECTION(backend, i), len);
+ if (p == NULL)
+ return POOL_END;
+
+ if (len != len1)
+ {
+ pool_debug("ParameterDescription: length does not match between backends master(%d) %d th backend(%d) kind:(%c)",
+ len, i, len1, kind);
+ }
+ }
+ }
+
+ pool_write(frontend, &kind, 1);
+
+ /* send back OIDs of parameters in original query and left are discarded */
+ len = sizeof(int16) + num_params * sizeof(int32);
+ sendlen = htonl(len + sizeof(int32));
+ pool_write(frontend, &sendlen, sizeof(int32));
+
+ send_num_params = htons(num_params);
+ pool_write(frontend, &send_num_params, sizeof(int16));
+
+ if (pool_write_and_flush(frontend, p1, num_params * sizeof(int32)) < 0)
+ {
+ pool_error("ParameterDescription: pool_write_and_flush failed");
+ free(p1);
+ return POOL_END;
+ }
+
+ free(p1);
+
+ return POOL_CONTINUE;
+ }
+
POOL_STATUS ErrorResponse3(POOL_CONNECTION *frontend,
POOL_CONNECTION_POOL *backend)
{
[parent: 551101226e1b]