/* ----------------------------------------------------------------
 *   FILE
 *	postinit.c
 *	
 *   DESCRIPTION
 *	postgres initialization utilities
 *
 *   INTERFACE ROUTINES
 *	InitPostgres()
 *
 *   NOTES
 *	InitializePostgres() is the function called from PostgresMain
 *	which does all non-trival initialization, mainly by calling
 *	all the other initialization functions.  InitializePostgres()
 *	is only used within the "postgres" backend and so that routine
 *	is in tcop/postgres.c  InitPostgres() is needed in cinterface.a
 *	because things like the bootstrap backend program need it. Hence
 *	you find that in this file...
 *
 *	If you feel the need to add more initialization code, it should be
 *	done in InitializePostgres() or someplace lower.  Do not start
 *	putting stuff in PostgresMain - if you do then someone
 *	will have to clean it up later, and it's not going to be me!
 *	-cim 10/3/90
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/utils/init/RCS/postinit.c,v 1.35 1992/08/03 17:42:01 mao Exp $
 * ----------------------------------------------------------------
 */

#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
#include <sys/file.h>
#include <sys/types.h>
#include <math.h>

#include "tmp/postgres.h"

#include "machine.h"		/* for BLCKSZ, for InitMyDatabaseId() */

#include "access/heapam.h"
#include "access/tqual.h"
#include "storage/bufpage.h"	/* for page layout, for InitMyDatabaseId() */
#include "storage/sinval.h"
#include "storage/sinvaladt.h"
#include "storage/lmgr.h"
#include "storage/bufmgr.h"
#include "support/master.h"

#include "tmp/miscadmin.h"
#include "tmp/portal.h"		/* for EnablePortalManager, etc. */

#include "utils/exc.h"		/* for EnableExceptionHandling, etc. */
#include "utils/fmgr.h"		/* for EnableDynamicFunctionManager, etc. */
#include "utils/log.h"
#include "utils/mcxt.h"		/* for EnableMemoryContext, etc. */

#include "catalog/catname.h"
#include "catalog/pg_database.h"

#include "tmp/miscadmin.h"

/* ----------------
 *	global variables & stuff
 *	XXX clean this up! -cim 10/5/90
 * ----------------
 */
    
extern bool	override;
extern int	Quiet;
IPCKey		PostgresIpcKey;

/*
 * XXX PostgresPath and PostgresDatabase belong somewhere else.
 */
String	PostgresPath = NULL;
String	PostgresDatabase = NULL;

extern int Debugfile, Ttyfile;
extern int Portfd, Packfd, Pipefd;

extern BackendId	MyBackendId;
extern BackendTag	MyBackendTag;
extern NameData		MyDatabaseNameData;
extern Name		MyDatabaseName;

extern bool		MyDatabaseIdIsInitialized;
extern ObjectId		MyDatabaseId;
extern bool		TransactionInitWasProcessed;

/* extern struct	bcommon	Ident;	No longer needed */

extern int	Debug_file, Err_file, Noversion;

extern int on_exitpg();

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

/* ----------------------------------------------------------------
 *			InitPostgres support
 * ----------------------------------------------------------------
 */

/* --------------------------------
 *  InitMyDatabaseId() -- Find and record the OID of the database we are
 *			  to open.
 *
 *	The database's oid forms half of the unique key for the system
 *	caches and lock tables.  We therefore want it initialized before
 *	we open any relations, since opening relations puts things in the
 *	cache.  To get around this problem, this code opens and scans the
 *	pg_database relation by hand.
 *
 *	This algorithm relies on the fact that first attribute in the
 *	pg_database relation schema is the database name.  It also knows
 *	about the internal format of tuples on disk and the length of
 *	the datname attribute.  It knows the location of the pg_database
 *	file.
 *
 *	This code is called from InitDatabase(), after we chdir() to the
 *	database directory but before we open any relations.
 * --------------------------------
 */

