Exemplo n.º 1
0
/*
 * Main entry point for syslogger process
 * argc/argv parameters are valid only in EXEC_BACKEND case.
 */
NON_EXEC_STATIC void
SysLoggerMain(int argc, char *argv[])
{
#ifndef WIN32
	char		logbuffer[READ_BUF_SIZE];
	int			bytes_in_logbuffer = 0;
#endif
	char	   *currentLogDir;
	char	   *currentLogFilename;
	int			currentLogRotationAge;
	pg_time_t	now;

	IsUnderPostmaster = true;	/* we are a postmaster subprocess now */

	MyProcPid = getpid();		/* reset MyProcPid */

	MyStartTime = time(NULL);	/* set our start time in case we call elog */
	now = MyStartTime;

#ifdef EXEC_BACKEND
	syslogger_parseArgs(argc, argv);
#endif   /* EXEC_BACKEND */

	am_syslogger = true;

	init_ps_display("logger process", "", "", "");

	/*
	 * If we restarted, our stderr is already redirected into our own input
	 * pipe.  This is of course pretty useless, not to mention that it
	 * interferes with detecting pipe EOF.	Point stderr to /dev/null. This
	 * assumes that all interesting messages generated in the syslogger will
	 * come through elog.c and will be sent to write_syslogger_file.
	 */
	if (redirection_done)
	{
		int			fd = open(DEVNULL, O_WRONLY, 0);

		/*
		 * The closes might look redundant, but they are not: we want to be
		 * darn sure the pipe gets closed even if the open failed.	We can
		 * survive running with stderr pointing nowhere, but we can't afford
		 * to have extra pipe input descriptors hanging around.
		 */
		close(fileno(stdout));
		close(fileno(stderr));
		if (fd != -1)
		{
			dup2(fd, fileno(stdout));
			dup2(fd, fileno(stderr));
			close(fd);
		}
	}

	/*
	 * Syslogger's own stderr can't be the syslogPipe, so set it back to text
	 * mode if we didn't just close it. (It was set to binary in
	 * SubPostmasterMain).
	 */
#ifdef WIN32
	else
		_setmode(_fileno(stderr), _O_TEXT);
#endif

	/*
	 * Also close our copy of the write end of the pipe.  This is needed to
	 * ensure we can detect pipe EOF correctly.  (But note that in the restart
	 * case, the postmaster already did this.)
	 */
#ifndef WIN32
	if (syslogPipe[1] >= 0)
		close(syslogPipe[1]);
	syslogPipe[1] = -1;
#else
	if (syslogPipe[1])
		CloseHandle(syslogPipe[1]);
	syslogPipe[1] = 0;
#endif

	/*
	 * If possible, make this process a group leader, so that the postmaster
	 * can signal any child processes too.	(syslogger probably never has any
	 * child processes, but for consistency we make all postmaster child
	 * processes do this.)
	 */
#ifdef HAVE_SETSID
	if (setsid() < 0)
		elog(FATAL, "setsid() failed: %m");
#endif

	InitializeLatchSupport();	/* needed for latch waits */

	/* Initialize private latch for use by signal handlers */
	InitLatch(&sysLoggerLatch);

	/*
	 * Properly accept or ignore signals the postmaster might send us
	 *
	 * Note: we ignore all termination signals, and instead exit only when all
	 * upstream processes are gone, to ensure we don't miss any dying gasps of
	 * broken backends...
	 */

	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, sigUsr1Handler);	/* 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);

#ifdef WIN32
	/* Fire up separate data transfer thread */
	InitializeCriticalSection(&sysloggerSection);
	EnterCriticalSection(&sysloggerSection);

	threadHandle = (HANDLE) _beginthreadex(NULL, 0, pipeThread, NULL, 0, NULL);
	if (threadHandle == 0)
		elog(FATAL, "could not create syslogger data transfer thread: %m");
