Thread: logger subprocess including win32

logger subprocess including win32

From
Andreas Pflug
Date:
Attached the patch, an orgy in #ifdefs, decorated with various indents
and crlf line ends (glad we have pgindent).

Remarks:
Log_destination for win32 is set to file, because stderr is equivalent
to /dev/null for services. This doesn't reflect correctly in
postgresql.conf.sample.

Log_destination=eventlog is like logging to /var/log/messages, you'd
make the sysadmin your enemy if he finds his app log flooded with
"NOTICE: created implicit index for pk". FYI: The size of the event log
is limited (default: 512k), cyclic overwriting can be selected, but *no*
rotation. Only really important messages should go there, not standard
logging. OTOH, really important messages *must* go there, so I changed
FATAL and PANIC messages to eventlog unconditionally.


Regards,
Andreas
Index: backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.18
diff -u -r1.18 Makefile
--- backend/postmaster/Makefile    21 Jul 2004 20:34:46 -0000    1.18
+++ backend/postmaster/Makefile    4 Aug 2004 18:40:01 -0000
@@ -12,7 +12,7 @@
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global

-OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
+OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o syslogger.o

 all: SUBSYS.o

Index: backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.418
diff -u -r1.418 postmaster.c
--- backend/postmaster/postmaster.c    1 Aug 2004 17:45:43 -0000    1.418
+++ backend/postmaster/postmaster.c    4 Aug 2004 18:40:06 -0000
@@ -117,7 +117,7 @@
 #include "utils/ps_status.h"
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
-
+#include "postmaster/syslogger.h"

 /*
  * List of active backends (or child processes anyway; we don't actually
@@ -200,6 +200,7 @@
             BgWriterPID = 0,
             PgArchPID = 0,
             PgStatPID = 0;
+pid_t       SysLoggerPID = 0;

 /* Startup/shutdown state */
 #define            NoShutdown        0
@@ -851,6 +852,12 @@
 #endif

     /*
+     * start logging to file
+     */
+
+    SysLoggerPID = SysLogger_Start();
+
+    /*
      * Reset whereToSendOutput from Debug (its starting state) to None.
      * This stops ereport from sending log messages to stderr unless
      * Log_destination permits.  We don't do this until the postmaster
@@ -1230,6 +1237,11 @@
             StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
             PgStatPID = pgstat_start();

+        /* If we have lost the system logger, try to start a new one */
+        if (SysLoggerPID == 0 &&
+            StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+            SysLoggerPID = SysLogger_Start();
+
         /*
          * Touch the socket and lock file at least every ten minutes, to ensure
          * that they are not removed by overzealous /tmp-cleaning tasks.
@@ -1770,6 +1782,8 @@
             kill(BgWriterPID, SIGHUP);
         if (PgArchPID != 0)
             kill(PgArchPID, SIGHUP);
+        if (SysLoggerPID != 0)
+            kill(SysLoggerPID, SIGHUP);
         /* PgStatPID does not currently need SIGHUP */
         load_hba();
         load_ident();
@@ -1836,7 +1850,7 @@
                 kill(PgStatPID, SIGQUIT);
             break;

-        case SIGINT:
+                        case SIGINT:
             /*
              * Fast Shutdown:
              *
@@ -2064,6 +2078,15 @@
             continue;
         }

+        /* was it the system logger, try to start a new one */
+        if (SysLoggerPID != 0 && pid == SysLoggerPID)
+        {
+            if (exitstatus != 0)
+                LogChildExit(LOG, gettext("system logger process"),
+                             pid, exitstatus);
+            SysLoggerPID = SysLogger_Start();
+            continue;
+        }
         /*
          * Else do standard backend child cleanup.
          */
@@ -2967,7 +2990,16 @@
         PgstatCollectorMain(argc, argv);
         proc_exit(0);
     }
+    if (strcmp(argv[1], "-forklog") == 0)
+    {
+        /* Close the postmaster's sockets */
+        ClosePostmasterPorts();

+        /* Do not want to attach to shared memory */
+
+        SysLoggerMain(argc, argv);
+        proc_exit(0);
+    }
     return 1;                    /* shouldn't get here */
 }

@@ -3023,7 +3055,7 @@
         if (Shutdown <= SmartShutdown)
             SignalChildren(SIGUSR1);
     }
-
+
     if (PgArchPID != 0 && Shutdown == NoShutdown)
     {
         if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3036,6 +3068,11 @@
         }
     }

+    if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+    {
+        kill(SysLoggerPID, SIGUSR1);
+    }
+
     PG_SETMASK(&UnBlockSig);

     errno = save_errno;
