Re: logfile subprocess and Fancy File Functions - Mailing list pgsql-patches
From | Andreas Pflug |
---|---|
Subject | Re: logfile subprocess and Fancy File Functions |
Date | |
Msg-id | 40FEA918.1000705@pse-consulting.de Whole thread Raw |
In response to | Re: logfile subprocess and Fancy File Functions (Bruce Momjian <pgman@candle.pha.pa.us>) |
Responses |
Re: logfile subprocess and Fancy File Functions
|
List | pgsql-patches |
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 <pgadmin@pse-consulting.de> * * 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 <pgadmin@pse-consulting.de> * * Copyright (c) 2004, PostgreSQL Global Development Group * * * IDENTIFICATION * $PostgreSQL: $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include <signal.h> #include <time.h> #include <unistd.h> #include "libpq/pqsignal.h" #include "miscadmin.h" #include "postmaster/postmaster.h" #include "storage/pmsignal.h" #include "storage/pg_shmem.h" #include "storage/ipc.h" #include "postmaster/syslogger.h" #include "utils/ps_status.h" #include "utils/guc.h" /* * GUC parameters */ int Log_RotationAge = 24*60; int Log_RotationSize = 10*1024; char * Log_directory = "pg_log"; char * Log_filename_prefix = "postgresql_"; /* * 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 */
pgsql-patches by date: