Adding log collector
authorMuhammad Usama <m.usama@HighGo.ca>
Fri, 26 Jun 2020 19:08:24 +0000 (00:08 +0500)
committerMuhammad Usama <m.usama@gmail.com>
Fri, 26 Jun 2020 19:14:03 +0000 (00:14 +0500)
Molding the syslogger of PostgreSQL to work with
Pgpool-II so that we can have log rotation

Documentation updates will follow soon.

30 files changed:
configure
src/Makefile.am
src/Makefile.in
src/config/pool_config_variables.c
src/include/main/pgpool_logger.h [new file with mode: 0644]
src/include/pool.h
src/include/pool_config.h
src/include/utils/timestamp.h [new file with mode: 0644]
src/libs/pcp/Makefile.in
src/main/main.c
src/main/pgpool_logger.c [new file with mode: 0644]
src/main/pgpool_main.c
src/main/pool_globals.c
src/pcp_con/pcp_child.c
src/sample/pgpool.conf.sample-logical
src/sample/pgpool.conf.sample-raw
src/sample/pgpool.conf.sample-replication
src/sample/pgpool.conf.sample-slony
src/sample/pgpool.conf.sample-snapshot
src/sample/pgpool.conf.sample-stream
src/test/regression/tests/010.rewrite_timestamp/timestamp/Makefile
src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c
src/tools/pcp/Makefile.in
src/utils/error/elog.c
src/watchdog/watchdog.c
src/watchdog/wd_escalation.c
src/watchdog/wd_heartbeat.c
src/watchdog/wd_if.c
src/watchdog/wd_lifecheck.c
src/watchdog/wd_ping.c

index 941a1b25cb5cd11f1d911b2b7ccbf605b62dbc0e..cd9b123850450ce685d1ca44220c797960601ab7 100755 (executable)
--- a/configure
+++ b/configure
@@ -793,6 +793,7 @@ with_pgsql_includedir
 with_pgsql_libdir
 with_sunifdef
 with_openssl
+with_ldap
 with_pam
 with_memcached
 enable_rpath
@@ -1462,6 +1463,7 @@ Optional Packages:
   --with-pgsql-libdir=DIR     site library files for PostgreSQL in DIR
   --with-sunifdef=DIR     install path for sunifdef utility
   --with-openssl     build with OpenSSL support
+  --with-ldap     build with LDAP support
   --with-pam     build with PAM support
   --with-memcached=DIR     site header files for libmemcached in DIR
 
 fi
 
 
+# Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+  withval=$with_ldap;
+$as_echo "#define USE_LDAP 1" >>confdefs.h
+
+fi
+
+if test "$with_ldap" = yes ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_bind in -lldap" >&5
+$as_echo_n "checking for ldap_bind in -lldap... " >&6; }
+if ${ac_cv_lib_ldap_ldap_bind+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lldap $EXTRA_LDAP_LIBS $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_bind ();
+int
+main ()
+{
+return ldap_bind ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_ldap_ldap_bind=yes
+else
+  ac_cv_lib_ldap_ldap_bind=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ldap_bind" >&5
+$as_echo "$ac_cv_lib_ldap_ldap_bind" >&6; }
+if test "x$ac_cv_lib_ldap_ldap_bind" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBLDAP 1
+_ACEOF
+
+  LIBS="-lldap $LIBS"
+
+else
+  as_fn_error $? "library 'ldap' is required for LDAP" "$LINENO" 5
+fi
+
+  for ac_header in ldap.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "ldap.h" "ac_cv_header_ldap_h" "$ac_includes_default"
+if test "x$ac_cv_header_ldap_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LDAP_H 1
+_ACEOF
+
+else
+  as_fn_error $? "header file <ldap.h> is required for LDAP" "$LINENO" 5
+fi
+
+done
+
+  for ac_func in ldap_initialize
+do :
+  ac_fn_c_check_func "$LINENO" "ldap_initialize" "ac_cv_func_ldap_initialize"
+if test "x$ac_cv_func_ldap_initialize" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LDAP_INITIALIZE 1
+_ACEOF
+
+fi
+done
+
+fi
+
+
 # Check whether --with-pam was given.
 if test "${with_pam+set}" = set; then :
   withval=$with_pam;
index dae2c26c4816840ad319328e5684e83bc941da17..65a17ab096514bf8a2b86f8b801211e23d4a0e73 100644 (file)
@@ -9,6 +9,7 @@ pgpool_SOURCES = main/main.c \
        main/pgpool_main.c \
        main/health_check.c \
        main/pool_internal_comms.c \
+       main/pgpool_logger.c \
        config/pool_config.l \
        config/pool_config_variables.c \
        pcp_con/pcp_child.c \
index 12ec70db48f05fb3fc5efc290fbd715db3870b97..e424d2a37bf656eb1d79a0025dfdeef6a0facc17 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -91,6 +81,9 @@ build_triplet = @build@
 host_triplet = @host@
 bin_PROGRAMS = pgpool$(EXEEXT)
 subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs config/pool_config.c \
+       $(top_srcdir)/ylwrap
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -101,7 +94,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -113,7 +105,7 @@ am__dirstamp = $(am__leading_dot)dirstamp
 am_pgpool_OBJECTS = main/main.$(OBJEXT) main/pool_globals.$(OBJEXT) \
        main/pgpool_main.$(OBJEXT) main/health_check.$(OBJEXT) \
        main/pool_internal_comms.$(OBJEXT) \
-       config/pool_config.$(OBJEXT) \
+       main/pgpool_logger.$(OBJEXT) config/pool_config.$(OBJEXT) \
        config/pool_config_variables.$(OBJEXT) \
        pcp_con/pcp_child.$(OBJEXT) pcp_con/pcp_worker.$(OBJEXT) \
        pcp_con/recovery.$(OBJEXT) auth/md5.$(OBJEXT) \
@@ -271,8 +263,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs \
-       $(top_srcdir)/ylwrap config/pool_config.c
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -431,7 +421,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -449,6 +438,7 @@ pgpool_SOURCES = main/main.c \
        main/pgpool_main.c \
        main/health_check.c \
        main/pool_internal_comms.c \
+       main/pgpool_logger.c \
        config/pool_config.l \
        config/pool_config_variables.c \
        pcp_con/pcp_child.c \
@@ -622,6 +612,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -696,6 +687,7 @@ main/pool_globals.$(OBJEXT): main/$(am__dirstamp)
 main/pgpool_main.$(OBJEXT): main/$(am__dirstamp)
 main/health_check.$(OBJEXT): main/$(am__dirstamp)
 main/pool_internal_comms.$(OBJEXT): main/$(am__dirstamp)
+main/pgpool_logger.$(OBJEXT): main/$(am__dirstamp)
 config/$(am__dirstamp):
        @$(MKDIR_P) config
        @: > config/$(am__dirstamp)
@@ -1164,8 +1156,6 @@ uninstall-am: uninstall-binPROGRAMS uninstall-pkgdataDATA \
        tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \
        uninstall-pkgdataDATA uninstall-sysconfDATA
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 2e641818f4f43c54bd675c413a9940248a59beb2..66998cc9eb0b2f618bda9c87fa01482eab6b579d 100644 (file)
@@ -712,6 +712,24 @@ static struct config_bool ConfigureNamesBool[] =
                false,
                NULL, NULL, NULL
        },
+       {
+               {"logging_collector", CFGCXT_INIT, LOGING_CONFIG,
+                       "Enable capturing of stderr into log files.",
+                       CONFIG_VAR_TYPE_BOOL, false, 0
+               },
+               &g_pool_config.logging_collector,
+               false,
+               NULL, NULL, NULL
+       },
+       {
+               {"log_truncate_on_rotation", CFGCXT_INIT, LOGING_CONFIG,
+                       "If on, an existing log file gets truncated on time based log rotation.",
+                       CONFIG_VAR_TYPE_BOOL, false, 0
+               },
+               &g_pool_config.log_truncate_on_rotation,
+               false,
+               NULL, NULL, NULL
+       },
 
        /* End-of-list marker */
        EMPTY_CONFIG_BOOL
@@ -1238,6 +1256,25 @@ static struct config_string ConfigureNamesString[] =
                DEFAULT_LOGDIR,
                NULL, NULL, NULL, NULL
        },
+       {
+               {"log_directory", CFGCXT_INIT, LOGING_CONFIG,
+                       "directory where log files are written.",
+                       CONFIG_VAR_TYPE_STRING, false, 0
+               },
+               &g_pool_config.log_directory,
+               "/tmp/pgpool_logs",
+               NULL, NULL, NULL, NULL
+       },
+
+       {
+               {"log_filename", CFGCXT_INIT, LOGING_CONFIG,
+                       "log file name pattern.",
+                       CONFIG_VAR_TYPE_STRING, false, 0
+               },
+               &g_pool_config.log_filename,
+               "pgpool-%Y-%m-%d_%H%M%S.log",
+               NULL, NULL, NULL, NULL
+       },
 
        /* End-of-list marker */
        EMPTY_CONFIG_STRING
@@ -2042,6 +2079,37 @@ static struct config_int ConfigureNamesInt[] =
                0, INT_MAX,
                NULL, NULL, NULL
        },
+       {
+               {"log_rotation_age", CFGCXT_INIT, LOGING_CONFIG,
+                       "Automatic rotation of logfiles will happen after that (minutes) time.",
+                       CONFIG_VAR_TYPE_INT, false, GUC_UNIT_MIN
+               },
+               &g_pool_config.log_rotation_age,
+               1440,/*1 day*/
+               10, INT_MAX,
+               NULL, NULL, NULL
+       },
+       {
+               {"log_rotation_size", CFGCXT_INIT, LOGING_CONFIG,
+                       "Automatic rotation of logfiles will happen after that much (kilobytes) log output.",
+                       CONFIG_VAR_TYPE_INT, false, GUC_UNIT_KB
+               },
+               &g_pool_config.log_rotation_size,
+               0,
+               1, INT_MAX,
+               NULL, NULL, NULL
+       },
+       {
+               {"log_file_mode", CFGCXT_INIT, LOGING_CONFIG,
+                       "creation mode for log files.",
+                       CONFIG_VAR_TYPE_INT, false, 0
+               },
+               &g_pool_config.log_file_mode,
+               0600,
+               0, INT_MAX,
+               NULL, NULL, NULL
+       },
+
 
        /* End-of-list marker */
        EMPTY_CONFIG_INT
diff --git a/src/include/main/pgpool_logger.h b/src/include/main/pgpool_logger.h
new file mode 100644 (file)
index 0000000..95675b1
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2020     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.
+ *
+ * ppgool_logger.h.: master definition header file
+ *
+ */
+/*-------------------------------------------------------------------------
+ * From: PostgreSQL
+ * src/include/postmaster/syslogger.h
+ *       Exports from postmaster/syslogger.c.
+ *
+ * Copyright (c) 2004-2020, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _SYSLOGGER_H
+#define _SYSLOGGER_H
+
+#include <limits.h>                            /* for PIPE_BUF */
+
+
+/*
+ * Primitive protocol structure for writing to syslogger pipe(s).  The idea
+ * here is to divide long messages into chunks that are not more than
+ * PIPE_BUF bytes long, which according to POSIX spec must be written into
+ * the pipe atomically.  The pipe reader then uses the protocol headers to
+ * reassemble the parts of a message into a single string.  The reader can
+ * also cope with non-protocol data coming down the pipe, though we cannot
+ * guarantee long strings won't get split apart.
+ *
+ * We use non-nul bytes in is_last to make the protocol a tiny bit
+ * more robust against finding a false double nul byte prologue. But
+ * we still might find it in the len and/or pid bytes unless we're careful.
+ */
+
+#ifdef PIPE_BUF
+/* Are there any systems with PIPE_BUF > 64K?  Unlikely, but ... */
+#if PIPE_BUF > 65536
+#define PIPE_CHUNK_SIZE  65536
+#else
+#define PIPE_CHUNK_SIZE  ((int) PIPE_BUF)
+#endif
+#else                                                  /* not defined */
+/* POSIX says the value of PIPE_BUF must be at least 512, so use that */
+#define PIPE_CHUNK_SIZE  512
+#endif
+
+typedef struct
+{
+       char            nuls[2];                /* always \0\0 */
+       uint16          len;                    /* size of this chunk (counts data only) */
+       int32           pid;                    /* writer's pid */
+       char            is_last;                /* last chunk of message? 't' or 'f' ('T' or
+                                                                * 'F' for CSV case) */
+       char            data[]; /* data payload starts here */
+} PipeProtoHeader;
+
+typedef union
+{
+       PipeProtoHeader proto;
+       char            filler[PIPE_CHUNK_SIZE];
+} PipeProtoChunk;
+
+#define PIPE_HEADER_SIZE  offsetof(PipeProtoHeader, data)
+#define PIPE_MAX_PAYLOAD  ((int) (PIPE_CHUNK_SIZE - PIPE_HEADER_SIZE))
+
+
+extern int     syslogPipe[2];
+extern bool redirection_done;
+
+
+extern int     SysLogger_Start(void);
+
+extern void write_syslogger_file(const char *buffer, int count, int dest);
+
+
+extern bool CheckLogrotateSignal(void);
+extern void RemoveLogrotateSignalFiles(void);
+
+
+#endif                                                 /* _SYSLOGGER_H */
index c17447cb0f3f94d05ebf735bf46a69cc088e58e8..7f395e03356f3a1bc71e1f055c8fdc48d380d5c5 100644 (file)
@@ -505,6 +505,7 @@ typedef enum
        PT_PCP,
        PT_PCP_WORKER,
        PT_HEALTH_CHECK,