Index: backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v
retrieving revision 1.144
diff -u -r1.144 elog.c
--- backend/utils/error/elog.c    31 Jul 2004 00:45:38 -0000    1.144
+++ backend/utils/error/elog.c    4 Aug 2004 18:40:10 -0000
@@ -71,7 +71,11 @@
 /* GUC parameters */
 PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
 char       *Log_line_prefix = NULL; /* format for extra log line info */
+#ifdef WIN32
+unsigned int Log_destination = LOG_DESTINATION_FILE;
+#else
 unsigned int Log_destination = LOG_DESTINATION_STDERR;
+#endif

 #ifdef HAVE_SYSLOG
 char       *Syslog_facility;    /* openlog() parameters */
@@ -83,6 +87,11 @@
 static void write_eventlog(int level, const char *line);
 #endif

+/* in syslogger.c */
+extern FILE *syslogFile;
+extern FILE *realStdErr;
+extern pid_t SysLoggerPID;
+
 /* We provide a small stack of ErrorData records for re-entrant cases */
 #define ERRORDATA_STACK_SIZE  5

@@ -1554,7 +1563,8 @@
     }
 #endif   /* HAVE_SYSLOG */
 #ifdef WIN32
-    if (Log_destination & LOG_DESTINATION_EVENTLOG)
+    if ((Log_destination & LOG_DESTINATION_EVENTLOG)
+            || edata->elevel >= FATAL)
     {
         int eventlog_level;
         switch (edata->elevel)
@@ -1583,10 +1593,31 @@
         write_eventlog(eventlog_level, buf.data);
     }
 #endif   /* WIN32 */
-    /* Write to stderr, if enabled */
-    if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug)
+    /*
+     * Write to stderr. If Log_destination is file or stderr
+     * if file is target, the logger process will handle this
+     */
+    if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE))
+        || whereToSendOutput == Debug)
     {
-        fprintf(stderr, "%s", buf.data);
+        if (SysLoggerPID == MyProcPid || syslogFile != 0)
+            {
+                if ((Log_destination & LOG_DESTINATION_STDERR)
+                            || whereToSendOutput == Debug)
+                {
+                     if (realStdErr != 0)
+                        fprintf(realStdErr, "%s", buf.data);
+                    else
+                        fprintf(stderr, "%s", buf.data) ;
+
+                }
+
+                if (syslogFile != 0)
+                    fprintf(syslogFile, "%s", buf.data) ;
+            }
+            else
+        fprintf(stderr, "%s", buf.data) ;
+
     }

     pfree(buf.data);
Index: backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.225
diff -u -r1.225 guc.c
--- backend/utils/misc/guc.c    28 Jul 2004 14:23:29 -0000    1.225
+++ backend/utils/misc/guc.c    4 Aug 2004 18:40:19 -0000
@@ -44,6 +44,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "postmaster/bgwriter.h"
+#include "postmaster/syslogger.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
@@ -1285,6 +1286,23 @@
         BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
     },

