#include "jabberd.h"
#include "single.h"
#define MAX_INCLUDE_NESTING 20
extern HASHTABLE cmd__line;
extern pool jabberd__runtime;
HASHTABLE instance__ids=NULL;
typedef struct shutdown_list
{
pool p;
shutdown_func f;
void *arg;
struct shutdown_list *next;
} _sd_list, *sd_list;
sd_list shutdown__list=NULL;
xmlnode greymatter__ = NULL;
void do_include(int nesting_level,xmlnode x)
{
xmlnode cur;
cur=xmlnode_get_firstchild(x);
for(;cur!=NULL;)
{
if(cur->type!=NTYPE_TAG)
{
cur=xmlnode_get_nextsibling(cur);
continue;
}
if(j_strcmp(xmlnode_get_name(cur),"jabberd:include")==0)
{
xmlnode include;
char *include_file=xmlnode_get_data(cur);
xmlnode include_x=xmlnode_file(include_file);
if(nesting_level>MAX_INCLUDE_NESTING)
{
fprintf(stderr, "ERROR: Included files nested %d levels deep. Possible Recursion\n",nesting_level);
exit(1);
}
include=cur;
xmlnode_hide(include);
if(j_strcmp(xmlnode_get_name(xmlnode_get_parent(cur)),xmlnode_get_name(include_x))==0)
xmlnode_insert_node(x,xmlnode_get_firstchild(include_x));
else
xmlnode_insert_node(x,include_x);
do_include(nesting_level+1,include_x);
cur=xmlnode_get_nextsibling(cur);
continue;
}
else
{
do_include(nesting_level,cur);
}
cur=xmlnode_get_nextsibling(cur);
}
}
void cmdline_replace(xmlnode x)
{
char *flag;
char *replace_text;
xmlnode cur=xmlnode_get_firstchild(x);
for(;cur!=NULL;cur=xmlnode_get_nextsibling(cur))
{
if(cur->type!=NTYPE_TAG)continue;
if(j_strcmp(xmlnode_get_name(cur),"jabberd:cmdline")!=0)
{
cmdline_replace(cur);
continue;
}
flag=xmlnode_get_attrib(cur,"flag");
replace_text=ghash_get(cmd__line,flag);
if(replace_text==NULL) replace_text=xmlnode_get_data(cur);
xmlnode_hide(xmlnode_get_firstchild(x));
xmlnode_insert_cdata(x,replace_text,-1);
break;
}
}
void show_pid(xmlnode x)
{
xmlnode pidfile;
char *path;
char pidstr[16];
int fd;
pid_t pid;
if(jabberd__signalflag == SIGHUP) return;
pidfile = xmlnode_get_tag(x, "pidfile");
if(pidfile == NULL)
return;
path = xmlnode_get_data(pidfile);
if(path == NULL)
{
return;
}
unlink(path);
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
if(fd < 0)
{
fprintf(stderr, "Error opening pidfile at [%s]. %s.", path, strerror(errno) );
exit(1);
}
pid = getpid();
snprintf(pidstr, 16, "%d", pid);
write(fd, &pidstr, strlen(pidstr));
close(fd);
return;
}
int configurate(char *file)
{
char def[] = "jabber.xml";
char *realfile = (char *)def;
xmlnode incl;
char *c;
if(file != NULL)
realfile = file;
greymatter__ = xmlnode_file(realfile);
#ifdef SINGLE
if(greymatter__ == NULL)
greymatter__ = xmlnode_str(SINGLE_CONFIG, strlen(SINGLE_CONFIG));
#endif
if(greymatter__ == NULL)
{
fprintf(stderr, "Configuration parsing using %s failed: %s\n",realfile,xmlnode_file_borked(realfile));
return 1;
}
if((realfile = ghash_get(cmd__line,"i")) != NULL)
while(realfile != NULL)
{
c = strchr(realfile,',');
if(c != NULL)
{
*c = '\0';
c++;
}
if((incl = xmlnode_file(realfile)) == NULL)
{
fprintf(stderr, "Configuration parsing included file %s failed: %s\n",realfile,xmlnode_file_borked(realfile));
return 1;
}else{
xmlnode_insert_tag_node(greymatter__,incl);
xmlnode_free(incl);
}
realfile = c;
}
do_include(0,greymatter__);
cmdline_replace(greymatter__);
show_pid(greymatter__);
return 0;
}
int config_reload(char *file)
{
xmlnode old_config=greymatter__;
int retval=configurate(file);
if(retval)
{
greymatter__=old_config;
return 1;
}
else
{
xmlnode_free(old_config);
return 0;
}
}
typedef struct cfg_struct
{
char *node;
cfhandler f;
void *arg;
struct cfg_struct *next;
} *cfg, _cfg;
cfg cfhandlers__ = NULL;
pool cfhandlers__p = NULL;
void register_config(char *node, cfhandler f, void *arg)
{
cfg newg;
cfhandlers__p = jabberd__runtime;
newg = pmalloc_x(cfhandlers__p, sizeof(_cfg), 0);
newg->node = pstrdup(cfhandlers__p,node);
newg->f = f;
newg->arg = arg;
newg->next = cfhandlers__;
cfhandlers__ = newg;
}
cfg cfget(char *node)
{
cfg next = NULL;
for(next = cfhandlers__; next != NULL && strcmp(node,next->node) != 0; next = next->next);
return next;
}
int _instance_cleanup(void *arg,const void *key,void *data)
{
instance i=(instance)data;
unregister_instance(i,i->id);
ghash_remove(instance__ids, i->id);
while(i->hds)
{
handel h=i->hds->next;
pool_free(i->hds->p);
i->hds=h;
}
pool_free(i->p);
return 1;
}
int instance_startup(xmlnode x, int exec)
{
ptype type;
xmlnode cur;
cfg c;
instance newi = NULL;
pool p;
type = p_NONE;
if(j_strcmp(xmlnode_get_name(x), "pidfile") == 0)
return 0;
if(j_strcmp(xmlnode_get_name(x), "io") == 0)
return 0;
if(j_strcmp(xmlnode_get_name(x), "log") == 0)
type = p_LOG;
if(j_strcmp(xmlnode_get_name(x), "xdb") == 0)
type = p_XDB;
if(j_strcmp(xmlnode_get_name(x), "service") == 0)
type = p_NORM;
if(type == p_NONE || xmlnode_get_attrib(x, "id") == NULL || xmlnode_get_firstchild(x) == NULL)
{
fprintf(stderr, "Configuration error in:\n%s\n", xmlnode2str(x));
if(type == p_NONE)
{
fprintf(stderr, "ERROR: Invalid Tag type: %s\n",xmlnode_get_name(x));
}
if(xmlnode_get_attrib(x, "id") == NULL)
{
fprintf(stderr, "ERROR: Section needs an 'id' attribute\n");
}
if(xmlnode_get_firstchild(x)==NULL)
{
fprintf(stderr, "ERROR: Section Has no data in it\n");
}
return -1;
}
if(exec == 1)
{
newi = ghash_get(instance__ids, xmlnode_get_attrib(x,"id"));
if(newi != NULL)
{
fprintf(stderr, "ERROR: Multiple Instances with same id: %s\n",xmlnode_get_attrib(x,"id"));
return -1;
}
}
if(exec)
{
jid temp;
p = pool_new();
newi = pmalloc_x(p, sizeof(_instance), 0);
newi->id = pstrdup(p,xmlnode_get_attrib(x,"id"));
newi->type = type;
newi->p = p;
newi->x = x;
temp = jid_new(p, newi->id);
if(temp == NULL || j_strcmp(temp->server, newi->id) != 0)
{
log_alert(NULL, "ERROR: Invalid id name: %s\n",newi->id);
pool_free(p);
return -1;
}
ghash_put(instance__ids,newi->id,newi);
register_instance(newi,newi->id);
}
for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur))
{
if(xmlnode_get_type(cur) != NTYPE_TAG)
continue;
c = cfget(xmlnode_get_name(cur));
if(c == NULL && xmlnode_get_attrib(cur, "xmlns") != NULL)
continue;
if(c == NULL || (c->f)(newi, cur, c->arg) == r_ERR)
{
char *error = pstrdup(xmlnode_pool(cur), xmlnode_get_attrib(cur,"error"));
xmlnode_hide_attrib(cur, "error");
fprintf(stderr, "Invalid Configuration in instance '%s':\n%s\n",xmlnode_get_attrib(x,"id"),xmlnode2str(cur));
if(c == NULL)
fprintf(stderr, "ERROR: Unknown Base Tag: %s\n",xmlnode_get_name(cur));
else if(error != NULL)
fprintf(stderr, "ERROR: Base Handler Returned an Error:\n%s\n", error);
return -1;
}
}
return 0;
}
int configo(int exec)
{
xmlnode cur;
if(instance__ids==NULL)
instance__ids = ghash_create_pool(jabberd__runtime, 19,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
for(cur = xmlnode_get_firstchild(greymatter__); cur != NULL; cur = xmlnode_get_nextsibling(cur))
{
if(xmlnode_get_type(cur) != NTYPE_TAG || strcmp(xmlnode_get_name(cur),"base") == 0)
continue;
if(instance_startup(cur, exec))
{
return 1;
}
}
return 0;
}
void instance_shutdown(instance i)
{
if(i != NULL)
{
unregister_instance(i,i->id);
ghash_remove(instance__ids, i->id);
while(i->hds)
{
handel h=i->hds->next;
pool_free(i->hds->p);
i->hds=h;
}
pool_free(i->p);
}
else
{
ghash_walk(instance__ids, _instance_cleanup, NULL);
}
}
void shutdown_callbacks(void)
{
while(shutdown__list)
{
sd_list s=shutdown__list->next;
(*shutdown__list->f)(shutdown__list->arg);
pool_free(shutdown__list->p);
shutdown__list=s;
}
}
void register_shutdown(shutdown_func f,void *arg)
{
pool p;
sd_list new;
if(f==NULL) return;
p=pool_new();
new=pmalloco(p,sizeof(_sd_list));
new->p=p;
new->f=f;
new->arg=arg;
new->next=shutdown__list;
shutdown__list=new;
}