/**
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.fastken.com/ for more detail.
**/

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include "fdfs_define.h"
#include "fastcommon/logger.h"
#include "fastcommon/sockopt.h"
#include "fdfs_global.h"
#include "fastcommon/shared_func.h"
#include "fastcommon/pthread_func.h"
#include "fastcommon/sched_thread.h"
#include "fdfs_shared_func.h"
#include "tracker_global.h"
#include "tracker_proto.h"
#include "tracker_func.h"
#include "tracker_relationship.h"
#include "tracker_mem.h"

#define TRACKER_MEM_ALLOC_ONCE	2

#define GROUP_SECTION_NAME_GLOBAL            "Global"
#define GROUP_SECTION_NAME_PREFIX            "Group"
#define GROUP_SECTION_NO_FORMAT              "%03d"
#define GROUP_ITEM_GROUP_COUNT               "group_count"
#define GROUP_ITEM_GROUP_NAME                "group_name"
#define GROUP_ITEM_STORAGE_PORT              "storage_port"
#define GROUP_ITEM_STORAGE_HTTP_PORT         "storage_http_port"
#define GROUP_ITEM_STORE_PATH_COUNT          "store_path_count"
#define GROUP_ITEM_SUBDIR_COUNT_PER_PATH     "subdir_count_per_path"
#define GROUP_ITEM_CURRENT_TRUNK_FILE_ID     "current_trunk_file_id"
#define GROUP_ITEM_LAST_TRUNK_SERVER         "last_trunk_server"
#define GROUP_ITEM_TRUNK_SERVER              "trunk_server"

#define STORAGE_SECTION_NAME_GLOBAL            "Global"
#define STORAGE_SECTION_NAME_PREFIX            "Storage"
#define STORAGE_SECTION_NO_FORMAT              "%03d"
#define STORAGE_ITEM_STORAGE_COUNT             "storage_count"

#define STORAGE_ITEM_GROUP_NAME                "group_name"
#define STORAGE_ITEM_SERVER_ID                 "id"
#define STORAGE_ITEM_IP_ADDR                   "ip_addr"
#define STORAGE_ITEM_STATUS                    "status"
#define STORAGE_ITEM_DOMAIN_NAME               "domain_name"
#define STORAGE_ITEM_VERSION                   "version"
#define STORAGE_ITEM_SYNC_SRC_SERVER           "sync_src_server"
#define STORAGE_ITEM_SYNC_UNTIL_TIMESTAMP      "sync_until_timestamp"
#define STORAGE_ITEM_JOIN_TIME                 "join_time"
#define STORAGE_ITEM_TOTAL_MB                  "total_mb"
#define STORAGE_ITEM_FREE_MB                   "free_mb"
#define STORAGE_ITEM_CHANGELOG_OFFSET          "changelog_offset"
#define STORAGE_ITEM_STORE_PATH_COUNT          "store_path_count"
#define STORAGE_ITEM_SUBDIR_COUNT_PER_PATH     "subdir_count_per_path"
#define STORAGE_ITEM_UPLOAD_PRIORITY           "upload_priority"
#define STORAGE_ITEM_STORAGE_PORT              "storage_port"
#define STORAGE_ITEM_STORAGE_HTTP_PORT         "storage_http_port"
#define STORAGE_ITEM_TOTAL_UPLOAD_COUNT        "total_upload_count"
#define STORAGE_ITEM_SUCCESS_UPLOAD_COUNT      "success_upload_count"
#define STORAGE_ITEM_TOTAL_APPEND_COUNT        "total_append_count"
#define STORAGE_ITEM_SUCCESS_APPEND_COUNT      "success_append_count"
#define STORAGE_ITEM_TOTAL_SET_META_COUNT      "total_set_meta_count"
#define STORAGE_ITEM_SUCCESS_SET_META_COUNT    "success_set_meta_count"
#define STORAGE_ITEM_TOTAL_DELETE_COUNT        "total_delete_count"
#define STORAGE_ITEM_SUCCESS_DELETE_COUNT      "success_delete_count"
#define STORAGE_ITEM_TOTAL_DOWNLOAD_COUNT      "total_download_count"
#define STORAGE_ITEM_SUCCESS_DOWNLOAD_COUNT    "success_download_count"
#define STORAGE_ITEM_TOTAL_GET_META_COUNT      "total_get_meta_count"
#define STORAGE_ITEM_SUCCESS_GET_META_COUNT    "success_get_meta_count"
#define STORAGE_ITEM_TOTAL_CREATE_LINK_COUNT   "total_create_link_count"
#define STORAGE_ITEM_SUCCESS_CREATE_LINK_COUNT "success_create_link_count"
#define STORAGE_ITEM_TOTAL_DELETE_LINK_COUNT   "total_delete_link_count"
#define STORAGE_ITEM_SUCCESS_DELETE_LINK_COUNT "success_delete_link_count"
#define STORAGE_ITEM_TOTAL_UPLOAD_BYTES        "total_upload_bytes"
#define STORAGE_ITEM_SUCCESS_UPLOAD_BYTES      "success_upload_bytes"
#define STORAGE_ITEM_TOTAL_APPEND_BYTES        "total_append_bytes"
#define STORAGE_ITEM_SUCCESS_APPEND_BYTES      "success_append_bytes"
#define STORAGE_ITEM_TOTAL_DOWNLOAD_BYTES      "total_download_bytes"
#define STORAGE_ITEM_SUCCESS_DOWNLOAD_BYTES    "success_download_bytes"
#define STORAGE_ITEM_TOTAL_SYNC_IN_BYTES       "total_sync_in_bytes"
#define STORAGE_ITEM_SUCCESS_SYNC_IN_BYTES     "success_sync_in_bytes"
#define STORAGE_ITEM_TOTAL_SYNC_OUT_BYTES      "total_sync_out_bytes"
#define STORAGE_ITEM_SUCCESS_SYNC_OUT_BYTES    "success_sync_out_bytes"
#define STORAGE_ITEM_TOTAL_FILE_OPEN_COUNT     "total_file_open_count"
#define STORAGE_ITEM_SUCCESS_FILE_OPEN_COUNT   "success_file_open_count"
#define STORAGE_ITEM_TOTAL_FILE_READ_COUNT     "total_file_read_count"
#define STORAGE_ITEM_SUCCESS_FILE_READ_COUNT   "success_file_read_count"
#define STORAGE_ITEM_TOTAL_FILE_WRITE_COUNT    "total_file_write_count"
#define STORAGE_ITEM_SUCCESS_FILE_WRITE_COUNT  "success_file_write_count"
#define STORAGE_ITEM_LAST_SOURCE_UPDATE        "last_source_update"
#define STORAGE_ITEM_LAST_SYNC_UPDATE          "last_sync_update"
#define STORAGE_ITEM_LAST_SYNCED_TIMESTAMP     "last_synced_timestamp"
#define STORAGE_ITEM_LAST_HEART_BEAT_TIME      "last_heart_beat_time"

TrackerServerGroup g_tracker_servers = {0, 0, -1, NULL};
TrackerServerInfo *g_last_tracker_servers = NULL;  //for delay free
int g_next_leader_index = -1;			   //next leader index
int g_trunk_server_chg_count = 1;		   //for notify other trackers
int g_tracker_leader_chg_count = 0;		   //for notify storage servers

int64_t g_changelog_fsize = 0; //storage server change log file size
static int changelog_fd = -1;  //storage server change log fd for write
static bool need_get_sys_files = true;
static bool get_sys_files_done = false;

static pthread_mutex_t mem_thread_lock;
static pthread_mutex_t mem_file_lock;

static void tracker_mem_find_store_server(FDFSGroupInfo *pGroup);
static int tracker_mem_find_trunk_server(FDFSGroupInfo *pGroup, 
		const bool save);

static int _tracker_mem_add_storage(FDFSGroupInfo *pGroup,
	FDFSStorageDetail **ppStorageServer, const char *id,
	const char *ip_addr, const bool bNeedSleep,
	const bool bNeedLock, bool *bInserted);

static int tracker_mem_add_storage(TrackerClientInfo *pClientInfo,
		const char *id, const char *ip_addr,
		const bool bNeedSleep, const bool bNeedLock, bool *bInserted);

static int tracker_mem_add_storage_from_file(FDFSGroups *pGroups,
        const char *data_path, TrackerClientInfo *pClientInfo,
		const char *group_name, const char *storage_id, char *ip_addr);

static int tracker_mem_add_group_ex(FDFSGroups *pGroups,
	TrackerClientInfo *pClientInfo, const char *group_name,
	const bool bNeedSleep, bool *bInserted);

static int tracker_mem_destroy_groups(FDFSGroups *pGroups, const bool saveFiles);

char *g_tracker_sys_filenames[TRACKER_SYS_FILE_COUNT] = {
	STORAGE_GROUPS_LIST_FILENAME_NEW,
	STORAGE_SERVERS_LIST_FILENAME_NEW,
	STORAGE_SYNC_TIMESTAMP_FILENAME,
	STORAGE_SERVERS_CHANGELOG_FILENAME
};      
   