+    {
+      {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur after n minutes"),
+       NULL
+      },
+      &Log_RotationAge,
+      24*60, 0, INT_MAX, NULL, NULL
+    },
+    {
+      {"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur if this size is reached (in kb)"),
+       NULL
+      },
+      &Log_RotationSize,
+      10*1024, 0, INT_MAX, NULL, NULL
+    },
+
     /* End-of-list marker */
     {
         {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -1625,14 +1643,37 @@
     },

     {
-        {"log_destination", PGC_SIGHUP, LOGGING_WHERE,
-         gettext_noop("Sets the destination for server log output."),
-         gettext_noop("Valid values are combinations of stderr, syslog "
+        {"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
+         gettext_noop("Sets the target for log output."),
+         gettext_noop("Valid values are combinations of stderr, file, syslog "
                       "and eventlog, depending on platform."),
          GUC_LIST_INPUT
         },
         &log_destination_string,
+#ifdef WIN32
+        "file", assign_log_destination, NULL
+#else
         "stderr", assign_log_destination, NULL
+#endif
+        },
+    {
+        {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("Sets the target directory for log output."),
+         gettext_noop("May be specified as relative to the cluster directory "
+                      "or as absolute path."),
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_directory,
+        "pg_log", NULL, NULL
+    },
+    {
+        {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("prefix for logfile names created in the log_directory."),
+         NULL,
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_filename_prefix,
+        "postgresql-", NULL, NULL
     },

 #ifdef HAVE_SYSLOG
@@ -5079,6 +5120,8 @@

         if (pg_strcasecmp(tok,"stderr") == 0)
             newlogdest |= LOG_DESTINATION_STDERR;
+        else if (pg_strcasecmp(tok,"file") == 0)
+            newlogdest |= LOG_DESTINATION_FILE;
 #ifdef HAVE_SYSLOG
         else if (pg_strcasecmp(tok,"syslog") == 0)
             newlogdest |= LOG_DESTINATION_SYSLOG;
Index: backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.118
diff -u -r1.118 postgresql.conf.sample
--- backend/utils/misc/postgresql.conf.sample    21 Jul 2004 20:34:46 -0000    1.118
+++ backend/utils/misc/postgresql.conf.sample    4 Aug 2004 18:40:20 -0000
@@ -167,9 +167,17 @@

 # - Where to Log -

-#log_destination = 'stderr'    # Valid values are combinations of stderr,
-                                # syslog and eventlog, depending on
-                                # platform.
+#log_destination = 'stderr' # Valid values are combinations of stderr, file,
+                            # syslog and eventlog, depending on platform.
+#log_directory = 'pg_log'   # subdirectory where logfiles are written
+                            # if 'file' log_destination is used.
+                            # May be specified absolute or relative to PGDATA
+#log_filename_prefix = 'postgresql_' # prefix for logfile names
+#log_rotation_age = 1440    # Automatic rotation of logfiles will happen if
+                            # specified age in minutes is reached. 0 to disable.
+#log_rotation_size = 10240  # Automatic rotation of logfiles will happen if
+                            # specified size in kb is reached. 0 to disable.
+
 #syslog_facility = 'LOCAL0'
 #syslog_ident = 'postgres'

Index: include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.9
diff -u -r1.9 pmsignal.h
--- include/storage/pmsignal.h    19 Jul 2004 02:47:15 -0000    1.9
+++ include/storage/pmsignal.h    4 Aug 2004 18:40:21 -0000
@@ -25,7 +25,7 @@
     PMSIGNAL_PASSWORD_CHANGE,    /* pg_pwd file has changed */
     PMSIGNAL_WAKEN_CHILDREN,    /* send a SIGUSR1 signal to all backends */
     PMSIGNAL_WAKEN_ARCHIVER,    /* send a NOTIFY signal to xlog archiver */
-
+    PMSIGNAL_ROTATE_LOGFILE,    /* send SIGUSR1 to syslogger to rotate logfile */
     NUM_PMSIGNALS                /* Must be last value of enum! */
 } PMSignalReason;

Index: elog.h===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v
retrieving revision 1.72
diff -u -r1.72 elog.h
--- elog.h    31 Jul 2004 23:04:55 -0000    1.72
+++ elog.h    4 Aug 2004 18:45:49 -0000
@@ -278,6 +278,7 @@
 #define LOG_DESTINATION_STDERR   1
 #define LOG_DESTINATION_SYSLOG   2
 #define LOG_DESTINATION_EVENTLOG 4
+#define LOG_DESTINATION_FILE     8

 /* Other exported functions */
 extern void DebugFileOpen(void);



/*-------------------------------------------------------------------------
 *
 * syslogger.c
 *
 * The system logger (syslogger) is new in Postgres 7.5. It catches all
 * stderr output from backends, the postmaster and subprocesses by
 * redirecting to a pipe, and writes it to a logfile and stderr if
 * configured.
 * It's possible to have size and age limits for the logfile configured
 * in postgresql.conf. If these limits are reached or passed, the
 * current logfile is closed and a new one is created (rotated).
 * The logfiles are stored in a subdirectory (configurable in
 * postgresql.conf), using an internal naming scheme that mangles
 * creation time and current postmaster pid.
 *
 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
 *
 * Copyright (c) 2004, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "storage/pmsignal.h"
#include "storage/pg_shmem.h"
#include "storage/ipc.h"
#include "postmaster/syslogger.h"
#include "utils/ps_status.h"
#include "utils/guc.h"

/*
 * GUC parameters
 */
int            Log_RotationAge = 24*60;
int            Log_RotationSize  = 10*1024;
char *      Log_directory = "pg_log";
char *      Log_filename_prefix = "postgresql-";


extern pid_t SysLoggerPID;


/*
 * 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;

#define MAXRETRIES  3
static pg_time_t    last_rotation_time = 0;
static char         currentLogDir[MAXPGPATH];


static void sigHupHandler(SIGNAL_ARGS);
static void rotationHandler(SIGNAL_ARGS);
void writeLogfile(char *buffer, int count);

#ifdef EXEC_BACKEND
static pid_t syslogger_forkexec(void);
#endif

static char* logfile_getname(pg_time_t timestamp);
static bool logfile_rotate(void);

FILE *realStdErr = NULL;
volatile FILE *syslogFile = NULL;

#ifdef WIN32
static unsigned int __stdcall pipeThread(void *arg);
static HANDLE readPipe=0, writePipe=0;
static HANDLE threadHandle=0;
static CRITICAL_SECTION sysfileSection;
#else
static int syslogPipe[2] = {0, 0};
#endif


void
writeLogfile(char *buffer, int count)
{
    int rc;
#ifdef WIN32
    EnterCriticalSection(&sysfileSection);
    rc = fwrite(buffer, 1, count, (FILE*)syslogFile);
    LeaveCriticalSection(&sysfileSection);
#else
    rc = fwrite(buffer, 1, count, (FILE*)syslogFile);
#endif
    if (rc < 1)
    {
        ereport(COMMERROR,
                            (errcode_for_file_access(),
                             errmsg("fwrite to logfile failed in system logger: %m")));
            exit(1);
    }

    if (Log_destination & LOG_DESTINATION_STDERR)
    {
        if (realStdErr)
            fwrite(buffer, 1, count, realStdErr);
        else
            fwrite(buffer, 1, count, stderr);
    }
}

#ifdef WIN32
unsigned int __stdcall
pipeThread(void *arg)
{
    DWORD bytesRead;
    char    logbuffer[1024];
    for (;;)
    {
        if (!ReadFile(readPipe,
            logbuffer, sizeof(logbuffer), &bytesRead, 0))
        {
                ereport(COMMERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not read from system logger pipe: %m")));
                exit(1);
        }
        if (bytesRead > 0)
            writeLogfile(logbuffer, bytesRead);
    }

    _endthread();
    return 0;
}
#endif

/*
 * Main entry point for syslogger process
 * argc/argv parameters are valid only in EXEC_BACKEND case.
 */

void
SysLoggerMain(int argc, char *argv[])
{
#ifdef EXEC_BACKEND
    int fd;
    Assert(argc == 8);

    argv += 3;
    strncpy(postgres_exec_path, *argv++, MAXPGPATH);

#ifdef WIN32
    readPipe = (HANDLE)atoi(*argv++);
    writePipe = (HANDLE)atoi(*argv++);
    fd = atoi(*argv++);
        if (fd != 0)
            fd = _open_osfhandle(fd, _O_APPEND);
    if (fd != 0)
        syslogFile = fdopen(fd, "a+");

    fd = atoi(*argv++);
        if (fd != 0)
            fd = _open_osfhandle(fd, _O_APPEND);
    if (fd != 0)
        {
        realStdErr = fdopen(fd, "a");
            setvbuf((FILE*)realStdErr, 0, _IONBF, 0);
        }
        InitializeCriticalSection(&sysfileSection);

        unsigned int tid;
        threadHandle = (HANDLE)_beginthreadex(0, 0, pipeThread, 0, 0, &tid);

#else
    syslogPipe[0] = atoi(*argv++);
    syslogPipe[1] = atoi(*argv++);
    fd = atoi(*argv++);
    if (fd != 0)
        syslogFile = fdopen(fd, "a+");
    fd = atoi(*argv++);
    if (fd != 0)
        {
        realStdErr = fdopen(fd, "a");
            setvbuf((FILE*)realStdErr, 0, _IONBF, 0);
        }
#endif

#endif

        setvbuf((FILE*)syslogFile, 0, _IONBF, 0);

    IsUnderPostmaster = true;
    MyProcPid = getpid();
    init_ps_display("system logger process", "", "");
    set_ps_display("");

    /*
     * Properly accept or ignore signals the postmaster might send us
     *
     * Note: we ignore all termination signals, and wait for the postmaster
     * to die to catch as much pipe output as possible.
     */

    pqsignal(SIGHUP, sigHupHandler);    /* set flag to read config file */
    pqsignal(SIGINT,  SIG_IGN);
    pqsignal(SIGTERM, SIG_IGN);
    pqsignal(SIGQUIT, SIG_IGN);
    pqsignal(SIGALRM, SIG_IGN);
    pqsignal(SIGPIPE, SIG_IGN);
    pqsignal(SIGUSR1, rotationHandler);  /* request log rotation */
    pqsignal(SIGUSR2, SIG_IGN);

    /*
     * Reset some signals that are accepted by postmaster but not here
     */
    pqsignal(SIGCHLD, SIG_DFL);
    pqsignal(SIGTTIN, SIG_DFL);
    pqsignal(SIGTTOU, SIG_DFL);
    pqsignal(SIGCONT, SIG_DFL);
    pqsignal(SIGWINCH, SIG_DFL);

    PG_SETMASK(&UnBlockSig);

    /*
     * if we restarted, our stderr is redirected.
     * Direct it back to system stderr.
     */
    if (realStdErr != NULL)
    {
        if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
        {
            char *errstr = strerror(errno);
            /*
             * Now we have a real problem: we can't redirect to stderr,
             * and can't ereport it correctly (it would go into our queue
             * which will never be read
             * We're writing everywhere, hoping to leave at least some
             * hint of what happened.
             */

            fprintf(realStdErr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
            fprintf(stderr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
            ereport(PANIC,
                    (errcode_for_file_access(),
                     (errmsg("Syslogger couldn't redirect its stderr to the saved stderr: %s", errstr))));
            exit(1);
        }

        realStdErr = NULL;
    }

    /* remember age of initial logfile */
    last_rotation_time = time(NULL);

    strncpy(currentLogDir, Log_directory, MAXPGPATH);
    /* main worker loop */
    for (;;)
    {
                pg_time_t   now;
                int         elapsed_secs;
#ifndef WIN32
        char        logbuffer[1024];
        int          bytesRead;
        int         rc;
                fd_set        rfds;
        struct timeval timeout;
#endif

                if (got_SIGHUP)
                {
                    got_SIGHUP = false;
                    ProcessConfigFile(PGC_SIGHUP);

                    /*
                     * check if the log directory changed
                     * in postgresql.conf. If so, we rotate to make sure
                     * we're writing the logfiles where the backends
                     * expect us to do so.
                     */
                    if (strncmp(Log_directory, currentLogDir, MAXPGPATH))
                    {
                            rotation_requested = true;
                            strncpy(currentLogDir, Log_directory, MAXPGPATH);
                    }
                }

                if (!rotation_requested && last_rotation_time != 0 && Log_RotationAge > 0)
                {
                    /*
                     * Do an unforced rotation if too much time has elapsed
                       * since the last one.
                     */
                    now = time(NULL);
                    elapsed_secs = now - last_rotation_time;
                    if (elapsed_secs >= Log_RotationAge * 60)
                                rotation_requested = true;
                }

                if (!rotation_requested && Log_RotationSize > 0)
                {
                    /*
                     * Do an unforced rotation if file is too big
                     */
                    if (ftell((FILE*)syslogFile) >= Log_RotationSize * 1024)
                        rotation_requested = true;
                }

                if (rotation_requested)
                {
                    if (!logfile_rotate())
                    {
                        ereport(ERROR,
                                            (errcode_for_file_access(),
                                             (errmsg("logfile rotation failed, disabling auto rotation (SIGHUP to
reenable):%m")))); 

                            Log_RotationAge = 0;
                            Log_RotationSize = 0;
                    }
                    rotation_requested = false;
                }

#ifdef WIN32
               pgwin32_backend_usleep(1000000);
#else
        FD_ZERO(&rfds);
        FD_SET(syslogPipe[0], &rfds);
        timeout.tv_sec=1;
        timeout.tv_usec=0;

        /*
         * Check if data is present
         */
        rc = select(syslogPipe[0]+1, &rfds, NULL, NULL, &timeout);
        PG_SETMASK(&UnBlockSig);

        if (rc < 0 && errno != EINTR)
        {
            ereport(COMMERROR,
                    (errcode_for_socket_access(),
                     errmsg("select() failed in system logger: %m")));
            exit(1);
        }
        if (rc > 0 && FD_ISSET(syslogPipe[0], &rfds))
        {
            bytesRead = piperead(syslogPipe[0],
                                 logbuffer, sizeof(logbuffer));

            if (bytesRead < 0 && errno != EINTR)
            {
                /*
                 * EMFILE has been observed when a backend terminated
                 * after a SSL client connection broke down.
                 * There might be others we should ignore.
                 */
                if (errno == EMFILE)
                    continue;

                ereport(COMMERROR,
                        (errcode_for_socket_access(),
                         errmsg("could not read from system logger pipe: %m")));
                exit(1);
            }
                        else if (bytesRead > 0)
            {
                                writeLogfile(logbuffer, bytesRead);
                continue;
            }
                }
#endif
        /*
         * If postmaster died, there's nothing to log any more.
         * We check this only after pipe timeouts to receive as much as possible
         * from the pipe.
         */
        if (!PostmasterIsAlive(true))
        {
#ifdef WIN32
                    /* make sure the pipe is empty */
                    Sleep(100);
                    SuspendThread(threadHandle);
                    CloseHandle(threadHandle);
#endif
                    if (syslogFile)
                fclose((FILE*)syslogFile);
            exit(0);
        }
    }
}


int
SysLogger_Start(void)
{
    pid_t sysloggerPid;
    pg_time_t now;
    char *filename;

    if (!(Log_destination & LOG_DESTINATION_FILE))
        return 0;

    /* create the pipe which will receive stderr output */
#ifdef WIN32
    if (!readPipe)
    {
            SECURITY_ATTRIBUTES sa;
            memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
            sa.nLength = sizeof(SECURITY_ATTRIBUTES);
            sa.bInheritHandle = TRUE;

            if (!CreatePipe(&readPipe, &writePipe, &sa, 32768))
            ereport(FATAL,
                    (errcode_for_file_access(),
                     (errmsg("pipe for syslogging not created: %m"))));
#else
    if (!syslogPipe[0])
    {
        if (pgpipe(syslogPipe) < 0)
            ereport(FATAL,
                    (errcode_for_socket_access(),
                     (errmsg("pipe for syslogging not created: %m"))));

        if (!set_noblock(syslogPipe[1]))
        {
            ereport(FATAL,
                    (errcode_for_socket_access(),
                     errmsg("could not set syslogging pipe to nonblocking mode: %m")));
        }
#endif
    }

    now = time(NULL);

        /*
           * create log directory, ignore error (will be caught on first logfile open)
           */
        if (is_absolute_path(Log_directory))
            mkdir(Log_directory, 0700);
        else
        {
            filename = palloc(MAXPGPATH);
            snprintf(filename, MAXPGPATH, "%s/%s", DataDir, Log_directory);
            mkdir(filename, 0700);
            pfree(filename);
        }

    /*
     * The initial logfile is created right in the postmaster,
     * to insure that the logger process has a writable file.
     */
    filename = logfile_getname(now);

    /*
     * The file is opened for appending, in case the syslogger
     * is restarted right after a rotation.
     */
    syslogFile = fopen(filename, "a+");

    if (!syslogFile)
    {
        /*
         * if we can't open the syslog file for the syslogger process,
         * we try to redirect stderr back to have some logging.
         */
        ereport(WARNING,
                (errcode_for_file_access(),
                 (errmsg("error opening syslog file %s: %m", filename))));

        if (realStdErr != NULL)
        {
            if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
                ereport(FATAL,
                        (errcode_for_file_access(),
                         (errmsg("error redirecting stderr to default: %m"))));

            ereport(FATAL,
                    (errmsg("logfile output corrupted")));
        }

    }

    pfree(filename);

    fflush(stdout);
    fflush(stderr);

#ifdef __BEOS__
    /* Specific beos actions before backend startup */
    beos_before_backend_startup();
#endif


#ifdef EXEC_BACKEND
    switch ((sysloggerPid = syslogger_forkexec()))
#else
    switch ((sysloggerPid = fork()))
#endif
    {
        case -1:
#ifdef __BEOS__
            /* Specific beos actions */
            beos_backend_startup_failed();
#endif
            ereport(LOG,
                    (errmsg("could not fork system logger: %m")));
            return 0;

#ifndef EXEC_BACKEND
        case 0:
            /* in postmaster child ... */
#ifdef __BEOS__
            /* Specific beos actions after backend startup */
            beos_backend_startup();
#endif
            /* Close the postmaster's sockets */
            ClosePostmasterPorts();

            /* Drop our connection to postmaster's shared memory, as well */
            PGSharedMemoryDetach();

            /* do the work */
            SysLoggerMain(0, NULL);
            break;
#endif

        default:
            /* now we redirect stderr, if not done already */
            if (realStdErr == NULL)
            {
                int dh= dup(fileno(stderr));

                if (dh < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                             (errmsg("stderr duplication failed: %m"))));

                realStdErr = fdopen(dh, "a");
                if (realStdErr == NULL)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                             (errmsg("realstderr reopen failed: %m"))));
#ifdef WIN32
                if (dup2(_open_osfhandle((long)writePipe, _O_APPEND), _fileno(stderr)) < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                          (errmsg("stderr pipe redirection failed: %m"))));
#else
                if (dup2(syslogPipe[1], fileno(stdout)) < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                          (errmsg("stdout pipe redirection failed: %m"))));
                if (dup2(syslogPipe[1], fileno(stderr)) < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                          (errmsg("stderr pipe redirection failed: %m"))));
#endif
            }

            /* postmaster will never write the file; close it */
            fclose((FILE*)syslogFile);
            syslogFile = NULL;
            return (int) sysloggerPid;
    }
    /* we should never reach here */
    return 0;
}


#ifdef EXEC_BACKEND
static pid_t
syslogger_forkexec()
{
    char *av[10];
    int ac = 0, bufc = 0, i;
    char numbuf[4][32];

    av[ac++] = "postgres";
    av[ac++] = "-forklog";
    av[ac++] = NULL;            /* filled in by postmaster_forkexec */

    /* postgres_exec_path is not passed by write_backend_variables */
    av[ac++] = postgres_exec_path;

    /* Pipe file ids (those not passed by write_backend_variables) */
#ifdef WIN32
    snprintf(numbuf[bufc++], 32, "%d", (int)readPipe);
    snprintf(numbuf[bufc++], 32, "%d", (int)writePipe);
    if (syslogFile != 0)
        snprintf(numbuf[bufc++], 32, "%ld", _get_osfhandle(_fileno((FILE*)syslogFile)));
    else
        strcpy(numbuf[bufc++], "0");
    if (realStdErr != 0)
        snprintf(numbuf[bufc++],32, "%ld", _get_osfhandle(_fileno(realStdErr)));
    else
        strcpy(numbuf[bufc++], "0");
#else
    snprintf(numbuf[bufc++],32, "%d", syslogPipe[0]);
    snprintf(numbuf[bufc++],32, "%d", syslogPipe[1]);
    if (syslogFile != 0)
        snprintf(numbuf[bufc++], 32, "%d", fileno(syslogFile)));
    else
        strcpy(numbuf[bufc++], "0");
    if (realStdErr != 0)
        snprintf(numbuf[bufc++], 32, "%d", fileno(realStdErr)));
    else
        strcpy(numbuf[bufc++], "0");
#endif

    /* Add to the arg list */
    Assert(bufc <= lengthof(pgstatBuf));
    for (i = 0; i < bufc; i++)
        av[ac++] = numbuf[i];

    av[ac] = NULL;
    Assert(ac < lengthof(av));

    return postmaster_forkexec(ac, av);
}
#endif

/* --------------------------------
 *        logfile routines
 * --------------------------------
 */


/*
 * perform rotation
 */
bool
logfile_rotate(void)
{
    char *filename;
    pg_time_t now;
    FILE *fh;

    now = time(NULL);
    filename = logfile_getname(now);

    fh = fopen(filename, "a+");
    if (!fh)
    {
        /*
         * if opening the new file fails, the caller is responsible
         * for taking consequences. */

        pfree(filename);
        return false;
    }
        setvbuf(fh, 0, _IONBF, 0);

#ifdef WIN32
        EnterCriticalSection(&sysfileSection);
    fclose((FILE*)syslogFile);
    syslogFile = fh;
        LeaveCriticalSection(&sysfileSection);
#else
    fclose(syslogFile);
    syslogFile = fh;
#endif
    last_rotation_time = now;

    /* official opening of the new logfile */
    ereport(NOTICE,
            (errcode(ERRCODE_WARNING),
             errmsg("Opened new log file %s", filename)));

    pfree(filename);
    return true;
}



/*
 * creates logfile name using timestamp information
 */

#define TIMESTAMPPATTERN "%Y-%m-%d_%H%M%S"

static char*
logfile_getname(pg_time_t timestamp)
{
    char *filetemplate;
    char *filename;


    if (is_absolute_path(Log_directory))
    {
      filetemplate = palloc(strlen(Log_directory)
                            + strlen(Log_filename_prefix)
                            + sizeof(TIMESTAMPPATTERN)+10 +2);
        if (filetemplate)
            sprintf(filetemplate, "%s/%s%s_%05lu.log",
                    Log_directory, Log_filename_prefix,
                    TIMESTAMPPATTERN, (unsigned long)PostmasterPid);
    }
    else
    {
        filetemplate = palloc(strlen(DataDir) + strlen(Log_directory)
                              + strlen(Log_filename_prefix)
                              + sizeof(TIMESTAMPPATTERN) +10 +3);
        if (filetemplate)
            sprintf(filetemplate, "%s/%s/%s%s_%05lu.log",
                    DataDir, Log_directory, Log_filename_prefix,
                    TIMESTAMPPATTERN, (unsigned long)PostmasterPid);
    }
    filename = palloc(MAXPGPATH);

    if (!filename || !filetemplate)
        ereport(FATAL,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("Out of memory")));

    pg_strftime(filename, MAXPGPATH, filetemplate, pg_localtime(×tamp));

    pfree(filetemplate);

    return filename;
}

/* --------------------------------
 *        API helper routines
 * --------------------------------
 */

/*
 * Rotate log file
 */
bool
LogFileRotate(void)
{
    if (!(Log_destination & LOG_DESTINATION_FILE))
    {
        ereport(NOTICE,
                (errcode(ERRCODE_WARNING),
                 errmsg("no logfile configured; rotation not supported")));
        return false;
    }

    SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);

    return true;
}

