 /*
 * cluster_info.c
 * 
 * Author: Jia Ming Pan <jmltc@cn.ibm.com>
 * Copyright (c) 2005 International Business Machines
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include <lha_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <regex.h>
#include <hb_api.h>
#include <ha_msg.h>
#include <heartbeat.h>
#include <cmpidt.h>
#include "cluster_info.h"
#include "mof_map.h"


/*FIXME: remove hardcode path */
#define         CLIHB 			"/etc/init.d/heartbeat"

#define 	CONFIG_FILENAME 	HA_HBCONF_DIR"/ha.cf"
#define         BACKUP_CONFIG_NAME 	CONFIG_NAME".bak"
#define 	BACKUP_KEYFILE 		KEYFILE".bak"

#define         CONFIG_COMMENT \
			"#This file was generated by provider\n\n\n"

#define CIM_RSCNAME_TABLE		"resource_name_table"
#define CIM_RSCTYPE_TABLE	 	"resource_type_table"
#define CIM_RSCOPS_TABLE		"resource_operations"
#define CIM_RSCATTRS_TABLE		"resource_attrubtes_table"
#define CIM_SUBRSC_NAME_TABLE		"subresource_name_table"
#define CIM_RESOURCE_TABLE		"resource_table"


typedef struct FunContext_s {
	int 	func_id;	/* function name */
	const char * cmnd[8];	/* mgmt cmnds */
	const char * pattern_exp;
	int (*handle_func)(MClient*, void*, const struct FunContext_s*, void*);
} FunContext;

#define MSG_MAX_DEPTH 2
struct msg_pattern_t {
	char **	keys[MSG_MAX_DEPTH];	/* keys of nodes*/
	int 	len[MSG_MAX_DEPTH];	/* keys length of each node */
	char * 	tag[MSG_MAX_DEPTH];	/* description for each node */
	int 	depth;			/* message depth */
};
 
static int node_return(MClient*, void*, const FunContext*, void*);
static int list_return(MClient*, void*, const FunContext*, void*);
static int node_update(MClient*, void*, const FunContext*, void*);

static struct msg_pattern_t * msg_pattern_parse(const char *pattern);
static void msg_pattern_free(void*);
 
static int InsertOption(struct ha_msg*, const char *, const char * option);
static int StrIsEmpty(const char * str);

static int		cib_changed(void);

static int		cim_rsctype_s2tid(const char * rsctype);
static const char*	cim_rsctype_tid2s(int rsctype);

#define cim_rscname_table_keys() cim_dbkeys(CIM_RSCNAME_TABLE)
#define cim_rscname_table_add(rscid)	\
		cim_dbput(CIM_RSCNAME_TABLE, rscid, "disabled")
#define cim_rscname_table_del(rscid)	\
		cim_dbdel(CIM_RSCNAME_TABLE, rscid)
#define cim_rscname_table_get(rscid)	\
		cim_dbget(CIM_RSCNAME_TABLE, rscid)

#define cim_rsctype_table_add(rscid,type) 	cim_dbput(CIM_RSCTYPE_TABLE, rscid, type) 
#define cim_rsctype_table_del( rscid)	cim_dbdel(CIM_RSCTYPE_TABLE, rscid)


#define STR_CONS_ORDER        "rsc_order"
#define STR_CONS_LOCATION     "rsc_location"
#define STR_CONS_COLOCATION   "rsc_colocation"


/*
description:
	return CRM configuration
format:
	MSG_CRM_CONFIG
return:
	MSG_OK transition_idle_timeout symmetric_cluster(True|False)
 	  stonith_enabled(True|False) no_quorum_policy(freeze|stop|ignore)
 	  default_resource_stickiness have_quorum(True|False)
or
	MSG_FAIL
*/

#define BEGIN_NODE	"{"
#define END_NODE 	"}"

#define BEGIN_NODE_CHAR '{'
#define END_NODE_CHAR '}'

#define BEGIN_KEY 	"("
#define END_KEY 	")"

#define BEGIN_KEY_CHAR	'('
#define END_KEY_CHAR	')'

#define REPEATE 	"?"
#define ANYTIMES 	"*"

#define REPEATE_CHAR	'?'
#define ANYTIMES_CHAR	'*'

#define MAXTIMES	999


static const char crm_config [] = 
BEGIN_NODE "crm_config"	
	BEGIN_KEY "transition_idle_timeout"	END_KEY 
	BEGIN_KEY "symmetric_cluster" 	   	END_KEY
	BEGIN_KEY "stonith_enabled" 		END_KEY
	BEGIN_KEY "no_quorum_policy" 		END_KEY
	BEGIN_KEY "default_resource_stickiness" END_KEY
	BEGIN_KEY "have_quorum" 		END_KEY
END_NODE;
	
/*
format:
	MSG_HB_CONFIG
return:
	MSG_OK apiauth auto_failback baud debug debugfile deadping deadtime
	  hbversion hopfudge initdead keepalive logfacility logfile msgfmt
	  nice_failback node normalpoll stonith udpport warntime watchdog
or
	MSG_FAIL
*/
static const char hb_config [] = 
BEGIN_NODE "hb_config"
	BEGIN_KEY "apiauth" 		END_KEY	
	BEGIN_KEY "auto_failback" 	END_KEY
	BEGIN_KEY "baud" 		END_KEY
	BEGIN_KEY "debug"		END_KEY
	BEGIN_KEY "debugfile"		END_KEY
	BEGIN_KEY "deadping"		END_KEY
	BEGIN_KEY "deadtime"		END_KEY
	BEGIN_KEY "hbversion"		END_KEY
	BEGIN_KEY "hopfudge"		END_KEY
	BEGIN_KEY "initdead"		END_KEY
	BEGIN_KEY "keepalive"		END_KEY
	BEGIN_KEY "logfacility"		END_KEY
	BEGIN_KEY "logfile"		END_KEY
	BEGIN_KEY "msgfmt"		END_KEY
	BEGIN_KEY "nice_failback"	END_KEY
	BEGIN_KEY "node"		END_KEY
	BEGIN_KEY "normalpoll"		END_KEY
	BEGIN_KEY "stonith"		END_KEY
	BEGIN_KEY "updport"		END_KEY
	BEGIN_KEY "warntime"		END_KEY
	BEGIN_KEY "watchdog"		END_KEY
END_NODE;

/*
	MSG_NODE_CONFIG NODENAME
return:
	MSG_OK uname online(True|False) standbyTrue|False) unclean(True|False)
 	  shutdown(True|False) expected_up(True|False) is_dc(True|False)
	  node_ping("ping|member")
*/
static const char node_info [] = 
BEGIN_NODE
	BEGIN_KEY "uname"	END_KEY
	BEGIN_KEY "online"	END_KEY
	BEGIN_KEY "standby"	END_KEY
	BEGIN_KEY "unclean"	END_KEY
	BEGIN_KEY "shutdown"	END_KEY
	BEGIN_KEY "expected_up"	END_KEY
	BEGIN_KEY "is_dc"	END_KEY
	BEGIN_KEY "node_ping"	END_KEY
END_NODE;

/*
description:
        return the operations of a given resource
format:
        MSG_RSC_OPS resource
return:
        MSG_OK id1 name1 interval1 timeout1 id2 name2 interval2 timeout2
                ... idn namen intervaln timeoutn
or
        MSG_FAIL
*/

static const char operations [] = 
BEGIN_NODE "operations"
	BEGIN_NODE "op" REPEATE ANYTIMES
		BEGIN_KEY "id"		END_KEY	
		BEGIN_KEY "name"	END_KEY	
		BEGIN_KEY "interval"	END_KEY	
		BEGIN_KEY "timeout"	END_KEY
	END_NODE
END_NODE;

static const char attributes []= 
BEGIN_NODE CIM_MSG_INST_ATTR
	BEGIN_NODE CIM_MSG_ATTR REPEATE ANYTIMES
		BEGIN_KEY "id"		END_KEY	
		BEGIN_KEY "name"	END_KEY	
		BEGIN_KEY "value"	END_KEY
	END_NODE
