///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoMain.cc
// -----------
// Cego main module
//
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: main
// 
// Description: Main module for the cego database program. The program can be invoked in different modes.
//
// Status: 2.50.3
//
///////////////////////////////////////////////////////////////////////////////

// POSIX INCLUDES
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>

// LFC INCLUDES
#include <lfcbase/Sleeper.h>
#include <lfcbase/Process.h>
#include <lfcbase/GetLongOpt.h>
#include <lfcbase/File.h>
#include <lfcbase/Timer.h>
#include <lfcbase/Tokenizer.h>
#include <lfcbase/Exception.h>
#include <lfcbase/AESCrypt.h>
#include <lfcbase/SigHandler.h>
#include <lfcbase/Host.h>
#include <lfcbase/ThreadLock.h>
#include <lfcxml/XMLSuite.h>
#include <lfcbase/Datetime.h>

// CEGO INCLUDES
#include "CegoDbThreadPool.h"
#include "CegoAdminThreadPool.h"
#include "CegoDatabaseManager.h"
#include "CegoQueryManager.h"
#include "CegoAction.h"
#include "CegoXMLdef.h"
#include "CegoSigCatch.h"
#include "CegoArchiveThread.h"
#include "CegoXPorter.h"
#include "CegoXMLdef.h"
#include "CegoModule.h"
#include "CegoCheckpoint.h"

#define USAGE "Usage:\n\n\
  Info:               cego [ --version ] [ --help ]\n\
\n\
  Initialization:     cego --mode=init --dbxml=<filename> --dbname=<id>\n\
                           [ --hostname=<id> ] [ --pgsize=<int> ]\n\
                           [ --dbport=<int> ] [ --admport=<int> ] [ --logport=<int> ]\n\
                           [ --pidfile=<filename> ] [ --csmode={ID|STR|NONE} ] [ --qescmode={ON|OFF} ] [ --loglevel=<log level> ]\n\
\n\
  User setup:         cego --mode=adduser --user=<string>/<string>\n\
                           --role=<string> --dbxml=<filename>\n\
\n\
                      cego --mode=setpwd --user=<string>/<string> --dbxml=<filename>\n\
\n\
  Role setup:         cego --mode=addrole --role=<string> --dbxml=<filename>\n\
\n\
  Permission setup:   cego --mode=addperm --role=<string> --permid=<string>\n\
                           --tableset=<string> --filter=<string>\n\
                           --perm=<string> --dbxml=<db xml>\n\
\n\
  Define Mode:        cego --mode=define --tsdef=<attr string> --tableset=<string>\n\
                           --dbxml=<filename>\n\
\n\
  Creation Mode:      cego --mode=create --tableset=<string> --dbxml=<filename>\n\
\n\
  Query Cache Mode:   cego --mode=querycache --tableset=<string> [ --enable ]\n\
                         [ --maxsize=<int> ] [ --maxentry=<int> ] [ --hashrange=<int> ] [ --threshold=<int> ] --dbxml=<filename>\n\
\n\
  Log Shift Mode:     cego --mode=shiftactive --tableset=<string>  --dbxml=<filename>\n\
\n\
  Export Mode:        cego --mode={xmlexport|binexport|plainexport} --expfile=<filename>\n \
                           --tableset=<string> --dbxml=<filename> --user=<string>/<string>\n\
\n\
  Import Mode:        cego --mode={xmlimport|binimport|plainimport} --impfile=<filename>\n\
                            --tableset=<id> --dbxml=<filename> --user=<string>/<string>\n\
\n\
  Batch Mode:         cego --mode=batch --batchfile=<string> --tableset=<string> --dbxml=<filename> --user=<string>/<string>\n\
                           [ --poolsize=[<numseg>x]<segsize> ] [ --cleanup ] [ --cpdump ] [ --ignore ]\n\
\n\
  Daemon Mode:        cego --mode=[daemon|server] --dbxml=<filename> [--tableset=<string>,<string>,... ]\n\
                           [ --cleanup ] [ --forceload ] [ --cpdump ] [ --notempreset ]\n\
                           [ --numdbthread=<int> ] [ --numadminthread=<int> ]\n\
                           [ --poolsize=[<numseg>x]<segsize> ] [ --pidfile=<filename> ]\n\
                           [ --protocol={serial|xml|fastserial}]\n\
\n\
  General Options:    [ --logfile=<filename> ] [--syslog=yes|no ] [ --lockfile=<filename> ] [ --lockexpire=<int> ] [ --lockstat ] [ --fsync ]\n"

// DEFINES
#define SYSLOGPROGNAME "cego"
#define DEFAULTLOGFILE "cego.log"
#define DEFAULTSYSLOG "yes"
#define DEFAULTLOCKFILE "/tmp/cego.lock"
#define DEFAULTPROTOCOL "fastserial"
#define DEFAULTPOOLSIZE "3x1000"
#define DEFAULTDBTHREADNUM 10
#define DEFAULTADMINTHREADNUM 5
#define DEFAULTBEATPORT 2000
#define DEFAULTBEATUSER "cgadm"
#define DEFAULTBEATPWD "cgadm"
#define DEFAULTROLE XML_ROLE_ALL_VALUE
#define DEFAULTTMPSIZE 100
#define DEFAULTSYSSIZE 100
#define DEFAULTAPPSIZE 3000
#define DEFAULTLOGNUM 3
#define DEFAULTLOGSIZE 1000000
#define DEFAULTORDERSIZE 10000000
#define DEFAULTTSROOT "./"
#define DEFAULTHOST "localhost"
#define DEFAULTPIDFILE "./pid"
#define DEFAULTPAGESIZE 16384
#define DEFAULTDBPORT 2200
#define DEFAULTADMPORT 2000
#define DEFAULTLOGPORT 3000
#define DEFAULTCSMODE "ID"
#define DEFAULTQESCMODE "ON"
#define DEFAULTLOCKEXPIRE 0
#define DEFAULTLOGLEVEL "NOTICE"

#define LANGUAGE_ENV "LANG"
#define LOCALE_CATEGORY LC_ALL

// tableset definition separator token
// changed to = token ( 23.05.2020 ), since handling with absolute path for mingw was broken
// 
#define TSDEFSEP "="

extern unsigned _funcSeed;
extern char __lfcVersionString[];
extern char __lfcxmlVersionString[];
extern char __caseSensitiveFlag;
extern char __quoteEscapeFlag;
extern char __allowDuplicateNull;
extern char __currencySymbol;
extern char __decimalPoint;
extern BigDecimal::RoundMode __roundMode;

// how to format date on default
extern Chain __dateTimeFormat;
extern unsigned __dateLength;
// valid date time scan formats
extern ListT<Chain> __dateFormatList;
// since the date format list is accessed by several threads, we have to provide a mutex lock for it
extern ThreadLock __dateFormatLock;

extern bool __lockStatOn;
extern bool __fsyncOn;

enum XPortFormat { BINARY, XML, PLAIN };

void readParameter(CegoDatabaseManager *pDBMng);
int init(GetLongOpt& longOpt);
int addUser(GetLongOpt& longOpt);
int setPwd(GetLongOpt& longOpt);
int addRole(GetLongOpt& longOpt);
int addPerm(GetLongOpt& longOpt);
int defineTableSet(GetLongOpt& longOpt);
int createTableSet(GetLongOpt& longOpt);
int importTableSet(GetLongOpt& longOpt, XPortFormat format);
int exportTableSet(GetLongOpt& longOpt, XPortFormat format);
int runBatch(GetLongOpt& longOpt);
int runDaemon(GetLongOpt& longOpt);
bool getPoolInfo(const Chain& poolSize, unsigned& numSeg, unsigned& segSize);
int setQueryCache(GetLongOpt& longOpt);
int shiftActive(GetLongOpt& longOpt);
    
int main(int argc, char **argv)
{	
    GetLongOpt longOpt(argc, argv);
    
    longOpt.addOpt("version");
    longOpt.addOpt("help");
    longOpt.addOpt("mode");
    longOpt.addOpt("user");
    longOpt.addOpt("dbxml");
    longOpt.addOpt("dbname");
    longOpt.addOpt("hostname", DEFAULTHOST);
    longOpt.addOpt("pidfile", DEFAULTPIDFILE);
    longOpt.addOpt("pgsize", Chain(DEFAULTPAGESIZE));
    longOpt.addOpt("dbport", Chain(DEFAULTDBPORT));
    longOpt.addOpt("admport", Chain(DEFAULTADMPORT));
    longOpt.addOpt("logport", Chain(DEFAULTLOGPORT));
    longOpt.addOpt("loglevel", Chain(DEFAULTLOGLEVEL));
    longOpt.addOpt("csmode", Chain(DEFAULTCSMODE));
    longOpt.addOpt("qescmode", Chain(DEFAULTQESCMODE));
    longOpt.addOpt("tableset");
    longOpt.addOpt("cleanup");
    longOpt.addOpt("cpdump");
    longOpt.addOpt("notempreset");
    longOpt.addOpt("forceload");
    longOpt.addOpt("lockexpire", Chain(DEFAULTLOCKEXPIRE));
    longOpt.addOpt("lockstat");
    longOpt.addOpt("fsync");
    longOpt.addOpt("role", DEFAULTROLE);
    longOpt.addOpt("filter");
    longOpt.addOpt("perm");
    longOpt.addOpt("permid");
    longOpt.addOpt("tsdef");
    longOpt.addOpt("expfile");
    longOpt.addOpt("impfile");
    longOpt.addOpt("batchfile");
    longOpt.addOpt("poolsize", Chain(DEFAULTPOOLSIZE));
    longOpt.addOpt("ignore");
    longOpt.addOpt("numdbthread", Chain(DEFAULTDBTHREADNUM));
    longOpt.addOpt("numadminthread", Chain(DEFAULTADMINTHREADNUM));
    longOpt.addOpt("master");
    // query cache options
    longOpt.addOpt("enable");
    longOpt.addOpt("maxsize");
    longOpt.addOpt("maxentry");
    longOpt.addOpt("hashrange");
    longOpt.addOpt("threshold");
    // general options
    longOpt.addOpt("logfile", DEFAULTLOGFILE);
    longOpt.addOpt("syslog", DEFAULTSYSLOG);
    longOpt.addOpt("lockfile", DEFAULTLOCKFILE);
    longOpt.addOpt("protocol", DEFAULTPROTOCOL);
    
    try
    {
	longOpt.parseOpt(); 
    }
    catch ( Exception e )
    {
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	cerr << USAGE << endl;
	exit(1);	
    }


    // for random generation in CegoFunction
    unsigned seed = 5 * (time(NULL) % 100000); 	
    srandom( seed  ); 
    
    // set localization
    char* lang = 0;
    if ( ( lang = getenv(LANGUAGE_ENV) ) != 0)
    {
	// if ( setlocale(LOCALE_CATEGORY, lang) == 0)
	if ( setlocale(LC_ALL, lang) == 0)
	{
	    Chain msg = Chain("Cannot set locale ") + Chain(lang);
	    cerr << msg << endl;
	    exit(1);
	}
    }

    const struct lconv * const currentlocale = localeconv();

    __currencySymbol = *currentlocale->currency_symbol;
    __decimalPoint = *currentlocale->decimal_point;        

    // cout << "Decimal Point = " << __decimalPoint << endl;
    
    if ( longOpt.isSet( Chain("help") ) )
    {	
	cerr << USAGE << endl;
	exit(0);
    }

    if ( longOpt.isSet( Chain("version") ) )
    {
	cout << CEGO_PRODUCT << " Server Program (" << sizeof(void*) * 8 << " bit), Version " << CEGO_VERSION 
	     << " [ lfcbase: " << __lfcVersionString  
	     << ", lfcxml: " <<  __lfcxmlVersionString << " ]" << endl;
	cout << CEGO_COPYRIGHT << endl;
	exit(0);
    }

    if ( longOpt.isSet("lockstat") )
	__lockStatOn = true;

    if ( longOpt.isSet("fsync") )
	__fsyncOn = true;

    __dateFormatLock.init(LCKMNG_LOCKWAITDELAY, __lockStatOn);

    // parameter validation

    Chain poolSize = longOpt.getOptValue("poolsize");
    unsigned numSeg, segSize;
    if ( getPoolInfo(poolSize, numSeg, segSize) == false )
    {
	cerr << "Invalid value for poolsize : " <<  poolSize << endl;
	exit(1);
    }

    Chain numDbThread = longOpt.getOptValue("numdbthread");
    if ( numDbThread.asUnsigned() <= 0 )
    {
	cerr << "Invalid value for numdbthread : " << numDbThread << endl;
	exit(1);
    }

    Chain numAdminThread = longOpt.getOptValue("numadminthread");
    if ( numAdminThread.asUnsigned() <= 0 )
    {
	cerr << "Invalid value for numadminthread : " << numAdminThread << endl;
	exit(1);
    }
    
    Chain mode = longOpt.getOptValue("mode");
    
    int exitCode = 0;

    if ( mode == Chain("init") )
    {
	exitCode = init(longOpt);
    }
    else if ( mode == Chain("adduser") )
    {
	exitCode = addUser(longOpt);
    }
    else if ( mode == Chain("setpwd") )
    {
	exitCode = setPwd(longOpt);
    }
    else if ( mode == Chain("addrole") )
    {
	exitCode = addRole(longOpt);
    }
    else if ( mode == Chain("addperm") )
    {
	exitCode = addPerm(longOpt);
    }
    else if ( mode == Chain("define") )
    {
	exitCode = defineTableSet(longOpt);
    }
    else if ( mode == Chain("create") )
    {
	exitCode = createTableSet(longOpt);
    }
    else if ( mode == Chain("xmlimport")  )
    {
	exitCode = importTableSet(longOpt, XML);
    }
    else if ( mode == Chain("binimport") )
    {
	exitCode = importTableSet(longOpt, BINARY);
    }
    else if ( mode == Chain("plainimport") )
    {
	exitCode = importTableSet(longOpt, PLAIN);
    }
    else if ( mode == Chain("xmlexport") )
    {
	exitCode = exportTableSet(longOpt, XML);
    }
    else if ( mode == Chain("binexport") )
    {
	exitCode = exportTableSet(longOpt, BINARY);
    }
    else if ( mode == Chain("plainexport") )
    {
	exitCode = exportTableSet(longOpt, PLAIN);
    }
    else if ( mode == Chain("batch") )
    {
	exitCode = runBatch(longOpt);
    }
    else if ( mode == Chain("daemon") )
    {
	pid_t pid;
	
	pid = fork();  
	if ( pid == -1 )
	{
	    cerr << "Cannot fork process" << endl;
	    exit(1);
	}
	else if ( pid != 0 )  
	{
	    // parent process exit
	    exit (0);  
	}

	if ( setsid() == -1 )  
	{
	    cerr << "Cannot set process group" << endl;
	    exit(1);
	}
      
	exitCode = runDaemon(longOpt);
    }
    else if ( mode == Chain("server") )
    {
	// don't run as background daemon
	exitCode = runDaemon(longOpt);
    }
    else if ( mode == Chain("querycache") )
    {
	exitCode = setQueryCache(longOpt);
    }
    else if ( mode == Chain("shiftactive") )
    {
	exitCode = shiftActive(longOpt);
    }

    else
    {
	cerr << USAGE << endl;
	exit(1);	
    }    
    exit(exitCode);
}

void readParameter(CegoDatabaseManager *pDBMng)
{
    __caseSensitiveFlag = pDBMng->getCaseSensitiveFlag();

    if ( pDBMng->allowDuplicateNull() )
	__allowDuplicateNull = 1;
    else
	__allowDuplicateNull = 0;

    if ( pDBMng->isQuoteEscapeMode() )	       
	__quoteEscapeFlag = 1;
    else
	__quoteEscapeFlag = 0;
    
    __dateTimeFormat = pDBMng->getDateTimeFormat();
    if ( __dateTimeFormat == Chain() )
	__dateTimeFormat = Chain(DEFAULTDATETIMEFORMAT1);

    Datetime d(PROOFDATE_VALUE, PROOFDATE_FORMAT);
    __dateLength = d.asChain(__dateTimeFormat).length();
    
    __dateFormatLock.writeLock(DF_LOCKTIMEOUT);    
    __dateFormatList = pDBMng->getDateFormatList();	
    if ( __dateFormatList.Size() == 0 )
    {
	__dateFormatList.Insert(DEFAULTDATETIMEFORMAT1);
	__dateFormatList.Insert(DEFAULTDATETIMEFORMAT2);
	__dateFormatList.Insert(DEFAULTDATETIMEFORMAT3);
	__dateFormatList.Insert(DEFAULTDATETIMEFORMAT4);
    }
    __dateFormatLock.unlock();

    __roundMode = pDBMng->getRoundMode();
    
}

int init(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain syslog = longOpt.getOptValue("syslog");	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain dbName = longOpt.getOptValue("dbname");
	Chain hostname = longOpt.getOptValue("hostname");
	Chain pgsize = longOpt.getOptValue("pgsize");
	Chain dbport = longOpt.getOptValue("dbport");
	Chain admport = longOpt.getOptValue("admport");
	Chain logport = longOpt.getOptValue("logport");
	Chain csmode = longOpt.getOptValue("csmode");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	
	if ( csmode != Chain(XML_ID_VALUE) && csmode != Chain(XML_STR_VALUE) && csmode != Chain(XML_NONE_VALUE))
	{
	    throw Exception(EXLOC, "Invalid value for csmode ( must be ID, STR or NONE)");
	}
	Chain qescmode = longOpt.getOptValue("qescmode");
	if ( qescmode != Chain(XML_ON_VALUE) && qescmode != Chain(XML_OFF_VALUE) )
	{
	    throw Exception(EXLOC, "Invalid value for qescmode ( must be either ON or OFF)");
	}
	Chain logLevel = longOpt.getOptValue("loglevel");
	if ( dbXML.length() == 0 || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( dbName.length() == 0 || dbName == Chain() )
	{
	    throw Exception(EXLOC, "No database name set");
	}
	if ( logLevel != Chain("ERROR") 
	     && logLevel != Chain("NOTICE")
	     && logLevel != Chain("DEBUG") 
	     && logLevel != Chain("NONE") )
	{
	    throw Exception(EXLOC, "Invalid log level");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();

	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);

	pDBMng->initXml(dbName, pgsize.asUnsigned(), hostname, dbport.asUnsigned(), admport.asUnsigned(), logport.asUnsigned(), logLevel, csmode, qescmode == Chain(XML_ON_VALUE) );
	
	delete pDBMng;
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    return exitCode;
}

int addUser(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	
	Chain syslog = longOpt.getOptValue("syslog");	
	Chain logFile = longOpt.getOptValue("logfile");	
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain userName = longOpt.getOptValue("user");
	Chain userRole = longOpt.getOptValue("role");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0 || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( user.length() == 0 || user == Chain() )
	{
	    throw Exception(EXLOC, "No user set");
	}
	if ( password.length() == 0 || password == Chain() )
	{
	    throw Exception(EXLOC, "No password set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	AESCrypt aescrypt(CEGOAESKEY, CEGOAESKEYLEN);
	pDBMng->addUser(user, aescrypt.encrypt(password));
	pDBMng->assignUserRole(user, userRole);
	
	pDBMng->doc2Xml();
	
	delete pDBMng;
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    return exitCode;
}

int setPwd(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	
	Chain syslog = longOpt.getOptValue("syslog");	
	Chain logFile = longOpt.getOptValue("logfile");	
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain userName = longOpt.getOptValue("user");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0 || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( user.length() == 0 || user == Chain() )
	{
	    throw Exception(EXLOC, "No user set");
	}
	if ( password.length() == 0 || password == Chain() )
	{
	    throw Exception(EXLOC, "No password set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	AESCrypt aescrypt(CEGOAESKEY, CEGOAESKEYLEN);
	pDBMng->setUserPwd(user, aescrypt.encrypt(password));
	
	pDBMng->doc2Xml();
	
	delete pDBMng;
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    return exitCode;
}

int addRole(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain role = longOpt.getOptValue("role");
	Chain filter = longOpt.getOptValue("filter");
	Chain tableset = longOpt.getOptValue("tableset");
		
	if ( dbXML.length() == 0 || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
		
	pDBMng->xml2Doc();
	pDBMng->configureLogger();     
	pDBMng->createRole(role);	
	pDBMng->doc2Xml();
	
	delete pDBMng;	
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;
	
	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    return exitCode;
}

int addPerm(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain role = longOpt.getOptValue("role");
	Chain filter = longOpt.getOptValue("filter");
	Chain tableset = longOpt.getOptValue("tableset");
	Chain perm = longOpt.getOptValue("perm");
	Chain permid = longOpt.getOptValue("permid");
	
	if ( dbXML.length() == 0 || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);       
	
	pDBMng->xml2Doc();
	pDBMng->configureLogger();	
	pDBMng->setPerm(role, permid, tableset, filter, perm);	
	pDBMng->doc2Xml();
	
	delete pDBMng;	
    }
    catch ( Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    return exitCode;
}

int defineTableSet(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire"); 
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain tsDef = longOpt.getOptValue("tsdef");
	
	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	if ( tsDef.length() == 0 || tsDef == Chain() )
	{
	    throw Exception(EXLOC, "No tsdef set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
	       
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	Chain tsRoot(DEFAULTTSROOT);
	
	unsigned sysSize = DEFAULTSYSSIZE;
	unsigned tmpSize = DEFAULTTMPSIZE;
	unsigned appSize = DEFAULTAPPSIZE;
	unsigned logFileSize = DEFAULTLOGSIZE;
	unsigned logFileNum = DEFAULTLOGNUM;
	
	long orderSize = DEFAULTORDERSIZE;
	unsigned branchId = 0;
	
	Tokenizer atok(tsDef, Chain(","));
	
	Chain attrTup;
	while ( atok.nextToken(attrTup) )
	{
	    Tokenizer vtok(attrTup, Chain(TSDEFSEP));
	    Chain attr;
	    Chain value;
	    vtok.nextToken(attr);
	    vtok.nextToken(value);

	    if ( attr == Chain("syssize"))
		sysSize = value.asUnsigned();
	    else if ( attr == Chain("tmpsize"))
		tmpSize = value.asUnsigned();
	    else if ( attr == Chain("appsize"))
		appSize = value.asUnsigned();
	    else if ( attr == Chain("logfilesize"))
		logFileSize = value.asUnsigned();
	    else if ( attr == Chain("logfilenum"))
		logFileNum = value.asUnsigned();
	    else if ( attr == Chain("tsroot"))
		tsRoot = value;
	    else if ( attr == Chain("sortareasize"))
		orderSize = value.asLong();
	    else if ( attr == Chain("branchid"))
		branchId = value.asUnsigned();
	}
	
	unsigned sysFid = pDBMng->nextTSID();
	
	unsigned tmpFid = pDBMng->nextFID();
		
	pDBMng->addTableSetDef(tableSet, 
			       tsRoot,
			       sysFid, 
			       tmpFid, 
			       sysSize, 
			       tmpSize,
			       appSize,
			       logFileSize, 
			       logFileNum,
			       orderSize,
			       branchId); 
	       	
	pDBMng->doc2Xml();
	
	delete pDBMng;	
    }
    catch (Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    
    return exitCode;
}

int createTableSet(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	
	Chain logFile = longOpt.getOptValue("logfile");
	Chain syslog = longOpt.getOptValue("syslog");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	unsigned numSeg, segSize;
	getPoolInfo(poolSize, numSeg, segSize);

	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire,logFile, progName);

	pDBMng->xml2Doc();
	pDBMng->initDoc();
	pDBMng->configureLogger();
	pDBMng->initPool(numSeg, segSize);

	unsigned long modId = pDBMng->getModId("CegoMain");
		
	CegoLockHandler* pLockHandler = new CegoLockHandler(pDBMng);

	pLockHandler->initLocks();	    
	
	CegoQueryManager tabMng(pDBMng);

	Chain status = pDBMng->getTableSetRunState(tableSet);
	
	if ( status == Chain(XML_OFFLINE_VALUE) || status == Chain(XML_CHECKPOINT_VALUE)  )
	{
	    pDBMng->log(modId, Logger::NOTICE, Chain("Detected existing tableset ") + tableSet + Chain(", dropping .."));
	    tabMng.dropTableSet(tableSet);
	}

	pDBMng->log(modId, Logger::NOTICE, Chain("Creating tableset ") + tableSet + Chain(" ..."));

	tabMng.createTableSet(tableSet);

	pDBMng->log(modId, Logger::NOTICE, Chain("Tableset ") + tableSet + Chain(" created"));
	
	pLockHandler->deleteLocks();
	delete pDBMng;
	delete pLockHandler;	
    }
    catch (Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }

    return exitCode;
}

int importTableSet(GetLongOpt& longOpt, XPortFormat format)
{
    Chain errorMsg;
    int exitCode=0;
	
    CegoDatabaseManager* pDBMng = 0;
    
    try 
    {	
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain impFileName = longOpt.getOptValue("impfile");
	Chain userName = longOpt.getOptValue("user");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	if ( impFileName.length() == 0 || impFileName == Chain() )
	{
	    throw Exception(EXLOC, "No impfile set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);

	pDBMng->xml2Doc();
	pDBMng->initDoc();
	pDBMng->configureLogger();
	
	unsigned numSeg, segSize;
	getPoolInfo(poolSize, numSeg, segSize);

	pDBMng->initPool(numSeg, segSize);

	unsigned long modId = pDBMng->getModId("CegoMain");
	
	CegoLockHandler* pLockHandler = 0;
	CegoQueryManager* pTabMng = 0;
	CegoArchiveThread* pArchThread = 0;
	
	try
	{
	    readParameter(pDBMng);
	    	    
	    pLockHandler = new CegoLockHandler(pDBMng);
	    
	    pLockHandler->initLocks();
	    
	    if ( pDBMng->isArchiveMode(tableSet) )
	    {
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Creating log threadpool ..."));
#endif
		pArchThread = new CegoArchiveThread(pDBMng);
		pArchThread->start(0);
	    }
	    
	    pTabMng = new CegoQueryManager(pDBMng);

	    pTabMng->setActiveUser(tableSet, user, password);
	    
#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet + Chain(" ..."));
#endif	    
	    

	    pTabMng->startQueryTableSet(tableSet, false, true, false);

	    unsigned tabSetId = pDBMng->getTabSetId(tableSet);
	    
	    // during import we stop any log activies
	    pDBMng->stopLog(tabSetId);
	    
	    CegoXPorter xp(pTabMng);
	    
	    if ( format == XML )
	    {
		cout << "Starting XML import of file " << impFileName << endl;
		xp.xmlImportTableSet(tableSet, false, impFileName);
	    }
	    else if ( format == BINARY )
	    {
		cout << "Starting binary import of file " << impFileName << endl;
		xp.binImportTableSet(tableSet, false, impFileName, false);
	    }
	    else if ( format == PLAIN )
	    {
		cout << "Starting plain import of file " << impFileName << endl;
		xp.binImportTableSet(tableSet, false, impFileName, true);
	    }

	    pTabMng->stopTableSet(tableSet, true);
	    
	    pDBMng->doc2Xml();	    
	}
	catch ( Exception e )
	{
	    Chain msg;
	    Chain module;
	    unsigned line;
	    
	    Chain exep;
	    while ( e.pop(module, line, msg) )
	    {
		exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
	    }
	    pDBMng->log(modId, Logger::LOGERR, Chain("Import Thread : ") + exep);
	    exitCode=1;
	    errorMsg=Chain("Import failed, see log for details");		
	}
	
	if ( pArchThread )
	    delete pArchThread;
	
	if ( pTabMng )
	    delete pTabMng;
	
	if ( pLockHandler )
	{
	    pLockHandler->deleteLocks();
	    delete pLockHandler;
	}
	
	delete pDBMng;
	pDBMng = 0;
    }
    catch ( Exception e )
    {	    
	if ( pDBMng ) 
	    delete pDBMng;
	
	e.pop(errorMsg);
	exitCode=1;
    }
    
    if ( exitCode == 0 )
    {
	cout << "Import done" << endl;
    }
    else
    {
	cerr << "ERROR: " << errorMsg << endl;
    }

    return exitCode;
}

int exportTableSet(GetLongOpt& longOpt, XPortFormat format)
{
    Chain errorMsg;
    int exitCode=0;
    
    CegoDatabaseManager* pDBMng = 0;
    
    try 
    {
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");

	Chain expFileName = longOpt.getOptValue("expfile");  
	Chain userName = longOpt.getOptValue("user");

	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}
	if ( expFileName.length() == 0 || expFileName == Chain() )
	{
	    throw Exception(EXLOC, "No expfile set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
	pDBMng->setReadOnly(true);	
	pDBMng->xml2Doc();
	pDBMng->initDoc();
	pDBMng->configureLogger();

	unsigned numSeg, segSize;
	getPoolInfo(poolSize, numSeg, segSize);
	pDBMng->initPool(numSeg, segSize);

	unsigned long modId = pDBMng->getModId("CegoMain");
		
	CegoLockHandler* pLockHandler = 0;
	CegoArchiveThread* pArchThread = 0;
	CegoQueryManager* pTabMng = 0;
	
	try
	{
	    readParameter(pDBMng);
	    
	    pLockHandler = new CegoLockHandler(pDBMng);

	    pLockHandler->initLocks();
	    	    
	    if ( pDBMng->isArchiveMode(tableSet) )
	    {
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Creating log threadpool ..."));
#endif

		pArchThread = new CegoArchiveThread(pDBMng);
		pArchThread->start(0);
	    }
	    
	    pTabMng = new CegoQueryManager(pDBMng);

	    pTabMng->setActiveUser(tableSet, user, password);
	    
#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet + Chain(" ..."));    	
#endif    


	    pTabMng->startQueryTableSet(tableSet, false, false, false);
	    
	    CegoXPorter xp(pTabMng);
	    
	    if ( format == XML )
	    {
		cout << "Starting XML export of file " << expFileName << endl;
		xp.xmlExportTableSet(tableSet, false, expFileName);
	    }
	    else if ( format == BINARY )
	    {
		cout << "Starting binary export of file " << expFileName << endl;
		xp.binExportTableSet(tableSet, false, expFileName, false);
	    }
	    else if ( format == PLAIN )
	    {
		cout << "Starting plain export of file " << expFileName << endl;
		xp.binExportTableSet(tableSet, false, expFileName, true);
	    }
	    
	    pTabMng->stopTableSet(tableSet, true);
	    
	    pDBMng->doc2Xml();	    
	}
	catch ( Exception e )
	{
	    Chain msg;
	    Chain module;
	    unsigned line;
	    
	    Chain exep;
	    while ( e.pop(module, line, msg) )
	    {
		exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
	    }
	    pDBMng->log(modId, Logger::LOGERR, Chain("Export Thread : ") + exep);
	    exitCode=1;
	    errorMsg=Chain("Export failed, see log for details");		
	}
	
	if ( pArchThread )
	    delete pArchThread;
	
	if ( pTabMng )
	    delete pTabMng;
	
	if ( pLockHandler )
	{
	    pLockHandler->deleteLocks();
	    delete pLockHandler;
	}
	
	delete pDBMng;
	pDBMng = 0;
    }
    catch ( Exception e )
    {	    
	if ( pDBMng ) 
	    delete pDBMng;
	
	e.pop(errorMsg);
	exitCode=1;
    }
    
    if ( exitCode == 0 )
    {
	cout << "Export done" << endl;
    }
    else
    {
	cerr << "ERROR: " << errorMsg << endl;	
    }	
    
    return exitCode;
}

int runBatch(GetLongOpt& longOpt)
{
    Chain errorMsg;
    int exitCode=0;
    
    CegoDatabaseManager* pDBMng = 0;
    CegoLockHandler* pLockHandler = 0;
    CegoArchiveThread* pArchThread = 0;
    CegoQueryManager* pTabMng = 0;
    CegoAction* pAction = 0;
    
    try 
    {
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain batchFileName = longOpt.getOptValue("batchfile");  
	bool ignoreError = longOpt.isSet("ignore");
	Chain userName = longOpt.getOptValue("user");
	bool cleanIt = longOpt.isSet("cleanup");
	bool cpDump = longOpt.isSet("cpdump");
	
	Tokenizer userTok(userName, Chain("/")); 
	
	Chain user;
	Chain password;
	
	userTok.nextToken(user);
	userTok.nextToken(password);
	
	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);

	pDBMng->xml2Doc();
	
	pDBMng->initDoc();
	pDBMng->configureLogger();

	unsigned numSeg, segSize;
	getPoolInfo(poolSize, numSeg, segSize);	
	pDBMng->initPool(numSeg, segSize);

	unsigned long modId = pDBMng->getModId("CegoMain");
			
	pLockHandler = new CegoLockHandler(pDBMng);
	
	readParameter(pDBMng);
	
	pLockHandler->initLocks();
	
	if ( pDBMng->isArchiveMode(tableSet) )
	{
#ifdef CGDEBUG	
	    pDBMng->log(modId, Logger::DEBUG, Chain("Creating log threadpool ..."));
#endif
	    
	    pArchThread = new CegoArchiveThread(pDBMng);
	    pArchThread->start(0);
	}
	    	    
	pTabMng = new CegoQueryManager(pDBMng);
	pTabMng->setActiveUser(tableSet, user, password);
	pTabMng->setThreadId(1);
	
#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet + Chain(" ..."));
#endif	    
	

	// for batch jobs we force offline state
	pDBMng->setTableSetRunState(tableSet, XML_OFFLINE_VALUE);
	    
	pTabMng->startQueryTableSet(tableSet, cleanIt, true, cpDump);
	
#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, Chain("Tableset ") + tableSet + Chain(" completed"));
#endif	    
	
	pAction = new CegoAction( pTabMng);
	
	pAction->setTableSet(tableSet);
	
	pDBMng->log(modId, Logger::NOTICE, Chain("Processing batchfile ") + batchFileName + Chain(" ..."));
	if ( pAction->processBatchFile(batchFileName, ignoreError, true, errorMsg) == false )
	    exitCode = 1;
	
	pDBMng->log(modId, Logger::NOTICE, Chain("Batchfile processed with exit code ") + Chain(exitCode));
	
	pTabMng->stopTableSet(tableSet, false);
	
	pDBMng->doc2Xml();	    
	
	
    }
    catch ( Exception e )
    {	    	
	e.pop(errorMsg);
	exitCode=1;
    }

    if ( pAction )
	delete pAction;
    
    if ( pArchThread )
	delete pArchThread;
    
    if ( pTabMng )
	delete pTabMng;
    
    if ( pLockHandler )
    {
	pLockHandler->deleteLocks();
	delete pLockHandler;
    }
    
    if ( pDBMng )
	delete pDBMng;	

    if ( exitCode == 0 )
    {
	cout << "Batch done" << endl;
    }
    else
    {
	cerr << "ERROR: " << errorMsg << endl;	
    }
    return exitCode;
}

int runDaemon(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoArchiveThread *pArchThread = 0;
    CegoLockHandler* pLockHandler = 0;

    CegoDbThreadPool* pDbPool = 0;
    CegoAdminThreadPool *pAdminPool = 0;

    CegoSigCatch *pSigCatch = 0;
    
    CegoDatabaseManager* pDBMng = 0;
    unsigned long modId = 0;

    try
    {
	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain pidFile = longOpt.getOptValue("pidfile");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSetList = longOpt.getOptValue("tableset");
	Chain poolSize = longOpt.getOptValue("poolsize");
	Chain numDbThread = longOpt.getOptValue("numdbthread");
	Chain numAdminThread = longOpt.getOptValue("numadminthread");
	Chain master = longOpt.getOptValue("master");
	Chain protocol = longOpt.getOptValue("protocol");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	bool cleanIt = longOpt.isSet("cleanup");
	bool forceload = longOpt.isSet("forceload");
	bool cpDump = longOpt.isSet("cpdump");
	bool noTempReset = longOpt.isSet("notempreset");
	
	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}

	CegoDbHandler::ProtocolType protType;
	if ( protocol == Chain("serial") )
	{
	    protType = CegoDbHandler::SERIAL;
	}
	else if ( protocol == Chain("xml") )
	{
	    protType = CegoDbHandler::XML;
	}
	else if ( protocol == Chain("fastserial") )
	{
	    protType = CegoDbHandler::FASTSERIAL;
	}
	else
	{
	    throw Exception(EXLOC, "Invalid protocol");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName, protType);
		

	pDBMng->xml2Doc();
	pDBMng->initDoc();
	pDBMng->configureLogger();

	modId = pDBMng->getModId("CegoMain");

	unsigned numSeg, segSize;
	getPoolInfo(poolSize, numSeg, segSize);
	pDBMng->initPool(numSeg, segSize);
	
#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, "Initializing page and record locks ...");
#endif	

	readParameter(pDBMng);
	
	pLockHandler = new CegoLockHandler(pDBMng);
	pLockHandler->initLocks();

#ifdef CGDEBUG		
	pDBMng->log(modId, Logger::DEBUG, "Creating db and admin threadpool ...");
#endif
		
	pDbPool = new CegoDbThreadPool(numDbThread.asUnsigned(), pDBMng, protType);

	pAdminPool = new CegoAdminThreadPool(numAdminThread.asUnsigned(), pDBMng, pDbPool);

#ifdef CGDEBUG		
	pDBMng->log(modId, Logger::DEBUG, "Init pools ...");
#endif

	if ( pDbPool->init() == false )
	{
	    throw Exception(EXLOC, "Cannot init db thread pool");
	}

	if ( pAdminPool->init() == false )
	{
	    throw Exception(EXLOC, "Cannot init admin thread pool");
	}

#ifdef CGDEBUG	
	pDBMng->log(modId, Logger::DEBUG, "Poos are ready");
#endif
	
	pDBMng->setThreadInfo(numDbThread.asUnsigned(), numAdminThread.asUnsigned());

	// Creating instances for archiving and signal catching
	pArchThread = new CegoArchiveThread(pDBMng);
	pSigCatch = new CegoSigCatch(pDBMng);
	
	pArchThread->start(0);
	pDbPool->start(0);
	pAdminPool->start(0);
	
	// write pidfile

	if ( pidFile != Chain() )
	{
	    File pf(pidFile);
	    pf.open(File::WRITE);
	    Process p;
	    int pid = p.getPid();
	    pf.writeChain(Chain(pid));
	    pf.close();
	}
	else
	{
	    throw Exception(EXLOC, "No pid filename set");
	}
		
	// correct online entries 
	ListT<Chain> corList;
	pDBMng->getActiveTableSet( corList, true, false);
	
	if ( corList.Size() > 0 )
	{
	    CegoQueryManager tabMng(pDBMng);
	    Chain *pCor = corList.First();
	    while ( pCor )
	    {
		
#ifdef CGDEBUG	
		pDBMng->log(modId, Logger::DEBUG, Chain("Correcting tableset ") + *pCor + Chain(" runstate"));
#endif

		pDBMng->setTableSetRunState(*pCor, XML_OFFLINE_VALUE);
		pCor = corList.Next();
	    }    
	}

	bool startTableSet = true;
	if ( tableSetList.length() == 0 || tableSetList == Chain() )
	{
	    startTableSet = false;
	}

	if ( startTableSet )
	{
	    Tokenizer tok(tableSetList, Chain(","));

	   CegoQueryManager tabMng(pDBMng);	

	    // we have to allow the main thread to access all objects
	    tabMng.disableAuth();

	    Chain tableSet;

	    try
	    {
		while ( tok.nextToken(tableSet) )
		{
		    
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Starting tableset ") + tableSet);
#endif		
    
		    Chain runState = pDBMng->getTableSetRunState(tableSet);
		    if ( runState == Chain(XML_DEFINED_VALUE) )
		    {		   
			Chain msg = Chain("Cannot start tableset ") + tableSet + Chain(", not yet created"); 
			throw Exception(EXLOC, msg);
		    }
		    
		    tabMng.startQueryTableSet(tableSet, cleanIt, cpDump, noTempReset == false);
		    
		    if ( forceload )
		    {
#ifdef CGDEBUG	
			pDBMng->log(modId, Logger::DEBUG, Chain("Forced loading object for ") + tableSet + Chain("..."));
#endif

			unsigned tabSetId = pDBMng->getTabSetId(tableSet);
			pDbPool->loadObjects(tabSetId);	
#ifdef CGDEBUG		
			pDBMng->log(modId, Logger::DEBUG, Chain("Objects for ") + tableSet + Chain(" loaded"));
#endif
		    }
		    
		    Chain batchFileName = pDBMng->getTSInitFile(tableSet);

		    if ( batchFileName != Chain() )
		    {
			try
			{
			    File batchFile(batchFileName);
			    
			    if ( batchFile.exists() ) 
			    {
				pDBMng->log(modId, Logger::NOTICE, Chain("Processing init file ") + batchFileName);
				
				CegoAction* pAction = new CegoAction(&tabMng);
				
				pAction->setLogToFile(true);
				
				try
				{
				    pAction->setTableSet(tableSet);
				    
				    Chain errorMsg;
				    if ( pAction->processBatchFile(batchFileName, false, false, errorMsg) == false )
					throw Exception(EXLOC, errorMsg);	    
				}
				catch ( Exception e )
				{
				    delete pAction;
				    Chain msg;
				    e.pop(msg);
				    throw Exception(EXLOC, Chain("Init error : ") + msg);
				}
				
				delete pAction;
			    }
			    else
			    {
				throw Exception(EXLOC, Chain("Init file <") + batchFileName + Chain("> does not exist"));
			    }
			}
			catch ( Exception e )
			{
			    Chain msg;
			    e.pop(msg);
			    cerr << msg << endl;
			}			
		    }
		}
	    }
	    catch ( Exception e )
	    {
		throw Exception(EXLOC, Chain("Cannot start tableset"), e);
	    }
	}

	cout << "Cego daemon up and running ..." << endl;

	CegoCheckpoint cpc;

	while ( ! pSigCatch->isTerminated() 
		&& ! pDbPool->isTerminated() 
		&& ! pAdminPool->isTerminated() )
	{
	    
	    ListT<Chain> actList;

	    // we get all active and online tableset, except the tablesets which actually are recovered
	    pDBMng->getActiveTableSet( actList, false, false);
	    
	    if ( actList.Size() > 0 )
	    {	
		Chain *pAct = actList.First();
		while ( pAct )
		{
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Checking tableset ") + *pAct + Chain(" ..."));
#endif

		    CegoQueryManager tabMng(pDBMng);
		    
		    try 
		    {
			tabMng.reorgTableSet(*pAct);
			unsigned cpi = pDBMng->getCheckpointInterval(*pAct);
			if ( cpc.checkpointReached(*pAct, cpi) )
			{
			    pDBMng->log(modId, Logger::NOTICE, Chain("Checkpoint reached for tableset ") + *pAct);
			    pDBMng->writeCheckPoint(*pAct, true, true, tabMng.getLockHandle());
			}
		    }
		    catch ( Exception e )
		    {
			Chain msg;
			Chain module;
			unsigned line;
			
			Chain exep;
			while ( e.pop(module, line, msg) )
			{
			    exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
			}
			
			pDBMng->log(modId, Logger::LOGALERT, Chain("Main Thread : ") + exep);
		    }
#ifdef CGDEBUG	
		    pDBMng->log(modId, Logger::DEBUG, Chain("Tableset ") + *pAct + Chain(" checked"));
#endif

		    pAct = actList.Next();
		}		
	    }

	    pDBMng->beat();
	    
	    Sleeper s;
	    s.secSleep(LCKMNG_BEATDELAY);
	}
	
	cout << "Terminating ..." << endl;
	
	pDBMng->log(modId, Logger::NOTICE, Chain("Shutting database down .."));
	
	ListT<Chain> atsList;
	
	pDBMng->getActiveTableSet( atsList, true, false);
	
	if ( atsList.Size() > 0 )
	{
	    CegoQueryManager tabMng(pDBMng);
	    Chain *pATS = atsList.First();
	    while ( pATS )
	    {	
		pDBMng->log(modId, Logger::NOTICE, Chain("Syncing active tableset ") + *pATS + Chain("..."));
		tabMng.stopTableSet(*pATS, true);		 
		pATS = atsList.Next();
	    }
	}
	
	// finally sync the xml file to be safe
	pDBMng->doc2Xml();
	

    }
    catch ( Exception e)
    {

	// e.print();
	
	Chain msg;
	Chain module;
	unsigned line;
	
	bool log2Console=true;

	if ( pDBMng ) 
	{	
	    if ( pDBMng->isLoggerConfigured() )
	    {
		Chain exep;
		while ( e.pop(module, line, msg) )
		{
		    exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;
		}		
		pDBMng->log(modId, Logger::LOGERR, Chain("Main Thread : ") + exep);	    

		cerr << "Cego daemon failed, see log for detailed information" << endl;
		log2Console=false;
	    }
	}
	
	if ( log2Console )
	{
	    cerr << "Cego daemon failed :";
	    Chain exep;
	    while ( e.pop(module, line, msg) )
	    {
		exep += Chain("\n\t") + module + Chain("(") +  Chain(line) + Chain(") : ") + msg;	    
	    }
	    cerr << exep << endl;
	}
	exitCode = 1;
    }


    if ( pAdminPool )
    	delete pAdminPool;

    if ( pDbPool )
	delete pDbPool;

    if ( pSigCatch )
	delete pSigCatch;

    if ( pArchThread )
	delete pArchThread;

    if ( pLockHandler )
    {
	pLockHandler->deleteLocks();
	delete pLockHandler;
    }

    if ( pDBMng )
    {
	pDBMng->log(modId, Logger::NOTICE, Chain("Shutdown finished"));
	delete pDBMng;
    }
    
    if ( exitCode == 0 )
    {
	cout << "Cego daemon is terminated." << endl;
    }

    return exitCode;
}

int setQueryCache(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	

	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	bool isEnabled = longOpt.isSet("enable");
	
	Chain maxSize;
	if ( longOpt.isSet("maxsize") )
	    maxSize = longOpt.getOptValue("maxsize");
	
	Chain maxEntry;
	if ( longOpt.isSet("maxentry") )
	    maxEntry = longOpt.getOptValue("maxentry");
	
	Chain hashRange;
	if ( longOpt.isSet("hashrange") )
	    hashRange = longOpt.getOptValue("hashrange");
	
	Chain threshold;
	if ( longOpt.isSet("threshold") )
	    threshold = longOpt.getOptValue("threshold");	

	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
	       
	pDBMng->xml2Doc();
	pDBMng->configureLogger();
	
	pDBMng->setQueryCacheMode(tableSet,
				  isEnabled);

	if ( maxSize.length() > 0 )
	{
	    if ( maxSize.isNum() )
		pDBMng->setMaxQueryCacheSize(tableSet, maxSize.asUnsigned());
	    else
		throw Exception(EXLOC, "Invalid querycache maxsize");
	}
	
	if ( maxEntry.length() > 0 )
	{
	    if ( maxEntry.isNum() )
		pDBMng->setMaxQueryCacheEntry(tableSet, maxEntry.asUnsigned());
	    else
		throw Exception(EXLOC, "Invalid querycache maxentry");
	}

	if ( threshold.length() > 0 )
	{
	    if ( threshold.isNum() )
		pDBMng->setQueryCacheThreshold(tableSet, threshold.asUnsigned());
	    else
		throw Exception(EXLOC, "Invalid querycache threshold");
	}
	
	if ( hashRange.length() > 0 )
	{
	    if ( hashRange.isNum() )
		pDBMng->setQueryCacheHashRange(tableSet, hashRange.asUnsigned());
	    else
		throw Exception(EXLOC, "Invalid querycache hashrange");
	}
	       	
	pDBMng->doc2Xml();
	
	delete pDBMng;	
    }
    catch (Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    
    return exitCode;
}

int shiftActive(GetLongOpt& longOpt)
{
    int exitCode = 0;

    CegoDatabaseManager* pDBMng = 0;

    try
    {	

	Chain syslog = longOpt.getOptValue("syslog");
	Chain logFile = longOpt.getOptValue("logfile");
	Chain lockFile = longOpt.getOptValue("lockfile");
	Chain lockExpire = longOpt.getOptValue("lockexpire");
	Chain dbXML = longOpt.getOptValue("dbxml");
	Chain tableSet = longOpt.getOptValue("tableset");
	
	if ( dbXML.length() == 0  || dbXML == Chain() )
	{
	    throw Exception(EXLOC, "No dbxml set");
	}
	if ( tableSet.length() == 0 || tableSet == Chain() )
	{
	    throw Exception(EXLOC, "No tableSet set");
	}

	Chain progName = SYSLOGPROGNAME;
	if ( syslog == Chain("no") )
	    progName=Chain();
	
	pDBMng = new CegoDatabaseManager(dbXML, lockFile, lockExpire, logFile, progName);
	       
	pDBMng->xml2Doc();
	pDBMng->configureLogger();

	CegoArchiveThread::shiftActive(pDBMng, tableSet);
		       	
	pDBMng->doc2Xml();
	
	delete pDBMng;	
    }
    catch (Exception e)
    {
	if ( pDBMng ) 
	    delete pDBMng;

	Chain msg;
	e.pop(msg);
	cerr << msg << endl;
	exitCode = 1;
    }
    
    return exitCode;
}


bool getPoolInfo(const Chain& poolSize, unsigned& numSeg, unsigned& segSize)
{
    Tokenizer tok(poolSize, "xX");
    Chain t1, t2;
    if ( tok.nextToken(t1) )
    {
	if ( tok.nextToken(t2) )
	{
	    // multisegment spec
	    if ( t1.asUnsigned() <= 0 || t2.asUnsigned() <= 0 )
	    {
		return false;
	    }
	    numSeg=t1.asUnsigned();
	    segSize=t2.asUnsigned();
	    return true;	    
	}
	else
	{
	    if ( t1.asUnsigned() <= 0 )
	    {
		return false;
	    }
	    else
	    {
		numSeg=1;
		segSize=t1.asUnsigned();
		return true;
	    }
	}
    }
    return false;    
}