+       PT_LOGGER,
        PT_LAST_PTYPE   /* last ptype marker. any ptype must be above this. */
 }                      ProcessType;
 
@@ -536,6 +537,7 @@ typedef struct
  * pool_global.c
  */
 extern pid_t mypid;                            /* parent pid */
+extern pid_t myProcPid;                        /* process pid */
 extern ProcessType processType;
 extern ProcessState processState;
 extern volatile SI_ManageInfo *si_manage_info;
index be81737b30a6c0951099c642c21e33ce59548dae..7eacd5b80b3441b443280e58104aa3f5075fba37 100644 (file)
@@ -261,6 +261,15 @@ typedef struct
                                                                                 * sent to client */
        int                     log_min_messages;       /* controls which message should be
                                                                         * emitted to server log */
+       /* log collector settings */
+       bool            logging_collector;
+       int                     log_rotation_age;
+       int                     log_rotation_size;
+       char            *log_directory;
+       char            *log_filename;
+       bool            log_truncate_on_rotation;
+       int                     log_file_mode;
+
        bool            master_slave_mode;      /* operate in master/slave mode */
        MasterSlaveSubModes master_slave_sub_mode;      /* either "slony" or "stream" */
        int64           delay_threshold;        /* If the standby server delays more than
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
new file mode 100644 (file)
index 0000000..78e65c4
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2020     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.
+ *
+ * ppgool_logger.h.: master definition header file
+ *
+ */
+/*-------------------------------------------------------------------------
+ *
+ * From: PostgreSQL
+ *    src/include/datatype/timestamp.h
+ *       Timestamp and Interval typedefs and related macros.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *-------------------------------------------------------------------------
+ */
+#ifndef DATATYPE_TIMESTAMP_H
+#define DATATYPE_TIMESTAMP_H
+
+/*
+ * Timestamp represents absolute time.
+ *
+ * Interval represents delta time. Keep track of months (and years), days,
+ * and hours/minutes/seconds separately since the elapsed time spanned is
+ * unknown until instantiated relative to an absolute time.
+ *
+ * Note that Postgres uses "time interval" to mean a bounded interval,
+ * consisting of a beginning and ending time, not a time span - thomas 97/03/20
+ *
+ * Timestamps, as well as the h/m/s fields of intervals, are stored as
+ * int64 values with units of microseconds.  (Once upon a time they were
+ * double values with units of seconds.)
+ *
+ * TimeOffset and fsec_t are convenience typedefs for temporary variables.
+ * Do not use fsec_t in values stored on-disk.
+ * Also, fsec_t is only meant for *fractional* seconds; beware of overflow
+ * if the value you need to store could be many seconds.
+ */
+
+typedef int64 Timestamp;
+typedef int64 TimestampTz;
+typedef int64 TimeOffset;
+typedef int32 fsec_t;                  /* fractional seconds (in microseconds) */
+
+typedef struct
+{
+       TimeOffset      time;                   /* all time units other than days, months and
+                                                                * years */
+       int32           day;                    /* days, after time for alignment */
+       int32           month;                  /* months and years, after time for alignment */
+} Interval;
+
+
+/* Limits on the "precision" option (typmod) for these data types */
+#define MAX_TIMESTAMP_PRECISION 6
+#define MAX_INTERVAL_PRECISION 6
+
+/*
+ *     Round off to MAX_TIMESTAMP_PRECISION decimal places.
+ *     Note: this is also used for rounding off intervals.
+ */
+#define TS_PREC_INV 1000000.0
+#define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
+
+
+/*
+ * Assorted constants for datetime-related calculations
+ */
+
+#define DAYS_PER_YEAR  365.25  /* assumes leap year every four years */
+#define MONTHS_PER_YEAR 12
+/*
+ *     DAYS_PER_MONTH is very imprecise.  The more accurate value is
+ *     365.2425/12 = 30.436875, or '30 days 10:29:06'.  Right now we only
+ *     return an integral number of days, but someday perhaps we should
+ *     also return a 'time' value to be used as well.  ISO 8601 suggests
+ *     30 days.
+ */
+#define DAYS_PER_MONTH 30              /* assumes exactly 30 days per month */
+#define HOURS_PER_DAY  24              /* assume no daylight savings time changes */
+
+/*
+ *     This doesn't adjust for uneven daylight savings time intervals or leap
+ *     seconds, and it crudely estimates leap years.  A more accurate value
+ *     for days per years is 365.2422.
+ */
+#define SECS_PER_YEAR  (36525 * 864)   /* avoid floating-point computation */
+#define SECS_PER_DAY   86400
+#define SECS_PER_HOUR  3600
+#define SECS_PER_MINUTE 60
+#define MINS_PER_HOUR  60
+
+#define USECS_PER_DAY  INT64CONST(86400000000)
+#define USECS_PER_HOUR INT64CONST(3600000000)
+#define USECS_PER_MINUTE INT64CONST(60000000)
+#define USECS_PER_SEC  INT64CONST(1000000)
+
+/*
+ * We allow numeric timezone offsets up to 15:59:59 either way from Greenwich.
+ * Currently, the record holders for wackiest offsets in actual use are zones
+ * Asia/Manila, at -15:56:00 until 1844, and America/Metlakatla, at +15:13:42
+ * until 1867.  If we were to reject such values we would fail to dump and
+ * restore old timestamptz values with these zone settings.
+ */
+#define MAX_TZDISP_HOUR                15      /* maximum allowed hour part */
+#define TZDISP_LIMIT           ((MAX_TZDISP_HOUR + 1) * SECS_PER_HOUR)
+
+/*
+ * DT_NOBEGIN represents timestamp -infinity; DT_NOEND represents +infinity
+ */
+#define DT_NOBEGIN             PG_INT64_MIN
+#define DT_NOEND               PG_INT64_MAX
+
+#define TIMESTAMP_NOBEGIN(j)   \
+       do {(j) = DT_NOBEGIN;} while (0)
+
+#define TIMESTAMP_IS_NOBEGIN(j) ((j) == DT_NOBEGIN)
+
+#define TIMESTAMP_NOEND(j)             \
+       do {(j) = DT_NOEND;} while (0)
+
+#define TIMESTAMP_IS_NOEND(j)  ((j) == DT_NOEND)
+
+#define TIMESTAMP_NOT_FINITE(j) (TIMESTAMP_IS_NOBEGIN(j) || TIMESTAMP_IS_NOEND(j))
+
+
+/*
+ * Julian date support.
+ *
+ * date2j() and j2date() nominally handle the Julian date range 0..INT_MAX,
+ * or 4714-11-24 BC to 5874898-06-03 AD.  In practice, date2j() will work and
+ * give correct negative Julian dates for dates before 4714-11-24 BC as well.
+ * We rely on it to do so back to 4714-11-01 BC.  Allowing at least one day's
+ * slop is necessary so that timestamp rotation doesn't produce dates that
+ * would be rejected on input.  For example, '4714-11-24 00:00 GMT BC' is a
+ * legal timestamptz value, but in zones east of Greenwich it would print as
+ * sometime in the afternoon of 4714-11-23 BC; if we couldn't process such a
+ * date we'd have a dump/reload failure.  So the idea is for IS_VALID_JULIAN
+ * to accept a slightly wider range of dates than we really support, and
+ * then we apply the exact checks in IS_VALID_DATE or IS_VALID_TIMESTAMP,
+ * after timezone rotation if any.  To save a few cycles, we can make
+ * IS_VALID_JULIAN check only to the month boundary, since its exact cutoffs
+ * are not very critical in this scheme.
+ *
+ * It is correct that JULIAN_MINYEAR is -4713, not -4714; it is defined to
+ * allow easy comparison to tm_year values, in which we follow the convention
+ * that tm_year <= 0 represents abs(tm_year)+1 BC.
+ */
+
+#define JULIAN_MINYEAR (-4713)
+#define JULIAN_MINMONTH (11)
+#define JULIAN_MINDAY (24)
+#define JULIAN_MAXYEAR (5874898)
+#define JULIAN_MAXMONTH (6)
+#define JULIAN_MAXDAY (3)
+
+#define IS_VALID_JULIAN(y,m,d) \
+       (((y) > JULIAN_MINYEAR || \
+         ((y) == JULIAN_MINYEAR && ((m) >= JULIAN_MINMONTH))) && \
+        ((y) < JULIAN_MAXYEAR || \
+         ((y) == JULIAN_MAXYEAR && ((m) < JULIAN_MAXMONTH))))
+
+/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */
+#define UNIX_EPOCH_JDATE               2440588 /* == date2j(1970, 1, 1) */
+#define POSTGRES_EPOCH_JDATE   2451545 /* == date2j(2000, 1, 1) */
+
+/*
+ * Range limits for dates and timestamps.
+ *
+ * We have traditionally allowed Julian day zero as a valid datetime value,
+ * so that is the lower bound for both dates and timestamps.
+ *
+ * The upper limit for dates is 5874897-12-31, which is a bit less than what
+ * the Julian-date code can allow.  For timestamps, the upper limit is
+ * 294276-12-31.  The int64 overflow limit would be a few days later; again,
+ * leaving some slop avoids worries about corner-case overflow, and provides
+ * a simpler user-visible definition.
+ */
+
+/* First allowed date, and first disallowed date, in Julian-date form */
+#define DATETIME_MIN_JULIAN (0)
+#define DATE_END_JULIAN (2147483494)   /* == date2j(JULIAN_MAXYEAR, 1, 1) */
+#define TIMESTAMP_END_JULIAN (109203528)       /* == date2j(294277, 1, 1) */
+
+/* Timestamp limits */
+#define MIN_TIMESTAMP  INT64CONST(-211813488000000000)
+/* == (DATETIME_MIN_JULIAN - POSTGRES_EPOCH_JDATE) * USECS_PER_DAY */
+#define END_TIMESTAMP  INT64CONST(9223371331200000000)
+/* == (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE) * USECS_PER_DAY */
+
+/* Range-check a date (given in Postgres, not Julian, numbering) */
+#define IS_VALID_DATE(d) \
+       ((DATETIME_MIN_JULIAN - POSTGRES_EPOCH_JDATE) <= (d) && \
+        (d) < (DATE_END_JULIAN - POSTGRES_EPOCH_JDATE))
+
+/* Range-check a timestamp */
+#define IS_VALID_TIMESTAMP(t)  (MIN_TIMESTAMP <= (t) && (t) < END_TIMESTAMP)
+
+#endif                                                 /* DATATYPE_TIMESTAMP_H */
index d956bb1b012e7cae2557f39bda579e5f1f178e41..58a6f018d73a80fdcb4a7dbd108a491c7d6c1b4a 100644 (file)
@@ -340,7 +340,7 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AM_CPPFLAGS = -D_GNU_SOURCE -DPOOL_PRIVATE -I @PGSQL_INCLUDE_DIR@
 lib_LTLIBRARIES = libpcp.la
-libpcp_la_LDFLAGS = -version-info 1:0:0
+libpcp_la_LDFLAGS = -version-info 2:0:0
 dist_libpcp_la_SOURCES = pcp.c \
                                        ../../utils/pool_path.c \
                                        ../../tools/fe_port.c \
index afffd3653055e89ffc73d64d8b4269006494da5f..0f79e7e567ded9301a3ea08c344127ddf9d0deca 100644 (file)
@@ -222,6 +222,7 @@ main(int argc, char **argv)
        hba_file = make_absolute_path(hba_file_path, base_dir);
 
        mypid = getpid();
+       myProcPid = mypid;
 
        pool_init_config();
 
@@ -478,6 +479,7 @@ daemonize(void)
 #endif
 
        mypid = getpid();