#endif   /* WIN32 */

	/*
	 * Remember active logfile's name.  We recompute this from the reference
	 * time because passing down just the pg_time_t is a lot cheaper than
	 * passing a whole file path in the EXEC_BACKEND case.
	 */
	last_file_name = logfile_getname(first_syslogger_file_time, NULL);

	/* remember active logfile parameters */
	currentLogDir = pstrdup(Log_directory);
	currentLogFilename = pstrdup(Log_filename);
	currentLogRotationAge = Log_RotationAge;
	/* set next planned rotation time */
	set_next_rotation_time();

	/* main worker loop */
	for (;;)
	{
		bool		time_based_rotation = false;
		int			size_rotation_for = 0;
		long		cur_timeout;
		int			cur_flags;

#ifndef WIN32
		int			rc;
#endif

		/* Clear any already-pending wakeups */
		ResetLatch(&sysLoggerLatch);

		/*
		 * Process any requests or signals received recently.
		 */
		if (got_SIGHUP)
		{
			got_SIGHUP = false;
			ProcessConfigFile(PGC_SIGHUP);

			/*
			 * Check if the log directory or filename pattern changed in
			 * postgresql.conf. If so, force rotation to make sure we're
			 * writing the logfiles in the right place.
			 */
			if (strcmp(Log_directory, currentLogDir) != 0)
			{
				pfree(currentLogDir);
				currentLogDir = pstrdup(Log_directory);
				rotation_requested = true;

				/*
				 * Also, create new directory if not present; ignore errors
				 */
				mkdir(Log_directory, S_IRWXU);
			}
			if (strcmp(Log_filename, currentLogFilename) != 0)
			{
				pfree(currentLogFilename);
				currentLogFilename = pstrdup(Log_filename);
				rotation_requested = true;
			}

			/*
			 * If rotation time parameter changed, reset next rotation time,
			 * but don't immediately force a rotation.
			 */
			if (currentLogRotationAge != Log_RotationAge)
			{
				currentLogRotationAge = Log_RotationAge;
				set_next_rotation_time();
			}

			/*
			 * If we had a rotation-disabling failure, re-enable rotation
			 * attempts after SIGHUP, and force one immediately.
			 */
			if (rotation_disabled)
			{
				rotation_disabled = false;
				rotation_requested = true;
			}
		}

		if (Log_RotationAge > 0 && !rotation_disabled)
		{
			/* Do a logfile rotation if it's time */
			now = (pg_time_t) time(NULL);
			if (now >= next_rotation_time)
				rotation_requested = time_based_rotation = true;
		}

		if (!rotation_requested && Log_RotationSize > 0 && !rotation_disabled)
		{
			/* Do a rotation if file is too big */
			if (ftell(syslogFile) >= Log_RotationSize * 1024L)
			{
				rotation_requested = true;
				size_rotation_for |= LOG_DESTINATION_STDERR;
			}
			if (csvlogFile != NULL &&
				ftell(csvlogFile) >= Log_RotationSize * 1024L)
			{
				rotation_requested = true;
				size_rotation_for |= LOG_DESTINATION_CSVLOG;
			}
		}

		if (rotation_requested)
		{
			/*
			 * Force rotation when both values are zero. It means the request
			 * was sent by pg_rotate_logfile.
			 */
			if (!time_based_rotation && size_rotation_for == 0)
				size_rotation_for = LOG_DESTINATION_STDERR | LOG_DESTINATION_CSVLOG;
			logfile_rotate(time_based_rotation, size_rotation_for);
		}

		/*
		 * Calculate time till next time-based rotation, so that we don't
		 * sleep longer than that.	We assume the value of "now" obtained
		 * above is still close enough.  Note we can't make this calculation
		 * until after calling logfile_rotate(), since it will advance
		 * next_rotation_time.
		 *
		 * Also note that we need to beware of overflow in calculation of the
		 * timeout: with large settings of Log_RotationAge, next_rotation_time
		 * could be more than INT_MAX msec in the future.  In that case we'll
		 * wait no more than INT_MAX msec, and try again.
		 */
		if (Log_RotationAge > 0 && !rotation_disabled)
		{
			pg_time_t	delay;

			delay = next_rotation_time - now;
			if (delay > 0)
			{
				if (delay > INT_MAX / 1000)
					delay = INT_MAX / 1000;
				cur_timeout = delay * 1000L;	/* msec */
			}
			else
				cur_timeout = 0;
			cur_flags = WL_TIMEOUT;
		}
		else
		{
			cur_timeout = -1L;
			cur_flags = 0;
		}

		/*
		 * Sleep until there's something to do
		 */
#ifndef WIN32
		rc = WaitLatchOrSocket(&sysLoggerLatch,
							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
							   syslogPipe[0],
							   cur_timeout);

		if (rc & WL_SOCKET_READABLE)
		{
			int			bytesRead;

			bytesRead = read(syslogPipe[0],
							 logbuffer + bytes_in_logbuffer,
							 sizeof(logbuffer) - bytes_in_logbuffer);
			if (bytesRead < 0)
			{
				if (errno != EINTR)
					ereport(LOG,
							(errcode_for_socket_access(),
							 errmsg("could not read from logger pipe: %m")));
			}
			else if (bytesRead > 0)
			{
				bytes_in_logbuffer += bytesRead;
				process_pipe_input(logbuffer, &bytes_in_logbuffer);
				continue;
			}
			else
			{
				/*
				 * Zero bytes read when select() is saying read-ready means
				 * EOF on the pipe: that is, there are no longer any processes
				 * with the pipe write end open.  Therefore, the postmaster
				 * and all backends are shut down, and we are done.
				 */
				pipe_eof_seen = true;

				/* if there's any data left then force it out now */
				flush_pipe_input(logbuffer, &bytes_in_logbuffer);
			}
		}
#else							/* WIN32 */

		/*
		 * On Windows we leave it to a separate thread to transfer data and
		 * detect pipe EOF.  The main thread just wakes up to handle SIGHUP
		 * and rotation conditions.
		 *
		 * Server code isn't generally thread-safe, so we ensure that only one
		 * of the threads is active at a time by entering the critical section
		 * whenever we're not sleeping.
		 */
		LeaveCriticalSection(&sysloggerSection);

		(void) WaitLatch(&sysLoggerLatch,
						 WL_LATCH_SET | cur_flags,
						 cur_timeout);

		EnterCriticalSection(&sysloggerSection);
#endif   /* WIN32 */

		if (pipe_eof_seen)
		{
			/*
			 * seeing this message on the real stderr is annoying - so we make
			 * it DEBUG1 to suppress in normal use.
			 */
			ereport(DEBUG1,
					(errmsg("logger shutting down")));

			/*
			 * Normal exit from the syslogger is here.	Note that we
			 * deliberately do not close syslogFile before exiting; this is to
			 * allow for the possibility of elog messages being generated
			 * inside proc_exit.  Regular exit() will take care of flushing
			 * and closing stdio channels.
			 */
			proc_exit(0);
		}
	}
}
Exemplo n.º 2
0
/*
 * Main entry point for syslogger process
 * argc/argv parameters are valid only in EXEC_BACKEND case.
 */
