Bruce Momjian wrote:
Andreas Pflug wrote:




OK, new idea.  Forget about modifying pg_dir_ls().  Instead add
pg_file_stat the returns the file size, times.  You can then easily use
that for file size and times.  Also, if you want, add an is_dir boolean
so people can write functions that walk the directory tree.

I now replaced pg_logfile_length, instead pg_logfile_stat(text) will return a record (len int8, ctime timestamp, atime timestamp, mtime timestamp, isdir bool).


For convenience, I'd like to have the function

CREATE FUNCTION pg_file_length(text) RETURNS int8
AS
$BODY$
SELECT len
  FROM pg_file_stat($1) AS stat
        (len int8, ctime timestamp,
        atime timestamp, mtime timestamp, isdir bool)
$BODY$ LANGUAGE SQL STRICT;

Where is the right place to put it?

Also, I wonder how to join pg_file_stat and pg_dir_ls to get a ls -l like listing. Apparently I can't do that, unless I don't code pg_dir_ls as returning records too, right?



I noticed we had a big logging discussion during 7.4 beta about logging and log rotation. This patch is clearly superior to the ideas we had at that time.


Currently, the discussion circles around file functions, not logging. If you think that part is clean, how about committing it separately so it can be tested/used (no problem if pg_logfile_rotate() isn't available right from the start). I'll supply docs RSN.


Regards,
Andreas
Index: src/backend/catalog/system_views.sql
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/catalog/system_views.sql,v
retrieving revision 1.6
diff -u -r1.6 system_views.sql
--- src/backend/catalog/system_views.sql	26 Apr 2004 15:24:41 -0000	1.6
+++ src/backend/catalog/system_views.sql	21 Jul 2004 09:49:22 -0000
@@ -273,3 +273,8 @@
     DO INSTEAD NOTHING;
 
 GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+
+CREATE VIEW pg_logdir_ls AS
+	SELECT *
+	FROM pg_logdir_ls() AS A
+	(filetime timestamp, pid int4, filename text);
Index: src/backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.16
diff -u -r1.16 Makefile
--- src/backend/postmaster/Makefile	19 Jul 2004 02:47:08 -0000	1.16
+++ src/backend/postmaster/Makefile	21 Jul 2004 09:49:23 -0000
@@ -12,7 +12,7 @@
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
+OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o syslogger.o
 
 all: SUBSYS.o
 
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.412
diff -u -r1.412 postmaster.c
--- src/backend/postmaster/postmaster.c	19 Jul 2004 02:47:08 -0000	1.412
+++ src/backend/postmaster/postmaster.c	21 Jul 2004 09:49:29 -0000
@@ -118,7 +118,7 @@
 #include "utils/ps_status.h"
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
-
+#include "postmaster/syslogger.h"
 
 /*
  * List of active backends (or child processes anyway; we don't actually
@@ -201,6 +201,7 @@
 			BgWriterPID = 0,
 			PgArchPID = 0,
 			PgStatPID = 0;
+pid_t       SysLoggerPID = 0;
 
 /* Startup/shutdown state */
 #define			NoShutdown		0
@@ -852,6 +853,12 @@
 #endif
 
 	/*
+	 * start logging to file
+	 */ 
+
+    SysLoggerPID = SysLogger_Start();
+
+	/*
 	 * Reset whereToSendOutput from Debug (its starting state) to None.
 	 * This prevents ereport from sending log messages to stderr unless
 	 * the syslog/stderr switch permits.  We don't do this until the
@@ -1230,6 +1237,11 @@
 			StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
 			PgStatPID = pgstat_start();
 
+		/* If we have lost the system logger, try to start a new one */
+		if (SysLoggerPID == 0 &&
+			StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+			SysLoggerPID = SysLogger_Start();
+
 		/*
 		 * Touch the socket and lock file at least every ten minutes, to ensure
 		 * that they are not removed by overzealous /tmp-cleaning tasks.
@@ -1770,6 +1782,9 @@
 			kill(BgWriterPID, SIGHUP);
 		if (PgArchPID != 0)
 			kill(PgArchPID, SIGHUP);
+		if (SysLoggerPID != 0)
+			kill(SysLoggerPID, SIGHUP);
+
 		/* PgStatPID does not currently need SIGHUP */
 		load_hba();
 		load_ident();
@@ -1835,7 +1850,6 @@
 			if (PgStatPID != 0)
 				kill(PgStatPID, SIGQUIT);
 			break;
-
 		case SIGINT:
 			/*
 			 * Fast Shutdown:
@@ -1902,6 +1916,7 @@
 				kill(PgStatPID, SIGQUIT);
 			if (DLGetHead(BackendList))
 				SignalChildren(SIGQUIT);
+
 			ExitPostmaster(0);
 			break;
 	}
@@ -2059,6 +2074,15 @@
 			continue;
 		}
 
+		/* was it the system logger, try to start a new one */
+		if (SysLoggerPID != 0 && pid == SysLoggerPID)
+		{
+			if (exitstatus != 0)
+				LogChildExit(LOG, gettext("system logger process"),
+							 pid, exitstatus);
+			SysLoggerPID = SysLogger_Start();
+			continue;
+		}
 		/*
 		 * Else do standard backend child cleanup.
 		 */
@@ -2956,6 +2980,16 @@
 		PgstatCollectorMain(argc, argv);
 		proc_exit(0);
 	}
+	if (strcmp(argv[1], "-forklog") == 0)
+	{
+		/* Close the postmaster's sockets */
+		ClosePostmasterPorts();
+
+		/* Do not want to attach to shared memory */
+
+		SysLoggerMain(argc, argv);
+		proc_exit(0);
+	}
 
 	return 1;					/* shouldn't get here */
 }