+       myProcPid = mypid;
        write_pid_file();
        if (chdir("/"))
                ereport(WARNING,
diff --git a/src/main/pgpool_logger.c b/src/main/pgpool_logger.c
new file mode 100644 (file)
index 0000000..0e6b56c
--- /dev/null
@@ -0,0 +1,1176 @@
+/* -*-pgpool_logger-c-*- */
+/*
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2020     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.
+ */
+/*-------------------------------------------------------------------------
+ *
+ * From: PostgreSQL
+ *       src/backend/postmaster/syslogger.c
+ * Copyright (c) 2004-2020, PostgreSQL Global Development Group
+ *-------------------------------------------------------------------------
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "pool.h"
+#include "pool_config.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+#include "utils/elog.h"
+#include "parser/pg_list.h"
+#include "parser/stringinfo.h"
+#include "utils/ps_status.h"
+#include "utils/timestamp.h"
+#include "utils/pool_signal.h"
+#include "main/pgpool_logger.h"
+
+#define DEVNULL "/dev/null"
+typedef int64 pg_time_t;
+/*
+ * We read() into a temp buffer twice as big as a chunk, so that any fragment
+ * left after processing can be moved down to the front and we'll still have
+ * room to read a full chunk.
+ */
+#define READ_BUF_SIZE (2 * PIPE_CHUNK_SIZE)
+
+/* Log rotation signal file path, relative to $PGDATA */
+#define LOGROTATE_SIGNAL_FILE  "logrotate"
+
+
+/*
+ * GUC parameters.  Logging_collector cannot be changed after postmaster
+ * start, but the rest can change at SIGHUP.
+ */
+
+
+bool redirection_done = false;
+
+/*
+ * Private state
+ */
+static pg_time_t next_rotation_time;
+static bool pipe_eof_seen = false;
+static bool rotation_disabled = false;
+static FILE *syslogFile = NULL;
+static FILE *csvlogFile = NULL;
+static pg_time_t first_syslogger_file_time = 0;
+static char *last_file_name = NULL;
+static char *last_csv_file_name = NULL;
+
+/*
+ * Buffers for saving partial messages from different backends.
+ *
+ * Keep NBUFFER_LISTS lists of these, with the entry for a given source pid
+ * being in the list numbered (pid % NBUFFER_LISTS), so as to cut down on
+ * the number of entries we have to examine for any one incoming message.
+ * There must never be more than one entry for the same source pid.
+ *
+ * An inactive buffer is not removed from its list, just held for re-use.
+ * An inactive buffer has pid == 0 and undefined contents of data.
+ */
+typedef struct
+{
+       int32           pid;                    /* PID of source process */
+       StringInfoData data;            /* accumulated data, as a StringInfo */
+} save_buffer;
+
+#define NBUFFER_LISTS 256
+static List *buffer_lists[NBUFFER_LISTS];
+
+int                    syslogPipe[2] = {-1, -1};
+
+
+/*
+ * Flags set by interrupt handlers for later service in the main loop.
+ */
+static volatile sig_atomic_t got_SIGHUP = false;
+static volatile sig_atomic_t rotation_requested = false;
+
+
+static void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
+static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
+static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
+static FILE *logfile_open(const char *filename, const char *mode,
+                                                 bool allow_errors);
+
+static void logfile_rotate(bool time_based_rotation, int size_rotation_for);
+static char *logfile_getname(pg_time_t timestamp, const char *suffix);
+static void set_next_rotation_time(void);
+static void sigHupHandler(int sig);
+static void sigUsr1Handler(int sig);
+
+
+/*
+ * Main entry point for syslogger process
+ * argc/argv parameters are valid only in EXEC_BACKEND case.
+ */
+static void
+SysLoggerMain(int argc, char *argv[])
+{
+       char            logbuffer[READ_BUF_SIZE];
+       int                     bytes_in_logbuffer = 0;
+       char       *currentLogDir;
+       char       *currentLogFilename;
+       int                     currentLogRotationAge;
+       pg_time_t       now;
+
+       now = time(NULL);
+
+
+       init_ps_display("", "", "", "");
+       set_ps_display("PgpoolLogger", false);
+
+       /*
+        * If we restarted, our stderr is already redirected into our own input
+        * pipe.  This is of course pretty useless, not to mention that it
+        * interferes with detecting pipe EOF.  Point stderr to /dev/null. This
+        * assumes that all interesting messages generated in the syslogger will
+        * come through elog.c and will be sent to write_syslogger_file.
+        */
+       if (redirection_done)
+       {
+               int                     fd = open(DEVNULL, O_WRONLY, 0);
+
+               /*
+                * The closes might look redundant, but they are not: we want to be
+                * darn sure the pipe gets closed even if the open failed.  We can
+                * survive running with stderr pointing nowhere, but we can't afford
+                * to have extra pipe input descriptors hanging around.
+                *
+                * As we're just trying to reset these to go to DEVNULL, there's not
+                * much point in checking for failure from the close/dup2 calls here,
+                * if they fail then presumably the file descriptors are closed and
+                * any writes will go into the bitbucket anyway.
+                */
+               close(fileno(stdout));
+               close(fileno(stderr));
+               if (fd != -1)
+               {
+                       (void) dup2(fd, fileno(stdout));
+                       (void) dup2(fd, fileno(stderr));
+                       close(fd);
+               }
+       }
+
+
+       /*
+        * Also close our copy of the write end of the pipe.  This is needed to
+        * ensure we can detect pipe EOF correctly.  (But note that in the restart
+        * case, the postmaster already did this.)
+        */
+       if (syslogPipe[1] >= 0)
+               close(syslogPipe[1]);
+       syslogPipe[1] = -1;
+
+       /*
+        * Properly accept or ignore signals the postmaster might send us
+        *
+        * Note: we ignore all termination signals, and instead exit only when all
+        * upstream processes are gone, to ensure we don't miss any dying gasps of
+        * broken backends...
+        */
+
+       pool_signal(SIGHUP, sigHupHandler);
+
+       pool_signal(SIGINT, SIG_IGN);
+       pool_signal(SIGTERM, SIG_IGN);
+       pool_signal(SIGQUIT, SIG_IGN);
+       pool_signal(SIGALRM, SIG_IGN);
+       pool_signal(SIGPIPE, SIG_IGN);
+       pool_signal(SIGUSR1, sigUsr1Handler);   /* request log rotation */
+       pool_signal(SIGUSR2, SIG_IGN);
+
+       /*
+        * Reset some signals that are accepted by postmaster but not here
+        */
+       pool_signal(SIGCHLD, SIG_DFL);
+
+       POOL_SETMASK(&UnBlockSig);
+
+
+       /*
+        * Remember active logfiles' name(s).  We recompute 'em from the reference
+        * time because passing down just the pg_time_t is a lot cheaper than
+        * passing a whole file path in the EXEC_BACKEND case.
+        */
+       last_file_name = logfile_getname(first_syslogger_file_time, NULL);
+       if (csvlogFile != NULL)
+               last_csv_file_name = logfile_getname(first_syslogger_file_time, ".csv");
+
+       /* remember active logfile parameters */
+       currentLogDir = pstrdup(pool_config->log_directory);
+       currentLogFilename = pstrdup(pool_config->log_filename);
+       currentLogRotationAge = pool_config->log_rotation_age;
+       /* set next planned rotation time */
+       set_next_rotation_time();
+
+       /*
+        * Set up a reusable WaitEventSet object we'll use to wait for our latch,
+        * and (except on Windows) our socket.
+        *
+        * Unlike all other postmaster child processes, we'll ignore postmaster
+        * death because we want to collect final log output from all backends and
+        * then exit last.  We'll do that by running until we see EOF on the
+        * syslog pipe, which implies that all other backends have exited
+        * (including the postmaster).
+        */
+
+       /* main worker loop */
+       for (;;)
+       {
+               bool            time_based_rotation = false;
+               int                     size_rotation_for = 0;
+               struct timeval timeout;
+               fd_set          rfds;
+               int                     rc;
+
+               /*
+                * Process any requests or signals received recently.
+                */
+               if (got_SIGHUP)
+               {
+                       got_SIGHUP = false;
+                       MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext);
+
+                       pool_get_config(get_config_file_name(), CFGCXT_RELOAD);
+                       MemoryContextSwitchTo(oldContext);
+
+                       /*
+                        * Check if the log directory or filename pattern changed in
+                        * pgpool.conf. If so, force rotation to make sure we're
+                        * writing the logfiles in the right place.
+                        */
+                       if (strcmp(pool_config->log_directory, currentLogDir) != 0)
+                       {
+                               pfree(currentLogDir);
+                               currentLogDir = pstrdup(pool_config->log_directory);
+                               rotation_requested = true;
+
+                               /*
+                                * Also, create new directory if not present; ignore errors
+                                */
+                               if (mkdir(pool_config->log_directory, S_IREAD | S_IWRITE | S_IEXEC) == -1)
+                               {
+                                       ereport(LOG,
+                                                       (errmsg("pgpool logger, failed to create directory:\"%s\". error:\"%s\"", pool_config->log_directory, strerror(errno))));
+                               }
+                       }
+                       if (strcmp(pool_config->log_filename, currentLogFilename) != 0)
+                       {
+                               pfree(currentLogFilename);
+                               currentLogFilename = pstrdup(pool_config->log_filename);
+                               rotation_requested = true;
+                       }
+
+                       /*
+                        * Force a rotation if CSVLOG output was just turned on or off and
+                        * we need to open or close csvlogFile accordingly.
+                        */
+                       if (((pool_config->log_destination & LOG_DESTINATION_CSVLOG) != 0) !=
+                               (csvlogFile != NULL))
+                               rotation_requested = true;
+
+                       /*
+                        * If rotation time parameter changed, reset next rotation time,
+                        * but don't immediately force a rotation.
+                        */
+                       if (currentLogRotationAge != pool_config->log_rotation_age)
+                       {
+                               currentLogRotationAge = pool_config->log_rotation_age;
+                               set_next_rotation_time();
+                       }
+
+                       /*
+                        * If we had a rotation-disabling failure, re-enable rotation
+                        * attempts after SIGHUP, and force one immediately.
+                        */
+                       if (rotation_disabled)
+                       {
+                               rotation_disabled = false;
+                               rotation_requested = true;
+                       }
+
+               }
+
+               if (pool_config->log_rotation_age > 0 && !rotation_disabled)
+               {
+                       /* Do a logfile rotation if it's time */
+                       now = (pg_time_t) time(NULL);
+                       if (now >= next_rotation_time)
+                               rotation_requested = time_based_rotation = true;
+               }
+
+               if (!rotation_requested && pool_config->log_rotation_size > 0 && !rotation_disabled)
+               {
+                       /* Do a rotation if file is too big */
+                       if (ftell(syslogFile) >= pool_config->log_rotation_size * 1024L)
+                       {
+                               rotation_requested = true;
+                               size_rotation_for |= LOG_DESTINATION_STDERR;
+                       }
+                       if (csvlogFile != NULL &&
+                               ftell(csvlogFile) >= pool_config->log_rotation_size * 1024L)
+                       {
+                               rotation_requested = true;
+                               size_rotation_for |= LOG_DESTINATION_CSVLOG;
+                       }
+               }
+
+               if (rotation_requested)
+               {
+                       /*
+                        * Force rotation when both values are zero. It means the request
+                        * was sent by pg_rotate_logfile() or "pg_ctl logrotate".
+                        */
+                       if (!time_based_rotation && size_rotation_for == 0)
+                               size_rotation_for = LOG_DESTINATION_STDERR | LOG_DESTINATION_CSVLOG;
+                       logfile_rotate(time_based_rotation, size_rotation_for);
+               }
+
+               /*
+                * Calculate time till next time-based rotation, so that we don't
+                * sleep longer than that.  We assume the value of "now" obtained
+                * above is still close enough.  Note we can't make this calculation
+                * until after calling logfile_rotate(), since it will advance
+                * next_rotation_time.
+                *
+                * Also note that we need to beware of overflow in calculation of the
+                * timeout: with large settings of pool_config->log_rotation_age, next_rotation_time
+                * could be more than INT_MAX msec in the future.  In that case we'll
+                * wait no more than INT_MAX msec, and try again.
+                */
+               timeout.tv_sec = 0;
+               if (pool_config->log_rotation_age > 0 && !rotation_disabled)
+               {
+                       pg_time_t       delay;
+
+                       delay = next_rotation_time - now;
+                       if (delay > 0)
+                       {
+                               if (delay > INT_MAX / 1000)
+                                       delay = INT_MAX / 1000;
+                               timeout.tv_sec = delay;
+                       }
+               }
+
+               /*
+                * Sleep until there's something to do
+                */
+               
+               FD_ZERO(&rfds);
+               FD_SET(syslogPipe[0], &rfds);
+               rc = select(syslogPipe[0] + 1, &rfds, NULL, NULL, timeout.tv_sec?&timeout:NULL);
+               if (rc == 1)
+               {
+                       int                     bytesRead;
+
+                       bytesRead = read(syslogPipe[0],
+                                                        logbuffer + bytes_in_logbuffer,
+                                                        sizeof(logbuffer) - bytes_in_logbuffer);
+                       if (bytesRead < 0)
+                       {
+                               if (errno != EINTR)
+                                       ereport(LOG,
+                                                       (errmsg("could not read from logger pipe: %m")));
+                       }
+                       else if (bytesRead > 0)
+                       {
+                               bytes_in_logbuffer += bytesRead;
+                               process_pipe_input(logbuffer, &bytes_in_logbuffer);
+                               continue;
+                       }
+                       else
+                       {
+                               /*
+                                * Zero bytes read when select() is saying read-ready means
+                                * EOF on the pipe: that is, there are no longer any processes
+                                * with the pipe write end open.  Therefore, the postmaster
+                                * and all backends are shut down, and we are done.
+                                */
+                               pipe_eof_seen = true;
+
+                               /* if there's any data left then force it out now */
+                               flush_pipe_input(logbuffer, &bytes_in_logbuffer);
+                       }
+               }
+
+               if (pipe_eof_seen)
+               {
+                       /*
+                        * seeing this message on the real stderr is annoying - so we make
+                        * it DEBUG1 to suppress in normal use.
+                        */
+                       ereport(DEBUG1,
+                                       (errmsg("logger shutting down")));
+
+                       /*
+                        * Normal exit from the syslogger is here.  Note that we
+                        * deliberately do not close syslogFile before exiting; this is to
+                        * allow for the possibility of elog messages being generated
+                        * inside proc_exit.  Regular exit() will take care of flushing
+                        * and closing stdio channels.
+                        */
+                       proc_exit(0);
+               }
+       }
+}
+
+/*
+ * Postmaster subroutine to start a syslogger subprocess.
+ */
+int
+SysLogger_Start(void)
+{
+       pid_t           sysloggerPid;
+       char       *filename;
+
+       if (!pool_config->logging_collector)
+               return 0;
+
+       /*
+        * If first time through, create the pipe which will receive stderr
+        * output.
+        *
+        * If the syslogger crashes and needs to be restarted, we continue to use
+        * the same pipe (indeed must do so, since extant backends will be writing
+        * into that pipe).
+        *
+        * This means the postmaster must continue to hold the read end of the
+        * pipe open, so we can pass it down to the reincarnated syslogger. This
+        * is a bit klugy but we have little choice.
+        *
+        * Also note that we don't bother counting the pipe FDs by calling
+        * Reserve/ReleaseExternalFD.  There's no real need to account for them
+        * accurately in the postmaster or syslogger process, and both ends of the
+        * pipe will wind up closed in all other postmaster children.
+        */
+       if (syslogPipe[0] < 0)
+       {
+               if (pipe(syslogPipe) < 0)
+                       ereport(FATAL,
+                                       (errmsg("could not create pipe for syslog: %m")));
+       }
+
+       /*
+        * Create log directory if not present; ignore errors
+        */
+       mkdir(pool_config->log_directory, S_IREAD | S_IWRITE | S_IEXEC);
+
+
+       /*
+        * The initial logfile is created right in the postmaster, to verify that
+        * the pool_config->log_directory is writable.  We save the reference time so that the
+        * syslogger child process can recompute this file name.
+        *
+        * It might look a bit strange to re-do this during a syslogger restart,
+        * but we must do so since the postmaster closed syslogFile after the
+        * previous fork (and remembering that old file wouldn't be right anyway).
+        * Note we always append here, we won't overwrite any existing file.  This
+        * is consistent with the normal rules, because by definition this is not
+        * a time-based rotation.
+        */
+       first_syslogger_file_time = time(NULL);
+
+       filename = logfile_getname(first_syslogger_file_time, NULL);
+
+       syslogFile = logfile_open(filename, "a", false);
+
+       pfree(filename);
+
+       /*
+        * Likewise for the initial CSV log file, if that's enabled.  (Note that
+        * we open syslogFile even when only CSV output is nominally enabled,
+        * since some code paths will write to syslogFile anyway.)
+        */
+       if (pool_config->log_destination & LOG_DESTINATION_CSVLOG)
+       {
+               filename = logfile_getname(first_syslogger_file_time, ".csv");
+
+               csvlogFile = logfile_open(filename, "a", false);
+
+               pfree(filename);
+       }
+
+       switch ((sysloggerPid = fork()))
+       {
+               case -1:
+                       ereport(LOG,
+                                       (errmsg("could not fork system logger: %m")));
+                       return 0;
+
+               case 0:
+                       on_exit_reset();
+                       myProcPid = getpid();
+                       processType = PT_LOGGER;
+                       /* do the work */
+                       SysLoggerMain(0, NULL);
+                       break;
+
+               default:
+                       /* success, in postmaster */
+
+                       /* now we redirect stderr, if not done already */
+                       if (!redirection_done)
+                       {
+
+                               /*
+                                * Leave a breadcrumb trail when redirecting, in case the user
+                                * forgets that redirection is active and looks only at the
+                                * original stderr target file.
+                                */
+                               ereport(LOG,
+                                               (errmsg("redirecting log output to logging collector process"),
+                                                errhint("Future log output will appear in directory \"%s\".",
+                                                                pool_config->log_directory)));
+
+#ifndef WIN32
+                               fflush(stdout);
+                               if (dup2(syslogPipe[1], fileno(stdout)) < 0)
+                                       ereport(FATAL,
+                                                       (errmsg("could not redirect stdout: %m")));
+                               fflush(stderr);
+                               if (dup2(syslogPipe[1], fileno(stderr)) < 0)
+                                       ereport(FATAL,
+                                                       (errmsg("could not redirect stderr: %m")));
+                               /* Now we are done with the write end of the pipe. */
+                               close(syslogPipe[1]);
+                               syslogPipe[1] = -1;
+#else
+
+                               /*
+                                * open the pipe in binary mode and make sure stderr is binary
+                                * after it's been dup'ed into, to avoid disturbing the pipe
+                                * chunking protocol.
+                                */
+                               fflush(stderr);
+                               fd = _open_osfhandle((intptr_t) syslogPipe[1],
+                                                                        _O_APPEND | _O_BINARY);
+                               if (dup2(fd, _fileno(stderr)) < 0)
+                                       ereport(FATAL,
+                                                       (errmsg("could not redirect stderr: %m")));
+                               close(fd);
+                               _setmode(_fileno(stderr), _O_BINARY);
+
+                               /*
+                                * Now we are done with the write end of the pipe.
+                                * CloseHandle() must not be called because the preceding
+                                * close() closes the underlying handle.
+                                */
+                               syslogPipe[1] = 0;
+#endif
+                               redirection_done = true;
+                       }
+
+                       /* postmaster will never write the file(s); close 'em */
+                       fclose(syslogFile);
+                       syslogFile = NULL;
+                       if (csvlogFile != NULL)
+                       {
+                               fclose(csvlogFile);
+                               csvlogFile = NULL;
+                       }
+                       return (int) sysloggerPid;
+       }
+
+       /* we should never reach here */
+       return 0;
+}
+
+
+/* --------------------------------
+ *             pipe protocol handling
+ * --------------------------------
+ */
+
+/*
+ * Process data received through the syslogger pipe.
+ *
+ * This routine interprets the log pipe protocol which sends log messages as
+ * (hopefully atomic) chunks - such chunks are detected and reassembled here.
+ *
+ * The protocol has a header that starts with two nul bytes, then has a 16 bit
+ * length, the pid of the sending process, and a flag to indicate if it is
+ * the last chunk in a message. Incomplete chunks are saved until we read some
+ * more, and non-final chunks are accumulated until we get the final chunk.
+ *
+ * All of this is to avoid 2 problems:
+ * . partial messages being written to logfiles (messes rotation), and
+ * . messages from different backends being interleaved (messages garbled).
+ *
+ * Any non-protocol messages are written out directly. These should only come
+ * from non-Pgpool sources, however (e.g. third party libraries writing to
+ * stderr).
+ *
+ * logbuffer is the data input buffer, and *bytes_in_logbuffer is the number
+ * of bytes present.  On exit, any not-yet-eaten data is left-justified in
+ * logbuffer, and *bytes_in_logbuffer is updated.
+ */
+static void
+process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
+{
+       char       *cursor = logbuffer;
+       int                     count = *bytes_in_logbuffer;
+       int                     dest = LOG_DESTINATION_STDERR;
+
+       /* While we have enough for a header, process data... */
+       while (count >= (int) (offsetof(PipeProtoHeader, data) + 1))
+       {
+               PipeProtoHeader p;
+               int                     chunklen;
+
+               /* Do we have a valid header? */
+               memcpy(&p, cursor, offsetof(PipeProtoHeader, data));
+               if (p.nuls[0] == '\0' && p.nuls[1] == '\0' &&
+                       p.len > 0 && p.len <= PIPE_MAX_PAYLOAD &&
+                       p.pid != 0 &&
+                       (p.is_last == 't' || p.is_last == 'f' ||
+                        p.is_last == 'T' || p.is_last == 'F'))
+               {
+                       List       *buffer_list;
+                       ListCell   *cell;
+                       save_buffer *existing_slot = NULL,
+                                          *free_slot = NULL;
+                       StringInfo      str;
+
+                       chunklen = PIPE_HEADER_SIZE + p.len;
+
+                       /* Fall out of loop if we don't have the whole chunk yet */
+                       if (count < chunklen)
+                               break;
+
+                       dest = (p.is_last == 'T' || p.is_last == 'F') ?
+                               LOG_DESTINATION_CSVLOG : LOG_DESTINATION_STDERR;
+
+                       /* Locate any existing buffer for this source pid */
+                       buffer_list = buffer_lists[p.pid % NBUFFER_LISTS];
+                       foreach(cell, buffer_list)
+                       {
+                               save_buffer *buf = (save_buffer *) lfirst(cell);
+
+                               if (buf->pid == p.pid)
+                               {
+                                       existing_slot = buf;
+                                       break;
+                               }
+                               if (buf->pid == 0 && free_slot == NULL)
+                                       free_slot = buf;
+                       }
+
+                       if (p.is_last == 'f' || p.is_last == 'F')
+                       {
+                               /*
+                                * Save a complete non-final chunk in a per-pid buffer
+                                */
+                               if (existing_slot != NULL)
+                               {
+                                       /* Add chunk to data from preceding chunks */
+                                       str = &(existing_slot->data);
+                                       appendBinaryStringInfo(str,
+                                                                                  cursor + PIPE_HEADER_SIZE,
+                                                                                  p.len);
+                               }
+                               else
+                               {
+                                       /* First chunk of message, save in a new buffer */
+                                       if (free_slot == NULL)
+                                       {
+                                               /*
+                                                * Need a free slot, but there isn't one in the list,
+                                                * so create a new one and extend the list with it.
+                                                */
+                                               free_slot = palloc(sizeof(save_buffer));
+                                               buffer_list = lappend(buffer_list, free_slot);
+                                               buffer_lists[p.pid % NBUFFER_LISTS] = buffer_list;
+                                       }
+                                       free_slot->pid = p.pid;
+                                       str = &(free_slot->data);
+                                       initStringInfo(str);
+                                       appendBinaryStringInfo(str,
+                                                                                  cursor + PIPE_HEADER_SIZE,
+                                                                                  p.len);
+                               }
+                       }
+                       else
+                       {
+                               /*
+                                * Final chunk --- add it to anything saved for that pid, and
+                                * either way write the whole thing out.
+                                */
+                               if (existing_slot != NULL)
+                               {
+                                       str = &(existing_slot->data);
+                                       appendBinaryStringInfo(str,
+                                                                                  cursor + PIPE_HEADER_SIZE,
+                                                                                  p.len);
+                                       write_syslogger_file(str->data, str->len, dest);
+                                       /* Mark the buffer unused, and reclaim string storage */
+                                       existing_slot->pid = 0;
+                                       pfree(str->data);
+                               }
+                               else
+                               {
+                                       /* The whole message was one chunk, evidently. */
+                                       write_syslogger_file(cursor + PIPE_HEADER_SIZE, p.len,
+                                                                                dest);
+                               }
+                       }
+
+                       /* Finished processing this chunk */
+                       cursor += chunklen;
+                       count -= chunklen;
+               }
+               else
+               {
+                       /* Process non-protocol data */
+
+                       /*
+                        * Look for the start of a protocol header.  If found, dump data
+                        * up to there and repeat the loop.  Otherwise, dump it all and
+                        * fall out of the loop.  (Note: we want to dump it all if at all
+                        * possible, so as to avoid dividing non-protocol messages across
+                        * logfiles.  We expect that in many scenarios, a non-protocol
+                        * message will arrive all in one read(), and we want to respect
+                        * the read() boundary if possible.)
+                        */
+                       for (chunklen = 1; chunklen < count; chunklen++)
+                       {
+                               if (cursor[chunklen] == '\0')
+                                       break;
+                       }
+                       /* fall back on the stderr log as the destination */
+                       write_syslogger_file(cursor, chunklen, LOG_DESTINATION_STDERR);
+                       cursor += chunklen;
+                       count -= chunklen;
+               }
+       }
+
+       /* We don't have a full chunk, so left-align what remains in the buffer */
+       if (count > 0 && cursor != logbuffer)
+               memmove(logbuffer, cursor, count);
+       *bytes_in_logbuffer = count;
+}
+
+/*
+ * Force out any buffered data
+ *
+ * This is currently used only at syslogger shutdown, but could perhaps be
+ * useful at other times, so it is careful to leave things in a clean state.
+ */
+static void
+flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
+{
+       int                     i;
+
+       /* Dump any incomplete protocol messages */
+       for (i = 0; i < NBUFFER_LISTS; i++)
+       {
+               List       *list = buffer_lists[i];
+               ListCell   *cell;
+
+               foreach(cell, list)
+               {
+                       save_buffer *buf = (save_buffer *) lfirst(cell);
+
+                       if (buf->pid != 0)
+                       {
+                               StringInfo      str = &(buf->data);
+
+                               write_syslogger_file(str->data, str->len,
+                                                                        LOG_DESTINATION_STDERR);
+                               /* Mark the buffer unused, and reclaim string storage */
+                               buf->pid = 0;
+                               pfree(str->data);
+                       }
+               }
+       }
+
+       /*
+        * Force out any remaining pipe data as-is; we don't bother trying to
+        * remove any protocol headers that may exist in it.
+        */
+       if (*bytes_in_logbuffer > 0)
+               write_syslogger_file(logbuffer, *bytes_in_logbuffer,
+                                                        LOG_DESTINATION_STDERR);
+       *bytes_in_logbuffer = 0;
+}
+
+
+/* --------------------------------
+ *             logfile routines
+ * --------------------------------
+ */
+
+/*
+ * Write text to the currently open logfile
+ *
+ * This is exported so that elog.c can call it when processType is PT_LOGGER;
+.
+ * This allows the syslogger process to record elog messages of its own,
+ * even though its stderr does not point at the syslog pipe.
+ */
+void
+write_syslogger_file(const char *buffer, int count, int destination)
+{
+       int                     rc;
+       FILE       *logfile;
+
+       /*
+        * If we're told to write to csvlogFile, but it's not open, dump the data
+        * to syslogFile (which is always open) instead.  This can happen if CSV
+        * output is enabled after postmaster start and we've been unable to open
+        * csvlogFile.  There are also race conditions during a parameter change
+        * whereby backends might send us CSV output before we open csvlogFile or
+        * after we close it.  Writing CSV-formatted output to the regular log
+        * file isn't great, but it beats dropping log output on the floor.
+        *
+        * Think not to improve this by trying to open csvlogFile on-the-fly.  Any
+        * failure in that would lead to recursion.
+        */
+       logfile = (destination == LOG_DESTINATION_CSVLOG &&
+                          csvlogFile != NULL) ? csvlogFile : syslogFile;
+
+       rc = fwrite(buffer, 1, count, logfile);
+
+       /*
+        * Try to report any failure.  We mustn't use ereport because it would
+        * just recurse right back here, but write_stderr is OK: it will write
+        * either to the postmaster's original stderr, or to /dev/null, but never
+        * to our input pipe which would result in a different sort of looping.
+        */
+       if (rc != count)
+               write_stderr("could not write to log file: %s\n", strerror(errno));
+}
+
+
+/*
+ * Open a new logfile with proper permissions and buffering options.
+ *
+ * If allow_errors is true, we just log any open failure and return NULL
+ * (with errno still correct for the fopen failure).
+ * Otherwise, errors are treated as fatal.
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode, bool allow_errors)
+{
+       FILE       *fh;
+       mode_t          oumask;
+
+       /*
+        * Note we do not let pool_config->log_file_mode disable IWUSR, since we certainly want
+        * to be able to write the files ourselves.
+        */
+       oumask = umask((mode_t) ((~(pool_config->log_file_mode | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+       fh = fopen(filename, mode);
+       umask(oumask);
+
+       if (fh)
+       {
+               setvbuf(fh, NULL, _IOLBF, 0);
+
+#ifdef WIN32
+               /* use CRLF line endings on Windows */
+               _setmode(_fileno(fh), _O_TEXT);
+#endif
+       }
+       else
+       {
+               int                     save_errno = errno;
+
+               ereport(allow_errors ? LOG : FATAL,
+                               (errmsg("could not open log file \"%s\": %m",
+                                               filename)));
+               errno = save_errno;
+       }
+
+       return fh;
+}
+
+/*
+ * perform logfile rotation
+ */
+static void
+logfile_rotate(bool time_based_rotation, int size_rotation_for)
+{
+       char       *filename;
+       char       *csvfilename = NULL;
+       pg_time_t       fntime;
+       FILE       *fh;
+
+       rotation_requested = false;
+
+       /*
+        * When doing a time-based rotation, invent the new logfile name based on
+        * the planned rotation time, not current time, to avoid "slippage" in the
+        * file name when we don't do the rotation immediately.
+        */
+       if (time_based_rotation)
+               fntime = next_rotation_time;
+       else
+               fntime = time(NULL);
+       filename = logfile_getname(fntime, NULL);
+       if (pool_config->log_destination & LOG_DESTINATION_CSVLOG)
+               csvfilename = logfile_getname(fntime, ".csv");
+
+       /*
+        * Decide whether to overwrite or append.  We can overwrite if (a)
+        * pool_config->log_truncate_on_rotation is set, (b) the rotation was triggered by
+        * elapsed time and not something else, and (c) the computed file name is
+        * different from what we were previously logging into.
+        *
+        * Note: last_file_name should never be NULL here, but if it is, append.
+        */
+       if (time_based_rotation || (size_rotation_for & LOG_DESTINATION_STDERR))
+       {
+               if (pool_config->log_truncate_on_rotation && time_based_rotation &&
+                       last_file_name != NULL &&
+                       strcmp(filename, last_file_name) != 0)
+                       fh = logfile_open(filename, "w", true);
+               else
+                       fh = logfile_open(filename, "a", true);
+
+               if (!fh)
+               {
+                       /*
+                        * ENFILE/EMFILE are not too surprising on a busy system; just
+                        * keep using the old file till we manage to get a new one.
+                        * Otherwise, assume something's wrong with pool_config->log_directory and stop
+                        * trying to create files.
+                        */
+                       if (errno != ENFILE && errno != EMFILE)
+                       {
+                               ereport(LOG,
+                                               (errmsg("disabling automatic rotation (use SIGHUP to re-enable)")));
+                               rotation_disabled = true;
+                       }
+
+                       if (filename)
+                               pfree(filename);
+                       if (csvfilename)
+                               pfree(csvfilename);
+                       return;
+               }
+
+               fclose(syslogFile);
+               syslogFile = fh;
+
+               /* instead of pfree'ing filename, remember it for next time */
+               if (last_file_name != NULL)
+                       pfree(last_file_name);
+               last_file_name = filename;
+               filename = NULL;
+       }
+
+       /*
+        * Same as above, but for csv file.  Note that if LOG_DESTINATION_CSVLOG
+        * was just turned on, we might have to open csvlogFile here though it was
+        * not open before.  In such a case we'll append not overwrite (since
+        * last_csv_file_name will be NULL); that is consistent with the normal
+        * rules since it's not a time-based rotation.
+        */
+       if ((pool_config->log_destination & LOG_DESTINATION_CSVLOG) &&
+               (csvlogFile == NULL ||
+                time_based_rotation || (size_rotation_for & LOG_DESTINATION_CSVLOG)))
+       {
+               if (pool_config->log_truncate_on_rotation && time_based_rotation &&
+                       last_csv_file_name != NULL &&
+                       strcmp(csvfilename, last_csv_file_name) != 0)
+                       fh = logfile_open(csvfilename, "w", true);
+               else
+                       fh = logfile_open(csvfilename, "a", true);
+
+               if (!fh)
+               {
+                       /*
+                        * ENFILE/EMFILE are not too surprising on a busy system; just
+                        * keep using the old file till we manage to get a new one.
+                        * Otherwise, assume something's wrong with pool_config->log_directory and stop
+                        * trying to create files.
+                        */
+                       if (errno != ENFILE && errno != EMFILE)
+                       {
+                               ereport(LOG,
+                                               (errmsg("disabling automatic rotation (use SIGHUP to re-enable)")));
+                               rotation_disabled = true;
+                       }
+
+                       if (filename)
+                               pfree(filename);
+                       if (csvfilename)
+                               pfree(csvfilename);
+                       return;
+               }
+
+               if (csvlogFile != NULL)
+                       fclose(csvlogFile);
+               csvlogFile = fh;
+
+               /* instead of pfree'ing filename, remember it for next time */
+               if (last_csv_file_name != NULL)
+                       pfree(last_csv_file_name);
+               last_csv_file_name = csvfilename;
+               csvfilename = NULL;
+       }
+       else if (!(pool_config->log_destination & LOG_DESTINATION_CSVLOG) &&
+                        csvlogFile != NULL)
+       {
+               /* CSVLOG was just turned off, so close the old file */
+               fclose(csvlogFile);
+               csvlogFile = NULL;
+               if (last_csv_file_name != NULL)
+                       pfree(last_csv_file_name);
+               last_csv_file_name = NULL;
+       }
+
+       if (filename)
+               pfree(filename);
+       if (csvfilename)
+               pfree(csvfilename);
+
+       set_next_rotation_time();
+}
+
+
+/*
+ * construct logfile name using timestamp information
+ *
+ * If suffix isn't NULL, append it to the name, replacing any ".log"
+ * that may be in the pattern.
+ *
+ * Result is palloc'd.
+ */
+static char *
+logfile_getname(pg_time_t timestamp, const char *suffix)
+{
+       char       *filename;
+       int                     len;
+
+       filename = palloc(MAXPGPATH);
+
+       snprintf(filename, MAXPGPATH, "%s/", pool_config->log_directory);
+
+       len = strlen(filename);
+
+       /* treat pool_config->log_filename as a strftime pattern */
+//     strftime(strbuf, 128, "%Y-%m-%d %H:%M:%S", localtime(&now));
+       strftime(filename + len, MAXPGPATH - len, pool_config->log_filename,
+                               localtime(&timestamp));
+
+       if (suffix != NULL)
+       {
+               len = strlen(filename);
+               if (len > 4 && (strcmp(filename + (len - 4), ".log") == 0))
+                       len -= 4;
+               strlcpy(filename + len, suffix, MAXPGPATH - len);
+       }
+
+       return filename;
+}
+
+/*
+ * Determine the next planned rotation time, and store in next_rotation_time.
+ */
+static void
+set_next_rotation_time(void)
+{
+       pg_time_t       now;
+       struct tm *tm;
+       int                     rotinterval;
+
+       /* nothing to do if time-based rotation is disabled */
+       if (pool_config->log_rotation_age <= 0)
+               return;
+
+       /*
+        * The requirements here are to choose the next time > now that is a
+        * "multiple" of the log rotation interval.  "Multiple" can be interpreted
+        * fairly loosely.  In this version we align to log_timezone rather than
+        * GMT.
+        */
+       rotinterval = pool_config->log_rotation_age * SECS_PER_MINUTE;  /* convert to seconds */
+       now = (pg_time_t) time(NULL);
+       tm = localtime(&now);
+       now += tm->tm_gmtoff;
+       now -= now % rotinterval;
+       now += rotinterval;
+       now -= tm->tm_gmtoff;
+       next_rotation_time = now;
+}
+
+
+/* --------------------------------
+ *             signal handler routines
+ * --------------------------------
+ */
+
+/*
+ * Check to see if a log rotation request has arrived.  Should be
+ * called by postmaster after receiving SIGUSR1.
+ */
+bool
+CheckLogrotateSignal(void)
+{
+       struct stat stat_buf;
+
+       if (stat(LOGROTATE_SIGNAL_FILE, &stat_buf) == 0)
+               return true;
+
+       return false;
+}
+
+/*
+ * Remove the file signaling a log rotation request.
+ */
+void
+RemoveLogrotateSignalFiles(void)
+{
+       unlink(LOGROTATE_SIGNAL_FILE);
+}
+
+/* SIGHUP: set flag to reload config file */
+static void
+sigHupHandler(int sig)
+{
+       int                     save_errno = errno;
+
+       got_SIGHUP = true;
+//     SetLatch(MyLatch);
+
+       errno = save_errno;
+}
+
+/* SIGUSR1: set flag to rotate logfile */
+static void
+sigUsr1Handler(int sig)
+{
+       int                     save_errno = errno;
+
+       rotation_requested = true;
+//     SetLatch(MyLatch);
+
+       errno = save_errno;
+}
index a2158ec326ff1399d20dffb1d09f824d9bf30c5b..86ef9b16bedfb73afe60d0bdbf296d8df36b2a1c 100644 (file)
@@ -39,6 +39,7 @@
 #include "pool_config.h"
 #include "main/health_check.h"
 #include "main/pool_internal_comms.h"
+#include "main/pgpool_logger.h"
 #include "utils/elog.h"
 #include "utils/palloc.h"
 #include "utils/memutils.h"
@@ -138,7 +139,7 @@ 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 terminate_all_childrens(int sig);
 static void system_will_go_down(int code, Datum arg);
 static char *process_name_from_pid(pid_t pid);
 static void sync_backend_from_watchdog(void);
@@ -170,7 +171,7 @@ BACKEND_STATUS private_backend_status[MAX_NUM_BACKENDS];
  */
 ConnectionInfo *con_info;
 
-static int *fds;                               /* listening file descriptors (UNIX socket,
+static int *fds = NULL;                                /* listening file descriptors (UNIX socket,
                                                                 * inet domain sockets) */
 
 static int     pcp_unix_fd;            /* unix domain socket fd for PCP (not used) */
@@ -200,6 +201,7 @@ static pid_t follow_pid = 0;        /* pid for child process handling follow
                                                                 * command */
 static pid_t pcp_pid = 0;              /* pid for child process handling PCP */
 static pid_t watchdog_pid = 0; /* pid for watchdog child process */
+static pid_t pgpool_logger_pid = 0; /* pid for pgpool_logger process */
 static pid_t wd_lifecheck_pid = 0;     /* pid for child process handling watchdog
                                                                         * lifecheck */
 
@@ -264,13 +266,28 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
        /* set up signal handlers */
        pool_signal(SIGPIPE, SIG_IGN);
 
-       /* create unix domain socket */
-       fds = malloc(sizeof(int) * 2);
-       if (fds == NULL)
-               ereport(FATAL,
-                               (errmsg("failed to allocate memory in startup process")));
+
+       /* start the log collector if enabled */
+       pgpool_logger_pid = SysLogger_Start();
+    /*
+     * If using syslogger, close the read side of the pipe.  We don't bother
+     * tracking this in fd.c, either.
+     */
+       if (syslogPipe[0] >= 0)
+               close(syslogPipe[0]);
+       syslogPipe[0] = -1;
 
        initialize_shared_mem_objects(clear_memcache_oidmaps);
+
+       /* setup signal handlers */
+       pool_signal(SIGCHLD, reap_handler);
+       pool_signal(SIGUSR1, sigusr1_handler);
+       pool_signal(SIGUSR2, wakeup_handler);
+       pool_signal(SIGTERM, exit_handler);
+       pool_signal(SIGINT, exit_handler);
+       pool_signal(SIGQUIT, exit_handler);
+       pool_signal(SIGHUP, reload_config_handler);
+
        if (pool_config->use_watchdog)
        {
                sigset_t        mask;
@@ -278,20 +295,15 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
                wakeup_request = 0;
 
                /*
-                * Watchdog process fires SIGUSR2 once in stable state, so install the
-                * SIGUSR2 handler first up. In addition, when wathcodg fails to start
-                * with FATAL, the process exits and SIGCHLD is fired, so SIGCHLD
-                * handelr is also needed. Finally, we also need to set the SIGUSR1
-                * handler for the failover requests from other watchdog nodes. In
+                * Watchdog process fires SIGUSR2 once in stable state
+                * In addition, when wathcodg fails to start with FATAL, the process
+                * exits and SIGCHLD is fired, so we can also expect SIGCHLD from
+                * watchdog process. Finally, we also need to look for the SIGUSR1
+                * signla for the failover requests from other watchdog nodes. In
                 * case a request arrives at the same time when the watchdog has just
                 * been initialized.
-                */
-               pool_signal(SIGUSR2, wakeup_handler);
-               pool_signal(SIGCHLD, reap_handler);
-               pool_signal(SIGUSR1, sigusr1_handler);
-
-               /*
-                * okay as we need to wait until watchdog is in stable state so only
+                *
+                * So we need to wait until watchdog is in stable state so only
                 * wait for SIGUSR1, SIGCHLD, and signals those are necessary to make
                 * sure we respond to user requests of shutdown if it arrives while we
                 * are in waiting state.
@@ -309,6 +321,8 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
                sigdelset(&mask, SIGTERM);
                sigdelset(&mask, SIGINT);
                sigdelset(&mask, SIGQUIT);
+               sigdelset(&mask, SIGHUP);
+
                watchdog_pid = initialize_watchdog();
                ereport(LOG,
                                (errmsg("waiting for watchdog to initialize")));
@@ -340,6 +354,12 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
                }
        }
 
+       /* create unix domain socket */
+       fds = malloc(sizeof(int) * 2);
+       if (fds == NULL)
+               ereport(FATAL,
+                               (errmsg("failed to allocate memory in startup process")));
+
        fds[0] = create_unix_domain_socket(un_addr);
        fds[1] = -1;
        on_proc_exit(FileUnlink, (Datum) un_addr.sun_path);
@@ -386,16 +406,6 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
                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, sigusr1_handler);
-       pool_signal(SIGUSR2, wakeup_handler);
-       pool_signal(SIGHUP, reload_config_handler);
-
        /* create pipe for delivering event */
        if (pipe(pipe_fds) < 0)
        {
@@ -612,7 +622,7 @@ pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file)
 
                /* Set the process type variable */
                processType = PT_PCP;
-
+               myProcPid = getpid();
                /* call PCP child main */
                POOL_SETMASK(&UnBlockSig);
                health_check_timer_expired = 0;
@@ -706,6 +716,7 @@ worker_fork_a_child(ProcessType type, void (*func) (), void *params)
 
                /* Set the process type variable */
                processType = type;
+               myProcPid = getpid();
                set_application_name(type);
 
                /* call child main */
@@ -1002,38 +1013,74 @@ create_unix_domain_socket(struct sockaddr_un un_addr_tmp)
 }
 
 /*
- * function called as shared memory exit call back to kill all childrens
+ * sends the kill signal to all Pgpool children except to
+ * the pgpool_logger child
+ * wait for the termination of all killed children before returning.
  */
 static void
-terminate_all_childrens()
+terminate_all_childrens(int sig)
 {
        pid_t           wpid;
        int                     i;
-
+       int                     killed_count = 0;
+       int                     terminated_count = 0;
        /*
         * This is supposed to be called from main process
         */
        if (processType != PT_MAIN)
                return;
-       POOL_SETMASK(&BlockSig);
 
-       kill_all_children(SIGINT);
+       if (sig != SIGTERM && sig != SIGINT && sig != SIGQUIT)
+       {
+               ereport(LOG,
+                               (errmsg("invalid terminate signal: \"%d\"", sig),
+                                errdetail("ignoring")));
+
+               return;
+       }
+
+       for (i = 0; i < pool_config->num_init_children; i++)
+       {
+               pid_t           pid = process_info[i].pid;
+
+               if (pid != 0)
+               {
+                       kill(pid, sig);
+                       process_info[i].pid = 0;
+                       killed_count++;
+               }
+       }
+
        if (pcp_pid != 0)
-               kill(pcp_pid, SIGINT);
+       {
+               kill(pcp_pid, sig);
+               killed_count++;
+       }
        pcp_pid = 0;
+
        if (worker_pid != 0)
-               kill(worker_pid, SIGINT);
+       {
+               kill(worker_pid, sig);
+               killed_count++;
+       }
        worker_pid = 0;
+
        if (pool_config->use_watchdog)
        {
                if (pool_config->use_watchdog)
                {
                        if (watchdog_pid)
-                               kill(watchdog_pid, SIGINT);
+                       {
+                               kill(watchdog_pid, sig);
+                               killed_count++;
+                       }
                        watchdog_pid = 0;
 
                        if (wd_lifecheck_pid)
-                               kill(wd_lifecheck_pid, SIGINT);
+                       {
+                               kill(wd_lifecheck_pid, sig);
+                               killed_count++;
+                       }
                        wd_lifecheck_pid = 0;
                }
        }
@@ -1042,33 +1089,32 @@ terminate_all_childrens()
        {
                if (health_check_pids[i] != 0)
                {
-                       kill(health_check_pids[i], SIGINT);
+                       kill(health_check_pids[i], sig);
                        health_check_pids[i] = 0;
+                       killed_count++;
                }
        }
-
-       /* wait for all children to exit */
+       /* wait for all killed children to exit */
        do
        {
                int                     ret_pid;
 
                wpid = waitpid(-1, &ret_pid, 0);
-       } while (wpid > 0 || (wpid == -1 && errno == EINTR));
+               if (wpid > 0)
+                       terminated_count++;
+       } while (terminated_count < killed_count &&
+                        (wpid > 0 || (wpid == -1 && errno == EINTR)));
 
        if (wpid == -1 && errno != ECHILD)
                ereport(LOG,
                                (errmsg("wait() failed. reason:%s", strerror(errno))));
 
-       POOL_SETMASK(&UnBlockSig);
 }
 
 
 static RETSIGTYPE exit_handler(int sig)
 {
-       int                     i;
-       pid_t           wpid;
        int                *walk;
-
        int                     save_errno = errno;
 
        POOL_SETMASK(&AuthBlockSig);
@@ -1092,57 +1138,23 @@ static RETSIGTYPE exit_handler(int sig)
        exiting = 1;
        processState = EXITING;
 
-       /* Close listen socket */
-       for (walk = fds; *walk != -1; walk++)
-               close(*walk);
-
-       for (i = 0; i < pool_config->num_init_children; i++)
-       {
-               pid_t           pid = process_info[i].pid;
-
-               if (pid != 0)
-               {
-                       kill(pid, sig);
-                       process_info[i].pid = 0;
-               }
-       }
-
-       for (i = 0; i < MAX_NUM_BACKENDS; i++)
+       ereport(LOG,
+                       (errmsg("shutting down")));
+       /* Close listen socket if they are already initialized */
+       if (fds)
        {
-               if (health_check_pids[i] != 0)
-               {
-                       kill(health_check_pids[i], sig);
-                       health_check_pids[i] = 0;
-               }
+               for (walk = fds; *walk != -1; walk++)
+                       close(*walk);
        }
 
-       if (pcp_pid != 0)
-               kill(pcp_pid, sig);
-       pcp_pid = 0;
-
-       if (worker_pid != 0)
-               kill(worker_pid, sig);
-       worker_pid = 0;
-
-       if (pool_config->use_watchdog)
-       {
-               if (watchdog_pid != 0)
-                       kill(watchdog_pid, sig);
-               watchdog_pid = 0;
+       ereport(LOG,
+                       (errmsg("terminating all child processes")));
+       terminate_all_childrens(sig);
 
-               if (wd_lifecheck_pid != 0)
-                       kill(wd_lifecheck_pid, sig);
-               wd_lifecheck_pid = 0;
-       }
 
        POOL_SETMASK(&UnBlockSig);
-       do
-       {
-               int                     ret_pid;
-
-               wpid = waitpid(-1, &ret_pid, 0);
-       } while (wpid > 0 || (wpid == -1 && errno == EINTR));
-
+       ereport(LOG,
+                       (errmsg("Pgpool-II system is shutdown")));
        process_info = NULL;
        exit(0);
 }
@@ -2207,6 +2219,8 @@ process_name_from_pid(pid_t pid)
                else if (pid == wd_lifecheck_pid)
                        return "watchdog lifecheck";
        }
