pgpool2.git / summary / log / commit / refs
commit 35037bcd837472f04b92008362db7f3cefa88bec
Author: Muhammad Usama <m.usama@HighGo.ca>
Commit: Muhammad Usama <m.usama@gmail.com>
Date: Fri Jun 26 19:08:24 2020 +0000
Adding log collector
Molding the syslogger of PostgreSQL to work with
Pgpool-II so that we can have log rotation
Documentation updates will follow soon.
configure | 85 ++
src/Makefile.am | 1 +
src/Makefile.in | 30 +-
src/config/pool_config_variables.c | 68 ++
src/include/main/pgpool_logger.h | 96 ++
src/include/pool.h | 2 +
src/include/pool_config.h | 9 +
src/include/utils/timestamp.h | 214 ++++
src/libs/pcp/Makefile.in | 2 +-
src/main/main.c | 2 +
src/main/pgpool_logger.c | 1176 ++++++++++++++++++++
src/main/pgpool_main.c | 216 ++--
src/main/pool_globals.c | 1 +
src/pcp_con/pcp_child.c | 1 +
src/sample/pgpool.conf.sample-logical | 34 +-
src/sample/pgpool.conf.sample-raw | 42 +-
src/sample/pgpool.conf.sample-replication | 34 +-
src/sample/pgpool.conf.sample-slony | 44 +-
src/sample/pgpool.conf.sample-snapshot | 44 +-
src/sample/pgpool.conf.sample-stream | 33 +-
.../tests/010.rewrite_timestamp/timestamp/Makefile | 2 +
.../tests/010.rewrite_timestamp/timestamp/main.c | 2 +-
src/tools/pcp/Makefile.in | 18 +-
src/utils/error/elog.c | 74 +-
src/watchdog/watchdog.c | 1 +
src/watchdog/wd_escalation.c | 2 +
src/watchdog/wd_heartbeat.c | 2 +
src/watchdog/wd_if.c | 1 +
src/watchdog/wd_lifecheck.c | 1 +
src/watchdog/wd_ping.c | 1 +
30 files changed, 2092 insertions(+), 146 deletions(-)
diff --git a/configure b/configure
index 941a1b25c..cd9b12385 100755
--- 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
@@ -14878,6 +14880,89 @@ fi
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;
diff --git a/src/Makefile.am b/src/Makefile.am
index dae2c26c4..65a17ab09 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 \
diff --git a/src/Makefile.in b/src/Makefile.in
index 12ec70db4..e424d2a37 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -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,
@@ -16,17 +16,7 @@
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.
diff --git a/src/config/pool_config_variables.c b/src/config/pool_config_variables.c
index 2e641818f..66998cc9e 100644
--- a/src/config/pool_config_variables.c
+++ b/src/config/pool_config_variables.c
@@ -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
index 000000000..95675b15f
--- /dev/null
+++ b/src/include/main/pgpool_logger.h
@@ -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 */
diff --git a/src/include/pool.h b/src/include/pool.h
index c17447cb0..7f395e033 100644
--- a/src/include/pool.h
+++ b/src/include/pool.h
@@ -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;
diff --git a/src/include/pool_config.h b/src/include/pool_config.h
index be81737b3..7eacd5b80 100644
--- a/src/include/pool_config.h
+++ b/src/include/pool_config.h
@@ -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
index 000000000..78e65c46c
--- /dev/null
+++ b/src/include/utils/timestamp.h
@@ -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 */
diff --git a/src/libs/pcp/Makefile.in b/src/libs/pcp/Makefile.in
index d956bb1b0..58a6f018d 100644
--- a/src/libs/pcp/Makefile.in
+++ b/src/libs/pcp/Makefile.in
@@ -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 \
diff --git a/src/main/main.c b/src/main/main.c
index afffd3653..0f79e7e56 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -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
index 000000000..0e6b56cdb
--- /dev/null
+++ b/src/main/pgpool_logger.c
@@ -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(×tamp));
+
+ 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;
+}
diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c
index a2158ec32..86ef9b16b 100644
--- a/src/main/pgpool_main.c
+++ b/src/main/pgpool_main.c
@@ -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);
diff --git a/src/main/pool_globals.c b/src/main/pool_globals.c
index 517701bd3..45a36750a 100644
--- a/src/main/pool_globals.c
+++ b/src/main/pool_globals.c
@@ -23,5 +23,6 @@
#include "pool.h"
pid_t mypid; /* pgpool parent process id */
+pid_t myProcPid; /* process pid */
ProcessType processType;
ProcessState processState;
diff --git a/src/pcp_con/pcp_child.c b/src/pcp_con/pcp_child.c
index 8ca00fa51..d60acc00b 100644
--- a/src/pcp_con/pcp_child.c
+++ b/src/pcp_con/pcp_child.c
@@ -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);
diff --git a/src/sample/pgpool.conf.sample-logical b/src/sample/pgpool.conf.sample-logical
index 6ea8c2a22..dff0276d3 100644
--- a/src/sample/pgpool.conf.sample-logical
+++ b/src/sample/pgpool.conf.sample-logical
@@ -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
#------------------------------------------------------------------------------
diff --git a/src/sample/pgpool.conf.sample-raw b/src/sample/pgpool.conf.sample-raw
index 16b14d184..6074fa4ab 100644
--- a/src/sample/pgpool.conf.sample-raw
+++ b/src/sample/pgpool.conf.sample-raw
@@ -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)
diff --git a/src/sample/pgpool.conf.sample-replication b/src/sample/pgpool.conf.sample-replication
index 5cd9d2fc9..2734ed41d 100644
--- a/src/sample/pgpool.conf.sample-replication
+++ b/src/sample/pgpool.conf.sample-replication
@@ -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
#------------------------------------------------------------------------------
diff --git a/src/sample/pgpool.conf.sample-slony b/src/sample/pgpool.conf.sample-slony
index 1548c8b7b..701f87384 100644
--- a/src/sample/pgpool.conf.sample-slony
+++ b/src/sample/pgpool.conf.sample-slony
@@ -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)
diff --git a/src/sample/pgpool.conf.sample-snapshot b/src/sample/pgpool.conf.sample-snapshot
index b7c7d2930..b84a8f05b 100644
--- a/src/sample/pgpool.conf.sample-snapshot
+++ b/src/sample/pgpool.conf.sample-snapshot
@@ -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)
diff --git a/src/sample/pgpool.conf.sample-stream b/src/sample/pgpool.conf.sample-stream
index e09aeaebc..4d7b3ab85 100644
--- a/src/sample/pgpool.conf.sample-stream
+++ b/src/sample/pgpool.conf.sample-stream
@@ -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
#------------------------------------------------------------------------------
diff --git a/src/test/regression/tests/010.rewrite_timestamp/timestamp/Makefile b/src/test/regression/tests/010.rewrite_timestamp/timestamp/Makefile
index f79ba0ea2..658ad6b3f 100644
--- a/src/test/regression/tests/010.rewrite_timestamp/timestamp/Makefile
+++ b/src/test/regression/tests/010.rewrite_timestamp/timestamp/Makefile
@@ -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
diff --git a/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c b/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c
index d8dba4ba5..71069be49 100644
--- a/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c
+++ b/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c
@@ -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 */
diff --git a/src/tools/pcp/Makefile.in b/src/tools/pcp/Makefile.in
index 3f95cac89..9ef63ed3e 100644
--- a/src/tools/pcp/Makefile.in
+++ b/src/tools/pcp/Makefile.in
@@ -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)
diff --git a/src/utils/error/elog.c b/src/utils/error/elog.c
index b7b761081..0c8f2c167 100644
--- a/src/utils/error/elog.c
+++ b/src/utils/error/elog.c
@@ -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);
}
diff --git a/src/watchdog/watchdog.c b/src/watchdog/watchdog.c
index 1e15bb1c2..ab6e7d8c6 100644
--- a/src/watchdog/watchdog.c
+++ b/src/watchdog/watchdog.c
@@ -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 */
diff --git a/src/watchdog/wd_escalation.c b/src/watchdog/wd_escalation.c
index 9b9105aad..7e8760432 100644
--- a/src/watchdog/wd_escalation.c
+++ b/src/watchdog/wd_escalation.c
@@ -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);
diff --git a/src/watchdog/wd_heartbeat.c b/src/watchdog/wd_heartbeat.c
index 11e281501..a6ed950cd 100644
--- a/src/watchdog/wd_heartbeat.c
+++ b/src/watchdog/wd_heartbeat.c
@@ -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);
diff --git a/src/watchdog/wd_if.c b/src/watchdog/wd_if.c
index 173766583..0c68a08c8 100644
--- a/src/watchdog/wd_if.c
+++ b/src/watchdog/wd_if.c
@@ -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]);
diff --git a/src/watchdog/wd_lifecheck.c b/src/watchdog/wd_lifecheck.c
index 12653934a..211ca9c2f 100644
--- a/src/watchdog/wd_lifecheck.c
+++ b/src/watchdog/wd_lifecheck.c
@@ -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 */
diff --git a/src/watchdog/wd_ping.c b/src/watchdog/wd_ping.c
index 2113e4e97..760166b9c 100644
--- a/src/watchdog/wd_ping.c
+++ b/src/watchdog/wd_ping.c
@@ -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);
[parent: b7ac3f76ad10]