__noexec_static__ void syslog_main(int argc, char *argv[])
{
#ifndef WIN32
	char logbuffer[READ_BUF_SIZE];
	int bytes_in_logbuffer = 0;
#endif

	char *current_log_dir;
	char *current_log_fname;
	int current_log_rotation_age;

	child = true;/* we are a postmaster subprocess now */
	current_pid = getpid();	/* reset current_pid */
	proc_start_time = time(NULL);/* set our start time in case we call elog */

#ifdef EXEC_BACKEND
	syslogger_parse_args(argc, argv);
#endif   /* EXEC_BACKEND */

	am_syslogger = true;
	init_ps_display("logger process", "", "", "");

	/*
	 * If we restarted, our stderr is already redirected into our own input
	 * pipe.  This is of course pretty useless, not to mention that it
	 * interferes with detecting pipe EOF.	Point stderr to /dev/null. This
	 * assumes that all interesting messages generated in the syslogger will
	 * come through elog.c and will be sent to write_syslogger.
	 */
	if (redirection_done) {
		int fd = open(DEVNULL, O_WRONLY, 0);

		/*
		 * The closes might look redundant, but they are not. We want 
		 * to be sure the pipe gets closed even if the open failed.
		 * We can survive running with stderr pointing nowhere, but we 
		 * can't afford to have extra pipe input descriptors hanging i
		 * around.
		 */
		close(fileno(stdout));
		close(fileno(stderr));
		if (fd != -1) {
			dup2(fd, fileno(stdout));
			dup2(fd, fileno(stderr));
			close(fd);
		}
	}

	/*
	 * Syslogger's own stderr can't be the syslog_pipe, so set it back to i
	 * text mode if we didn't just close it. It was set to binary in
	 * submaster_main.
	 */
#ifdef WIN32
	else {
		_setmode(_fileno(stderr), _O_TEXT);
	}
#endif

	/*
	 * Also close our copy of the write end of the pipe.  This is needed to
	 * ensure we can detect pipe EOF correctly. But note that in the restart
	 * case, the postmaster already did this.
	 */
#ifndef WIN32
	if (syslog_pipe[1] >= 0)
		close(syslog_pipe[1]);

	syslog_pipe[1] = -1;
#else
	if (syslog_pipe[1])
		CloseHandle(syslog_pipe[1]);

	syslog_pipe[1] = 0;
#endif

	/*
	 * If possible, make this process a group leader, so that the postmaster
	 * can signal any child processes too. Syslogger probably never has any
	 * child processes, but for consistency we make all postmaster child
	 * processes do this.
	 */
#ifdef HAVE_SETSID
	if (setsid() < 0)
		elog(FATAL, "setsid() failed: %m");
#endif

	/*
	 * Properly accept or ignore signals the postmaster might send us
	 * We ignore all termination signals, and instead exit only when all
	 * upstream processes are gone, to ensure we don't miss any dying gaspsi
	 * of broken backends...
	 */
	pqsignal(SIGHUP, syslog_sighup_handler); 	/* set flag to read conf file */
	pqsignal(SIGINT, SIG_IGN);
	pqsignal(SIGTERM, SIG_IGN);
	pqsignal(SIGQUIT, SIG_IGN);
	pqsignal(SIGALRM, SIG_IGN);
	pqsignal(SIGPIPE, SIG_IGN);
	pqsignal(SIGUSR1, syslog_sigusr1_handler);	/* 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);

	SIGSET_MASK(&unblock_signal);

#ifdef WIN32
	/* Fire up separate data transfer thread */
	InitializeCriticalSection(&sysloggerSection);
	EnterCriticalSection(&sysloggerSection);

	threadHandle = (HANDLE) _beginthreadex(NULL, 0, pipeThread, NULL, 0, NULL);
	if (threadHandle == 0)
		elog(FATAL, "could not create syslogger thread: %m");
#endif   /* WIN32 */

	/* 
	 * Remember active logfile parameters
	 */
	current_log_dir = pstrdup(log_directory);
	current_log_fname = pstrdup(log_filename);
	current_log_rotation_age = log_rotation_age;

	/* 
	 * Set next planned rotation time
	 */
	set_next_rotation_time();

	/* 
	 * Main worker loop
	 */
	for (;;) {
		bool time_based_rotation = false;
		int size_rotation_for = 0;
#ifndef WIN32
		int bytesRead;
		int rc;
		fd_set rfds;
		struct timeval timeout;
#endif

		if (got_sighup) {
			got_sighup = false;
			process_config_file(PGC_SIGHUP);

			/*
			 * Check if the log directory or filename pattern 
			 * changed in postgresql.conf. If so, force rotation to
			 * make sure we're writing the logfiles in the right
			 * place.
			 */
			if (strcmp(log_directory, current_log_dir) != 0) {
				pfree(current_log_dir);
				current_log_dir = pstrdup(log_directory);
				rotation_requested = true;
			}

			if (strcmp(log_filename, current_log_fname) != 0) {
				pfree(current_log_fname);
				current_log_fname = pstrdup(log_filename);
				rotation_requested = true;
			}

			/*
			 * If rotation time parameter changed, reset next 
			 * rotation time, but don't immediately force aa 
			 * rotation.
			 */
			if (current_log_rotation_age != log_rotation_age) {
				current_log_rotation_age = log_rotation_age;
				set_next_rotation_time();
			}
		}

		if (!rotation_requested && log_rotation_age > 0) {
			/* Do a logfile rotation if it's time */
			pg_time_t now = (pg_time_t) time(NULL);
			if (now >= next_rotation_time)
				rotation_requested = time_based_rotation = true;
		}

		if (!rotation_requested && log_rotation_size > 0) {
			/* 
			 * Do a rotation if file is too big
			 */
			if (ftell(syslog_file) >= log_rotation_size * 1024L) {
				rotation_requested = true;
				size_rotation_for |= DEST_STDERR;
			}

			if (cvs_logfile != NULL
				&& ftell(cvs_logfile) >= log_rotation_size * 1024L) {
				rotation_requested = true;
				size_rotation_for |= DEST_CSVLOG;
			}
		}

		if (rotation_requested) {
			/*
			 * Force rotation when both values are zero. It means 
			 * the request was sent by pg_rotate_logfile.
			 */
			if (!time_based_rotation && size_rotation_for == 0)
				size_rotation_for = DEST_STDERR | DEST_CSVLOG;

			logfile_rotate(time_based_rotation, size_rotation_for);
		}

#ifndef WIN32

		/*
		 * Wait for some data, timing out after 1 second
		 */
		FD_ZERO(&rfds);
		FD_SET(syslog_pipe[0], &rfds);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		rc = select(syslog_pipe[0] + 1, &rfds, NULL, NULL, &timeout);

		if (rc < 0) {
			if (errno != EINTR) {
				ereport(LOG, (
				errcode_sock_access(),
				errmsg("select() failed in logger process: %m")));
			}
		} else if (rc > 0 && FD_ISSET(syslog_pipe[0], &rfds)) {
			bytesRead = piperead(
				syslog_pipe[0],
				logbuffer + bytes_in_logbuffer,
				sizeof(logbuffer) - bytes_in_logbuffer);
			if (bytesRead < 0) {
				if (errno != EINTR)
					ereport(LOG, (errcode_sock_access(),
					errmsg("could not read from logger pipe: %m")));
			} else if (bytesRead > 0) {
				bytes_in_logbuffer += bytesRead;
				process_pipe_input(logbuffer, &bytes_in_logbuffer);
				continue;
			} else {
				/*
				 * Zero bytes read when select() is saying 
				 * read-ready means EOF on the pipe. That is,
				 * there are no longer any processes with the
				 * pipe write end open. Therefore, the PM and
				 * all backends are shut down, and we are done.
				 */
				pipe_eof_seen = true;

				/* 
				 * If there's any data left then force it out 
				 * now
				 */
				flush_pipe_input(logbuffer, &bytes_in_logbuffer);
			}
		}
#else					/* WIN32 */

		/*
		 * On Windows we leave it to a separate thread to transfer data
		 * and detect pipe EOF. The main thread just wakes up once a
		 * second to check for SIGHUP and rotation conditions.
		 *
		 * Server code isn't generally thread-safe, so we ensure that 
		 * only one of the threads is active at a time by entering the
		 * critical section whenever we're not sleeping.
		 */
		LeaveCriticalSection(&sysloggerSection);
		pg_usleep(1000000L);
		EnterCriticalSection(&sysloggerSection);
#endif   /* WIN32 */

		if (pipe_eof_seen) {
			/*
			 * Seeing this message on the real stderr is annoying.
			 * So we make it DEBUG1 to suppress in normal use.
			 */
			ereport(DEBUG1, (errmsg("logger shutting down")));

			/*
			 * Normal exit from the syslogger is here. Note that we
			 * deliberately do not close syslog_file before exiting.
			 * This is to allow for the possibility of elog 
			 * messages being generated inside proc_exit. Regular
			 * exit() will take care of flushing and closing stdio
			 * channels.
			 */
			proc_exit(0);
		}
	}
}
Exemplo n.º 3
0
/*
 * perform logfile rotation
 */