+       if (pid == pgpool_logger_pid)
+               return "pgpool log collector";
        return "child";
 }
 
@@ -2329,6 +2343,15 @@ reaper(void)
                        else
                                worker_pid = 0;
                }
+               else if (pid == pgpool_logger_pid)
+               {
+                       if (restart_child)
+                       {
+                               pgpool_logger_pid = SysLogger_Start();
+                       }
+                       else
+                               pgpool_logger_pid = 0;
+               }
 
                /* exiting process was watchdog process */
                else if (pool_config->use_watchdog)
@@ -3229,7 +3252,7 @@ fork_follow_child(int old_master, int new_primary, int old_primary)
        {
                on_exit_reset();
                processType = PT_FOLLOWCHILD;
-
+               myProcPid = getpid();
                ereport(LOG,
                                (errmsg("start triggering follow command.")));
                for (i = 0; i < pool_config->backend_desc->num_backends; i++)
@@ -3714,8 +3737,13 @@ system_will_go_down(int code, Datum arg)
         * Terminate all childrens. But we may already have killed all the
         * childrens if we come to this function because of shutdown signal.
         */
+
        if (processState != EXITING)
-               terminate_all_childrens();
+       {
+               ereport(LOG,
+                               (errmsg("shutting down")));
+               terminate_all_childrens(SIGINT);
+       }
        processState = EXITING;
        POOL_SETMASK(&UnBlockSig);
 
index 517701bd3f0c7e9502249da4da27bfa5ebba129b..45a36750ac74b7b4694606ed63c00c46f6f2ffdc 100644 (file)
@@ -23,5 +23,6 @@
 #include "pool.h"
 
 pid_t          mypid;                          /* pgpool parent process id */
+pid_t          myProcPid;              /* process pid */
 ProcessType processType;
 ProcessState processState;
index 8ca00fa519293bef299c0d06cd3f5639e78f1fd6..d60acc00bc07ddc7c1d54a717795158f2b1cb1bd 100644 (file)
@@ -280,6 +280,7 @@ start_pcp_command_processor_process(int port)
                /* Close the listen sockets sockets */
                close(pcp_unix_fd);
                close(pcp_inet_fd);
+               myProcPid = getpid();
                /* call PCP child main */
                if (pcp_worker_children)
                        list_free(pcp_worker_children);
index 6ea8c2a22704a662c6d30d14a7baa9b7a3939cd9..dff0276d3b0d76d8892559bd6cefc524f9349398 100644 (file)
@@ -164,7 +164,7 @@ max_pool = 4
 
 # - Life time -
 
-child_life_time = 300
+child_life_time = 5min
                                    # Pool exits after being idle for this many seconds
 child_max_connections = 0
                                    # Pool exits after receiving that many connections
@@ -247,6 +247,38 @@ syslog_ident = 'pgpool'
                                         #   fatal
                                         #   panic
 
+# This is used when logging to stderr:
+#logging_collector = off                # Enable capturing of stderr
+                                        # into log files.
+                                        # (change requires restart)
+
+# -- Only used if logging_collector is on ---
+
+#log_directory = '/tmp/pgpool_log'      # directory where log files are written,
+                                        # can be absolute
+#log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
+                                        # log file name pattern,
+                                        # can include strftime() escapes
+
+#log_file_mode = 0600                   # creation mode for log files,
+                                        # begin with 0 to use octal notation
+
+#log_truncate_on_rotation = off         # If on, an existing log file with the
+                                        # same name as the new log file will be
+                                        # truncated rather than appended to.
+                                        # But such truncation only occurs on
+                                        # time-driven rotation, not on restarts
+                                        # or size-driven rotation.  Default is
+                                        # off, meaning append to existing files
+                                        # in all cases.
+
+#log_rotation_age = 1d                # Automatic rotation of logfiles will
+                                        # happen after that (minutes)time.
+                                        # 0 disables time based rotation.
+#log_rotation_size = 10MB               # Automatic rotation of logfiles will
+                                        # happen after that much (KB) log output.
+                                        # 0 disables size based rotation.
+
 #------------------------------------------------------------------------------
 # FILE LOCATIONS
 #------------------------------------------------------------------------------
index 16b14d1847cfc2f880ea95781dbbd077092138e1..6074fa4ab069c5e57b79a798c2283caf2fa0b9e8 100644 (file)
@@ -165,7 +165,7 @@ max_pool = 4
 
 # - Life time -
 
-child_life_time = 300
+child_life_time = 5min
                                    # Pool exits after being idle for this many seconds
 child_max_connections = 0
                                    # Pool exits after receiving that many connections
@@ -248,6 +248,38 @@ syslog_ident = 'pgpool'
                                         #   fatal
                                         #   panic
 
+# This is used when logging to stderr:
+#logging_collector = off                # Enable capturing of stderr
+                                        # into log files.
+                                        # (change requires restart)
+
+# -- Only used if logging_collector is on ---
+
+#log_directory = '/tmp/pgpool_log'      # directory where log files are written,
+                                        # can be absolute
+#log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
+                                        # log file name pattern,
+                                        # can include strftime() escapes
+
+#log_file_mode = 0600                   # creation mode for log files,
+                                        # begin with 0 to use octal notation
+
+#log_truncate_on_rotation = off         # If on, an existing log file with the
+                                        # same name as the new log file will be
+                                        # truncated rather than appended to.
+                                        # But such truncation only occurs on
+                                        # time-driven rotation, not on restarts
+                                        # or size-driven rotation.  Default is
+                                        # off, meaning append to existing files
+                                        # in all cases.
+
+#log_rotation_age = 1d                  # Automatic rotation of logfiles will
+                                        # happen after that (minutes)time.
+                                        # 0 disables time based rotation.
+#log_rotation_size = 10MB               # Automatic rotation of logfiles will
+                                        # happen after that much (KB) log output.
+                                        # 0 disables size based rotation.
+
 #------------------------------------------------------------------------------
 # FILE LOCATIONS
 #------------------------------------------------------------------------------
@@ -538,7 +570,7 @@ client_idle_limit_in_recovery = 0
 auto_failback = off
                                    # Dettached backend node reattach automatically
                                    # if replication_state is 'streaming'.
-auto_failback_interval = 60
+auto_failback_interval = 1min
                                    # Min interval of executing auto_failback in
                                    # seconds.
 
@@ -802,7 +834,7 @@ memqcache_memcached_port = 11211
                                    # Memcached port number. Mondatory if memqcache_method = 'memcached'.
                                    # Defaults to 11211.
                                    # (change requires restart)
-memqcache_total_size = 67108864
+memqcache_total_size = 64MB
                                    # Total memory size in bytes for storing memory cache.
                                    # Mandatory if memqcache_method = 'shmem'.
                                    # Defaults to 64MB.
@@ -822,11 +854,11 @@ memqcache_auto_cache_invalidation = on
                                    # DDL/DML/DCL(and memqcache_expire).  If off, it is only triggered
                                    # by memqcache_expire.  on by default.
                                    # (change requires restart)
-memqcache_maxcache = 409600
+memqcache_maxcache = 400kB
                                    # Maximum SELECT result size in bytes.
                                    # Must be smaller than memqcache_cache_block_size. Defaults to 400KB.
                                    # (change requires restart)
-memqcache_cache_block_size = 1048576
+memqcache_cache_block_size = 1MB
                                    # Cache block size in bytes. Mandatory if memqcache_method = 'shmem'.
                                    # Defaults to 1MB.
                                    # (change requires restart)
index 5cd9d2fc9cd30ba03b53369384289fc88746e163..2734ed41d5f704f6170fbeb5e88c111b22552baf 100644 (file)
@@ -160,7 +160,7 @@ max_pool = 4
 
 # - Life time -
 
-child_life_time = 300
+child_life_time = 5min
                                    # Pool exits after being idle for this many seconds
 child_max_connections = 0
                                    # Pool exits after receiving that many connections
@@ -243,6 +243,38 @@ syslog_ident = 'pgpool'
                                         #   fatal
                                         #   panic
 
+# This is used when logging to stderr:
+#logging_collector = off                # Enable capturing of stderr
+                                        # into log files.
+                                        # (change requires restart)
+
+# -- Only used if logging_collector is on ---
+
+#log_directory = '/tmp/pgpool_log'      # directory where log files are written,
+                                        # can be absolute
+#log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
+                                        # log file name pattern,
+                                        # can include strftime() escapes
+
+#log_file_mode = 0600                   # creation mode for log files,
+                                        # begin with 0 to use octal notation
+
+#log_truncate_on_rotation = off         # If on, an existing log file with the
+                                        # same name as the new log file will be
+                                        # truncated rather than appended to.
+                                        # But such truncation only occurs on
+                                        # time-driven rotation, not on restarts
+                                        # or size-driven rotation.  Default is
+                                        # off, meaning append to existing files
+                                        # in all cases.
+
+#log_rotation_age = 1d                  # Automatic rotation of logfiles will
+                                        # happen after that (minutes)time.
+                                        # 0 disables time based rotation.
+#log_rotation_size = 10MB               # Automatic rotation of logfiles will
+                                        # happen after that much (KB) log output.
+                                        # 0 disables size based rotation.
+
 #------------------------------------------------------------------------------
 # FILE LOCATIONS
 #------------------------------------------------------------------------------
index 1548c8b7bd78ac3a49169004edcef911c896cb25..701f873849b9b13c30ca3bb28fa58c7c0805d44d 100644 (file)
@@ -161,7 +161,7 @@ max_pool = 4
 
 # - Life time -
 
-child_life_time = 300
+child_life_time = 5min
                                    # Pool exits after being idle for this many seconds
 child_max_connections = 0
                                    # Pool exits after receiving that many connections
@@ -244,6 +244,38 @@ syslog_ident = 'pgpool'
                                         #   fatal
                                         #   panic
 
+# This is used when logging to stderr:
+#logging_collector = off                # Enable capturing of stderr
+                                        # into log files.
+                                        # (change requires restart)
+
+# -- Only used if logging_collector is on ---
+
+#log_directory = '/tmp/pgpool_log'      # directory where log files are written,
+                                        # can be absolute
+#log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
+                                        # log file name pattern,
+                                        # can include strftime() escapes
+
+#log_file_mode = 0600                   # creation mode for log files,
+                                        # begin with 0 to use octal notation
+
+#log_truncate_on_rotation = off         # If on, an existing log file with the
+                                        # same name as the new log file will be
+                                        # truncated rather than appended to.
+                                        # But such truncation only occurs on
+                                        # time-driven rotation, not on restarts
+                                        # or size-driven rotation.  Default is
+                                        # off, meaning append to existing files
+                                        # in all cases.
+
+#log_rotation_age = 1d                  # Automatic rotation of logfiles will
+                                        # happen after that (minutes)time.
+                                        # 0 disables time based rotation.
+#log_rotation_size = 10MB               # Automatic rotation of logfiles will
+                                        # happen after that much (KB) log output.
+                                        # 0 disables size based rotation.
+
 #------------------------------------------------------------------------------
 # FILE LOCATIONS
 #------------------------------------------------------------------------------
@@ -503,7 +535,7 @@ detach_false_primary = off
                                    # mode and with PostgreSQL 9.6 or
                                    # after.
 
-search_primary_node_timeout = 300
+search_primary_node_timeout = 5min
                                    # Timeout in seconds to search for the
                                    # primary node when a failover occurs.
                                    # 0 means no timeout, keep searching
@@ -512,7 +544,7 @@ search_primary_node_timeout = 300
 auto_failback = off
                                    # Dettached backend node reattach automatically
                                    # if replication_state is 'streaming'.
-auto_failback_interval = 60
+auto_failback_interval = 1min
                                    # Min interval of executing auto_failback in
                                    # seconds.
 
@@ -803,7 +835,7 @@ memqcache_memcached_port = 11211
                                    # Memcached port number. Mondatory if memqcache_method = 'memcached'.
                                    # Defaults to 11211.
                                    # (change requires restart)
-memqcache_total_size = 67108864
+memqcache_total_size = 64MB
                                    # Total memory size in bytes for storing memory cache.
                                    # Mandatory if memqcache_method = 'shmem'.
                                    # Defaults to 64MB.
@@ -823,11 +855,11 @@ memqcache_auto_cache_invalidation = on
                                    # DDL/DML/DCL(and memqcache_expire).  If off, it is only triggered
                                    # by memqcache_expire.  on by default.
                                    # (change requires restart)
-memqcache_maxcache = 409600
+memqcache_maxcache = 400kB
                                    # Maximum SELECT result size in bytes.
                                    # Must be smaller than memqcache_cache_block_size. Defaults to 400KB.
                                    # (change requires restart)
-memqcache_cache_block_size = 1048576
+memqcache_cache_block_size = 1MB
                                    # Cache block size in bytes. Mandatory if memqcache_method = 'shmem'.
                                    # Defaults to 1MB.
                                    # (change requires restart)
index b7c7d29306d01fdd3352269e1e3c2a2b6f7fb0f5..b84a8f05b9a923c30efd03733b30e7e6781048d9 100644 (file)
@@ -160,7 +160,7 @@ max_pool = 4
 
 # - Life time -
 
-child_life_time = 300
+child_life_time = 5min
                                    # Pool exits after being idle for this many seconds
 child_max_connections = 0
                                    # Pool exits after receiving that many connections
@@ -243,6 +243,38 @@ syslog_ident = 'pgpool'
                                         #   fatal
                                         #   panic
 
+# This is used when logging to stderr:
+#logging_collector = off                # Enable capturing of stderr
+                                        # into log files.
+                                        # (change requires restart)
+
+# -- Only used if logging_collector is on ---
+
+#log_directory = '/tmp/pgpool_log'      # directory where log files are written,
+                                        # can be absolute
+#log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
+                                        # log file name pattern,
+                                        # can include strftime() escapes
+
+#log_file_mode = 0600                   # creation mode for log files,
+                                        # begin with 0 to use octal notation
+
+#log_truncate_on_rotation = off         # If on, an existing log file with the
+                                        # same name as the new log file will be
+                                        # truncated rather than appended to.
+                                        # But such truncation only occurs on
+                                        # time-driven rotation, not on restarts
+                                        # or size-driven rotation.  Default is
+                                        # off, meaning append to existing files
+                                        # in all cases.
+
+#log_rotation_age = 1d                  # Automatic rotation of logfiles will
+                                        # happen after that (minutes)time.
+                                        # 0 disables time based rotation.
+#log_rotation_size = 10MB               # Automatic rotation of logfiles will
+                                        # happen after that much (KB) log output.
+                                        # 0 disables size based rotation.
+
 #------------------------------------------------------------------------------
 # FILE LOCATIONS
 #------------------------------------------------------------------------------
@@ -512,7 +544,7 @@ detach_false_primary = off
                                    # mode and with PostgreSQL 9.6 or
                                    # after.
 
-search_primary_node_timeout = 300
+search_primary_node_timeout = 5min
                                    # Timeout in seconds to search for the
                                    # primary node when a failover occurs.
                                    # 0 means no timeout, keep searching
@@ -521,7 +553,7 @@ search_primary_node_timeout = 300
 auto_failback = off
                                    # Dettached backend node reattach automatically
                                    # if replication_state is 'streaming'.
-auto_failback_interval = 60
+auto_failback_interval = 1min
                                    # Min interval of executing auto_failback in
                                    # seconds.
 
@@ -815,7 +847,7 @@ memqcache_memcached_port = 11211
                                    # Memcached port number. Mondatory if memqcache_method = 'memcached'.
                                    # Defaults to 11211.
                                    # (change requires restart)
-memqcache_total_size = 67108864
+memqcache_total_size = 64MB
                                    # Total memory size in bytes for storing memory cache.
                                    # Mandatory if memqcache_method = 'shmem'.
                                    # Defaults to 64MB.
@@ -835,11 +867,11 @@ memqcache_auto_cache_invalidation = on
                                    # DDL/DML/DCL(and memqcache_expire).  If off, it is only triggered
                                    # by memqcache_expire.  on by default.
                                    # (change requires restart)
-memqcache_maxcache = 409600
+memqcache_maxcache = 400kB
                                    # Maximum SELECT result size in bytes.
                                    # Must be smaller than memqcache_cache_block_size. Defaults to 400KB.
                                    # (change requires restart)
-memqcache_cache_block_size = 1048576
+memqcache_cache_block_size = 1MB
                                    # Cache block size in bytes. Mandatory if memqcache_method = 'shmem'.
                                    # Defaults to 1MB.
                                    # (change requires restart)
index e09aeaebcca9ee91e187ff5a723ca8d4f57e797f..4d7b3ab85ed4ff4f42f3e2fdd8d97c04f9347d8b 100644 (file)
@@ -165,7 +165,7 @@ max_pool = 4
 
 # - Life time -
 
-child_life_time = 300
+child_life_time = 5min
                                    # Pool exits after being idle for this many seconds
 child_max_connections = 0
                                    # Pool exits after receiving that many connections
@@ -248,6 +248,37 @@ syslog_ident = 'pgpool'
                                         #   fatal
                                         #   panic
 
+# This is used when logging to stderr:
+#logging_collector = off                # Enable capturing of stderr
+                                        # into log files.
+                                        # (change requires restart)
+
+# -- Only used if logging_collector is on ---
+
+#log_directory = '/tmp/pgpool_log'      # directory where log files are written,
+                                        # can be absolute
+#log_filename = 'pgpool-%Y-%m-%d_%H%M%S.log'
+                                        # log file name pattern,
+                                        # can include strftime() escapes
+
+#log_file_mode = 0600                   # creation mode for log files,
+                                        # begin with 0 to use octal notation
+
+#log_truncate_on_rotation = off         # If on, an existing log file with the
+                                        # same name as the new log file will be
+                                        # truncated rather than appended to.
+                                        # But such truncation only occurs on
+                                        # time-driven rotation, not on restarts
+                                        # or size-driven rotation.  Default is
+                                        # off, meaning append to existing files
+                                        # in all cases.
+
+#log_rotation_age = 1d                  # Automatic rotation of logfiles will
+                                        # happen after that (minutes)time.
+                                        # 0 disables time based rotation.
+#log_rotation_size = 10MB               # Automatic rotation of logfiles will
+                                        # happen after that much (KB) log output.
+                                        # 0 disables size based rotation.
 #------------------------------------------------------------------------------
 # FILE LOCATIONS
 #------------------------------------------------------------------------------
index f79ba0ea255789ad94080f434af0d3a58fae9a82..658ad6b3fb59bcbd4e18b9f345a87e5e86e9136d 100644 (file)
@@ -7,6 +7,7 @@ CC=gcc
 OBJS=main.o \
         $(topsrc_dir)/utils/strlcpy.o \
         $(topsrc_dir)/utils/psprintf.o \
+        $(topsrc_dir)/main/pool_globals.o \
         $(topsrc_dir)/rewrite/pool_timestamp.o \
         $(topsrc_dir)/parser/libsql-parser.a
 
@@ -15,6 +16,7 @@ all: all-pre $(PROGRAM)
 all-pre:
        $(MAKE) -C $(topsrc_dir)/utils strlcpy.o
        $(MAKE) -C $(topsrc_dir)/utils psprintf.o
+       $(MAKE) -C $(topsrc_dir)/main pool_globals.o
        $(MAKE) -C $(topsrc_dir)/rewrite pool_timestamp.o
        $(MAKE) -C $(topsrc_dir)/parser
 
index d8dba4ba5d9be79aa83bb38012f28eff25899efa..71069be495e2c7da29c6106b9170f23f4ae76e8e 100644 (file)
@@ -17,7 +17,7 @@ POOL_REQUEST_INFO *Req_info = &_req_info;
 POOL_CONFIG _pool_config;
 POOL_CONFIG *pool_config = &_pool_config;
 ProcessType processType;
-
+bool redirection_done = false;
 typedef struct
 {
        char       *attrname;           /* attribute name */
index 3f95cac89c94ee13a9fe663538b6e24c126b1f89..9ef63ed3eb6abe88b489d8856ad3bc40ff8a806e 100644 (file)
@@ -83,7 +83,8 @@ bin_PROGRAMS = pcp_stop_pgpool$(EXEEXT) pcp_node_count$(EXEEXT) \
        pcp_proc_count$(EXEEXT) pcp_proc_info$(EXEEXT) \
        pcp_detach_node$(EXEEXT) pcp_attach_node$(EXEEXT) \
        pcp_recovery_node$(EXEEXT) pcp_promote_node$(EXEEXT) \
-       pcp_pool_status$(EXEEXT) pcp_watchdog_info$(EXEEXT)
+       pcp_pool_status$(EXEEXT) pcp_watchdog_info$(EXEEXT) \
+       pcp_reload_config$(EXEEXT)
 subdir = src/tools/pcp
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
        $(top_srcdir)/mkinstalldirs
@@ -141,6 +142,9 @@ pcp_promote_node_DEPENDENCIES = $(libs_dir)/pcp/libpcp.la
 am_pcp_recovery_node_OBJECTS = $(am__objects_1)
 pcp_recovery_node_OBJECTS = $(am_pcp_recovery_node_OBJECTS)
 pcp_recovery_node_DEPENDENCIES = $(libs_dir)/pcp/libpcp.la
+am_pcp_reload_config_OBJECTS = $(am__objects_1)
+pcp_reload_config_OBJECTS = $(am_pcp_reload_config_OBJECTS)
+pcp_reload_config_DEPENDENCIES = $(libs_dir)/pcp/libpcp.la
 am_pcp_stop_pgpool_OBJECTS = $(am__objects_1)
 pcp_stop_pgpool_OBJECTS = $(am_pcp_stop_pgpool_OBJECTS)
 pcp_stop_pgpool_DEPENDENCIES = $(libs_dir)/pcp/libpcp.la
@@ -185,13 +189,15 @@ SOURCES = $(pcp_attach_node_SOURCES) $(pcp_detach_node_SOURCES) \
        $(pcp_node_info_SOURCES) $(pcp_pool_status_SOURCES) \
        $(pcp_proc_count_SOURCES) $(pcp_proc_info_SOURCES) \
        $(pcp_promote_node_SOURCES) $(pcp_recovery_node_SOURCES) \
-       $(pcp_stop_pgpool_SOURCES) $(pcp_watchdog_info_SOURCES)
+       $(pcp_reload_config_SOURCES) $(pcp_stop_pgpool_SOURCES) \
+       $(pcp_watchdog_info_SOURCES)
 DIST_SOURCES = $(pcp_attach_node_SOURCES) $(pcp_detach_node_SOURCES) \
        $(pcp_health_check_stats_SOURCES) $(pcp_node_count_SOURCES) \
        $(pcp_node_info_SOURCES) $(pcp_pool_status_SOURCES) \
        $(pcp_proc_count_SOURCES) $(pcp_proc_info_SOURCES) \
        $(pcp_promote_node_SOURCES) $(pcp_recovery_node_SOURCES) \
-       $(pcp_stop_pgpool_SOURCES) $(pcp_watchdog_info_SOURCES)
+       $(pcp_reload_config_SOURCES) $(pcp_stop_pgpool_SOURCES) \
+       $(pcp_watchdog_info_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -381,6 +387,8 @@ pcp_promote_node_SOURCES = $(client_sources)
 pcp_promote_node_LDADD = $(libs_dir)/pcp/libpcp.la
 pcp_watchdog_info_SOURCES = $(client_sources)
 pcp_watchdog_info_LDADD = $(libs_dir)/pcp/libpcp.la
+pcp_reload_config_SOURCES = $(client_sources)
+pcp_reload_config_LDADD = $(libs_dir)/pcp/libpcp.la
 all: all-am
 
 .SUFFIXES:
@@ -516,6 +524,10 @@ pcp_recovery_node$(EXEEXT): $(pcp_recovery_node_OBJECTS) $(pcp_recovery_node_DEP
        @rm -f pcp_recovery_node$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(pcp_recovery_node_OBJECTS) $(pcp_recovery_node_LDADD) $(LIBS)
 
+pcp_reload_config$(EXEEXT): $(pcp_reload_config_OBJECTS) $(pcp_reload_config_DEPENDENCIES) $(EXTRA_pcp_reload_config_DEPENDENCIES) 
+       @rm -f pcp_reload_config$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(pcp_reload_config_OBJECTS) $(pcp_reload_config_LDADD) $(LIBS)
+
 pcp_stop_pgpool$(EXEEXT): $(pcp_stop_pgpool_OBJECTS) $(pcp_stop_pgpool_DEPENDENCIES) $(EXTRA_pcp_stop_pgpool_DEPENDENCIES) 
        @rm -f pcp_stop_pgpool$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(pcp_stop_pgpool_OBJECTS) $(pcp_stop_pgpool_LDADD) $(LIBS)
index b7b761081be095ba696d4b7cf40ac4a2dc08c90a..0c8f2c16722f481d49e22b7fbcf843385514330f 100644 (file)
@@ -69,6 +69,7 @@
 #include <stdarg.h>
 #include <errno.h>
 #include <sys/fcntl.h>
+#include "main/pgpool_logger.h"
 #include "utils/elog.h"
 #include "utils/memutils.h"
 #include "pool_config.h"
@@ -115,7 +116,6 @@ ErrorContextCallback *error_context_stack = NULL;
 
 sigjmp_buf *PG_exception_stack = NULL;
 
-extern bool redirection_done;
 
 /*
  * Hook for intercepting messages before they are sent to the server log.
@@ -148,6 +148,7 @@ static void write_syslog(int level, const char *line);
 
 static void send_message_to_server_log(ErrorData *edata);
 static void send_message_to_frontend(ErrorData *edata);
+static void write_pipe_chunks(char *data, int len, int dest);
 static void write_console(const char *line, int len);
 static void log_line_prefix(StringInfo buf, const char *line_prefix, ErrorData *edata);
 static const char *process_log_prefix_padding(const char *p, int *ppadding);
@@ -1748,6 +1749,57 @@ write_console(const char *line, int len)
        (void) rc;
 }
 
+/*
+ * Send data to the syslogger using the chunked protocol
+ *
+ * Note: when there are multiple backends writing into the syslogger pipe,
+ * it's critical that each write go into the pipe indivisibly, and not
+ * get interleaved with data from other processes.  Fortunately, the POSIX
+ * spec requires that writes to pipes be atomic so long as they are not
+ * more than PIPE_BUF bytes long.  So we divide long messages into chunks
+ * that are no more than that length, and send one chunk per write() call.
+ * The collector process knows how to reassemble the chunks.
+ *
+ * Because of the atomic write requirement, there are only two possible
+ * results from write() here: -1 for failure, or the requested number of
+ * bytes.  There is not really anything we can do about a failure; retry would
+ * probably be an infinite loop, and we can't even report the error usefully.
+ * (There is noplace else we could send it!)  So we might as well just ignore
+ * the result from write().  However, on some platforms you get a compiler
+ * warning from ignoring write()'s result, so do a little dance with casting
+ * rc to void to shut up the compiler.
+ */
+static void
+write_pipe_chunks(char *data, int len, int dest)
+{
+       PipeProtoChunk p;
+       int                     fd = fileno(stderr);
+       int                     rc;
+
+       Assert(len > 0);
+
+       p.proto.nuls[0] = p.proto.nuls[1] = '\0';
+       p.proto.pid = myProcPid;
+
+       /* write all but the last chunk */
+       while (len > PIPE_MAX_PAYLOAD)
+       {
+               p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
+               p.proto.len = PIPE_MAX_PAYLOAD;
+               memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
+               rc = write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
+               (void) rc;
+               data += PIPE_MAX_PAYLOAD;
+               len -= PIPE_MAX_PAYLOAD;
+       }
+
+       /* write the last chunk */
+       p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
+       p.proto.len = len;
+       memcpy(p.proto.data, data, len);
+       rc = write(fd, &p, PIPE_HEADER_SIZE + len);
+       (void) rc;
+}
 
 /*
  * Write error report to frontend log
@@ -1944,7 +1996,6 @@ log_line_prefix(StringInfo buf, const char *line_prefix, ErrorData *edata)
        static int      log_my_pid = 0;
        int                     padding;
        const char *p;
-       int                     MyProcPid = getpid();
 
        POOL_CONNECTION *frontend = NULL;
        POOL_SESSION_CONTEXT *session = pool_get_session_context(true);
@@ -1958,10 +2009,10 @@ log_line_prefix(StringInfo buf, const char *line_prefix, ErrorData *edata)
         * MyProcPid changes. MyStartTime also changes when MyProcPid does, so
         * reset the formatted start timestamp too.
         */
-       if (log_my_pid != MyProcPid)
+       if (log_my_pid != myProcPid)
        {
                log_line_number = 0;
-               log_my_pid = MyProcPid;
+               log_my_pid = myProcPid;
        }
        log_line_number++;
 
@@ -2059,9 +2110,9 @@ log_line_prefix(StringInfo buf, const char *line_prefix, ErrorData *edata)
                                break;
                        case 'p':
                                if (padding != 0)
-                                       appendStringInfo(buf, "%*d", padding, MyProcPid);
+                                       appendStringInfo(buf, "%*d", padding, myProcPid);
                                else
-                                       appendStringInfo(buf, "%d", MyProcPid);
+                                       appendStringInfo(buf, "%d", myProcPid);
                                break;
                        case 'l':
                                if (padding != 0)
@@ -2215,7 +2266,16 @@ send_message_to_server_log(ErrorData *edata)
 
        if (pool_config->log_destination & LOG_DESTINATION_STDERR)
        {
-               write_console(buf.data, buf.len);
+               /*
+                * Use the chunking protocol if we know the syslogger should be
+                * catching stderr output, and we are not ourselves the syslogger.
+                * Otherwise, just do a vanilla write to stderr.
+                */
+
+               if (redirection_done && processType != PT_LOGGER)
+                       write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
+               else
+                       write_console(buf.data, buf.len);
        }
        pfree(buf.data);
 }
index 1e15bb1c2ea5223ec62b57d78c7e4c9864629ee1..ab6e7d8c63fc515e82f64602f366ae72191137c4 100644 (file)
@@ -1101,6 +1101,7 @@ fork_watchdog_child(void)
 
                /* Set the process type variable */
                processType = PT_WATCHDOG;
+               myProcPid = getpid();
                set_application_name(processType);
 
                /* call watchdog child main */
index 9b9105aad8618b580b907149ffd451c7548b9e02..7e876043288675aee20aa8686ccaeaef687e1ade 100644 (file)
@@ -74,6 +74,7 @@ fork_escalation_process(void)
        }
        on_exit_reset();
        processType = PT_WATCHDOG_UTILITY;
+       myProcPid = getpid();
        set_application_name(processType);
 
        POOL_SETMASK(&UnBlockSig);
@@ -162,6 +163,7 @@ fork_plunging_process(void)
                return pid;
        }
        on_exit_reset();
+       myProcPid = getpid();
        processType = PT_WATCHDOG_UTILITY;
        set_application_name(processType);
 
index 11e2815014675688035e81fc1e66eaddae3324b9..a6ed950cd1218e8c3e2ff7b1105f90fc2afe305e 100644 (file)
@@ -365,6 +365,7 @@ wd_hb_receiver(int fork_wait_time, WdHbIf * hb_if)
 
        on_exit_reset();
        processType = PT_HB_RECEIVER;
+       myProcPid = getpid();
        set_application_name(processType);
 
        if (fork_wait_time > 0)
@@ -498,6 +499,7 @@ wd_hb_sender(int fork_wait_time, WdHbIf * hb_if)
        }
 
        on_exit_reset();
