/*
 * admin.c
 * miscellaneous administrative functions
 *
 * Copyright (c) 2004, PostgreSQL Global Development Group
 *
 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
 *
 * IDENTIFICATION
 *	  $PostgreSQL: $
 *
 */

#include "postgres.h"

#include <sys/file.h>
#include <signal.h>
#include <dirent.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"

Datum pg_reload_conf(PG_FUNCTION_ARGS);
Datum pg_logfile_rotate(PG_FUNCTION_ARGS);
Datum pg_logdir_ls(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(pg_reload_conf);
PG_FUNCTION_INFO_V1(pg_logfile_rotate);
PG_FUNCTION_INFO_V1(pg_logdir_ls);


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
 */


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);
}