@@ -3012,7 +3046,6 @@
 		if (Shutdown <= SmartShutdown)
 			SignalChildren(SIGUSR1);
 	}
- 
 	if (PgArchPID != 0 && Shutdown == NoShutdown)
 	{
 		if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3024,6 +3057,10 @@
             kill(PgArchPID, SIGUSR1);
 		}
     }
+	if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+	{
+	    kill(SysLoggerPID, SIGUSR1);
+	}
 
 	PG_SETMASK(&UnBlockSig);
 
Index: src/backend/utils/adt/misc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/misc.c,v
retrieving revision 1.35
diff -u -r1.35 misc.c
--- src/backend/utils/adt/misc.c	2 Jul 2004 18:59:22 -0000	1.35
+++ src/backend/utils/adt/misc.c	21 Jul 2004 09:49:30 -0000
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include <sys/file.h>
+#include <unistd.h>
 #include <signal.h>
 #include <dirent.h>
 
@@ -26,6 +27,49 @@
 #include "funcapi.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_tablespace.h"
+#include "postmaster/syslogger.h"
+
+/*-----------------------
+ * some helper functions
+ */
+
+/*
+ * Return an absolute path. Argument may be absolute or 
+ * relative to the DataDir.
+ */
+static char *absClusterPath(text *arg)
+{
+	char *filename;
+	int len=VARSIZE(arg) - VARHDRSZ;
+
+	filename = palloc(len+1);
+	memcpy(filename, VARDATA(arg), len);
+	filename[len] = 0;
+
+	if (is_absolute_path(filename))
+	    return filename;
+	else
+	{
+	    char *absname = palloc(strlen(DataDir)+len+2);
+		sprintf(absname, "%s/%s", DataDir, filename);
+		pfree(filename);
+		return absname;
+	}
+}
+
+
+/*
+ * check for superuser, bark if not.
+ */
+static void
+requireSuperuser(void)
+{
+	if (!superuser())
+	    ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("only superuser may access generic file functions"))));
+}
+
 
 
 /*
@@ -109,18 +153,37 @@
 	PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGINT));
 }
 
+Datum
+pg_reload_conf(PG_FUNCTION_ARGS)
+{
+	if (!superuser()) 
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("only superuser can signal the postmaster"))));
+
+	if (kill(PostmasterPid, SIGHUP))
+	{
+		ereport(WARNING,
+				(errmsg("failed to send signal to postmaster: %m")));
+
+		PG_RETURN_INT32(0);
+	}
+
+	PG_RETURN_INT32(1);
+}
+
 
 typedef struct 
 {
 	char *location;
 	DIR *dirdesc;
-} ts_db_fctx;
+} directory_fctx;
 
 Datum pg_tablespace_databases(PG_FUNCTION_ARGS)
 {
 	FuncCallContext *funcctx;
 	struct dirent *de;
-	ts_db_fctx *fctx;
+	directory_fctx *fctx;
 
 	if (SRF_IS_FIRSTCALL())
 	{
@@ -130,7 +193,7 @@
 		funcctx=SRF_FIRSTCALL_INIT();
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-		fctx = palloc(sizeof(ts_db_fctx));
+		fctx = palloc(sizeof(directory_fctx));
 
 		/*
 		 * size = path length + tablespace dirname length
@@ -164,7 +227,7 @@
 	}
 
 	funcctx=SRF_PERCALL_SETUP();
-	fctx = (ts_db_fctx*) funcctx->user_fctx;
+	fctx = (directory_fctx*) funcctx->user_fctx;
 
 	if (!fctx->dirdesc)  /* not a tablespace */
 		SRF_RETURN_DONE(funcctx);
