/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program 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.

**********************************************************************/



#include	<stdlib.h>
#ifdef VA2
#include	<varargs.h>
#else
#include	<stdarg.h>
#endif
#include	<fcntl.h>


#include	"pri_level.h"
#include	"lock_level.h"
#include	"memory_debug.h"
#include	"xl.h"
#include	"stream.h"
#include	"queue.h"
#include	"utils.h"
#include	"xlerror.h"

#define LOG_QUEUE_SIZE	1000

typedef struct sym_data_type {
	char *		name;
	L_CHAR *	(*func)();
} SYM_DATA_TYPE;

typedef struct log_lock_path_list {
	struct log_lock_path_list *	next;
	L_CHAR *			path;
	unsigned int			access;
	CALL_LOCK_DESCRIPTER		id;
	STREAM * 			st;
	unsigned			lock:1;
} LOG_LOCK_PATH_LIST;

char * log_level_msg[LOG_MAX_LEVEL] = {
	"TEST",
	"DEBUG",
	"WARNING",
	"ERROR",
	"SYSTEM",
	"MESSAGE"
};

char * log_layer[LOG_LAYER_MAX] = {
	"NANE",
	"LIB",
	"XLoHTTP",
	"STREAM",
	"XL",
	"GB",
	"UI"
};

L_CHAR * get_preference();
L_CHAR * get_data_info();

SYM_DATA_TYPE sym_data_list[] = {
	{"preference",get_preference},
	{"time",get_data_info},
	{"level",get_data_info},
	{"URL.proto",get_data_info},
	{"URL.server",get_data_info},
	{"URL.port",get_data_info},
	{"URL.agent",get_data_info},
	{"URL.database",get_data_info},
	{"URL.resource",get_data_info},
	{"user",get_data_info},
	{"key",get_data_info},
	{"data",get_data_info},
	{0,0}
};


#define FS_ASTARISK	1
#define FS_EQU		2
#define FS_LARGE	3
#define FS_EQU_LARGE	4
#define FS_SYMBOL	5

char *fs_table[] = {
	0,
	"*",
	"=",
	"<",
	"<=",

	"time",
	"level",
	"URL.proto",
	"URL.server",
	"URL.port",
	"URL.agent",
	"URL.database",
	"URL.resource",
	"user",
	"key",
	"data",
	0
};

ATTR_TABLE log_cond_attr_table[] = {
	{"unlock",LO_UNLOCK},
	{"lock",0},
	{"time",LO_TIME},
	{"level",LO_LEVEL},
	{"URL",LO_URL},
	{"layer",LO_LAYER},
	{"user",LO_USER},
	{"key",LO_KEY},
	{"data",LO_DATA},
	{"all",LO_ALL},
	{0,0}
};





extern L_CHAR * agent_name;
SYS_QUEUE	log_queue;
SYS_QUEUE	log_wait_queue;
SYS_QUEUE	log_poll_queue;
LOG_COND *	condition_list;
LOG_LOCK_PATH_LIST * 	ll_path_list;
LOG_LOCK_PATH_LIST * _lock_llpl(L_CHAR * path);
void _unlock_llpl(LOG_LOCK_PATH_LIST * pl);


void div_logfile(L_CHAR * path,int log_size,int log_filenos,
			LOG_LOCK_PATH_LIST *);
void llpl_check();
void log_queue_task1();
void log_queue_task2();
LOG_LOCK_PATH_LIST * _search_llpl(L_CHAR * path);
LOG_LOCK_PATH_LIST * _invalid_llpl(L_CHAR * path);

int log_session;
XL_SEXP * xl_get_log();
SEM lock_log;
XL_SEXP * xl_set_log_condition();
XL_SEXP * xl_fetch_log();

unsigned int last_log_ins_time;
unsigned int last_log_del_time;

void
setup_value(XLISP_ENV * env0,char ** table,int max)
{
int i;
	for ( i = 0 ; i < max ; i ++ ) {
		set_env(env0,
			l_string(std_cm,table[i]),
			get_integer(i,0));
	}
}

void
init_log(XLISP_ENV * env0,XLISP_ENV * env1)
{
	lock_log = new_lock(LL_LOG);
	memset(&log_queue,0,sizeof(SYS_QUEUE));
	log_queue.flags = QF_FIFO;
	log_queue.gc_func = 0;
	log_queue.gc_get = 0;
	log_queue.key_func = 0;
	log_queue.pri = 0;
	log_queue.total_limit = LOG_QUEUE_SIZE;
	setup_queue(&log_queue);

	memset(&log_wait_queue,0,sizeof(SYS_QUEUE));
	log_wait_queue.flags = QF_FIFO;
	log_wait_queue.gc_func = 0;
	log_wait_queue.gc_get = 0;
	log_wait_queue.key_func = 0;
	log_wait_queue.pri = 0;
	log_wait_queue.total_limit = 4*LOG_QUEUE_SIZE;
	setup_queue(&log_wait_queue);
	log_session = open_session(SEST_OPTIMIZE);

	__log_vprintf = xl_log_vprintf;

	set_env(env0,l_string(std_cm,"GetLog"),
		get_func_prim(xl_get_log,FO_APPLICATIVE,0,1,1));
	set_env(env1,l_string(std_cm,"SetLogCondition"),
		get_func_prim(xl_set_log_condition,FO_NORMAL,0,1,-1));
	set_env(env1,l_string(std_cm,"FetchLog"),
		get_func_prim(xl_fetch_log,FO_APPLICATIVE,0,1,1));
	setup_value(env0,log_level_msg,LOG_MAX_LEVEL);
	setup_value(env0,log_layer,LOG_LAYER_MAX);

	create_task(log_queue_task1,0,PRI_LOG);

//	new_tick(llpl_check,2,0);
}


