/*
 *	cinit.c		- initialize the C global variables
 *
 *	Initializes global variables that are needed by the access
 *	method code.  This is loaded by lisp and the catalog creation
 *	support routines.
 *
 * Note:
 *	This file should someday contain all the functions which should be
 *	called at initialization, uninitialization, and recovery time.
 */
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "master.h"
#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
#include "postgres.h"
#include "log.h"
#include "context.h"

#include "c.h"

#include "bufmgr.h"
#include "catname.h"
#include "heapam.h"
#include "hrnd.h"
#include "htup.h"
#include "ipc.h"
#include "ipci.h"
#include "lmgr.h"
#include "log.h"
#include "rel.h"
#include "relscan.h"
#include "rproc.h"
#include "sinval.h"
#include "sinvaladt.h"
#include "skey.h"
#include "tqual.h"
#include "xcxt.h"

RcsId("$Header: cinit.c,v 1.2 89/02/10 09:21:12 goh Exp $");

/*
 *	cinit
 *
 *	Returns 1 iff this is a non-interactive backend.  Otherwise,
 *	-1 is returned and path is filled with the directory to
 *	chdir() into before calling the access method functions.
 *	0 is returned if an error occurs.
 *
 *	Bugs:
 *		Cinit currently returns a string iff file descriptor
 *	number 4 has been defined.  In general, this indicates that
 *	the current working directory for the postgres backend has
 *	been preset by the postmaster.  Otherwise, it is assumed that
 *	the current working directory needs to be set by hand.
 *
 *	File descriptors are set by the postmaster if we are started
 *	by the postmaster.  See support/postmaster.c
 */

int	Debugfile, Ttyfile, Dblog, Slog, Portfd, Packfd, Slog, Pipefd;
BackendId	MyBackendId;
BackendTag	MyBackendTag;
static NameData	MyDatabaseNameData;
Name		MyDatabaseName = &MyDatabaseNameData;
bool		MyDatabaseIdIsInitialized = false;
ObjectId	MyDatabaseId = InvalidObjectId;
bool		TransactionInitWasProcessed = false;

struct	bcommon	Ident;

static bool	IsUnderPostmaster = false;

#ifndef	private
#ifndef	EBUG
#define	private	static
#else	/* !defined(EBUG) */
#define private
#endif	/* !defined(EBUG) */
#endif	/* !defined(private) */

/*
 * InitCommunication --
 *	Initializes the shared memory and semaphores for inter-backend communication.
 */
private
void
InitCommunication ARGS((
	void
));

/*
 * InitSystemCaches --
 *	Initializes the various system caches.
 */
private
void
InitSystemCaches ARGS((
	void
));

/*
 * InitMyDatabaseId --
 *	Initializes this backend's database identifier global variable.
 *
 * Note:
 *	Assumes MyDatabaseName is valid.
 */
private
void
InitMyDatabaseId ARGS((
	void
));

/*
 * ReinitAtFirstTransaction --
 *	Reprocesses the "at first transaction" initialization.
 */
private
void
ReinitAtFirstTransaction ARGS((
	void
));

int
cinit(path, database, xid)
char	path[], database[];
XID	*xid;
{
	char		*home;
	int		i;
	struct stat	statbuf;
	extern		Noversion;
	extern		Err_file;
	extern		Debug_file;
	extern		isatty();
	extern		bcopy(), setbuf();
	extern		initXact(), initmmgr(), initfmgr(), initcaches();
	extern int	ValidPgVersion();
	extern char	*GetDataHome();

	ElogDebugIndentLevel = 0;

	IsUnderPostmaster = (bool)(fcntl(3, F_GETFD, 0) >= 0);
	/* && errno == EBADF */

	strncpy(&MyDatabaseNameData, database, 16);

	setuid(geteuid());	/* who cares if it succeeds */

	InitCommunication();

	Ttyfile = 0;
	Debugfile = Debug_file = DebugFileOpen();

	initXact(xid);	/* obsolete? */
	BufferManagerInit();
	initmmgr();
	initfmgr();
	initcaches();

	if (IsUnderPostmaster) {
		struct	dpacket	pack;

		if (!ValidPgVersion(".") || !ValidPgVersion("../.."))
			elog(FATAL, "");
		Slog = Err_file = 3;
		Portfd = 4;
		Packfd = 5;
		Dblog = 6;
		Pipefd = 7;
		pinit();
		read(Packfd, (char *)&pack, sizeof(pack));
		bcopy(pack.data, (char *)&Ident, sizeof(Ident));
		return(1);
	} 
	if (!isatty(fileno(stdout)) && !isatty(fileno(stderr))) {
		setbuf(stdout, (char *)NULL);
		setbuf(stderr, (char *)NULL);
	}
	Dblog = dup(Debugfile);
	Slog = Err_file = ErrorFileOpen();
	home = GetDataHome();
	sprintf(path, "%s/data\0", home);
	i = ValidPgVersion(path);
	sprintf(path, "%s/data/base/%s\0", home, database);

	i = ValidPgVersion(path) || i;

	if (stat(path, &statbuf) < 0)
		elog(FATAL, "database %s does not exist, bailing out...",
		     database);
	return((i || Noversion) ? -1 : 0);
}