END_NODE;

static const char primitive []= 
BEGIN_NODE "primitive"
	BEGIN_KEY "id"		END_KEY
	BEGIN_KEY "class"	END_KEY
	BEGIN_KEY "provider"	END_KEY
	BEGIN_KEY "type"	END_KEY
END_NODE;

static const char clone [] =
BEGIN_NODE "clone"
	BEGIN_KEY "id"			END_KEY
	BEGIN_KEY "clone_max"		END_KEY
	BEGIN_KEY "clone_node_max"	END_KEY
END_NODE;
 
static const char master []= 
BEGIN_NODE "master_slave"
	BEGIN_KEY "id"			END_KEY
	BEGIN_KEY "clone_max"		END_KEY
	BEGIN_KEY "clone_node_max"	END_KEY
	BEGIN_KEY "master_max"		END_KEY
	BEGIN_KEY "master_node_max"	END_KEY
END_NODE;
 

static const char order_constraint [] = 
BEGIN_NODE "rsc_order"
	BEGIN_KEY "id"		END_KEY
	BEGIN_KEY "from"	END_KEY
	BEGIN_KEY "type"	END_KEY
	BEGIN_KEY "to"		END_KEY
END_NODE;

static const char co_constraint[] = 
BEGIN_NODE "rsc_colocation"
	BEGIN_KEY "id"		END_KEY
	BEGIN_KEY "from"	END_KEY
	BEGIN_KEY "to"		END_KEY
	BEGIN_KEY "score"	END_KEY
END_NODE;

/*
	rsc_location:
		MSG_OK id resource score 
                        expr_id1 attribute1 operation1 value1
			expr_id2 attribute2 operation2 value2 ...
			expr_idn attributen operationn valuen
*/

static const char location_constraint[] = 
BEGIN_NODE "rsc_location"
	BEGIN_KEY "id"		END_KEY
	BEGIN_KEY "resource"	END_KEY
	BEGIN_KEY "score"	END_KEY
	BEGIN_NODE "rule" REPEATE ANYTIMES 	/* constraint rule */
		BEGIN_KEY "id"		END_KEY
		BEGIN_KEY "attribute"	END_KEY
		BEGIN_KEY "operation"	END_KEY
		BEGIN_KEY "value"	END_KEY
	END_NODE
END_NODE;


#define A_1(a)		{a,NULL}
#define A_2(a,b)	{a,b,NULL}
#define A_3(a,b,c)	{a,b,c,NULL}
#define A_4(a,b,c,d)	{a,b,c,d,NULL}
#define A_5(a,b,c,d,e)  {a,b,c,d,e,NULL}

static const FunContext query_ctx_table [] = {
	/* cluster */
	{GET_CRM_CONFIG,A_1(MSG_CRM_CONFIG), crm_config, node_return},
	{GET_HB_CONFIG, A_1(MSG_HB_CONFIG),  hb_config,  node_return},
	{GET_DC, 	A_1(MSG_DC), 	     "{(dc)}",     node_return},
	{GET_NODE_INFO, A_1(MSG_NODE_CONFIG),node_info,  node_return},
	{GET_NODE_LIST, A_1(MSG_ALLNODES),   NULL,	list_return},

	/* resource */
	{GET_RSC_OPERATIONS, A_1(MSG_RSC_OPS), operations, node_return},
	{GET_RSC_ATTRIBUTES, A_1(MSG_RSC_PARAMS),attributes, node_return},
	{GET_RSC_TYPE, 	A_1(MSG_RSC_TYPE), 	"{(type)}", node_return},
	{GET_PRIMITIVE, A_1(MSG_RSC_ATTRS), 	primitive, node_return},
	{GET_CLONE, 	A_1(MSG_GET_CLONE), 	clone, node_return},
	{GET_MASTER, 	A_1(MSG_GET_MASTER), 	master, node_return},

	{GET_SUB_RSC, 	A_1(MSG_SUB_RSC), 	NULL, 	list_return},
	{GET_RSC_LIST,	A_1(MSG_ALL_RSC), 	NULL, 	list_return},
	{GET_RSC_HOST, 	A_1(MSG_RSC_RUNNING_ON), "{(host)}", node_return},
	{GET_RSC_STATUS,A_1(MSG_RSC_STATUS), 	"{(status)}", node_return},

	/* metadata */
	{GET_RSC_CLASSES, A_1(MSG_RSC_CLASSES), NULL, list_return},
	{GET_RSC_TYPES, A_1(MSG_RSC_TYPE), 	NULL, list_return},
	{GET_RSC_PROVIDERS, A_1(MSG_RSC_PROVIDERS), NULL, list_return},

	/* constaint */
	{GET_ORDER_CONSTRAINT, A_2(MSG_GET_CONSTRAINT, STR_CONS_ORDER), 
		order_constraint, node_return 
	},
	{GET_LOCATION_CONSTRAINT, A_2(MSG_GET_CONSTRAINT, STR_CONS_LOCATION),
		location_constraint, node_return 
	},
	{GET_COLOCATION_CONSTRAINT, A_2(MSG_GET_CONSTRAINT,STR_CONS_COLOCATION),
		co_constraint, node_return
	},
	{GET_ORDER_CONS_LIST, A_2(MSG_GET_CONSTRAINTS, STR_CONS_ORDER),
		NULL, list_return
	},
	{GET_LOCATION_CONS_LIST, A_2(MSG_GET_CONSTRAINTS, STR_CONS_LOCATION),
		NULL, list_return
	},
	{GET_COLOCATION_CONS_LIST, A_2(MSG_GET_CONSTRAINTS,STR_CONS_COLOCATION),
		NULL, list_return
	}
};

/*
description:
        add a new resource
format:
        MSGA_DD_RSC rsc_id rsc_class rsc_type rsc_provider group("" for NONE)
                advance(""|"clone"|"master") advance_id clone_max
                clone_node_max master_max master_node_max
                param_id1 param_name1 param_value1
                param_id2 param_name2 param_value2
                ...
                param_idn param_namen param_valuen
*/
static const char resource[] =
BEGIN_NODE
	BEGIN_KEY "id"		END_KEY
	BEGIN_KEY "class"	END_KEY
	BEGIN_KEY "type"	END_KEY
	BEGIN_KEY "provider"	END_KEY
	BEGIN_KEY "groupid"	END_KEY
	BEGIN_KEY "advance"	END_KEY
	BEGIN_KEY "advance_id"	END_KEY
	BEGIN_KEY "clone_max"	END_KEY
	BEGIN_KEY "clone_node_max"	END_KEY
	BEGIN_KEY "master_max"		END_KEY
	BEGIN_KEY "master_node_max"	END_KEY
	BEGIN_NODE CIM_MSG_ATTR REPEATE
		BEGIN_KEY "id"		END_KEY
		BEGIN_KEY "name"	END_KEY
		BEGIN_KEY "value"	END_KEY
	END_NODE
END_NODE;

static const FunContext update_ctx_table [] = {
	{DEL_OPERATION,		A_1(MSG_DEL_RSC_OP),  NULL, NULL},
	{DEL_ATTRIBUTES,	A_1(MSG_DEL_RSC_PARAM),NULL, NULL},
	{DEL_RESOURCE, 		A_1(MSG_DEL_RSC), NULL, NULL},
	{CLEANUP_RESOURCE,	A_1(MSG_CLEANUP_RSC), NULL, NULL},

	{CREATE_RSC_GROUP, 	A_1(MSG_ADD_GRP),NULL, NULL},
	{CREATE_RESOURCE, 	A_1(MSG_ADD_RSC), resource, node_update},
	{DEL_ORDER_CONSTRAINT, 	
		A_2(MSG_DEL_CONSTRAINT, STR_CONS_ORDER), NULL, NULL
	},
	{DEL_LOCATION_CONSTRAINT,  
		A_2(MSG_DEL_CONSTRAINT, STR_CONS_LOCATION), NULL, NULL
	},
	{DEL_COLOCATION_CONSTRAINT,
		A_2(MSG_DEL_CONSTRAINT, STR_CONS_COLOCATION), NULL, NULL
	},

	{UPDATE_CLONE, 		A_1(MSG_UPDATE_CLONE), clone, node_update},
	{UPDATE_MASTER,	 	A_1(MSG_UPDATE_MASTER),master, node_update},
	{UPDATE_OPERATIONS, 	A_1(MSG_UP_RSC_OPS), operations, node_update},
	{UPDATE_ATTRIBUTES, 	A_1(MSG_UP_RSC_PARAMS), 
		attributes, node_update 
	}, 
	{UPDATE_ORDER_CONSTRAINT, A_2(MSG_UP_CONSTRAINT, STR_CONS_ORDER),
		order_constraint, node_update
	},
	{UPDATE_LOCATION_CONSTRAINT, A_2(MSG_UP_CONSTRAINT, STR_CONS_LOCATION),
		location_constraint, node_update
	},
	{UPDATE_COLOCATION_CONSTRAINT, 
		A_2(MSG_UP_CONSTRAINT, STR_CONS_COLOCATION), 
		co_constraint, node_update
	},
};