static void
logfile_rotate(bool time_based_rotation)
{
	char	   *filename;
	FILE	   *fh;

	/*
	 * When doing a time-based rotation, invent the new logfile name based
	 * on the planned rotation time, not current time, to avoid "slippage"
	 * in the file name when we don't do the rotation immediately.
	 */
	if (time_based_rotation)
		filename = logfile_getname(next_rotation_time);
	else
		filename = logfile_getname(time(NULL));

	/*
	 * Decide whether to overwrite or append.  We can overwrite if (a)
	 * Log_truncate_on_rotation is set, (b) the rotation was triggered by
	 * elapsed time and not something else, and (c) the computed file name
	 * is different from what we were previously logging into.
	 *
	 * Note: during the first rotation after forking off from the postmaster,
	 * last_file_name will be NULL.  (We don't bother to set it in the
	 * postmaster because it ain't gonna work in the EXEC_BACKEND case.)
	 * So we will always append in that situation, even though truncating
	 * would usually be safe.
	 */
	if (Log_truncate_on_rotation && time_based_rotation &&
		last_file_name != NULL && strcmp(filename, last_file_name) != 0)
		fh = fopen(filename, "w");
	else
		fh = fopen(filename, "a");

	if (!fh)
	{
		int			saveerrno = errno;

		ereport(LOG,
				(errcode_for_file_access(),
				 errmsg("could not open new log file \"%s\": %m",
						filename)));

		/*
		 * ENFILE/EMFILE are not too surprising on a busy system; just
		 * keep using the old file till we manage to get a new one.
		 * Otherwise, assume something's wrong with Log_directory and stop
		 * trying to create files.
		 */
		if (saveerrno != ENFILE && saveerrno != EMFILE)
		{
			ereport(LOG,
			(errmsg("disabling automatic rotation (use SIGHUP to reenable)")));
			Log_RotationAge = 0;
			Log_RotationSize = 0;
		}
		pfree(filename);
		return;
	}

	setvbuf(fh, NULL, LBF_MODE, 0);

	/* On Windows, need to interlock against data-transfer thread */
#ifdef WIN32
	EnterCriticalSection(&sysfileSection);
#endif
	fclose(syslogFile);
	syslogFile = fh;
#ifdef WIN32
	LeaveCriticalSection(&sysfileSection);
#endif

	set_next_rotation_time();

	/* instead of pfree'ing filename, remember it for next time */
	if (last_file_name != NULL)
		pfree(last_file_name);
	last_file_name = filename;
}
Exemplo n.º 4
0
/*
 * perform logfile rotation
 */