void
InitMyDatabaseId()
{
    int		dbfd;
    int		nbytes;
    int		max, i;
    HeapTuple	tup;
    Page	pg;
    PageHeader	ph;
    Form_pg_database	tup_db;

    /* ----------------
     *  If we're unable to open pg_database, we're not running in the
     *  data/base/dbname directory.  This happens only when we're
     *  creating the first database.  In that case, just use
     *  InvalidObjectId as the dbid, since we're not using shared memory.
     * ----------------
     */
    if ((dbfd = open("../../pg_database", O_RDONLY, 0666)) < 0) {
	LockDisable(true);
	return;
    }

    /* ----------------
     *	read and examine every page in pg_database
     *
     *	Raw I/O! Read those tuples the hard way! Yow!
     *
     *  Why don't we use the access methods or move this code
     *  someplace else?  This is really pg_database schema dependent
     *  code.  Perhaps it should go in lib/catalog/pg_database?
     *  -cim 10/3/90
     *
     *  mao replies 4 apr 91:  yeah, maybe this should be moved to
     *  lib/catalog.  however, we CANNOT use the access methods since
     *  those use the buffer cache, which uses the relation cache, which
     *  requires that the dbid be set, which is what we're trying to do
     *  here.
     * ----------------
     */
    pg = (Page) palloc(BLCKSZ);
    ph = (PageHeader) pg;

    while ((nbytes = read(dbfd, pg, BLCKSZ)) == BLCKSZ) {
	max = PageGetMaxOffsetIndex(pg);

	/* look at each tuple on the page */
	for (i = 0; i <= max; i++) {
	    int offset;

	    /* if it's a freed tuple, ignore it */
	    if (!(ph->pd_linp[i].lp_flags & LP_USED))
		continue;

	    /* get a pointer to the tuple itself */
	    offset = (int) ph->pd_linp[i].lp_off;
	    tup = (HeapTuple) (((char *) pg) + offset);

	    /*
	     *  if the tuple has been deleted (the database was destroyed),
	     *  skip this tuple.  XXX warning, will robinson:  violation of
	     *  transaction semantics happens right here.  we should check
	     *  to be sure that the xact that deleted this tuple actually
	     *  committed.  only way to do this at init time is to paw over
	     *  the log relation by hand, too.  let's be optimistic.
	     *
	     *  XXX This is an evil type cast.  tup->t_xmax is char[5] while
	     *  TransactionId is struct * { char data[5] }.  It works but
	     *  if data is ever moved and no longer the first field this 
	     *  will be broken!! -mer 11 Nov 1991.
	     */
	    if (TransactionIdIsValid((TransactionId)tup->t_xmax))
		continue;

	    /*
	     *  Okay, see if this is the one we want.
	     *	XXX 1 july 91:  mao and mer discover that tuples now squash
	     *			t_bits.  Why is this?
	     *
	     *     24 july 92:  mer realizes that the t_bits field is only
	     *                  used in the event of null values.  If no
	     *                  fields are null we reduce the header size
	     *                  by doing the squash.  t_hoff tells you exactly
	     *                  how big the header actually is. use the PC
	     *                  means of getting at sys cat attrs.
	     */
	    tup_db = (Form_pg_database)GETSTRUCT(tup);

	    /* note: MyDatabaseName set by call to SetDatabaseName() */
	    if (strncmp(&(MyDatabaseName->data[0]),
			&(tup_db->datname.data[0]),
			16) == 0)
	    {
		MyDatabaseId = tup->t_oid;
		goto done;
	    }
	}
    }
    
done:
    (void) close(dbfd);
    pfree(pg);

    if (!ObjectIdIsValid(MyDatabaseId))
	elog(FATAL,
	     "Database %s does not exist in %s",
	     MyDatabaseName,
	     &DatabaseRelationName->data[0]);
}

/* ----------------
 *	DoChdirAndInitDatabaseNameAndPath
 *
 *	this just chdir's to the proper data/base directory
 *	XXX clean this up more.
 *
 * XXX The following code is an incorrect of the semantics
 * XXX described in the header file.  Handling of defaults
 * XXX should happen here, too.
 * ----------------
 */