static const FunContext * 
find_query_ctx(int func_id)
{
	int i, len;
	const FunContext * ctx_table;

	len = sizeof(query_ctx_table)/sizeof(FunContext);
	ctx_table = query_ctx_table;

	for (i = 0; i < len; i++){
		if (ctx_table[i].func_id == func_id){
			return &ctx_table[i];
		}
	}
	return NULL;
}

static const FunContext * 
find_update_ctx(int func_id)
{
	int i, len;
	const FunContext * ctx_table;

	len = sizeof(update_ctx_table)/sizeof(FunContext);
	ctx_table = update_ctx_table;
	for (i = 0; i < len; i++){
		if (ctx_table[i].func_id == func_id){
			return &ctx_table[i];
		}
	}
	return NULL;
}


struct ha_msg *
cim_query_dispatch(int func_id, const char * param, void * out)
{
	const FunContext * ctx;
	MClient * client;
	const char * arg;
	int i = 0;
	struct ha_msg * msg = NULL;
	char pathname[MAXLEN];

	cim_debug2(LOG_INFO, "cim_query_dispatch: %d, %s", 
				func_id, param? param : "<null>");
	/* look for msg from cache first */
	snprintf(pathname, MAXLEN, 
			"cache_1_%d_%s", func_id, param?param:"list");	
	if (!cib_changed() &&(msg = cim_disk2msg(pathname))){
		return msg;
	}

	/* not found: get msg from client */ 
	if ((ctx = find_query_ctx(func_id)) == NULL ) {
		cl_log(LOG_ERR, "%s: can't find function %d.", 
			__FUNCTION__, func_id);
		return NULL;
	}

	if ((msg = ha_msg_new(1)) == NULL ) {
		cl_log(LOG_ERR, "%s: alloc msg failed.", __FUNCTION__);
		return NULL;
	}

	if ( (client = mclient_new()) == NULL ) {
		cl_log(LOG_ERR, "%s: can't create client", __FUNCTION__);	
		ha_msg_del(msg);
               	return NULL;	
        }
	
	while ((arg = ctx->cmnd[i++])){
		mclient_cmnd_append(client, arg);
	}

	if(param){
		mclient_cmnd_append(client, param);
	}

	/* execute the command and call callback to process the result */
	if( mclient_process(client) == MC_OK && ctx->handle_func ) {
		if( client->rlen == 0 ) {
			cl_log(LOG_WARNING, "cim_qeury_dispatch:"
				"client only return 'ok'");
			goto clean_and_return_null;
		} else  if ( ctx->handle_func(client, msg, ctx, out)!= HA_OK) {
			cl_log(LOG_ERR, 
				"cim_query_dispatch: handle_func failed."); 
			goto clean_and_return_null;
		}
	} else {
		goto clean_and_return_null;
	}
	mclient_free(client);

	/* cache it */	
	if ( msg ) {
		snprintf(pathname, MAXLEN, 
			"cache_1_%d_%s", func_id, param?param:"list");
		cim_msg2disk(pathname, msg); 
	}
	return msg;

clean_and_return_null:
	mclient_free(client);
	ha_msg_del(msg);
	return NULL;
}

int
cim_update_dispatch(int func_id, const char* param, void* in, void* out)
{
	const FunContext * ctx;
	MClient * client;
	const char * arg;
	int ret = HA_OK;
	int i = 0;

	if ((ctx = find_update_ctx(func_id)) == NULL ) {
		cl_log(LOG_ERR, "cim_update: can't find function %d", func_id);
		return HA_FAIL; 
	}
	if ( (client = mclient_new()) == NULL ) {
		cl_log(LOG_ERR, "%s: can't create client", __FUNCTION__);	
               	return HA_FAIL;	
        }

	while ((arg = ctx->cmnd[i++])){
		mclient_cmnd_append(client, arg);
	}
	
	if(param){
		mclient_cmnd_append(client, param);
	}

	ret = HA_OK;
	if ( ctx->handle_func ){
		ret = ctx->handle_func(client, in, ctx, out); 
	}
	
	if ( ret == HA_OK ) {
		if ( mclient_process(client) != MC_OK ) {
			cl_log(LOG_ERR, "%s: update failed.", __FUNCTION__);
			ret = HA_FAIL;
		}
	}

	mclient_free(client);
	return ret;
}


static void 
msg_pattern_free(void * param)
{
	struct msg_pattern_t *pattern = NULL;
	int i;

	if ((pattern = (struct msg_pattern_t*)param) == NULL ) {
		return;
	}
	
	for(i = 0; i < MSG_MAX_DEPTH; i++) {
		int j;
		for(j=0; j<pattern->len[i]; j++) {
			if ( pattern->keys[i][j] ) {
				cim_free(pattern->keys[i][j]);
			}
		}
		if ( pattern->tag[i] ) {
			cim_free(pattern->tag[i]);
		}
	}
	cim_free(pattern);
}

static struct msg_pattern_t * 
msg_pattern_parse(const char *exp)
{
	const char *p, *q;
	char ch, tmp[128];
	int depth = -1;
	struct msg_pattern_t * pattern;

	cim_debug2(LOG_INFO, "msg_pattern_parse: pattern_exp: %s", exp);
	pattern = (struct msg_pattern_t *)
			cim_malloc(sizeof(struct msg_pattern_t));
	if ( pattern ==  NULL ) {
		cl_log(LOG_ERR, "%s: alloc msg_pattern failed.", __FUNCTION__);
		return NULL;
	}
	memset(pattern, 0, sizeof(struct msg_pattern_t));
	pattern->depth = 0;

	p = exp;
	while((ch = *(p++) )) {
		if ( ch ==  BEGIN_NODE_CHAR ) {
			depth++; 
			if ( depth >= MSG_MAX_DEPTH) {
				cl_log(LOG_ERR, "%s: depth >= %d.", 
					__FUNCTION__, MSG_MAX_DEPTH); 
				msg_pattern_free(pattern);
				return NULL;
			} 
			q = p;
			while(	*p != BEGIN_KEY_CHAR 
			     && *p != BEGIN_NODE_CHAR && *p != REPEATE_CHAR){
				p++;
			}
			if ( p != q ) {
				memcpy(tmp, q, p - q);
				tmp[p - q] = EOS;
				cim_debug2(LOG_INFO, "Got tag %d:%s", depth, tmp);	
				pattern->tag[depth] = cim_strdup(tmp);
			}
			continue;
		} 
		if ( ch == REPEATE_CHAR ) {
			if(*p == ANYTIMES_CHAR){ /* the only case by now */
				p++;
			}
		}
		if ( ch == BEGIN_KEY_CHAR) {	/* found one pattern*/
			q = p;				/* its start position */
			while(*p != END_KEY_CHAR) {
				p++; 			/* its end position */
			}
			memcpy(tmp, q, p - q); 
			tmp[p - q] = EOS;
			pattern->len[depth] ++;
			pattern->keys[depth] = cim_realloc(pattern->keys[depth], 
						pattern->len[depth]*sizeof(char*));
			pattern->keys[depth][pattern->len[depth]-1] = cim_strdup(tmp); 
			continue;
		}
	}
	pattern->depth = depth;
	return pattern;
}

