with_pgsql_libdir
with_sunifdef
with_openssl
+with_ldap
with_pam
with_memcached
enable_rpath
--with-pgsql-libdir=DIR site library files for PostgreSQL in DIR
--with-sunifdef=DIR install path for sunifdef utility
--with-openssl build with OpenSSL support
+ --with-ldap build with LDAP support
--with-pam build with PAM support
--with-memcached=DIR site header files for libmemcached in DIR
fi
+# Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+ withval=$with_ldap;
+$as_echo "#define USE_LDAP 1" >>confdefs.h
+
+fi
+
+if test "$with_ldap" = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_bind in -lldap" >&5
+$as_echo_n "checking for ldap_bind in -lldap... " >&6; }
+if ${ac_cv_lib_ldap_ldap_bind+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lldap $EXTRA_LDAP_LIBS $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_bind ();
+int
+main ()
+{
+return ldap_bind ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ldap_ldap_bind=yes
+else
+ ac_cv_lib_ldap_ldap_bind=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ldap_bind" >&5
+$as_echo "$ac_cv_lib_ldap_ldap_bind" >&6; }
+if test "x$ac_cv_lib_ldap_ldap_bind" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBLDAP 1
+_ACEOF
+
+ LIBS="-lldap $LIBS"
+
+else
+ as_fn_error $? "library 'ldap' is required for LDAP" "$LINENO" 5
+fi
+
+ for ac_header in ldap.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "ldap.h" "ac_cv_header_ldap_h" "$ac_includes_default"
+if test "x$ac_cv_header_ldap_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LDAP_H 1
+_ACEOF
+
+else
+ as_fn_error $? "header file <ldap.h> is required for LDAP" "$LINENO" 5
+fi
+
+done
+
+ for ac_func in ldap_initialize
+do :
+ ac_fn_c_check_func "$LINENO" "ldap_initialize" "ac_cv_func_ldap_initialize"
+if test "x$ac_cv_func_ldap_initialize" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LDAP_INITIALIZE 1
+_ACEOF
+
+fi
+done
+
+fi
+
+
# Check whether --with-pam was given.
if test "${with_pam+set}" = set; then :
withval=$with_pam;
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 \
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
VPATH = @srcdir@
-am__is_gnu_make = { \
- if test -z '$(MAKELEVEL)'; then \
- false; \
- elif test -n '$(MAKE_HOST)'; then \
- true; \
- elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
- true; \
- else \
- false; \
- fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
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 \
$(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 =
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) \
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`; \
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
-runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
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 \
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*) \
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)
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.
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
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
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
--- /dev/null
+/*
+ *
+ * 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 */
PT_PCP,
PT_PCP_WORKER,
PT_HEALTH_CHECK,
+ PT_LOGGER,
PT_LAST_PTYPE /* last ptype marker. any ptype must be above this. */
} ProcessType;
* 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;
* 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
--- /dev/null
+/*
+ *
+ * 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 */
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 \
hba_file = make_absolute_path(hba_file_path, base_dir);
mypid = getpid();
+ myProcPid = mypid;
pool_init_config();
#endif
mypid = getpid();
+ myProcPid = mypid;
write_pid_file();
if (chdir("/"))
ereport(WARNING,
--- /dev/null
+/* -*-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;
+}
#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"
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);
*/
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) */
* 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 */
/* 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;
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.
sigdelset(&mask, SIGTERM);
sigdelset(&mask, SIGINT);
sigdelset(&mask, SIGQUIT);
+ sigdelset(&mask, SIGHUP);
+
watchdog_pid = initialize_watchdog();
ereport(LOG,
(errmsg("waiting for watchdog to initialize")));
}
}
+ /* 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);
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)
{
/* Set the process type variable */
processType = PT_PCP;
-
+ myProcPid = getpid();
/* call PCP child main */
POOL_SETMASK(&UnBlockSig);
health_check_timer_expired = 0;
/* Set the process type variable */
processType = type;
+ myProcPid = getpid();
set_application_name(type);
/* call child main */
}
/*
- * 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;
}
}
{
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);
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);
}
else if (pid == wd_lifecheck_pid)
return "watchdog lifecheck";
}
+ if (pid == pgpool_logger_pid)
+ return "pgpool log collector";
return "child";
}
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)
{
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++)
* 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);
#include "pool.h"
pid_t mypid; /* pgpool parent process id */
+pid_t myProcPid; /* process pid */
ProcessType processType;
ProcessState processState;
/* 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);
# - 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
# 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
#------------------------------------------------------------------------------
# - 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
# 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
#------------------------------------------------------------------------------
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.
# 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.
# 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)
# - 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
# 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
#------------------------------------------------------------------------------
# - 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
# 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
#------------------------------------------------------------------------------
# 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
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.
# 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.
# 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)
# - 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
# 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
#------------------------------------------------------------------------------
# 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
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.
# 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.
# 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)
# - 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
# 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
#------------------------------------------------------------------------------
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
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
POOL_CONFIG _pool_config;
POOL_CONFIG *pool_config = &_pool_config;
ProcessType processType;
-
+bool redirection_done = false;
typedef struct
{
char *attrname; /* attribute name */
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
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
$(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;; \
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:
@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)
#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"
sigjmp_buf *PG_exception_stack = NULL;
-extern bool redirection_done;
/*
* Hook for intercepting messages before they are sent to the server log.
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);
(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
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);
* 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++;
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)
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);
}
/* Set the process type variable */
processType = PT_WATCHDOG;
+ myProcPid = getpid();
set_application_name(processType);
/* call watchdog child main */
}
on_exit_reset();
processType = PT_WATCHDOG_UTILITY;
+ myProcPid = getpid();
set_application_name(processType);
POOL_SETMASK(&UnBlockSig);
return pid;
}
on_exit_reset();
+ myProcPid = getpid();
processType = PT_WATCHDOG_UTILITY;
set_application_name(processType);
on_exit_reset();
processType = PT_HB_RECEIVER;
+ myProcPid = getpid();
set_application_name(processType);
if (fork_wait_time > 0)
}
on_exit_reset();
+ myProcPid = getpid();
processType = PT_HB_SENDER;
set_application_name(processType);
on_exit_reset();
processType = PT_WATCHDOG_UTILITY;
set_application_name(processType);
+ myProcPid = getpid();
close(STDOUT_FILENO);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[0]);
/* Set the process type variable */
processType = PT_LIFECHECK;
+ myProcPid = getpid();
set_application_name(processType);
/* call lifecheck child main */
/* CHILD */
on_exit_reset();
processType = PT_WATCHDOG_UTILITY;
+ myProcPid = getpid();
set_application_name(processType);
close(STDOUT_FILENO);
dup2(pfd[1], STDOUT_FILENO);