/* --------------------------------
 *        signal handler routines
 * --------------------------------
 */

/* SIGHUP: set flag to reload config file */
static void
sigHupHandler(SIGNAL_ARGS)
{
    got_SIGHUP = true;
}

/* SIGUSR1: set flag to rotate logfile */
static void
rotationHandler(SIGNAL_ARGS)
{
    rotation_requested = true;
}
/*-------------------------------------------------------------------------
 *
 * syslogger.h
 *      Exports from postmaster/syslogger.c.
 *
 * Copyright (c) 2004, PostgreSQL Global Development Group
 *
 * $PostgreSQL: $
 *
 *-------------------------------------------------------------------------
 */
#ifndef _SYSLOGGER_H
#define _SYSLOGGER_H

#include "pgtime.h"

/* GUC options */
extern int        Log_RotationAge;
extern int        Log_RotationSize;
extern char *   Log_directory;
extern char *   Log_filename_prefix;


extern int SysLogger_Start(void);
extern void SysLoggerMain(int argc, char *argv[]);

extern bool LogFileRotate(void);

#endif   /* _SYSLOGGER_H */

Re: logger subprocess including win32

From
Tom Lane
Date:
Andreas Pflug <pgadmin@pse-consulting.de> writes:
> Attached the patch, an orgy in #ifdefs, decorated with various indents
> and crlf line ends (glad we have pgindent).