static int      
list_return(MClient *client, void *data, const FunContext *ctx, void *out)
{
	struct ha_msg *msg;
	int i;
	if((msg = (struct ha_msg *)data) == NULL ) {
		cl_log(LOG_ERR, "%s: msg not alloced.", __FUNCTION__);
		return HA_FAIL;
	}

	for(i=0; i < client->rlen; i++){
		char *value = mclient_nth_value(client, i);
		if(value == NULL ) {
			cl_log(LOG_ERR, "%s: failed to get at %d.",
				__FUNCTION__, i);
			return HA_FAIL;
		}
		cim_list_add(msg, value);
	}
	return HA_OK;
}

/* default callback for query_dispatch */
static int      
node_return(MClient *client, void *data, const FunContext *ctx, void *out)
{
	struct ha_msg *msg;
	int i, index, len;
	struct msg_pattern_t * pattern;

	if ((pattern = msg_pattern_parse(ctx->pattern_exp)) == NULL ) {
		cl_log(LOG_ERR, "node_return: pattern parse failed.");
		return HA_FAIL;
	}
	index = 0;
	if((msg = (struct ha_msg *)data) == NULL ) {
		cl_log(LOG_ERR, "%s: msg not alloced.", __FUNCTION__);
		return HA_FAIL;
	}

	/* parent node */
	for(i=0; i < pattern->len[0]; i++){
		char *value = mclient_nth_value(client, index ++);
		if(value == NULL ) {
			cl_log(LOG_ERR, "%s: get pattern:%d failed.",
				__FUNCTION__, i);
			return HA_FAIL;
		}

		ha_msg_add(msg, pattern->keys[0][i], value);
	}

	if( pattern->tag[0] ) {
		ha_msg_add(msg, CIM_MSG_TAG, pattern->tag[0]);
	}
	/* no child node, done */
	if(pattern->len[1] == 0 || pattern->keys[1] == NULL) {
		 goto done;
	}

	/* otherwise, create child node and add it to upper node */
	len = (client->rlen - index)/pattern->len[1];
	for(i=0; i < len; i++){
		char * value = NULL;
		struct ha_msg *child;
		int j = 0;

		child = ha_msg_new(pattern->len[1]);
		for (j=0; j < pattern->len[1]; j++) {
			value = mclient_nth_value(client, index ++);
			if(value == NULL ) {
				ha_msg_del(child);
				goto done;
			}
			ha_msg_add(child, pattern->keys[1][j], value);
		}
		if( pattern->tag[1] ) {
			ha_msg_add(child, CIM_MSG_TAG, pattern->tag[1]);
		}
		cim_msg_add_child(msg, cl_get_string(child, "id"), child);
	}
done:
	msg_pattern_free(pattern);
	return HA_OK;
}

/* default callback for update_dispatch */
static int
node_update(MClient* client, void* data, const FunContext* ctx, void* out)
{
	int len, i;
	struct msg_pattern_t * pattern;
	struct ha_msg *msg = NULL;

	if ( (msg = (struct ha_msg *)data) == NULL ) {
		return HA_OK;
	}
	if ((pattern = msg_pattern_parse(ctx->pattern_exp)) == NULL ) {
		DEBUG_LEAVE();
		return HA_FAIL;
	}

	for(i=0; i < pattern->len[0]; i++){
		const char *value;
		value = cl_get_string(msg, pattern->keys[0][i]);
		mclient_cmnd_append(client, value? value : "");
	}

	/* no child msg, done */
	if(pattern->len[1] == 0 || pattern->keys[1] == NULL) {
		goto done;
	}

	/* otherwise, get child struct and append to client */
	len = cim_msg_children_count(msg);
	for(i=0; i < len; i++){
		const char *value = NULL;
		struct ha_msg *child;
		int j = 0;
		child = cim_msg_child_index(msg, i);
		cl_log(LOG_INFO, "%s: child: %s", __FUNCTION__, msg2string(child));
		for (j=0; j < pattern->len[1]; j++) {
			value = cl_get_string(child, pattern->keys[1][j]);
			cl_log(LOG_INFO, "%s: %s -- %s", __FUNCTION__, 
						pattern->keys[1][j], value);
			mclient_cmnd_append(client, value?value : "");
		}
	}
done:
	msg_pattern_free(pattern);
	return HA_OK;
}

int
cim_get_hb_status ()
{
	char ** std_out = NULL;
	int     ret;
	int     status = HB_UNKNOWN;

	run_shell_cmnd(CLIHB" status", &ret, &std_out, NULL);
	if ( std_out == NULL || std_out[0] == NULL ) {
		return HB_UNKNOWN;
	} 

	if ( strstr ( std_out[0], "running") != NULL ) {
		status = HB_RUNNING;
	} else {
		status = HB_STOPED;
	}

	free_2d_zarray(std_out, cim_free);	
	return status;
}

int
cim_change_hb_state (int state)
{
	int	status;
	int	rc;
	const char * cmnd = NULL;
	switch(state) {
	case START_HB:
		cmnd = CLIHB" start"; break;
	case STOP_HB:
		cmnd = CLIHB" stop"; break;
	case RESTART_HB:
		cmnd = CLIHB" restart"; break;
	default:
		cl_log(LOG_ERR, "cim_change_hb_state: unknown opeation");
	}

	/* run it */
	rc=system(cmnd);
	if (rc != 0) {
		cl_log(LOG_ERR, "%s: system(%s) returned %d",
		__FUNCTION__, cmnd, rc);
	}

	/* verify the status */
	status = cim_get_hb_status();
	if ( (state == START_HB || state == RESTART_HB) 
				&& (status == HB_RUNNING) ) {
		return HA_OK;
	} else if( state == STOP_HB && status == HB_STOPED ) {
		return HA_OK;
	}	
	return HA_FAIL;
}

static int
InsertOption(struct ha_msg *msg, const char * directive, const char * option)
{
	int i = 0;
	const struct map_t *map = cim_query_map(HA_CLUSTER);

	if (map== NULL ) {
		return HA_FAIL;
	}

	for (i=0; i<map->len; i++) {
		if ( strncmp(directive, map->entry[i].key, MAXLEN) == 0){
			break;
		}
	}	

	if (map->entry[i].type == CMPI_charsA) {
		cl_msg_list_add_string(msg, directive, option);
	} else {
		cl_msg_modstring(msg, directive, option);
	}
	return HA_OK;
}

struct ha_msg*
cim_get_hacf_config ()
{
	FILE *       f = NULL;
	char	     buf[MAXLEN];
	char *	     cp;
	char 	     directive[MAXLEN];
	size_t	     dirlength;
	char  	     option[MAXLEN];
	size_t	     optionlength;
	struct ha_msg * info = NULL;
	const char * cfgfile = CONFIG_FILENAME;
	
	DEBUG_ENTER();
	if ((info = ha_msg_new(16)) == NULL ) {
		return NULL;
	}

	if ((f = fopen(cfgfile, "r")) == NULL ) {
		cl_log(LOG_ERR, "Failed to open %s", cfgfile);
		ha_msg_del(info);
		return NULL;
	}

	while (fgets(buf, MAXLEN, f) != NULL) {
		char *   bp = buf; 
		char     option_full[MAXLEN] = "";

		/* Skip over white space */
		bp += strspn(bp, WHITESPACE);
		/* Zap comments on the line */
		if ((cp = strchr(bp, COMMENTCHAR)) != NULL)  {
			*cp = EOS;
		}
		/* Strip '\n' and '\r' chars */
		if ((cp = strpbrk(bp, CRLF)) != NULL) {
			*cp = EOS;
		}

		/* Ignore blank (and comment) lines */
		if (*bp == EOS) {
			continue;
		}

		/* Now we expect a directive name */
		dirlength = strcspn(bp, WHITESPACE);
		strncpy(directive, bp, dirlength);
		directive[dirlength] = EOS;

		while (*bp != EOS) {
			optionlength = strcspn(bp, DELIMS);
			strncpy(option, bp, optionlength);
			option[optionlength] = EOS;
			bp += optionlength;
			
			if ( strcmp(option, directive) != 0 ) {	
				if ( strcmp(directive, "node") != 0) {
					strcat(option_full, option);	
					strcat(option_full, " ");
				}else{
					/* directive is "node" */
					InsertOption(info, directive, option);
				}
			}
			/* Skip over Delimiters */
			bp += strspn(bp, DELIMS);
		}

		if ( strcmp(directive, "node") != 0 ) {
			option_full[strlen(option_full) - 1] = EOS;
			InsertOption(info, directive, option_full);
		}
	}
	fclose(f);	
	DEBUG_LEAVE();
	return info;
}