@@ -202,3 +265,467 @@
 	FreeDir(fctx->dirdesc);
 	SRF_RETURN_DONE(funcctx);
 }
+
+
+
+/* ------------------------------------
+ * generic file handling functions
+ */
+
+
+Datum pg_file_stat(PG_FUNCTION_ARGS)
+{
+    AttInMetadata *attinmeta = NULL;
+	char *	filename = absClusterPath(PG_GETARG_TEXT_P(0));
+	struct stat fst;
+	int64 length;
+	char lenbuf[30];
+	char cbuf[30], abuf[30], mbuf[30], dbuf[]="f";
+	char *values[5]=
+	  {lenbuf, cbuf, abuf, mbuf, dbuf};
+
+	pg_time_t timestamp;
+	HeapTuple tuple;
+
+	if (attinmeta == NULL)
+	{
+		TupleDesc tupdesc = CreateTemplateTupleDesc(5, false);
+
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "length",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ctime",
+						   TIMESTAMPOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "atime",
+						   TIMESTAMPOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "mtime",
+						   TIMESTAMPOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "isdir",
+						   BOOLOID, -1, 0);
+
+		attinmeta = TupleDescGetAttInMetadata(tupdesc);
+	}
+
+	if (stat(filename, &fst) < 0)
+	{
+		ereport(WARNING,
+				(errcode_for_file_access(),
+				 errmsg("could not stat file %s: %m", filename)));
+
+		PG_RETURN_NULL();
+	}
+
+	length = fst.st_size;  
+	snprintf(lenbuf, 30, INT64_FORMAT, length);
+
+	timestamp = fst.st_ctime;
+	pg_strftime(cbuf, 30, "%F %T", pg_localtime(&timestamp));
+
+	timestamp = fst.st_atime;
+	pg_strftime(abuf, 30, "%F %T", pg_localtime(&timestamp));
+
+	timestamp = fst.st_mtime;
+	pg_strftime(mbuf, 30, "%F %T", pg_localtime(&timestamp));
+
+	if (fst.st_mode & S_IFDIR)
+	    dbuf[0] = 't';
+
+	tuple = BuildTupleFromCStrings(attinmeta, values);
+
+	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+}
+
+
+Datum pg_file_read(PG_FUNCTION_ARGS)
+{
+	size_t size;
+	char *buf=0;
+	size_t nbytes;
+	int64 pos;
+	FILE *f;
+	char *filename;
+
+	requireSuperuser();
+
+	filename = absClusterPath(PG_GETARG_TEXT_P(0));
+	pos = PG_GETARG_INT64(1);
+	size = PG_GETARG_INT64(2);
+
+	f = fopen(filename, "r");
+	if (!f)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not open file %s for reading: %m", filename)));
+		PG_RETURN_NULL();
+	}
+
+	if (pos >= 0)
+	    fseek(f, pos, SEEK_SET);
+	else
+	    fseek(f, pos, SEEK_END);
+
+
+	buf = palloc(size + VARHDRSZ);
+
+	nbytes = fread(VARDATA(buf), 1, size, f);
+	if (nbytes < 0)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read file %s: %m", filename)));
+		PG_RETURN_NULL();
+	}
+	VARATT_SIZEP(buf) = nbytes + VARHDRSZ;
+	fclose(f);
+
+	PG_RETURN_TEXT_P(buf);
+}
+
+
+Datum pg_file_write(PG_FUNCTION_ARGS)
+{
+	FILE *f;
+	char *filename;
+	text *data;
+	int64 count = 0;
+
+	requireSuperuser();
+
+	filename = absClusterPath(PG_GETARG_TEXT_P(0));
+	data = PG_GETARG_TEXT_P(1);
+
+	if (PG_ARGISNULL(2) || !PG_GETARG_BOOL(2))
+	{
+	    struct stat fst;
+		if (stat(filename, &fst) >= 0)
+		    ereport(ERROR,
+					(ERRCODE_DUPLICATE_FILE,
+					 errmsg("file %s exists", filename)));
+
+	    f = fopen(filename, "w");
+	}
+	else
+	    f = fopen(filename, "a");
+
+	if (!f)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could open file %s for writing: %m", filename)));
+	}
+
+	if (VARSIZE(data) != 0)
+	{
+		count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
+
+		if (count != VARSIZE(data))
+		    ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("error writing file %s: %m", filename)));
+	}
+	fclose(f);
+
+	PG_RETURN_INT64(count);
+}
+
+
+Datum pg_file_rename(PG_FUNCTION_ARGS)
+{
+    char *fn1, *fn2, *fn3;
+	int rc;
+
+	requireSuperuser();
+
+	fn1=absClusterPath(PG_GETARG_TEXT_P(0));
+	fn2=absClusterPath(PG_GETARG_TEXT_P(1));
+	if (PG_ARGISNULL(2))
+	    fn3=0;
+	else
+	    fn3=absClusterPath(PG_GETARG_TEXT_P(2));
+
+    struct stat fst;
+	if (stat(fn1, &fst) < 0)
+	{
+		ereport(WARNING,
+				(errcode_for_file_access(),
+				 errmsg("could not stat file %s: %m", fn1)));
+
+	    PG_RETURN_BOOL(false);
+	}
+
+	if (fn3 && stat(fn2, &fst) < 0)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not stat file %s: %m", fn2)));
+
+	    PG_RETURN_BOOL(false);
+	}
+
+
+	rc = stat(fn3 ? fn3 : fn2, &fst);
+	if (rc >= 0 || errno != ENOENT)
+	{
+		ereport(ERROR,
+				(ERRCODE_DUPLICATE_FILE,
+				 errmsg("cannot rename: target file %s exists", fn3 ? fn3 : fn2)));
+	}
+	
+	if (fn3)
+	{
+	    if (rename(fn2, fn3) != 0)
+		{
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not rename %s to %s: %m", fn2, fn3)));
+		}
+		if (rename(fn1, fn2) != 0)
+		{
+			ereport(WARNING,
+					(errcode_for_file_access(),
+					 errmsg("could not rename %s to %s: %m", fn1, fn2)));
+
+			if (rename(fn3, fn2) != 0)
+			{
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not rename %s back to %s: %m", fn3, fn2)));
+			}
+			else
+			{
+				ereport(ERROR,
+						(ERRCODE_UNDEFINED_FILE,
+						 errmsg("renaming %s to %s was reverted", fn2, fn3)));
+
+			}
+		}
+	}
+	if (rename(fn1, fn2) != 0)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not rename %s to %s: %m", fn1, fn2)));
+	}
+
+	PG_RETURN_BOOL(true);
+}
+
+
+Datum pg_file_unlink(PG_FUNCTION_ARGS)
+{
+    char *filename;
+
+	requireSuperuser();
+
+    filename = absClusterPath(PG_GETARG_TEXT_P(0));
+
+	if (unlink(filename) < 0)
+	{
+		ereport(WARNING,
+				(errcode_for_file_access(),
+				 errmsg("could not unlink file %s", filename)));
+
+		PG_RETURN_BOOL(false);
+	}
+	PG_RETURN_BOOL(true);
+}
+
+
+Datum pg_dir_ls(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	struct dirent *de;
+	directory_fctx *fctx;
+
+	requireSuperuser();
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+
+		funcctx=SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		fctx = palloc(sizeof(directory_fctx));
+		fctx->location = absClusterPath(PG_GETARG_TEXT_P(0));
+
+		fctx->dirdesc = AllocateDir(fctx->location);
+
+		if (!fctx->dirdesc)
+		    ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("%s is not browsable: %m", fctx->location)));
+
+		if (PG_ARGISNULL(1) || !PG_GETARG_BOOL(1))
+		{
+			pfree(fctx->location);
+			fctx->location = 0;
+		}
+		funcctx->user_fctx = fctx;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx=SRF_PERCALL_SETUP();
+	fctx = (directory_fctx*) funcctx->user_fctx;
+
+	if (!fctx->dirdesc)  /* not a readable directory  */
+		SRF_RETURN_DONE(funcctx);
+
+	while ((de = readdir(fctx->dirdesc)) != NULL)
+	{
+	    char *name;
+		text *result;
+		int len;
+		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+		    continue;
+		if (fctx->location)
+		{
+			char *path=palloc(strlen(fctx->location) + strlen(de->d_name) +2);
+			sprintf(path, "%s/%s", fctx->location, de->d_name);
+
+			name = path;
+		}
+		else
+		    name = de->d_name;
+
+
+		len = strlen(name);
+		result = palloc(len + VARHDRSZ);
+		VARATT_SIZEP(result) = len + VARHDRSZ;
+		memcpy(VARDATA(result), name, len);
+
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(result));
+	}
+
+	FreeDir(fctx->dirdesc);
+	SRF_RETURN_DONE(funcctx);
+}
+
+
+/*
+ * logfile handling functions
+ */
+
+
+Datum pg_logfile_rotate(PG_FUNCTION_ARGS)
+{
+    requireSuperuser();
+
+    PG_RETURN_BOOL(LogFileRotate());
+}
+
+
+Datum pg_logdir_ls(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	struct dirent *de;
+	directory_fctx *fctx;
+
+	requireSuperuser();
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+		TupleDesc tupdesc;
+
+		funcctx=SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		fctx = palloc(sizeof(directory_fctx));
+
+		if (is_absolute_path(Log_directory))
+		    fctx->location = Log_directory;
+		else
+		{
+			fctx->location = palloc(strlen(DataDir) + strlen(Log_directory) +2);
+			sprintf(fctx->location, "%s/%s", DataDir, Log_directory);
+		}
+
+		tupdesc = CreateTemplateTupleDesc(3, false);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
+						   TIMESTAMPOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
+						   INT4OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "filename",
+						   TEXTOID, -1, 0);
+
+		funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
+		
+		fctx->dirdesc = AllocateDir(fctx->location);
+
+		if (!fctx->dirdesc)
+		    ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("%s is not browsable: %m", fctx->location)));
+
+		funcctx->user_fctx = fctx;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx=SRF_PERCALL_SETUP();
+	fctx = (directory_fctx*) funcctx->user_fctx;
+
+	if (!fctx->dirdesc)  /* not a readable directory  */
+		SRF_RETURN_DONE(funcctx);
+
+	while ((de = readdir(fctx->dirdesc)) != NULL)
+	{
+	    char *values[3];
+		HeapTuple tuple;
+		int prefixLen=strlen(Log_filename_prefix);
+
+		char	   *field[MAXDATEFIELDS];
+		char		lowstr[MAXDATELEN + 1];
+		int			dtype;
+		int			nf, ftype[MAXDATEFIELDS];
+		fsec_t		fsec;
+		int			tz = 0;
+		struct pg_tm date;
+		int         i;
+
+		/*
+		 * format as created in logfile_getname():
+		 *        prefix_YYYY-MM-DD_HHMMSS_PPPPP.log
+		 *   prefixLen   ^
+		 *                 prefixLen+17   ^
+		 *                       prefixLen+23   ^
+		 */
+
+		if (strlen(de->d_name) != prefixLen + 27
+		    || memcmp(de->d_name, Log_filename_prefix, prefixLen)
+			|| de->d_name[prefixLen + 17] != '_'
+			|| strcmp(de->d_name + prefixLen + 23, ".log"))
+		      continue;
+
+		values[2] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
+		sprintf(values[2], "%s/%s", fctx->location, de->d_name);
+
+		values[0] = de->d_name + prefixLen;       /* timestamp */
+		values[0][17] = 0;
+
+		values[1] = de->d_name + prefixLen + 18;  /* pid */
+		values[1][5] = 0;
+
+		/* check if pid is purely numeric as expected */
+		for (i = 0 ; i < 5 ; i++)
+		    if (!isdigit(values[0][i]))
+				continue;
+
+		/* parse and decode expected timestamp */
+		if (ParseDateTime(values[0], lowstr, field, ftype, MAXDATEFIELDS, &nf))
+		    continue;
+
+		if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
+		    continue;
+
+		/* Seems the format fits the expected format; feed it into the tuple */
+
+
+		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+	}
+
+	FreeDir(fctx->dirdesc);
+	SRF_RETURN_DONE(funcctx);
+}
Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v
retrieving revision 1.142
diff -u -r1.142 elog.c
--- src/backend/utils/error/elog.c	24 Jun 2004 21:03:13 -0000	1.142
+++ src/backend/utils/error/elog.c	21 Jul 2004 09:49:33 -0000
@@ -84,6 +84,10 @@
 static void write_eventlog(int level, const char *line);
 #endif
 