LOG_LOCK_PATH_LIST *
_search_llpl(L_CHAR * path)
{
LOG_LOCK_PATH_LIST * ret;
	ret = ll_path_list;
	for ( ; ret ; ret = ret->next )
		if ( l_strcmp(path,ret->path) == 0 )
			return ret;
	ret = d_alloc(sizeof(*ret));
	ret->path = ll_copy_str(path);
	ret->access = get_xltime();
	ret->st = 0;
	ret->id = cl_invalid();
	ret->next= ll_path_list;
	ll_path_list = ret;
	return ret;
}

LOG_LOCK_PATH_LIST * 
_lock_llpl(L_CHAR * path)
{
LOG_LOCK_PATH_LIST * pl;
	pl = _search_llpl(path);
	if ( cl_error_check(pl->id) )
		pl->id = call_lock(path,CLT_WRITE_LOCK,0,0);
	pl->lock = 1;
	return pl;
}

LOG_LOCK_PATH_LIST * 
_invalid_llpl(L_CHAR * path)
{
LOG_LOCK_PATH_LIST * pl;
	pl = _search_llpl(path);
	if ( cl_error_check(pl->id) )
		pl->id = cl_invalid();
	pl->lock = 1;
	return pl;
}


void
_unlock_llpl(LOG_LOCK_PATH_LIST * pl)
{
	if ( pl == 0 )
		return;
	pl->lock = 0;
}


void
llpl_check(int f)
{
LOG_LOCK_PATH_LIST ** plp;
LOG_LOCK_PATH_LIST * pl;
XL_INTERPRETER * xli;

	xli = get_my_xli();
	if ( xli == 0 ) {
		xli = new_xl_interpreter();
		xli->a_type = XLA_SELF;
		setup_i(xli);
	}


	lock_task(lock_log);
	for ( plp = &ll_path_list ; *plp ; ) {
		pl = *plp;
		if ( pl->lock ) {
			plp = &(*plp)->next;
			continue;
		}
		if ( f == 0 ) {
			if ( get_xltime() - pl->access < 2 ) {
				plp = &(*plp)->next;
				continue;
			}
		}
		if ( pl->st )
			s_close(pl->st);
		if ( cl_error_check(pl->id) )
			goto err;
		call_unlock(pl->id);
	err:
		*plp = pl->next;
		d_f_ree(pl->path);
		d_f_ree(pl);
	}
	unlock_task(lock_log,"llpl_check");
}



void
free_v_log_node_data(LOG_NODE_DATA * ld)
{
int i;
	for ( i = 0 ; i < LOG_NODE_ELEMENT_LEN ; i ++ ) {
		if ( (1<<i) & LOG_NODE_DATA_INT_PATTERN )
			continue;
		if ( ld->_chr[i] )
			d_f_ree( ld->_chr[i] );
	}
}

void
free_log_node(LOG_NODE * ln)
{
	free_v_log_node_data(&ln->d);
	if ( ln->h.key )
		d_f_ree(ln->h.key);
	d_f_ree(ln);
}


char * get_logfile(L_CHAR * path,int no)
{
char * ret;
int len;
	ret = ln_copy_str(std_cm,path);
	len = strlen(ret);
	ret = d_re_alloc(ret,len + 50);
	sprintf(&ret[len],"log%i",no);
	return ret;
}

void
div_logfile(L_CHAR * path,int log_size,int log_filenos,
	LOG_LOCK_PATH_LIST * id)
{
int fd;
char * filename1,* filename2;
int size;
int no;
int er;
	filename1 = get_logfile(path,0);
	fd = u_open(filename1,O_RDONLY);
	if ( fd < 0 )
		return;
	size = u_lseek(fd,0,SEEK_END);
retry_c:
	er = u_close(fd);
	if ( er < 0 && errno == EINTR )
		goto retry_c;
	if ( size < log_size ) {
		d_f_ree(filename1);
		return;
	}
	d_f_ree(filename1);

	if ( id->st ) {
		s_close(id->st);
		id->st = 0;
	}

	filename1 = get_logfile(path,log_filenos-1);
	u_unlink(filename1);
	d_f_ree(filename1);
	for ( no = log_filenos-2 ; no >= 0 ; no -- ) {
		filename1 = get_logfile(path,no);
		filename2 = get_logfile(path,no+1);
		rename(filename1,filename2);
		d_f_ree(filename1);
		d_f_ree(filename2);
	}
}

