/*
 * Copyright (c) 2005 William Pitcock, et al.
 * The rights to this code are as documented in doc/LICENSE.
 *
 * This file contains data structures concerning modules.
 *
 */

#ifndef MODULE_H
#define MODULE_H

#include "privs.h"
#include "abirev.h"

typedef enum {
	MODULE_UNLOAD_INTENT_PERM,
	MODULE_UNLOAD_INTENT_RELOAD,
} module_unload_intent_t;

typedef enum {
	MODULE_UNLOAD_CAPABILITY_OK,
	MODULE_UNLOAD_CAPABILITY_NEVER,
	MODULE_UNLOAD_CAPABILITY_RELOAD_ONLY,
} module_unload_capability_t;

typedef struct module_ module_t;
typedef struct v4_moduleheader_ v4_moduleheader_t;

typedef void (*module_unload_handler_t)(module_t *, module_unload_intent_t);

/* Module structure. Might be a loaded .so module, or something else that
 * behaves as a module for dependency purposes (perl script, etc).
 */
struct module_ {
	char name[BUFSIZE];
	char modpath[BUFSIZE];
	module_unload_capability_t can_unload;

	unsigned int mflags;

	/* These three are real-module-specific. Either all will be set, or all
	 * will be null.
	 */
	v4_moduleheader_t *header;
	void *address;
	mowgli_module_t *handle;

	/* If this module is not a loaded .so (the above three are null), and
	 * can_unload is not never, then * this must be set to a working unload
	 * function.
	 */
	module_unload_handler_t unload_handler;

	mowgli_list_t dephost;
	mowgli_list_t deplist;

	mowgli_list_t symlist;		/* MAPIv2 symbol dependencies. */
};

#define MODTYPE_STANDARD	0
#define MODTYPE_CORE		1 /* Can't be unloaded. */
#define MODTYPE_FAIL		0x8000 /* modinit failed */

#define MAPI_ATHEME_MAGIC	0xdeadbeef
#define MAPI_ATHEME_V4		4

#define MAX_CMD_PARC		20

struct v4_moduleheader_ {
	unsigned int atheme_mod;
	unsigned int abi_ver;
	unsigned int abi_rev;
	const char *serial;
	const char *name;
	module_unload_capability_t can_unload;
	void (*modinit)(module_t *m);
	void (*deinit)(module_unload_intent_t intent);
	const char *vendor;
	const char *version;
};

/* name is the module name we're searching for.
 * path is the likely full path name, which may be ignored.
 * If it is found, set module to the loaded module_t pointer
 */
typedef struct {
	const char *name;
	const char *path;
	module_t *module;
	int handled;
} hook_module_load_t;

#define DECLARE_MODULE_V1(name, norestart, modinit, deinit, ver, ven) \
	v4_moduleheader_t _header = { \
		MAPI_ATHEME_MAGIC, MAPI_ATHEME_V4, \
		CURRENT_ABI_REVISION, "unknown", \
		name, norestart, modinit, deinit, ven, ver \
	}

E void _modinit(module_t *m);
E void _moddeinit(module_unload_intent_t intent);

E void modules_init(void);
E module_t *module_load(const char *filespec);
E void module_load_dir(const char *dirspec);
E void module_load_dir_match(const char *dirspec, const char *pattern);
E void *module_locate_symbol(const char *modname, const char *sym);
E void module_unload(module_t *m, module_unload_intent_t intent);
E module_t *module_find(const char *name);
E module_t *module_find_published(const char *name);
E bool module_request(const char *name);

#define MODULE_TRY_REQUEST_DEPENDENCY(self, modname) \
	if (module_request(modname) == false)				\
	{								\
		(self)->mflags = MODTYPE_FAIL;				\
		return;							\
	}

#define MODULE_TRY_REQUEST_SYMBOL(self, dest, modname, sym) \
	if ((dest = module_locate_symbol(modname, sym)) == NULL)		\
	{									\
		MODULE_TRY_REQUEST_DEPENDENCY(self, modname);			\
		if ((dest = module_locate_symbol(modname, sym)) == NULL)	\
		{								\
			(self)->mflags = MODTYPE_FAIL;				\
			return;							\
		}								\
	}

#define MODULE_CONFLICT(self, modname) \
	if (module_find_published(modname))					\
	{									\
		slog(LG_DEBUG, "module %s conflicts with %s, unloading",	\
		     self->name, modname);					\
		(self)->mflags = MODTYPE_FAIL;					\
		return;								\
	}

typedef struct module_dependency_ {
	char *name;
	module_unload_capability_t can_unload;
} module_dependency_t;

#endif

/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
 * vim:ts=8
 * vim:sw=8
 * vim:noexpandtab
 */