+/* in syslogger.c */
+extern FILE *syslogFile;
+extern FILE *realStdErr;
+extern pid_t SysLoggerPID;
 /*
  * ErrorData holds the data accumulated during any one ereport() cycle.
  * Any non-NULL pointers must point to palloc'd data in ErrorContext.
@@ -1451,10 +1455,31 @@
 		write_eventlog(eventlog_level, buf.data);
 	}
 #endif   /* WIN32 */
-	/* Write to stderr, if enabled */
-	if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug)
+	
+	/*
+	 * Write to stderr. If Log_destination is file or stderr
+	 * if file is target, the logger process will handle this
+	 */
+	if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE)) 
+		|| whereToSendOutput == Debug)
 	{
-		fprintf(stderr, "%s", buf.data);
+	    if (SysLoggerPID == MyProcPid && realStdErr != 0)
+		{
+		    /*
+			 * If realStdErr is not null in the SysLogger process,
+			 * there's something really wrong because stderr is probably
+			 * redirected to the pipe. To avoid circular writes, we
+			 * write to realStdErr which is hopefully the stderr the postmaster
+			 * was started with.
+			 */
+		    fprintf(realStdErr, "%s", buf.data);
+		}
+		else
+		    fprintf(stderr, "%s", buf.data) ;
+
+		/* syslogFile is open in SysLogger only */
+		if (syslogFile != 0)
+		    fprintf(syslogFile, "%s", buf.data) ;
 	}
 
 	pfree(buf.data);
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.221
diff -u -r1.221 guc.c
--- src/backend/utils/misc/guc.c	19 Jul 2004 21:39:47 -0000	1.221
+++ src/backend/utils/misc/guc.c	21 Jul 2004 09:49:42 -0000
@@ -44,6 +44,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "postmaster/bgwriter.h"
+#include "postmaster/syslogger.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
@@ -1285,6 +1286,23 @@
 		BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
 	},
 