static void
logfile_rotate(bool time_based_rotation, int size_rotation_for)
{
	char* filename;
	char* csv_filename = NULL;
	pg_time_t fntime;
	FILE* fh;

	rotation_requested = false;

	/*
	 * When doing a time-based rotation, invent the new logfile name based on
	 * the planned rotation time, not current time, to avoid "slippage" in the
	 * file name when we don't do the rotation immediately.
	 */
	if (time_based_rotation)
		fntime = next_rotation_time;
	else
		fntime = time(NULL);

	filename = logfile_getname(fntime, NULL);
	if (cvs_logfile != NULL)
		csv_filename = logfile_getname(fntime, ".csv");

	/*
	 * Decide whether to overwrite or append.  We can overwrite if (a)
	 * log_truncate_on_rotation is set, (b) the rotation was triggered by
	 * elapsed time and not something else, and (c) the computed file name is
	 * different from what we were previously logging into.
	 *
	 * Note: during the first rotation after forking off from the postmaster,
	 * last_file_name will be NULL.  (We don't bother to set it in the
	 * postmaster because it ain't gonna work in the EXEC_BACKEND case.) So we
	 * will always append in that situation, even though truncating would
	 * usually be safe.
	 *
	 * For consistency, we treat CSV logs the same even though they aren't
	 * opened in the postmaster.
	 */
	if (time_based_rotation || (size_rotation_for & DEST_STDERR)) {
		if (log_truncate_on_rotation
			&& time_based_rotation
			&& last_file_name != NULL
			&& strcmp(filename, last_file_name) != 0)
			fh = logfile_open(filename, "w", true);
		else
			fh = logfile_open(filename, "a", true);

		if (!fh) {
			/*
			 * ENFILE/EMFILE are not too surprising on a busy system; just
			 * keep using the old file till we manage to get a new one.
			 * Otherwise, assume something's wrong with log_directory and stop
			 * trying to create files.
			 */
			if (errno != ENFILE && errno != EMFILE) {
				ereport(LOG, (
				errmsg("disabling automatic rotation (use SIGHUP to re-enable)")));
				log_rotation_age = 0;
				log_rotation_size = 0;
			}

			if (filename)
				pfree(filename);

			if (csv_filename)
				pfree(csv_filename);

			return;
		}

		fclose(syslog_file);
		syslog_file = fh;

		/* instead of pfree'ing filename, remember it for next time */
		if (last_file_name != NULL)
			pfree(last_file_name);

		last_file_name = filename;
		filename = NULL;
	}

	/* Same as above, but for csv file. */
	if (cvs_logfile != NULL
		&& (time_based_rotation || (size_rotation_for & DEST_CSVLOG))) {
		if (log_truncate_on_rotation
			&& time_based_rotation
			&& last_csv_file_name != NULL
			&& strcmp(csv_filename, last_csv_file_name) != 0)
			fh = logfile_open(csv_filename, "w", true);
		else
			fh = logfile_open(csv_filename, "a", true);

		if (!fh) {
			/*
			 * ENFILE/EMFILE are not too surprising on a busy system; just
			 * keep using the old file till we manage to get a new one.
			 * Otherwise, assume something's wrong with log_directory and stop
			 * trying to create files.
			 */
			if (errno != ENFILE && errno != EMFILE) {
				ereport(LOG, (
				errmsg("disabling automatic rotation (use SIGHUP to re-enable)")));
				log_rotation_age = 0;
				log_rotation_size = 0;
			}

			if (filename)
				pfree(filename);

			if (csv_filename)
				pfree(csv_filename);

			return;
		}

		fclose(cvs_logfile);
		cvs_logfile = fh;

		/* instead of pfree'ing filename, remember it for next time */
		if (last_csv_file_name != NULL)
			pfree(last_csv_file_name);

		last_csv_file_name = csv_filename;
		csv_filename = NULL;
	}

	if (filename)
		pfree(filename);

	if (csv_filename)
		pfree(csv_filename);

	set_next_rotation_time();
}
Exemplo n.º 5
0
/*
 * Main entry point for syslogger process
 * argc/argv parameters are valid only in EXEC_BACKEND case.
 */
