/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2003
 */

#include "Modules.h"

#if DLOPEN

char *module_types[MOD_LASTONE]={
	"none",
	"logs",
	"exec",
	"env",
	"req"
};

module_s *modules=0;
int nmods=0;

module_t
getModuleType(type)
	const char **type;
{
	loop_t i;

	/* TODO extra module checks
	 * if (type[3]&&type[4]) return -1; // probably binary data */

	for(i=0;i<MOD_LASTONE;i++)
	{
		if (!strcmp(*type,module_types[i]))
			return i;
	}
	return -1;
}

bool
screws_module_del(name)
	const char *name;
{
	int i;
	for (i=0;i<nmods;i++)
	{
		if (!strcmp(modules[i].name,name) )
		{
			// TODO CODE for dropping modules on the fly
			return true;
		}
	}
	return false;
}

/*
 * Add a module
 *
 * RETURNS:
 *  Module name on success
 *  0 - Failure
 */

char *
screws_module_add(file)
	const char *file;
{
	module_t type;
	char *errmsg="";
	char *tmpfile;
    bool ok;
	const char **name;
	//FILE *_stdout,*_stderr;
	struct stat ss;
	char ext[32]; /* default module extension */
	strcpy(ext,".so");
	
	if (modules==0)
		modules=(module_s *)malloc(sizeof(module_s));
	modules=(module_s *)realloc(modules,sizeof(module_s)*(nmods+1));

	/* not absolute path */
	if ( (file[0]!='/') && (file[0]!='.') ) 
	{
		if (!strcmp(ext,file+strlen(file)-strlen(ext)))
			ext[0]=0;
		tmpfile=(char *)malloc(strlen(file)+sizeof(MODULE_PATH)+strlen(ext)+2);
		sprintf(tmpfile,"%s/%s%s",MODULE_PATH,file,ext);
	} else {
		tmpfile=strdup(file);
	}
	modules[nmods].filename=tmpfile;
	if (stat(tmpfile,&ss))
	{
		errmsg="Module not found";
		goto failed;
	}
	modules[nmods]._dlopen=dlopen(tmpfile,RTLD_NOW);//|RTLD_GLOBAL);
	if (!modules[nmods]._dlopen)
	{
		errmsg="Cannot dlopen";
		goto failed;
	}

// TODO Check for no modules repeated.

	/* Get Name */
	name=dlsym(modules[nmods]._dlopen,"name");
	if (!name)
	{
		errmsg="Cannot resolve name";
		goto failed;
	}
	modules[nmods].name=strdup(*name);

	/* Get Init */
	modules[nmods]._init=dlsym(modules[nmods]._dlopen,"init");
	if ((!modules[nmods]._init))
	{
		errmsg="Cannot resolve init function";
		goto failed;
	}
	
	/* Get Exec */
	modules[nmods]._exec=dlsym(modules[nmods]._dlopen,"exec");
	if ((!modules[nmods]._exec))
	{
		errmsg="Cannot resolve exec function";
		goto failed;
	}

	/* Get Type */
	type=getModuleType( dlsym(modules[nmods]._dlopen,"type") );
	if (type==-1) 
	{
		errmsg="Invalid module type";
		goto failed;
	}
	modules[nmods].type=type;	

	/* Module Options */
	modules[nmods].opts=(char **)malloc(sizeof(char *));
	modules[nmods].opts[0]=0;
	modules[nmods].nopts=0;
	
	/* Init Module */
	if (Svr.v>=V_LOAD)
	 printf("[m] init module: %s (%s).\n",modules[nmods].name,file);

	ok=(*modules[nmods]._init)();
	if (!ok)
	{
		printf("[E] Error initializing module '%s'.\n",
			modules[nmods].name);
		//printf("(%s)\n",dlerror());
		exit(1);
	}

	nmods++;
	//screws_module_del(modules[nmods-1].name);
	return modules[nmods-1].name;

	failed:
		printf("[m] Cannot load module: '%s' '%s'.\n",errmsg,tmpfile);
		printf("    dlerror(%s)\n",dlerror());
		free(modules[nmods].filename);
		return false;
}

/*
 * module options
 */

bool
screws_module_option(name,opt)
	const char *name, *opt;
{
	loop_t i=0;

	for (i=0;i<nmods;i++)
	{
		if (!strcmp(name,modules[i].name))
		{
			if (Svr.v>=V_LOAD)
			 printf("-> Add Option '%s' to module '%s'\n",opt,name);

			modules[i].opts[modules[i].nopts]=
					strdup(opt);
			modules[i].nopts++;
			modules[i].opts=(char **)
				realloc(modules[i].opts,
					(modules[i].nopts+1)*sizeof(char **));
			modules[i].opts[modules[i].nopts]=0;
			return true;
		}
	}
	return false;
}

/*
 * return value:
 *   - false - Will exec the next step.
 *   - true  - Do not exec more
 */
bool
execModule(type)
	module_t type;
{
	loop_t i;
	bool (*execfunc)(char **)=0;
	bool ret;

	// TODO mutex locks for add/remove modules
	
	for(i=0;i<nmods;i++)
	{
		if (modules[i].type==type)
		{
		fflush(stdout);
		execfunc=modules[i]._exec;
		ret=(*execfunc)(modules[i].opts);
		fflush(stdout);
		if (ret) return true;
		//return ret;
		}
	}

	return false;
}

#endif