+	{
+	  {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
+	   gettext_noop("Automatic logfile rotation will occur after n minutes"),
+	   NULL
+	  },
+	  &Log_RotationAge,
+	  24*60, 0, INT_MAX, NULL, NULL
+	},
+	{
+	  {"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
+	   gettext_noop("Automatic logfile rotation will occur if this size is reached (in kb)"),
+	   NULL
+	  },
+	  &Log_RotationSize,
+	  10*1024, 0, INT_MAX, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -1627,13 +1645,32 @@
 	{
 		{"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
 		 gettext_noop("Sets the target for log output."),
-		 gettext_noop("Valid values are combinations of stderr, syslog "
+		 gettext_noop("Valid values are combinations of stderr, file, syslog "
 					  "and eventlog, depending on platform."),
 		 GUC_LIST_INPUT
 		},
 		&log_destination_string,
 		"stderr", assign_log_destination, NULL
 	},
+	{
+		{"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+		 gettext_noop("Sets the target directory for log output."),
+		 gettext_noop("May be specified as relative to the cluster directory "
+					  "or as absolute path."),
+		 GUC_LIST_INPUT | GUC_REPORT
+		},
+		&Log_directory,
+		"pg_log", NULL, NULL
+	},
+	{
+		{"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+		 gettext_noop("prefix for logfile names created in the log_directory."),
+		 NULL,
+		 GUC_LIST_INPUT | GUC_REPORT
+		},
+		&Log_filename_prefix,
+		"postgresql-", NULL, NULL
+	},
 
 #ifdef HAVE_SYSLOG
 	{
@@ -5079,6 +5116,8 @@
 	
 		if (pg_strcasecmp(tok,"stderr") == 0)
 			newlogdest |= LOG_DESTINATION_STDERR;
+		else if (pg_strcasecmp(tok,"file") == 0)
+			newlogdest |= LOG_DESTINATION_FILE;
 #ifdef HAVE_SYSLOG
 		else if (pg_strcasecmp(tok,"syslog") == 0)
 			newlogdest |= LOG_DESTINATION_SYSLOG;
Index: src/backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.116
diff -u -r1.116 postgresql.conf.sample
--- src/backend/utils/misc/postgresql.conf.sample	19 Jul 2004 02:47:10 -0000	1.116
+++ src/backend/utils/misc/postgresql.conf.sample	21 Jul 2004 09:49:43 -0000
@@ -167,9 +167,17 @@
 
 # - Where to Log -
 
-#log_destination = 'stderr'	# Valid values are combinations of stderr,
-                                # syslog and eventlog, depending on
-                                # platform.
+#log_destination = 'stderr' # Valid values are combinations of stderr, file,
+                            # syslog and eventlog, depending on platform.
+#log_directory = 'pg_log'   # subdirectory where logfiles are written 
+                            # if 'file' log_destination is used.
+                            # May be specified absolute or relative to PGDATA
+#log_filename_prefix = 'postgresql_' # prefix for logfile names
+#log_rotation_age = 1440    # Automatic rotation of logfiles will happen if 
+                            # specified age in minutes is reached. 0 to disable.
+#log_rotation_size = 10240  # Automatic rotation of logfiles will happen if 
+                            # specified size in kb is reached. 0 to disable.
+
 #syslog_facility = 'LOCAL0'
 #syslog_ident = 'postgres'
 
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.342
diff -u -r1.342 pg_proc.h
--- src/include/catalog/pg_proc.h	12 Jul 2004 20:23:53 -0000	1.342
+++ src/include/catalog/pg_proc.h	21 Jul 2004 09:49:57 -0000
@@ -2819,6 +2819,8 @@
 DESCR("Terminate a backend process");
 DATA(insert OID = 2172 ( pg_cancel_backend              PGNSP PGUID 12 f f t f s 1 23 "23" _null_ pg_cancel_backend - _null_ ));
 DESCR("Cancel running query on a backend process");
+DATA(insert OID = 2173 ( pg_reload_conf                PGNSP PGUID 12 f f t f s 1 23 "" _null_ pg_reload_conf - _null_ ));
+DESCR("Reload postgresql.conf");
 
 DATA(insert OID = 1946 (  encode						PGNSP PGUID 12 f f t f i 2 25 "17 25" _null_  binary_encode - _null_ ));
 DESCR("Convert bytea value into some ascii-only text string");
@@ -3607,6 +3609,26 @@
 DATA(insert OID = 2556 ( pg_tablespace_databases	PGNSP PGUID 12 f f t t s 1 26 "26" _null_ pg_tablespace_databases - _null_));
 DESCR("returns database oids in a tablespace");
 
+DATA(insert OID = 2557( pg_file_stat		           PGNSP PGUID 12 f f t f v 1 2249 "25" _null_ pg_file_stat - _null_ ));
+DESCR("stat properties of generic file");
+DATA(insert OID = 2558( pg_file_read		           PGNSP PGUID 12 f f t f v 3 25 "25 20 20" _null_ pg_file_read - _null_ ));
+DESCR("read contents of generic file");
+DATA(insert OID = 2559( pg_file_write		           PGNSP PGUID 12 f f t f v 3 20 "25 25 16" _null_ pg_file_write - _null_ ));
+DESCR("write generic file");
+DATA(insert OID = 2560( pg_file_rename                PGNSP PGUID 12 f f t f v 2 16 "25 25" _null_ pg_file_rename - _null_ ));
+DESCR("rename generic file");
+DATA(insert OID = 2561( pg_file_rename                PGNSP PGUID 12 f f t f v 33 16 "25 25 25" _null_ pg_file_rename - _null_ ));
+DESCR("rename generic file");
+DATA(insert OID = 2562( pg_file_unlink		           PGNSP PGUID 12 f f t f v 1 16 "25" _null_ pg_file_unlink - _null_ ));
+DESCR("remove generic file");
+
+DATA(insert OID = 2563( pg_dir_ls		               PGNSP PGUID 12 f f t t v 2 25 "25 16" _null_ pg_dir_ls - _null_ ));
+DESCR("list generic directory");
+
+DATA(insert OID = 2564( pg_logfile_rotate		       PGNSP PGUID 12 f f t f v 0 16 "" _null_ pg_logfile_rotate - _null_ ));
+DESCR("rotate log file");
+DATA(insert OID = 2565( pg_logdir_ls		           PGNSP PGUID 12 f f t t v 0 2249 "" _null_ pg_logdir_ls - _null_ ));
+DESCR("list all available log files");
 
 /*
  * Symbolic values for provolatile column: these indicate whether the result
Index: src/include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.9
diff -u -r1.9 pmsignal.h
--- src/include/storage/pmsignal.h	19 Jul 2004 02:47:15 -0000	1.9
+++ src/include/storage/pmsignal.h	21 Jul 2004 09:49:58 -0000
@@ -25,7 +25,7 @@
 	PMSIGNAL_PASSWORD_CHANGE,	/* pg_pwd file has changed */
 	PMSIGNAL_WAKEN_CHILDREN,	/* send a SIGUSR1 signal to all backends */
 	PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to xlog archiver */
-
+	PMSIGNAL_ROTATE_LOGFILE,	/* send SIGUSR1 to syslogger to rotate logfile */
 	NUM_PMSIGNALS				/* Must be last value of enum! */
 } PMSignalReason;
 
Index: src/include/utils/builtins.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.246
diff -u -r1.246 builtins.h
--- src/include/utils/builtins.h	12 Jul 2004 20:23:59 -0000	1.246
+++ src/include/utils/builtins.h	21 Jul 2004 09:50:00 -0000
@@ -362,8 +362,20 @@
 extern Datum current_database(PG_FUNCTION_ARGS);
 extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
 extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
 extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
 
+extern Datum pg_logfile_rotate(PG_FUNCTION_ARGS);
+extern Datum pg_logdir_ls(PG_FUNCTION_ARGS);
+
+extern Datum pg_file_stat(PG_FUNCTION_ARGS);
+extern Datum pg_file_read(PG_FUNCTION_ARGS);
+extern Datum pg_file_write(PG_FUNCTION_ARGS);
+extern Datum pg_file_rename(PG_FUNCTION_ARGS);
+extern Datum pg_file_unlink(PG_FUNCTION_ARGS);
+
+extern Datum pg_dir_ls(PG_FUNCTION_ARGS);
+
 /* not_in.c */
 extern Datum int4notin(PG_FUNCTION_ARGS);
 extern Datum oidnotin(PG_FUNCTION_ARGS);
Index: src/include/utils/elog.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v
retrieving revision 1.70
diff -u -r1.70 elog.h
--- src/include/utils/elog.h	6 Jul 2004 19:51:59 -0000	1.70
+++ src/include/utils/elog.h	21 Jul 2004 09:50:01 -0000
@@ -185,10 +185,10 @@
 #define LOG_DESTINATION_STDERR   1
 #define LOG_DESTINATION_SYSLOG   2
 #define LOG_DESTINATION_EVENTLOG 4
+#define LOG_DESTINATION_FILE     8
 
 /* Other exported functions */
 extern void DebugFileOpen(void);
-
 /*
  * Write errors to stderr (or by equal means when stderr is
  * not available). Used before ereport/elog can be used
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to