static int
StrIsEmpty(const char * str)
{
	char ch;
	while ( (ch = *(str++) )){
		if ( ch ==' ' || ch == '\t'){
			continue;
		} else {
			return 0;
		}
	}
	return 1;
}

#define SPACELEN 20
#define FILLSPACE(f, n) do{ 	\
	int i=0; 		\
	for(i=0; i<(n); i++){	\
		fprintf(f, " ");\
	}			\
} while(0);

static void
write_file_foreach(char *key, struct ha_msg *msg, void *user)
{
	FILE * f = (FILE *) user;
	int i, len;
	const struct map_t *map = cim_query_map(HA_CLUSTER);

	if (map == NULL ) {
		return;
	}

	for (i=0; i<map->len; i++) {
		if ( strncmp(key, map->entry[i].key, MAXLEN) == 0){
			break;
		}
	}	
	if (map->entry[i].type == CMPI_charsA) {
		len = cl_msg_list_length(msg, key);
		for (i =0; i < len; i++) {
			char * value;
			value = (char*)cl_msg_list_nth_data(msg, key, i);
			if ( !StrIsEmpty(value) ){
				fprintf(f, "%s", (char *)key);
				FILLSPACE(f, SPACELEN - strlen(key))
				fprintf(f, "%s\n", value);
			}
		}
	} else {
		const char *value = cl_get_string(msg, key);
		if (value && !StrIsEmpty(value)) {
			fprintf(f, "%s", (char *)key);
			FILLSPACE(f, SPACELEN - strlen(key))
			fprintf(f, "%s\n", value);
		}
	}
}	

int
cim_update_hacf(struct ha_msg *info) 
{
        FILE * f = NULL;
	int i;
        /* backup old file */
        if ( rename(CONFIG_NAME, BACKUP_CONFIG_NAME) != 0 ) {
		cl_log(LOG_WARNING, "Backup ha.cf failed.");
        }

        if ( ( f = fopen(CONFIG_NAME, "w") ) == NULL ) {
		cl_log(LOG_ERR, "Could not open ha.cf");
                return HA_FAIL;
        }
	cl_log(LOG_INFO, "Begin dump config infomation.");
	fprintf(f, CONFIG_COMMENT);
	for(i=0; i<info->nfields; i++) {
		write_file_foreach(info->names[i], info, f);	
	}

        fclose(f);
        return HA_OK;
}

struct ha_msg *
cim_get_authkeys(void) 
{
        FILE *       	f = NULL;
        struct stat  	keyfilestat;
        int          	authnum = -1;
        char         	buf[MAXLEN];
        char         	key[MAXLEN];
        char         	method[MAXLEN];
        int          	i;
        int          	src;
	struct ha_msg * authinfo; 

	if ( (authinfo = ha_msg_new(2)) == NULL ) {
		cl_log(LOG_ERR, "Alloc table failed.");		
                return NULL;
        }

        if ((f = fopen(KEYFILE, "r")) == NULL) {
                cl_log(LOG_ERR, "Cannot open keyfile [%s].  Stop."
                ,       KEYFILE);
                ha_msg_del(authinfo);
		return NULL;
        } 

        if (fstat(fileno(f), &keyfilestat) < 0
            ||  keyfilestat.st_mode & (S_IROTH | S_IRGRP)) {
                cl_log(LOG_ERR, "Bad permissions on keyfile"
                       " [%s], 600 recommended.", KEYFILE);
                fclose(f);
                ha_msg_del(authinfo);
                return NULL;
        }

        while(fgets(buf, MAXLEN, f) != NULL) {
                char *  bp = buf;

                bp += strspn(bp, WHITESPACE);
                if (*bp == COMMENTCHAR || *bp == EOS) {
                        continue;
                }
                if (*bp == 'a') {
                        if ((src=sscanf(bp, "auth %d", &authnum)) != 1) {
                                cl_log(LOG_ERR
                                ,       "Invalid auth line [%s] in " KEYFILE
                                ,        buf);
                        }
                        /* Parsing of this line now complete */
                        continue;
                }
                key[0] = EOS;
                if ((src=sscanf(bp, "%d%s%s", &i, method, key)) >= 2) {
                        if ((i < 0) || (i >= MAXAUTH)) {
                                ha_log(LOG_ERR, "Invalid authnum [%d] in "
                                       KEYFILE, i);
                                continue;
                        }
                        
                        /* found */
                        if ( i == authnum ) { break; }
                } else if (*bp != EOS) {
                        ha_log(LOG_ERR, "Auth line [%s] is invalid."
                        ,       buf);
                }
        }
        
        /* add auth method, key to dict */
        ha_msg_add(authinfo, "authmethod", method);
        ha_msg_add(authinfo, "authkey", key);

        fclose(f);
        return authinfo;
}

int 
cim_update_authkeys(struct ha_msg *msg)
{
        FILE * f;
        const char *method, *key;        

        /* backup old file */ 
        if ( rename(KEYFILE, BACKUP_KEYFILE) != 0 ) {
		cl_log(LOG_WARNING, "Backup authkeys failed.");
        }

        if ( ( f = fopen(KEYFILE, "w") ) == NULL ) {
		cl_log(LOG_ERR, "Can not open authkeys.");
                return HA_FAIL;
        }

        /* set mode to 0600 */
        if ( chmod(KEYFILE, 0600) != 0 ) {
		cl_log(LOG_ERR, "Cat not chmod authkeys to 0600.");
                fclose(f);
                return HA_FAIL;
        }

	/* get new value */
        method = cl_get_string(msg, "authmethod");
        key    = cl_get_string(msg, "authkey");

	if (method == NULL || key == NULL ) {
		cl_log(LOG_ERR, "FATAL: method or key is NULL.");
		fclose(f);
		return HA_FAIL;
	}

        /* write file */
	fprintf(f, CONFIG_COMMENT);
        fprintf(f, "auth %u\n", 1);
        fprintf(f, "%u %s %s\n", 1, method, key);

        fclose(f);
        return HA_OK;
}

struct ha_msg *
cim_get_software_identity(void)
{
	char ** out = NULL;
	int     ret;
	DEBUG_ENTER();
	run_shell_cmnd(HA_LIBDIR"/heartbeat/heartbeat -V", &ret, &out, NULL);

	if ( out ) {
		struct ha_msg * msg;
		char *		hbversion;

		if ( (msg = ha_msg_new(1)) == NULL){
			free_2d_zarray(out, cim_free);
			return NULL;
		}
		hbversion = out[0];
		if ( hbversion ){
			hbversion[strlen(hbversion)-1]=EOS;
			ha_msg_add(msg, "hbversion", hbversion);
			DEBUG_LEAVE();
			return msg;
		} else {
			ha_msg_del(msg);
		}
	}

	DEBUG_LEAVE();
	return NULL;
}

static int
cib_changed()
{
	/*FIXME: implementation required */
	return 1;
}


/****************************************************************
 * resource list
 ***************************************************************/
int
cim_rsc_is_in_cib(const char *rscid)
{
	char * value;
	/*
	struct ha_msg *list=NULL;
	
	if ((list = cim_query_dispatch(GET_RSC_LIST, NULL, NULL))){
		if ( cim_list_find(list, rscid)) {
			ha_msg_del(list);
			cim_debug2(LOG_INFO, "%s: %s is in CIB, "
				"reason: it's in the CIB resource list.", 
				__FUNCTION__, rscid);
			return TRUE;
		}	
	}
	*/
	if ((value = cim_rscname_table_get(rscid)) == NULL ) {
		return TRUE;
	} else { 
		cim_free(value);
		return FALSE;
	}
}


	

struct ha_msg*	
cim_get_all_rsc_list(void)
{
	struct ha_msg *list1 = NULL, *list2;
	int len2;
	
	/* list in CIB */
	list1 = cim_query_dispatch(GET_RSC_LIST, NULL, NULL);
	if (list1 == NULL && (list1 = ha_msg_new(1)) == NULL ) {
		cl_log(LOG_ERR, "cim_get_all_rsc_list: can't get list1.");
		return NULL;
	}

	/* merge with diabled list */
	if ((list2 = cim_rscname_table_keys())) {
		int i;
		len2 = cim_list_length(list2);
		cim_debug2(LOG_INFO, "len2 = %d", len2);
		for(i=0; i<len2; i++) {
			char *rsc = cim_list_index(list2, i);
			cim_debug2(LOG_INFO, "rsc at %d is %s", i , rsc);
			if (rsc) {
				cim_list_add(list1, rsc);	
			}
		}
	}
	return list1;
}

struct ha_msg* 	
cim_traverse_allrsc(struct ha_msg* list)
{
	struct ha_msg * newlist;
	int i, len;

	len = cim_list_length(list);
	if ((newlist = ha_msg_new(len))== NULL ) {
		return NULL;
	}
	cim_debug2(LOG_INFO, "length of list: %d", len);
	for (i = 0; i < len; i++) {
		char * rscid;
		struct ha_msg *sublist, *next;
		int j, nextlen;

		rscid = cim_list_index(list, i);
		if ( rscid == NULL ) {
			continue;
		}
		cim_list_add(newlist, rscid);
		if (cim_get_rsctype(rscid) == TID_RES_PRIMITIVE) {
			continue;
		}
	
		cim_debug2(LOG_INFO, 
			"rsc %s is not primitive, get its subrsc.", rscid);
		if ((sublist = cim_get_subrsc_list(rscid)) == NULL ) {
			continue;
		}
		
		next = cim_traverse_allrsc(sublist);
		nextlen = cim_list_length(next);
		for (j = 0; j < nextlen; j++) {
			char * nextrscid;
			nextrscid = cim_list_index(next, j);
			if ( nextrscid == NULL ) {
				continue;
			}
			cim_list_add(newlist, nextrscid);
		}
		ha_msg_del(sublist);
	}
	return newlist;
}

/*********************************************************
 * resource type 
 ********************************************************/


static int
cim_rsctype_s2tid(const char *type)
{
	int tid = TID_UNKNOWN;
	if ( type == NULL ) {
		return TID_UNKNOWN;
	}
	if(strncmp(type, S_RES_PRIMITIVE, MAXLEN)== 0){
		tid = TID_RES_PRIMITIVE;
	} else if (strncmp(type, S_RES_GROUP, MAXLEN) == 0) {
		tid = TID_RES_GROUP;
	} else if (strncmp(type, S_RES_CLONE, MAXLEN) == 0){
		tid = TID_RES_CLONE;
	} else if (strncmp(type, S_RES_MASTER, MAXLEN) == 0){
		tid = TID_RES_MASTER;
	}

	return tid;
}
static const char*
cim_rsctype_tid2s(int type)
{
	const char * rsctype = NULL;
	if (type == TID_RES_PRIMITIVE ) {
		rsctype = S_RES_PRIMITIVE;
	} else if ( type == TID_RES_CLONE ) {
		rsctype = S_RES_CLONE;
	} else if ( type == TID_RES_MASTER) {
		rsctype = S_RES_MASTER;
	} else if ( type == TID_RES_GROUP ) {
		rsctype = S_RES_GROUP;
	} else {
		rsctype = S_RES_UNKNOWN;
	}
	return rsctype;
}

int
cim_get_rsctype(const char * rscid)
{
	char * type = NULL;
	int tid = TID_UNKNOWN;
	
	DEBUG_ENTER();
	if ( RESOURCE_DISABLED(rscid)) {
		type = cim_dbget(CIM_RSCTYPE_TABLE, rscid);
	} else {
		struct ha_msg *msg;
		msg = cim_query_dispatch(GET_RSC_TYPE, rscid, NULL);
		if ( msg ) {
			type = cim_strdup(cl_get_string(msg, "type"));
			ha_msg_del(msg);
		}
	} 
	tid = cim_rsctype_s2tid(type);
	if (type) {
		cim_free(type);
	}

	DEBUG_LEAVE();
	return tid;
}

/*******************************************************
 * resource operations 
 ******************************************************/
struct ha_msg *
cim_get_rscops(const char *rscid)
{
	struct ha_msg *ops = NULL;
	ops = ( RESOURCE_ENABLED(rscid)) ?/* from CIB */
		cim_query_dispatch(GET_RSC_OPERATIONS, rscid, NULL) :
		cim_dbget_msg(CIM_RSCOPS_TABLE, rscid);
	return ops;
}

int
cim_del_rscop(const char *rscid, const char *opid)
{
	struct ha_msg *ops;
	int ret;

	if ( RESOURCE_ENABLED(rscid) ) {	/* update to CIB */
		ret = cim_update_dispatch(DEL_OPERATION, opid, NULL, NULL);
		return ret;
	} ;

	if ((ops = cim_get_rscops(rscid)) == NULL ) {
		cl_log(LOG_WARNING, "cim_del_rscop: ops not exists");
		return HA_OK;
	}

	cim_msg_remove_child(ops, opid);
	ret = cim_dbput_msg(CIM_RSCOPS_TABLE, rscid, ops);
	return ret;
}

int
cim_add_rscop(const char *rscid, struct ha_msg *op)
{
	struct ha_msg *ops;
	int ret;

	if ((ops = cim_get_rscops(rscid)) == NULL) {
		if ((ops = ha_msg_new(1)) == NULL ) {
				return HA_FAIL;
		}
	}

	cim_msg_add_child(ops, cl_get_string(op, "id"), op);
	ret = ( RESOURCE_ENABLED(rscid) ) ? /* update into CIB */
		cim_update_dispatch(UPDATE_OPERATIONS, rscid, ops, NULL) :
		cim_dbput_msg(CIM_RSCOPS_TABLE, rscid, ops);
	ha_msg_del(ops);
	return ret;
}


int
cim_update_rscop(const char* rscid, const char* id, struct ha_msg* op)
{
	struct ha_msg *ops;
	int ret;

	if ((ops = cim_get_rscops(rscid)) == NULL) {
		cl_log(LOG_ERR, "%s: ops of %s not found.", 
				__FUNCTION__, rscid);
		return HA_FAIL;
	}

	/* update op */
	cim_msg_remove_child(ops, id);
	cim_msg_add_child(ops, id, op);
	
	ret = RESOURCE_ENABLED(rscid) ?	/* update into CIB */
		cim_update_dispatch(UPDATE_OPERATIONS, rscid, ops, NULL) :
		cim_dbput_msg(CIM_RSCOPS_TABLE, rscid, ops);
	ha_msg_del(ops);
	return ret;
}

/*********************************************************
 * resource attributes 
 ********************************************************/
int
cim_rscattrs_del(const char *rscid)
{
	return cim_dbdel(CIM_RSCATTRS_TABLE, rscid);
}

struct ha_msg *
cim_rscattrs_get(const char *rscid)
{
	struct ha_msg *attrs = NULL;
	attrs = (RESOURCE_ENABLED(rscid)) ?
			cim_query_dispatch(GET_RSC_ATTRIBUTES, rscid, NULL) :
			cim_dbget_msg(CIM_RSCATTRS_TABLE, rscid);
	return attrs;
}

int
cim_update_attrnvpair(const char*rscid, const char*nvid, struct ha_msg *nvpair)
{
	int ret;
	struct ha_msg *attrs = cim_rscattrs_get(rscid);

	if ( attrs == NULL ) {
		if ((attrs = ha_msg_new(1)) == NULL ) {
			cl_log(LOG_ERR, "%s: attributes not found for %s",
					__FUNCTION__, rscid);
			return HA_FAIL;
		}
	}

	cl_msg_modstruct(attrs, nvid, nvpair);
	ret =  RESOURCE_DISABLED(rscid)?
		cim_dbput_msg(CIM_RSCATTRS_TABLE, rscid, attrs) :
	 	cim_update_dispatch(UPDATE_ATTRIBUTES, rscid, attrs, NULL);
	ha_msg_del(attrs);
	return ret;
}

int
cim_remove_attrnvpair(const char* rscid, const char* nvid)
{
	int ret;
	struct ha_msg *attrs = cim_rscattrs_get(rscid);
	if ( attrs == NULL ) {
		return HA_FAIL;
	}

	cl_msg_remove(attrs, nvid);
	ret =  RESOURCE_DISABLED(rscid)? cim_dbdel(CIM_RSCATTRS_TABLE, rscid) :
			cim_update_dispatch(DEL_ATTRIBUTES, nvid, NULL, NULL);
	ha_msg_del(attrs);
	return ret;
}

/**********************************************************
 * resource
 *********************************************************/

static void
AddPrimitiveForUpdate(struct ha_msg *msg, struct ha_msg* rsc, struct ha_msg *attrs)
{
	int len, i;
	const char * id;

	cim_debug2(LOG_INFO, "In AddPrimitiveForUpdate.");
	id = cl_get_string(rsc, "id");
	ha_msg_add(msg, "id", cl_get_string(rsc, "id"));
	ha_msg_add(msg, "provider", cl_get_string(rsc, "provider"));
	ha_msg_add(msg, "class", cl_get_string(rsc, "class"));
	ha_msg_add(msg, "type", cl_get_string(rsc, "type"));
	
	attrs = attrs? attrs : cim_rscattrs_get(id);
	if ( attrs == NULL ) {
		cl_log(LOG_ERR, "%s: attributes of %s not found.", __FUNCTION__, id);
		return ;
	}
	len = cim_msg_children_count(attrs);
	for (i = 0; i < len; i++) {
		struct ha_msg *nvpair = NULL;
		nvpair = cim_msg_child_index(attrs, i);
		cim_msg_add_child(msg, cl_get_string(nvpair, "id"), nvpair);
	}
}

int
cim_rsc_submit(const char *rscid)
{
	int type, ret;
	struct ha_msg *rsc, *sublist, *msg;

	if ( RESOURCE_ENABLED(rscid) ){
		cl_log(LOG_ERR, "%s: resource %s is already enabled. ",
			__FUNCTION__, rscid);
		return HA_FAIL;
	}
	type = cim_get_rsctype(rscid);
	if ( (rsc = cim_find_rsc(type, rscid)) == NULL ) {
		cl_log(LOG_ERR, "%s: resource %s not found.",
			__FUNCTION__, rscid);
		return HA_FAIL;
	}

	if ( (msg = ha_msg_new(16)) == NULL ) {
		cl_log(LOG_ERR, "%s: msg alloc failed.", __FUNCTION__);
		return HA_FAIL;
	}

	if ( type == TID_RES_GROUP ) {
		struct ha_msg *primitive;
		int i, count;

		if ((sublist = cim_get_subrsc_list(rscid)) == NULL ) {
			cl_log(LOG_ERR, 
				"%s: no primitive resource for %s found."
				"imcomplete resource.", __FUNCTION__, rscid);
			ha_msg_del(rsc);
			ha_msg_del(msg);
			return HA_FAIL;
		}

		/* first create a group in CIB */
		ret = cim_update_dispatch(CREATE_RSC_GROUP, rscid, NULL, NULL); 
		if ( ret == HA_FAIL) {
			ha_msg_del(rsc);
			ha_msg_del(msg);
			return HA_FAIL;
		}

		cim_rscdb_cleanup(TID_RES_GROUP, rscid);

		/* then add sub primitive resources into group */
		count = cim_list_length(sublist);
		for (i = 0; i < count; i++) {
			char *subrscid = cim_list_index(sublist, i);
			if (subrscid == NULL ) {
				continue;
			}
			primitive = cim_find_rsc(TID_RES_PRIMITIVE, subrscid);
			cim_debug_msg(primitive, "%s: primitve:%s to be added to group %s",
					__FUNCTION__, subrscid, rscid);
			cim_debug2(LOG_INFO, "%s: ready to add", __FUNCTION__);
			ret = cim_add_subrsc(rsc, primitive);
		}

		ha_msg_del(sublist);
	} else if ( type == TID_RES_CLONE || type == TID_RES_MASTER) {
		struct ha_msg *primitive;
		if ((sublist = cim_get_subrsc_list(rscid)) == NULL ) {
			cl_log(LOG_ERR, 
				"%s: no primitive resource for %s found."
				"imcomplete resource.", __FUNCTION__, rscid);
			ha_msg_del(rsc);
			ha_msg_del(msg);
			return HA_FAIL;
		}
		primitive = cim_find_rsc(TID_RES_PRIMITIVE, 
						cim_list_index(sublist, 0)); 
		if ( primitive == NULL ) {
			cl_log(LOG_ERR, "%s: can't find primitive %s.",
				 __FUNCTION__, cim_list_index(sublist, 0));
			ha_msg_del(rsc);
			ha_msg_del(msg);
			return HA_FAIL;
		}
		AddPrimitiveForUpdate(msg, primitive, NULL);
		ha_msg_add(msg, "groupid", "");	
		ha_msg_add(msg, "advance", cim_rsctype_tid2s(type)); 
		ha_msg_add(msg, "advance_id", rscid);
		ha_msg_add(msg, "clone_max", 
				cl_get_string(rsc, "clone_max"));
		ha_msg_add(msg, "clone_node_max", 
				cl_get_string(rsc, "clone_node_max"));
		ha_msg_add(msg, "master_max", ( type == TID_RES_MASTER ) ? 
				cl_get_string(rsc, "master_max") : "");
		ha_msg_add(msg, "master_node_max", ( type == TID_RES_MASTER ) ? 
				cl_get_string(rsc, "master_node_max") : "");

		ret = cim_update_dispatch(CREATE_RESOURCE, NULL, msg, NULL);

		cim_rscdb_cleanup(TID_RES_PRIMITIVE, 
				cl_get_string(primitive, "id"));
		ha_msg_del(sublist);
	} else if ( type == TID_RES_PRIMITIVE ) {
		AddPrimitiveForUpdate(msg, rsc, NULL);
		ha_msg_add(msg, "groupid", "");	
		ha_msg_add(msg, "advance", "");
		ha_msg_add(msg, "advance_id", "");
		ha_msg_add(msg, "clone_max", "");
		ha_msg_add(msg, "clone_node_max", "");
		ha_msg_add(msg, "master_max", "");
		ha_msg_add(msg, "master_node_max", "");
		ret = cim_update_dispatch(CREATE_RESOURCE, NULL, msg, NULL);

		cim_rscdb_cleanup(type, rscid);
	}

	ha_msg_del(rsc);
	ha_msg_del(msg);
	return HA_OK;
}

int
cim_rscdb_cleanup(int type, const char * rscid)
{
	if ( cim_rscname_table_del(rscid) != HA_OK ) {
		cl_log(LOG_ERR, "%s: FATAL: can't update %s.",
			__FUNCTION__, CIM_RSCNAME_TABLE);
		return HA_FAIL;
	}

	if ( type == TID_RES_PRIMITIVE && 
			cim_dbdel(CIM_RESOURCE_TABLE, rscid) != HA_OK ) {
		cl_log(LOG_WARNING, "%s: FATAL: can't update %s.",
			__FUNCTION__, CIM_RESOURCE_TABLE);
	}
	if ( type == TID_RES_PRIMITIVE && 
			cim_rscattrs_del(rscid) != HA_OK ) {
		cl_log(LOG_WARNING, "%s: FATAL: can't erase the attributes.",
			__FUNCTION__);
	}
	if ( cim_rsctype_table_del(rscid) != HA_OK ) {
		cl_log(LOG_WARNING, "%s: FATAL: can't remove from type list..",
			__FUNCTION__);
	}

	if ( type == TID_RES_GROUP ) {
		cim_debug2(LOG_INFO, "%s: %s is group, clean up subrsc_table.",
			__FUNCTION__, rscid);
		cim_dbdel(CIM_SUBRSC_NAME_TABLE, rscid);	
	}

	return HA_OK;
}

int
cim_rscdb_store(int type, const char *rscid, struct ha_msg *rsc)
{
	const char *rsctype = NULL;
	if ( cim_dbput_msg(CIM_RESOURCE_TABLE, rscid, rsc) != HA_OK ) {
		cl_log(LOG_ERR, "%s: cant't write resource image.", 
			__FUNCTION__);
		return HA_FAIL;
	}

	/* update rsctype table */
	rsctype = cim_rsctype_tid2s(type);
	if (cim_rsctype_table_add(rscid, rsctype) != HA_OK ) {
		cl_log(LOG_ERR, "%s: cant't update resource type table.", 
			__FUNCTION__);
		cim_dbdel(CIM_RESOURCE_TABLE, rscid);
		return HA_FAIL;
	}

	/* update rsc list */
	if ( cim_rscname_table_add(rscid) != HA_OK) {
		cl_log(LOG_ERR, "%s: cant't update %s.", 
			__FUNCTION__, CIM_RSCNAME_TABLE);
		cim_dbdel(CIM_RESOURCE_TABLE, rscid);
		cim_rsctype_table_del(rscid);
		return HA_FAIL;
	}

	return HA_OK;
}

struct ha_msg*	
cim_find_rsc(int type, const char * rscid)
{
	struct ha_msg *rsc = NULL/*, * attributes*/;

	if (RESOURCE_DISABLED(rscid)) {
		rsc = cim_dbget_msg(CIM_RESOURCE_TABLE, rscid);
		goto done;
	}
 
	switch(type){
	case TID_RES_PRIMITIVE:
		rsc = cim_query_dispatch(GET_PRIMITIVE, rscid, NULL);
		break;
	case TID_RES_MASTER:
		rsc = cim_query_dispatch(GET_MASTER, rscid, NULL);
		break;
	case TID_RES_CLONE:
		rsc = cim_query_dispatch(GET_CLONE, rscid, NULL);
		break;
	case TID_RES_GROUP:
		if ((rsc = ha_msg_new(1))){
			ha_msg_add(rsc, "id", rscid);
		}
		break;
	default:
		break;
	}
done:
	return rsc;
}

int
cim_update_rsc(int type, const char *rscid, struct ha_msg *resource)
{
	int ret = HA_FAIL;

	if ( RESOURCE_ENABLED(rscid) ) {	/* in-cib resource */
		if ( type == TID_RES_CLONE ) {
			ret = cim_update_dispatch(UPDATE_CLONE, 
						NULL, resource, NULL); 
		} else if (type == TID_RES_MASTER ) {
			ret = cim_update_dispatch(UPDATE_MASTER, 
						NULL, resource, NULL);
		} else if (type == TID_RES_PRIMITIVE ) {
			ret = HA_OK;
		}
	} else {	/* update disk image */
		ret = cim_dbput_msg(CIM_RESOURCE_TABLE, rscid, resource);
	}
	return ret;
}

int
cim_remove_rsc(const char * rscid)
{
	int ret;
	if ( RESOURCE_ENABLED(rscid)) {
		int rsctype = cim_get_rsctype(rscid);
		/* if it's a resource container, remove its sub resource first */
		if ( rsctype == TID_RES_GROUP || rsctype == TID_RES_CLONE ||
				rsctype == TID_RES_MASTER) {
			struct ha_msg *sublist = cim_get_subrsc_list(rscid);
			if (sublist) {
				int i , len = cim_list_length(sublist);
				for( i =0; i < len; i++) {
					char *subrscid = NULL;
					subrscid = cim_list_index(sublist,i);
					cim_remove_rsc(subrscid);
				}
			}
		}

		if ( rsctype == TID_RES_PRIMITIVE ) {
			struct ha_msg * msg;
			msg = cim_query_dispatch(GET_RSC_HOST, rscid, NULL);
			if (msg) {
				const char * host = cl_get_string(msg, "host");
				char * param=mclient_makeup_param(host, rscid);
				ret = cim_update_dispatch(CLEANUP_RESOURCE, 
						param, NULL, NULL);
			}
			ha_msg_del(msg);
		}

		ret = cim_update_dispatch(DEL_RESOURCE, rscid, NULL, NULL);
	} else {
		cim_rscdb_cleanup(cim_get_rsctype(rscid), rscid);
		ret = HA_OK;
	}
	return ret;
}

/*****************************************************************
 * sub resources 
 ****************************************************************/
struct ha_msg *
cim_get_subrsc_list(const char *rscid)
{
	struct ha_msg * sublist;

	sublist =  ( RESOURCE_ENABLED(rscid) )?
		cim_query_dispatch(GET_SUB_RSC, rscid, NULL) :
		cim_dbget_msg(CIM_SUBRSC_NAME_TABLE, rscid);
	return sublist;
}

int
cim_add_subrsc(struct ha_msg *rsc, struct ha_msg *subrsc)
{
	struct ha_msg * sublist;
	const char * rscid, *subrscid;
	int ret, type;


	rscid    = cl_get_string(rsc, "id");
	subrscid = cl_get_string(subrsc, "id");
	cim_debug2(LOG_INFO, "%s: adding subrsc: %s to %s.",
			__FUNCTION__, rscid, subrscid);

	if ( RESOURCE_ENABLED(subrscid) ){
		cl_log(LOG_ERR, "%s: resource %s is already in CIB.",
			__FUNCTION__, subrscid);
		return HA_FAIL;
	}

	if ( RESOURCE_DISABLED(rscid) ) {	/* write to disk */
		sublist = cim_dbget_msg(CIM_SUBRSC_NAME_TABLE, rscid);
		if (sublist == NULL ) {	/* not exist yet */
			if ((sublist = ha_msg_new(1)) == NULL ) {
				cl_log(LOG_ERR, "cim_add_subrsc: "
						"alloc sublist failed.");
				return HA_FAIL;
			}
		}
		/* add subrscid and write back to disk */
		cim_list_add(sublist, subrscid);
		ret = cim_dbput_msg(CIM_SUBRSC_NAME_TABLE, rscid, sublist);
	} else {		/* update to CIB */
		struct ha_msg * msg;

		type = cim_get_rsctype(rscid);
		if ( type != TID_RES_GROUP ){  
			cl_log(LOG_ERR, "%s: resource %s can't be added to %s,"
					"%s is not a resource group.",
				__FUNCTION__, subrscid, rscid, rscid);
			return HA_FAIL;
		}
		
		/* create new resource */
		if ( (msg = ha_msg_new(16)) == NULL ) {
			cl_log(LOG_ERR, "cim_add_subrsc: copy msg failed.");
			return HA_FAIL;
		}
		cim_debug2(LOG_INFO, "%s: call AddPrimitiveForUpdate", __FUNCTION__);
		AddPrimitiveForUpdate(msg, subrsc, NULL);
		if ( type == TID_RES_GROUP ) {
			ha_msg_add(msg, "groupid", rscid);	
			ha_msg_add(msg, "advance", "");
			ha_msg_add(msg, "advance_id", "");
			ha_msg_add(msg, "clone_max", "");
			ha_msg_add(msg, "clone_node_max", "");
			ha_msg_add(msg, "master_max", "");
			ha_msg_add(msg, "master_node_max", "");
		}

		cim_debug2(LOG_INFO, "%s: create resource.", __FUNCTION__);
		ret = cim_update_dispatch(CREATE_RESOURCE, NULL, msg, NULL);
	
		cim_rscdb_cleanup(TID_RES_PRIMITIVE, subrscid);
		ha_msg_del(msg);
	}
	return ret;
}