I spent a fair amount of time fooling with this, trying to extract
something that I trusted enough to apply at this late date, but got
stuck on one point.  Exiting when the postmaster dies is *not* good
enough; we want the logger to stick around until the last process
upstream of the logger pipe is gone.  In the Unix case we can detect
this by watching for EOF on the pipe, but I don't know how to do the
equivalent in this threaded scheme you've devised for Windows.

(Why is the separate thread needed, again?)

            regards, tom lane

Re: logger subprocess including win32

From
Andreas Pflug
Date:
Tom Lane wrote:
> Andreas Pflug <pgadmin@pse-consulting.de> writes:
>
>>Attached the patch, an orgy in #ifdefs, decorated with various indents
>>and crlf line ends (glad we have pgindent).
>
>
> I spent a fair amount of time fooling with this, trying to extract
> something that I trusted enough to apply at this late date, but got
> stuck on one point.  Exiting when the postmaster dies is *not* good
> enough; we want the logger to stick around until the last process
> upstream of the logger pipe is gone.  In the Unix case we can detect
> this by watching for EOF on the pipe,


I saw strange errnos coming from that pipe, i.e. EMFILE. I'm not sure if
EOF is really reliable.


  but I don't know how to do the equivalent in this threaded scheme
you've devised for Windows.