int tracker_mem_pthread_lock()
{
	int result;
	if ((result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	return 0;
}

int tracker_mem_pthread_unlock()
{
	int result;
	if ((result=pthread_mutex_unlock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_unlock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	return 0;
}

int tracker_mem_file_lock()
{
	int result;
	if ((result=pthread_mutex_lock(&mem_file_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	return 0;
}

int tracker_mem_file_unlock()
{
	int result;
	if ((result=pthread_mutex_unlock(&mem_file_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_unlock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	return 0;
}

static int tracker_write_to_changelog(FDFSGroupInfo *pGroup, \
		FDFSStorageDetail *pStorage, const char *pArg)
{
	char buff[256];
	int len;
	int result;

	tracker_mem_file_lock();

	len = snprintf(buff, sizeof(buff), "%d %s %s %d %s\n", \
		(int)g_current_time, pGroup->group_name, pStorage->id, \
		pStorage->status, pArg != NULL ? pArg : "");

	if (fc_safe_write(changelog_fd, buff, len) != len)
	{
		tracker_mem_file_unlock();

		result = errno != 0 ? errno : EIO;
		logError("file: "__FILE__", line: %d, " \
			"write to file: %s fail, " \
			"errno: %d, error info: %s", \
			__LINE__, STORAGE_SERVERS_CHANGELOG_FILENAME, \
			result, STRERROR(result));

		return result;
	}

	g_changelog_fsize += len;
	result = fsync(changelog_fd);
	if (result != 0)
	{
		result = errno != 0 ? errno : EIO;
		logError("file: "__FILE__", line: %d, " \
			"call fsync of file: %s fail, " \
			"errno: %d, error info: %s", \
			__LINE__, STORAGE_SERVERS_CHANGELOG_FILENAME, \
			result, STRERROR(result));
	}

	tracker_mem_file_unlock();

	return result;
}

static int tracker_malloc_storage_path_mbs(FDFSStorageDetail *pStorage, \
		const int store_path_count)
{
	int alloc_bytes;

	if (store_path_count <= 0)
	{
		return 0;
	}

	alloc_bytes = sizeof(int64_t) * store_path_count;
	pStorage->path_total_mbs = (int64_t *)malloc(alloc_bytes);
	if (pStorage->path_total_mbs == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail, " \
			"errno: %d, error info: %s", \
			__LINE__, alloc_bytes, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOMEM;
	}

	pStorage->path_free_mbs = (int64_t *)malloc(alloc_bytes);
	if (pStorage->path_free_mbs == NULL)
	{
		free(pStorage->path_total_mbs);
		pStorage->path_total_mbs = NULL;

		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail, " \
			"errno: %d, error info: %s", \
			__LINE__, alloc_bytes, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOMEM;
	}

	memset(pStorage->path_total_mbs, 0, alloc_bytes);
	memset(pStorage->path_free_mbs, 0, alloc_bytes);

	return 0;
}

static int tracker_realloc_storage_path_mbs(FDFSStorageDetail *pStorage, \
		const int old_store_path_count, const int new_store_path_count)
{
	int alloc_bytes;
	int copy_bytes;
	int64_t *new_path_total_mbs;
	int64_t *new_path_free_mbs;

	if (new_store_path_count <= 0)
	{
		return EINVAL;
	}

	alloc_bytes = sizeof(int64_t) * new_store_path_count;

	new_path_total_mbs = (int64_t *)malloc(alloc_bytes);
	if (new_path_total_mbs == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail, " \
			"errno: %d, error info: %s", \
			__LINE__, alloc_bytes, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOMEM;
	}

	new_path_free_mbs = (int64_t *)malloc(alloc_bytes);
	if (new_path_free_mbs == NULL)
	{
		free(new_path_total_mbs);

		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail, " \
			"errno: %d, error info: %s", \
			__LINE__, alloc_bytes, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOMEM;
	}

	memset(new_path_total_mbs, 0, alloc_bytes);
	memset(new_path_free_mbs, 0, alloc_bytes);

	if (old_store_path_count == 0)
	{
		pStorage->path_total_mbs = new_path_total_mbs;
		pStorage->path_free_mbs = new_path_free_mbs;

		return 0;
	}

	copy_bytes = (old_store_path_count < new_store_path_count ? \
		old_store_path_count : new_store_path_count) * sizeof(int64_t);
	memcpy(new_path_total_mbs, pStorage->path_total_mbs, copy_bytes);
	memcpy(new_path_free_mbs, pStorage->path_free_mbs, copy_bytes);

	free(pStorage->path_total_mbs);
	free(pStorage->path_free_mbs);

	pStorage->path_total_mbs = new_path_total_mbs;
	pStorage->path_free_mbs = new_path_free_mbs;

	return 0;
}

static int tracker_realloc_group_path_mbs(FDFSGroupInfo *pGroup, \
		const int new_store_path_count)
{
	FDFSStorageDetail **ppStorage;
	FDFSStorageDetail **ppEnd;
	int result;

	ppEnd = pGroup->all_servers + pGroup->alloc_size;
	for (ppStorage=pGroup->all_servers; ppStorage<ppEnd; ppStorage++)
	{
		if ((result=tracker_realloc_storage_path_mbs(*ppStorage, \
			pGroup->store_path_count, new_store_path_count)) != 0)
		{
			return result;
		}
	}

	pGroup->store_path_count = new_store_path_count;

	return 0;
}

static int tracker_malloc_group_path_mbs(FDFSGroupInfo *pGroup)
{
	FDFSStorageDetail **ppStorage;
	FDFSStorageDetail **ppEnd;
	int result;

	ppEnd = pGroup->all_servers + pGroup->alloc_size;
	for (ppStorage=pGroup->all_servers; ppStorage<ppEnd; ppStorage++)
	{
		if ((result=tracker_malloc_storage_path_mbs(*ppStorage, \
				pGroup->store_path_count)) != 0)
		{
			return result;
		}
	}

	return 0;
}

static int tracker_malloc_all_group_path_mbs(FDFSGroups *pGroups)
{
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppEnd;
	int result;

	ppEnd = pGroups->groups + pGroups->alloc_size;
	for (ppGroup=pGroups->groups; ppGroup<ppEnd; ppGroup++)
	{
		if ((*ppGroup)->store_path_count == 0)
		{
			continue;
		}

		if ((result=tracker_malloc_group_path_mbs(*ppGroup)) != 0)
		{
			return result;
		}
	}

	return 0;
}

static int tracker_load_groups_old(FDFSGroups *pGroups, const char *data_path)
{
#define STORAGE_DATA_GROUP_FIELDS	4

	FILE *fp;
	char szLine[256];
	char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
	char *fields[STORAGE_DATA_GROUP_FIELDS];
	int result;
	int col_count;
	TrackerClientInfo clientInfo;
	bool bInserted;

	if ((fp=fopen(STORAGE_GROUPS_LIST_FILENAME_OLD, "r")) == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"open file \"%s/%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, data_path, STORAGE_GROUPS_LIST_FILENAME_OLD, \
			errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	result = 0;
	while (fgets(szLine, sizeof(szLine), fp) != NULL)
	{
		if (*szLine == '\0')
		{
			continue;
		}

		col_count = splitEx(szLine, STORAGE_DATA_FIELD_SEPERATOR, \
			fields, STORAGE_DATA_GROUP_FIELDS);
		if (col_count != STORAGE_DATA_GROUP_FIELDS && \
			col_count != STORAGE_DATA_GROUP_FIELDS - 2)
		{
			logError("file: "__FILE__", line: %d, " \
				"the format of the file \"%s/%s\" is invalid", \
				__LINE__, data_path, \
				STORAGE_GROUPS_LIST_FILENAME_OLD);
			result = errno != 0 ? errno : EINVAL;
			break;
		}
	
		memset(&clientInfo, 0, sizeof(TrackerClientInfo));
		snprintf(group_name, sizeof(group_name),\
				"%s", fc_trim(fields[0]));
		if ((result=tracker_mem_add_group_ex(pGroups, &clientInfo, \
				group_name, false, &bInserted)) != 0)
		{
			break;
		}

		if (!bInserted)
		{
			logError("file: "__FILE__", line: %d, " \
				"in the file \"%s/%s\", " \
				"group \"%s\" is duplicate", \
				__LINE__, data_path, \
				STORAGE_GROUPS_LIST_FILENAME_OLD, \
				group_name);
			result = errno != 0 ? errno : EEXIST;
			break;
		}

		clientInfo.pGroup->storage_port = atoi(fc_trim(fields[1]));
		if (col_count == STORAGE_DATA_GROUP_FIELDS - 2)
		{  //version < V1.12
			clientInfo.pGroup->store_path_count = 0;
			clientInfo.pGroup->subdir_count_per_path = 0;
		}
		else
		{
			clientInfo.pGroup->store_path_count = \
				atoi(fc_trim(fields[2]));
			clientInfo.pGroup->subdir_count_per_path = \
				atoi(fc_trim(fields[3]));
		}
	}

	fclose(fp);
	return result;
}

static int tracker_mem_get_storage_id(const char *group_name, \
		const char *pIpAddr, char *storage_id)
{
	FDFSStorageIdInfo *pStorageIdInfo;
	pStorageIdInfo = fdfs_get_storage_id_by_ip(group_name, pIpAddr);
	if (pStorageIdInfo == NULL)
	{
		return ENOENT;
	}

	strcpy(storage_id, pStorageIdInfo->id);
	return 0;
}

static int tracker_load_groups_new(FDFSGroups *pGroups, const char *data_path, 
		FDFSStorageSync **ppTrunkServers, int *nTrunkServerCount)
{
	IniContext iniContext;
	FDFSGroupInfo *pGroup;
	char *group_name;
	char *pValue;
	int nStorageSyncSize;
	int group_count;
	int result;
	int i;
	char section_name[64];
	TrackerClientInfo clientInfo;
	bool bInserted;

	*nTrunkServerCount = 0;
	*ppTrunkServers = NULL;
	if (!fileExists(STORAGE_GROUPS_LIST_FILENAME_NEW) && \
	     fileExists(STORAGE_GROUPS_LIST_FILENAME_OLD))
	{
		logDebug("file: "__FILE__", line: %d, " \
			"convert old data file %s to new data file %s", \
			__LINE__, STORAGE_GROUPS_LIST_FILENAME_OLD, \
			STORAGE_GROUPS_LIST_FILENAME_NEW);
		if ((result=tracker_load_groups_old(pGroups, data_path)) == 0)
		{
			if ((result=tracker_save_groups()) == 0)
			{
				unlink(STORAGE_GROUPS_LIST_FILENAME_OLD);
			}
		}

		return result;
	}

	if ((result=iniLoadFromFile(STORAGE_GROUPS_LIST_FILENAME_NEW, \
			&iniContext)) != 0)
	{
		return result;
	}

	group_count = iniGetIntValue(GROUP_SECTION_NAME_GLOBAL, \
				GROUP_ITEM_GROUP_COUNT, \
                		&iniContext, -1);
	if (group_count < 0)
	{
		iniFreeContext(&iniContext);
		logError("file: "__FILE__", line: %d, " \
			"in the file \"%s/%s\", " \
			"item \"%s\" is not found", \
			__LINE__, data_path, \
			STORAGE_GROUPS_LIST_FILENAME_NEW, \
			GROUP_ITEM_GROUP_COUNT);
		return ENOENT;
	}

	nStorageSyncSize = 0;
	for (i=1; i<=group_count; i++)
	{
		sprintf(section_name, "%s"GROUP_SECTION_NO_FORMAT, \
			GROUP_SECTION_NAME_PREFIX, i);

		group_name = iniGetStrValue(section_name, \
				GROUP_ITEM_GROUP_NAME, &iniContext);
		if (group_name == NULL)
		{
			logError("file: "__FILE__", line: %d, " \
				"in the file \"%s/%s\", " \
				"item \"%s\" is not found", \
				__LINE__, data_path, \
				STORAGE_GROUPS_LIST_FILENAME_NEW, \
				GROUP_ITEM_GROUP_NAME);
			result = ENOENT;
			break;
		}

		memset(&clientInfo, 0, sizeof(TrackerClientInfo));
		if ((result=tracker_mem_add_group_ex(pGroups, &clientInfo, \
				group_name, false, &bInserted)) != 0)
		{
			break;
		}

		if (!bInserted)
		{
			logError("file: "__FILE__", line: %d, " \
				"in the file \"%s/%s\", " \
				"group \"%s\" is duplicate", \
				__LINE__, data_path, \
				STORAGE_GROUPS_LIST_FILENAME_NEW, \
				group_name);
			result = errno != 0 ? errno : EEXIST;
			break;
		}

		pGroup = clientInfo.pGroup;
		pGroup->storage_port = iniGetIntValue(section_name, \
			GROUP_ITEM_STORAGE_PORT, &iniContext, 0);
		pGroup->storage_http_port = iniGetIntValue(section_name, \
			GROUP_ITEM_STORAGE_HTTP_PORT, &iniContext, 0);
		pGroup->store_path_count = iniGetIntValue(section_name, \
			GROUP_ITEM_STORE_PATH_COUNT, &iniContext, 0);
		pGroup->subdir_count_per_path = iniGetIntValue(section_name, \
			GROUP_ITEM_SUBDIR_COUNT_PER_PATH, &iniContext, 0);
		pGroup->current_trunk_file_id = iniGetIntValue(section_name, \
			GROUP_ITEM_CURRENT_TRUNK_FILE_ID, &iniContext, 0);
		pValue = iniGetStrValue(section_name, \
			GROUP_ITEM_LAST_TRUNK_SERVER, &iniContext);
		if (pValue != NULL)
		{
			snprintf(pGroup->last_trunk_server_id, 
				sizeof(pGroup->last_trunk_server_id), \
				"%s", pValue);
			if (g_use_storage_id && (*pValue != '\0' && \
				!fdfs_is_server_id_valid(pValue)))
			{
				if (tracker_mem_get_storage_id( \
					pGroup->group_name, pValue, \
					pGroup->last_trunk_server_id) != 0)
				{
					logWarning("file: "__FILE__", line: %d,"\
						"server id of group name: %s " \
						"and last trunk ip address: %s"\
						" NOT exist", __LINE__, \
						pGroup->group_name, pValue);
					*pGroup->last_trunk_server_id = '\0';
				}
			}
		}

		pValue = iniGetStrValue(section_name, \
			GROUP_ITEM_TRUNK_SERVER, &iniContext);
		if (pValue != NULL && *pValue != '\0')
		{
		if (nStorageSyncSize <= *nTrunkServerCount)
		{
			nStorageSyncSize += 8;
			*ppTrunkServers = (FDFSStorageSync *)realloc( \
				*ppTrunkServers, \
				sizeof(FDFSStorageSync) * nStorageSyncSize);
			if (*ppTrunkServers == NULL)
			{
				result = errno != 0 ? errno : ENOMEM;
				logError("file: "__FILE__", line: %d, " \
					"realloc %d bytes fail", __LINE__, \
					(int)sizeof(FDFSStorageSync) * \
					nStorageSyncSize);
				break;
			}
		}

		strcpy((*ppTrunkServers)[*nTrunkServerCount].group_name, \
			clientInfo.pGroup->group_name);
		snprintf((*ppTrunkServers)[*nTrunkServerCount].id, \
			FDFS_STORAGE_ID_MAX_SIZE, "%s", pValue);
		if (g_use_storage_id && !fdfs_is_server_id_valid(pValue))
		{
			if ((result=tracker_mem_get_storage_id( \
				clientInfo.pGroup->group_name, pValue, \
				(*ppTrunkServers)[*nTrunkServerCount].id)) != 0)
			{
				logError("file: "__FILE__", line: %d, " \
					"server id of group name: %s and " \
					"trunk server ip address: %s NOT " \
					"exist", __LINE__, \
					clientInfo.pGroup->group_name, pValue);
				break;
			}
		}

		(*nTrunkServerCount)++;
		}
	}

	iniFreeContext(&iniContext);

	return result;
}

static int tracker_locate_group_trunk_servers(FDFSGroups *pGroups, \
		FDFSStorageSync *pTrunkServers, \
		const int nTrunkServerCount, const bool bLoadFromFile)
{
	FDFSGroupInfo *pGroup;
	FDFSStorageDetail *pStorage;
	FDFSStorageSync *pServer;
	FDFSStorageSync *pTrunkEnd;

	if (nTrunkServerCount == 0)
	{
		return 0;
	}

	pTrunkEnd = pTrunkServers + nTrunkServerCount;
	for (pServer=pTrunkServers; pServer<pTrunkEnd; pServer++)
	{
		pGroup = tracker_mem_get_group_ex(pGroups, \
				pServer->group_name);
		if (pGroup == NULL)
		{
			continue;
		}

		pStorage = tracker_mem_get_storage(pGroup, pServer->id);
		if (pStorage == NULL)
		{
			char buff[MAX_PATH_SIZE+64];
			if (bLoadFromFile)
			{
				snprintf(buff, sizeof(buff), \
					"in the file \"%s/data/%s\", ", \
					SF_G_BASE_PATH_STR, \
					STORAGE_GROUPS_LIST_FILENAME_NEW);
			}
			else
			{
				*buff = '\0';
			}

			logError("file: "__FILE__", line: %d, " \
				"%sgroup_name: %s, trunk server \"%s\" " \
				"does not exist", __LINE__, buff, \
				pServer->group_name, pServer->id);

			return ENOENT;
		}

		pGroup->pTrunkServer = pStorage;
		pGroup->trunk_chg_count++;
		if (*(pGroup->last_trunk_server_id) == '\0')
		{
			strcpy(pGroup->last_trunk_server_id, pStorage->id);
		}
	}

	return 0;
}

static int tracker_locate_storage_sync_server(FDFSGroups *pGroups, \
		FDFSStorageSync *pStorageSyncs, \
		const int nStorageSyncCount, const bool bLoadFromFile)
{
	FDFSGroupInfo *pGroup;
	FDFSStorageDetail *pStorage;
	FDFSStorageSync *pSyncServer;
	FDFSStorageSync *pSyncEnd;

	if (nStorageSyncCount == 0)
	{
		return 0;
	}

	pSyncEnd = pStorageSyncs + nStorageSyncCount;
	for (pSyncServer=pStorageSyncs; pSyncServer<pSyncEnd; pSyncServer++)
	{
		pGroup = tracker_mem_get_group_ex(pGroups, \
				pSyncServer->group_name);
		if (pGroup == NULL)
		{
			continue;
		}

		pStorage=tracker_mem_get_storage(pGroup, pSyncServer->id);
		if (pStorage == NULL)
		{
			continue;
		}

		pStorage->psync_src_server = tracker_mem_get_storage(pGroup, \
			pSyncServer->sync_src_id);
		if (pStorage->psync_src_server == NULL)
		{
			char buff[MAX_PATH_SIZE+64];
			if (bLoadFromFile)
			{
				snprintf(buff, sizeof(buff), \
					"in the file \"%s/data/%s\", ", \
					SF_G_BASE_PATH_STR, \
					STORAGE_SERVERS_LIST_FILENAME_NEW);
			}
			else
			{
				*buff = '\0';
			}

			logError("file: "__FILE__", line: %d, " \
				"%sgroup_name: %s, storage server \"%s\" " \
				"does not exist", __LINE__, buff, \
				pSyncServer->group_name, \
				pSyncServer->sync_src_id);

			return ENOENT;
		}
	}

	return 0;
}

static int tracker_load_storages_old(FDFSGroups *pGroups, const char *data_path)
{
#define STORAGE_DATA_SERVER_FIELDS	22

	FILE *fp;
	char szLine[256];
	char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
	char *fields[STORAGE_DATA_SERVER_FIELDS];
	char ip_addr[IP_ADDRESS_SIZE];
	char *psync_src_id;
	FDFSStorageDetail *pStorage;
	FDFSStorageSync *pStorageSyncs;
	int nStorageSyncSize;
	int nStorageSyncCount;
	int cols;
	int result;
	TrackerClientInfo clientInfo;
	bool bInserted;

	if ((fp=fopen(STORAGE_SERVERS_LIST_FILENAME_OLD, "r")) == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"open file \"%s/%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, data_path, STORAGE_SERVERS_LIST_FILENAME_OLD, \
			errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	nStorageSyncSize = 0;
	nStorageSyncCount = 0;
	pStorageSyncs = NULL;
	result = 0;
	while (fgets(szLine, sizeof(szLine), fp) != NULL)
	{
		if (*szLine == '\0')
		{
			continue;
		}

		cols = splitEx(szLine, STORAGE_DATA_FIELD_SEPERATOR, \
				fields, STORAGE_DATA_SERVER_FIELDS);
		if (cols != STORAGE_DATA_SERVER_FIELDS && \
		    cols != STORAGE_DATA_SERVER_FIELDS - 2 && \
		    cols != STORAGE_DATA_SERVER_FIELDS - 4 && \
		    cols != STORAGE_DATA_SERVER_FIELDS - 5)
		{
			logError("file: "__FILE__", line: %d, " \
				"the format of the file \"%s/%s\" is invalid" \
				", colums: %d != expect colums: " \
				"%d or %d or %d or %d", \
				__LINE__, data_path, \
				STORAGE_SERVERS_LIST_FILENAME_OLD, \
				cols, STORAGE_DATA_SERVER_FIELDS, \
				STORAGE_DATA_SERVER_FIELDS - 2, \
				STORAGE_DATA_SERVER_FIELDS - 4, \
				STORAGE_DATA_SERVER_FIELDS - 5);
			result = EINVAL;
			break;
		}
	
		memset(&clientInfo, 0, sizeof(TrackerClientInfo));
		snprintf(group_name, sizeof(group_name), "%s", fc_trim(fields[0]));
		snprintf(ip_addr, sizeof(ip_addr), "%s", fc_trim(fields[1]));
		if ((clientInfo.pGroup=tracker_mem_get_group_ex(pGroups, \
						group_name)) == NULL)
		{
			logError("file: "__FILE__", line: %d, " \
				"in the file \"%s/%s\", " \
				"group \"%s\" is not found", \
				__LINE__, data_path, \
				STORAGE_SERVERS_LIST_FILENAME_OLD, \
				group_name);
			result = errno != 0 ? errno : ENOENT;
			break;
		}

		if ((result=tracker_mem_add_storage(&clientInfo, NULL, ip_addr,
				false, false, &bInserted)) != 0)
		{
			break;
		}

		if (!bInserted)
		{
			logError("file: "__FILE__", line: %d, " \
				"in the file \"%s/%s\", " \
				"storage \"%s\" is duplicate", \
				__LINE__, data_path, \
				STORAGE_SERVERS_LIST_FILENAME_OLD, ip_addr);
			result = errno != 0 ? errno : EEXIST;
			break;
		}
	
		pStorage = clientInfo.pStorage;
		pStorage->status = atoi(trim_left(fields[2]));
		if (!((pStorage->status == \
				FDFS_STORAGE_STATUS_WAIT_SYNC) || \
			(pStorage->status == \
				FDFS_STORAGE_STATUS_SYNCING) || \
			(pStorage->status == \
				FDFS_STORAGE_STATUS_INIT)))
		{
			pStorage->status = \
				FDFS_STORAGE_STATUS_OFFLINE;
		}

		psync_src_id = fc_trim(fields[3]);
		pStorage->sync_until_timestamp = atoi( \
					trim_left(fields[4]));
		pStorage->stat.total_upload_count = strtoll( \
					trim_left(fields[5]), NULL, 10);
		pStorage->stat.success_upload_count = strtoll( \
					trim_left(fields[6]), NULL, 10);
		pStorage->stat.total_set_meta_count = strtoll( \
					trim_left(fields[7]), NULL, 10);
		pStorage->stat.success_set_meta_count = strtoll( \
					trim_left(fields[8]), NULL, 10);
		pStorage->stat.total_delete_count = strtoll( \
					trim_left(fields[9]), NULL, 10);
		pStorage->stat.success_delete_count = strtoll( \
					trim_left(fields[10]), NULL, 10);
		pStorage->stat.total_download_count = strtoll( \
					trim_left(fields[11]), NULL, 10);
		pStorage->stat.success_download_count = strtoll( \
					trim_left(fields[12]), NULL, 10);
		pStorage->stat.total_get_meta_count = strtoll( \
					trim_left(fields[13]), NULL, 10);
		pStorage->stat.success_get_meta_count = strtoll( \
					trim_left(fields[14]), NULL, 10);
		pStorage->stat.last_source_update = atoi( \
					trim_left(fields[15]));
		pStorage->stat.last_sync_update = atoi( \
					trim_left(fields[16]));
		if (cols > STORAGE_DATA_SERVER_FIELDS - 5)
		{
			pStorage->changelog_offset = strtoll( \
					trim_left(fields[17]), NULL, 10);
			if (pStorage->changelog_offset < 0)
			{
				pStorage->changelog_offset = 0;
			}
			if (pStorage->changelog_offset > \
				g_changelog_fsize)
			{
				pStorage->changelog_offset = \
					g_changelog_fsize;
			}

			if (cols > STORAGE_DATA_SERVER_FIELDS - 4)
			{
				pStorage->storage_port = \
					atoi(trim_left(fields[18]));
				pStorage->storage_http_port = \
					atoi(trim_left(fields[19]));
				if (cols > STORAGE_DATA_SERVER_FIELDS - 2)
				{
					pStorage->join_time = \
					(time_t)atoi(trim_left(fields[20]));

					snprintf(pStorage->version, \
					sizeof(pStorage->version), 
					 "%s", fc_trim(fields[21]));
				}
			}
		}

		if (*psync_src_id == '\0')
		{
			continue;
		}

		if (nStorageSyncSize <= nStorageSyncCount)
		{
			nStorageSyncSize += 8;
			pStorageSyncs = (FDFSStorageSync *)realloc( \
				pStorageSyncs, \
				sizeof(FDFSStorageSync) * nStorageSyncSize);
			if (pStorageSyncs == NULL)
			{
				result = errno != 0 ? errno : ENOMEM;
				logError("file: "__FILE__", line: %d, " \
					"realloc %d bytes fail", __LINE__, \
					(int)sizeof(FDFSStorageSync) * \
					nStorageSyncSize);
				break;
			}
		}

		strcpy(pStorageSyncs[nStorageSyncCount].group_name, \
			clientInfo.pGroup->group_name);
		strcpy(pStorageSyncs[nStorageSyncCount].id, pStorage->id);
		snprintf(pStorageSyncs[nStorageSyncCount].sync_src_id, \
			FDFS_STORAGE_ID_MAX_SIZE, "%s", psync_src_id);

		nStorageSyncCount++;

	}

	fclose(fp);

	if (pStorageSyncs == NULL)
	{
		return result;
	}

	if (result != 0)
	{
		free(pStorageSyncs);
		return result;
	}

	result = tracker_locate_storage_sync_server(pGroups, pStorageSyncs, \
			nStorageSyncCount, true);
	free(pStorageSyncs);
	return result;
}

static int tracker_load_storages_new(FDFSGroups *pGroups, const char *data_path)
{
	IniContext iniContext;
	char *group_name;
	char *storage_id;
	char *ip_addr;
	char *psync_src_id;
	char *pValue;
	FDFSStorageDetail *pStorage;
	FDFSStorageStat *pStat;
	FDFSStorageSync *pStorageSyncs;
	int nStorageSyncSize;
	int nStorageSyncCount;
	int storage_count;
	int i;
	int result;
	char section_name[64];
	TrackerClientInfo clientInfo;

	if (!fileExists(STORAGE_SERVERS_LIST_FILENAME_NEW) && \
	     fileExists(STORAGE_SERVERS_LIST_FILENAME_OLD))
	{
		logDebug("file: "__FILE__", line: %d, " \
			"convert old data file %s to new data file %s", \
			__LINE__, STORAGE_SERVERS_LIST_FILENAME_OLD, \
			STORAGE_SERVERS_LIST_FILENAME_NEW);
		if ((result=tracker_load_storages_old(pGroups, data_path)) == 0)
		{
			if ((result=tracker_save_storages()) == 0)
			{
				unlink(STORAGE_SERVERS_LIST_FILENAME_OLD);
			}
		}

		return result;
	}

	if ((result=iniLoadFromFile(STORAGE_SERVERS_LIST_FILENAME_NEW, \
			&iniContext)) != 0)
	{
		return result;
	}

	storage_count = iniGetIntValue(STORAGE_SECTION_NAME_GLOBAL, \
				STORAGE_ITEM_STORAGE_COUNT, \
                		&iniContext, -1);
	if (storage_count < 0)
	{
		iniFreeContext(&iniContext);
		logError("file: "__FILE__", line: %d, " \
			"in the file \"%s/%s\", " \
			"item \"%s\" is not found", \
			__LINE__, data_path, \
			STORAGE_SERVERS_LIST_FILENAME_NEW, \
			STORAGE_ITEM_STORAGE_COUNT);
		return ENOENT;
	}

	nStorageSyncSize = 0;
	nStorageSyncCount = 0;
	pStorageSyncs = NULL;
	result = 0;
	for (i=1; i<=storage_count; i++)
	{
		sprintf(section_name, "%s"STORAGE_SECTION_NO_FORMAT, \
			STORAGE_SECTION_NAME_PREFIX, i);

		group_name = iniGetStrValue(section_name, \
				STORAGE_ITEM_GROUP_NAME, &iniContext);
		if (group_name == NULL)
		{
			logError("file: "__FILE__", line: %d, " \
				"in the file \"%s/%s\", " \
				"item \"%s\" is not found", \
				__LINE__, data_path, \
				STORAGE_SERVERS_LIST_FILENAME_NEW, \
				STORAGE_ITEM_GROUP_NAME);
			result = ENOENT;
			break;
		}

		storage_id = iniGetStrValue(section_name, \
				STORAGE_ITEM_SERVER_ID, &iniContext);

		ip_addr = iniGetStrValue(section_name, \
				STORAGE_ITEM_IP_ADDR, &iniContext);
	
        if ((result=tracker_mem_add_storage_from_file(pGroups,
                        data_path, &clientInfo, group_name,
                        storage_id, ip_addr)) != 0)
        {
            break;
        }

		pStorage = clientInfo.pStorage;
		pStat = &(pStorage->stat);
		pStorage->status = iniGetIntValue(section_name, \
				STORAGE_ITEM_STATUS, &iniContext, 0);

		pValue = iniGetStrValue(section_name, \
				STORAGE_ITEM_DOMAIN_NAME, &iniContext);
		if (pValue != NULL)
		{
			snprintf(pStorage->domain_name, \
				sizeof(pStorage->domain_name), "%s", pValue);
		}

		pValue = iniGetStrValue(section_name, \
				STORAGE_ITEM_VERSION, &iniContext);
		if (pValue != NULL)
		{
			snprintf(pStorage->version, \
				sizeof(pStorage->version), "%s", pValue);
		}

		if (!((pStorage->status == \
				FDFS_STORAGE_STATUS_WAIT_SYNC) || \
			(pStorage->status == \
				FDFS_STORAGE_STATUS_SYNCING) || \
			(pStorage->status == \
				FDFS_STORAGE_STATUS_INIT)))
		{
			pStorage->status = FDFS_STORAGE_STATUS_OFFLINE;
		}

		psync_src_id = iniGetStrValue(section_name, \
				STORAGE_ITEM_SYNC_SRC_SERVER, &iniContext);
		if (psync_src_id == NULL)
		{
			psync_src_id = "";
		}

		pStorage->sync_until_timestamp = iniGetIntValue(section_name, \
			STORAGE_ITEM_SYNC_UNTIL_TIMESTAMP, &iniContext, 0);
		pStorage->join_time = iniGetIntValue(section_name, \
			STORAGE_ITEM_JOIN_TIME, &iniContext, 0);
		pStorage->total_mb = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_MB, &iniContext, 0);
		pStorage->free_mb = iniGetInt64Value(section_name, \
			STORAGE_ITEM_FREE_MB, &iniContext, 0);
		pStorage->store_path_count = iniGetIntValue(section_name, \
			STORAGE_ITEM_STORE_PATH_COUNT, &iniContext, 0);
		pStorage->subdir_count_per_path = iniGetIntValue(section_name, \
			STORAGE_ITEM_SUBDIR_COUNT_PER_PATH, &iniContext, 0);
		pStorage->upload_priority = iniGetIntValue(section_name, \
			STORAGE_ITEM_UPLOAD_PRIORITY, &iniContext, 0);
		pStorage->storage_port = iniGetIntValue(section_name, \
			STORAGE_ITEM_STORAGE_PORT, &iniContext, 0);
		pStorage->storage_http_port = iniGetIntValue(section_name, \
			STORAGE_ITEM_STORAGE_HTTP_PORT, &iniContext, 0);
		pStat->total_upload_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_UPLOAD_COUNT, &iniContext, 0);
		pStat->success_upload_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_UPLOAD_COUNT, &iniContext, 0);
		pStat->total_append_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_APPEND_COUNT, &iniContext, 0);
		pStat->success_append_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_APPEND_COUNT, &iniContext, 0);
		pStat->total_set_meta_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_SET_META_COUNT, &iniContext, 0);
		pStat->success_set_meta_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_SET_META_COUNT, &iniContext, 0);
		pStat->total_delete_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_DELETE_COUNT, &iniContext, 0);
		pStat->success_delete_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_DELETE_COUNT, &iniContext, 0);
		pStat->total_download_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_DOWNLOAD_COUNT, &iniContext, 0);
		pStat->success_download_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_DOWNLOAD_COUNT, &iniContext, 0);
		pStat->total_get_meta_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_GET_META_COUNT, &iniContext, 0);
		pStat->success_get_meta_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_GET_META_COUNT, &iniContext, 0);
		pStat->total_create_link_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_CREATE_LINK_COUNT, &iniContext, 0);
		pStat->success_create_link_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_CREATE_LINK_COUNT, &iniContext, 0);
		pStat->total_delete_link_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_DELETE_LINK_COUNT, &iniContext, 0);
		pStat->success_delete_link_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_DELETE_LINK_COUNT, &iniContext, 0);
		pStat->total_upload_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_UPLOAD_BYTES, &iniContext, 0);
		pStat->success_upload_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_UPLOAD_BYTES, &iniContext, 0);
		pStat->total_append_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_APPEND_BYTES, &iniContext, 0);
		pStat->success_append_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_APPEND_BYTES, &iniContext, 0);
		pStat->total_download_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_DOWNLOAD_BYTES, &iniContext, 0);
		pStat->success_download_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_DOWNLOAD_BYTES, &iniContext, 0);
		pStat->total_sync_in_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_SYNC_IN_BYTES, &iniContext, 0);
		pStat->success_sync_in_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_SYNC_IN_BYTES, &iniContext, 0);
		pStat->total_sync_out_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_SYNC_OUT_BYTES, &iniContext, 0);
		pStat->success_sync_out_bytes = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_SYNC_OUT_BYTES, &iniContext, 0);
		pStat->total_file_open_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_FILE_OPEN_COUNT, &iniContext, 0);
		pStat->success_file_open_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_FILE_OPEN_COUNT, &iniContext, 0);
		pStat->total_file_read_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_FILE_READ_COUNT, &iniContext, 0);
		pStat->success_file_read_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_FILE_READ_COUNT, &iniContext, 0);
		pStat->total_file_write_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_TOTAL_FILE_WRITE_COUNT, &iniContext, 0);
		pStat->success_file_write_count = iniGetInt64Value(section_name, \
			STORAGE_ITEM_SUCCESS_FILE_WRITE_COUNT, &iniContext, 0);
		pStat->last_source_update = iniGetIntValue(section_name, \
			STORAGE_ITEM_LAST_SOURCE_UPDATE, &iniContext, 0);
		pStat->last_sync_update = iniGetIntValue(section_name, \
			STORAGE_ITEM_LAST_SYNC_UPDATE, &iniContext, 0);
		pStat->last_synced_timestamp = iniGetIntValue(section_name, \
			STORAGE_ITEM_LAST_SYNCED_TIMESTAMP, &iniContext, 0);
		pStat->last_heart_beat_time = iniGetIntValue(section_name, \
			STORAGE_ITEM_LAST_HEART_BEAT_TIME, &iniContext, 0);
		pStorage->changelog_offset = iniGetInt64Value(section_name, \
			STORAGE_ITEM_CHANGELOG_OFFSET, &iniContext, 0);

		if (*psync_src_id == '\0')
		{
			continue;
		}

		if (nStorageSyncSize <= nStorageSyncCount)
		{
			if (nStorageSyncSize == 0)
			{
				nStorageSyncSize = 8;
			}
			else
			{
				nStorageSyncSize *= 2;
			}
			pStorageSyncs = (FDFSStorageSync *)realloc( \
				pStorageSyncs, \
				sizeof(FDFSStorageSync) * nStorageSyncSize);
			if (pStorageSyncs == NULL)
			{
				result = errno != 0 ? errno : ENOMEM;
				logError("file: "__FILE__", line: %d, " \
					"realloc %d bytes fail", __LINE__, \
					(int)sizeof(FDFSStorageSync) * \
					nStorageSyncSize);
				break;
			}
		}

		strcpy(pStorageSyncs[nStorageSyncCount].group_name, \
			clientInfo.pGroup->group_name);
		strcpy(pStorageSyncs[nStorageSyncCount].id, pStorage->id);
		snprintf(pStorageSyncs[nStorageSyncCount].sync_src_id, \
			FDFS_STORAGE_ID_MAX_SIZE, "%s", psync_src_id);
		if (g_use_storage_id && !fdfs_is_server_id_valid( \
						psync_src_id))
		{
			if ((result=tracker_mem_get_storage_id( \
				clientInfo.pGroup->group_name, psync_src_id, \
				pStorageSyncs[nStorageSyncCount].sync_src_id))\
				 != 0)
			{
				logError("file: "__FILE__", line: %d, " \
					"server id of group name: %s and " \
					"src storage ip address: %s NOT " \
					"exist", __LINE__, \
					clientInfo.pGroup->group_name, \
					psync_src_id);
				break;
			}
		}

		nStorageSyncCount++;
	}

	iniFreeContext(&iniContext);

	if (pStorageSyncs == NULL)
	{
		return result;
	}

	if (result != 0)
	{
		free(pStorageSyncs);
		return result;
	}

	result = tracker_locate_storage_sync_server(pGroups, pStorageSyncs, \
			nStorageSyncCount, true);
	free(pStorageSyncs);
	return result;
}