void
DoChdirAndInitDatabaseNameAndPath(name, path)
    String	name;		/* name of database */
    String	path;		/* full path to database */
{
    /* ----------------
     *	sanity check (database may not change once set)
     * ----------------
     */
    AssertState(!StringIsValid(GetDatabasePath()));
    AssertState(!StringIsValid(GetDatabaseName()));

    /* ----------------
     *	check the path
     * ----------------
     */
    if (StringIsValid(path))
	SetDatabasePath(path);
    else
	elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: path:%s is not valid",
	     path);

    /* ----------------
     *	check the name
     * ----------------
     */
    if (StringIsValid(name))
	SetDatabaseName(name);
    else 
	elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: name:%s is not valid",
	     name);

    /* ----------------
     *	change to the directory, or die trying.
     * ----------------
     */
    if (*path && chdir(path) < 0)
	elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: chdir(\"%s\"): %m",
	     path);
}

/* --------------------------------
 *	InitUserid
 *
 *	initializes crap associated with the user id.
 * --------------------------------
 */
void
InitUserid() {
    setuid(geteuid());
    SetUserId();
}

/* --------------------------------
 *	InitCommunication
 *
 *	This routine initializes stuff needed for ipc, locking, etc.
 *	it should be called something more informative.
 *
 * Note:
 *	This does not set MyBackendId.  MyBackendTag is set, however.
 * --------------------------------
 */

void
InitCommunication()
{
    String	getenv();	/* XXX style */
    String	postid;
    String	postport;
    IPCKey	key;

    /* ----------------
     *	try and get the backend tag from POSTID
     * ----------------
     */
    MyBackendId = -1;

    postid = getenv("POSTID");
    if (!PointerIsValid(postid)) {
	MyBackendTag = -1;
    } else {
	MyBackendTag = atoi(postid);
	Assert(MyBackendTag >= 0);
    }

    /* ----------------
     *  try and get the ipc key from POSTPORT
     * ----------------
     */
    postport = getenv("POSTPORT");
    
    if (PointerIsValid(postport)) {
	SystemPortAddress address = atoi(postport);

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

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

	key = SystemPortAddressCreateIPCKey(address);

/*
 * Enable this if you are trying to force the backend to run as if it is
 * running under the postmaster.
 *
 * This goto forces Postgres to attach to shared memory instead of using
 * malloc'ed memory (which is the normal behavior if run directly).
 *
 * To enable emulation, run the following shell commands (in addition
 * to enabling this goto)
 *
 *     % setenv POSTID 1
 *     % setenv POSTPORT 4321
 *     % postmaster &
 *     % kill -9 %1
 *
 * Upon doing this, Postmaster will have allocated the shared memory resources
 * that Postgres will attach to if you enable EMULATE_UNDER_POSTMASTER.
 *
 * This comment may well age with time - it is current as of 8 January 1990
 * 
 * Greg
 */

#ifdef EMULATE_UNDER_POSTMASTER

	goto forcesharedmemory;

#endif

    } else if (IsUnderPostmaster) {
	elog(FATAL,
	     "InitCommunication: under postmaster and POSTPORT not set");
    } else {
	/* ----------------
	 *  assume we're running a postgres backend by itself with
	 *  no front end or postmaster.
	 * ----------------
	 */
	if (MyBackendTag == -1) {
	    MyBackendTag = 1;
	}

	key = PrivateIPCKey;
    }

    /* ----------------
     *  initialize shared memory and semaphores appropriately.
     * ----------------
     */
#ifdef EMULATE_UNDER_POSTMASTER

forcesharedmemory:

#endif

    PostgresIpcKey = key;
    AttachSharedMemoryAndSemaphores(key);
}


/* --------------------------------
 *	InitStdio
 *
 *	this routine consists of a bunch of code fragments
 *	that used to be randomly scattered through cinit().
 *	they all seem to do stuff associated with io.
 * --------------------------------
 */
void
InitStdio()
{
    /* ----------------
     *	this appears to be a funky variable used with the IN and OUT
     *  macros for controlling the error message indent level.
     * ----------------
     */
    ElogDebugIndentLevel = 0;

    /* ----------------
     *	old socket initialization crud starts here
     * ----------------
     */
    Ttyfile = 0;
    Debugfile = Debug_file = DebugFileOpen();

    if (IsUnderPostmaster) {
	struct	dpacket	pack;
	    
	/* ----------------
	 *	why the hell are we doing this here???  -cim 
	 * ----------------
	 */
	if (!ValidPgVersion(".") || !ValidPgVersion("../.."))
	{
		extern char *DBName;
	    elog(NOTICE, "InitStdio: !ValidPgVersion");
	    if (strcmp(DBName, "template1"))
	        elog(FATAL, "Did you run createdb on %s yet??", DBName);
	    else
	        elog(FATAL, "Did you run initdb yet??");
	}
    }

    Err_file = ErrorFileOpen();
}