int cmp_log_node_data(LOG_NODE_DATA * d,LOG_COND * lc)
{
int i;
int size,size1;
	for ( i = 0 ; i < LOG_NODE_ELEMENT_LEN ; i ++ )
		switch ( lc->op[i] ) {
		case LC_OP_NONE:
			break;
		case LC_OP_EQU:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				if ( lc->low._int[i] == d->_int[i] )
					break;
			}
			else if ( lc->low._chr[i] == 0 &&
					d->_chr[i] == 0 )
				break;
			else if ( lc->low._chr[i] == 0 )
				return -1;
			else if ( d->_chr[i] == 0 )
				return -1;
			else {
				if ( l_strcmp(lc->low._chr[i],
						d->_chr[i]) == 0 )
					break;
			}
			return -1;
		case LC_OP_FLONT:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				break;
			}
			else if ( lc->low._chr[i] == 0 ||
					d->_chr[i] == 0 )
				return -1;
			else {
				size = l_strlen(lc->low._chr[i]);
				size1 = l_strlen(d->_chr[i]);
				if ( size1 < size )
					return -1;
				if ( memcmp(lc->low._chr[i],
						d->_chr[i],
						size*sizeof(L_CHAR)) == 0 )
					break;
			}
			return -1;
		case LC_OP_BACK:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				break;
			}
			else if ( lc->low._chr[i] == 0 ||
					d->_chr[i] == 0 )
				return -1;
			else {
				size = l_strlen(lc->low._chr[i]);
				size1 = l_strlen(d->_chr[i]);
				if ( size1 < size )
					return -1;
				if ( memcmp(lc->low._chr[i],
						&d->_chr[i][size1-size],
						size*sizeof(L_CHAR)) == 0 )
					break;
			}
			return -1;
		case LC_OP_BOUND_NN:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				if ( lc->low._int[i] < d->_int[i] &&
						d->_int[i] < lc->high._int[i] )
					break;
			}
			else {
				if ( l_strcmp(lc->low._chr[i],d->_chr[i]) < 0 &&
					l_strcmp(d->_chr[i],lc->high._chr[i]) < 0 )
					break;
			}
			return -1;
		case LC_OP_BOUND_EN:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				if ( lc->low._int[i] <= d->_int[i] &&
						d->_int[i] < lc->high._int[i] )
					break;
			}
			else {
				if ( l_strcmp(lc->low._chr[i],d->_chr[i]) <= 0 &&
					l_strcmp(d->_chr[i],lc->high._chr[i]) < 0 )
					break;
			}
			return -1;
		case LC_OP_BOUND_NE:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				if ( lc->low._int[i] < d->_int[i] &&
						d->_int[i] <= lc->high._int[i] )
					break;
			}
			else {
				if ( l_strcmp(lc->low._chr[i],d->_chr[i]) < 0 &&
					l_strcmp(d->_chr[i],lc->high._chr[i]) <= 0 )
					break;
			}
			return -1;
		case LC_OP_BOUND_EE:
			if ( LOG_NODE_DATA_INT_PATTERN & (1<<i) ) {
				if ( lc->low._int[i] <= d->_int[i] &&
						d->_int[i] <= lc->high._int[i] )
					break;
			}
			else {
				if ( l_strcmp(lc->low._chr[i],d->_chr[i]) <= 0 &&
					l_strcmp(d->_chr[i],lc->high._chr[i]) <= 0 )
					break;
			}
			return -1;
		default:
			er_panic("cmp_log_node_data");
		}
	return 0;
}

void
copy_log_node_data(LOG_NODE_DATA * ln1,LOG_NODE_DATA * ln2)
{
	ln1->d.time = ln2->d.time;
	ln1->d.level = ln2->d.level;
	copy_url(&ln1->d.u,&ln2->d.u);
	ln1->d.layer = ln2->d.layer;
	ln1->d.user = ll_copy_str(ln2->d.user);
	ln1->d.key = ll_copy_str(ln2->d.key);
	ln1->d.data = ll_copy_str(ln2->d.data);
}

LOG_NODE * copy_log_node(LOG_NODE * ln)
{
LOG_NODE * ret;
	ret = d_alloc(sizeof(LOG_NODE));
	memset(&ret,0,sizeof(*ret));
	copy_log_node_data(&ret->d,&ln->d);
	ret->wait_action = ln->wait_action;
	return ret;
}


void
set_log_condition(LOG_COND * cond)
{
LOG_COND ** cp;
	lock_task(lock_log);
	cond->next = 0;
	for ( cp = &condition_list ; *cp ; cp = &(*cp)->next );
	*cp = cond;
	unlock_task(lock_log,"set_log_condition");
}

void
free_log_action(LOG_ACTION * a)
{
	if ( a->path )
		d_f_ree(a->path);
	d_f_ree(a);
}

void
free_log_cond(LOG_COND * lc)
{
LOG_ACTION * a;
	free_v_log_node_data(&lc->low);
	free_v_log_node_data(&lc->high);
	for ( ; lc->action_list ; ) {
		a = lc->action_list;
		lc->action_list = a->next;
		free_log_action(a);
	}
	d_f_ree(lc);
}

void
reset_log_condition()
{
LOG_COND * lc;
	lock_task(lock_log);
	for ( ; condition_list ; ) {
		lc = condition_list;
		condition_list = lc->next;
		free_log_cond(lc);
	}
	unlock_task(lock_log,"reset_log_condition");
}

L_CHAR *
get_preference()
{
char * p;
	p = get_preference_path();
	return nl_copy_str(std_cm,p);
}

L_CHAR *
get_data_info(LOG_NODE_DATA * lnd,L_CHAR * sym)
{
int f;
char buf[20];
	f = get_log_cond_symbol(sym);
	if ( f < FS_SYMBOL )
		er_panic("get_data_info");
	f = f-FS_SYMBOL;
	if ( LOG_NODE_DATA_INT_PATTERN & (1<<f) ) {
		sprintf(buf,"%i",lnd->_int[f]);
		return nl_copy_str(std_cm,buf);
	}
	else {
		if ( lnd->_chr[f] )
			return ll_copy_str(lnd->_chr[f]);
		else	return nl_copy_str(std_cm,"");
	}
}



L_CHAR *
get_sym_data(LOG_NODE_DATA * lnd,L_CHAR * sym)
{
SYM_DATA_TYPE * sdp;
	for ( sdp = sym_data_list ; sdp->name ; sdp ++ ) {
		if ( l_strcmp(sym,l_string(std_cm,sdp->name)) )
			continue;
		return (*sdp->func)(lnd,sym);
	}
	return 0;
}