static int tracker_load_sync_timestamps(FDFSGroups *pGroups, const char *data_path)
{
#define STORAGE_SYNC_TIME_MAX_FIELDS	2 + FDFS_MAX_SERVERS_EACH_GROUP

	FILE *fp;
	char szLine[512];
	char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
	char previous_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
	char src_storage_id[FDFS_STORAGE_ID_MAX_SIZE];
	char *fields[STORAGE_SYNC_TIME_MAX_FIELDS];
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppEnd;
	FDFSGroupInfo *pGroup;
	int cols;
	int src_index;
	int dest_index;
	int curr_synced_timestamp;
	int result;

	if (!fileExists(STORAGE_SYNC_TIMESTAMP_FILENAME))
	{
		return 0;
	}

	if ((fp=fopen(STORAGE_SYNC_TIMESTAMP_FILENAME, "r")) == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"open file \"%s/%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, data_path, STORAGE_SYNC_TIMESTAMP_FILENAME, \
			errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	pGroup = NULL;
	src_index = 0;
	*previous_group_name = '\0';
	result = 0;
	while (fgets(szLine, sizeof(szLine), fp) != NULL)
	{
		if (*szLine == '\0' || *szLine == '\n')
		{
			continue;
		}

		if ((cols=splitEx(szLine, STORAGE_DATA_FIELD_SEPERATOR, \
			fields, STORAGE_SYNC_TIME_MAX_FIELDS)) <= 2)
		{
			logError("file: "__FILE__", line: %d, " \
				"the format of the file \"%s/%s\" is invalid" \
				", colums: %d <= 2", \
				__LINE__, data_path, \
				STORAGE_SYNC_TIMESTAMP_FILENAME, cols);
			result = errno != 0 ? errno : EINVAL;
			break;
		}
	
		snprintf(group_name, sizeof(group_name), \
				"%s", fc_trim(fields[0]));
		snprintf(src_storage_id, sizeof(src_storage_id), \
				"%s", fc_trim(fields[1]));
		if (strcmp(group_name, previous_group_name) != 0 || \
			pGroup == NULL)
		{
			if ((pGroup=tracker_mem_get_group_ex(pGroups, \
						group_name)) == NULL)
			{
				logError("file: "__FILE__", line: %d, " \
					"in the file \"%s/%s\", " \
					"group \"%s\" is not found", \
					__LINE__, data_path, \
					STORAGE_SYNC_TIMESTAMP_FILENAME, \
					group_name);
				result = errno != 0 ? errno : ENOENT;
				break;
			}

			strcpy(previous_group_name, group_name);
			src_index = 0;
		}
		
		if (src_index >= pGroup->count)
		{
			logError("file: "__FILE__", line: %d, " \
				"the format of the file \"%s/%s\" is invalid" \
				", group: %s, row count:%d > server count:%d",\
				__LINE__, data_path, \
				STORAGE_SYNC_TIMESTAMP_FILENAME, \
				group_name, src_index+1, pGroup->count);
			result = errno != 0 ? errno : EINVAL;
			break;
		}

		if (g_use_storage_id && !fdfs_is_server_id_valid( \
						src_storage_id))
		{
			if ((result=tracker_mem_get_storage_id( \
				group_name, src_storage_id, \
				src_storage_id)) != 0)
			{
				logError("file: "__FILE__", line: %d, " \
					"server id of group name: %s and " \
					"storage ip address: %s NOT " \
					"exist", __LINE__, \
					group_name, src_storage_id);
				break;
			}
		}

		if (strcmp(pGroup->all_servers[src_index]->id, \
			src_storage_id) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"in data file: \"%s/%s\", " \
				"group: %s, src server id: %s != %s",\
				__LINE__, data_path, \
				STORAGE_SYNC_TIMESTAMP_FILENAME, \
				group_name, src_storage_id, \
				pGroup->all_servers[src_index]->id);
			result = errno != 0 ? errno : EINVAL;
			break;
		}

		if (cols > pGroup->count + 2)
		{
			logError("file: "__FILE__", line: %d, " \
				"the format of the file \"%s/%s\" is invalid" \
				", group_name: %s, colums: %d > %d", \
				__LINE__, data_path, \
				STORAGE_SYNC_TIMESTAMP_FILENAME, \
				group_name, cols, pGroup->count + 2);
			result = errno != 0 ? errno : EINVAL;
			break;
		}

		for (dest_index=0; dest_index<cols-2; dest_index++)
		{
			pGroup->last_sync_timestamps[src_index][dest_index] = \
				atoi(trim_left(fields[2 + dest_index]));
		}

		src_index++;
	}

	fclose(fp);

	if (result != 0)
	{
		return result;
	}

	ppEnd = pGroups->groups + pGroups->count;
	for (ppGroup=pGroups->groups; ppGroup<ppEnd; ppGroup++)
	{
		if ((*ppGroup)->count <= 1)
		{
			continue;
		}

		for (dest_index=0; dest_index<(*ppGroup)->count; dest_index++)
		{
			if (pGroups->store_server == FDFS_STORE_SERVER_ROUND_ROBIN)
			{
				int min_synced_timestamp;

				min_synced_timestamp = 0;
				for (src_index=0; src_index<(*ppGroup)->count; \
					src_index++)
				{
					if (src_index == dest_index)
					{
						continue;
					}

					curr_synced_timestamp = \
						(*ppGroup)->last_sync_timestamps \
							[src_index][dest_index];
					if (curr_synced_timestamp == 0)
					{
						continue;
					}

					if (min_synced_timestamp == 0)
					{
						min_synced_timestamp = \
							curr_synced_timestamp;
					}
					else if (curr_synced_timestamp < \
						min_synced_timestamp)
					{
						min_synced_timestamp = \
							curr_synced_timestamp;
					}
				}

				(*ppGroup)->all_servers[dest_index]->stat. \
					last_synced_timestamp = min_synced_timestamp;
			}
			else
			{
				int max_synced_timestamp;

				max_synced_timestamp = 0;
				for (src_index=0; src_index<(*ppGroup)->count; \
					src_index++)
				{
					if (src_index == dest_index)
					{
						continue;
					}

					curr_synced_timestamp = \
						(*ppGroup)->last_sync_timestamps \
							[src_index][dest_index];
					if (curr_synced_timestamp > \
						max_synced_timestamp)
					{
						max_synced_timestamp = \
							curr_synced_timestamp;
					}
				}

				(*ppGroup)->all_servers[dest_index]->stat. \
					last_synced_timestamp = max_synced_timestamp;
			}
		}
	}

	return result;
}