NON_EXEC_STATIC void
SysLoggerMain(int argc, char *argv[])
{
	char	   *currentLogDir;
	char	   *currentLogFilename;
	int			currentLogRotationAge;

	IsUnderPostmaster = true;	/* we are a postmaster subprocess now */

	MyProcPid = getpid();		/* reset MyProcPid */

	/* Lose the postmaster's on-exit routines */
	on_exit_reset();

#ifdef EXEC_BACKEND
	syslogger_parseArgs(argc, argv);
#endif   /* EXEC_BACKEND */

	am_syslogger = true;

	init_ps_display("logger process", "", "");
	set_ps_display("");

	/*
	 * If we restarted, our stderr is already redirected into our own
	 * input pipe.	This is of course pretty useless, not to mention that
	 * it interferes with detecting pipe EOF.  Point stderr to /dev/null.
	 * This assumes that all interesting messages generated in the
	 * syslogger will come through elog.c and will be sent to
	 * write_syslogger_file.
	 */
	if (redirection_done)
	{
		int			fd = open(NULL_DEV, O_WRONLY);

		/*
		 * The closes might look redundant, but they are not: we want to
		 * be darn sure the pipe gets closed even if the open failed.  We
		 * can survive running with stderr pointing nowhere, but we can't
		 * afford to have extra pipe input descriptors hanging around.
		 */
		close(fileno(stdout));
		close(fileno(stderr));
		dup2(fd, fileno(stdout));
		dup2(fd, fileno(stderr));
		close(fd);
	}

	/*
	 * Also close our copy of the write end of the pipe.  This is needed
	 * to ensure we can detect pipe EOF correctly.	(But note that in the
	 * restart case, the postmaster already did this.)
	 */
#ifndef WIN32
	if (syslogPipe[1] >= 0)
		close(syslogPipe[1]);
	syslogPipe[1] = -1;
#else
	if (syslogPipe[1])
		CloseHandle(syslogPipe[1]);
	syslogPipe[1] = 0;
#endif

	/*
	 * Properly accept or ignore signals the postmaster might send us
	 *
	 * Note: we ignore all termination signals, and instead exit only when
	 * all upstream processes are gone, to ensure we don't miss any dying
	 * gasps of broken backends...
	 */

	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, SIG_IGN);
	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);

