Bruce Momjian wrote:
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).
You mean pg_file_stat(text), right?
Of course.
>
For convenience, I'd like to have the function
CREATE FUNCTION pg_file_length(text) RETURNS int8
Take a look at obj_description in include/catalog/pg_proc.h. That should be a good example.
Done.
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?
Ideally you want:
select filename, pg_file_stat(filename) from pg_dir_ls()
or something like that. However, I don't think you can have a function call returning multiple values in the target list, and I can't figure out how to pass an argument to the function if it is in the target list. Ideas?
I thought of SELECT filename, len, ctime FROM pg_dir_ls('/etc') AS d (filename text...) JOIN pg_file_stat(filename) AS s(len int8, ....) WHERE filename like 's%'
but that wouldn't work either.
Hm, is it really worth thinking about this further. We won't contribute a Konqueror plugin to browse a file server through a pgsql connection, I believe...
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.
Is pg_logfile_rotate() not working? You mean pg_file_length().
pg_logfile_rotate() *is* working, it's just buried in a bunch of generic file functions in adt/misc.c. My suggestion was to commit without pg_proc.h, builtins.h and misc.c. For automatic logfile rotation, no function is needed.
I now separated the generic file functions in a separate file misc/adt/genfile.c. syslogger.c/h are still unchanged, appended for convenience.
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 17:26:06 -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 17:26:07 -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 17:26:12 -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/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/Makefile,v
retrieving revision 1.57
diff -u -r1.57 Makefile
--- src/backend/utils/adt/Makefile 1 Apr 2004 21:28:45 -0000 1.57
+++ src/backend/utils/adt/Makefile 21 Jul 2004 17:26:13 -0000
@@ -24,7 +24,7 @@
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_net_ntop.o inet_net_pton.o \
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
- ascii.o quote.o pgstatfuncs.o encode.o
+ ascii.o quote.o pgstatfuncs.o encode.o genfile.o
like.o: like.c like_match.c
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 17:26:13 -0000
@@ -26,6 +26,7 @@
#include "funcapi.h"
#include "catalog/pg_type.h"
#include "catalog/pg_tablespace.h"
+#include "postmaster/syslogger.h"
/*
@@ -109,18 +110,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 +150,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 +184,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 +222,139 @@
FreeDir(fctx->dirdesc);
SRF_RETURN_DONE(funcctx);
}
+
+
+/*
+ * logfile handling functions
+ */
+
+
+Datum pg_logfile_rotate(PG_FUNCTION_ARGS)
+{
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("only superuser can issue a logfile rotation command"))));
+
+ PG_RETURN_BOOL(LogFileRotate());
+}
+
+
+Datum pg_logdir_ls(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ struct dirent *de;
+ directory_fctx *fctx;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("only superuser can list the log directory"))));
+
+ 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 17:26:16 -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 17:26:26 -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 17:26:26 -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 17:26:41 -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,30 @@
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");
+/* logfile functions */
+DATA(insert OID = 2244( 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 = 2245( pg_logdir_ls PGNSP PGUID 12 f f t t v 0 2249 "" _null_ pg_logdir_ls - _null_ ));
+DESCR("list all available log files");
+
+/* generic file functions */
+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_length PGNSP PGUID 14 f f t f v 1 20 "25" _null_ "SELECT len FROM pg_file_stat($1) AS s(len int8, c timestamp, a timestamp, m timestamp, d bool)" - _null_ ));
+DESCR("length of a generic file");
+DATA(insert OID = 2559( 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 = 2560( 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 = 2561( pg_file_rename PGNSP PGUID 12 f f t f v 2 16 "25 25 25" _null_ pg_file_rename - _null_ ));
+DESCR("rename generic file");
+DATA(insert OID = 2562( pg_file_rename PGNSP PGUID 14 f f t f v 2 16 "25 25" _null_ "SELECT pg_file_rename($1,$2,NULL)" - _null_ ));
+DESCR("rename generic file");
+DATA(insert OID = 2563( 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 = 2564( pg_dir_ls PGNSP PGUID 12 f f t t v 2 25 "25 16" _null_ pg_dir_ls - _null_ ));
+DESCR("list generic directory");
/*
* 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 17:26:42 -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 17:26:44 -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 17:26:44 -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
/*------------------------------------------------------------------------- * * genfile.c * * * Copyright (c) 2004, PostgreSQL Global Development Group * * Author: Andreas Pflug <[EMAIL PROTECTED]> * * IDENTIFICATION * $PostgreSQL: $ * *------------------------------------------------------------------------- */ #include "postgres.h"
#include <sys/file.h>
#include <unistd.h>
#include <dirent.h>
#include "miscadmin.h"
#include "storage/fd.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
#include "funcapi.h"
typedef struct
{
char *location;
DIR *dirdesc;
} directory_fctx;
/*-----------------------
* 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"))));
}
/* ------------------------------------
* 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)));
strcpy(lenbuf, "-1");
strcpy(cbuf, "NULL");
strcpy(abuf, "NULL");
strcpy(mbuf, "NULL");
}
else
{
length = fst.st_size;
snprintf(lenbuf, 30, INT64_FORMAT, length);
timestamp = fst.st_ctime;
pg_strftime(cbuf, 30, "%F %T", pg_localtime(×tamp));
timestamp = fst.st_atime;
pg_strftime(abuf, 30, "%F %T", pg_localtime(×tamp));
timestamp = fst.st_mtime;
pg_strftime(mbuf, 30, "%F %T", pg_localtime(×tamp));
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);
}
/*-------------------------------------------------------------------------
*
* syslogger.c
*
* The system logger (syslogger) is new in Postgres 7.5. It catches all
* stderr output from backends, the postmaster and subprocesses by
* redirecting to a pipe, and writes it to a logfile and stderr if
* configured.
* It's possible to have size and age limits for the logfile configured
* in postgresql.conf. If these limits are reached or passed, the
* current logfile is closed and a new one is created (rotated).
* The logfiles are stored in a subdirectory (configurable in
* postgresql.conf), using an internal naming scheme that mangles
* creation time and current postmaster pid.
*
* Author: Andreas Pflug <[EMAIL PROTECTED]>
*
* Copyright (c) 2004, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* $PostgreSQL: $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "storage/pmsignal.h"
#include "storage/pg_shmem.h"
#include "storage/ipc.h"
#include "postmaster/syslogger.h"
#include "utils/ps_status.h"
#include "utils/guc.h"
/*
* GUC parameters
*/
int Log_RotationAge = 24*60;
int Log_RotationSize = 10*1024;
char * Log_directory = "pg_log";
char * Log_filename_prefix = "postgresql_";
/*
* Flags set by interrupt handlers for later service in the main loop.
*/
static volatile sig_atomic_t got_SIGHUP = false;
static volatile sig_atomic_t rotation_requested = false;
static pg_time_t last_rotation_time = 0;
static void sigHupHandler(SIGNAL_ARGS);
static void rotationHandler(SIGNAL_ARGS);
#ifdef EXEC_BACKEND
static pid_t syslogger_forkexec();
#endif
static char* logfile_getname(pg_time_t timestamp);
static bool logfile_rotate(void);
FILE *realStdErr = NULL;
FILE *syslogFile = NULL;
int syslogPipe[2] = {0, 0};
/*
* Main entry point for syslogger process
* argc/argv parameters are valid only in EXEC_BACKEND case.
*/
void
SysLoggerMain(int argc, char *argv[])
{
IsUnderPostmaster = true;
MyProcPid = getpid();
init_ps_display("system logger process", "", "");
set_ps_display("");
#ifdef EXEC_BACKEND
Assert(argc == 6);
argv += 3;
StrNCpy(postgres_exec_path, argv++, MAXPGPATH);
syslogPipe[0] = atoi(argv++);
syslogPipe[1] = atoi(argv);
#endif
/*
* Properly accept or ignore signals the postmaster might send us
*
* Note: we ignore all termination signals, and wait for the postmaster
* to die to catch as much pipe output as possible.
*/
pqsignal(SIGHUP, sigHupHandler); /* set flag to read config file */
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGTERM, SIG_IGN);
pqsignal(SIGQUIT, SIG_IGN);
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, rotationHandler); /* request log rotation */
pqsignal(SIGUSR2, SIG_IGN);
/*
* Reset some signals that are accepted by postmaster but not here
*/
pqsignal(SIGCHLD, SIG_DFL);
pqsignal(SIGTTIN, SIG_DFL);
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
PG_SETMASK(&UnBlockSig);
/*
* if we restarted, our stderr is redirected.
* Direct it back to system stderr.
*/
if (realStdErr != NULL)
{
if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
{
char *errstr = strerror(errno);
/*
* Now we have a real problem: we can't redirect to stderr,
* and can't ereport it correctly (it would go into our queue
* which will never be read
* We're writing everywhere, hoping to leave at least some
* hint of what happened.
*/
fprintf(realStdErr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
fprintf(stderr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
ereport(PANIC,
(errcode_for_file_access(),
(errmsg("Syslogger couldn't redirect its stderr to the saved stderr: %s", errstr))));
exit(1);
}
realStdErr = NULL;
}
/* we'll never write that pipe */
close(syslogPipe[1]);
syslogPipe[1] = 0;
/* remember age of initial logfile */
last_rotation_time = time(NULL);
/* main worker loop */
for (;;)
{
pg_time_t now;
int elapsed_secs;
char logbuffer[1024];
char bytesRead;
fd_set rfds;
struct timeval timeout;
int rc;
if (got_SIGHUP)
{
char *olddir=pstrdup(Log_directory);
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
/*
* check if the log directory changed
* in postgresql.conf. If so, we rotate to make sure
* we're writing the logfiles where the backends
* expect us to do so.
*/
if (strcmp(Log_directory, olddir))
rotation_requested = true;
pfree(olddir);
}
if (!rotation_requested && last_rotation_time != 0 && Log_RotationAge > 0)
{
/*
* Do an unforced rotation if too much time has elapsed
* since the last one.
*/
now = time(NULL);
elapsed_secs = now - last_rotation_time;
if (elapsed_secs >= Log_RotationAge * 60)
rotation_requested = true;
}
if (!rotation_requested && Log_RotationSize > 0)
{
/*
* Do an unforced rotation if file is too big
*/
if (ftell(syslogFile) >= Log_RotationSize * 1024)
rotation_requested = true;
}
if (rotation_requested)
{
if (!logfile_rotate())
{
ereport(ERROR,
(errcode_for_file_access(),
(errmsg("logfile rotation failed, disabling auto rotation (SIGHUP to reenable): %m"))));
Log_RotationAge = 0;
Log_RotationSize = 0;
}
rotation_requested = false;
}
FD_ZERO(&rfds);
FD_SET(syslogPipe[0], &rfds);
timeout.tv_sec=1;
timeout.tv_usec=0;
/*
* Check if data is present
*/
rc = select(syslogPipe[0]+1, &rfds, NULL, NULL, &timeout);
PG_SETMASK(&UnBlockSig);
if (rc > 0 && FD_ISSET(syslogPipe[0], &rfds))
{
bytesRead = piperead(syslogPipe[0],
logbuffer, sizeof(logbuffer));
if (bytesRead > 0)
{
if (fwrite(logbuffer, 1, bytesRead, syslogFile) < 1)
{
ereport(COMMERROR,
(errcode_for_file_access(),
errmsg("fwrite to logfile failed in system logger: %m")));
exit(1);
}
fflush(syslogFile);
if (Log_destination & LOG_DESTINATION_STDERR)
{
fwrite(logbuffer, 1, bytesRead, stderr);
fflush(stderr);
}
continue;
}
else if (bytesRead < 0 && errno != EINTR)
{
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("could not read from system logger pipe: %m")));
exit(1);
}
}
if (rc < 0 && errno != EINTR)
{
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("select() failed in system logger: %m")));
exit(1);
}
/*
* If postmaster died, there's nothing to log any more.
* We check this only after pipe timeouts to receive as much as possible
* from the pipe.
*/
if (!PostmasterIsAlive(true))
{
if (syslogFile)
fclose(syslogFile);
exit(1);
}
}
}
int
SysLogger_Start(void)
{
pid_t sysloggerPid;
pg_time_t now;
char *filename;
if (!(Log_destination & LOG_DESTINATION_FILE))
return 0;
/* create the pipe which will receive stderr output */
if (!syslogPipe[0])
{
if (pgpipe(syslogPipe) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("pipe for syslogging not created: %m"))));
if (!set_noblock(syslogPipe[1]))
{
ereport(FATAL,
(errcode_for_socket_access(),
errmsg("could not set syslogging pipe to nonblocking mode: %m")));
}
}
now = time(NULL);
/*
* The initial logfile is created right in the postmaster,
* to insure that the logger process has a writable file.
*/
filename = logfile_getname(now);
/*
* The file is opened for appending, in case the syslogger
* is restarted right after a rotation.
*/
syslogFile = fopen(filename, "a+");
if (!syslogFile)
{
/*
* if we can't open the syslog file for the syslogger process,
* we try to redirect stderr back to have some logging.
*/
ereport(WARNING,
(errcode_for_file_access(),
(errmsg("error opening syslog file %s: %m", filename))));
if (realStdErr != NULL)
{
if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("error redirecting stderr to default: %m"))));
ereport(FATAL,
(errmsg("logfile output corrupted")));
}
}
pfree(filename);
fflush(stdout);
fflush(stderr);
#ifdef __BEOS__
/* Specific beos actions before backend startup */
beos_before_backend_startup();
#endif
#ifdef EXEC_BACKEND
switch ((sysloggerPid = syslogger_forkexec()))
#else
switch ((sysloggerPid = fork()))
#endif
{
case -1:
#ifdef __BEOS__
/* Specific beos actions */
beos_backend_startup_failed();
#endif
ereport(LOG,
(errmsg("could not fork system logger: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* in postmaster child ... */
#ifdef __BEOS__
/* Specific beos actions after backend startup */
beos_backend_startup();
#endif
/* Close the postmaster's sockets */
ClosePostmasterPorts();
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
/* do the work */
SysLoggerMain(0, NULL);
break;
#endif
default:
/* now we redirect stderr, if not done already */
if (realStdErr == NULL)
{
int dh= dup(fileno(stderr));
if (dh < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stderr duplication failed: %m"))));
realStdErr = fdopen(dh, "a");
if (realStdErr == NULL)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("realstderr reopen failed: %m"))));
if (dup2(syslogPipe[1], fileno(stdout)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stdout pipe redirection failed: %m"))));
if (dup2(syslogPipe[1], fileno(stderr)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stderr pipe redirection failed: %m"))));
}
/* postmaster will never write the file; close it */
fclose(syslogFile);
syslogFile = NULL;
return (int) sysloggerPid;
}
/* we should never reach here */
return 0;
}
#ifdef EXEC_BACKEND
static pid_t
syslogger_forkexec()
{
char *av[10];
int ac = 0, bufc = 0, i;
char numbuf[2][32];
av[ac++] = "postgres";
av[ac++] = "-forklog";
av[ac++] = NULL; /* filled in by postmaster_forkexec */
/* postgres_exec_path is not passed by write_backend_variables */
av[ac++] = postgres_exec_path;
/* Pipe file ids (those not passed by write_backend_variables) */
snprintf(numbuf[bufc++],32,"%d",syslogPipe[0]);
snprintf(numbuf[bufc++],32,"%d",syslogPipe[1]);
/* Add to the arg list */
Assert(bufc <= lengthof(pgstatBuf));
for (i = 0; i < bufc; i++)
av[ac++] = numbuf[i];
av[ac] = NULL;
Assert(ac < lengthof(av));
return postmaster_forkexec(ac, av);
}
#endif
/* --------------------------------
* logfile routines
* --------------------------------
*/
/*
* perform rotation
*/
bool
logfile_rotate(void)
{
char *filename;
pg_time_t now;
FILE *fh;
now = time(NULL);
filename = logfile_getname(now);
fh = fopen(filename, "a+");
if (!fh)
{
/*
* if opening the new file fails, the caller is responsible
* for taking consequences. */
pfree(filename);
return false;
}
fclose(syslogFile);
syslogFile = fh;
last_rotation_time = now;
/* official opening of the new logfile */
ereport(NOTICE,
(errcode(ERRCODE_WARNING),
errmsg("Opened new log file %s", filename)));
pfree(filename);
return true;
}
/*
* creates logfile name using timestamp information
*/
#define TIMESTAMPPATTERN "%Y-%m-%d_%H%M%S"
static char*
logfile_getname(pg_time_t timestamp)
{
char *filetemplate;
char *filename;
if (is_absolute_path(Log_directory))
{
filetemplate = palloc(strlen(Log_directory)
+ strlen(Log_filename_prefix)
+ sizeof(TIMESTAMPPATTERN)+10 +2);
if (filetemplate)
sprintf(filetemplate, "%s/%s%s_%05d.log",
Log_directory, Log_filename_prefix,
TIMESTAMPPATTERN, PostmasterPid);
}
else
{
filetemplate = palloc(strlen(DataDir) + strlen(Log_directory)
+ strlen(Log_filename_prefix)
+ sizeof(TIMESTAMPPATTERN) +10 +3);
if (filetemplate)
sprintf(filetemplate, "%s/%s/%s%s_%05d.log",
DataDir, Log_directory, Log_filename_prefix,
TIMESTAMPPATTERN, PostmasterPid);
}
filename = palloc(MAXPGPATH);
if (!filename || !filetemplate)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("Out of memory")));
pg_strftime(filename, MAXPGPATH, filetemplate, pg_localtime(×tamp));
pfree(filetemplate);
return filename;
}
/* --------------------------------
* API helper routines
* --------------------------------
*/
/*
* Rotate log file
*/
bool
LogFileRotate(void)
{
if (!(Log_destination & LOG_DESTINATION_FILE))
{
ereport(NOTICE,
(errcode(ERRCODE_WARNING),
errmsg("no logfile configured; rotation not supported")));
return false;
}
SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
return true;
}
/* --------------------------------
* signal handler routines
* --------------------------------
*/
/* SIGHUP: set flag to reload config file */
static void
sigHupHandler(SIGNAL_ARGS)
{
got_SIGHUP = true;
}
/* SIGUSR1: set flag to rotate logfile */
static void
rotationHandler(SIGNAL_ARGS)
{
rotation_requested = true;
}
/*------------------------------------------------------------------------- * * syslogger.h * Exports from postmaster/syslogger.c. * * Portions Copyright (c) 2004, PostgreSQL Global Development Group * * $PostgreSQL: $ * *------------------------------------------------------------------------- */ #ifndef _SYSLOGGER_H #define _SYSLOGGER_H #include "pgtime.h" /* GUC options */ extern int Log_RotationAge; extern int Log_RotationSize; extern char * Log_directory; extern char * Log_filename_prefix; int SysLogger_Start(void); void SysLoggerMain(int argc, char *argv[]); extern bool LogFileRotate(void); #endif /* _SYSLOGGER_H */
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])
