/* rsyslog.c - the main entry point into rsyslog's runtime library (RTL)
 *
 * This module contains all function which work on a RTL global level. It's
 * name is abbreviated to "rsrt" (rsyslog runtime).
 *
 * Please note that the runtime library tends to be plugin-safe. That is, it must be
 * initialized by calling a global initialization function. However, that 
 * function checks if the library is already initialized and, if so, does
 * nothing except incrementing a refeence count. Similarly, the deinit
 * function does nothing as long as there are still other users (which
 * is tracked via the refcount). As such, it is safe to call init and
 * exit multiple times, as long as this are always matching calls. This
 * capability is needed for a plugin system, where one plugin never
 * knows what the other did. HOWEVER, as of this writing, not all runtime
 * library objects may work cleanly without static global data (the
 * debug system is a very good example of this). So while we aim at the
 * ability to work well in a plugin environment, things may not really work
 * out. If you intend to use the rsyslog runtime library inside plugins,
 * you should investigate the situation in detail. Please note that the
 * rsyslog project itself does not yet need this functionality - thus you
 * can safely assume it is totally untested ;).
 *
 * rgerhards, 2008-04-17: I have now once again checked on the plugin-safety.
 * Unfortunately, there is currently no hook at all with which we could
 * abstract a global data instance class. As such, we can NOT make the
 * runtime plugin-safe in the above-described sense. As the rsyslog
 * project itself does not need this functionality (and it is quesationable
 * if someone else ever will), we do currently do not make an effort to
 * support it. So if you intend to use rsyslog runtime inside a non-rsyslog
 * plugin system, be careful!
 *
 * The rsyslog runtime library is in general reentrant and thread-safe. There
 * are some intentional exceptions (e.g. inside the msg object). These are
 * documented. Any other threading and reentrency issue can be considered a bug.
 *
 * Module begun 2008-04-16 by Rainer Gerhards
 *
 * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
 *
 * This file is part of the rsyslog runtime library.
 *
 * The rsyslog runtime library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * The rsyslog runtime library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the rsyslog runtime library.  If not, see <http://www.gnu.org/licenses/>.
 *
 * A copy of the GPL can be found in the file "COPYING" in this distribution.
 * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
 */
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "rsyslog.h"
#include "obj.h"
#include "stringbuf.h"
#include "wti.h"
#include "wtp.h"
#include "datetime.h"
#include "queue.h"
#include "conf.h"
#include "rsconf.h"
#include "glbl.h"
#include "errmsg.h"
#include "prop.h"
#include "ruleset.h"
#include "parser.h"
#include "strgen.h"
#include "statsobj.h"
#include "atomic.h"

#ifdef HAVE_PTHREAD_SETSCHEDPARAM
struct sched_param default_sched_param;
pthread_attr_t default_thread_attr;
int default_thr_sched_policy;
#endif

/* forward definitions */
static rsRetVal dfltErrLogger(int, uchar *errMsg);

/* globally visible static data - see comment in rsyslog.h for details */
uchar *glblModPath; /* module load path */
rsRetVal (*glblErrLogger)(int, uchar*) = dfltErrLogger; /* the error logger to use by the errmsg module */

/* static data */
static int iRefCount = 0; /* our refcount - it MUST exist only once inside a process (not thread)
 		             thus it is perfectly OK to use a static. MUST be initialized to 0! */

/* This is the default instance of the error logger. It simply writes the message
 * to stderr. It is expected that this is replaced by the runtime user very early
 * during startup (at least if the default is unsuitable). However, we provide a
 * default so that we can log errors during the intial phase, most importantly
 * during initialization. -- rgerhards. 2008-04-17
 */
static rsRetVal dfltErrLogger(int iErr, uchar *errMsg)
{
	DEFiRet;
	fprintf(stderr, "rsyslog runtime error(%d): %s\n", iErr, errMsg);
	RETiRet;
}


/* set the error log function
 * rgerhards, 2008-04-18
 */
rsRetVal
rsrtSetErrLogger(rsRetVal (*errLogger)(int, uchar*))
{
	DEFiRet;
	assert(errLogger != NULL);
	glblErrLogger = errLogger;
	RETiRet;
}