L_CHAR * 
parse_dir_path(LOG_NODE_DATA * lnd,L_CHAR * path)
{
L_CHAR * ret;
int len,d;
L_CHAR * p1,* p2;
L_CHAR * p3;
L_CHAR * sym;
int s_len;
L_CHAR * sym_data;
	len = l_strlen(path);
	ret = d_alloc(len*sizeof(L_CHAR));
	p2 = ret;
	for ( p1 = path ; *p1 ; ) {
		if ( *p1 != '\\' ) {
			d = p2 - ret;
			if ( d+1 >= len ) {
				ret = d_re_alloc(ret,2*len*sizeof(L_CHAR));
				p2 = ret + d;
				len = 2*len;
			}
			*p2 = *p1;
			p1 ++;
			p2 ++;
			continue;
		}
		p1 ++;
		if ( *p1 != '{' ) {
			*p2++ = '\\';
			*p2++ = '{';
			p1 ++;
			continue;
		}
		p1 ++;
		p3 = p1;
		for ( ; *p3 && *p3 != '}' ; p3 ++ );
		if ( *p3 == 0 ) {
			*p2++ = '\\';
			*p2++ = '{';
			continue;
		}
		sym = d_alloc((p3 - p1 + 1)*sizeof(L_CHAR));
		memcpy(sym,p1,(s_len = p3-p1)*sizeof(L_CHAR));
		sym[s_len] = 0;
		sym_data = get_sym_data(lnd,sym);
		d_f_ree(sym);
		if ( sym_data == 0 ) {
			*p2++ = '\\';
			*p2++ = '{';
			continue;
		}
		d = p2 - ret;
		s_len = l_strlen(sym_data);
		for ( ; d+s_len >= len ; ) {
			ret = d_re_alloc(ret,2*len*sizeof(L_CHAR));
			p2 = ret + d;
			len = 2*len;
		}
		memcpy(p2,sym_data,s_len*sizeof(L_CHAR));
		p2 += s_len;
		p1 = p3+1;
		d_f_ree(sym_data);
	}
	d = p2 - ret;
	ret = d_re_alloc(ret,(d+1)*sizeof(L_CHAR));
	ret[d] = 0;
	return ret;
}


void
do_log_action_save_file(
	LOG_ACTION * a,
	LOG_NODE * ld)
{
LOG_LOCK_PATH_LIST * id;
char * buf;
char * filename;
L_CHAR * path;

	path = parse_dir_path(&ld->d,a->path);
	if ( path == 0 )
		return;
	gc_push(0,0,"do_log_action_save_file");
	if ( (a->option & LO_UNLOCK) == 0 )
		id = _lock_llpl(path);
	else	id = _invalid_llpl(path);
	if ( a->size ) {
		div_logfile(path,a->size,a->div_nos,id);
		filename = get_logfile(path,0);
		if ( id->st == 0 ) {
			id->st = s_open_file(filename,
				O_RDWR|O_CREAT|O_APPEND,0644);
		}
		d_f_ree(filename);
	}
	else {
		if ( id->st == 0 )
		id->st = s_open_file(n_string(std_cm,path),O_RDWR|O_CREAT|O_APPEND,0644);
	}
	if ( id->st == 0 ) {
		_unlock_llpl(id);
		gc_pop(0,0);
		d_f_ree(path);
		return;
	}
	if ( ld->d.d.level >= LOG_MAX_LEVEL )
		ld->d.d.level = LOG_SYSTEM;
	buf = get_xltime_str(ld->d.d.time,TFMT_UNIX);
	buf[strlen(buf)-1] = 0;
	if ( a->option & (LO_TIME|LO_LEVEL|LO_URL|LO_USER|LO_LAYER) ) {
	L_CHAR * u_str;
		s_printf(id->st,"[");
		if ( a->option & LO_LEVEL )
			s_printf(id->st,"%s ",log_level_msg[ld->d.d.level]);
		if ( a->option & LO_TIME )
			s_printf(id->st,"%s ",buf);
		u_str = get_url_str2(&ld->d.d.u);
		if ( (a->option & LO_URL) ) {
			if ( u_str )
				s_printf(id->st,"%ls ",u_str);
			else {
				if ( ld->d.d.u.server )
					s_printf(id->st,"%ls",ld->d.d.u.server);
				if ( ld->d.d.u.port )
					s_printf(id->st,":%i",ld->d.d.u.port);
				if ( ld->d.d.u.agent )
					s_printf(id->st,"@%ls",ld->d.d.u.agent);
				s_printf(id->st," ");
			}
		}
		if ( (a->option & LO_USER) && ld->d.d.user )
			s_printf(id->st,"%ls ",ld->d.d.user);
		if ( a->option & LO_LAYER )
			s_printf(id->st,"%s ",log_layer[ld->d.d.layer]);
		s_printf(id->st,"] ");
	}
	if ( (a->option & LO_DATA) && ld->d.d.data ) {
	int len;
		s_printf(id->st,"%ls",ld->d.d.data);
		len = l_strlen(ld->d.d.data);
		if ( len == 0 )
			s_printf(id->st,"\n");
		else switch ( ld->d.d.data[len-1] ) {
		case '\n':
		case '\r':
			break;
		default:
			s_printf(id->st,"\n");
		}
	}
	else	s_printf(id->st,"\n");
	d_f_ree(buf);
	_unlock_llpl(id);
	gc_pop(0,0);
	d_f_ree(path);
}

XL_SEXP *
get_log_node_sexp(LOG_NODE_DATA * ld)
{
XL_SEXP * send;
	send = 0;
	if ( ld->d.data )
		send = cons(
			List(n_get_symbol("data"),
				get_string(ld->d.data),
				-1),
			send);
	if ( ld->d.user )
		send = cons(
			List(n_get_symbol("user"),
				get_string(ld->d.user),
				-1),
			send);
	if ( ld->d.layer )
		send = cons(
			List(n_get_symbol("layer"),
				n_get_string(log_layer[ld->d.layer]),
				-1),
			send);
	send = cons(
		List(n_get_symbol("URL"),
			get_string(get_url_str2(&ld->d.u)),
			-1),
		send);
	send = cons(
		List(n_get_symbol("level"),
			n_get_string(log_level_msg[ld->d.level]),
			-1),
		send);
	send = cons(
		List(n_get_symbol("time"),
			get_integer(ld->d.time,l_string(std_cm,"sec")),
			-1),
		send);
	send = cons(n_get_symbol("Log"),send);
	return send;
}

void
do_log_action_relay(LOG_ACTION * a,LOG_NODE * ld)
{
XL_SEXP * send;
URL target;
	gc_push(0,0,"do_log_action_rerlay");
	send = get_log_node_sexp(&ld->d);
	get_url2(&target,a->path);
	remote_session(
		gblisp_top_env0,
		log_session,
		&target,
		l_string(std_cm,"server"),
		l_string(std_cm,"user"),
		l_string(std_cm,"Get"),
		List(send,-1),
		0,0,0,0);
	gc_pop(0,0);
	free_url(&target);
}

void
do_log_action_wait(LOG_ACTION * a,LOG_NODE * ld)
{
LOG_NODE * ld1, * ld2;
	ld1 = copy_log_node(ld);
	ld1->wait_action = a;

last_log_ins_time = get_xltime();

	for ( ; insert_queue(&log_wait_queue,ld1,0) < 0 ; ) {
ss_printf("LOG OVER FLOW %i\n",(int)getpid());
		ld2 = delete_queue(&log_wait_queue,0,0,0);
		free_log_node(ld2);
	}
}

void
do_log_action(LOG_ACTION * a,LOG_NODE * ld)
{
	switch ( a->type ) {
	case LAT_SAVE_FILE:
		do_log_action_save_file(a,ld);
		break;
	case LAT_CONSOLE:
		ss_printf("%ls",ld->d.d.data);
		if ( l_strlen(ld->d.d.data) > 0 )
			switch ( ld->d.d.data[l_strlen(ld->d.d.data)-1] ) {
			case '\n':
			case '\r':
				break;
			default:
				ss_printf("\n");
			}
		break;
	case LAT_RELAY:
		do_log_action_relay(a,ld);
		break;
	case LAT_WAIT:
		do_log_action_wait(a,ld);
		break;
	default:
		er_panic("do_log_action");
	}
}

void
log_queue_task1()
{
LOG_NODE * ln;
LOG_COND * lc;
LOG_ACTION * a;
XL_INTERPRETER * xli;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);

	for ( ; ; ) {
		llpl_check(0);
		ln = delete_queue(&log_queue,0,0,0);
		if ( ln == 0 ) {
			llpl_check(1);
			ln = delete_queue(&log_queue,0,0,1);
		}
last_log_del_time = get_xltime();

		lock_task(lock_log);
		for ( lc = condition_list ; lc ; lc = lc->next ) {
			if ( cmp_log_node_data(&ln->d,lc) )
				continue;
			for ( a = lc->action_list ; a ; a = a->next ) {
				do_log_action(a,ln);
			}
		}
		unlock_task(lock_log,"log_queue_task1");
		free_log_node(ln);
	}
}


void
get_log_node_data_from_sexp(LOG_NODE_DATA * ln,XL_SEXP * el)
{
XL_SEXP * d;
int i;
	d = get_el_by_symbol(el,l_string(std_cm,"time"),0);
	if ( d ) {
		d = get_el(d,1);
		if ( get_type(d) != XLT_INTEGER )
			return;
		ln->d.time = d->integer.data;
	}
	d = get_el_by_symbol(el,l_string(std_cm,"level"),0);
	if ( d ) {
		d = get_el(d,1);
		switch ( get_type(d) ) {
		case XLT_INTEGER:
			ln->d.level = d->integer.data;
			break;
		case XLT_STRING:
			for ( i = 0 ; i < LOG_MAX_LEVEL ; i ++ )
				if ( l_strcmp(d->string.data,
					l_string(std_cm,log_level_msg[i]))
					== 0 )
					break;
			if ( i == LOG_MAX_LEVEL )
				return;
			ln->d.level = i;
			break;
		default:
			return;
		}
	}
	d = get_el_by_symbol(el,l_string(std_cm,"URL"),0);
	if ( d ) {
		d = get_el(d,1);
		if ( get_type(d) != XLT_STRING )
			return;
		get_url2(&ln->d.u,d->string.data);
	}
	d = get_el_by_symbol(el,l_string(std_cm,"layer"),0);
	if ( d ) {
		d = get_el(d,1);
		switch ( get_type(d) ) {
		case XLT_INTEGER:
			ln->d.level = d->integer.data;
			break;
		case XLT_STRING:
			for ( i = 0 ; i < LOG_LAYER_MAX ; i ++ )
				if ( l_strcmp(d->string.data,
					l_string(std_cm,log_layer[i]))
					== 0 )
					break;
			if ( i == LOG_LAYER_MAX )
				return;
			ln->d.level = i;
			break;
		default:
			return;
		}
	}
	d = get_el_by_symbol(el,l_string(std_cm,"user"),0);
	if ( d ) {
		d = get_el(d,1);
		if ( get_type(d) != XLT_STRING )
			return;
		ln->d.user = ll_copy_str(d->string.data);
	}
	d = get_el_by_symbol(el,l_string(std_cm,"data"),0);
	if ( d ) {
		d = get_el(d,1);
		if ( get_type(d) != XLT_STRING )
			return;
		ln->d.data = ll_copy_str(d->string.data);
	}
}

void
log_get_element(XL_SEXP * el)
{
LOG_NODE * ln,*ln2;
	ln = new_queue_node(sizeof(*ln));
	get_log_node_data_from_sexp(&ln->d,el);

last_log_ins_time = get_xltime();

	for ( ; insert_queue(&log_queue,ln,0) < 0 ; ) {
ss_printf("LOG OVER FLOW %i\n",(int)getpid());
		ln2 = delete_queue(&log_queue,0,0,0);
		free_log_node(ln2);
	}
}


void
log_queue_task2(TKEY d)
{
L_CHAR * target;
URL u_target;
XL_SEXP * p;
XL_SEXP * send;
XL_INTERPRETER * xli;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);

	target = (L_CHAR*)GET_TKEY(d);
	get_url2(&u_target,target);
	for ( ; ; ) {
		gc_push(0,0,"log_queue_task2");
		send = List(n_get_symbol("GetLog"),
				-1);
		p = remote_session(
			gblisp_top_env0,
			log_session,
			&u_target,
			l_string(std_cm,"server"),
			l_string(std_cm,"user"),
			l_string(std_cm,"Get"),
			List(send,-1),
			0,0,0,0);
		for ( ; get_type(p) == XLT_PAIR ; p = cdr(p) )
			log_get_element(car(p));
		gc_pop(0,0);
	}
	close_self_interpreter();
}

int
log_wait_q_cond(SYS_QUEUE * q,LOG_NODE * a,int * ip)
{
	if ( a->wait_action == 0 )
		return -1;
	if ( (a->wait_action->ip & a->wait_action->mask)
			== ((*ip) & a->wait_action->mask) )
		return 0;
	return -1;
}


XL_SEXP *
xl_get_log(XLISP_ENV * env,XL_SEXP * s)
{
int max;
LOG_NODE * ln;
XL_INTERPRETER * xli;
XL_SEXP * ret;
	xli = get_my_xli();
	max = 2*LOG_QUEUE_SIZE;
	ret = 0;
	for ( ; max > 0 ; max -- ) {
		ln = delete_queue(&log_wait_queue,
			log_wait_q_cond,
			&xli->ip,0);
		if ( ln == 0 )
			break;

last_log_del_time = get_xltime();

		ret = cons(
			get_log_node_sexp(&ln->d),
			ret);
		free_log_node(ln);
	}
	return reverse(ret);
}

int
get_log_cond_symbol(L_CHAR * el_data)
{
int ret;
	for ( ret = 1 ; ; ret ++ ) {
		if ( fs_table[ret] == 0 )
			return -1;
		if ( l_strcmp(el_data,l_string(std_cm,fs_table[ret]))
					== 0 )
			return ret;
	}
	return -1;
}

XL_SEXP *
get_log_cond_element(LOG_COND * lc,XL_SEXP * lst)
{
XL_SEXP * el;
XL_SEXP * data1, * data2;
int field,f;
int op;
L_CHAR * tmp;
/*
	EQU
		FIELD = data
		data = FIELD
	FLONT
		FIELD = data *
		data * = FIELD
	BACK
		FIELD = * data
		* data = FIELD
	BOUND
		FIELD < data
		data < FIELD
		data < FIELD < data
*/

	op = 0;
	data1 = data2 = 0;
	field = 0;
	el = car(lst);
	switch ( get_type(el) ) {
	case XLT_INTEGER:
	case XLT_STRING:
		data1 = el;
		goto equ_flont_bound_DATA;
	case XLT_SYMBOL:
		f = get_log_cond_symbol(el->symbol.data);
		if ( f < 0 )
			goto err;
		if ( f == FS_ASTARISK )
			goto back_ASTARISK;
		if ( f < FS_SYMBOL )
			return get_error(
				lst->h.file,
				lst->h.line,
				XLE_PROTO_INV_PARAM,
				l_string(std_cm,"log condition element parameter"),
				el);
		field = f;
		goto equ_flont_bound_FIELD;
	case XLT_ERROR:
		return el;
	default:
		goto err;
	}
equ_flont_bound_DATA:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	switch ( f ) {
	case FS_EQU:
		op = LC_OP_EQU;
		goto equ_DATA_EQU;
	case FS_ASTARISK:
		goto flont_DATA_ASTARISK;
	case FS_LARGE:
		op = LC_OP_BOUND_NN|LC_OP_BOUND_NE;
		goto bound_DATA_LARGE;
	case FS_EQU_LARGE:
		op = LC_OP_BOUND_EN|LC_OP_BOUND_EE;
		goto bound_DATA_LARGE;
	default:
		goto err;
	}
equ_flont_bound_FIELD:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	switch ( f ) {
	case FS_EQU:
		goto equ_flont_FIELD_EQU;
	case FS_LARGE:
		op = LC_OP_BOUND_NN|LC_OP_BOUND_NE;
		goto bound_FIELD_LARGE;
	case FS_EQU_LARGE:
		op = LC_OP_BOUND_EN|LC_OP_BOUND_EE;
		goto bound_FIELD_LARGE;
	default:
		goto err;
	}
back_ASTARISK:
	lst = cdr(lst);
	el = car(lst);
	switch ( get_type(el) ) {
	case XLT_INTEGER:
	case XLT_STRING:
		data1 = el;
		goto back_ASTARISK_DATA;
	default:
		goto err;
	}
back_ASTARISK_DATA:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f != FS_EQU )
		goto err;
	goto back_ASTARISK_DATA_EQU;
back_ASTARISK_DATA_EQU:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f < FS_SYMBOL )
		goto err;
	field = f;
	goto finish;
equ_DATA_EQU:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f < FS_SYMBOL )
		goto err;
	field = f;
	goto finish;
flont_DATA_ASTARISK:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f != FS_EQU )
		goto err;
	op = LC_OP_FLONT;
	goto flont_DATA_ASTARISK_EQU;
flont_DATA_ASTARISK_EQU:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f < FS_SYMBOL )
		goto err;
	field = f;
	goto finish;
bound_DATA_LARGE:
	lst = cdr(lst);
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f < FS_SYMBOL )
		goto err;
	field = f;
	goto bound_DATA_LARGE_FIELD;
