Log Message:
-----------
Move admin pack into the main tree for ease of distribution.

Added Files:
-----------
    pgadmin3/xtra/admin:
        Makefile (r1.1)
        README.admin (r1.1)
        admin.sql.in (r1.1)
        genfile.c (r1.1)
        misc.c (r1.1)

--- /dev/null
+++ xtra/admin/admin.sql.in
@@ -0,0 +1,63 @@
+/* **********************************
+ * Administrative functions
+ * ********************************* */
+
+/* generic file access functions (genfile.c) */
+
+CREATE FUNCTION pg_file_stat(text) RETURNS record
+   AS 'MODULE_PATHNAME', 'pg_file_stat'
+       LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION pg_file_length(text) RETURNS bigint
+   AS 'SELECT len FROM pg_file_stat($1) AS s(len int8, c timestamp, a 
timestamp, m timestamp, i bool)'
+       LANGUAGE SQL VOLATILE STRICT;
+
+CREATE FUNCTION pg_file_read(text, bigint, bigint) RETURNS text
+   AS 'MODULE_PATHNAME', 'pg_file_read'
+       LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION pg_file_write(text, text, bool) RETURNS bigint
+   AS 'MODULE_PATHNAME', 'pg_file_write'
+       LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION pg_file_rename(text, text, text) RETURNS bool
+   AS 'MODULE_PATHNAME', 'pg_file_rename'
+       LANGUAGE C VOLATILE;
+
+CREATE FUNCTION pg_file_unlink(text) RETURNS bool
+   AS 'MODULE_PATHNAME', 'pg_file_unlink'
+       LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION pg_file_rename(text, text) RETURNS bool
+   AS 'SELECT pg_file_rename($1, $2, NULL); '
+       LANGUAGE SQL VOLATILE STRICT;
+
+CREATE FUNCTION pg_dir_ls(text, bool) RETURNS setof text
+   AS 'MODULE_PATHNAME', 'pg_dir_ls'
+       LANGUAGE C VOLATILE STRICT;
+
+
+/* Miscellaneous functions (misc.c) */
+
+CREATE FUNCTION pg_reload_conf() RETURNS int4
+   AS 'MODULE_PATHNAME', 'pg_reload_conf'
+       LANGUAGE C STABLE STRICT;
+
+/*
+CREATE FUNCTION pg_logfile_rotate() RETURNS bool
+   AS 'MODULE_PATHNAME', 'pg_logfile_rotate'
+       LANGUAGE C STABLE STRICT;
+*/
+
+CREATE FUNCTION pg_postmaster_starttime() RETURNS timestamp
+   AS 'MODULE_PATHNAME', 'pg_postmaster_starttime'
+       LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION pg_logdir_ls() RETURNS setof record
+   AS 'MODULE_PATHNAME', 'pg_logdir_ls'
+       LANGUAGE C VOLATILE STRICT;
+
+CREATE VIEW pg_logdir_ls AS
+       SELECT *
+       FROM pg_logdir_ls() AS A
+       (filetime timestamp, filename text);
--- /dev/null
+++ xtra/admin/genfile.c
@@ -0,0 +1,476 @@
+/*-------------------------------------------------------------------------
+ *
+ * genfile.c
+ *
+ *
+ * Copyright (c) 2004 - 2005, PostgreSQL Global Development Group
+ * 
+ * Author: Andreas Pflug <[EMAIL PROTECTED]>
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL: $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "miscadmin.h"
+#include "storage/fd.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
+
+
+#ifdef WIN32
+
+#ifdef rename
+#undef rename
+#endif
+
+#ifdef unlink
+#undef unlink
+#endif
+
+#endif
+
+extern DLLIMPORT char *DataDir;
+extern DLLIMPORT char *Log_directory;
+extern DLLIMPORT char *Log_filename_prefix;
+
+
+Datum pg_file_stat(PG_FUNCTION_ARGS);
+Datum pg_file_read(PG_FUNCTION_ARGS);
+Datum pg_file_write(PG_FUNCTION_ARGS);
+Datum pg_file_rename(PG_FUNCTION_ARGS);
+Datum pg_file_unlink(PG_FUNCTION_ARGS);
+Datum pg_dir_ls(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(pg_file_stat);
+PG_FUNCTION_INFO_V1(pg_file_read);
+PG_FUNCTION_INFO_V1(pg_file_write);
+PG_FUNCTION_INFO_V1(pg_file_rename);
+PG_FUNCTION_INFO_V1(pg_file_unlink);
+PG_FUNCTION_INFO_V1(pg_dir_ls);
+
+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, bool logAllowed)
+{
+       char *filename;
+       int len=VARSIZE(arg) - VARHDRSZ;
+       int dlen = strlen(DataDir);
+
+       filename = palloc(len+1);
+       memcpy(filename, VARDATA(arg), len);
+       filename[len] = 0;
+
+       if (strstr(filename, "..") != NULL)
+         ereport(ERROR,
+                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                          (errmsg("No .. allowed in filenames"))));
+       
+       if (is_absolute_path(filename))
+       {
+           if (logAllowed && !strncmp(filename, Log_directory, 
strlen(Log_directory)))
+                   return filename;
+               if (strncmp(filename, DataDir, dlen))
+                   ereport(ERROR,
+                                       
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                        (errmsg("Absolute path not 
allowed"))));
+
+               return filename;
+       }
+       else
+       {
+           char *absname = palloc(dlen+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), true);
+       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(&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), true);
+       pos = PG_GETARG_INT64(1);
+       size = PG_GETARG_INT64(2);
+
+       f = fopen(filename, "rb");
+       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), false);
+       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, "wb");
+       }
+       else
+           f = fopen(filename, "ab");
+
+       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) - VARHDRSZ)
+                   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();
+
+       if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
+               PG_RETURN_NULL();
+
+       fn1=absClusterPath(PG_GETARG_TEXT_P(0), false);
+       fn2=absClusterPath(PG_GETARG_TEXT_P(1), false);
+       if (PG_ARGISNULL(2))
+           fn3=0;
+       else
+           fn3=absClusterPath(PG_GETARG_TEXT_P(2), false);
+
+       if (access(fn1, W_OK) < 0)
+       {
+               ereport(WARNING,
+                               (errcode_for_file_access(),
+                                errmsg("file %s not accessible: %m", fn1)));
+
+           PG_RETURN_BOOL(false);
+       }
+
+       if (fn3 && access(fn2, W_OK) < 0)
+       {
+               ereport(WARNING,
+                               (errcode_for_file_access(),
+                                errmsg("file %s not accessible: %m", fn2)));
+
+           PG_RETURN_BOOL(false);
+       }
+
+
+       rc = access(fn3 ? fn3 : fn2, 2);
+       if (rc >= 0 || errno != ENOENT)
+       {
+               ereport(ERROR,
+                               (ERRCODE_DUPLICATE_FILE,
+                                errmsg("cannot rename to target file %s", 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)));
+
+                       }
+               }
+       }
+       else if (rename(fn1, fn2) != 0)
+       {
+                       ereport(WARNING,
+                                       (errcode_for_file_access(),
+                                        errmsg("renaming %s to %s %m", fn1, 
fn2)));
+               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), false);
+
+       if (access(filename, W_OK) < 0)
+       {
+           if (errno == ENOENT)
+                   PG_RETURN_BOOL(false);
+               else
+                   ereport(ERROR,
+                                       (errcode_for_file_access(),
+                                        errmsg("file %s not accessible: %m", 
filename)));
+
+       }
+
+       if (unlink(filename) < 0)
+       {
+               ereport(WARNING,
+                               (errcode_for_file_access(),
+                                errmsg("could not unlink file %s: %m", 
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), false);
+
+               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);
+}
+
+
--- /dev/null
+++ xtra/admin/Makefile
@@ -0,0 +1,11 @@
+subdir = contrib/admin
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+
+MODULE_big = admin
+PG_CPPFLAGS = -I$(libpq_srcdir)
+DATA_built = admin.sql
+DOCS = README.admin
+OBJS = genfile.o misc.o
+
+include $(top_srcdir)/contrib/contrib-global.mk
--- /dev/null
+++ xtra/admin/misc.c
@@ -0,0 +1,303 @@
+/*
+ * admin.c
+ * miscellaneous administrative functions
+ *
+ * Copyright (c) 2004 - 2005, PostgreSQL Global Development Group
+ *
+ * Author: Andreas Pflug <[EMAIL PROTECTED]>
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL: $
+ *
+ */
+
+#include "postgres.h"
+
+#include <sys/file.h>
+#include <signal.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "commands/dbcommands.h"
+#include "miscadmin.h"
+#include "storage/sinval.h"
+#include "storage/fd.h"
+#include "funcapi.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_tablespace.h"
+#include "postmaster/syslogger.h"
+
+extern DLLIMPORT char *DataDir;
+extern DLLIMPORT char *Log_directory;
+extern DLLIMPORT char *Log_filename;
+extern DLLIMPORT pid_t PostmasterPid;
+
+Datum pg_reload_conf(PG_FUNCTION_ARGS);
+Datum pg_logfile_rotate(PG_FUNCTION_ARGS);
+Datum pg_logdir_ls(PG_FUNCTION_ARGS);
+Datum pg_postmaster_starttime(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(pg_reload_conf);
+PG_FUNCTION_INFO_V1(pg_logfile_rotate);
+PG_FUNCTION_INFO_V1(pg_logdir_ls);
+PG_FUNCTION_INFO_V1(pg_postmaster_starttime);
+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;
+} directory_fctx;
+
+
+/*
+ * logfile handling functions
+ */
+
+#if ROTATE_SUPPORTED
+/* not yet in backend */
+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());
+}
+
+#endif
+
+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 (memcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log", 30) != 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                (errmsg("the log_filename parameter must equal 
'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
+
+       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(2, false);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
+                                                  TIMESTAMPOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 2, "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[2];
+               HeapTuple tuple;
+            
+               char            *field[MAXDATEFIELDS];
+               char            lowstr[MAXDATELEN + 1];
+               int             dtype;
+               int             nf, ftype[MAXDATEFIELDS];
+               fsec_t          fsec;
+               int             tz = 0;
+               struct          pg_tm date;
+
+               /*
+                * Default format:
+                *        postgresql-YYYY-MM-DD_HHMMSS.log
+                */
+               if (strlen(de->d_name) != 32
+                   || memcmp(de->d_name, "postgresql-", 11)
+                       || de->d_name[21] != '_'
+                       || strcmp(de->d_name + 28, ".log"))
+                     continue;
+
+               values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) 
+ 2);
+               sprintf(values[1], "%s/%s", fctx->location, de->d_name);
+
+               values[0] = de->d_name + 11;       /* timestamp */
+               values[0][17] = 0;
+
+                    /* 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);
+}
+
+
+Datum pg_postmaster_starttime(PG_FUNCTION_ARGS)
+{
+    Timestamp result;
+    
+    if (!superuser()) 
+            ereport(ERROR,
+                            (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                             (errmsg("only superuser can query the postmaster 
starttime"))));
+
+#ifdef WIN32
+    {
+           struct pg_tm tm;
+        extern DLLIMPORT HANDLE PostmasterHandle;
+        FILETIME creationTime;
+        FILETIME exitTime, kernelTime, userTime;
+        SYSTEMTIME st;
+    
+        bool rc=GetProcessTimes(PostmasterHandle, &creationTime,
+            &exitTime, &kernelTime, &userTime);
+        if (!rc)
+            PG_RETURN_NULL();
+    
+        FileTimeToSystemTime(&creationTime, &st);
+    
+        tm.tm_year = st.wYear;
+        tm.tm_mon = st.wMonth;
+        tm.tm_mday = st.wDay;
+        tm.tm_hour = st.wHour;
+        tm.tm_min = st.wMinute;
+        tm.tm_sec = st.wSecond;
+
+               if (tm2timestamp(&tm, 0, NULL, &result) != 0)
+               {
+                       ereport(ERROR,
+            (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+            errmsg("postmaster timestamp out of range")));
+               }
+    }
+#else
+       {
+               FILE *fp;
+               char buffer[MAXPGPATH];
+           pg_time_t now = time(NULL);
+               struct pg_tm *tm = pg_localtime(&now);
+               double systemSeconds;
+               long procJiffies;
+               int i;
+
+               tm->tm_year += 1900;
+               tm->tm_mon += 1;
+
+               if (tm2timestamp(tm, 0, NULL, &result) != 0)
+               {
+                       ereport(ERROR,
+            (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+            errmsg("postmaster timestamp out of range")));
+               }
+
+               fp = fopen("/proc/uptime", "r");
+               if (!fp)
+                   ereport(ERROR,
+                                       (errcode_for_file_access(), 
+                                        errmsg("could not open /proc/uptime: 
%m")));
+
+               if (fscanf(fp, "%lf", &systemSeconds) != 1)
+                   ereport(ERROR,
+                                       (errcode_for_file_access(), 
+                                        errmsg("could not interpret 
/proc/uptime: %m")));
+
+               fclose(fp);
+
+               sprintf(buffer, "/proc/%u/stat", PostmasterPid);
+               fp = fopen(buffer, "r");
+               if (!fp)
+                   ereport(ERROR,
+                                       (errcode_for_file_access(), 
+                                        errmsg("could not open %s: %m", 
buffer)));
+
+
+#define PROC_STAT_POS 22
+
+               for (i=1 ; i < PROC_STAT_POS ; i++)
+               {
+                   char c;
+                       do
+                       {
+                               c = fgetc(fp);
+                       }
+                       while (c && c != ' ');
+               }
+
+               if (fscanf(fp, "%ld", &procJiffies) != 1)
+                   ereport(ERROR,
+                                       (errcode_for_file_access(), 
+                                        errmsg("could not interpret %s: %m", 
buffer)));
+
+               fclose(fp);
+
+#ifdef HAVE_INT64_TIMESTAMP
+               result += (procJiffies/100. - systemSeconds) / 
INT64CONST(1000000);
+#else
+               result += (procJiffies/100. - systemSeconds);
+#endif
+       }
+#endif
+
+
+    PG_RETURN_TIMESTAMP(result);
+}
--- /dev/null
+++ xtra/admin/README.admin
@@ -0,0 +1,48 @@
+PostgreSQL Administration Functions
+===================================
+
+This directory is a PostgreSQL 'contrib' module which implements a number of
+support functions which pgAdmin will use to provide additional functionality
+if installed on a server. 
+
+Installation
+============
+
+You will need a PostgreSQL source tree which has been properly configured
+for the local system using the 'configure' script.
+
+Copy this directory into the /contrib directory of the PostgreSQL source tree,
+and then run the following commands as a users with appropriate privileges:
+
+make
+make install
+
+pgAdmin will look for the functions in the Initial Database specified in the
+connection dialogue for the server. To install the functions in the database,
+either run the admin.sql script using the pgAdmin SQL tool (and then restart
+pgAdmin), or run the script using psql, eg:
+
+psql -u postgres template1 < admin.sql
+
+Objects implemented
+===================
+
+genfile.c (superuser only):
+-----------------------------
+record pg_file_stat(fname text)
+int8 pg_file_length(fname text)
+text pg_file_read(fname text, offs int8, len int8)
+int8 pg_file_write(fname text, data text, append bool)
+bool pg_file_rename(oldname text, newname text)
+bool pg_file_rename(oldname text, newname text, archivname text)
+bool pg_file_unlink(fname text)
+setof text pg_dir_ls(dirname text, bool fullpath)
+
+
+misc.c (superuser only):
+-----------------------------
+int4 pg_reload_conf()
+bool pg_logfile_rotate()
+setof record pg_logdir_ls()
+
+VIEW pg_logdir_ls(filetime timestamp, filename text)
---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faq

Reply via email to