/* globally initialze the runtime system
 * NOTE: this is NOT thread safe and must not be called concurrently. If that
 * ever poses a problem, we may use proper mutex calls - not considered needed yet.
 * If ppErrObj is provided, it receives a char pointer to the name of the object that
 * caused the problem (if one occured). The caller must never free this pointer. If
 * ppErrObj is NULL, no such information will be provided. pObjIF is the pointer to
 * the "obj" object interface, which may be used to query any other rsyslog objects.
 * rgerhards, 2008-04-16
 */
rsRetVal
rsrtInit(char **ppErrObj, obj_if_t *pObjIF)
{
	DEFiRet;

	if(iRefCount == 0) {
		/* init runtime only if not yet done */
#ifdef HAVE_PTHREAD_SETSCHEDPARAM
	    	CHKiRet(pthread_getschedparam(pthread_self(),
			    		      &default_thr_sched_policy,
					      &default_sched_param));
		CHKiRet(pthread_attr_init(&default_thread_attr));
		CHKiRet(pthread_attr_setschedpolicy(&default_thread_attr,
			    			    default_thr_sched_policy));
		CHKiRet(pthread_attr_setschedparam(&default_thread_attr,
			    			   &default_sched_param));
		CHKiRet(pthread_attr_setinheritsched(&default_thread_attr,
			    			     PTHREAD_EXPLICIT_SCHED));
#endif
		if(ppErrObj != NULL) *ppErrObj = "obj";
		CHKiRet(objClassInit(NULL)); /* *THIS* *MUST* always be the first class initilizer being called! */
		CHKiRet(objGetObjInterface(pObjIF)); /* this provides the root pointer for all other queries */

		/* initialize core classes. We must be very careful with the order of events. Some
		 * classes use others and if we do not initialize them in the right order, we may end
		 * up with an invalid call. The most important thing that can happen is that an error
		 * is detected and needs to be logged, wich in turn requires a broader number of classes
		 * to be available. The solution is that we take care in the order of calls AND use a
		 * class immediately after it is initialized. And, of course, we load those classes
		 * first that we use ourselfs... -- rgerhards, 2008-03-07
		 */
		if(ppErrObj != NULL) *ppErrObj = "statsobj";
		CHKiRet(statsobjClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "prop";
		CHKiRet(propClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "glbl";
		CHKiRet(glblClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "msg";
		CHKiRet(msgClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "ruleset";
		CHKiRet(rulesetClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "wti";
		CHKiRet(wtiClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "wtp";
		CHKiRet(wtpClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "queue";
		CHKiRet(qqueueClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "conf";
		CHKiRet(confClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "parser";
		CHKiRet(parserClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "strgen";
		CHKiRet(strgenClassInit(NULL));
		if(ppErrObj != NULL) *ppErrObj = "rsconf";
		CHKiRet(rsconfClassInit(NULL));

		/* dummy "classes" */
		if(ppErrObj != NULL) *ppErrObj = "str";
		CHKiRet(strInit());
	}

	++iRefCount;
	dbgprintf("rsyslog runtime initialized, version %s, current users %d\n", VERSION, iRefCount);

finalize_it:
	RETiRet;
}


/* globally de-initialze the runtime system
 * NOTE: this is NOT thread safe and must not be called concurrently. If that
 * ever poses a problem, we may use proper mutex calls - not considered needed yet.
 * This function must be provided with the caller's obj object pointer. This is
 * automatically deinitialized by the runtime system.
 * rgerhards, 2008-04-16
 */
rsRetVal
rsrtExit(void)
{
	DEFiRet;

	if(iRefCount == 1) {
		/* do actual de-init only if we are the last runtime user */
		confClassExit();
		glblClassExit();
		rulesetClassExit();

		objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */
	}

	--iRefCount;
	/* TODO we must deinit this pointer! pObjIF = NULL; / * no longer exists for this caller */

	dbgprintf("rsyslog runtime de-initialized, current users %d\n", iRefCount);

	RETiRet;
}


/* returns 0 if the rsyslog runtime is not initialized and another value
 * if it is. This function is primarily meant to be used by runtime functions
 * itself. However, it is safe to call it before initializing the runtime.
 * Plugins should NOT rely on this function. The reason is that another caller
 * may have already initialized it but deinits it before this plugin is done.
 * So for plugins and like architectures, the right course of action is to
 * call rsrtInit() and rsrtExit(), which can be called by multiple callers.
 * rgerhards, 2008-04-16
 */
int rsrtIsInit(void)
{
	return iRefCount;
}


/* vim:set ai:
 */