#ifdef WIN32
	/* Fire up separate data transfer thread */
	InitializeCriticalSection(&sysfileSection);

	{
		unsigned int tid;

		threadHandle = (HANDLE) _beginthreadex(0, 0, pipeThread, 0, 0, &tid);
	}
#endif   /* WIN32 */

	/* remember active logfile parameters */
	currentLogDir = pstrdup(Log_directory);
	currentLogFilename = pstrdup(Log_filename);
	currentLogRotationAge = Log_RotationAge;
	/* set next planned rotation time */
	set_next_rotation_time();

	/* main worker loop */
	for (;;)
	{
		bool		rotation_requested = false;
		bool		time_based_rotation = false;

#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 or filename pattern changed in 
			 * postgresql.conf. If so, force rotation to make sure we're 
			 * writing the logfiles in the right place.
			 */
			if (strcmp(Log_directory, currentLogDir) != 0)
			{
				pfree(currentLogDir);
				currentLogDir = pstrdup(Log_directory);
				rotation_requested = true;
			}
			if (strcmp(Log_filename, currentLogFilename) != 0)
			{
				pfree(currentLogFilename);
				currentLogFilename = pstrdup(Log_filename);
				rotation_requested = true;
			}
			/*
			 * If rotation time parameter changed, reset next rotation time,
			 * but don't immediately force a rotation.
			 */
			if (currentLogRotationAge != Log_RotationAge)
			{
				currentLogRotationAge = Log_RotationAge;
				set_next_rotation_time();
			}
		}

		if (!rotation_requested && Log_RotationAge > 0)
		{
			/* Do a logfile rotation if it's time */
			pg_time_t	now = time(NULL);

			if (now >= next_rotation_time)
				rotation_requested = time_based_rotation = true;
		}

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

		if (rotation_requested)
			logfile_rotate(time_based_rotation);

#ifndef WIN32

		/*
		 * Wait for some data, timing out after 1 second
		 */
		FD_ZERO(&rfds);
		FD_SET(syslogPipe[0], &rfds);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;

		rc = select(syslogPipe[0] + 1, &rfds, NULL, NULL, &timeout);

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

			if (bytesRead < 0)
			{
				if (errno != EINTR)
					ereport(LOG,
							(errcode_for_socket_access(),
						 errmsg("could not read from logger pipe: %m")));
			}
			else if (bytesRead > 0)
			{
				write_syslogger_file_binary(logbuffer, bytesRead);
				continue;
			}
			else
			{
				/*
				 * Zero bytes read when select() is saying read-ready
				 * means EOF on the pipe: that is, there are no longer any
				 * processes with the pipe write end open.	Therefore, the
				 * postmaster and all backends are shut down, and we are
				 * done.
				 */
				pipe_eof_seen = true;
			}
		}
#else							/* WIN32 */

		/*
		 * On Windows we leave it to a separate thread to transfer data
		 * and detect pipe EOF.  The main thread just wakes up once a
		 * second to check for SIGHUP and rotation conditions.
		 */
		pgwin32_backend_usleep(1000000);
#endif   /* WIN32 */

		if (pipe_eof_seen)
		{
			ereport(LOG,
					(errmsg("logger shutting down")));

			/*
			 * Normal exit from the syslogger is here.	Note that we
			 * deliberately do not close syslogFile before exiting; this
			 * is to allow for the possibility of elog messages being
			 * generated inside proc_exit.	Regular exit() will take care
			 * of flushing and closing stdio channels.
			 */
			proc_exit(0);
		}
	}
}