static int tracker_load_data(FDFSGroups *pGroups)
{
	char data_path[MAX_PATH_SIZE];
	int result;
	FDFSStorageSync *pTrunkServers;
	int nTrunkServerCount;

	snprintf(data_path, sizeof(data_path), "%s/data", SF_G_BASE_PATH_STR);
	if (!fileExists(data_path))
	{
		if (mkdir(data_path, 0755) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"mkdir \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, data_path, errno, STRERROR(errno));
			return errno != 0 ? errno : ENOENT;
		}
		SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(data_path);
	}

	if (chdir(data_path) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"chdir \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, data_path, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	if (!fileExists(STORAGE_GROUPS_LIST_FILENAME_OLD) && \
	    !fileExists(STORAGE_GROUPS_LIST_FILENAME_NEW))
	{
		return 0;
	}

	if ((result=tracker_load_groups_new(pGroups, data_path, \
		&pTrunkServers, &nTrunkServerCount)) != 0)
	{
		return result;
	}

	if ((result=tracker_load_storages_new(pGroups, data_path)) != 0)
	{
		return result;
	}

	if ((result=tracker_malloc_all_group_path_mbs(pGroups)) != 0)
	{
		return result;
	}

	if ((result=tracker_load_sync_timestamps(pGroups, data_path)) != 0)
	{
		return result;
	}

	if (g_if_use_trunk_file)
	{
	if ((result=tracker_locate_group_trunk_servers(pGroups, \
		pTrunkServers, nTrunkServerCount, true)) != 0)
	{
		return result;
	}
	}

	if (pTrunkServers != NULL)
	{
		free(pTrunkServers);
	}

	return 0;
}

int tracker_save_groups()
{
	char tmpFilename[MAX_PATH_SIZE];
	char trueFilename[MAX_PATH_SIZE];
	char buff[FDFS_GROUP_NAME_MAX_LEN + 256];
	int fd;
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppEnd;
	int result;
	int len;

	tracker_mem_file_lock();

	snprintf(trueFilename, sizeof(trueFilename), "%s/data/%s", \
		SF_G_BASE_PATH_STR, STORAGE_GROUPS_LIST_FILENAME_NEW);
	snprintf(tmpFilename, sizeof(tmpFilename), "%s.tmp", trueFilename);
	if ((fd=open(tmpFilename, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
	{
		tracker_mem_file_unlock();

		logError("file: "__FILE__", line: %d, " \
			"open \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, tmpFilename, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	len = sprintf(buff, \
			"# global section\n" \
			"[%s]\n" \
			"\t%s=%d\n\n", \
			GROUP_SECTION_NAME_GLOBAL, \
			GROUP_ITEM_GROUP_COUNT, g_groups.count);
	if (fc_safe_write(fd, buff, len) != len)
	{
		logError("file: "__FILE__", line: %d, " \
			"write to file \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, tmpFilename, \
			errno, STRERROR(errno));
		result = errno != 0 ? errno : EIO;
	}
	else
	{
	result = 0;

	ppEnd = g_groups.sorted_groups + g_groups.count;
	for (ppGroup=g_groups.sorted_groups; ppGroup<ppEnd; ppGroup++)
	{
		len = sprintf(buff, \
				"# group: %s\n" \
				"[%s"GROUP_SECTION_NO_FORMAT"]\n" \
				"\t%s=%s\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%s\n" \
				"\t%s=%s\n\n", \
				(*ppGroup)->group_name, \
				GROUP_SECTION_NAME_PREFIX, \
				(int)(ppGroup - g_groups.sorted_groups) + 1, \
				GROUP_ITEM_GROUP_NAME, \
				(*ppGroup)->group_name, \
				GROUP_ITEM_STORAGE_PORT, \
				(*ppGroup)->storage_port, \
				GROUP_ITEM_STORAGE_HTTP_PORT, \
				(*ppGroup)->storage_http_port, \
				GROUP_ITEM_STORE_PATH_COUNT, \
				(*ppGroup)->store_path_count, \
				GROUP_ITEM_SUBDIR_COUNT_PER_PATH, \
				(*ppGroup)->subdir_count_per_path, \
				GROUP_ITEM_CURRENT_TRUNK_FILE_ID, \
				(*ppGroup)->current_trunk_file_id, \
				GROUP_ITEM_TRUNK_SERVER, \
				(*ppGroup)->pTrunkServer != NULL ? \
					(*ppGroup)->pTrunkServer->id : "",
				GROUP_ITEM_LAST_TRUNK_SERVER, \
				(*ppGroup)->last_trunk_server_id
			);

		if (fc_safe_write(fd, buff, len) != len)
		{
			logError("file: "__FILE__", line: %d, " \
				"write to file \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
			break;
		}
	}
	}

	if (result == 0)
	{
		if (fsync(fd) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"fsync file \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}
	}

	close(fd);

	if (result == 0)
	{
		if (rename(tmpFilename, trueFilename) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"rename file \"%s\" to \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, trueFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}

		SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(trueFilename);
	}

	if (result != 0)
	{
		unlink(tmpFilename);
	}

	tracker_mem_file_unlock();

	return result;
}

int tracker_save_storages()
{
	char tmpFilename[MAX_PATH_SIZE];
	char trueFilename[MAX_PATH_SIZE];
	char buff[4096];
	char id_buff[128];
	int fd;
	int len;
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;
	FDFSStorageDetail **ppStorage;
	FDFSStorageDetail **ppStorageEnd;
	FDFSStorageDetail *pStorage;
	int result;
	int count;

	tracker_mem_file_lock();

	snprintf(trueFilename, sizeof(trueFilename), "%s/data/%s", \
		SF_G_BASE_PATH_STR, STORAGE_SERVERS_LIST_FILENAME_NEW);
	snprintf(tmpFilename, sizeof(tmpFilename), "%s.tmp", trueFilename);
	if ((fd=open(tmpFilename, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
	{
		tracker_mem_file_unlock();

		logError("file: "__FILE__", line: %d, " \
			"open \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, tmpFilename, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	*id_buff = '\0';
	count = 0;
	result = 0;
	ppGroupEnd = g_groups.sorted_groups + g_groups.count;
	for (ppGroup=g_groups.sorted_groups; \
		(ppGroup < ppGroupEnd) && (result == 0); ppGroup++)
	{
		ppStorageEnd = (*ppGroup)->all_servers + (*ppGroup)->count;
		for (ppStorage=(*ppGroup)->all_servers; \
			ppStorage<ppStorageEnd; ppStorage++)
		{
			pStorage = *ppStorage;
			if (pStorage->status == FDFS_STORAGE_STATUS_DELETED
			 || pStorage->status == FDFS_STORAGE_STATUS_IP_CHANGED)
			{
				continue;
			}

			if (g_use_storage_id)
			{
				sprintf(id_buff, "\t%s=%s\n",
					STORAGE_ITEM_SERVER_ID, pStorage->id);
			}

			count++;
			len = sprintf(buff, \
				"# storage %s:%u\n" \
				"[%s"STORAGE_SECTION_NO_FORMAT"]\n" \
				"%s" \
				"\t%s=%s\n" \
				"\t%s=%s\n" \
				"\t%s=%d\n" \
				"\t%s=%s\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%s\n" \
				"\t%s=%s\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%"PRId64"\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%d\n" \
				"\t%s=%"PRId64"\n\n", \
                FDFS_CURRENT_IP_ADDR(pStorage), \
				pStorage->storage_port, \
				STORAGE_SECTION_NAME_PREFIX, count, id_buff, \
				STORAGE_ITEM_GROUP_NAME, \
				(*ppGroup)->group_name, \
				STORAGE_ITEM_IP_ADDR, FDFS_CURRENT_IP_ADDR(pStorage), \
				STORAGE_ITEM_STATUS, pStorage->status, \
				STORAGE_ITEM_VERSION, pStorage->version, \
				STORAGE_ITEM_JOIN_TIME, \
				(int)pStorage->join_time, \
				STORAGE_ITEM_STORAGE_PORT, \
				pStorage->storage_port, \
				STORAGE_ITEM_STORAGE_HTTP_PORT, \
				pStorage->storage_http_port,  \
				STORAGE_ITEM_DOMAIN_NAME, \
				pStorage->domain_name, \
				STORAGE_ITEM_SYNC_SRC_SERVER, \
				(pStorage->psync_src_server != NULL ? \
				pStorage->psync_src_server->id: ""), \
				STORAGE_ITEM_SYNC_UNTIL_TIMESTAMP, \
				(int)pStorage->sync_until_timestamp, \
				STORAGE_ITEM_STORE_PATH_COUNT, \
				pStorage->store_path_count, \
				STORAGE_ITEM_SUBDIR_COUNT_PER_PATH, \
				pStorage->subdir_count_per_path, \
				STORAGE_ITEM_UPLOAD_PRIORITY, \
				pStorage->upload_priority, \
				STORAGE_ITEM_TOTAL_MB, pStorage->total_mb, \
				STORAGE_ITEM_FREE_MB, pStorage->free_mb, \
				STORAGE_ITEM_TOTAL_UPLOAD_COUNT, \
				pStorage->stat.total_upload_count, \
				STORAGE_ITEM_SUCCESS_UPLOAD_COUNT, \
				pStorage->stat.success_upload_count, \
				STORAGE_ITEM_TOTAL_APPEND_COUNT, \
				pStorage->stat.total_append_count, \
				STORAGE_ITEM_SUCCESS_APPEND_COUNT, \
				pStorage->stat.success_append_count, \
				STORAGE_ITEM_TOTAL_SET_META_COUNT, \
				pStorage->stat.total_set_meta_count, \
				STORAGE_ITEM_SUCCESS_SET_META_COUNT, \
				pStorage->stat.success_set_meta_count, \
				STORAGE_ITEM_TOTAL_DELETE_COUNT, \
				pStorage->stat.total_delete_count, \
				STORAGE_ITEM_SUCCESS_DELETE_COUNT, \
				pStorage->stat.success_delete_count, \
				STORAGE_ITEM_TOTAL_DOWNLOAD_COUNT, \
				pStorage->stat.total_download_count, \
				STORAGE_ITEM_SUCCESS_DOWNLOAD_COUNT, \
				pStorage->stat.success_download_count, \
				STORAGE_ITEM_TOTAL_GET_META_COUNT, \
				pStorage->stat.total_get_meta_count, \
				STORAGE_ITEM_SUCCESS_GET_META_COUNT, \
				pStorage->stat.success_get_meta_count, \
				STORAGE_ITEM_TOTAL_CREATE_LINK_COUNT, \
				pStorage->stat.total_create_link_count, \
				STORAGE_ITEM_SUCCESS_CREATE_LINK_COUNT, \
				pStorage->stat.success_create_link_count, \
				STORAGE_ITEM_TOTAL_DELETE_LINK_COUNT, \
				pStorage->stat.total_delete_link_count, \
				STORAGE_ITEM_SUCCESS_DELETE_LINK_COUNT, \
				pStorage->stat.success_delete_link_count, \
				STORAGE_ITEM_TOTAL_UPLOAD_BYTES, \
				pStorage->stat.total_upload_bytes, \
				STORAGE_ITEM_SUCCESS_UPLOAD_BYTES, \
				pStorage->stat.success_upload_bytes, \
				STORAGE_ITEM_TOTAL_APPEND_BYTES, \
				pStorage->stat.total_append_bytes, \
				STORAGE_ITEM_SUCCESS_APPEND_BYTES, \
				pStorage->stat.success_append_bytes, \
				STORAGE_ITEM_TOTAL_DOWNLOAD_BYTES, \
				pStorage->stat.total_download_bytes, \
				STORAGE_ITEM_SUCCESS_DOWNLOAD_BYTES, \
				pStorage->stat.success_download_bytes, \
				STORAGE_ITEM_TOTAL_SYNC_IN_BYTES, \
				pStorage->stat.total_sync_in_bytes, \
				STORAGE_ITEM_SUCCESS_SYNC_IN_BYTES, \
				pStorage->stat.success_sync_in_bytes, \
				STORAGE_ITEM_TOTAL_SYNC_OUT_BYTES, \
				pStorage->stat.total_sync_out_bytes, \
				STORAGE_ITEM_SUCCESS_SYNC_OUT_BYTES, \
				pStorage->stat.success_sync_out_bytes, \
				STORAGE_ITEM_TOTAL_FILE_OPEN_COUNT, \
				pStorage->stat.total_file_open_count, \
				STORAGE_ITEM_SUCCESS_FILE_OPEN_COUNT, \
				pStorage->stat.success_file_open_count, \
				STORAGE_ITEM_TOTAL_FILE_READ_COUNT, \
				pStorage->stat.total_file_read_count, \
				STORAGE_ITEM_SUCCESS_FILE_READ_COUNT, \
				pStorage->stat.success_file_read_count, \
				STORAGE_ITEM_TOTAL_FILE_WRITE_COUNT, \
				pStorage->stat.total_file_write_count, \
				STORAGE_ITEM_SUCCESS_FILE_WRITE_COUNT, \
				pStorage->stat.success_file_write_count, \
				STORAGE_ITEM_LAST_SOURCE_UPDATE, \
				(int)(pStorage->stat.last_source_update), \
				STORAGE_ITEM_LAST_SYNC_UPDATE, \
				(int)(pStorage->stat.last_sync_update), \
				STORAGE_ITEM_LAST_SYNCED_TIMESTAMP, \
				(int)pStorage->stat.last_synced_timestamp, \
				STORAGE_ITEM_LAST_HEART_BEAT_TIME, \
				(int)pStorage->stat.last_heart_beat_time, \
				STORAGE_ITEM_CHANGELOG_OFFSET, \
				pStorage->changelog_offset \
	 		     );

			if (fc_safe_write(fd, buff, len) != len)
			{
				logError("file: "__FILE__", line: %d, " \
					"write to file \"%s\" fail, " \
					"errno: %d, error info: %s", \
					__LINE__, tmpFilename, \
					errno, STRERROR(errno));
				result = errno != 0 ? errno : EIO;
				break;
			}
		}
	}

	if (result == 0)
	{
		len = sprintf(buff, \
			"\n# global section\n" \
			"[%s]\n" \
			"\t%s=%d\n", \
			STORAGE_SECTION_NAME_GLOBAL, \
			STORAGE_ITEM_STORAGE_COUNT, count);
		if (fc_safe_write(fd, buff, len) != len)
		{
			logError("file: "__FILE__", line: %d, " \
				"write to file \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}
	}

	if (result == 0)
	{
		if (fsync(fd) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"fsync file \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}
	}

	close(fd);

	if (result == 0)
	{
		if (rename(tmpFilename, trueFilename) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"rename file \"%s\" to \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, trueFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}

		SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(trueFilename);
	}

	if (result != 0)
	{
		unlink(tmpFilename);
	}

	tracker_mem_file_unlock();

	return result;
}

int tracker_save_sync_timestamps()
{
	char tmpFilename[MAX_PATH_SIZE];
	char trueFilename[MAX_PATH_SIZE];
	char buff[512];
	int fd;
	int len;
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;
	int **last_sync_timestamps;
	int i;
	int k;
	int result;

	tracker_mem_file_lock();

	snprintf(trueFilename, sizeof(trueFilename), "%s/data/%s", \
		SF_G_BASE_PATH_STR, STORAGE_SYNC_TIMESTAMP_FILENAME);
	snprintf(tmpFilename, sizeof(tmpFilename), "%s.tmp", trueFilename);
	if ((fd=open(tmpFilename, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
	{
		tracker_mem_file_unlock();

		logError("file: "__FILE__", line: %d, " \
			"open \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, tmpFilename, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	result = 0;
	ppGroupEnd = g_groups.sorted_groups + g_groups.count;
	for (ppGroup=g_groups.sorted_groups; \
		(ppGroup < ppGroupEnd) && (result == 0); ppGroup++)
	{
		last_sync_timestamps = (*ppGroup)->last_sync_timestamps;
		for (i=0; i<(*ppGroup)->count; i++)
		{
			if ((*ppGroup)->all_servers[i]->status == \
				FDFS_STORAGE_STATUS_DELETED \
			 || (*ppGroup)->all_servers[i]->status == \
				FDFS_STORAGE_STATUS_IP_CHANGED)
			{
				continue;
			}

			len = sprintf(buff, "%s%c%s", (*ppGroup)->group_name, \
				STORAGE_DATA_FIELD_SEPERATOR, \
				(*ppGroup)->all_servers[i]->id);
			for (k=0; k<(*ppGroup)->count; k++)
			{
				if ((*ppGroup)->all_servers[k]->status == \
					FDFS_STORAGE_STATUS_DELETED \
				 || (*ppGroup)->all_servers[k]->status == \
					FDFS_STORAGE_STATUS_IP_CHANGED)
				{
					continue;
				}

				len += sprintf(buff + len, "%c%d", \
					STORAGE_DATA_FIELD_SEPERATOR, \
					last_sync_timestamps[i][k]);
			}
			*(buff + len) = '\n';
			len++;

			if (fc_safe_write(fd, buff, len) != len)
			{
				logError("file: "__FILE__", line: %d, " \
					"write to file \"%s\" fail, " \
					"errno: %d, error info: %s", \
					__LINE__, tmpFilename, \
					errno, STRERROR(errno));
				result = errno != 0 ? errno : EIO;
				break;
			}
		}
	}

	if (result == 0)
	{
		if (fsync(fd) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"fsync file \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}
	}

	close(fd);

	if (result == 0)
	{
		if (rename(tmpFilename, trueFilename) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"rename file \"%s\" to \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, tmpFilename, trueFilename, \
				errno, STRERROR(errno));
			result = errno != 0 ? errno : EIO;
		}

		SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(trueFilename);
	}

	if (result != 0)
	{
		unlink(tmpFilename);
	}

	tracker_mem_file_unlock();

	return result;
}

int tracker_save_sys_files()
{
	int result;

	if ((result=tracker_save_groups()) != 0)
	{
		return result;
	}

	if ((result=tracker_save_storages()) != 0)
	{
		return result;
	}

	return tracker_save_sync_timestamps();
}

static int tracker_open_changlog_file()
{
	char data_path[MAX_PATH_SIZE];
	char filename[MAX_PATH_SIZE];

	snprintf(data_path, sizeof(data_path), "%s/data", SF_G_BASE_PATH_STR);
	if (!fileExists(data_path))
	{
		if (mkdir(data_path, 0755) != 0)
		{
			logError("file: "__FILE__", line: %d, " \
				"mkdir \"%s\" fail, " \
				"errno: %d, error info: %s", \
				__LINE__, data_path, errno, STRERROR(errno));
			return errno != 0 ? errno : ENOENT;
		}
		SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(data_path);
	}

	snprintf(filename, sizeof(filename), "%s/data/%s", \
		SF_G_BASE_PATH_STR, STORAGE_SERVERS_CHANGELOG_FILENAME);
	changelog_fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
	if (changelog_fd < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"open \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, filename, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	g_changelog_fsize = lseek(changelog_fd, 0, SEEK_END);
        if (g_changelog_fsize < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"lseek file \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, filename, errno, STRERROR(errno));
		return errno != 0 ? errno : EIO;
	}

	SF_FCHOWN_TO_RUNBY_RETURN_ON_ERROR(changelog_fd, filename);

	return 0;
}

static int tracker_mem_init_groups(FDFSGroups *pGroups)
{
	int result;
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;

	pGroups->alloc_size = TRACKER_MEM_ALLOC_ONCE;
	pGroups->count = 0;
	pGroups->current_write_group = 0;
	pGroups->pStoreGroup = NULL;
	pGroups->groups = (FDFSGroupInfo **)malloc( \
			sizeof(FDFSGroupInfo *) * pGroups->alloc_size);
	if (pGroups->groups == NULL)
	{
		logCrit("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail!", __LINE__, \
			(int)sizeof(FDFSGroupInfo *) * pGroups->alloc_size);
		return errno != 0 ? errno : ENOMEM;
	}

	memset(pGroups->groups, 0, \
		sizeof(FDFSGroupInfo *) * pGroups->alloc_size);

	ppGroupEnd = pGroups->groups + pGroups->alloc_size;
	for (ppGroup=pGroups->groups; ppGroup<ppGroupEnd; ppGroup++)
	{
		*ppGroup = (FDFSGroupInfo *)malloc(sizeof(FDFSGroupInfo));
		if (*ppGroup == NULL)
		{
			logCrit("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail!", \
				__LINE__, (int)sizeof(FDFSGroupInfo));
			return errno != 0 ? errno : ENOMEM;
		}

		memset(*ppGroup, 0, sizeof(FDFSGroupInfo));
	}

	pGroups->sorted_groups = (FDFSGroupInfo **) \
			malloc(sizeof(FDFSGroupInfo *) * pGroups->alloc_size);
	if (pGroups->sorted_groups == NULL)
	{
		free(pGroups->groups);
		pGroups->groups = NULL;

		logCrit("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail!", __LINE__, \
			(int)sizeof(FDFSGroupInfo *) * pGroups->alloc_size);
		return errno != 0 ? errno : ENOMEM;
	}

	memset(pGroups->sorted_groups, 0, \
		sizeof(FDFSGroupInfo *) * pGroups->alloc_size);

	if ((result=tracker_load_data(pGroups)) != 0)
	{
		return result;
	}

	return 0;
}

int tracker_mem_init()
{
	int result;

	if ((result=init_pthread_lock(&mem_thread_lock)) != 0)
	{
		return result;
	}

	if ((result=init_pthread_lock(&mem_file_lock)) != 0)
	{
		return result;
	}

	if ((result=tracker_open_changlog_file()) != 0)
	{
		return result;
	}

	return tracker_mem_init_groups(&g_groups);
}

static void tracker_free_last_sync_timestamps(int **last_sync_timestamps, \
		const int alloc_size)
{
	int i;

	if (last_sync_timestamps != NULL)
	{
		for (i=0; i<alloc_size; i++)
		{
			if (last_sync_timestamps[i] != NULL)
			{
				free(last_sync_timestamps[i]);
				last_sync_timestamps[i] = NULL;
			}
		}

		free(last_sync_timestamps);
	}
}

static int **tracker_malloc_last_sync_timestamps(const int alloc_size, \
		int *err_no)
{
	int **results;
	int i;

	results = (int **)malloc(sizeof(int *) * alloc_size);
	if (results == NULL)
	{
		*err_no = errno != 0 ? errno : ENOMEM;
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", __LINE__, \
			(int)sizeof(int *) * alloc_size);
		return NULL;
	}

	memset(results, 0, sizeof(int *) * alloc_size);
	for (i=0; i<alloc_size; i++)
	{
		results[i] = (int *)malloc(sizeof(int) * alloc_size);
		if (results[i] == NULL)
		{
			*err_no = errno != 0 ? errno : ENOMEM;
			logError("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail", __LINE__, \
				(int)sizeof(int) * alloc_size);

			tracker_free_last_sync_timestamps(results, alloc_size);
			return NULL;
		}

		memset(results[i], 0, sizeof(int) * alloc_size);
	}

	*err_no = 0;
	return results;
}

static void tracker_mem_free_storages(FDFSStorageDetail **servers, const int count)
{
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;

	ppServerEnd = servers + count;
	for (ppServer=servers; ppServer<ppServerEnd; ppServer++)
	{
		if (*ppServer != NULL)
		{
			free(*ppServer);
		}
	}

	free(servers);
}

static void tracker_mem_free_group(FDFSGroupInfo *pGroup)
{
	if (pGroup->sorted_servers != NULL)
	{
		free(pGroup->sorted_servers);
		pGroup->sorted_servers = NULL;
	}

	if (pGroup->active_servers != NULL)
	{
		free(pGroup->active_servers);
		pGroup->active_servers = NULL;
	}

	if (pGroup->all_servers != NULL)
	{
		tracker_mem_free_storages(pGroup->all_servers, \
				pGroup->alloc_size);
		pGroup->all_servers = NULL;
	}

#ifdef WITH_HTTPD
	if (g_http_check_interval > 0)
	{
		if (pGroup->http_servers != NULL)
		{
			free(pGroup->http_servers);
			pGroup->http_servers = NULL;
		}
	}
#endif

	tracker_free_last_sync_timestamps(pGroup->last_sync_timestamps, \
				pGroup->alloc_size);
	pGroup->last_sync_timestamps = NULL;
}

static int tracker_mem_init_group(FDFSGroupInfo *pGroup)
{
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;
	int err_no;

	pGroup->alloc_size = TRACKER_MEM_ALLOC_ONCE;
	pGroup->count = 0;
	pGroup->all_servers = (FDFSStorageDetail **) \
			malloc(sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
	if (pGroup->all_servers == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", __LINE__, \
			(int)sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
		return errno != 0 ? errno : ENOMEM;
	}

	memset(pGroup->all_servers, 0, \
		sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
	ppServerEnd = pGroup->all_servers + pGroup->alloc_size;	
	for (ppServer=pGroup->all_servers; ppServer<ppServerEnd; ppServer++)
	{
		*ppServer = (FDFSStorageDetail *)malloc( \
					sizeof(FDFSStorageDetail));
		if (*ppServer == NULL)
		{
			tracker_mem_free_group(pGroup);

			logError("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail", __LINE__, \
				(int)sizeof(FDFSStorageDetail));
			return errno != 0 ? errno : ENOMEM;
		}

		memset(*ppServer, 0, sizeof(FDFSStorageDetail));
	}

	pGroup->sorted_servers = (FDFSStorageDetail **) \
		malloc(sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
	if (pGroup->sorted_servers == NULL)
	{
		tracker_mem_free_group(pGroup);

		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", __LINE__, \
			(int)sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
		return errno != 0 ? errno : ENOMEM;
	}
	memset(pGroup->sorted_servers, 0, \
		sizeof(FDFSStorageDetail *) * pGroup->alloc_size);

	pGroup->active_servers = (FDFSStorageDetail **) \
		malloc(sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
	if (pGroup->active_servers == NULL)
	{
		tracker_mem_free_group(pGroup);

		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", __LINE__, \
			(int)sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
		return errno != 0 ? errno : ENOMEM;
	}
	memset(pGroup->active_servers, 0, \
		sizeof(FDFSStorageDetail *) * pGroup->alloc_size);

#ifdef WITH_HTTPD
	if (g_http_check_interval <= 0)
	{
		pGroup->http_servers = pGroup->active_servers;
	}
	else
	{
		pGroup->http_servers = (FDFSStorageDetail **) \
			malloc(sizeof(FDFSStorageDetail *)*pGroup->alloc_size);
		if (pGroup->http_servers == NULL)
		{
			tracker_mem_free_group(pGroup);

			logError("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail", __LINE__, \
				(int)sizeof(FDFSStorageDetail *) * \
				pGroup->alloc_size);
			return errno != 0 ? errno : ENOMEM;
		}
		memset(pGroup->http_servers, 0, \
			sizeof(FDFSStorageDetail *) * pGroup->alloc_size);
		g_http_servers_dirty = true;
	}
#endif

	pGroup->last_sync_timestamps = tracker_malloc_last_sync_timestamps( \
			pGroup->alloc_size, &err_no);
	return err_no;
}

static int tracker_mem_destroy_groups(FDFSGroups *pGroups, const bool saveFiles)
{
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppEnd;
	int result;

	if (pGroups->groups == NULL)
	{
		result = 0;
	}
	else
	{
		if (saveFiles)
		{
			result = tracker_save_sys_files();
		}
		else
		{
			result = 0;
		}

		ppEnd = pGroups->groups + pGroups->count;
		for (ppGroup=pGroups->groups; ppGroup<ppEnd; ppGroup++)
		{
			tracker_mem_free_group(*ppGroup);
		}

		if (pGroups->sorted_groups != NULL)
		{
			free(pGroups->sorted_groups);
			pGroups->sorted_groups = NULL;
		}

		free(pGroups->groups);
		pGroups->groups = NULL;
	}

	return result;
}

int tracker_mem_destroy()
{
	int result;

	result = tracker_mem_destroy_groups(&g_groups, true);

	if (changelog_fd >= 0)
	{
		close(changelog_fd);
		changelog_fd = -1;
	}

	if (pthread_mutex_destroy(&mem_thread_lock) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_destroy fail", \
			__LINE__);
	}

	if (pthread_mutex_destroy(&mem_file_lock) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_destroy fail", \
			__LINE__);
	}

	return result;
}

static void tracker_mem_free_groups(FDFSGroupInfo **groups, const int count)
{
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;

	ppGroupEnd = groups + count;
	for (ppGroup=groups; ppGroup<ppGroupEnd; ppGroup++)
	{
		if (*ppGroup != NULL)
		{
			free(*ppGroup);
		}
	}

	free(groups);
}

static int tracker_mem_realloc_groups(FDFSGroups *pGroups, const bool bNeedSleep)
{
	FDFSGroupInfo **old_groups;
	FDFSGroupInfo **old_sorted_groups;
	FDFSGroupInfo **new_groups;
	FDFSGroupInfo **new_sorted_groups;
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;
	int new_size;

	new_size = pGroups->alloc_size + TRACKER_MEM_ALLOC_ONCE;
	new_groups = (FDFSGroupInfo **)malloc(sizeof(FDFSGroupInfo *) * new_size);
	if (new_groups == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", \
			__LINE__, (int)sizeof(FDFSGroupInfo *) * new_size);
		return errno != 0 ? errno : ENOMEM;
	}
	memset(new_groups, 0, sizeof(FDFSGroupInfo *) * new_size);

	ppGroupEnd = new_groups + new_size;
	for (ppGroup=new_groups+pGroups->count; ppGroup<ppGroupEnd; ppGroup++)
	{
		*ppGroup = (FDFSGroupInfo *)malloc(sizeof(FDFSGroupInfo));
		if (*ppGroup == NULL)
		{
			tracker_mem_free_groups(new_groups, new_size);

			logCrit("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail", \
				__LINE__, (int)sizeof(FDFSGroupInfo));
			return errno != 0 ? errno : ENOMEM;
		}

		memset(*ppGroup, 0, sizeof(FDFSGroupInfo));
	}

	memcpy(new_groups, pGroups->groups, \
		sizeof(FDFSGroupInfo *) * pGroups->count);

	new_sorted_groups = (FDFSGroupInfo **)malloc( \
			sizeof(FDFSGroupInfo *) * new_size);
	if (new_sorted_groups == NULL)
	{
		tracker_mem_free_groups(new_groups, new_size);

		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", \
			__LINE__, (int)sizeof(FDFSGroupInfo *) * new_size);
		return errno != 0 ? errno : ENOMEM;
	}

	memset(new_sorted_groups, 0, sizeof(FDFSGroupInfo *) * new_size);
	memcpy(new_sorted_groups, pGroups->sorted_groups, \
		sizeof(FDFSGroupInfo *) * pGroups->count);

	old_groups = pGroups->groups;
	old_sorted_groups = pGroups->sorted_groups;
	pGroups->alloc_size = new_size;
	pGroups->groups = new_groups;
	pGroups->sorted_groups = new_sorted_groups;

	if (bNeedSleep)
	{
		sleep(1);
	}

	free(old_groups);
	free(old_sorted_groups);

	return 0;
}

int tracker_get_group_file_count(FDFSGroupInfo *pGroup)
{
	int count;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;

	count = 0;
	ppServerEnd = pGroup->all_servers + pGroup->count;
	for (ppServer=pGroup->all_servers; ppServer<ppServerEnd; ppServer++)
	{
		count += (*ppServer)->stat.success_upload_count - \
				(*ppServer)->stat.success_delete_count;
	}

	return count;
}

int tracker_get_group_success_upload_count(FDFSGroupInfo *pGroup)
{
	int count;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;

	count = 0;
	ppServerEnd = pGroup->all_servers + pGroup->count;
	for (ppServer=pGroup->all_servers; ppServer<ppServerEnd; ppServer++)
	{
		count += (*ppServer)->stat.success_upload_count;
	}

	return count;
}

FDFSStorageDetail *tracker_get_group_sync_src_server(FDFSGroupInfo *pGroup, \
			FDFSStorageDetail *pDestServer)
{
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;

	ppServerEnd = pGroup->active_servers + pGroup->active_count;
	for (ppServer=pGroup->active_servers; ppServer<ppServerEnd; ppServer++)
	{
		if (strcmp((*ppServer)->id, pDestServer->id) == 0)
		{
			continue;
		}

		return *ppServer;
	}

	return NULL;
}

static int tracker_mem_realloc_store_servers(FDFSGroupInfo *pGroup, \
		const int inc_count, const bool bNeedSleep)
{
	int result;
	FDFSStorageDetail **old_servers;
	FDFSStorageDetail **old_sorted_servers;
	FDFSStorageDetail **old_active_servers;
	int **old_last_sync_timestamps;
	FDFSStorageDetail **new_servers;
	FDFSStorageDetail **new_sorted_servers;
	FDFSStorageDetail **new_active_servers;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;
#ifdef WITH_HTTPD
	FDFSStorageDetail **old_http_servers;
	FDFSStorageDetail **new_http_servers;
#endif
	int **new_last_sync_timestamps;
	int old_size;
	int new_size;
	int err_no;
	int i;
	
	new_size = pGroup->alloc_size + inc_count + TRACKER_MEM_ALLOC_ONCE;
	new_servers = (FDFSStorageDetail **) \
		malloc(sizeof(FDFSStorageDetail *) * new_size);
	if (new_servers == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", \
			__LINE__, (int)sizeof(FDFSStorageDetail *) * new_size);
		return errno != 0 ? errno : ENOMEM;
	}
	memset(new_servers, 0, sizeof(FDFSStorageDetail *) * new_size);

	ppServerEnd = new_servers + new_size;	
	for (ppServer=new_servers+pGroup->count; ppServer<ppServerEnd; ppServer++)
	{
		*ppServer = (FDFSStorageDetail *)malloc( \
					sizeof(FDFSStorageDetail));
		if (*ppServer == NULL)
		{
			tracker_mem_free_storages(new_servers, new_size);

			logError("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail", __LINE__, \
				(int)sizeof(FDFSStorageDetail));
			return errno != 0 ? errno : ENOMEM;
		}

		memset(*ppServer, 0, sizeof(FDFSStorageDetail));
	}

	memcpy(new_servers, pGroup->all_servers, \
		sizeof(FDFSStorageDetail *) * pGroup->count);

	new_sorted_servers = (FDFSStorageDetail **) \
		malloc(sizeof(FDFSStorageDetail *) * new_size);
	if (new_sorted_servers == NULL)
	{
		free(new_servers);
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", \
			__LINE__, (int)sizeof(FDFSStorageDetail *) * new_size);
		return errno != 0 ? errno : ENOMEM;
	}

	new_active_servers = (FDFSStorageDetail **) \
		malloc(sizeof(FDFSStorageDetail *) * new_size);
	if (new_active_servers == NULL)
	{
		free(new_servers);
		free(new_sorted_servers);

		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail", \
			__LINE__, (int)sizeof(FDFSStorageDetail *) * new_size);
		return errno != 0 ? errno : ENOMEM;
	}

#ifdef WITH_HTTPD
	if (g_http_check_interval > 0)
	{
		new_http_servers = (FDFSStorageDetail **) \
			malloc(sizeof(FDFSStorageDetail *) * new_size);
		if (new_http_servers == NULL)
		{
			free(new_servers);
			free(new_sorted_servers);
			free(new_active_servers);

			logError("file: "__FILE__", line: %d, " \
				"malloc %d bytes fail", __LINE__, \
				(int)sizeof(FDFSStorageDetail *) * new_size);
			return errno != 0 ? errno : ENOMEM;
		}

		memset(new_http_servers,0,sizeof(FDFSStorageDetail *)*new_size);

		memcpy(new_http_servers, pGroup->http_servers, \
			sizeof(FDFSStorageDetail *) * pGroup->count);

	}
	else
	{
		new_http_servers = NULL;
	}
#endif

	memset(new_sorted_servers, 0, sizeof(FDFSStorageDetail *) * new_size);
	memset(new_active_servers, 0, sizeof(FDFSStorageDetail *) * new_size);
	if (pGroup->store_path_count > 0)
	{
		for (i=pGroup->count; i<new_size; i++)
		{
			result=tracker_malloc_storage_path_mbs(*(new_servers+i), \
				pGroup->store_path_count);
			if (result != 0)
			{
				free(new_servers);
				free(new_sorted_servers);
		        free(new_active_servers);

				return result;
			}
		}
	}

	memcpy(new_sorted_servers, pGroup->sorted_servers, \
		sizeof(FDFSStorageDetail *) * pGroup->count);

	memcpy(new_active_servers, pGroup->active_servers, \
		sizeof(FDFSStorageDetail *) * pGroup->count);

	new_last_sync_timestamps = tracker_malloc_last_sync_timestamps( \
		new_size, &err_no);
	if (new_last_sync_timestamps == NULL)
	{
		free(new_servers);
		free(new_sorted_servers);
		free(new_active_servers);

		return err_no;
	}
	for (i=0; i<pGroup->alloc_size; i++)
	{
		memcpy(new_last_sync_timestamps[i],  \
			pGroup->last_sync_timestamps[i], \
			(int)sizeof(int) *  pGroup->alloc_size);
	}

	old_size = pGroup->alloc_size;
	old_servers = pGroup->all_servers;
	old_sorted_servers = pGroup->sorted_servers;
	old_active_servers = pGroup->active_servers;
	old_last_sync_timestamps = pGroup->last_sync_timestamps;

	pGroup->alloc_size = new_size;
	pGroup->all_servers = new_servers;
	pGroup->sorted_servers = new_sorted_servers;
	pGroup->active_servers = new_active_servers;
	pGroup->last_sync_timestamps = new_last_sync_timestamps;

	tracker_mem_find_store_server(pGroup);
	if (g_if_leader_self && g_if_use_trunk_file)
	{
		tracker_mem_find_trunk_server(pGroup, true);
	}

#ifdef WITH_HTTPD
	if (g_http_check_interval <= 0)
	{
		old_http_servers = NULL;
		pGroup->http_servers = pGroup->active_servers;
	}
	else
	{
		old_http_servers = pGroup->http_servers;
		pGroup->http_servers = new_http_servers;
		g_http_servers_dirty = true;
	}
#endif

	if (bNeedSleep)
	{
		sleep(1);
	}

	free(old_servers);
	
	free(old_sorted_servers);
	free(old_active_servers);

#ifdef WITH_HTTPD
	if (old_http_servers != NULL)
	{
		free(old_http_servers);
	}
#endif

	tracker_free_last_sync_timestamps(old_last_sync_timestamps, \
				old_size);

	return 0;
}

static int tracker_mem_cmp_by_group_name(const void *p1, const void *p2)
{
	return strcmp((*((FDFSGroupInfo **)p1))->group_name,
			(*((FDFSGroupInfo **)p2))->group_name);
}

static int tracker_mem_cmp_by_storage_id(const void *p1, const void *p2)
{
	return strcmp((*((FDFSStorageDetail **)p1))->id,
			(*((FDFSStorageDetail **)p2))->id);
}

static void tracker_mem_insert_into_sorted_servers( \
		FDFSStorageDetail *pTargetServer,   \
		FDFSStorageDetail **sorted_servers, const int count)
{
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppEnd;

	ppEnd = sorted_servers + count;
	for (ppServer=ppEnd; ppServer>sorted_servers; ppServer--)
	{
		if (strcmp(pTargetServer->id, (*(ppServer-1))->id) > 0)
		{
			break;
		}
		else
		{
			*ppServer = *(ppServer-1);
		}
	}

	*ppServer = pTargetServer;
}

static void tracker_mem_insert_into_sorted_groups(FDFSGroups *pGroups, \
		FDFSGroupInfo *pTargetGroup)
{
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppEnd;

	ppEnd = pGroups->sorted_groups + pGroups->count;
	for (ppGroup=ppEnd; ppGroup > pGroups->sorted_groups; ppGroup--)
	{
		if (strcmp(pTargetGroup->group_name, \
			   (*(ppGroup-1))->group_name) > 0)
		{
			*ppGroup = pTargetGroup;
			return;
		}
		else
		{
			*ppGroup = *(ppGroup-1);
		}
	}

	*ppGroup = pTargetGroup;
}

FDFSGroupInfo *tracker_mem_get_group_ex(FDFSGroups *pGroups, \
		const char *group_name)
{
	FDFSGroupInfo target_groups;
	FDFSGroupInfo *pTargetGroups;
	FDFSGroupInfo **ppGroup;

	memset(&target_groups, 0, sizeof(target_groups));
	strcpy(target_groups.group_name, group_name);
	pTargetGroups = &target_groups;
	ppGroup = (FDFSGroupInfo **)bsearch(&pTargetGroups, \
			pGroups->sorted_groups, \
			pGroups->count, sizeof(FDFSGroupInfo *), \
			tracker_mem_cmp_by_group_name);

	if (ppGroup != NULL)
	{
		return *ppGroup;
	}
	else
	{
		return NULL;
	}
}

static int tracker_mem_add_group_ex(FDFSGroups *pGroups, \
	TrackerClientInfo *pClientInfo, const char *group_name, \
	const bool bNeedSleep, bool *bInserted)
{
	FDFSGroupInfo *pGroup;
	int result;

	if ((result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	do
	{	
		result = 0;
		*bInserted = false;
		pGroup = tracker_mem_get_group_ex(pGroups, group_name);
		if (pGroup != NULL)
		{
			break;
		}

		if (pGroups->count >= pGroups->alloc_size)
		{
			result = tracker_mem_realloc_groups(pGroups, bNeedSleep);
			if (result != 0)
			{
				break;
			}
		}

		pGroup = *(pGroups->groups + pGroups->count);
		result = tracker_mem_init_group(pGroup);
		if (result != 0)
		{
			break;
		}

		strcpy(pGroup->group_name, group_name);
		tracker_mem_insert_into_sorted_groups(pGroups, pGroup);
		pGroups->count++;

		if ((pGroups->store_lookup == \
				FDFS_STORE_LOOKUP_SPEC_GROUP) && \
				(pGroups->pStoreGroup == NULL) && \
				(strcmp(pGroups->store_group, \
					pGroup->group_name) == 0))
		{
			pGroups->pStoreGroup = pGroup;
		}

		*bInserted = true;
	} while (0);

	if (pthread_mutex_unlock(&mem_thread_lock) != 0)
	{
		logError("file: "__FILE__", line: %d, "   \
			"call pthread_mutex_unlock fail", \
			__LINE__);
	}

	if (result != 0)
	{
		return result;
	}

	pClientInfo->pGroup = pGroup;
	return 0;
}

static FDFSStorageDetail *tracker_mem_get_active_storage_by_id( \
		FDFSGroupInfo *pGroup, const char *id)
{
	FDFSStorageDetail target_storage;
	FDFSStorageDetail *pTargetStorage;
	FDFSStorageDetail **ppStorageServer;

	if (id == NULL)
	{
		return NULL;
	}

	memset(&target_storage, 0, sizeof(target_storage));
	strcpy(target_storage.id, id);
	pTargetStorage = &target_storage;
	ppStorageServer = (FDFSStorageDetail **)bsearch(&pTargetStorage, \
			pGroup->active_servers, \
			pGroup->active_count, \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer != NULL)
	{
		return *ppStorageServer;
	}
	else
	{
		return NULL;
	}
}

static FDFSStorageDetail *tracker_mem_get_active_storage_by_ip( \
		FDFSGroupInfo *pGroup, const char *ip_addr)
{
	FDFSStorageIdInfo *pStorageId;

	if (!g_use_storage_id)
	{
		return tracker_mem_get_active_storage_by_id(pGroup, ip_addr);
	}

	pStorageId = fdfs_get_storage_id_by_ip(pGroup->group_name, ip_addr);
	if (pStorageId == NULL)
	{
		return NULL;
	}
	return tracker_mem_get_active_storage_by_id(pGroup, pStorageId->id);
}

#ifdef WITH_HTTPD
static FDFSStorageDetail *tracker_mem_get_active_http_server_by_ip( \
			FDFSGroupInfo *pGroup, const char *ip_addr)
{
	FDFSStorageDetail target_storage;
	FDFSStorageDetail *pTargetStorage;
	FDFSStorageDetail **ppStorageServer;

	memset(&target_storage, 0, sizeof(target_storage));
	if (!g_use_storage_id)
	{
		strcpy(target_storage.id, ip_addr);
	}
	else
	{
		FDFSStorageIdInfo *pStorageId;
		pStorageId = fdfs_get_storage_id_by_ip( \
				pGroup->group_name, ip_addr);
		if (pStorageId == NULL)
		{
			return NULL;
		}
		strcpy(target_storage.id, pStorageId->id);
	}
	pTargetStorage = &target_storage;
	ppStorageServer = (FDFSStorageDetail **)bsearch(&pTargetStorage, \
			pGroup->http_servers, \
			pGroup->http_server_count, \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer != NULL)
	{
		return *ppStorageServer;
	}
	else
	{
		return NULL;
	}
}

static FDFSStorageDetail *tracker_mem_get_active_http_server_by_id( \
			FDFSGroupInfo *pGroup, const char *storage_id)
{
	FDFSStorageDetail target_storage;
	FDFSStorageDetail *pTargetStorage;
	FDFSStorageDetail **ppStorageServer;

	memset(&target_storage, 0, sizeof(target_storage));
	strcpy(target_storage.id, storage_id);
	pTargetStorage = &target_storage;
	ppStorageServer = (FDFSStorageDetail **)bsearch(&pTargetStorage, \
			pGroup->http_servers, \
			pGroup->http_server_count, \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer != NULL)
	{
		return *ppStorageServer;
	}
	else
	{
		return NULL;
	}
}
#endif

FDFSStorageDetail *tracker_mem_get_storage_by_ip(FDFSGroupInfo *pGroup, \
				const char *ip_addr)
{
	const char *storage_id;

	if (g_use_storage_id)
	{
		FDFSStorageIdInfo *pStorageIdInfo;
		pStorageIdInfo = fdfs_get_storage_id_by_ip( \
				pGroup->group_name, ip_addr);
		if (pStorageIdInfo == NULL)
		{
			return NULL;
		}
		storage_id = pStorageIdInfo->id;
	}
	else
	{
		storage_id = ip_addr;
	}

	return tracker_mem_get_storage(pGroup, storage_id);
}

FDFSStorageDetail *tracker_mem_get_storage(FDFSGroupInfo *pGroup, \
				const char *id)
{
	FDFSStorageDetail target_storage;
	FDFSStorageDetail *pTargetStorage;
	FDFSStorageDetail **ppStorageServer;

	memset(&target_storage, 0, sizeof(target_storage));
	strcpy(target_storage.id, id);
	pTargetStorage = &target_storage;
	ppStorageServer = (FDFSStorageDetail **)bsearch(&pTargetStorage, \
			pGroup->sorted_servers, \
			pGroup->count, \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer != NULL)
	{
		return *ppStorageServer;
	}
	else
	{
		return NULL;
	}
}

static void tracker_mem_clear_storage_fields(FDFSStorageDetail *pStorageServer)
{
        if (pStorageServer->path_total_mbs != NULL)
	{
		memset(pStorageServer->path_total_mbs, 0, sizeof(int64_t) \
			* pStorageServer->store_path_count);
	}

        if (pStorageServer->path_free_mbs != NULL)
	{
		memset(pStorageServer->path_free_mbs, 0, sizeof(int64_t) \
			* pStorageServer->store_path_count);
	}

	pStorageServer->psync_src_server = NULL;
	pStorageServer->sync_until_timestamp = 0;
	pStorageServer->total_mb = 0;
	pStorageServer->free_mb = 0;
	pStorageServer->changelog_offset = 0;
	pStorageServer->store_path_count = 0;
	pStorageServer->subdir_count_per_path = 0;
	pStorageServer->upload_priority = 0;
	pStorageServer->current_write_path = 0;

	memset(&(pStorageServer->stat), 0, sizeof(FDFSStorageStat));
}

static int tracker_mem_remove_group(FDFSGroupInfo **groups, FDFSGroupInfo *pGroup)
{
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppEnd;
	FDFSGroupInfo **pp;

    ppEnd = groups + g_groups.count;
    for (ppGroup=groups; ppGroup<ppEnd; ppGroup++)
    {
        if (*ppGroup == pGroup)
        {
            break;
        }
    }

    if (ppGroup == ppEnd)
    {
        return ENOENT;
    }

    for (pp=ppGroup + 1; pp<ppEnd; pp++)
    {
        *(pp - 1) = *pp;
    }

    return 0;
}

int tracker_mem_delete_group(const char *group_name)
{
    FDFSGroupInfo *pGroup;
    int result;

    pGroup = tracker_mem_get_group(group_name);
    if (pGroup == NULL)
    {
        return ENOENT;
    }

    if (pGroup->count != 0)
    {
        return EBUSY;
    }

	pthread_mutex_lock(&mem_thread_lock);
    if (pGroup->count != 0)
    {
        result = EBUSY;
    }
    else
    {
    result = tracker_mem_remove_group(g_groups.groups, pGroup);
    if (result == 0)
    {
        result = tracker_mem_remove_group(g_groups.sorted_groups, pGroup);
    }
    }
    if (result == 0)
    {
        if (g_groups.pStoreGroup == pGroup)
        {
            g_groups.pStoreGroup = NULL;
        }
        g_groups.count--;
    }
	pthread_mutex_unlock(&mem_thread_lock);

    if (result != 0)
    {
        return result;
    }

    logDebug("file: "__FILE__", line: %d, " \
            "delete empty group: %s", \
            __LINE__, group_name);
    sleep(1);
    free(pGroup);

    return tracker_save_groups();
}

int tracker_mem_delete_storage(FDFSGroupInfo *pGroup, const char *id)
{
	FDFSStorageDetail *pStorageServer;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppEnd;

	pStorageServer = tracker_mem_get_storage(pGroup, id);
	if (pStorageServer == NULL || pStorageServer->status == \
		FDFS_STORAGE_STATUS_IP_CHANGED)
	{
		return ENOENT;
	}

	if (pStorageServer->status == FDFS_STORAGE_STATUS_ONLINE || \
	    pStorageServer->status == FDFS_STORAGE_STATUS_ACTIVE || \
	    pStorageServer->status == FDFS_STORAGE_STATUS_RECOVERY)
	{
		return EBUSY;
	}

	if (pStorageServer->status == FDFS_STORAGE_STATUS_DELETED)
	{
		return EALREADY;
	}

	ppEnd = pGroup->all_servers + pGroup->count;
	for (ppServer=pGroup->all_servers; ppServer<ppEnd; ppServer++)
	{
		if ((*ppServer)->psync_src_server != NULL && \
			strcmp((*ppServer)->psync_src_server->id, id) == 0)
		{
			(*ppServer)->psync_src_server = NULL;
		}
	}

    logDebug("file: "__FILE__", line: %d, "
            "delete storage server: %s:%u, group: %s",
            __LINE__, pStorageServer->ip_addrs.ips[0].address,
            pStorageServer->storage_port, pGroup->group_name);

	tracker_mem_clear_storage_fields(pStorageServer);

	pStorageServer->status = FDFS_STORAGE_STATUS_DELETED;
	pGroup->chg_count++;

	tracker_write_to_changelog(pGroup, pStorageServer, NULL);
	return 0;
}

int tracker_mem_storage_ip_changed(FDFSGroupInfo *pGroup, \
		const char *old_storage_ip, const char *new_storage_ip)
{
	FDFSStorageDetail *pOldStorageServer;
	FDFSStorageDetail *pNewStorageServer;
	int result;
	bool bInserted;

	if (g_use_storage_id)
	{
		logError("file: "__FILE__", line: %d, " \
			"client ip: %s, do NOT support ip changed adjust " \
			"because cluster use server ID instead of " \
			"IP address", __LINE__, new_storage_ip);
		return EOPNOTSUPP;
	}

	pOldStorageServer = tracker_mem_get_storage(pGroup, old_storage_ip);
	if (pOldStorageServer == NULL || pOldStorageServer->status == \
		FDFS_STORAGE_STATUS_DELETED)
	{
		logError("file: "__FILE__", line: %d, " \
			"client ip: %s, old storage server: %s not exists", \
			__LINE__, new_storage_ip, old_storage_ip);
		return ENOENT;
	}

	if (pOldStorageServer->status == FDFS_STORAGE_STATUS_ONLINE || \
	    pOldStorageServer->status == FDFS_STORAGE_STATUS_ACTIVE || \
	    pOldStorageServer->status == FDFS_STORAGE_STATUS_RECOVERY)
	{
		logError("file: "__FILE__", line: %d, " \
			"client ip: %s, old storage server: %s is online", \
			__LINE__, new_storage_ip, old_storage_ip);
		return EBUSY;
	}

	if (pOldStorageServer->status == FDFS_STORAGE_STATUS_IP_CHANGED)
	{
		logError("file: "__FILE__", line: %d, " \
			"client ip: %s, old storage server: %s " \
			"'s ip address already changed", \
			__LINE__, new_storage_ip, old_storage_ip);
		return EALREADY;
	}

	pNewStorageServer = tracker_mem_get_storage(pGroup, new_storage_ip);
	if (!(pNewStorageServer == NULL || pNewStorageServer->status == \
		FDFS_STORAGE_STATUS_DELETED))
	{
		logError("file: "__FILE__", line: %d, " \
			"client ip: %s, new storage server: %s already exists",\
			__LINE__, new_storage_ip, new_storage_ip);
		return EEXIST;
	}

	result = _tracker_mem_add_storage(pGroup, &pNewStorageServer, \
			new_storage_ip, new_storage_ip, true, true, &bInserted);
	if (result != 0)
	{
		return result;
	}

	if (!bInserted)
	{
		logError("file: "__FILE__", line: %d, " \
			"client ip: %s, new storage server: %s already exists",\
			__LINE__, new_storage_ip, new_storage_ip);
		return EEXIST;
	}

	pthread_mutex_lock(&mem_thread_lock);

	//exchange old and new storage server
	snprintf(pOldStorageServer->id, sizeof(pOldStorageServer->id),
		"%s", new_storage_ip);
	snprintf(pOldStorageServer->ip_addrs.ips[0].address,
		sizeof(pOldStorageServer->ip_addrs.ips[0].address),
        "%s", new_storage_ip);

	snprintf(pNewStorageServer->id, sizeof(pNewStorageServer->id),
		"%s", old_storage_ip);
    pNewStorageServer->ip_addrs.count = 1;
	snprintf(pNewStorageServer->ip_addrs.ips[0].address,
		sizeof(pNewStorageServer->ip_addrs.ips[0].address),
        "%s", old_storage_ip);
	pNewStorageServer->status = FDFS_STORAGE_STATUS_IP_CHANGED;

	pGroup->chg_count++;

	//need re-sort
	qsort(pGroup->sorted_servers, pGroup->count,
		sizeof(FDFSStorageDetail *), tracker_mem_cmp_by_storage_id);

	pthread_mutex_unlock(&mem_thread_lock);

	tracker_write_to_changelog(pGroup, pNewStorageServer, new_storage_ip);

	return tracker_save_sys_files();
}

static int tracker_mem_add_storage(TrackerClientInfo *pClientInfo,
		const char *id, const char *ip_addr,
		const bool bNeedSleep, const bool bNeedLock, bool *bInserted)
{
	int result;
	FDFSStorageDetail *pStorageServer;

	pStorageServer = NULL;
	result = _tracker_mem_add_storage(pClientInfo->pGroup, \
			&pStorageServer, id, ip_addr, bNeedSleep, \
			bNeedLock, bInserted);
	if (result == 0)
	{
		pClientInfo->pStorage = pStorageServer;
	}

	return result;
}

static int tracker_mem_add_storage_from_file(FDFSGroups *pGroups,
        const char *data_path, TrackerClientInfo *pClientInfo,
		const char *group_name, const char *storage_id, char *ip_addr)
{
    int result;
    bool bInserted;

    if (g_use_storage_id)
    {
        if (storage_id == NULL || *storage_id == '\0')
        {
            FDFSStorageIdInfo *idInfo;
            idInfo = fdfs_get_storage_id_by_ip(group_name, ip_addr);
            if (idInfo == NULL)
            {
                logError("file: "__FILE__", line: %d, "
                        "in the file \"%s/%s\", "
                        "group: %s, item \"%s\" is not found or empty, "
                        "and storage ip %s not configed in storage_ids.conf",
                        __LINE__, data_path,
                        STORAGE_SERVERS_LIST_FILENAME_NEW,
                        group_name, STORAGE_ITEM_SERVER_ID, ip_addr);
                return ENOENT;
            }

            storage_id = idInfo->id;
        }
    }

    if (ip_addr == NULL)
    {
        logError("file: "__FILE__", line: %d, "
                "in the file \"%s/%s\", "
                "group: %s, item \"%s\" is not found",
                __LINE__, data_path,
                STORAGE_SERVERS_LIST_FILENAME_NEW,
                group_name, STORAGE_ITEM_IP_ADDR);
        return ENOENT;
    }
    if (*ip_addr == '\0')
    {
        logWarning("file: "__FILE__", line: %d, "
                "in the file \"%s/%s\", "
                "group: %s, item \"%s\" is empty",
                __LINE__, data_path,
                STORAGE_SERVERS_LIST_FILENAME_NEW,
                group_name, STORAGE_ITEM_IP_ADDR);
        return ENOENT;
    }

    memset(pClientInfo, 0, sizeof(TrackerClientInfo));
    if ((pClientInfo->pGroup=tracker_mem_get_group_ex(pGroups,
                    group_name)) == NULL)
    {
        logError("file: "__FILE__", line: %d, "
                "in the file \"%s/%s\", "
                "group \"%s\" is not found",
                __LINE__, data_path,
                STORAGE_SERVERS_LIST_FILENAME_NEW,
                group_name);
        return errno != 0 ? errno : ENOENT;
    }

    if ((result=tracker_mem_add_storage(pClientInfo, storage_id,
                    ip_addr, false, false, &bInserted)) != 0)
    {
        return result;
    }

    if (!bInserted)
    {
        logError("file: "__FILE__", line: %d, "
                "in the file \"%s/%s\", "
                "storage \"%s\" is duplicate",
                __LINE__, data_path,
                STORAGE_SERVERS_LIST_FILENAME_NEW, ip_addr);
        return EEXIST;
    }

    return 0;
}

static int _tracker_mem_add_storage(FDFSGroupInfo *pGroup,
	FDFSStorageDetail **ppStorageServer, const char *id,
	const char *ip_addr, const bool bNeedSleep,
	const bool bNeedLock, bool *bInserted)
{
	int result;
	const char *storage_id;
    FDFSStorageIdInfo *pStorageIdInfo;
    FDFSMultiIP multi_ip;

	if (*ip_addr == '\0')
	{
		logError("file: "__FILE__", line: %d, " \
			"ip address is empty!", __LINE__);
		return EINVAL;
	}

    memset(&multi_ip, 0, sizeof(multi_ip));
    if (!g_use_storage_id)
    {
        multi_ip.count = 1;
        multi_ip.index = 0;
        strcpy(multi_ip.ips[0].address, ip_addr);
    }

	if (id != NULL)
	{
		if (g_use_storage_id)
		{
            pStorageIdInfo = fdfs_get_storage_by_id(id);
            if (pStorageIdInfo == NULL)
            {
				logError("file: "__FILE__", line: %d, "
					"storage id: %s not exist in config file, "
					"group_name: %s, storage ip: %s", __LINE__,
					id, pGroup->group_name, ip_addr);
				return ENOENT;
            }

			if (strcmp(pStorageIdInfo->group_name, pGroup->group_name) != 0)
			{
				logError("file: "__FILE__", line: %d, "
					"check storage id fail, inconsistent group names, "
					"id: %s, storage ip: %s, "
                    "reported group_name: %s != "
                    "group name in config file: %s", __LINE__,
					pGroup->group_name, id, ip_addr,
                    pStorageIdInfo->group_name);
				return EINVAL;
			}

             multi_ip = pStorageIdInfo->ip_addrs;
		}

		storage_id = id;
	}
	else if (g_use_storage_id)
	{
		pStorageIdInfo = fdfs_get_storage_id_by_ip(
				pGroup->group_name, ip_addr);
		if (pStorageIdInfo == NULL)
		{
			logError("file: "__FILE__", line: %d, "
				"get storage id info fail, "
				"group_name: %s, storage ip: %s not exist in config file",
				__LINE__, pGroup->group_name, ip_addr);
			return ENOENT;
		}

        multi_ip = pStorageIdInfo->ip_addrs;
		storage_id = pStorageIdInfo->id;
	}
	else
	{
		storage_id = ip_addr;
	}

	if (bNeedLock && (result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	do
	{
		result = 0;
		*bInserted = false;
		*ppStorageServer = tracker_mem_get_storage(pGroup, storage_id);
		if (*ppStorageServer != NULL)
		{
			if (g_use_storage_id)
			{
                fdfs_set_multi_ip_index(&(*ppStorageServer)->ip_addrs, ip_addr);
			}

			if ((*ppStorageServer)->status==FDFS_STORAGE_STATUS_DELETED \
			 || (*ppStorageServer)->status==FDFS_STORAGE_STATUS_IP_CHANGED)
			{
			 	(*ppStorageServer)->status = FDFS_STORAGE_STATUS_INIT;
			}

			break;
		}

		if (pGroup->count >= pGroup->alloc_size)
		{
			result = tracker_mem_realloc_store_servers(
					pGroup, 1, bNeedSleep);
			if (result != 0)
			{
				break;
			}
		}

		*ppStorageServer = *(pGroup->all_servers + pGroup->count);
		snprintf((*ppStorageServer)->id, FDFS_STORAGE_ID_MAX_SIZE,
				"%s", storage_id);
        (*ppStorageServer)->ip_addrs = multi_ip;
        if (g_use_storage_id)
        {
            fdfs_set_multi_ip_index(&(*ppStorageServer)->ip_addrs, ip_addr);
        }

		tracker_mem_insert_into_sorted_servers(*ppStorageServer,
				pGroup->sorted_servers, pGroup->count);
		pGroup->count++;
		pGroup->chg_count++;

		*bInserted = true;
	} while (0);

	if (bNeedLock && pthread_mutex_unlock(&mem_thread_lock) != 0)
	{
		logError("file: "__FILE__", line: %d, "   \
			"call pthread_mutex_unlock fail", \
			__LINE__);
	}

	return result;
}

void tracker_calc_running_times(TrackerRunningStatus *pStatus)
{
	pStatus->running_time = g_current_time - g_sf_global_vars.up_time;

	if (g_tracker_last_status.last_check_time == 0)
	{
		pStatus->restart_interval = 0;
	}
	else
	{
		pStatus->restart_interval = g_sf_global_vars.up_time -
				g_tracker_last_status.last_check_time;
	}

#define FDFS_TRIM_TIME(t, i) (t / i) * i

	pStatus->running_time = FDFS_TRIM_TIME(pStatus->running_time, \
					TRACKER_SYNC_STATUS_FILE_INTERVAL);
	pStatus->restart_interval = FDFS_TRIM_TIME(pStatus->restart_interval, \
					TRACKER_SYNC_STATUS_FILE_INTERVAL);
}

static int tracker_mem_get_sys_file_piece(ConnectionInfo *pTrackerServer, \
	const int file_index, int fd, int64_t *offset, int64_t *file_size)
{
	char out_buff[sizeof(TrackerHeader) + 1 + FDFS_PROTO_PKG_LEN_SIZE];
	char in_buff[TRACKER_MAX_PACKAGE_SIZE];
	TrackerHeader *pHeader;
	char *p;
	char *pInBuff;
	char *pContent;
	int64_t in_bytes;
	int64_t write_bytes;
	int result;

	memset(out_buff, 0, sizeof(out_buff));
	pHeader = (TrackerHeader *)out_buff;
	pHeader->cmd = TRACKER_PROTO_CMD_TRACKER_GET_ONE_SYS_FILE;
	long2buff(1 + FDFS_PROTO_PKG_LEN_SIZE, pHeader->pkg_len);

	p = out_buff + sizeof(TrackerHeader);
	*p++ = file_index;
	long2buff(*offset, p);
	if ((result=tcpsenddata_nb(pTrackerServer->sock, out_buff, \
			sizeof(out_buff), SF_G_NETWORK_TIMEOUT)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"send data to tracker server %s:%u fail, " \
			"errno: %d, error info: %s", __LINE__, \
			pTrackerServer->ip_addr, \
			pTrackerServer->port, \
			result, STRERROR(result));

		return (result == ENOENT ? EACCES : result);
	}

	pInBuff = in_buff;
	result = fdfs_recv_response(pTrackerServer, &pInBuff, \
				sizeof(in_buff), &in_bytes);
	if (result != 0)
	{
        logError("file: "__FILE__", line: %d, "
                "fdfs_recv_response fail, result: %d",
                __LINE__, result);
		return result;
	}

	if (in_bytes < FDFS_PROTO_PKG_LEN_SIZE)
	{
		logError("file: "__FILE__", line: %d, " \
			"tracker server %s:%u response data " \
			"length: %"PRId64" is invalid, " \
			"expect length >= %d.", __LINE__, \
			pTrackerServer->ip_addr, pTrackerServer->port, \
			in_bytes, FDFS_PROTO_PKG_LEN_SIZE);
		return EINVAL;
	}

	*file_size = buff2long(in_buff);
	write_bytes = in_bytes - FDFS_PROTO_PKG_LEN_SIZE;

	if (*file_size < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"tracker server %s:%u, file size: %"PRId64\
			" < 0", __LINE__, pTrackerServer->ip_addr, \
			pTrackerServer->port, *file_size);
		return EINVAL;
	}

	if (*file_size > 0 && write_bytes == 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"tracker server %s:%u, file size: %"PRId64\
			" > 0, but file content is empty", __LINE__, \
			pTrackerServer->ip_addr, pTrackerServer->port, \
			*file_size);
		return EINVAL;
	}

	pContent = pInBuff + FDFS_PROTO_PKG_LEN_SIZE;
	if (write_bytes > 0 &&
            fc_safe_write(fd, pContent, write_bytes) != write_bytes)
	{
		logError("file: "__FILE__", line: %d, " \
			"write to file %s fail, " \
			"errno: %d, error info: %s", \
			__LINE__, g_tracker_sys_filenames[file_index], \
			errno, STRERROR(errno));
		return errno != 0 ? errno : EIO;
        }

	*offset += write_bytes;
	return 0;
}

static int tracker_mem_get_one_sys_file(ConnectionInfo *pTrackerServer, \
		const int file_index)
{
	char full_filename[MAX_PATH_SIZE];
	int fd;
	int result;
	int64_t offset;
	int64_t file_size;

	snprintf(full_filename, sizeof(full_filename), "%s/data/%s", \
			SF_G_BASE_PATH_STR, g_tracker_sys_filenames[file_index]);
	fd = open(full_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (fd < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"open file %s fail, " \
			"errno: %d, error info: %s", \
			__LINE__, full_filename, \
			errno, STRERROR(errno));
		return errno != 0 ? errno : EACCES;
	}

	SF_FCHOWN_TO_RUNBY_RETURN_ON_ERROR(fd, full_filename);

	offset = 0;
	file_size = 0;
	while (1)
	{
		result = tracker_mem_get_sys_file_piece(pTrackerServer, \
			file_index, fd, &offset, &file_size);
		if (result != 0)
		{
			break;
		}

		if (offset >= file_size)
		{
			break;
		}
	}

	close(fd);
	return result;
}

static int tracker_mem_get_sys_files(TrackerServerInfo *pTrackerServer)
{
	ConnectionInfo *conn;
	int result;
	int index;

    fdfs_server_sock_reset(pTrackerServer);
	if ((conn=tracker_connect_server(pTrackerServer, &result)) == NULL)
	{
		return result;
	}

	if ((result=tracker_get_sys_files_start(conn)) != 0)
	{
		tracker_close_connection_ex(conn, true);
		return result;
	}

	for (index=0; index<TRACKER_SYS_FILE_COUNT; index++)
	{
		result = tracker_mem_get_one_sys_file(conn, index);
		if (result != 0)
		{
			break;
		}
	}

	result = tracker_get_sys_files_end(conn);
	tracker_close_connection_ex(conn, result != 0);

	return result;
}

static int tracker_mem_cmp_tracker_running_status(const void *p1, const void *p2)
{
	TrackerRunningStatus *pStatus1;
	TrackerRunningStatus *pStatus2;
	int sub;

	pStatus1 = (TrackerRunningStatus *)p1;
	pStatus2 = (TrackerRunningStatus *)p2;

    if (pStatus1->if_leader)
    {
        return 1;
    }
    else if (pStatus2->if_leader)
    {
        return -1;
    }

	sub = pStatus1->running_time - pStatus2->running_time;
	if (sub != 0)
	{
		return sub;
	}

	return pStatus2->restart_interval - pStatus1->restart_interval;
}

static int find_my_ip_in_tracker_list()
{
    const char *current_ip;
    const char *previous_ip;
    TrackerServerInfo *pServer;
    char buff[256];

    previous_ip = NULL;
    while ((current_ip=get_next_local_ip(previous_ip)) != NULL)
    {
        pServer = fdfs_tracker_group_get_server(&g_tracker_servers,
                current_ip, SF_G_INNER_PORT);
        if (pServer != NULL)
        {
            if (pServer->count > 1)
            {
                ConnectionInfo *conn;
                ConnectionInfo *end;

                end = pServer->connections + pServer->count;
                for (conn=pServer->connections; conn<end; conn++)
                {
                    insert_into_local_host_ip(conn->ip_addr);
                }
            }
            return 0;
        }

        previous_ip = current_ip;
    }

    logError("file: "__FILE__", line: %d, "
            "my ip NOT in tracker server list. %s",
            __LINE__, local_host_ip_addrs_to_string(buff, sizeof(buff)));
    return ENOENT;
}

static int tracker_mem_first_add_tracker_servers(FDFSStorageJoinBody *pJoinBody)
{
	TrackerServerInfo *pLocalTracker;
	TrackerServerInfo *pLocalEnd;
	TrackerServerInfo *servers;
	int tracker_count;
	int bytes;

	tracker_count = pJoinBody->tracker_count;
	bytes = sizeof(TrackerServerInfo) * tracker_count;
	servers = (TrackerServerInfo *)malloc(bytes);
	if (servers == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail, " \
			"errno: %d, error info: %s", \
			__LINE__, bytes, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOMEM;
	}
	memcpy(servers, pJoinBody->tracker_servers, bytes);

	pLocalEnd = servers + tracker_count;
       	for (pLocalTracker=servers; pLocalTracker<pLocalEnd; \
		pLocalTracker++)
	{
        fdfs_server_sock_reset(pLocalTracker);
	}

	g_tracker_servers.servers = servers;
	g_tracker_servers.server_count = tracker_count;
	return find_my_ip_in_tracker_list();
}

static int tracker_mem_copy_uniq_tracker_servers(
        TrackerServerInfo *pSrcServer,
        TrackerServerInfo *pDestServer)
{
	ConnectionInfo *conn;
	ConnectionInfo *end;

	end = pSrcServer->connections + pSrcServer->count;
	for (conn=pSrcServer->connections; conn<end; conn++)
    {
		if (!fdfs_server_contain1(pDestServer, conn))
        {
            if (pDestServer->count == FDFS_MULTI_IP_MAX_COUNT)
            {
                logError("file: "__FILE__", line: %d, "
                        "tracker IPs reach max count: %d",
                        __LINE__, FDFS_MULTI_IP_MAX_COUNT);
                return ENOSPC;
            }

            pDestServer->connections[pDestServer->count++] = *conn;
        }
    }
    return 0;
}

static int tracker_mem_check_add_tracker_servers(FDFSStorageJoinBody *pJoinBody)
{
	TrackerServerInfo *pJoinTracker;
	TrackerServerInfo *pJoinEnd;
	TrackerServerInfo *pLocalTracker;
	TrackerServerInfo *pLocalEnd;
	TrackerServerInfo *pNewServer;
	TrackerServerInfo *new_servers;
    char ip_str_join[256];
    char ip_str_before[256];
    char ip_str_after[256];
	int add_count;
	int bytes;

	add_count = 0;
	pLocalEnd = g_tracker_servers.servers + g_tracker_servers.server_count;
	pJoinEnd = pJoinBody->tracker_servers + pJoinBody->tracker_count;
        for (pJoinTracker=pJoinBody->tracker_servers;
                pJoinTracker<pJoinEnd; pJoinTracker++)
	{
        	for (pLocalTracker=g_tracker_servers.servers;
               		pLocalTracker<pLocalEnd; pLocalTracker++)
		{
            if (fdfs_server_equal(pJoinTracker, pLocalTracker))
            {
				break;
            }
            if (fdfs_server_contain_ex(pJoinTracker, pLocalTracker))
			{
                fdfs_server_info_to_string(pJoinTracker,
                        ip_str_join, sizeof(ip_str_join));
                fdfs_server_info_to_string(pLocalTracker,
                        ip_str_before, sizeof(ip_str_before));

                logWarning("file: "__FILE__", line: %d, "
                        "tracker server ips not consistent, "
                        "join: %s, local: %s", __LINE__,
                        ip_str_join, ip_str_before);

                if (pJoinTracker->count > pLocalTracker->count)
                {
                    if (tracker_mem_copy_uniq_tracker_servers(pJoinTracker,
                            pLocalTracker) == 0)
                    {
                        fdfs_server_info_to_string(pLocalTracker,
                                ip_str_after, sizeof(ip_str_after));
                        logInfo("file: "__FILE__", line: %d, "
                                "merge tracker server ips, before: %s, "
                                "after: %s", __LINE__,
                                ip_str_before, ip_str_after);
                    }
                }
				break;
			}
		}

		if (pLocalTracker == pLocalEnd)
		{
			add_count++;
		}
	}

	if (add_count == 0)
	{
		return 0;
	}

	if (g_last_tracker_servers != NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"last tracker servers does not freed, " \
			"should try again!", __LINE__);
		return EAGAIN;
	}

	if (g_tracker_servers.server_count + add_count > FDFS_MAX_TRACKERS)
	{
		logError("file: "__FILE__", line: %d, "
			"too many tracker servers: %d",
			__LINE__, g_tracker_servers.server_count + add_count);
		return ENOSPC;
	}

	bytes = sizeof(TrackerServerInfo) * (g_tracker_servers.server_count
						 + add_count);
	new_servers = (TrackerServerInfo *)malloc(bytes);
	if (new_servers == NULL)
	{
		logError("file: "__FILE__", line: %d, "
			"malloc %d bytes fail, "
			"errno: %d, error info: %s",
			__LINE__, bytes, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOMEM;
	}

	memcpy(new_servers, g_tracker_servers.servers, sizeof(TrackerServerInfo) *
				g_tracker_servers.server_count);
	pNewServer = new_servers + g_tracker_servers.server_count;
	for (pJoinTracker=pJoinBody->tracker_servers;
		pJoinTracker<pJoinEnd; pJoinTracker++)
	{
		for (pLocalTracker=new_servers;
			pLocalTracker<pNewServer; pLocalTracker++)
		{
            if (fdfs_server_contain_ex(pJoinTracker, pLocalTracker))
			{
				break;
			}
		}

		if (pLocalTracker == pNewServer)
		{
			memcpy(pNewServer, pJoinTracker,
				sizeof(TrackerServerInfo));
            fdfs_server_sock_reset(pNewServer);
			pNewServer++;
		}
	}

	add_count = (pNewServer - new_servers) - g_tracker_servers.server_count;
	g_last_tracker_servers = g_tracker_servers.servers;
	g_tracker_servers.servers = new_servers;
	g_tracker_servers.server_count += add_count;

	logInfo("file: "__FILE__", line: %d, "
		"add %d tracker servers, total tracker servers: %d",
		__LINE__, add_count, g_tracker_servers.server_count);

	return find_my_ip_in_tracker_list();
}

static int tracker_mem_get_tracker_server(FDFSStorageJoinBody *pJoinBody, \
		TrackerRunningStatus *pTrackerStatus)
{
	TrackerServerInfo *pTrackerServer;
	TrackerServerInfo *pTrackerEnd;
	TrackerRunningStatus *pStatus;
	TrackerRunningStatus trackerStatus[FDFS_MAX_TRACKERS];
	int count;
	int result;
	int r;
	int i;

	memset(pTrackerStatus, 0, sizeof(TrackerRunningStatus));
	pStatus = trackerStatus;
	result = 0;
	pTrackerEnd = pJoinBody->tracker_servers + pJoinBody->tracker_count;
    for (pTrackerServer=pJoinBody->tracker_servers;
            pTrackerServer<pTrackerEnd; pTrackerServer++)
	{
		if (fdfs_server_contain_local_service(pTrackerServer, SF_G_INNER_PORT))
		{
			continue;
		}

		pStatus->pTrackerServer = pTrackerServer;
		r = fdfs_get_tracker_status(pTrackerServer, pStatus);
		if (r == 0)
		{
			pStatus++;
		}
		else if (r != ENOENT)
		{
			result = r;
		}
	}

	count = pStatus - trackerStatus;
	if (count == 0)
	{
		return result == 0 ? ENOENT : result;
	}

	if (count > 1)
	{
		qsort(trackerStatus, count, sizeof(TrackerRunningStatus), \
			tracker_mem_cmp_tracker_running_status);
	}

	for (i=0; i<count; i++)
	{
		logDebug("file: "__FILE__", line: %d, "
			"%s:%u leader: %d, running time: %d, "
			"restart interval: %d", __LINE__,
			trackerStatus[i].pTrackerServer->connections[0].ip_addr,
			trackerStatus[i].pTrackerServer->connections[0].port,
			trackerStatus[i].if_leader,
			trackerStatus[i].running_time,
			trackerStatus[i].restart_interval);
	}

	//copy the last
	memcpy(pTrackerStatus, trackerStatus + (count - 1), \
			sizeof(TrackerRunningStatus));
	return 0;
}

static int tracker_mem_get_sys_files_from_others(FDFSStorageJoinBody *pJoinBody,
		 TrackerRunningStatus *pRunningStatus)
{
	int result;
	TrackerRunningStatus trackerStatus;
	TrackerServerInfo *pTrackerServer;
	FDFSGroups newGroups;
	FDFSGroups tempGroups;

	if (pJoinBody->tracker_count == 0)
	{
		return 0;
	}

	result = tracker_mem_get_tracker_server(pJoinBody, &trackerStatus);
	if (result != 0)
	{
		return result == ENOENT ? 0 : result;
	}

	if (pRunningStatus != NULL)
	{
		if (tracker_mem_cmp_tracker_running_status(pRunningStatus,
							&trackerStatus) >= 0)
		{
			logDebug("file: "__FILE__", line: %d, "
				"%s:%u running time: %d, restart interval: %d, "
				"my running time: %d, restart interval: %d, "
				"do not need sync system files", __LINE__,
				trackerStatus.pTrackerServer->connections[0].ip_addr,
				trackerStatus.pTrackerServer->connections[0].port,
				trackerStatus.running_time,
				trackerStatus.restart_interval,
				pRunningStatus->running_time,
				pRunningStatus->restart_interval);
			
			return 0;
		}
	}

	pTrackerServer = trackerStatus.pTrackerServer;
	result = tracker_mem_get_sys_files(pTrackerServer);
	if (result != 0)
	{
		return result;
	}

	logInfo("file: "__FILE__", line: %d, "
		"sys files loaded from tracker server %s:%u",
		__LINE__, pTrackerServer->connections[0].ip_addr,
		pTrackerServer->connections[0].port);

	memset(&newGroups, 0, sizeof(newGroups));
	newGroups.store_lookup = g_groups.store_lookup;
	newGroups.store_server = g_groups.store_server;
	newGroups.download_server = g_groups.download_server;
	newGroups.store_path = g_groups.store_path;
	strcpy(newGroups.store_group, g_groups.store_group);
	if ((result=tracker_mem_init_groups(&newGroups)) != 0)
	{
 		tracker_mem_destroy_groups(&newGroups, false);
		return result;
	}

	memcpy(&tempGroups, &g_groups, sizeof(FDFSGroups));
	memcpy(&g_groups, &newGroups, sizeof(FDFSGroups));

	usleep(100000);

 	tracker_mem_destroy_groups(&tempGroups, false);
	tracker_write_status_to_file(NULL);

	if (changelog_fd >= 0)
	{
		close(changelog_fd);
		changelog_fd = -1;
	}

	return tracker_open_changlog_file();
}

int tracker_mem_add_group_and_storage(TrackerClientInfo *pClientInfo, \
		const char *ip_addr, FDFSStorageJoinBody *pJoinBody, \
		const bool bNeedSleep)
{
	int result;
	bool bStorageInserted;
	bool bGroupInserted;
	FDFSStorageDetail *pStorageServer;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppEnd;
	FDFSStorageIdInfo *pStorageIdInfo;
	const char *storage_id;

	tracker_mem_file_lock();

	if (need_get_sys_files)
	{
		if (g_tracker_last_status.last_check_time > 0 && g_sf_global_vars.
                up_time - g_tracker_last_status.last_check_time >
				2 * TRACKER_SYNC_STATUS_FILE_INTERVAL)
		{ /* stop time exceeds 2 * interval */
			TrackerRunningStatus runningStatus;

			runningStatus.if_leader = false;
			tracker_calc_running_times(&runningStatus);
			result = tracker_mem_get_sys_files_from_others(\
						pJoinBody, &runningStatus);
			if (result != 0)
			{
				tracker_mem_file_unlock();
			    logError("file: "__FILE__", line: %d, "
                        "get sys files from other trackers fail, errno: %d",
                        __LINE__, result);
				return EAGAIN;
			}

			get_sys_files_done = true;
		}

		need_get_sys_files = false;
	}

	if ((!get_sys_files_done) && (g_groups.count == 0))
	{
		if (g_groups.count == 0)
		{
			if ((result=tracker_mem_get_sys_files_from_others( \
				pJoinBody, NULL)) != 0)
			{
				tracker_mem_file_unlock();
			    logError("file: "__FILE__", line: %d, "
                        "get sys files from other trackers fail, errno: %d",
                        __LINE__, result);
				return EAGAIN;
			}

			get_sys_files_done = true;
		}
	}

	if (g_tracker_servers.servers == NULL)
	{
		result = tracker_mem_first_add_tracker_servers(pJoinBody);
		if (result != 0)
		{
			tracker_mem_file_unlock();
			return result;
		}
	}
	else
	{
		result = tracker_mem_check_add_tracker_servers(pJoinBody);
		if (result != 0)
		{
			tracker_mem_file_unlock();
			return result;
		}
	}

	tracker_mem_file_unlock();

	if ((result=tracker_mem_add_group_ex(&g_groups, pClientInfo, \
		pJoinBody->group_name, bNeedSleep, &bGroupInserted)) != 0)
	{
		return result;
	}

	if (bGroupInserted)
	{
		if ((result=tracker_save_groups()) != 0)
		{
			return result;
		}
	}

	if (g_use_storage_id)
	{
		pStorageIdInfo = fdfs_get_storage_id_by_ip(
				pClientInfo->pGroup->group_name, ip_addr);
		if (pStorageIdInfo == NULL)
		{
			logError("file: "__FILE__", line: %d, " \
				"get storage id info fail, group_name: %s, " \
				"storage ip: %s", __LINE__, \
				pClientInfo->pGroup->group_name, ip_addr);
			return ENOENT;
		}
		storage_id = pStorageIdInfo->id;
	}
	else
	{
		pStorageIdInfo = NULL;
		storage_id = ip_addr;
	}

	if (pClientInfo->pGroup->storage_port == 0)
	{
		pClientInfo->pGroup->storage_port = pJoinBody->storage_port;
		if ((result=tracker_save_groups()) != 0)
		{
			return result;
		}
	}
	else
	{
		if (pClientInfo->pGroup->storage_port !=  \
			pJoinBody->storage_port)
		{
			ppEnd = pClientInfo->pGroup->all_servers + \
				pClientInfo->pGroup->count;
			for (ppServer=pClientInfo->pGroup->all_servers; \
				ppServer<ppEnd; ppServer++)
			{
				if (strcmp((*ppServer)->id, storage_id) == 0)
				{
					(*ppServer)->storage_port = \
						pJoinBody->storage_port;
					break;
				}
			}

			for (ppServer=pClientInfo->pGroup->all_servers; \
				ppServer<ppEnd; ppServer++)
			{
				if ((*ppServer)->storage_port != \
					pJoinBody->storage_port)
				{
					break;
				}
			}
			if (ppServer == ppEnd)  //all servers are same, adjust
			{
				pClientInfo->pGroup->storage_port = \
						pJoinBody->storage_port;
				if ((result=tracker_save_groups()) != 0)
				{
					return result;
				}
			}
			else
			{
			logError("file: "__FILE__", line: %d, " \
				"client ip: %s, port %d is not same " \
				"in the group \"%s\", group port is %d", \
				__LINE__, ip_addr, pJoinBody->storage_port, \
				pJoinBody->group_name, \
				pClientInfo->pGroup->storage_port);
			return EINVAL;
			}
		}
	}

	if (pClientInfo->pGroup->storage_http_port == 0)
	{
		pClientInfo->pGroup->storage_http_port = \
			pJoinBody->storage_http_port;
		if ((result=tracker_save_groups()) != 0)
		{
			return result;
		}
	}
	else
	{
		if (pClientInfo->pGroup->storage_http_port !=  \
			pJoinBody->storage_http_port)
		{
			ppEnd = pClientInfo->pGroup->all_servers + \
				pClientInfo->pGroup->count;
			for (ppServer=pClientInfo->pGroup->all_servers; \
				ppServer<ppEnd; ppServer++)
			{
				if (strcmp((*ppServer)->id, storage_id) == 0)
				{
					(*ppServer)->storage_http_port = \
						pJoinBody->storage_http_port;
					break;
				}
			}

			for (ppServer=pClientInfo->pGroup->all_servers; \
				ppServer<ppEnd; ppServer++)
			{
				if ((*ppServer)->storage_http_port != \
					pJoinBody->storage_http_port)
				{
					break;
				}
			}
			if (ppServer == ppEnd)  //all servers are same, adjust
			{
				pClientInfo->pGroup->storage_http_port = \
					pJoinBody->storage_http_port;
				if ((result=tracker_save_groups()) != 0)
				{
					return result;
				}
			}
			else
			{
			logError("file: "__FILE__", line: %d, " \
				"client ip: %s, http port %d is not same " \
				"in the group \"%s\", group http port is %d", \
				__LINE__, ip_addr, \
				pJoinBody->storage_http_port, \
				pJoinBody->group_name, \
				pClientInfo->pGroup->storage_http_port);
#ifdef WITH_HTTPD
			return EINVAL;
#endif
			}
		}
	}
	
	if ((result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}
	pStorageServer = tracker_mem_get_storage(pClientInfo->pGroup, storage_id);
	if (pthread_mutex_unlock(&mem_thread_lock) != 0)
	{
		logError("file: "__FILE__", line: %d, "   \
			"call pthread_mutex_unlock fail", \
			__LINE__);
	}

	if (pStorageServer == NULL)
	{
		if (!pJoinBody->init_flag)
		{
			if (pJoinBody->status < 0 || \
			pJoinBody->status == FDFS_STORAGE_STATUS_DELETED || \
			pJoinBody->status == FDFS_STORAGE_STATUS_IP_CHANGED || \
			pJoinBody->status == FDFS_STORAGE_STATUS_NONE)
			{
				logError("file: "__FILE__", line: %d, " \
					"client ip: %s:%u, invalid storage " \
					"status %d, in the group \"%s\"", \
					__LINE__, ip_addr, \
					pJoinBody->storage_port, \
					pJoinBody->status, \
					pJoinBody->group_name);
				return EFAULT;
			}
		}
	}

	if ((result=tracker_mem_add_storage(pClientInfo, storage_id, ip_addr,
			bNeedSleep, true, &bStorageInserted)) != 0)
	{
		return result;
	}

	pStorageServer = pClientInfo->pStorage;
	pStorageServer->store_path_count = pJoinBody->store_path_count;
	pStorageServer->subdir_count_per_path = pJoinBody->subdir_count_per_path;
	pStorageServer->upload_priority = pJoinBody->upload_priority;
	pStorageServer->join_time = pJoinBody->join_time;
	pStorageServer->up_time = pJoinBody->up_time;
	snprintf(pStorageServer->version, sizeof(pStorageServer->version), \
		"%s", pJoinBody->version);
	snprintf(pStorageServer->domain_name, \
		sizeof(pStorageServer->domain_name), \
		"%s", pJoinBody->domain_name);
	pStorageServer->storage_port = pJoinBody->storage_port;
	pStorageServer->storage_http_port = pJoinBody->storage_http_port;

	if (pClientInfo->pGroup->store_path_count == 0)
	{
		pClientInfo->pGroup->store_path_count = \
				pJoinBody->store_path_count;
		if ((result=tracker_malloc_group_path_mbs( \
				pClientInfo->pGroup)) != 0)
		{
			return result;
		}
		if ((result=tracker_save_groups()) != 0)
		{
			return result;
		}
	}
	else
	{
		if (pClientInfo->pGroup->store_path_count !=
			pJoinBody->store_path_count)
		{
			ppEnd = pClientInfo->pGroup->all_servers +
				pClientInfo->pGroup->count;
			for (ppServer=pClientInfo->pGroup->all_servers;
				ppServer<ppEnd; ppServer++)
			{
				if ((*ppServer)->status == FDFS_STORAGE_STATUS_DELETED)
                {
                    continue;
                }

                if ((*ppServer)->store_path_count !=
                         pJoinBody->store_path_count)
				{
					break;
				}
			}

			if (ppServer == ppEnd)  //all servers are same, adjust
			{
				if ((result=tracker_realloc_group_path_mbs(
                                pClientInfo->pGroup, pJoinBody->
                                store_path_count)) != 0)
				{
					return result;
				}

				if ((result=tracker_save_groups()) != 0)
				{
					return result;
				}

				logDebug("file: "__FILE__", line: %d, " \
				"all storage server's store_path_count " \
				"are same, adjust to %d", \
				__LINE__, pJoinBody->store_path_count);
			}
			else if (pJoinBody->store_path_count < \
				pClientInfo->pGroup->store_path_count)
			{
				logError("file: "__FILE__", line: %d, " \
				"client ip: %s, store_path_count %d less " \
				"than that of the group \"%s\", " \
				"the group store_path_count is %d", \
				__LINE__, ip_addr, \
				pJoinBody->store_path_count, \
				pJoinBody->group_name, \
				pClientInfo->pGroup->store_path_count);
				return EINVAL;
			}
		}
	}

	if (pClientInfo->pGroup->subdir_count_per_path == 0)
	{
		pClientInfo->pGroup->subdir_count_per_path = \
				pJoinBody->subdir_count_per_path;
		if ((result=tracker_save_groups()) != 0)
		{
			return result;
		}
	}
	else
	{
		if (pClientInfo->pGroup->subdir_count_per_path != \
				pJoinBody->subdir_count_per_path)
		{
			ppEnd = pClientInfo->pGroup->all_servers + \
				pClientInfo->pGroup->count;
			for (ppServer=pClientInfo->pGroup->all_servers; \
				ppServer<ppEnd; ppServer++)
			{
				if ((*ppServer)->subdir_count_per_path != \
					pJoinBody->subdir_count_per_path)
				{
					break;
				}
			}

			if (ppServer == ppEnd)  //all servers are same, adjust
			{
				pClientInfo->pGroup->subdir_count_per_path = \
					pJoinBody->subdir_count_per_path;
				if ((result=tracker_save_groups()) != 0)
				{
					return result;
				}
			}
			else
			{
				logError("file: "__FILE__", line: %d, " \
				"client ip: %s, subdir_count_per_path %d is " \
				"not same in the group \"%s\", " \
				"group subdir_count_per_path is %d", \
				__LINE__, ip_addr, \
				pJoinBody->subdir_count_per_path, \
				pJoinBody->group_name,\
				pClientInfo->pGroup->subdir_count_per_path);

				return EINVAL;
			}
		}
	}

	if (bStorageInserted)
	{
		if ((!pJoinBody->init_flag) && pJoinBody->status > 0)
		{
			if (pJoinBody->status == FDFS_STORAGE_STATUS_ACTIVE)
			{
				pStorageServer->status = FDFS_STORAGE_STATUS_ONLINE;
			}
			else
			{
				pStorageServer->status = pJoinBody->status;
			}
		}

		if ((result=tracker_save_sys_files()) != 0)
		{
			return result;
		}
	}

	if (pStorageServer->status == FDFS_STORAGE_STATUS_OFFLINE || \
	    pStorageServer->status == FDFS_STORAGE_STATUS_RECOVERY)
	{
		pStorageServer->status = FDFS_STORAGE_STATUS_ONLINE;
	}
	else if (pStorageServer->status == FDFS_STORAGE_STATUS_INIT)
	{
	 	pStorageServer->changelog_offset = g_changelog_fsize;
	}

	logDebug("file: "__FILE__", line: %d, " \
		"storage server %s::%s join in, remain changelog bytes: " \
		"%"PRId64, __LINE__, \
		pClientInfo->pGroup->group_name, ip_addr, \
	 	g_changelog_fsize - pStorageServer->changelog_offset);

	return 0;
}

int tracker_mem_sync_storages(FDFSGroupInfo *pGroup, \
		FDFSStorageBrief *briefServers, const int server_count)
{
	int result;
	FDFSStorageBrief *pServer;
	FDFSStorageBrief *pEnd;
	FDFSStorageDetail target_storage;
	FDFSStorageDetail *pTargetStorage;
	FDFSStorageDetail **ppFound;

	if ((result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	result = 0;
	do
	{
		memset(&target_storage, 0, sizeof(target_storage));
		pEnd = briefServers + server_count;
		for (pServer=briefServers; pServer<pEnd; pServer++)
		{
			pServer->id[FDFS_STORAGE_ID_MAX_SIZE - 1] = '\0';
			pServer->ip_addr[IP_ADDRESS_SIZE - 1] = '\0';
			if (pServer->status == FDFS_STORAGE_STATUS_NONE)
			{
				continue;
			}

			memcpy(target_storage.id, pServer->id,
				FDFS_STORAGE_ID_MAX_SIZE);
			pTargetStorage = &target_storage;
			if ((ppFound=(FDFSStorageDetail **)bsearch(
				&pTargetStorage,
				pGroup->sorted_servers,
				pGroup->count,
				sizeof(FDFSStorageDetail *),
				tracker_mem_cmp_by_storage_id)) != NULL)
			{
				if ((*ppFound)->status == pServer->status \
				 || (*ppFound)->status == \
					FDFS_STORAGE_STATUS_INIT \
				 || (*ppFound)->status == \
					FDFS_STORAGE_STATUS_ONLINE \
				 || (*ppFound)->status == \
					FDFS_STORAGE_STATUS_ACTIVE
				 || (*ppFound)->status == \
					FDFS_STORAGE_STATUS_RECOVERY)
				{
					continue;
				}

                logWarning("file: "__FILE__", line: %d, "
                        "storage server: %s:%u, dest status: %d, "
                        "my status: %d, should change my status!",
                        __LINE__, FDFS_CURRENT_IP_ADDR(*ppFound),
                        (*ppFound)->storage_port,
                        pServer->status, (*ppFound)->status);

				if (pServer->status == \
					FDFS_STORAGE_STATUS_DELETED
				 || pServer->status == \
					FDFS_STORAGE_STATUS_IP_CHANGED)
				{
					(*ppFound)->status = pServer->status;
					pGroup->chg_count++;
					continue;
				}

				if (pServer->status > (*ppFound)->status)
				{
					(*ppFound)->status = pServer->status;
					pGroup->chg_count++;
				}
			}
			else if (pServer->status == FDFS_STORAGE_STATUS_DELETED
			   || pServer->status == FDFS_STORAGE_STATUS_IP_CHANGED)
			{
				//ignore deleted storage server
			}
            else if (pServer->status == FDFS_STORAGE_STATUS_ACTIVE
			   || pServer->status == FDFS_STORAGE_STATUS_ONLINE)
            {
				//ignore online or active storage server
            }
			else
			{
				FDFSStorageDetail *pStorageServer;
				bool bInserted;

				result = _tracker_mem_add_storage(pGroup,
					&pStorageServer, pServer->id,
					pServer->ip_addr, true, false,
					&bInserted);
				if (result == 0 && bInserted)
				{
					pStorageServer->status = pServer->status;
				}
			}
		}
	} while (0);

	if (pthread_mutex_unlock(&mem_thread_lock) != 0)
	{
		logError("file: "__FILE__", line: %d, "   \
			"call pthread_mutex_unlock fail", \
			__LINE__);
	}

	return result;
}

static void tracker_mem_find_store_server(FDFSGroupInfo *pGroup)
{
	if (pGroup->active_count == 0)
	{
		pGroup->pStoreServer = NULL;
		return;
	}

	if (g_groups.store_server == FDFS_STORE_SERVER_FIRST_BY_PRI)
	{
		FDFSStorageDetail **ppEnd;
		FDFSStorageDetail **ppServer;
		FDFSStorageDetail *pMinPriServer;

		pMinPriServer = *(pGroup->active_servers);
		ppEnd = pGroup->active_servers + pGroup->active_count;
		for (ppServer=pGroup->active_servers+1; ppServer<ppEnd; \
			ppServer++)
		{
			if ((*ppServer)->upload_priority < \
				pMinPriServer->upload_priority)
			{
				pMinPriServer = *ppServer;
			}
		}

		pGroup->pStoreServer = pMinPriServer;
	}
	else
	{
		pGroup->pStoreServer = *(pGroup->active_servers);
	}
}

static int _storage_get_trunk_binlog_size(
	ConnectionInfo *pStorageServer, int64_t *file_size)
{
	char out_buff[sizeof(TrackerHeader)];
	char in_buff[8];
	TrackerHeader *pHeader;
	char *pInBuff;
	int64_t in_bytes;
	int result;

	pHeader = (TrackerHeader *)out_buff;
	memset(out_buff, 0, sizeof(out_buff));
	pHeader->cmd = STORAGE_PROTO_CMD_TRUNK_GET_BINLOG_SIZE;
	if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
			sizeof(out_buff), SF_G_NETWORK_TIMEOUT)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"storage server %s:%u, send data fail, " \
			"errno: %d, error info: %s.", \
			__LINE__, pStorageServer->ip_addr, \
			pStorageServer->port, \
			result, STRERROR(result));
		return result;
	}

	pInBuff = in_buff;
	if ((result=fdfs_recv_response(pStorageServer, \
		&pInBuff, sizeof(in_buff), &in_bytes)) != 0)
	{
        logError("file: "__FILE__", line: %d, "
                "fdfs_recv_response fail, result: %d",
                __LINE__, result);
		return result;
	}

	if (in_bytes != sizeof(in_buff))
	{
		logError("file: "__FILE__", line: %d, " \
			"storage server %s:%u, recv body length: " \
			"%"PRId64" != %d",  \
			__LINE__, pStorageServer->ip_addr, \
			pStorageServer->port, in_bytes, (int)sizeof(in_buff));
		return EINVAL;
	}

	*file_size = buff2long(in_buff);
	return 0;
}

static int tracker_mem_get_trunk_binlog_size(
	const char *storage_ip, const int port, int64_t *file_size)
{
	ConnectionInfo storage_server;
	ConnectionInfo *conn;
	int result;

	*file_size = 0;
	strcpy(storage_server.ip_addr, storage_ip);
	storage_server.port = port;
	storage_server.sock = -1;
	if ((conn=tracker_make_connection(&storage_server, &result)) == NULL)
	{
		return result;
	}

	result = _storage_get_trunk_binlog_size(conn, file_size);
	tracker_close_connection_ex(conn, result != 0);


	logDebug("file: "__FILE__", line: %d, " \
		"storage %s:%u, trunk binlog file size: %"PRId64, \
		__LINE__, storage_server.ip_addr, storage_server.port, \
		*file_size);
	return result;
}

static int tracker_write_to_trunk_change_log(FDFSGroupInfo *pGroup, \
		FDFSStorageDetail *pLastTrunkServer)
{
	char full_filename[MAX_PATH_SIZE];
	char buff[256];
	int fd;
	int len;
	struct tm tm;
	time_t current_time;
	FDFSStorageDetail *pLastTrunk;

	tracker_mem_file_lock();

	snprintf(full_filename, sizeof(full_filename), "%s/logs/%s", \
		SF_G_BASE_PATH_STR, TRUNK_SERVER_CHANGELOG_FILENAME);
	if ((fd=open(full_filename, O_WRONLY | O_CREAT | O_APPEND, 0644)) < 0)
	{
		tracker_mem_file_unlock();
		logError("file: "__FILE__", line: %d, " \
			"open \"%s\" fail, errno: %d, error info: %s", \
			__LINE__, full_filename, errno, STRERROR(errno));
		return errno != 0 ? errno : ENOENT;
	}

	current_time = g_current_time;
	localtime_r(&current_time, &tm);
	len = sprintf(buff, "[%04d-%02d-%02d %02d:%02d:%02d] %s ", \
		tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \
		tm.tm_hour, tm.tm_min, tm.tm_sec, pGroup->group_name);

	pLastTrunk = pLastTrunkServer;
	if (pLastTrunk == NULL && *(pGroup->last_trunk_server_id) != '\0')
	{
		pLastTrunk = tracker_mem_get_storage(pGroup, \
				pGroup->last_trunk_server_id);
	}
	if (g_use_storage_id)
	{
		if (pLastTrunk == NULL)
		{
			len += sprintf(buff + len, " %s/%s  => ",
				*(pGroup->last_trunk_server_id) == '\0' ?
				  "-" : pGroup->last_trunk_server_id, "-");
		}
		else
		{
			len += sprintf(buff + len, " %s/%s  => ",
				pLastTrunk->id, FDFS_CURRENT_IP_ADDR(pLastTrunk));
		}

		if (pGroup->pTrunkServer == NULL)
		{
			len += sprintf(buff + len, " %s/%s\n", "-", "-");
		}
		else
		{
			len += sprintf(buff + len, " %s/%s\n",
				pGroup->pTrunkServer->id,
                FDFS_CURRENT_IP_ADDR(pGroup->pTrunkServer));
		}
	}
	else
	{
		if (pLastTrunk == NULL)
		{
			len += sprintf(buff + len, " %s  => ",
				*(pGroup->last_trunk_server_id) == '\0' ?
				  "-" : pGroup->last_trunk_server_id);
		}
		else
		{
			len += sprintf(buff + len, " %s  => ",
                    FDFS_CURRENT_IP_ADDR(pLastTrunk));
		}

		if (pGroup->pTrunkServer == NULL)
		{
			len += sprintf(buff + len, " %s\n", "-");
		}
		else
		{
			len += sprintf(buff + len, " %s\n",
                    FDFS_CURRENT_IP_ADDR(pGroup->pTrunkServer));
		}
	}

	if (fc_safe_write(fd, buff, len) != len)
	{
		logError("file: "__FILE__", line: %d, " \
			"write to file \"%s\" fail, " \
			"errno: %d, error info: %s", \
			__LINE__, full_filename, \
			errno, STRERROR(errno));
	}

	close(fd);
	tracker_mem_file_unlock();

	return 0;
}

static int tracker_set_trunk_server_and_log(FDFSGroupInfo *pGroup, \
		FDFSStorageDetail *pNewTrunkServer)
{
	FDFSStorageDetail *pLastTrunkServer;

	pLastTrunkServer = pGroup->pTrunkServer;
	pGroup->pTrunkServer = pNewTrunkServer;
	if (pNewTrunkServer == NULL || strcmp(pNewTrunkServer->id, \
					pGroup->last_trunk_server_id) != 0)
	{
		int result;
		result = tracker_write_to_trunk_change_log(pGroup, \
				pLastTrunkServer);
		if (pNewTrunkServer == NULL)
		{
			*(pGroup->last_trunk_server_id) = '\0';
		}
		else
		{
			strcpy(pGroup->last_trunk_server_id, \
				pNewTrunkServer->id);
		}
		return result;
	}

	return 0;
}

static int tracker_mem_do_set_trunk_server(FDFSGroupInfo *pGroup, 
	FDFSStorageDetail *pTrunkServer, const bool save)
{
	int result;

	if (*(pGroup->last_trunk_server_id) != '\0' && 
	    strcmp(pTrunkServer->id, pGroup->last_trunk_server_id) != 0)
	{
		if ((result=fdfs_deal_no_body_cmd_ex(
                        FDFS_CURRENT_IP_ADDR(pTrunkServer),
                        pGroup->storage_port, 
                        STORAGE_PROTO_CMD_TRUNK_DELETE_BINLOG_MARKS)) != 0)
		{
            logError("file: "__FILE__", line: %d, "
                    "fdfs_deal_no_body_cmd_ex fail, result: %d",
                    __LINE__, result);
			return result;
		}
	}

	tracker_set_trunk_server_and_log(pGroup, pTrunkServer);
	pGroup->trunk_chg_count++;
	g_trunk_server_chg_count++;

	logInfo("file: "__FILE__", line: %d, "
		"group: %s, trunk server set to %s(%s:%u)", __LINE__,
		pGroup->group_name, pGroup->pTrunkServer->id,
		FDFS_CURRENT_IP_ADDR(pGroup->pTrunkServer),
        pGroup->storage_port);
	if (save)
	{
		return tracker_save_groups();
	}

	return 0;
}

static int tracker_mem_find_trunk_server(FDFSGroupInfo *pGroup, 
		const bool save)
{
	FDFSStorageDetail *pStoreServer;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;
	int result;
	int64_t file_size;
	int64_t max_file_size;

	pStoreServer = pGroup->pStoreServer;
	if (pStoreServer == NULL)
	{
		return ENOENT;
	}

	result = tracker_mem_get_trunk_binlog_size(
            FDFS_CURRENT_IP_ADDR(pStoreServer),
            pGroup->storage_port, &max_file_size);
	if (result != 0)
	{
		return result;
	}

	ppServerEnd = pGroup->active_servers + pGroup->active_count;
	for (ppServer=pGroup->active_servers; ppServer<ppServerEnd; ppServer++)
	{
		if (*ppServer == pStoreServer)
		{
			continue;
		}

		result = tracker_mem_get_trunk_binlog_size(
                FDFS_CURRENT_IP_ADDR(*ppServer),
                pGroup->storage_port, &file_size);
		if (result != 0)
		{
			continue;
		}

		if (file_size > max_file_size)
		{
			pStoreServer = *ppServer;
		}
	}

	return tracker_mem_do_set_trunk_server(pGroup, pStoreServer, save);
}

const FDFSStorageDetail *tracker_mem_set_trunk_server( \
	FDFSGroupInfo *pGroup, const char *pStroageId, int *result)
{
	FDFSStorageDetail *pServer;
	FDFSStorageDetail *pTrunkServer;

	if (!(g_if_leader_self && g_if_use_trunk_file))
	{
		*result = EOPNOTSUPP;
		return NULL;
	}

	pTrunkServer = pGroup->pTrunkServer;
	if (pStroageId == NULL || *pStroageId == '\0')
	{
		if (pTrunkServer != NULL && pTrunkServer-> \
			status == FDFS_STORAGE_STATUS_ACTIVE)
		{
			*result = 0;
			return pTrunkServer;
		}

		*result = tracker_mem_find_trunk_server(pGroup, true);
		if (*result != 0)
		{
			return NULL;
		}
		return pGroup->pTrunkServer;
	}

	if (pTrunkServer != NULL && pTrunkServer->status == \
			FDFS_STORAGE_STATUS_ACTIVE)
	{
		if (strcmp(pStroageId, pTrunkServer->id) == 0)
		{
			*result = EALREADY;
		}
		else
		{
			*result = EEXIST;
		}
		return pTrunkServer;
	}

	pServer = tracker_mem_get_storage(pGroup, pStroageId);
	if (pServer == NULL)
	{
		*result = ENOENT;
		return NULL;
	}

	if (pServer->status != FDFS_STORAGE_STATUS_ACTIVE)
	{
		*result = ENONET;
		return NULL;
	}

	*result = tracker_mem_do_set_trunk_server(pGroup, \
			pServer, true);
	return *result == 0 ? pGroup->pTrunkServer : NULL;
}

int tracker_mem_deactive_store_server(FDFSGroupInfo *pGroup,
			FDFSStorageDetail *pTargetServer) 
{
	int result;
	FDFSStorageDetail **ppStorageServer;
	FDFSStorageDetail **ppEnd;
	FDFSStorageDetail **ppServer;

	if ((result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	ppStorageServer = (FDFSStorageDetail **)bsearch( \
			&pTargetServer, \
			pGroup->active_servers, \
			pGroup->active_count,   \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer != NULL)
	{
		(*ppStorageServer)->chg_count = 0;
		(*ppStorageServer)->trunk_chg_count = 0;

		ppEnd = pGroup->active_servers + pGroup->active_count - 1;
		for (ppServer=ppStorageServer; ppServer<ppEnd; ppServer++)
		{
			*ppServer = *(ppServer+1);
		}

		pGroup->active_count--;
		pGroup->chg_count++;

#ifdef WITH_HTTPD
		if (g_http_check_interval <= 0)
		{
			pGroup->http_server_count = pGroup->active_count;
		}
#endif
	}

	tracker_mem_find_store_server(pGroup);

	if ((result=pthread_mutex_unlock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_unlock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	return 0;
}

int tracker_mem_active_store_server(FDFSGroupInfo *pGroup, \
			FDFSStorageDetail *pTargetServer) 
{
	int result;
	FDFSStorageDetail **ppStorageServer;

	if ((pTargetServer->status == FDFS_STORAGE_STATUS_WAIT_SYNC) || \
		(pTargetServer->status == FDFS_STORAGE_STATUS_SYNCING) || \
		(pTargetServer->status == FDFS_STORAGE_STATUS_IP_CHANGED) || \
		(pTargetServer->status == FDFS_STORAGE_STATUS_INIT))
	{
		return 0;
	}

	/*
	if (pTargetServer->status == FDFS_STORAGE_STATUS_DELETED)
	{
		logError("file: "__FILE__", line: %d, " \
			"storage ip: %s already deleted, you can " \
			"restart the tracker servers to reset.", \
			__LINE__, pTargetServer->ip_addr);
		return EAGAIN;
	}
	*/

	pTargetServer->status = FDFS_STORAGE_STATUS_ACTIVE;

	ppStorageServer = (FDFSStorageDetail **)bsearch(&pTargetServer, \
			pGroup->active_servers, \
			pGroup->active_count,   \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer != NULL)
	{
		return 0;
	}

	if ((result=pthread_mutex_lock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_lock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	ppStorageServer = (FDFSStorageDetail **)bsearch(&pTargetServer, \
			pGroup->active_servers, \
			pGroup->active_count,   \
			sizeof(FDFSStorageDetail *), \
			tracker_mem_cmp_by_storage_id);
	if (ppStorageServer == NULL)
	{
		tracker_mem_insert_into_sorted_servers( \
			pTargetServer, pGroup->active_servers, \
			pGroup->active_count);
		pGroup->active_count++;
		pGroup->chg_count++;

#ifdef WITH_HTTPD
		if (g_http_check_interval <= 0)
		{
			pGroup->http_server_count = pGroup->active_count;
		}
#endif

		if (g_use_storage_id)
		{
			logDebug("file: "__FILE__", line: %d, "
				"storage server %s::%s(%s) now active",
				__LINE__, pGroup->group_name,
				pTargetServer->id, FDFS_CURRENT_IP_ADDR(pTargetServer));
		}
		else
		{
			logDebug("file: "__FILE__", line: %d, "
				"storage server %s::%s now active",
				__LINE__, pGroup->group_name,
				FDFS_CURRENT_IP_ADDR(pTargetServer));
		}
	}

	tracker_mem_find_store_server(pGroup);
	if (g_if_leader_self && g_if_use_trunk_file && \
		pGroup->pTrunkServer == NULL)
	{
		tracker_mem_find_trunk_server(pGroup, true);
	}

	if ((result=pthread_mutex_unlock(&mem_thread_lock)) != 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call pthread_mutex_unlock fail, " \
			"errno: %d, error info: %s", \
			__LINE__, result, STRERROR(result));
		return result;
	}

	return 0;
}

void tracker_mem_find_trunk_servers()
{
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;

	if (!(g_if_leader_self && g_if_use_trunk_file))
	{
		return;
	}

	pthread_mutex_lock(&mem_thread_lock);
	ppGroupEnd = g_groups.groups + g_groups.count;
	for (ppGroup=g_groups.groups; ppGroup<ppGroupEnd; ppGroup++)
	{
		if ((*ppGroup)->pTrunkServer == NULL)
		{
			tracker_mem_find_trunk_server(*ppGroup, true);
		}
	}
	g_trunk_server_chg_count++;
	pthread_mutex_unlock(&mem_thread_lock);
}

int tracker_mem_offline_store_server(FDFSGroupInfo *pGroup, \
			FDFSStorageDetail *pStorage)
{
	pStorage->up_time = 0;
	if ((pStorage->status == FDFS_STORAGE_STATUS_WAIT_SYNC) || \
		(pStorage->status == FDFS_STORAGE_STATUS_SYNCING) || \
		(pStorage->status == FDFS_STORAGE_STATUS_INIT) || \
		(pStorage->status == FDFS_STORAGE_STATUS_DELETED) || \
		(pStorage->status == FDFS_STORAGE_STATUS_IP_CHANGED) || \
		(pStorage->status == FDFS_STORAGE_STATUS_RECOVERY))
	{
		return 0;
	}

	if (g_use_storage_id)
	{
		logDebug("file: "__FILE__", line: %d, "
			"storage server %s::%s (%s) offline",
			__LINE__, pGroup->group_name,
			pStorage->id, FDFS_CURRENT_IP_ADDR(pStorage));
	}
	else
	{
		logDebug("file: "__FILE__", line: %d, "
			"storage server %s::%s offline",
			__LINE__, pGroup->group_name,
			FDFS_CURRENT_IP_ADDR(pStorage));
	}

	pStorage->status = FDFS_STORAGE_STATUS_OFFLINE;
	return tracker_mem_deactive_store_server(pGroup, pStorage);
}

FDFSStorageDetail *tracker_get_writable_storage(FDFSGroupInfo *pStoreGroup)
{
	int write_server_index;
	if (g_groups.store_server == FDFS_STORE_SERVER_ROUND_ROBIN)
	{
		write_server_index = pStoreGroup->current_write_server++;
		if (pStoreGroup->current_write_server >= \
				pStoreGroup->active_count)
		{
			pStoreGroup->current_write_server = 0;
		}

		if (write_server_index >= pStoreGroup->active_count)
		{
			write_server_index = 0;
		}
		return  *(pStoreGroup->active_servers + write_server_index);
	}
	else //use the first server
	{
		return pStoreGroup->pStoreServer;
	}
}

int tracker_mem_get_storage_by_filename(const byte cmd,FDFS_DOWNLOAD_TYPE_PARAM\
	const char *group_name, const char *filename, const int filename_len, \
	FDFSGroupInfo **ppGroup, FDFSStorageDetail **ppStoreServers, \
	int *server_count)
{
	char szIpAddr[IP_ADDRESS_SIZE];
	char storage_id[FDFS_STORAGE_ID_MAX_SIZE];
	FDFSStorageDetail *pStoreSrcServer;
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;
	FDFSStorageDetail *pGroupStoreServer;
	int file_timestamp;
	int storage_ip;
	int read_server_index;
	int cmp_res;
	struct in_addr ip_addr;
	time_t current_time;
	bool bNormalFile;

	*server_count = 0;
	*ppGroup = tracker_mem_get_group(group_name);
	if (*ppGroup == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"invalid group_name: %s", \
			__LINE__, group_name);
		return ENOENT;
	}

#ifdef WITH_HTTPD
	if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
	{
		if ((*ppGroup)->active_count == 0)
		{
			return ENOENT;
		}
	}
	else
	{
		if ((*ppGroup)->http_server_count == 0)
		{
			return ENOENT;
		}
	}
#else
	if ((*ppGroup)->active_count == 0)
	{
		return ENOENT;
	}
#endif

	pGroupStoreServer = (*ppGroup)->pStoreServer;
	if (pGroupStoreServer == NULL)
	{
		return ENOENT;
	}

	//file generated by version < v1.12
	if (filename_len < 32 + (FDFS_FILE_EXT_NAME_MAX_LEN + 1))
	{
		storage_ip = INADDR_NONE;
		*storage_id = '\0';
		file_timestamp = 0;
		bNormalFile = true;
	}
	else //file generated by version >= v1.12
	{
		int64_t file_size;

		char name_buff[64];
		int decoded_len;

		base64_decode_auto(&g_base64_context, (char *)filename + \
			FDFS_LOGIC_FILE_PATH_LEN, FDFS_FILENAME_BASE64_LENGTH, \
			name_buff, &decoded_len);
		storage_ip = ntohl(buff2int(name_buff));
		file_timestamp = buff2int(name_buff+sizeof(int));
		file_size = buff2long(name_buff + sizeof (int) * 2);

		if (fdfs_get_server_id_type(storage_ip) == FDFS_ID_TYPE_SERVER_ID)
		{
			sprintf(storage_id, "%d", storage_ip);
		}
		else
		{
			*storage_id = '\0';
		}

		bNormalFile = !(IS_SLAVE_FILE(filename_len, file_size) || \
				IS_APPENDER_FILE(file_size));
	}

	/*
	//logInfo("cmd=%d, bNormalFile=%d, storage_ip=%d, storage_id=%s, "
    "file_timestamp=%d\n", cmd, bNormalFile, storage_ip, 
    storage_id, file_timestamp);
	*/

	memset(szIpAddr, 0, sizeof(szIpAddr));
	if (cmd == TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE)
	{
		if (g_groups.download_server == \
				FDFS_DOWNLOAD_SERVER_SOURCE_FIRST)
		{
#ifdef WITH_HTTPD
			if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
			{
				if (*storage_id != '\0')
				{
					pStoreSrcServer=tracker_mem_get_active_storage_by_id(\
						*ppGroup, storage_id);
				}
				else
				{
					memset(&ip_addr, 0, sizeof(ip_addr));
					ip_addr.s_addr = storage_ip;
					pStoreSrcServer=tracker_mem_get_active_storage_by_ip( \
						*ppGroup, inet_ntop(AF_INET, &ip_addr, \
						szIpAddr, sizeof(szIpAddr)));
				}
			}
			else
			{
				if (*storage_id != '\0')
				{
				pStoreSrcServer=tracker_mem_get_active_http_server_by_id( \
				*ppGroup, storage_id);
				}
				else
				{
				memset(&ip_addr, 0, sizeof(ip_addr));
				ip_addr.s_addr = storage_ip;
				pStoreSrcServer=tracker_mem_get_active_http_server_by_ip( \
				*ppGroup, inet_ntop(AF_INET, &ip_addr, \
				szIpAddr, sizeof(szIpAddr)));
				}
			}
#else
			if (*storage_id != '\0')
			{
			pStoreSrcServer=tracker_mem_get_active_storage_by_id(\
				*ppGroup, storage_id);
			}
			else
			{
			memset(&ip_addr, 0, sizeof(ip_addr));
			ip_addr.s_addr = storage_ip;
			pStoreSrcServer=tracker_mem_get_active_storage_by_ip(\
				*ppGroup, inet_ntop(AF_INET, &ip_addr, \
				szIpAddr, sizeof(szIpAddr)));
			}
#endif
			if (pStoreSrcServer != NULL)
			{
				ppStoreServers[(*server_count)++] = \
						 pStoreSrcServer;
				return 0;
			}
		}

		//round robin
#ifdef WITH_HTTPD
		if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
		{
		read_server_index = (*ppGroup)->current_read_server;
		if (read_server_index >= (*ppGroup)->active_count)
		{
			read_server_index = 0;
		}
		ppStoreServers[(*server_count)++]=*((*ppGroup)->active_servers \
				+ read_server_index);
		}
		else
		{
		read_server_index = (*ppGroup)->current_http_server;
		if (read_server_index >= (*ppGroup)->http_server_count)
		{
			read_server_index = 0;
		}
		ppStoreServers[(*server_count)++]=*((*ppGroup)->http_servers \
				+ read_server_index);
		}
#else
		read_server_index = (*ppGroup)->current_read_server;
		if (read_server_index >= (*ppGroup)->active_count)
		{
			read_server_index = 0;
		}
		ppStoreServers[(*server_count)++]=*((*ppGroup)->active_servers \
				+ read_server_index);
#endif
		/*
		//logInfo("filename=%s, storage server ip=%s, " \
		"file_timestamp=%d, last_synced_timestamp=%d\n", 
		filename, ppStoreServers[0]->ip_addr, file_timestamp, \
		(int)ppStoreServers[0]->stat.last_synced_timestamp);
		*/

		do
		{
			if (bNormalFile)
			{
			current_time = g_current_time;
			if ((file_timestamp < current_time - \
				g_storage_sync_file_max_delay) || \
			(ppStoreServers[0]->stat.last_synced_timestamp > \
				file_timestamp) || \
			(ppStoreServers[0]->stat.last_synced_timestamp + 1 >= \
			 file_timestamp && current_time - file_timestamp > \
				g_storage_sync_file_max_time)\
			|| (storage_ip == INADDR_NONE \
			&& g_groups.store_server == FDFS_STORE_SERVER_ROUND_ROBIN))
			{
				break;
			}

			if (storage_ip == INADDR_NONE)
			{
#ifdef WITH_HTTPD
				if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
				{
				ppStoreServers[0] = pGroupStoreServer;
				break;
				}
				else
				{
				pStoreSrcServer=tracker_mem_get_active_storage_by_id(
					*ppGroup, pGroupStoreServer->id);
				if (pStoreSrcServer != NULL)
				{
					ppStoreServers[0] = pStoreSrcServer;
					break;
				}
				}
#else
				ppStoreServers[0] = pGroupStoreServer;
				break;
#endif
			}
			}

      memset(&ip_addr, 0, sizeof(ip_addr));
      if (*storage_id != '\0')
      {
          cmp_res = strcmp(storage_id, ppStoreServers[0]->id);
      }
      else
      {
          ip_addr.s_addr = storage_ip;
          inet_ntop(AF_INET, &ip_addr, szIpAddr, sizeof(szIpAddr));
          cmp_res = strcmp(szIpAddr, FDFS_CURRENT_IP_ADDR(ppStoreServers[0]));
      }
      if (cmp_res == 0)
			{
#ifdef WITH_HTTPD
				if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
				{
					break;
				}
				else //http
				{
          if (*storage_id != '\0')
          {
            if (tracker_mem_get_active_http_server_by_id(
	  					*ppGroup, storage_id) != NULL)
		  			{
			  			break;
				  	}
          }
          else if (tracker_mem_get_active_http_server_by_ip(
						*ppGroup, szIpAddr) != NULL)
					{
					  break;
					}
				}
#else
				break;
#endif
			}

			if (g_groups.download_server == \
					FDFS_DOWNLOAD_SERVER_ROUND_ROBIN)
			{  //avoid search again
#ifdef WITH_HTTPD
				if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
				{
        if (*storage_id != '\0')
        {
				pStoreSrcServer=tracker_mem_get_active_storage_by_id(
					*ppGroup, storage_id);
        }
        else
        {
				pStoreSrcServer=tracker_mem_get_active_storage_by_ip(
					*ppGroup, szIpAddr);
        }
				}
				else //http
				{
        if (*storage_id != '\0')
        {
				pStoreSrcServer=tracker_mem_get_active_http_server_by_id(
					*ppGroup, storage_id);
        }
        else
        {
				pStoreSrcServer=tracker_mem_get_active_http_server_by_ip(
					*ppGroup, szIpAddr);
        }
				}
#else
        if (*storage_id != '\0')
        {
				pStoreSrcServer=tracker_mem_get_active_storage_by_id(
					*ppGroup, storage_id);
        }
        else
        {
				pStoreSrcServer=tracker_mem_get_active_storage_by_ip(
					*ppGroup, szIpAddr);
        }
#endif
				if (pStoreSrcServer != NULL)
				{
					ppStoreServers[0] = pStoreSrcServer;
					break;
				}
			}

			if (g_groups.store_server != \
				FDFS_STORE_SERVER_ROUND_ROBIN)
			{
#ifdef WITH_HTTPD
				if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
				{
				ppStoreServers[0] = pGroupStoreServer;
				}
				else  //http
				{
				pStoreSrcServer=tracker_mem_get_active_http_server_by_id(
					*ppGroup, pGroupStoreServer->id);
				if (pStoreSrcServer != NULL)
				{
				ppStoreServers[0] = pStoreSrcServer;
				}
				else
				{
				ppStoreServers[0] = *((*ppGroup)->http_servers);
				}
				}
#else
				ppStoreServers[0] = pGroupStoreServer;
#endif
				break;
			}
		} while (0);

#ifdef WITH_HTTPD
		if (download_type == FDFS_DOWNLOAD_TYPE_TCP)
		{
		(*ppGroup)->current_read_server++;
		if ((*ppGroup)->current_read_server >= (*ppGroup)->active_count)
		{
			(*ppGroup)->current_read_server = 0;
		}
		}
		else  //http
		{
			(*ppGroup)->current_http_server++;
			if ((*ppGroup)->current_http_server >= \
				(*ppGroup)->http_server_count)
			{
				(*ppGroup)->current_http_server = 0;
			}
		}
#else
		(*ppGroup)->current_read_server++;
		if ((*ppGroup)->current_read_server >= (*ppGroup)->active_count)
		{
			(*ppGroup)->current_read_server = 0;
		}
#endif
	}
	else if (cmd == TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE)
	{
		if (storage_ip != INADDR_NONE)
		{
      if (*storage_id != '\0')
      {
			pStoreSrcServer=tracker_mem_get_active_storage_by_id(\
					*ppGroup, storage_id);
      }
      else
      {
			memset(&ip_addr, 0, sizeof(ip_addr));
			ip_addr.s_addr = storage_ip;
			pStoreSrcServer=tracker_mem_get_active_storage_by_ip(\
					*ppGroup, inet_ntop(AF_INET, &ip_addr, \
					szIpAddr, sizeof(szIpAddr)));
      }

			if (pStoreSrcServer != NULL)
			{
				ppStoreServers[(*server_count)++] = \
							 pStoreSrcServer;
				return 0;
			}
		}

		ppStoreServers[0] = tracker_get_writable_storage(*ppGroup);
		*server_count = ppStoreServers[0] != NULL ? 1 : 0;
	}
	else //TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL
	{
		memset(szIpAddr, 0, sizeof(szIpAddr));
		if (storage_ip != INADDR_NONE)
		{
			memset(&ip_addr, 0, sizeof(ip_addr));
			ip_addr.s_addr = storage_ip;
			inet_ntop(AF_INET, &ip_addr,szIpAddr,sizeof(szIpAddr));
		}

		if (bNormalFile)
		{
		current_time = g_current_time;
		ppServerEnd = (*ppGroup)->active_servers + \
				(*ppGroup)->active_count;

		for (ppServer=(*ppGroup)->active_servers; \
				ppServer<ppServerEnd; ppServer++)
		{
			if ((file_timestamp < current_time -
				g_storage_sync_file_max_delay) ||
			((*ppServer)->stat.last_synced_timestamp >
				file_timestamp) ||
			((*ppServer)->stat.last_synced_timestamp + 1 >=
			 file_timestamp && current_time - file_timestamp >
				g_storage_sync_file_max_time)
				|| (storage_ip == INADDR_NONE
					&& g_groups.store_server ==
					FDFS_STORE_SERVER_ROUND_ROBIN)
				|| strcmp(FDFS_CURRENT_IP_ADDR(*ppServer), szIpAddr) == 0)
			{
				ppStoreServers[(*server_count)++] = *ppServer;
			}
		}
		}
		else
		{
      if (*storage_id != '\0')
      {
			pStoreSrcServer = tracker_mem_get_active_storage_by_id( \
						*ppGroup, storage_id);
      }
      else
      {
			pStoreSrcServer = tracker_mem_get_active_storage_by_ip( \
						*ppGroup, szIpAddr);
      }

			if (pStoreSrcServer != NULL)
			{
				ppStoreServers[(*server_count)++] = \
						pStoreSrcServer;
			}
		}

		if (*server_count == 0)
		{
			ppStoreServers[(*server_count)++] = pGroupStoreServer;
		}
	}

	return *server_count > 0 ? 0 : ENOENT;
}

int tracker_mem_check_alive(void *arg)
{
	FDFSStorageDetail **ppServer;
	FDFSStorageDetail **ppServerEnd;
	FDFSGroupInfo **ppGroup;
	FDFSGroupInfo **ppGroupEnd;
	FDFSStorageDetail *deactiveServers[FDFS_MAX_SERVERS_EACH_GROUP];
	int deactiveCount;
	time_t current_time;

	current_time = g_current_time;
	ppGroupEnd = g_groups.groups + g_groups.count;
	for (ppGroup=g_groups.groups; ppGroup<ppGroupEnd; ppGroup++)
	{
	deactiveCount = 0;
	ppServerEnd = (*ppGroup)->active_servers + (*ppGroup)->active_count;
	for (ppServer=(*ppGroup)->active_servers; ppServer<ppServerEnd; ppServer++)
	{
		if (current_time - (*ppServer)->stat.last_heart_beat_time > \
			g_check_active_interval)
		{
			deactiveServers[deactiveCount] = *ppServer;
			deactiveCount++;
			if (deactiveCount >= FDFS_MAX_SERVERS_EACH_GROUP)
			{
				break;
			}
		}
	}

	if (deactiveCount == 0)
	{
		continue;
	}

	ppServerEnd = deactiveServers + deactiveCount;
	for (ppServer=deactiveServers; ppServer<ppServerEnd; ppServer++)
	{
		(*ppServer)->status = FDFS_STORAGE_STATUS_OFFLINE;
		tracker_mem_deactive_store_server(*ppGroup, *ppServer);
		if (g_use_storage_id)
		{
			logInfo("file: "__FILE__", line: %d, "
				"storage server %s(%s:%u) idle too long, "
				"status change to offline!", __LINE__,
				(*ppServer)->id, FDFS_CURRENT_IP_ADDR(*ppServer),
				(*ppGroup)->storage_port);
		}
		else
		{
			logInfo("file: "__FILE__", line: %d, "
				"storage server %s:%u idle too long, "
				"status change to offline!", __LINE__,
				FDFS_CURRENT_IP_ADDR(*ppServer),
                (*ppGroup)->storage_port);
		}
	}
	}

	if ((!g_if_leader_self) || (!g_if_use_trunk_file))
	{
		return 0;
	}

	for (ppGroup=g_groups.groups; ppGroup<ppGroupEnd; ppGroup++)
	{
	if ((*ppGroup)->pTrunkServer != NULL)
	{
		int check_trunk_times;
		int check_trunk_interval;
		int last_beat_interval;

		if (current_time - (*ppGroup)->pTrunkServer->up_time <= \
			10 * g_check_active_interval)
		{
			if (g_trunk_init_check_occupying)
			{
				check_trunk_times = 5;
			}
			else
			{
				check_trunk_times = 3;
			}

			if (g_trunk_init_reload_from_binlog)
			{
				check_trunk_times *= 2;
			}
		}
		else
		{
			check_trunk_times = 2;
		}

		last_beat_interval = current_time - (*ppGroup)-> \
				pTrunkServer->stat.last_heart_beat_time;
		check_trunk_interval = check_trunk_times * \
					g_check_active_interval;
	    	if (last_beat_interval > check_trunk_interval)
		{
			logInfo("file: "__FILE__", line: %d, "
				"trunk server %s(%s:%u) offline because idle "
				"time: %d s > threshold: %d s, should "
				"re-select trunk server", __LINE__,
				(*ppGroup)->pTrunkServer->id,
				FDFS_CURRENT_IP_ADDR((*ppGroup)->pTrunkServer),
				(*ppGroup)->storage_port, last_beat_interval,
				check_trunk_interval);

			(*ppGroup)->pTrunkServer = NULL;
			tracker_mem_find_trunk_server(*ppGroup, false);
			if ((*ppGroup)->pTrunkServer == NULL)
			{
				tracker_set_trunk_server_and_log(*ppGroup, NULL);
			}

			(*ppGroup)->trunk_chg_count++;
			g_trunk_server_chg_count++;

			tracker_save_groups();
		}
	}
	else
	{
		tracker_mem_find_trunk_server(*ppGroup, true);
	}
	}

	return 0;
}

int tracker_mem_get_storage_index(FDFSGroupInfo *pGroup, \
		FDFSStorageDetail *pStorage)
{
	FDFSStorageDetail **ppStorage;
	FDFSStorageDetail **ppEnd;

	ppEnd = pGroup->all_servers + pGroup->count;
	for (ppStorage=pGroup->all_servers; ppStorage<ppEnd; ppStorage++)
	{
		if (*ppStorage == pStorage)
		{
			return ppStorage - pGroup->all_servers;
		}
	}

	logError("file: "__FILE__", line: %d, "
		"get index of storage %s fail!!!",
		__LINE__, FDFS_CURRENT_IP_ADDR(pStorage));

	return -1;
}