bound_DATA_LARGE_FIELD:
	lst = cdr(lst);
	if ( get_type(lst) == XLT_NULL ) {
		op &= LC_OP_BOUND_NE|LC_OP_BOUND_EE;
		goto finish;
	}
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	switch ( f ) {
	case FS_LARGE:
		op &= LC_OP_BOUND_EN|LC_OP_BOUND_NN;
		goto bound_DATA_LARGE_FIELD_LARGE;
	case FS_EQU_LARGE:
		op &= LC_OP_BOUND_EE|LC_OP_BOUND_NE;
		goto bound_DATA_LARGE_FIELD_LARGE;
	default:
		goto err;
	}
bound_DATA_LARGE_FIELD_LARGE:
	lst = cdr(lst);
	el = car(lst);
	switch ( get_type(el) ) {
	case XLT_INTEGER:
	case XLT_STRING:
		data2 = el;
		goto finish;
	default:
		goto err;
	}
equ_flont_FIELD_EQU:
	lst = cdr(lst);
	el = car(lst);
	switch ( get_type(el) ) {
	case XLT_INTEGER:
	case XLT_STRING:
		data1 = el;
		goto equ_flont_FIELD_EQU_DATA;
	default:
		goto err;
	}
equ_flont_FIELD_EQU_DATA:
	lst = cdr(lst);
	if ( get_type(lst) == XLT_NULL ) {
		op = LC_OP_EQU;
		goto finish;
	}
	el = car(lst);
	if ( get_type(el) != XLT_SYMBOL )
		goto err;
	f = get_log_cond_symbol(el->symbol.data);
	if ( f != FS_ASTARISK )
		goto err;
	op = LC_OP_FLONT;
	goto finish;
bound_FIELD_LARGE:
	lst = cdr(lst);
	el = car(lst);
	switch ( get_type(el) ) {
	case XLT_INTEGER:
	case XLT_STRING:
		data2 = el;
		op &= LC_OP_BOUND_NE|LC_OP_BOUND_EE;
		goto finish;
	default:
		goto err;
	}
finish:
	if ( get_type(lst) != XLT_NULL ) {
		lst = cdr(lst);
		if ( get_type(lst) != XLT_NULL ) {
			el = lst;
			goto err;
		}
	}
	lc->op[field-FS_SYMBOL] = op;
	switch ( get_type(data1) ) {
	case XLT_INTEGER:
		lc->low._int[field-FS_SYMBOL] = data1->integer.data;
		break;
	case XLT_STRING:
		lc->low._chr[field-FS_SYMBOL] = ll_copy_str(data1->string.data);
		break;
	case XLT_NULL:
		if ( LOG_NODE_DATA_INT_PATTERN & (field-FS_SYMBOL) )
			lc->low._int[field-FS_SYMBOL] = 0;
		else {
			lc->high._chr[field-FS_SYMBOL] = tmp = 
				d_alloc(sizeof(L_CHAR));
			tmp[0] = 0;
		}
		break;
	}
	switch ( get_type(data2) ) {
	case XLT_INTEGER:
		lc->high._int[field-FS_SYMBOL] = data2->integer.data;
		break;
	case XLT_STRING:
		lc->high._chr[field-FS_SYMBOL] = ll_copy_str(data2->string.data);
		break;
	case XLT_NULL:
		if ( LOG_NODE_DATA_INT_PATTERN & (field-FS_SYMBOL) )
			lc->high._int[field-FS_SYMBOL] = 0x7fffffff;
		else {
			lc->high._chr[field-FS_SYMBOL] = tmp = 
				d_alloc(2*sizeof(L_CHAR));
			tmp[0] = 0x7fffffff;
			tmp[1] = 0;
		}
		break;
	}
	return 0;
err:
	return get_error(
		lst->h.file,
		lst->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"log condition element parameter"),
		el);
}

XL_SEXP *
get_log_cond_action(LOG_COND * lc,XL_SEXP * sym)
{
L_CHAR * type;
L_CHAR * path;
L_CHAR * option;
L_CHAR * ip;
L_CHAR * mask;
L_CHAR * div_nos;
L_CHAR * size;
LOG_ACTION * a;
int er;
	type = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"type"));
	path = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"path"));
	option = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"option"));
	ip = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"ip"));
	mask = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"mask"));
	div_nos = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"divide"));
	size = get_sf_attribute(sym->symbol.field,
			l_string(std_cm,"size"));
	if ( type == 0 ) {
		return get_error(
			sym->h.file,
			sym->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"log condition action attribute (type is required)"),
			sym);
	}
	a = d_alloc(sizeof(*a));
	memset(a,0,sizeof(*a));
	if ( l_strcmp(type,l_string(std_cm,"save")) == 0 )
		a->type = LAT_SAVE_FILE;
	else if ( l_strcmp(type,l_string(std_cm,"console")) == 0 )
		a->type = LAT_CONSOLE;
	else if ( l_strcmp(type,l_string(std_cm,"relay")) == 0 )
		a->type = LAT_RELAY;
	else if ( l_strcmp(type,l_string(std_cm,"wait")) == 0 )
		a->type = LAT_WAIT;
	else
		return get_error(
			sym->h.file,
			sym->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"log condition action attribute (type)"),
			sym);
	a->path = ll_copy_str(path);
	if ( option ) {
		a->option = get_attr_flags(&er,option,log_cond_attr_table);
		if ( er )
			return get_error(
				sym->h.file,
				sym->h.line,
				XLE_PROTO_INV_PARAM,
				l_string(std_cm,"log condition action attribute (option)"),
				sym);
	}
	else {
		switch ( a->type ) {
		case LAT_SAVE_FILE:
			a->option = LO_ALL;
			break;
		}
	}
	if ( ip )
		a->ip = inet_addr(n_string(std_cm,ip));
	if ( mask )
		a->mask = inet_addr(n_string(std_cm,mask));
	if ( div_nos )
		a->div_nos = atoi(n_string(std_cm,div_nos));
	if ( size )
		a->size = atoi(n_string(std_cm,size));
	a->next = lc->action_list;
	lc->action_list = a;
	return 0;
}