if (realStdErr !0 NULL)
{
    ...
}
#ifdef WIN32
CloseHandle(writePipe);
#else
close(syslogPipe[1]);
#endif


You probably found out yourself.


In pipeThread:

if (!ReadFile(...))
{
    DWORD error = GetLastError();
    if (error == ERROR_HANDLE_EOF)
       exit(0);

/* errno is not set */
    ereport(COMERROR,
    errmsg("could not read from system logger pipe: %d", error)))}
}


> (Why is the separate thread needed, again?)

On unnamed pipes, WaitForSingleObject does not work (it always reports
"signaled", so the blocking ReadFile won't allow for
sighup/IsPostmasterRunning; select is for sockets only).



Regards,
Andreas



Re: logger subprocess including win32

From
Tom Lane
Date:
Andreas Pflug <pgadmin@pse-consulting.de> writes:
> Tom Lane wrote:
>>   but I don't know how to do the equivalent in this threaded scheme
>> you've devised for Windows.

> In pipeThread:

> if (!ReadFile(...))
> {
>     DWORD error = GetLastError();
>     if (error == ERROR_HANDLE_EOF)
>        exit(0);

Got it.  And there's no reason that the pipe thread can't do exit(0)
for itself?  Might be cleaner to make it set a flag for the main line
to look at ...

            regards, tom lane

Re: logger subprocess including win32

From
Andreas Pflug
Date:
Tom Lane wrote:

>
>
>>if (!ReadFile(...))
>>{
>>    DWORD error = GetLastError();
>>    if (error == ERROR_HANDLE_EOF)
>>       exit(0);
>
>
> Got it.  And there's no reason that the pipe thread can't do exit(0)
> for itself?

Not really. All threads are equivalent.
BTW, should there be a last NOTICE "syslogger shutting down"?

Regards,
Andreas