/* --------------------------------
 *	InitPostgres
 *
 * Note:
 *	Be very careful with the order of calls in the InitPostgres function.
 * --------------------------------
 */
bool	PostgresIsInitialized = false;
extern int NBuffers;

/*
 *  this global is used by wei for testing his code, but must be declared
 *  here rather than in postgres.c so that it's defined for cinterface.a
 *  applications.
 */

int	testFlag = 0;
int	lockingOff = 0;

/*
 */
void
InitPostgres(name)
    String	name;		/* database name */
{
    bool	bootstrap;	/* true if BootstrapProcessing */
	extern	DestroyLocalRelList();

    /* ----------------
     *	see if we're running in BootstrapProcessing mode
     * ----------------
     */
    bootstrap = IsBootstrapProcessingMode();
    
    /* ----------------
     *	turn on the exception handler.  Note: we cannot use elog, Assert,
     *  AssertState, etc. until after exception handling is on.
     * ----------------
     */
    EnableExceptionHandling(true);

    /* ----------------
     *	A stupid check to make sure we don't call this more than once.
     *  But things like ReinitPostgres() get around this by just diddling
     *	the PostgresIsInitialized flag.
     * ----------------
     */
    AssertState(!PostgresIsInitialized);

    /* ----------------
     *	EnableTrace is poorly understood by the current postgres
     *  implementation people.  Since none of us know how it really
     *  is intended to function, we keep it turned off for now.
     *  Turning it on will cause a core dump in all the non-sequent
     *  ports.  Actually it may fail there too.  Who knows. -cim 10/3/90
     * ----------------
     */
    EnableTrace(false);

    /* ----------------
     *	Memory system initialization.
     *  (we may call palloc after EnableMemoryContext())
     *
     *  Note EnableMemoryContext() must happen before EnablePortalManager().
     * ----------------
     */
    EnableMemoryContext(true);	/* initializes the "top context" */
    EnablePortalManager(true);	/* memory for portal/transaction stuff */

    /* ----------------
     *	initialize the backend local portal stack used by
     *  internal PQ function calls.  see src/lib/libpq/be-dumpdata.c
     *  This is different from the "portal manager" so this goes here.
     *  -cim 2/12/91
     * ----------------
     */    
    be_portalinit();

    /* ----------------
     *	 attach to shared memory and semaphores, and initialize our
     *   input/output/debugging file descriptors.
     * ----------------
     */
    InitCommunication();
    InitStdio();

    if (!TransactionFlushEnabled())
        on_exitpg(FlushBufferPool, (caddr_t) 0);

    /* ----------------
     *	check for valid "meta gunk" (??? -cim 10/5/90) and change to
     *  database directory.
     *
     *  Note:  DatabaseName, MyDatabaseName, and DatabasePath are all
     *  initialized with DatabaseMetaGunkIsConsistent(), strncpy() and
     *  DoChdirAndInitDatabase() below!  XXX clean this crap up!
     *  -cim 10/5/90
     * ----------------
     */
    {
	static char  myPath[MAXPGPATH];	/* DatabasePath points here! */
	static char  myName[17]; 	/* DatabaseName points here! */

	/* ----------------
	 *  DatabaseMetaGunkIsConsistent fills in myPath, but what about
	 *  when bootstrap or Noversion is true?? -cim 10/5/90
	 * ----------------
	 */
	myPath[0] = '\0';
	
	if (! bootstrap)
	    if (! DatabaseMetaGunkIsConsistent(name, myPath))
		if (! Noversion)
		    elog(FATAL,
			 "InitPostgres: ! DatabaseMetaGunkIsConsistent");
	
	(void) strncpy(myName, name, sizeof(myName));

	/* ----------------
	 *  ok, we've figured out myName and myPath, now save these
	 *  and chdir to myPath.
	 * ----------------
	 */
	DoChdirAndInitDatabaseNameAndPath(myName, myPath);
    }
    
    /* ********************************
     *	code after this point assumes we are in the proper directory!
     * ********************************
     */
    
    /* ----------------
     *	initialize the database id used for system caches and lock tables
     * ----------------
     */
    InitMyDatabaseId();

    smgrinit();

    /* ----------------
     *	initialize the transaction system and the relation descriptor
     *  cache.  Note we have to make certain the lock manager is off while
     *  we do this.
     * ----------------
     */
    AmiTransactionOverride(IsBootstrapProcessingMode());
    LockDisable(true);

    /*
     * Part of the initialization processing done here sets a read
     * lock on pg_log.  Since locking is disabled the set doesn't have
     * intended effect of locking out writers, but this is ok, since
     * we only lock it to examine AMI transaction status, and this is
     * never written after initdb is done. -mer 15 June 1992
     */
    RelationInitialize();	   /* pre-allocated reldescs created here */
    InitializeTransactionSystem(); /* pg_log,etc init/crash recovery here */
    
    LockDisable(false);

    /* ----------------
     *	anyone knows what this does?  something having to do with
     *  system catalog cache invalidation in the case of multiple
     *  backends, I think -cim 10/3/90
     *  Sets up MyBackendId a unique backend identifier.
     * ----------------
     */
    InitSharedInvalidationState();

    /* ----------------
     * Set up a per backend process in shared memory.  Must be done after
     * InitSharedInvalidationState() as it relies on MyBackendId being
     * initialized already.  XXX -mer 11 Aug 1991
     * ----------------
     */
    InitProcess(PostgresIpcKey);

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

    /* ----------------
     *  initialize the access methods and set the heap-randomization
     *  behaviour  (if POSTGROWS is set  then we disable the usual
     *  random-search for a free page when we do an insertion).
     * ----------------
     */
    initam();
    InitRandom();		/* XXX move this to initam() */

    /* ----------------
     *	initialize all the system catalog caches.
     * ----------------
     */
    zerocaches();
    InitCatalogCache();

    /* ----------------
     *   set ourselves to the proper user id and figure out our postgres
     *   user id.  If we ever add security so that we check for valid
     *   postgres users, we might do it here.
     * ----------------
     */
    InitUserid();

    /* ----------------
     *	ok, all done, now let's make sure we don't do it again.
     * ----------------
     */
    PostgresIsInitialized = true;
	on_exitpg(DestroyLocalRelList, NULL);

    /* ----------------
     *  Done with "InitPostgres", now change to NormalProcessing unless
     *  we're in BootstrapProcessing mode.
     * ----------------
     */
    if (!bootstrap)
	SetProcessingMode(NormalProcessing);
    if (testFlag || lockingOff)
	LockDisable(true);
}

/* --------------------------------
 *	ReinitPostgres
 *
 *	It is not clear how this routine is supposed to work, or under
 *	what circumstances it should be called.  But hirohama wrote it
 *	for some reason so it's still here.  -cim 10/3/90
 * --------------------------------
 */
void
ReinitPostgres()
{
    /* assumes exception handling initialized at least once before */
    AssertState(PostgresIsInitialized);

    /* reset all modules */

    EnablePortalManager(false);
    EnableMemoryContext(false);

    EnableExceptionHandling(false);
    EnableTrace(false);

    /* now reinitialize */
    PostgresIsInitialized = false;	/* does it matter where this goes? */
    InitPostgres(NULL);	
}


/* ----------------------------------------------------------------
 *		traps to catch calls to obsolete routines
 * ----------------------------------------------------------------
 */
/* ----------------
 *	cinit
 * ----------------
 */
bool
cinit()
{
    fprintf(stderr, "cinit called! eliminate this!!\n");
    AbortPostgres();
}

/* ----------------
 *	cinit2
 * ----------------
 */
bool
cinit2()
{
    fprintf(stderr, "cinit2 called! eliminate this!!\n");
    AbortPostgres();
}