XL_SEXP *
get_log_cond(XL_SEXP * lst)
{
LOG_COND * lc;
XL_SEXP * el;
XL_SEXP * sym;
XL_SEXP * ret;
	lc = d_alloc(sizeof(*lc));
	memset(lc,0,sizeof(*lc));
	for ( lst = cdr(lst) ; get_type(lst) == XLT_PAIR ; lst = cdr(lst) ) {
		el = car(lst);
		if ( get_type(el) != XLT_PAIR )
			continue;
		sym = car(el);
		if ( get_type(sym) != XLT_SYMBOL ) {
			ret = get_log_cond_element(lc,el);
		}
		else {
			if ( l_strcmp(sym->string.data,
					l_string(std_cm,"Action")) == 0 )
				ret = get_log_cond_action(lc,sym);
			else	ret = get_log_cond_element(lc,el);
		}
		if ( get_type(ret) == XLT_ERROR )
			goto err;
	}
	set_log_condition(lc);

	return 0;
err:

ss_printf("get_log_cond err\n");
	free_log_cond(lc);
	return ret;
}

XL_SEXP *
xl_set_log_condition(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,XL_SYM_FIELD * sf)
{
XL_SEXP * sym;
XL_SEXP * el;
XL_SEXP * ret;
L_CHAR * trunc_file;
STREAM * st;
L_CHAR * type;
	type = get_sf_attribute(sf,l_string(std_cm,"type"));
	s = gb_quote_trace(env,cdr(s),type);
	if  ( get_type(s) == XLT_ERROR )
		return s;

	reset_log_condition();

	for ( ; get_type(s) == XLT_PAIR ; s = cdr(s) ) {
		el = car(s);
		if ( get_type(el) != XLT_PAIR )
			continue;
		sym = car(el);
		if ( get_type(sym) != XLT_SYMBOL )
			continue;
		if ( l_strcmp(sym->symbol.data,l_string(std_cm,"Condition")) == 0 ) {
			ret = get_log_cond(el);
			if ( get_type(ret) == XLT_ERROR ) {
				reset_log_condition();
				return ret;
			}
		}
		else if ( l_strcmp(sym->symbol.data,l_string(std_cm,"Reset")) == 0 ) {
			trunc_file = get_sf_attribute(sym->symbol.field,
					l_string(std_cm,"file"));
			if ( trunc_file ) {
				trunc_file = parse_dir_path(0,trunc_file);
				st = s_open_file(n_string(std_cm,trunc_file),
						O_RDWR|O_CREAT|O_TRUNC,0644);
				s_close(st);
				d_f_ree(trunc_file);
			}
		}
	}
	log_printf(LOG_MESSAGE,LOG_LAYER_XL,0,"Launch up the Log system");
	return 0;
}

XL_SEXP *
xl_fetch_log(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,XL_SYM_FIELD * sf)
{
L_CHAR * target;
	target = get_sf_attribute(sf,l_string(std_cm,"target"));
	if ( target == 0 )
		return 0;
	target = ll_copy_str(target);
	create_task(log_queue_task2,(int)target,PRI_FETCH);
	return 0;
}

int
xl_log_vprintf(
	int level,
	int layer,
	LOG_NODE_DATA * opt,
	char * fmt,
	va_list p)
{
LOG_NODE * ln,* ln2;
STREAM * st;


	st = s_open_string_write(&int_cm);

	ln = new_queue_node(sizeof(*ln));
	ln->wait_action = 0;
	if ( opt )
		copy_log_node_data(&ln->d,opt);
	ln->d.d.time = get_xltime();
	ln->d.d.level = level;
	ln->d.d.layer = layer;
	if ( ln->d.d.u.server )
		d_f_ree(ln->d.d.u.server);
	ln->d.d.u.server = ll_copy_str(get_xllisp_site());
	if ( ln->d.d.u.agent )
		d_f_ree(ln->d.d.u.agent);
	if ( agent_name )
		ln->d.d.u.agent = ll_copy_str(agent_name);
	else	ln->d.d.u.agent = 0;

	_s_printf(st,0,fmt,p);
	ln->d.d.data = ll_copy_str(s_get_l_string(st));

	s_close(st);


last_log_ins_time = get_xltime();

	for ( ; insert_queue(&log_queue,ln,0) < 0 ; ) {
ss_printf("LOG OVER FLOW %i\n",(int)getpid());
		ln2 = delete_queue(&log_queue,0,0,0);
		free_log_node(ln2);
	}
	return 0;
}


int
log_print_sexp(
	int level,
	int layer,
	LOG_NODE_DATA * opt,
	char * str,
	XL_SEXP * s,
	int flags)
{
STREAM * st;
char * d;
int ret;
	st = s_open_string_write(std_cm);
	print_sexp(st,s,flags);
	d = s_get_string(st);
	ret = log_printf(level,layer,opt,"%s = %s",str,d);
	s_close(st);
	return ret;
}



void
set_log_path(L_CHAR * str,int size,int filenos)
{
LOG_COND * lc;
LOG_ACTION * a;
	reset_log_condition();
	lc = d_alloc(sizeof(*lc));
	memset(lc,0,sizeof(*lc));
	a = d_alloc(sizeof(*a));
	memset(a,0,sizeof(*a));
	a->type = LAT_SAVE_FILE;
	a->path = ll_copy_str(str);
	a->option = LO_ALL;
	a->div_nos = filenos;
	a->size = size;
	lc->action_list = a;
	set_log_condition(lc);
}
