LCOV - code coverage report
Current view: top level - src/backend/postmaster - syslogger.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 61.1 % 373 228
Test Date: 2026-03-05 08:14:41 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * syslogger.c
       4              :  *
       5              :  * The system logger (syslogger) appeared in Postgres 8.0. It catches all
       6              :  * stderr output from the postmaster, backends, and other subprocesses
       7              :  * by redirecting to a pipe, and writes it to a set of logfiles.
       8              :  * It's possible to have size and age limits for the logfile configured
       9              :  * in postgresql.conf. If these limits are reached or passed, the
      10              :  * current logfile is closed and a new one is created (rotated).
      11              :  * The logfiles are stored in a subdirectory (configurable in
      12              :  * postgresql.conf), using a user-selectable naming scheme.
      13              :  *
      14              :  * Author: Andreas Pflug <pgadmin@pse-consulting.de>
      15              :  *
      16              :  * Copyright (c) 2004-2026, PostgreSQL Global Development Group
      17              :  *
      18              :  *
      19              :  * IDENTIFICATION
      20              :  *    src/backend/postmaster/syslogger.c
      21              :  *
      22              :  *-------------------------------------------------------------------------
      23              :  */
      24              : #include "postgres.h"
      25              : 
      26              : #include <fcntl.h>
      27              : #include <limits.h>
      28              : #include <signal.h>
      29              : #include <time.h>
      30              : #include <unistd.h>
      31              : #include <sys/stat.h>
      32              : #include <sys/time.h>
      33              : 
      34              : #include "common/file_perm.h"
      35              : #include "lib/stringinfo.h"
      36              : #include "libpq/pqsignal.h"
      37              : #include "miscadmin.h"
      38              : #include "nodes/pg_list.h"
      39              : #include "pgstat.h"
      40              : #include "pgtime.h"
      41              : #include "port/pg_bitutils.h"
      42              : #include "postmaster/interrupt.h"
      43              : #include "postmaster/postmaster.h"
      44              : #include "postmaster/syslogger.h"
      45              : #include "storage/dsm.h"
      46              : #include "storage/fd.h"
      47              : #include "storage/ipc.h"
      48              : #include "storage/latch.h"
      49              : #include "storage/pg_shmem.h"
      50              : #include "tcop/tcopprot.h"
      51              : #include "utils/guc.h"
      52              : #include "utils/memutils.h"
      53              : #include "utils/ps_status.h"
      54              : 
      55              : /*
      56              :  * We read() into a temp buffer twice as big as a chunk, so that any fragment
      57              :  * left after processing can be moved down to the front and we'll still have
      58              :  * room to read a full chunk.
      59              :  */
      60              : #define READ_BUF_SIZE (2 * PIPE_CHUNK_SIZE)
      61              : 
      62              : /* Log rotation signal file path, relative to $PGDATA */
      63              : #define LOGROTATE_SIGNAL_FILE   "logrotate"
      64              : 
      65              : 
      66              : /*
      67              :  * GUC parameters.  Logging_collector cannot be changed after postmaster
      68              :  * start, but the rest can change at SIGHUP.
      69              :  */
      70              : bool        Logging_collector = false;
      71              : int         Log_RotationAge = HOURS_PER_DAY * MINS_PER_HOUR;
      72              : int         Log_RotationSize = 10 * 1024;
      73              : char       *Log_directory = NULL;
      74              : char       *Log_filename = NULL;
      75              : bool        Log_truncate_on_rotation = false;
      76              : int         Log_file_mode = S_IRUSR | S_IWUSR;
      77              : 
      78              : /*
      79              :  * Private state
      80              :  */
      81              : static pg_time_t next_rotation_time;
      82              : static bool pipe_eof_seen = false;
      83              : static bool rotation_disabled = false;
      84              : static FILE *syslogFile = NULL;
      85              : static FILE *csvlogFile = NULL;
      86              : static FILE *jsonlogFile = NULL;
      87              : NON_EXEC_STATIC pg_time_t first_syslogger_file_time = 0;
      88              : static char *last_sys_file_name = NULL;
      89              : static char *last_csv_file_name = NULL;
      90              : static char *last_json_file_name = NULL;
      91              : 
      92              : /*
      93              :  * Buffers for saving partial messages from different backends.
      94              :  *
      95              :  * Keep NBUFFER_LISTS lists of these, with the entry for a given source pid
      96              :  * being in the list numbered (pid % NBUFFER_LISTS), so as to cut down on
      97              :  * the number of entries we have to examine for any one incoming message.
      98              :  * There must never be more than one entry for the same source pid.
      99              :  *
     100              :  * An inactive buffer is not removed from its list, just held for re-use.
     101              :  * An inactive buffer has pid == 0 and undefined contents of data.
     102              :  */
     103              : typedef struct
     104              : {
     105              :     int32       pid;            /* PID of source process */
     106              :     StringInfoData data;        /* accumulated data, as a StringInfo */
     107              : } save_buffer;
     108              : 
     109              : #define NBUFFER_LISTS 256
     110              : static List *buffer_lists[NBUFFER_LISTS];
     111              : 
     112              : /* These must be exported for EXEC_BACKEND case ... annoying */
     113              : #ifndef WIN32
     114              : int         syslogPipe[2] = {-1, -1};
     115              : #else
     116              : HANDLE      syslogPipe[2] = {0, 0};
     117              : #endif
     118              : 
     119              : #ifdef WIN32
     120              : static HANDLE threadHandle = 0;
     121              : static CRITICAL_SECTION sysloggerSection;
     122              : #endif
     123              : 
     124              : /*
     125              :  * Flags set by interrupt handlers for later service in the main loop.
     126              :  */
     127              : static volatile sig_atomic_t rotation_requested = false;
     128              : 
     129              : 
     130              : /* Local subroutines */
     131              : #ifdef EXEC_BACKEND
     132              : static int  syslogger_fdget(FILE *file);
     133              : static FILE *syslogger_fdopen(int fd);
     134              : #endif
     135              : static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
     136              : static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
     137              : static FILE *logfile_open(const char *filename, const char *mode,
     138              :                           bool allow_errors);
     139              : 
     140              : #ifdef WIN32
     141              : static unsigned int __stdcall pipeThread(void *arg);
     142              : #endif
     143              : static void logfile_rotate(bool time_based_rotation, int size_rotation_for);
     144              : static bool logfile_rotate_dest(bool time_based_rotation,
     145              :                                 int size_rotation_for, pg_time_t fntime,
     146              :                                 int target_dest, char **last_file_name,
     147              :                                 FILE **logFile);
     148              : static char *logfile_getname(pg_time_t timestamp, const char *suffix);
     149              : static void set_next_rotation_time(void);
     150              : static void sigUsr1Handler(SIGNAL_ARGS);
     151              : static void update_metainfo_datafile(void);
     152              : 
     153              : typedef struct
     154              : {
     155              :     int         syslogFile;
     156              :     int         csvlogFile;
     157              :     int         jsonlogFile;
     158              : } SysloggerStartupData;
     159              : 
     160              : /*
     161              :  * Main entry point for syslogger process
     162              :  * argc/argv parameters are valid only in EXEC_BACKEND case.
     163              :  */
     164              : void
     165            1 : SysLoggerMain(const void *startup_data, size_t startup_data_len)
     166              : {
     167              : #ifndef WIN32
     168              :     char        logbuffer[READ_BUF_SIZE];
     169            1 :     int         bytes_in_logbuffer = 0;
     170              : #endif
     171              :     char       *currentLogDir;
     172              :     char       *currentLogFilename;
     173              :     int         currentLogRotationAge;
     174              :     pg_time_t   now;
     175              :     WaitEventSet *wes;
     176              : 
     177              :     /*
     178              :      * Re-open the error output files that were opened by SysLogger_Start().
     179              :      *
     180              :      * We expect this will always succeed, which is too optimistic, but if it
     181              :      * fails there's not a lot we can do to report the problem anyway.  As
     182              :      * coded, we'll just crash on a null pointer dereference after failure...
     183              :      */
     184              : #ifdef EXEC_BACKEND
     185              :     {
     186              :         const SysloggerStartupData *slsdata = startup_data;
     187              : 
     188              :         Assert(startup_data_len == sizeof(*slsdata));
     189              :         syslogFile = syslogger_fdopen(slsdata->syslogFile);
     190              :         csvlogFile = syslogger_fdopen(slsdata->csvlogFile);
     191              :         jsonlogFile = syslogger_fdopen(slsdata->jsonlogFile);
     192              :     }
     193              : #else
     194              :     Assert(startup_data_len == 0);
     195              : #endif
     196              : 
     197              :     /*
     198              :      * Now that we're done reading the startup data, release postmaster's
     199              :      * working memory context.
     200              :      */
     201            1 :     if (PostmasterContext)
     202              :     {
     203            1 :         MemoryContextDelete(PostmasterContext);
     204            1 :         PostmasterContext = NULL;
     205              :     }
     206              : 
     207            1 :     now = MyStartTime;
     208              : 
     209            1 :     init_ps_display(NULL);
     210              : 
     211              :     /*
     212              :      * If we restarted, our stderr is already redirected into our own input
     213              :      * pipe.  This is of course pretty useless, not to mention that it
     214              :      * interferes with detecting pipe EOF.  Point stderr to /dev/null. This
     215              :      * assumes that all interesting messages generated in the syslogger will
     216              :      * come through elog.c and will be sent to write_syslogger_file.
     217              :      */
     218            1 :     if (redirection_done)
     219              :     {
     220            0 :         int         fd = open(DEVNULL, O_WRONLY, 0);
     221              : 
     222              :         /*
     223              :          * The closes might look redundant, but they are not: we want to be
     224              :          * darn sure the pipe gets closed even if the open failed.  We can
     225              :          * survive running with stderr pointing nowhere, but we can't afford
     226              :          * to have extra pipe input descriptors hanging around.
     227              :          *
     228              :          * As we're just trying to reset these to go to DEVNULL, there's not
     229              :          * much point in checking for failure from the close/dup2 calls here,
     230              :          * if they fail then presumably the file descriptors are closed and
     231              :          * any writes will go into the bitbucket anyway.
     232              :          */
     233            0 :         close(STDOUT_FILENO);
     234            0 :         close(STDERR_FILENO);
     235            0 :         if (fd != -1)
     236              :         {
     237            0 :             (void) dup2(fd, STDOUT_FILENO);
     238            0 :             (void) dup2(fd, STDERR_FILENO);
     239            0 :             close(fd);
     240              :         }
     241              :     }
     242              : 
     243              :     /*
     244              :      * Syslogger's own stderr can't be the syslogPipe, so set it back to text
     245              :      * mode if we didn't just close it. (It was set to binary in
     246              :      * SubPostmasterMain).
     247              :      */
     248              : #ifdef WIN32
     249              :     else
     250              :         _setmode(STDERR_FILENO, _O_TEXT);
     251              : #endif
     252              : 
     253              :     /*
     254              :      * Also close our copy of the write end of the pipe.  This is needed to
     255              :      * ensure we can detect pipe EOF correctly.  (But note that in the restart
     256              :      * case, the postmaster already did this.)
     257              :      */
     258              : #ifndef WIN32
     259            1 :     if (syslogPipe[1] >= 0)
     260            1 :         close(syslogPipe[1]);
     261            1 :     syslogPipe[1] = -1;
     262              : #else
     263              :     if (syslogPipe[1])
     264              :         CloseHandle(syslogPipe[1]);
     265              :     syslogPipe[1] = 0;
     266              : #endif
     267              : 
     268              :     /*
     269              :      * Properly accept or ignore signals the postmaster might send us
     270              :      *
     271              :      * Note: we ignore all termination signals, and instead exit only when all
     272              :      * upstream processes are gone, to ensure we don't miss any dying gasps of
     273              :      * broken backends...
     274              :      */
     275              : 
     276            1 :     pqsignal(SIGHUP, SignalHandlerForConfigReload); /* set flag to read config
     277              :                                                      * file */
     278            1 :     pqsignal(SIGINT, SIG_IGN);
     279            1 :     pqsignal(SIGTERM, SIG_IGN);
     280            1 :     pqsignal(SIGQUIT, SIG_IGN);
     281            1 :     pqsignal(SIGALRM, SIG_IGN);
     282            1 :     pqsignal(SIGPIPE, SIG_IGN);
     283            1 :     pqsignal(SIGUSR1, sigUsr1Handler);  /* request log rotation */
     284            1 :     pqsignal(SIGUSR2, SIG_IGN);
     285              : 
     286              :     /*
     287              :      * Reset some signals that are accepted by postmaster but not here
     288              :      */
     289            1 :     pqsignal(SIGCHLD, SIG_DFL);
     290              : 
     291            1 :     sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
     292              : 
     293              : #ifdef WIN32
     294              :     /* Fire up separate data transfer thread */
     295              :     InitializeCriticalSection(&sysloggerSection);
     296              :     EnterCriticalSection(&sysloggerSection);
     297              : 
     298              :     threadHandle = (HANDLE) _beginthreadex(NULL, 0, pipeThread, NULL, 0, NULL);
     299              :     if (threadHandle == 0)
     300              :         elog(FATAL, "could not create syslogger data transfer thread: %m");
     301              : #endif                          /* WIN32 */
     302              : 
     303              :     /*
     304              :      * Remember active logfiles' name(s).  We recompute 'em from the reference
     305              :      * time because passing down just the pg_time_t is a lot cheaper than
     306              :      * passing a whole file path in the EXEC_BACKEND case.
     307              :      */
     308            1 :     last_sys_file_name = logfile_getname(first_syslogger_file_time, NULL);
     309            1 :     if (csvlogFile != NULL)
     310            1 :         last_csv_file_name = logfile_getname(first_syslogger_file_time, ".csv");
     311            1 :     if (jsonlogFile != NULL)
     312            1 :         last_json_file_name = logfile_getname(first_syslogger_file_time, ".json");
     313              : 
     314              :     /* remember active logfile parameters */
     315            1 :     currentLogDir = pstrdup(Log_directory);
     316            1 :     currentLogFilename = pstrdup(Log_filename);
     317            1 :     currentLogRotationAge = Log_RotationAge;
     318              :     /* set next planned rotation time */
     319            1 :     set_next_rotation_time();
     320            1 :     update_metainfo_datafile();
     321              : 
     322              :     /*
     323              :      * Reset whereToSendOutput, as the postmaster will do (but hasn't yet, at
     324              :      * the point where we forked).  This prevents duplicate output of messages
     325              :      * from syslogger itself.
     326              :      */
     327            1 :     whereToSendOutput = DestNone;
     328              : 
     329              :     /*
     330              :      * Set up a reusable WaitEventSet object we'll use to wait for our latch,
     331              :      * and (except on Windows) our socket.
     332              :      *
     333              :      * Unlike all other postmaster child processes, we'll ignore postmaster
     334              :      * death because we want to collect final log output from all backends and
     335              :      * then exit last.  We'll do that by running until we see EOF on the
     336              :      * syslog pipe, which implies that all other backends have exited
     337              :      * (including the postmaster).
     338              :      */
     339            1 :     wes = CreateWaitEventSet(NULL, 2);
     340            1 :     AddWaitEventToSet(wes, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL);
     341              : #ifndef WIN32
     342            1 :     AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], NULL, NULL);
     343              : #endif
     344              : 
     345              :     /* main worker loop */
     346              :     for (;;)
     347           20 :     {
     348           21 :         bool        time_based_rotation = false;
     349           21 :         int         size_rotation_for = 0;
     350              :         long        cur_timeout;
     351              :         WaitEvent   event;
     352              : 
     353              : #ifndef WIN32
     354              :         int         rc;
     355              : #endif
     356              : 
     357              :         /* Clear any already-pending wakeups */
     358           21 :         ResetLatch(MyLatch);
     359              : 
     360              :         /*
     361              :          * Process any requests or signals received recently.
     362              :          */
     363           21 :         if (ConfigReloadPending)
     364              :         {
     365            0 :             ConfigReloadPending = false;
     366            0 :             ProcessConfigFile(PGC_SIGHUP);
     367              : 
     368              :             /*
     369              :              * Check if the log directory or filename pattern changed in
     370              :              * postgresql.conf. If so, force rotation to make sure we're
     371              :              * writing the logfiles in the right place.
     372              :              */
     373            0 :             if (strcmp(Log_directory, currentLogDir) != 0)
     374              :             {
     375            0 :                 pfree(currentLogDir);
     376            0 :                 currentLogDir = pstrdup(Log_directory);
     377            0 :                 rotation_requested = true;
     378              : 
     379              :                 /*
     380              :                  * Also, create new directory if not present; ignore errors
     381              :                  */
     382            0 :                 (void) MakePGDirectory(Log_directory);
     383              :             }
     384            0 :             if (strcmp(Log_filename, currentLogFilename) != 0)
     385              :             {
     386            0 :                 pfree(currentLogFilename);
     387            0 :                 currentLogFilename = pstrdup(Log_filename);
     388            0 :                 rotation_requested = true;
     389              :             }
     390              : 
     391              :             /*
     392              :              * Force a rotation if CSVLOG output was just turned on or off and
     393              :              * we need to open or close csvlogFile accordingly.
     394              :              */
     395            0 :             if (((Log_destination & LOG_DESTINATION_CSVLOG) != 0) !=
     396              :                 (csvlogFile != NULL))
     397            0 :                 rotation_requested = true;
     398              : 
     399              :             /*
     400              :              * Force a rotation if JSONLOG output was just turned on or off
     401              :              * and we need to open or close jsonlogFile accordingly.
     402              :              */
     403            0 :             if (((Log_destination & LOG_DESTINATION_JSONLOG) != 0) !=
     404              :                 (jsonlogFile != NULL))
     405            0 :                 rotation_requested = true;
     406              : 
     407              :             /*
     408              :              * If rotation time parameter changed, reset next rotation time,
     409              :              * but don't immediately force a rotation.
     410              :              */
     411            0 :             if (currentLogRotationAge != Log_RotationAge)
     412              :             {
     413            0 :                 currentLogRotationAge = Log_RotationAge;
     414            0 :                 set_next_rotation_time();
     415              :             }
     416              : 
     417              :             /*
     418              :              * If we had a rotation-disabling failure, re-enable rotation
     419              :              * attempts after SIGHUP, and force one immediately.
     420              :              */
     421            0 :             if (rotation_disabled)
     422              :             {
     423            0 :                 rotation_disabled = false;
     424            0 :                 rotation_requested = true;
     425              :             }
     426              : 
     427              :             /*
     428              :              * Force rewriting last log filename when reloading configuration.
     429              :              * Even if rotation_requested is false, log_destination may have
     430              :              * been changed and we don't want to wait the next file rotation.
     431              :              */
     432            0 :             update_metainfo_datafile();
     433              :         }
     434              : 
     435           21 :         if (Log_RotationAge > 0 && !rotation_disabled)
     436              :         {
     437              :             /* Do a logfile rotation if it's time */
     438            0 :             now = (pg_time_t) time(NULL);
     439            0 :             if (now >= next_rotation_time)
     440            0 :                 rotation_requested = time_based_rotation = true;
     441              :         }
     442              : 
     443           21 :         if (!rotation_requested && Log_RotationSize > 0 && !rotation_disabled)
     444              :         {
     445              :             /* Do a rotation if file is too big */
     446           20 :             if (ftello(syslogFile) >= Log_RotationSize * (pgoff_t) 1024)
     447              :             {
     448            0 :                 rotation_requested = true;
     449            0 :                 size_rotation_for |= LOG_DESTINATION_STDERR;
     450              :             }
     451           20 :             if (csvlogFile != NULL &&
     452           20 :                 ftello(csvlogFile) >= Log_RotationSize * (pgoff_t) 1024)
     453              :             {
     454            0 :                 rotation_requested = true;
     455            0 :                 size_rotation_for |= LOG_DESTINATION_CSVLOG;
     456              :             }
     457           20 :             if (jsonlogFile != NULL &&
     458           20 :                 ftello(jsonlogFile) >= Log_RotationSize * (pgoff_t) 1024)
     459              :             {
     460            0 :                 rotation_requested = true;
     461            0 :                 size_rotation_for |= LOG_DESTINATION_JSONLOG;
     462              :             }
     463              :         }
     464              : 
     465           21 :         if (rotation_requested)
     466              :         {
     467              :             /*
     468              :              * Force rotation when both values are zero. It means the request
     469              :              * was sent by pg_rotate_logfile() or "pg_ctl logrotate".
     470              :              */
     471            1 :             if (!time_based_rotation && size_rotation_for == 0)
     472            1 :                 size_rotation_for = LOG_DESTINATION_STDERR |
     473              :                     LOG_DESTINATION_CSVLOG |
     474              :                     LOG_DESTINATION_JSONLOG;
     475            1 :             logfile_rotate(time_based_rotation, size_rotation_for);
     476              :         }
     477              : 
     478              :         /*
     479              :          * Calculate time till next time-based rotation, so that we don't
     480              :          * sleep longer than that.  We assume the value of "now" obtained
     481              :          * above is still close enough.  Note we can't make this calculation
     482              :          * until after calling logfile_rotate(), since it will advance
     483              :          * next_rotation_time.
     484              :          *
     485              :          * Also note that we need to beware of overflow in calculation of the
     486              :          * timeout: with large settings of Log_RotationAge, next_rotation_time
     487              :          * could be more than INT_MAX msec in the future.  In that case we'll
     488              :          * wait no more than INT_MAX msec, and try again.
     489              :          */
     490           21 :         if (Log_RotationAge > 0 && !rotation_disabled)
     491            0 :         {
     492              :             pg_time_t   delay;
     493              : 
     494            0 :             delay = next_rotation_time - now;
     495            0 :             if (delay > 0)
     496              :             {
     497            0 :                 if (delay > INT_MAX / 1000)
     498            0 :                     delay = INT_MAX / 1000;
     499            0 :                 cur_timeout = delay * 1000L;    /* msec */
     500              :             }
     501              :             else
     502            0 :                 cur_timeout = 0;
     503              :         }
     504              :         else
     505           21 :             cur_timeout = -1L;
     506              : 
     507              :         /*
     508              :          * Sleep until there's something to do
     509              :          */
     510              : #ifndef WIN32
     511           21 :         rc = WaitEventSetWait(wes, cur_timeout, &event, 1,
     512              :                               WAIT_EVENT_SYSLOGGER_MAIN);
     513              : 
     514           21 :         if (rc == 1 && event.events == WL_SOCKET_READABLE)
     515              :         {
     516              :             int         bytesRead;
     517              : 
     518           20 :             bytesRead = read(syslogPipe[0],
     519              :                              logbuffer + bytes_in_logbuffer,
     520              :                              sizeof(logbuffer) - bytes_in_logbuffer);
     521           20 :             if (bytesRead < 0)
     522              :             {
     523            0 :                 if (errno != EINTR)
     524            0 :                     ereport(LOG,
     525              :                             (errcode_for_socket_access(),
     526              :                              errmsg("could not read from logger pipe: %m")));
     527              :             }
     528           20 :             else if (bytesRead > 0)
     529              :             {
     530           19 :                 bytes_in_logbuffer += bytesRead;
     531           19 :                 process_pipe_input(logbuffer, &bytes_in_logbuffer);
     532           19 :                 continue;
     533              :             }
     534              :             else
     535              :             {
     536              :                 /*
     537              :                  * Zero bytes read when select() is saying read-ready means
     538              :                  * EOF on the pipe: that is, there are no longer any processes
     539              :                  * with the pipe write end open.  Therefore, the postmaster
     540              :                  * and all backends are shut down, and we are done.
     541              :                  */
     542            1 :                 pipe_eof_seen = true;
     543              : 
     544              :                 /* if there's any data left then force it out now */
     545            1 :                 flush_pipe_input(logbuffer, &bytes_in_logbuffer);
     546              :             }
     547              :         }
     548              : #else                           /* WIN32 */
     549              : 
     550              :         /*
     551              :          * On Windows we leave it to a separate thread to transfer data and
     552              :          * detect pipe EOF.  The main thread just wakes up to handle SIGHUP
     553              :          * and rotation conditions.
     554              :          *
     555              :          * Server code isn't generally thread-safe, so we ensure that only one
     556              :          * of the threads is active at a time by entering the critical section
     557              :          * whenever we're not sleeping.
     558              :          */
     559              :         LeaveCriticalSection(&sysloggerSection);
     560              : 
     561              :         (void) WaitEventSetWait(wes, cur_timeout, &event, 1,
     562              :                                 WAIT_EVENT_SYSLOGGER_MAIN);
     563              : 
     564              :         EnterCriticalSection(&sysloggerSection);
     565              : #endif                          /* WIN32 */
     566              : 
     567            2 :         if (pipe_eof_seen)
     568              :         {
     569              :             /*
     570              :              * seeing this message on the real stderr is annoying - so we make
     571              :              * it DEBUG1 to suppress in normal use.
     572              :              */
     573            1 :             ereport(DEBUG1,
     574              :                     (errmsg_internal("logger shutting down")));
     575              : 
     576              :             /*
     577              :              * Normal exit from the syslogger is here.  Note that we
     578              :              * deliberately do not close syslogFile before exiting; this is to
     579              :              * allow for the possibility of elog messages being generated
     580              :              * inside proc_exit.  Regular exit() will take care of flushing
     581              :              * and closing stdio channels.
     582              :              */
     583            1 :             proc_exit(0);
     584              :         }
     585              :     }
     586              : }
     587              : 
     588              : /*
     589              :  * Postmaster subroutine to start a syslogger subprocess.
     590              :  */
     591              : int
     592            1 : SysLogger_Start(int child_slot)
     593              : {
     594              :     pid_t       sysloggerPid;
     595              :     char       *filename;
     596              : #ifdef EXEC_BACKEND
     597              :     SysloggerStartupData startup_data;
     598              : #endif                          /* EXEC_BACKEND */
     599              : 
     600              :     Assert(Logging_collector);
     601              : 
     602              :     /*
     603              :      * If first time through, create the pipe which will receive stderr
     604              :      * output.
     605              :      *
     606              :      * If the syslogger crashes and needs to be restarted, we continue to use
     607              :      * the same pipe (indeed must do so, since extant backends will be writing
     608              :      * into that pipe).
     609              :      *
     610              :      * This means the postmaster must continue to hold the read end of the
     611              :      * pipe open, so we can pass it down to the reincarnated syslogger. This
     612              :      * is a bit klugy but we have little choice.
     613              :      *
     614              :      * Also note that we don't bother counting the pipe FDs by calling
     615              :      * Reserve/ReleaseExternalFD.  There's no real need to account for them
     616              :      * accurately in the postmaster or syslogger process, and both ends of the
     617              :      * pipe will wind up closed in all other postmaster children.
     618              :      */
     619              : #ifndef WIN32
     620            1 :     if (syslogPipe[0] < 0)
     621              :     {
     622            1 :         if (pipe(syslogPipe) < 0)
     623            0 :             ereport(FATAL,
     624              :                     (errcode_for_socket_access(),
     625              :                      errmsg("could not create pipe for syslog: %m")));
     626              :     }
     627              : #else
     628              :     if (!syslogPipe[0])
     629              :     {
     630              :         SECURITY_ATTRIBUTES sa;
     631              : 
     632              :         memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
     633              :         sa.nLength = sizeof(SECURITY_ATTRIBUTES);
     634              :         sa.bInheritHandle = TRUE;
     635              : 
     636              :         if (!CreatePipe(&syslogPipe[0], &syslogPipe[1], &sa, 32768))
     637              :             ereport(FATAL,
     638              :                     (errcode_for_file_access(),
     639              :                      errmsg("could not create pipe for syslog: %m")));
     640              :     }
     641              : #endif
     642              : 
     643              :     /*
     644              :      * Create log directory if not present; ignore errors
     645              :      */
     646            1 :     (void) MakePGDirectory(Log_directory);
     647              : 
     648              :     /*
     649              :      * The initial logfile is created right in the postmaster, to verify that
     650              :      * the Log_directory is writable.  We save the reference time so that the
     651              :      * syslogger child process can recompute this file name.
     652              :      *
     653              :      * It might look a bit strange to re-do this during a syslogger restart,
     654              :      * but we must do so since the postmaster closed syslogFile after the
     655              :      * previous fork (and remembering that old file wouldn't be right anyway).
     656              :      * Note we always append here, we won't overwrite any existing file.  This
     657              :      * is consistent with the normal rules, because by definition this is not
     658              :      * a time-based rotation.
     659              :      */
     660            1 :     first_syslogger_file_time = time(NULL);
     661              : 
     662            1 :     filename = logfile_getname(first_syslogger_file_time, NULL);
     663              : 
     664            1 :     syslogFile = logfile_open(filename, "a", false);
     665              : 
     666            1 :     pfree(filename);
     667              : 
     668              :     /*
     669              :      * Likewise for the initial CSV log file, if that's enabled.  (Note that
     670              :      * we open syslogFile even when only CSV output is nominally enabled,
     671              :      * since some code paths will write to syslogFile anyway.)
     672              :      */
     673            1 :     if (Log_destination & LOG_DESTINATION_CSVLOG)
     674              :     {
     675            1 :         filename = logfile_getname(first_syslogger_file_time, ".csv");
     676              : 
     677            1 :         csvlogFile = logfile_open(filename, "a", false);
     678              : 
     679            1 :         pfree(filename);
     680              :     }
     681              : 
     682              :     /*
     683              :      * Likewise for the initial JSON log file, if that's enabled.  (Note that
     684              :      * we open syslogFile even when only JSON output is nominally enabled,
     685              :      * since some code paths will write to syslogFile anyway.)
     686              :      */
     687            1 :     if (Log_destination & LOG_DESTINATION_JSONLOG)
     688              :     {
     689            1 :         filename = logfile_getname(first_syslogger_file_time, ".json");
     690              : 
     691            1 :         jsonlogFile = logfile_open(filename, "a", false);
     692              : 
     693            1 :         pfree(filename);
     694              :     }
     695              : 
     696              : #ifdef EXEC_BACKEND
     697              :     startup_data.syslogFile = syslogger_fdget(syslogFile);
     698              :     startup_data.csvlogFile = syslogger_fdget(csvlogFile);
     699              :     startup_data.jsonlogFile = syslogger_fdget(jsonlogFile);
     700              :     sysloggerPid = postmaster_child_launch(B_LOGGER, child_slot,
     701              :                                            &startup_data, sizeof(startup_data), NULL);
     702              : #else
     703            1 :     sysloggerPid = postmaster_child_launch(B_LOGGER, child_slot,
     704              :                                            NULL, 0, NULL);
     705              : #endif                          /* EXEC_BACKEND */
     706              : 
     707            1 :     if (sysloggerPid == -1)
     708              :     {
     709            0 :         ereport(LOG,
     710              :                 (errmsg("could not fork system logger: %m")));
     711            0 :         return 0;
     712              :     }
     713              : 
     714              :     /* success, in postmaster */
     715              : 
     716              :     /* now we redirect stderr, if not done already */
     717            1 :     if (!redirection_done)
     718              :     {
     719              : #ifdef WIN32
     720              :         int         fd;
     721              : #endif
     722              : 
     723              :         /*
     724              :          * Leave a breadcrumb trail when redirecting, in case the user forgets
     725              :          * that redirection is active and looks only at the original stderr
     726              :          * target file.
     727              :          */
     728            1 :         ereport(LOG,
     729              :                 (errmsg("redirecting log output to logging collector process"),
     730              :                  errhint("Future log output will appear in directory \"%s\".",
     731              :                          Log_directory)));
     732              : 
     733              : #ifndef WIN32
     734            1 :         fflush(stdout);
     735            1 :         if (dup2(syslogPipe[1], STDOUT_FILENO) < 0)
     736            0 :             ereport(FATAL,
     737              :                     (errcode_for_file_access(),
     738              :                      errmsg("could not redirect stdout: %m")));
     739            1 :         fflush(stderr);
     740            1 :         if (dup2(syslogPipe[1], STDERR_FILENO) < 0)
     741            0 :             ereport(FATAL,
     742              :                     (errcode_for_file_access(),
     743              :                      errmsg("could not redirect stderr: %m")));
     744              :         /* Now we are done with the write end of the pipe. */
     745            1 :         close(syslogPipe[1]);
     746            1 :         syslogPipe[1] = -1;
     747              : #else
     748              : 
     749              :         /*
     750              :          * open the pipe in binary mode and make sure stderr is binary after
     751              :          * it's been dup'ed into, to avoid disturbing the pipe chunking
     752              :          * protocol.
     753              :          */
     754              :         fflush(stderr);
     755              :         fd = _open_osfhandle((intptr_t) syslogPipe[1],
     756              :                              _O_APPEND | _O_BINARY);
     757              :         if (dup2(fd, STDERR_FILENO) < 0)
     758              :             ereport(FATAL,
     759              :                     (errcode_for_file_access(),
     760              :                      errmsg("could not redirect stderr: %m")));
     761              :         close(fd);
     762              :         _setmode(STDERR_FILENO, _O_BINARY);
     763              : 
     764              :         /*
     765              :          * Now we are done with the write end of the pipe.  CloseHandle() must
     766              :          * not be called because the preceding close() closes the underlying
     767              :          * handle.
     768              :          */
     769              :         syslogPipe[1] = 0;
     770              : #endif
     771            1 :         redirection_done = true;
     772              :     }
     773              : 
     774              :     /* postmaster will never write the file(s); close 'em */
     775            1 :     fclose(syslogFile);
     776            1 :     syslogFile = NULL;
     777            1 :     if (csvlogFile != NULL)
     778              :     {
     779            1 :         fclose(csvlogFile);
     780            1 :         csvlogFile = NULL;
     781              :     }
     782            1 :     if (jsonlogFile != NULL)
     783              :     {
     784            1 :         fclose(jsonlogFile);
     785            1 :         jsonlogFile = NULL;
     786              :     }
     787            1 :     return (int) sysloggerPid;
     788              : }
     789              : 
     790              : 
     791              : #ifdef EXEC_BACKEND
     792              : 
     793              : /*
     794              :  * syslogger_fdget() -
     795              :  *
     796              :  * Utility wrapper to grab the file descriptor of an opened error output
     797              :  * file.  Used when building the command to fork the logging collector.
     798              :  */
     799              : static int
     800              : syslogger_fdget(FILE *file)
     801              : {
     802              : #ifndef WIN32
     803              :     if (file != NULL)
     804              :         return fileno(file);
     805              :     else
     806              :         return -1;
     807              : #else
     808              :     if (file != NULL)
     809              :         return (int) _get_osfhandle(_fileno(file));
     810              :     else
     811              :         return 0;
     812              : #endif                          /* WIN32 */
     813              : }
     814              : 
     815              : /*
     816              :  * syslogger_fdopen() -
     817              :  *
     818              :  * Utility wrapper to re-open an error output file, using the given file
     819              :  * descriptor.  Used when parsing arguments in a forked logging collector.
     820              :  */
     821              : static FILE *
     822              : syslogger_fdopen(int fd)
     823              : {
     824              :     FILE       *file = NULL;
     825              : 
     826              : #ifndef WIN32
     827              :     if (fd != -1)
     828              :     {
     829              :         file = fdopen(fd, "a");
     830              :         setvbuf(file, NULL, PG_IOLBF, 0);
     831              :     }
     832              : #else                           /* WIN32 */
     833              :     if (fd != 0)
     834              :     {
     835              :         fd = _open_osfhandle(fd, _O_APPEND | _O_TEXT);
     836              :         if (fd > 0)
     837              :         {
     838              :             file = fdopen(fd, "a");
     839              :             setvbuf(file, NULL, PG_IOLBF, 0);
     840              :         }
     841              :     }
     842              : #endif                          /* WIN32 */
     843              : 
     844              :     return file;
     845              : }
     846              : #endif                          /* EXEC_BACKEND */
     847              : 
     848              : 
     849              : /* --------------------------------
     850              :  *      pipe protocol handling
     851              :  * --------------------------------
     852              :  */
     853              : 
     854              : /*
     855              :  * Process data received through the syslogger pipe.
     856              :  *
     857              :  * This routine interprets the log pipe protocol which sends log messages as
     858              :  * (hopefully atomic) chunks - such chunks are detected and reassembled here.
     859              :  *
     860              :  * The protocol has a header that starts with two nul bytes, then has a 16 bit
     861              :  * length, the pid of the sending process, and a flag to indicate if it is
     862              :  * the last chunk in a message. Incomplete chunks are saved until we read some
     863              :  * more, and non-final chunks are accumulated until we get the final chunk.
     864              :  *
     865              :  * All of this is to avoid 2 problems:
     866              :  * . partial messages being written to logfiles (messes rotation), and
     867              :  * . messages from different backends being interleaved (messages garbled).
     868              :  *
     869              :  * Any non-protocol messages are written out directly. These should only come
     870              :  * from non-PostgreSQL sources, however (e.g. third party libraries writing to
     871              :  * stderr).
     872              :  *
     873              :  * logbuffer is the data input buffer, and *bytes_in_logbuffer is the number
     874              :  * of bytes present.  On exit, any not-yet-eaten data is left-justified in
     875              :  * logbuffer, and *bytes_in_logbuffer is updated.
     876              :  */
     877              : static void
     878           19 : process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
     879              : {
     880           19 :     char       *cursor = logbuffer;
     881           19 :     int         count = *bytes_in_logbuffer;
     882           19 :     int         dest = LOG_DESTINATION_STDERR;
     883              : 
     884              :     /* While we have enough for a header, process data... */
     885           79 :     while (count >= (int) (offsetof(PipeProtoHeader, data) + 1))
     886              :     {
     887              :         PipeProtoHeader p;
     888              :         int         chunklen;
     889              :         bits8       dest_flags;
     890              : 
     891              :         /* Do we have a valid header? */
     892           60 :         memcpy(&p, cursor, offsetof(PipeProtoHeader, data));
     893           60 :         dest_flags = p.flags & (PIPE_PROTO_DEST_STDERR |
     894              :                                 PIPE_PROTO_DEST_CSVLOG |
     895              :                                 PIPE_PROTO_DEST_JSONLOG);
     896           60 :         if (p.nuls[0] == '\0' && p.nuls[1] == '\0' &&
     897           60 :             p.len > 0 && p.len <= PIPE_MAX_PAYLOAD &&
     898           60 :             p.pid != 0 &&
     899           60 :             pg_number_of_ones[dest_flags] == 1)
     900           60 :         {
     901              :             List       *buffer_list;
     902              :             ListCell   *cell;
     903           60 :             save_buffer *existing_slot = NULL,
     904           60 :                        *free_slot = NULL;
     905              :             StringInfo  str;
     906              : 
     907           60 :             chunklen = PIPE_HEADER_SIZE + p.len;
     908              : 
     909              :             /* Fall out of loop if we don't have the whole chunk yet */
     910           60 :             if (count < chunklen)
     911            0 :                 break;
     912              : 
     913           60 :             if ((p.flags & PIPE_PROTO_DEST_STDERR) != 0)
     914           20 :                 dest = LOG_DESTINATION_STDERR;
     915           40 :             else if ((p.flags & PIPE_PROTO_DEST_CSVLOG) != 0)
     916           20 :                 dest = LOG_DESTINATION_CSVLOG;
     917           20 :             else if ((p.flags & PIPE_PROTO_DEST_JSONLOG) != 0)
     918           20 :                 dest = LOG_DESTINATION_JSONLOG;
     919              :             else
     920              :             {
     921              :                 /* this should never happen as of the header validation */
     922              :                 Assert(false);
     923              :             }
     924              : 
     925              :             /* Locate any existing buffer for this source pid */
     926           60 :             buffer_list = buffer_lists[p.pid % NBUFFER_LISTS];
     927           60 :             foreach(cell, buffer_list)
     928              :             {
     929            0 :                 save_buffer *buf = (save_buffer *) lfirst(cell);
     930              : 
     931            0 :                 if (buf->pid == p.pid)
     932              :                 {
     933            0 :                     existing_slot = buf;
     934            0 :                     break;
     935              :                 }
     936            0 :                 if (buf->pid == 0 && free_slot == NULL)
     937            0 :                     free_slot = buf;
     938              :             }
     939              : 
     940           60 :             if ((p.flags & PIPE_PROTO_IS_LAST) == 0)
     941              :             {
     942              :                 /*
     943              :                  * Save a complete non-final chunk in a per-pid buffer
     944              :                  */
     945            0 :                 if (existing_slot != NULL)
     946              :                 {
     947              :                     /* Add chunk to data from preceding chunks */
     948            0 :                     str = &(existing_slot->data);
     949            0 :                     appendBinaryStringInfo(str,
     950            0 :                                            cursor + PIPE_HEADER_SIZE,
     951            0 :                                            p.len);
     952              :                 }
     953              :                 else
     954              :                 {
     955              :                     /* First chunk of message, save in a new buffer */
     956            0 :                     if (free_slot == NULL)
     957              :                     {
     958              :                         /*
     959              :                          * Need a free slot, but there isn't one in the list,
     960              :                          * so create a new one and extend the list with it.
     961              :                          */
     962            0 :                         free_slot = palloc_object(save_buffer);
     963            0 :                         buffer_list = lappend(buffer_list, free_slot);
     964            0 :                         buffer_lists[p.pid % NBUFFER_LISTS] = buffer_list;
     965              :                     }
     966            0 :                     free_slot->pid = p.pid;
     967            0 :                     str = &(free_slot->data);
     968            0 :                     initStringInfo(str);
     969            0 :                     appendBinaryStringInfo(str,
     970            0 :                                            cursor + PIPE_HEADER_SIZE,
     971            0 :                                            p.len);
     972              :                 }
     973              :             }
     974              :             else
     975              :             {
     976              :                 /*
     977              :                  * Final chunk --- add it to anything saved for that pid, and
     978              :                  * either way write the whole thing out.
     979              :                  */
     980           60 :                 if (existing_slot != NULL)
     981              :                 {
     982            0 :                     str = &(existing_slot->data);
     983            0 :                     appendBinaryStringInfo(str,
     984            0 :                                            cursor + PIPE_HEADER_SIZE,
     985            0 :                                            p.len);
     986            0 :                     write_syslogger_file(str->data, str->len, dest);
     987              :                     /* Mark the buffer unused, and reclaim string storage */
     988            0 :                     existing_slot->pid = 0;
     989            0 :                     pfree(str->data);
     990              :                 }
     991              :                 else
     992              :                 {
     993              :                     /* The whole message was one chunk, evidently. */
     994           60 :                     write_syslogger_file(cursor + PIPE_HEADER_SIZE, p.len,
     995              :                                          dest);
     996              :                 }
     997              :             }
     998              : 
     999              :             /* Finished processing this chunk */
    1000           60 :             cursor += chunklen;
    1001           60 :             count -= chunklen;
    1002              :         }
    1003              :         else
    1004              :         {
    1005              :             /* Process non-protocol data */
    1006              : 
    1007              :             /*
    1008              :              * Look for the start of a protocol header.  If found, dump data
    1009              :              * up to there and repeat the loop.  Otherwise, dump it all and
    1010              :              * fall out of the loop.  (Note: we want to dump it all if at all
    1011              :              * possible, so as to avoid dividing non-protocol messages across
    1012              :              * logfiles.  We expect that in many scenarios, a non-protocol
    1013              :              * message will arrive all in one read(), and we want to respect
    1014              :              * the read() boundary if possible.)
    1015              :              */
    1016            0 :             for (chunklen = 1; chunklen < count; chunklen++)
    1017              :             {
    1018            0 :                 if (cursor[chunklen] == '\0')
    1019            0 :                     break;
    1020              :             }
    1021              :             /* fall back on the stderr log as the destination */
    1022            0 :             write_syslogger_file(cursor, chunklen, LOG_DESTINATION_STDERR);
    1023            0 :             cursor += chunklen;
    1024            0 :             count -= chunklen;
    1025              :         }
    1026              :     }
    1027              : 
    1028              :     /* We don't have a full chunk, so left-align what remains in the buffer */
    1029           19 :     if (count > 0 && cursor != logbuffer)
    1030            0 :         memmove(logbuffer, cursor, count);
    1031           19 :     *bytes_in_logbuffer = count;
    1032           19 : }
    1033              : 
    1034              : /*
    1035              :  * Force out any buffered data
    1036              :  *
    1037              :  * This is currently used only at syslogger shutdown, but could perhaps be
    1038              :  * useful at other times, so it is careful to leave things in a clean state.
    1039              :  */
    1040              : static void
    1041            1 : flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
    1042              : {
    1043              :     int         i;
    1044              : 
    1045              :     /* Dump any incomplete protocol messages */
    1046          257 :     for (i = 0; i < NBUFFER_LISTS; i++)
    1047              :     {
    1048          256 :         List       *list = buffer_lists[i];
    1049              :         ListCell   *cell;
    1050              : 
    1051          256 :         foreach(cell, list)
    1052              :         {
    1053            0 :             save_buffer *buf = (save_buffer *) lfirst(cell);
    1054              : 
    1055            0 :             if (buf->pid != 0)
    1056              :             {
    1057            0 :                 StringInfo  str = &(buf->data);
    1058              : 
    1059            0 :                 write_syslogger_file(str->data, str->len,
    1060              :                                      LOG_DESTINATION_STDERR);
    1061              :                 /* Mark the buffer unused, and reclaim string storage */
    1062            0 :                 buf->pid = 0;
    1063            0 :                 pfree(str->data);
    1064              :             }
    1065              :         }
    1066              :     }
    1067              : 
    1068              :     /*
    1069              :      * Force out any remaining pipe data as-is; we don't bother trying to
    1070              :      * remove any protocol headers that may exist in it.
    1071              :      */
    1072            1 :     if (*bytes_in_logbuffer > 0)
    1073            0 :         write_syslogger_file(logbuffer, *bytes_in_logbuffer,
    1074              :                              LOG_DESTINATION_STDERR);
    1075            1 :     *bytes_in_logbuffer = 0;
    1076            1 : }
    1077              : 
    1078              : 
    1079              : /* --------------------------------
    1080              :  *      logfile routines
    1081              :  * --------------------------------
    1082              :  */
    1083              : 
    1084              : /*
    1085              :  * Write text to the currently open logfile
    1086              :  *
    1087              :  * This is exported so that elog.c can call it when MyBackendType is B_LOGGER.
    1088              :  * This allows the syslogger process to record elog messages of its own,
    1089              :  * even though its stderr does not point at the syslog pipe.
    1090              :  */
    1091              : void
    1092           60 : write_syslogger_file(const char *buffer, int count, int destination)
    1093              : {
    1094              :     int         rc;
    1095              :     FILE       *logfile;
    1096              : 
    1097              :     /*
    1098              :      * If we're told to write to a structured log file, but it's not open,
    1099              :      * dump the data to syslogFile (which is always open) instead.  This can
    1100              :      * happen if structured output is enabled after postmaster start and we've
    1101              :      * been unable to open logFile.  There are also race conditions during a
    1102              :      * parameter change whereby backends might send us structured output
    1103              :      * before we open the logFile or after we close it.  Writing formatted
    1104              :      * output to the regular log file isn't great, but it beats dropping log
    1105              :      * output on the floor.
    1106              :      *
    1107              :      * Think not to improve this by trying to open logFile on-the-fly.  Any
    1108              :      * failure in that would lead to recursion.
    1109              :      */
    1110           60 :     if ((destination & LOG_DESTINATION_CSVLOG) && csvlogFile != NULL)
    1111           20 :         logfile = csvlogFile;
    1112           40 :     else if ((destination & LOG_DESTINATION_JSONLOG) && jsonlogFile != NULL)
    1113           20 :         logfile = jsonlogFile;
    1114              :     else
    1115           20 :         logfile = syslogFile;
    1116              : 
    1117           60 :     rc = fwrite(buffer, 1, count, logfile);
    1118              : 
    1119              :     /*
    1120              :      * Try to report any failure.  We mustn't use ereport because it would
    1121              :      * just recurse right back here, but write_stderr is OK: it will write
    1122              :      * either to the postmaster's original stderr, or to /dev/null, but never
    1123              :      * to our input pipe which would result in a different sort of looping.
    1124              :      */
    1125           60 :     if (rc != count)
    1126            0 :         write_stderr("could not write to log file: %m\n");
    1127           60 : }
    1128              : 
    1129              : #ifdef WIN32
    1130              : 
    1131              : /*
    1132              :  * Worker thread to transfer data from the pipe to the current logfile.
    1133              :  *
    1134              :  * We need this because on Windows, WaitForMultipleObjects does not work on
    1135              :  * unnamed pipes: it always reports "signaled", so the blocking ReadFile won't
    1136              :  * allow for SIGHUP; and select is for sockets only.
    1137              :  */
    1138              : static unsigned int __stdcall
    1139              : pipeThread(void *arg)
    1140              : {
    1141              :     char        logbuffer[READ_BUF_SIZE];
    1142              :     int         bytes_in_logbuffer = 0;
    1143              : 
    1144              :     for (;;)
    1145              :     {
    1146              :         DWORD       bytesRead;
    1147              :         BOOL        result;
    1148              : 
    1149              :         result = ReadFile(syslogPipe[0],
    1150              :                           logbuffer + bytes_in_logbuffer,
    1151              :                           sizeof(logbuffer) - bytes_in_logbuffer,
    1152              :                           &bytesRead, 0);
    1153              : 
    1154              :         /*
    1155              :          * Enter critical section before doing anything that might touch
    1156              :          * global state shared by the main thread. Anything that uses
    1157              :          * palloc()/pfree() in particular are not safe outside the critical
    1158              :          * section.
    1159              :          */
    1160              :         EnterCriticalSection(&sysloggerSection);
    1161              :         if (!result)
    1162              :         {
    1163              :             DWORD       error = GetLastError();
    1164              : 
    1165              :             if (error == ERROR_HANDLE_EOF ||
    1166              :                 error == ERROR_BROKEN_PIPE)
    1167              :                 break;
    1168              :             _dosmaperr(error);
    1169              :             ereport(LOG,
    1170              :                     (errcode_for_file_access(),
    1171              :                      errmsg("could not read from logger pipe: %m")));
    1172              :         }
    1173              :         else if (bytesRead > 0)
    1174              :         {
    1175              :             bytes_in_logbuffer += bytesRead;
    1176              :             process_pipe_input(logbuffer, &bytes_in_logbuffer);
    1177              :         }
    1178              : 
    1179              :         /*
    1180              :          * If we've filled the current logfile, nudge the main thread to do a
    1181              :          * log rotation.
    1182              :          */
    1183              :         if (Log_RotationSize > 0)
    1184              :         {
    1185              :             if (ftello(syslogFile) >= Log_RotationSize * (pgoff_t) 1024 ||
    1186              :                 (csvlogFile != NULL &&
    1187              :                  ftello(csvlogFile) >= Log_RotationSize * (pgoff_t) 1024) ||
    1188              :                 (jsonlogFile != NULL &&
    1189              :                  ftello(jsonlogFile) >= Log_RotationSize * (pgoff_t) 1024))
    1190              :                 SetLatch(MyLatch);
    1191              :         }
    1192              :         LeaveCriticalSection(&sysloggerSection);
    1193              :     }
    1194              : 
    1195              :     /* We exit the above loop only upon detecting pipe EOF */
    1196              :     pipe_eof_seen = true;
    1197              : 
    1198              :     /* if there's any data left then force it out now */
    1199              :     flush_pipe_input(logbuffer, &bytes_in_logbuffer);
    1200              : 
    1201              :     /* set the latch to waken the main thread, which will quit */
    1202              :     SetLatch(MyLatch);
    1203              : 
    1204              :     LeaveCriticalSection(&sysloggerSection);
    1205              :     _endthread();
    1206              :     return 0;
    1207              : }
    1208              : #endif                          /* WIN32 */
    1209              : 
    1210              : /*
    1211              :  * Open a new logfile with proper permissions and buffering options.
    1212              :  *
    1213              :  * If allow_errors is true, we just log any open failure and return NULL
    1214              :  * (with errno still correct for the fopen failure).
    1215              :  * Otherwise, errors are treated as fatal.
    1216              :  */
    1217              : static FILE *
    1218            6 : logfile_open(const char *filename, const char *mode, bool allow_errors)
    1219              : {
    1220              :     FILE       *fh;
    1221              :     mode_t      oumask;
    1222              : 
    1223              :     /*
    1224              :      * Note we do not let Log_file_mode disable IWUSR, since we certainly want
    1225              :      * to be able to write the files ourselves.
    1226              :      */
    1227            6 :     oumask = umask((mode_t) ((~(Log_file_mode | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
    1228            6 :     fh = fopen(filename, mode);
    1229            6 :     umask(oumask);
    1230              : 
    1231            6 :     if (fh)
    1232              :     {
    1233            6 :         setvbuf(fh, NULL, PG_IOLBF, 0);
    1234              : 
    1235              : #ifdef WIN32
    1236              :         /* use CRLF line endings on Windows */
    1237              :         _setmode(_fileno(fh), _O_TEXT);
    1238              : #endif
    1239              :     }
    1240              :     else
    1241              :     {
    1242            0 :         int         save_errno = errno;
    1243              : 
    1244            0 :         ereport(allow_errors ? LOG : FATAL,
    1245              :                 (errcode_for_file_access(),
    1246              :                  errmsg("could not open log file \"%s\": %m",
    1247              :                         filename)));
    1248            0 :         errno = save_errno;
    1249              :     }
    1250              : 
    1251            6 :     return fh;
    1252              : }
    1253              : 
    1254              : /*
    1255              :  * Do logfile rotation for a single destination, as specified by target_dest.
    1256              :  * The information stored in *last_file_name and *logFile is updated on a
    1257              :  * successful file rotation.
    1258              :  *
    1259              :  * Returns false if the rotation has been stopped, or true to move on to
    1260              :  * the processing of other formats.
    1261              :  */
    1262              : static bool
    1263            3 : logfile_rotate_dest(bool time_based_rotation, int size_rotation_for,
    1264              :                     pg_time_t fntime, int target_dest,
    1265              :                     char **last_file_name, FILE **logFile)
    1266              : {
    1267            3 :     char       *logFileExt = NULL;
    1268              :     char       *filename;
    1269              :     FILE       *fh;
    1270              : 
    1271              :     /*
    1272              :      * If the target destination was just turned off, close the previous file
    1273              :      * and unregister its data.  This cannot happen for stderr as syslogFile
    1274              :      * is assumed to be always opened even if stderr is disabled in
    1275              :      * log_destination.
    1276              :      */
    1277            3 :     if ((Log_destination & target_dest) == 0 &&
    1278              :         target_dest != LOG_DESTINATION_STDERR)
    1279              :     {
    1280            0 :         if (*logFile != NULL)
    1281            0 :             fclose(*logFile);
    1282            0 :         *logFile = NULL;
    1283            0 :         if (*last_file_name != NULL)
    1284            0 :             pfree(*last_file_name);
    1285            0 :         *last_file_name = NULL;
    1286            0 :         return true;
    1287              :     }
    1288              : 
    1289              :     /*
    1290              :      * Leave if it is not time for a rotation or if the target destination has
    1291              :      * no need to do a rotation based on the size of its file.
    1292              :      */
    1293            3 :     if (!time_based_rotation && (size_rotation_for & target_dest) == 0)
    1294            0 :         return true;
    1295              : 
    1296              :     /* file extension depends on the destination type */
    1297            3 :     if (target_dest == LOG_DESTINATION_STDERR)
    1298            1 :         logFileExt = NULL;
    1299            2 :     else if (target_dest == LOG_DESTINATION_CSVLOG)
    1300            1 :         logFileExt = ".csv";
    1301            1 :     else if (target_dest == LOG_DESTINATION_JSONLOG)
    1302            1 :         logFileExt = ".json";
    1303              :     else
    1304              :     {
    1305              :         /* cannot happen */
    1306              :         Assert(false);
    1307              :     }
    1308              : 
    1309              :     /* build the new file name */
    1310            3 :     filename = logfile_getname(fntime, logFileExt);
    1311              : 
    1312              :     /*
    1313              :      * Decide whether to overwrite or append.  We can overwrite if (a)
    1314              :      * Log_truncate_on_rotation is set, (b) the rotation was triggered by
    1315              :      * elapsed time and not something else, and (c) the computed file name is
    1316              :      * different from what we were previously logging into.
    1317              :      */
    1318            3 :     if (Log_truncate_on_rotation && time_based_rotation &&
    1319            0 :         *last_file_name != NULL &&
    1320            0 :         strcmp(filename, *last_file_name) != 0)
    1321            0 :         fh = logfile_open(filename, "w", true);
    1322              :     else
    1323            3 :         fh = logfile_open(filename, "a", true);
    1324              : 
    1325            3 :     if (!fh)
    1326              :     {
    1327              :         /*
    1328              :          * ENFILE/EMFILE are not too surprising on a busy system; just keep
    1329              :          * using the old file till we manage to get a new one.  Otherwise,
    1330              :          * assume something's wrong with Log_directory and stop trying to
    1331              :          * create files.
    1332              :          */
    1333            0 :         if (errno != ENFILE && errno != EMFILE)
    1334              :         {
    1335            0 :             ereport(LOG,
    1336              :                     (errmsg("disabling automatic rotation (use SIGHUP to re-enable)")));
    1337            0 :             rotation_disabled = true;
    1338              :         }
    1339              : 
    1340            0 :         if (filename)
    1341            0 :             pfree(filename);
    1342            0 :         return false;
    1343              :     }
    1344              : 
    1345              :     /* fill in the new information */
    1346            3 :     if (*logFile != NULL)
    1347            3 :         fclose(*logFile);
    1348            3 :     *logFile = fh;
    1349              : 
    1350              :     /* instead of pfree'ing filename, remember it for next time */
    1351            3 :     if (*last_file_name != NULL)
    1352            3 :         pfree(*last_file_name);
    1353            3 :     *last_file_name = filename;
    1354              : 
    1355            3 :     return true;
    1356              : }
    1357              : 
    1358              : /*
    1359              :  * perform logfile rotation
    1360              :  */
    1361              : static void
    1362            1 : logfile_rotate(bool time_based_rotation, int size_rotation_for)
    1363              : {
    1364              :     pg_time_t   fntime;
    1365              : 
    1366            1 :     rotation_requested = false;
    1367              : 
    1368              :     /*
    1369              :      * When doing a time-based rotation, invent the new logfile name based on
    1370              :      * the planned rotation time, not current time, to avoid "slippage" in the
    1371              :      * file name when we don't do the rotation immediately.
    1372              :      */
    1373            1 :     if (time_based_rotation)
    1374            0 :         fntime = next_rotation_time;
    1375              :     else
    1376            1 :         fntime = time(NULL);
    1377              : 
    1378              :     /* file rotation for stderr */
    1379            1 :     if (!logfile_rotate_dest(time_based_rotation, size_rotation_for, fntime,
    1380              :                              LOG_DESTINATION_STDERR, &last_sys_file_name,
    1381              :                              &syslogFile))
    1382            0 :         return;
    1383              : 
    1384              :     /* file rotation for csvlog */
    1385            1 :     if (!logfile_rotate_dest(time_based_rotation, size_rotation_for, fntime,
    1386              :                              LOG_DESTINATION_CSVLOG, &last_csv_file_name,
    1387              :                              &csvlogFile))
    1388            0 :         return;
    1389              : 
    1390              :     /* file rotation for jsonlog */
    1391            1 :     if (!logfile_rotate_dest(time_based_rotation, size_rotation_for, fntime,
    1392              :                              LOG_DESTINATION_JSONLOG, &last_json_file_name,
    1393              :                              &jsonlogFile))
    1394            0 :         return;
    1395              : 
    1396            1 :     update_metainfo_datafile();
    1397              : 
    1398            1 :     set_next_rotation_time();
    1399              : }
    1400              : 
    1401              : 
    1402              : /*
    1403              :  * construct logfile name using timestamp information
    1404              :  *
    1405              :  * If suffix isn't NULL, append it to the name, replacing any ".log"
    1406              :  * that may be in the pattern.
    1407              :  *
    1408              :  * Result is palloc'd.
    1409              :  */
    1410              : static char *
    1411            9 : logfile_getname(pg_time_t timestamp, const char *suffix)
    1412              : {
    1413              :     char       *filename;
    1414              :     int         len;
    1415              : 
    1416            9 :     filename = palloc(MAXPGPATH);
    1417              : 
    1418            9 :     snprintf(filename, MAXPGPATH, "%s/", Log_directory);
    1419              : 
    1420            9 :     len = strlen(filename);
    1421              : 
    1422              :     /* treat Log_filename as a strftime pattern */
    1423            9 :     pg_strftime(filename + len, MAXPGPATH - len, Log_filename,
    1424            9 :                 pg_localtime(&timestamp, log_timezone));
    1425              : 
    1426            9 :     if (suffix != NULL)
    1427              :     {
    1428            6 :         len = strlen(filename);
    1429            6 :         if (len > 4 && (strcmp(filename + (len - 4), ".log") == 0))
    1430            6 :             len -= 4;
    1431            6 :         strlcpy(filename + len, suffix, MAXPGPATH - len);
    1432              :     }
    1433              : 
    1434            9 :     return filename;
    1435              : }
    1436              : 
    1437              : /*
    1438              :  * Determine the next planned rotation time, and store in next_rotation_time.
    1439              :  */
    1440              : static void
    1441            2 : set_next_rotation_time(void)
    1442              : {
    1443              :     pg_time_t   now;
    1444              :     struct pg_tm *tm;
    1445              :     int         rotinterval;
    1446              : 
    1447              :     /* nothing to do if time-based rotation is disabled */
    1448            2 :     if (Log_RotationAge <= 0)
    1449            2 :         return;
    1450              : 
    1451              :     /*
    1452              :      * The requirements here are to choose the next time > now that is a
    1453              :      * "multiple" of the log rotation interval.  "Multiple" can be interpreted
    1454              :      * fairly loosely.  In this version we align to log_timezone rather than
    1455              :      * GMT.
    1456              :      */
    1457            0 :     rotinterval = Log_RotationAge * SECS_PER_MINUTE;    /* convert to seconds */
    1458            0 :     now = (pg_time_t) time(NULL);
    1459            0 :     tm = pg_localtime(&now, log_timezone);
    1460            0 :     now += tm->tm_gmtoff;
    1461            0 :     now -= now % rotinterval;
    1462            0 :     now += rotinterval;
    1463            0 :     now -= tm->tm_gmtoff;
    1464            0 :     next_rotation_time = now;
    1465              : }
    1466              : 
    1467              : /*
    1468              :  * Store the name of the file(s) where the log collector, when enabled, writes
    1469              :  * log messages.  Useful for finding the name(s) of the current log file(s)
    1470              :  * when there is time-based logfile rotation.  Filenames are stored in a
    1471              :  * temporary file and which is renamed into the final destination for
    1472              :  * atomicity.  The file is opened with the same permissions as what gets
    1473              :  * created in the data directory and has proper buffering options.
    1474              :  */
    1475              : static void
    1476            2 : update_metainfo_datafile(void)
    1477              : {
    1478              :     FILE       *fh;
    1479              :     mode_t      oumask;
    1480              : 
    1481            2 :     if (!(Log_destination & LOG_DESTINATION_STDERR) &&
    1482            0 :         !(Log_destination & LOG_DESTINATION_CSVLOG) &&
    1483            0 :         !(Log_destination & LOG_DESTINATION_JSONLOG))
    1484              :     {
    1485            0 :         if (unlink(LOG_METAINFO_DATAFILE) < 0 && errno != ENOENT)
    1486            0 :             ereport(LOG,
    1487              :                     (errcode_for_file_access(),
    1488              :                      errmsg("could not remove file \"%s\": %m",
    1489              :                             LOG_METAINFO_DATAFILE)));
    1490            0 :         return;
    1491              :     }
    1492              : 
    1493              :     /* use the same permissions as the data directory for the new file */
    1494            2 :     oumask = umask(pg_mode_mask);
    1495            2 :     fh = fopen(LOG_METAINFO_DATAFILE_TMP, "w");
    1496            2 :     umask(oumask);
    1497              : 
    1498            2 :     if (fh)
    1499              :     {
    1500            2 :         setvbuf(fh, NULL, PG_IOLBF, 0);
    1501              : 
    1502              : #ifdef WIN32
    1503              :         /* use CRLF line endings on Windows */
    1504              :         _setmode(_fileno(fh), _O_TEXT);
    1505              : #endif
    1506              :     }
    1507              :     else
    1508              :     {
    1509            0 :         ereport(LOG,
    1510              :                 (errcode_for_file_access(),
    1511              :                  errmsg("could not open file \"%s\": %m",
    1512              :                         LOG_METAINFO_DATAFILE_TMP)));
    1513            0 :         return;
    1514              :     }
    1515              : 
    1516            2 :     if (last_sys_file_name && (Log_destination & LOG_DESTINATION_STDERR))
    1517              :     {
    1518            2 :         if (fprintf(fh, "stderr %s\n", last_sys_file_name) < 0)
    1519              :         {
    1520            0 :             ereport(LOG,
    1521              :                     (errcode_for_file_access(),
    1522              :                      errmsg("could not write file \"%s\": %m",
    1523              :                             LOG_METAINFO_DATAFILE_TMP)));
    1524            0 :             fclose(fh);
    1525            0 :             return;
    1526              :         }
    1527              :     }
    1528              : 
    1529            2 :     if (last_csv_file_name && (Log_destination & LOG_DESTINATION_CSVLOG))
    1530              :     {
    1531            2 :         if (fprintf(fh, "csvlog %s\n", last_csv_file_name) < 0)
    1532              :         {
    1533            0 :             ereport(LOG,
    1534              :                     (errcode_for_file_access(),
    1535              :                      errmsg("could not write file \"%s\": %m",
    1536              :                             LOG_METAINFO_DATAFILE_TMP)));
    1537            0 :             fclose(fh);
    1538            0 :             return;
    1539              :         }
    1540              :     }
    1541              : 
    1542            2 :     if (last_json_file_name && (Log_destination & LOG_DESTINATION_JSONLOG))
    1543              :     {
    1544            2 :         if (fprintf(fh, "jsonlog %s\n", last_json_file_name) < 0)
    1545              :         {
    1546            0 :             ereport(LOG,
    1547              :                     (errcode_for_file_access(),
    1548              :                      errmsg("could not write file \"%s\": %m",
    1549              :                             LOG_METAINFO_DATAFILE_TMP)));
    1550            0 :             fclose(fh);
    1551            0 :             return;
    1552              :         }
    1553              :     }
    1554            2 :     fclose(fh);
    1555              : 
    1556            2 :     if (rename(LOG_METAINFO_DATAFILE_TMP, LOG_METAINFO_DATAFILE) != 0)
    1557            0 :         ereport(LOG,
    1558              :                 (errcode_for_file_access(),
    1559              :                  errmsg("could not rename file \"%s\" to \"%s\": %m",
    1560              :                         LOG_METAINFO_DATAFILE_TMP, LOG_METAINFO_DATAFILE)));
    1561              : }
    1562              : 
    1563              : /* --------------------------------
    1564              :  *      signal handler routines
    1565              :  * --------------------------------
    1566              :  */
    1567              : 
    1568              : /*
    1569              :  * Check to see if a log rotation request has arrived.  Should be
    1570              :  * called by postmaster after receiving SIGUSR1.
    1571              :  */
    1572              : bool
    1573            2 : CheckLogrotateSignal(void)
    1574              : {
    1575              :     struct stat stat_buf;
    1576              : 
    1577            2 :     if (stat(LOGROTATE_SIGNAL_FILE, &stat_buf) == 0)
    1578            1 :         return true;
    1579              : 
    1580            1 :     return false;
    1581              : }
    1582              : 
    1583              : /*
    1584              :  * Remove the file signaling a log rotation request.
    1585              :  */
    1586              : void
    1587          927 : RemoveLogrotateSignalFiles(void)
    1588              : {
    1589          927 :     unlink(LOGROTATE_SIGNAL_FILE);
    1590          927 : }
    1591              : 
    1592              : /* SIGUSR1: set flag to rotate logfile */
    1593              : static void
    1594            1 : sigUsr1Handler(SIGNAL_ARGS)
    1595              : {
    1596            1 :     rotation_requested = true;
    1597            1 :     SetLatch(MyLatch);
    1598            1 : }
        

Generated by: LCOV version 2.0-1