bool
cinit2()
{
	LockingIsDisabled = true;
	RelationInitialize();
	InitializeTransactionSystem();
	LockingIsDisabled = false;

	InitSharedInvalidationState();

	if (MyBackendId > MaxBackendId || MyBackendId <= 0) {
		elog(FATAL, "cinit2: bad backend id %d (%d)", MyBackendTag,
			MyBackendId);
	}

	InitRandom();

	return (true);
}

void
InitAtFirstTransaction()
{
	if (TransactionInitWasProcessed) {
		ReinitAtFirstTransaction();
	}

	InitSystemCaches();

	InitMyDatabaseId();
	/* Walk the relcache? */

	TransactionInitWasProcessed = true;	/* XXX ...InProgress also? */
}

/*
 * Note:
 *	This does not set MyBackendId.  MyBackendTag is set, however.
 */
private
void
InitCommunication()
{
	String	string;
	String	getenv();	/* XXX style */

	MyBackendId = -1;

	string = getenv("POSTID");
	if (!PointerIsValid(string)) {
		MyBackendTag = -1;
	} else {
		MyBackendTag = atoi(string);

		Assert(MyBackendTag >= 0);
	}

#ifndef	PRODUCTION
	string = getenv("POSTPORT");
	if (PointerIsValid(string)) {
		SystemPortAddress	address = atoi(string);

		if (address == 0) {
			elog(FATAL, "InitCommunication: invalid POSTPORT");
		}

		if (MyBackendTag == -1) {
			elog(FATAL, "InitCommunication: missing POSTID");
		}

		AttachSharedMemoryAndSemaphores(
			SystemPortAddressCreateIPCKey(address));
	} else if (IsUnderPostmaster) {
		elog(FATAL, "cinit2: missing POSTPORT");
	} else {
		if (MyBackendTag == -1) {
			MyBackendTag = 1;
		}
		AttachSharedMemoryAndSemaphores(PrivateIPCKey);
	}
#else	/* defined(PRODUCTION) */
	Assert(0);	/* XXX the following code needs to be fixed someday */
	if (MyBackendTag == -1) {
		elog(FATAL, "cinit2: missing POSTPORT");
	}
	AttachSharedMemoryAndSemaphores(DefaultMemoryKey);
#endif	/* defined(PRODUCTION) */
}

private
void
InitSystemCaches()
{
	InitCatalogCache();
}

private
void
InitMyDatabaseId()
{
	Relation	relation;
	HeapScan	scan;
	HeapTuple	tuple;
	ScanKeyData	keyData;
	extern bool	AMI_OVERRIDE;

	Assert(!LockingIsDisabled);
	Assert(!ObjectIdIsValid(MyDatabaseId));
	Assert(IsTransactionState());

	LockingIsDisabled = true;

	if (AMI_OVERRIDE) {
		MyDatabaseIdIsInitialized = true;
		return;
	}

	relation = amopenr(DatabaseRelationName);
	keyData.data[0].flags = 0;
	keyData.data[0].attributeNumber = 1;
	keyData.data[0].procedure = NameEqualRegProcedure;
	keyData.data[0].argument = NameGetDatum(MyDatabaseName);

	scan = ambeginscan(relation, 0, NowTimeQual, 1, &keyData.data[0]);
	tuple = amgetnext(scan, 0, (Buffer *)NULL);
	if (HeapTupleIsValid(tuple)) {
		MyDatabaseIdIsInitialized = true;
		MyDatabaseId = tuple->t_oid;
	}
	amendscan(scan);
	amclose(relation);

	if (ObjectIdIsValid(MyDatabaseId)) {
		LockingIsDisabled = false;
	} else {
		LockingIsDisabled = true;
#ifdef	LOCKDEBUG
		elog(NOTICE, "InitMyDatabaseId: locking disabled on %.16s",
			MyDatabaseName);
#endif	/* defined(LOCKDEBUG) */
	}
}

private
void
ReinitAtFirstTransaction()
{
	elog(FATAL, "ReinitAtFirstTransaction: not implemented, yet");
}

void
StatusBackendExit(status)
	int	status;
{
	/* someday, do some real cleanup and then call the LISP exit */
	/* someday, call StatusPostmasterExit if running without postmaster */
	exit(status);
}

void
StatusPostmasterExit(status)
	int	status;
{
	/* someday, do some real cleanup and then call the LISP exit */
	exit(status);
}