+       myProcPid = getpid();
        processType = PT_HB_SENDER;
        set_application_name(processType);
 
index 1737665838fdb322e52f492b9275ebb46e47385b..0c68a08c829ccaf47a78fb52cd20c571734a1a71 100644 (file)
@@ -313,6 +313,7 @@ exec_if_cmd(char *path, char *command)
                on_exit_reset();
                processType = PT_WATCHDOG_UTILITY;
                set_application_name(processType);
+               myProcPid = getpid();
                close(STDOUT_FILENO);
                dup2(pfd[1], STDOUT_FILENO);
                close(pfd[0]);
index 12653934abb80b84af5928ffca5392c0975eec7b..211ca9c2f37146c12eb857983ad879989b36e6ed 100644 (file)
@@ -359,6 +359,7 @@ fork_lifecheck_child(void)
 
                /* Set the process type variable */
                processType = PT_LIFECHECK;
+               myProcPid = getpid();
                set_application_name(processType);
 
                /* call lifecheck child main */
index 2113e4e971eed67568cb35eb7415bfd947a8e16f..760166b9c9614122fc8d32f8bfee812eef9d5a01 100644 (file)
@@ -135,6 +135,7 @@ wd_issue_ping_command(char *hostname, int *outfd)
                /* CHILD */
                on_exit_reset();
                processType = PT_WATCHDOG_UTILITY;
+               myProcPid = getpid();
                set_application_name(processType);
                close(STDOUT_FILENO);
                dup2(pfd[1], STDOUT_FILENO);