commit 0dd3d17c92b94a39d8770cfe7dfd04ca20c9ffec Author: yuqing Date: Thu May 29 09:21:50 2014 +0800 update source code from FastDFS V5.02 diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..8b83ddc --- /dev/null +++ b/HISTORY @@ -0,0 +1,27 @@ + +Version 1.06 2014-05-28 + * update source code from FastDFS V5.02 + +Version 1.05 2012-07-08 + * update source code from FastDFS V3.09 + +Version 1.04 2011-01-31 + * update source code from FastDFS V2.08 + +Version 1.03 2010-11-16 + * add local ip functions local_ip_func.c + +Version 1.02 2010-07-02 + * sockopt.c: tcprecvfile and tcpdiscard add parameter total_recv_bytes + * sockopt.h add non-block connect function connectserverbyip_nb + * log_init set log to cache to false (no cache) + +Version 1.01 2010-05-15 + * source file move to directory src + * header files add comments + * logger.h: correct function name from log_destory_ex to log_destroy_ex + * shared_func.h: getExeAbsolutePath change to getAbsolutePath + +Version 1.00 2010-05-08 + * first version + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c1087f0 --- /dev/null +++ b/INSTALL @@ -0,0 +1,23 @@ +Copy right 2010 Happy Fish / YuQing + +libfastcommon may be copied only under the terms of the Less GNU General +Public License(LGPL). +Please visit the libfastcommon Home Page for more detail. +English language: http://code.google.com/p/libfastcommon/ +Chinese language: http://linux.chinaunix.net/bbs/forum-75-1.html + + +#step 1. download libfastcommon source package and unpack it, +tar xzf libfastcommon_v1.x.tar.gz +#for example: +tar xzf libfastcommon_v1.3.tar.gz + +#step 2. enter the libfastcommon dir +cd libfastcommon + +#step 3. make +./make.sh + +#step 4. make install +./make.sh install + diff --git a/README b/README new file mode 100644 index 0000000..789d0d3 --- /dev/null +++ b/README @@ -0,0 +1,17 @@ +Copyright (C) 2010 Happy Fish / YuQing + +libfastcommon may be copied only under the terms of the Less GNU General +Public License(LGPL). +Please visit the libfastcommon Home Page for more detail. +English language: http://code.google.com/p/libfastcommon/ +Chinese language: http://linux.chinaunix.net/bbs/forum-75-1.html + + +c common functions library extracted from my open source projects FastDFS and +FastDHT. this library is very simple and stable. + +functions including: string, logger, chain, hash, socket, ini file reader, +base64 encode / decode, url encode / decode etc. + +detail info please see the c header files. + diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..55119a1 --- /dev/null +++ b/make.sh @@ -0,0 +1,99 @@ +tmp_src_filename=fast_check_bits.c +cat < $tmp_src_filename +#include +#include +#include +int main() +{ + printf("%d\n", sizeof(long)); + printf("%d\n", sizeof(off_t)); + return 0; +} +EOF + +gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename +output=`./a.out` + +if [ -f /bin/expr ]; then + EXPR=/bin/expr +else + EXPR=/usr/bin/expr +fi + +count=0 +int_bytes=4 +off_bytes=8 +for col in $output; do + if [ $count -eq 0 ]; then + int_bytes=$col + else + off_bytes=$col + fi + + count=`$EXPR $count + 1` +done + +/bin/rm -f a.out $tmp_src_filename +if [ "$int_bytes" -eq 8 ]; then + OS_BITS=64 +else + OS_BITS=32 +fi + +if [ "$off_bytes" -eq 8 ]; then + OFF_BITS=64 +else + OFF_BITS=32 +fi + +cat < src/_os_bits.h +#ifndef _OS_BITS_H +#define _OS_BITS_H + +#define OS_BITS $OS_BITS +#define OFF_BITS $OFF_BITS + +#endif +EOF + +DEBUG_FLAG=1 + +CFLAGS='-Wall -D_FILE_OFFSET_BITS=64' +if [ "$DEBUG_FLAG" = "1" ]; then + CFLAGS="$CFLAGS -g -DDEBUG_FLAG" +else + CFLAGS="$CFLAGS -O3" +fi + +LIBS='' +uname=`uname` +if [ "$uname" = "Linux" ]; then + CFLAGS="$CFLAGS -DOS_LINUX -DIOEVENT_USE_EPOLL" +elif [ "$uname" = "FreeBSD" ]; then + CFLAGS="$CFLAGS -DOS_FREEBSD -DIOEVENT_USE_KQUEUE" +elif [ "$uname" = "SunOS" ]; then + CFLAGS="$CFLAGS -DOS_SUNOS -D_THREAD_SAFE -DIOEVENT_USE_PORT" + LIBS="$LIBS -lsocket -lnsl -lresolv" + export CC=gcc +elif [ "$uname" = "AIX" ]; then + CFLAGS="$CFLAGS -DOS_AIX -D_THREAD_SAFE" + export CC=gcc +elif [ "$uname" = "HP-UX" ]; then + CFLAGS="$CFLAGS -DOS_HPUX" +fi + +if [ -f /usr/lib/libpthread.so ] || [ -f /usr/local/lib/libpthread.so ] || [ -f /usr/lib64/libpthread.so ] || [ -f /usr/lib/libpthread.a ] || [ -f /usr/local/lib/libpthread.a ] || [ -f /usr/lib64/libpthread.a ]; then + LIBS="$LIBS -lpthread" +else + line=`nm -D /usr/lib/libc_r.so | grep pthread_create | grep -w T` + if [ -n "$line" ]; then + LIBS="$LIBS -lc_r" + fi +fi + +cd src +cp Makefile.in Makefile +perl -pi -e "s#\\\$\(CFLAGS\)#$CFLAGS#g" Makefile +perl -pi -e "s#\\\$\(LIBS\)#$LIBS#g" Makefile +make $1 $2 + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..ef7ecce --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,43 @@ +.SUFFIXES: .c .o .lo + +COMPILE = $(CC) $(CFLAGS) +INC_PATH = +LIB_PATH = -L/usr/local/lib $(LIBS) + +FAST_SHARED_OBJS = hash.lo chain.lo shared_func.lo ini_file_reader.lo \ + logger.lo sockopt.lo base64.lo sched_thread.lo \ + http_func.lo md5.lo pthread_func.lo local_ip_func.lo \ + avl_tree.lo ioevent.lo ioevent_loop.lo fast_task_queue.lo \ + fast_timer.lo process_ctrl.lo + +HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \ + shared_func.h pthread_func.h ini_file_reader.h _os_bits.h \ + sockopt.h sched_thread.h http_func.h md5.h local_ip_func.h \ + avl_tree.h ioevent.h ioevent_loop.h fast_task_queue.h \ + fast_timer.h process_ctrl.h + +ALL_OBJS = $(STATIC_OBJS) $(FAST_SHARED_OBJS) + +ALL_PRGS = +ALL_LIBS = libfastcommon.so.1 + +all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) +libfastcommon.so.1: + $(COMPILE) -o $@ $< -shared $(FAST_SHARED_OBJS) $(LIB_PATH) +.o: + $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) +.c: + $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) +.c.o: + $(COMPILE) -c -o $@ $< $(INC_PATH) +.c.lo: + $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) +install: + cp -f $(ALL_LIBS) /usr/local/lib/ + mkdir -p /usr/local/include/fastcommon + cp -f $(HEADER_FILES) /usr/local/include/fastcommon + ln -fs /usr/local/lib/libfastcommon.so.1 /usr/local/lib/libfastcommon.so + sh ./fast_link_library.sh +clean: + rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) + diff --git a/src/_os_bits.h b/src/_os_bits.h new file mode 100644 index 0000000..81e1626 --- /dev/null +++ b/src/_os_bits.h @@ -0,0 +1,7 @@ +#ifndef _OS_BITS_H +#define _OS_BITS_H + +#define OS_BITS 64 +#define OFF_BITS 64 + +#endif diff --git a/src/avl_tree.c b/src/avl_tree.c new file mode 100644 index 0000000..65c7e36 --- /dev/null +++ b/src/avl_tree.c @@ -0,0 +1,789 @@ +#include "avl_tree.h" + +int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \ + CompareFunc compare_func) +{ + tree->root = NULL; + tree->free_data_func = free_data_func; + tree->compare_func = compare_func; + return 0; +} + +static void avl_tree_destroy_loop(FreeDataFunc free_data_func, \ + AVLTreeNode *pCurrentNode) +{ + if (pCurrentNode->left != NULL) + { + avl_tree_destroy_loop(free_data_func, pCurrentNode->left); + } + if (pCurrentNode->right != NULL) + { + avl_tree_destroy_loop(free_data_func, pCurrentNode->right); + } + + if (free_data_func != NULL) + { + free_data_func(pCurrentNode->data); + } + free(pCurrentNode); +} + +void avl_tree_destroy(AVLTreeInfo *tree) +{ + if (tree == NULL) + { + return; + } + + if (tree->root != NULL) + { + avl_tree_destroy_loop(tree->free_data_func, tree->root); + tree->root = NULL; + } +} + +static AVLTreeNode *createTreeNode(AVLTreeNode *pParentNode, void *target_data) +{ + AVLTreeNode *pNewNode; + pNewNode = (AVLTreeNode *)malloc(sizeof(AVLTreeNode)); + if (pNewNode == NULL) + { + /* + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "malloc %d bytes fail!\n", __LINE__, \ + (int)sizeof(AVLTreeNode)); + */ + return NULL; + } + + pNewNode->left = pNewNode->right = NULL; + pNewNode->data = target_data; + pNewNode->balance = 0; + + return pNewNode; +} + +static void avlRotateLeft(AVLTreeNode *pRotateNode, AVLTreeNode **ppRaiseNode) +{ + *ppRaiseNode = pRotateNode->right; + pRotateNode->right = (*ppRaiseNode)->left; + (*ppRaiseNode)->left = pRotateNode; +} + +static void avlRotateRight(AVLTreeNode *pRotateNode, AVLTreeNode **ppRaiseNode) +{ + *ppRaiseNode = pRotateNode->left; + pRotateNode->left = (*ppRaiseNode)->right; + (*ppRaiseNode)->right = pRotateNode; +} + +static void avlLeftBalanceWhenInsert(AVLTreeNode **pTreeNode, int *taller) +{ + AVLTreeNode *leftsub; + AVLTreeNode *rightsub; + + leftsub = (*pTreeNode)->left; + switch (leftsub->balance) + { + case -1 : + (*pTreeNode)->balance = leftsub->balance = 0; + avlRotateRight (*pTreeNode, pTreeNode); + *taller = 0; + break; + case 0 : + break; + case 1 : + rightsub = leftsub->right; + switch ( rightsub->balance ) + { + case -1: + (*pTreeNode)->balance = 1; + leftsub->balance = 0; + break; + case 0 : + (*pTreeNode)->balance = leftsub->balance = 0; + break; + case 1 : + (*pTreeNode)->balance = 0; + leftsub->balance = -1; + break; + } + + rightsub->balance = 0; + avlRotateLeft( leftsub, &((*pTreeNode)->left)); + avlRotateRight(*pTreeNode, pTreeNode); + *taller = 0; + } +} + +static void avlRightBalanceWhenInsert(AVLTreeNode **pTreeNode, int *taller) +{ + AVLTreeNode *rightsub; + AVLTreeNode *leftsub; + + rightsub = (*pTreeNode)->right; + switch (rightsub->balance) + { + case 1: + (*pTreeNode)->balance = rightsub->balance = 0; + avlRotateLeft(*pTreeNode, pTreeNode); + *taller = 0; + break; + case 0: + break; + case -1: + leftsub = rightsub->left; + switch (leftsub->balance) + { + case 1 : + (*pTreeNode)->balance = -1; + rightsub->balance = 0; + break; + case 0 : + (*pTreeNode)->balance = rightsub->balance = 0; + break; + case -1 : + (*pTreeNode)->balance = 0; + rightsub->balance = 1; + break; + } + + leftsub->balance = 0; + avlRotateRight(rightsub, &((*pTreeNode)->right)); + avlRotateLeft(*pTreeNode, pTreeNode); + *taller = 0; + } +} + +static int avl_tree_insert_loop(CompareFunc compare_func, AVLTreeNode **pCurrentNode, \ + void *target_data, int *taller) +{ + int nCompRes; + int success; + + if (*pCurrentNode == NULL) + { + *pCurrentNode = createTreeNode(*pCurrentNode, target_data); + if (*pCurrentNode != NULL) + { + *taller = 1; + return 1; + } + else + { + return -ENOMEM; + } + } + + nCompRes = compare_func((*pCurrentNode)->data, target_data); + if (nCompRes > 0) + { + success = avl_tree_insert_loop(compare_func, \ + &((*pCurrentNode)->left), target_data, taller); + if (*taller != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1: + avlLeftBalanceWhenInsert(pCurrentNode, taller); + break; + case 0: + (*pCurrentNode)->balance = -1; + break; + case 1: + (*pCurrentNode)->balance = 0; + *taller = 0; + break; + } + } + } + else if (nCompRes < 0) + { + success = avl_tree_insert_loop(compare_func, \ + &((*pCurrentNode)->right), target_data, taller); + if (*taller != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1: + (*pCurrentNode)->balance = 0; + *taller = 0; + break; + case 0: + (*pCurrentNode)->balance = 1; + break; + case 1: + avlRightBalanceWhenInsert(pCurrentNode, taller); + break; + } + } + } + else + { + return 0; + } + + return success; +} + +int avl_tree_insert(AVLTreeInfo *tree, void *data) +{ + int taller; + + taller = 0; + return avl_tree_insert_loop(tree->compare_func, &(tree->root), \ + data, &taller); +} + +static int avl_tree_replace_loop(CompareFunc compare_func, \ + FreeDataFunc free_data_func, AVLTreeNode **pCurrentNode, \ + void *target_data, int *taller) +{ + int nCompRes; + int success; + + if (*pCurrentNode == NULL ) + { + *pCurrentNode = createTreeNode(*pCurrentNode, target_data); + if (*pCurrentNode != NULL) + { + *taller = 1; + return 1; + } + else + { + return -ENOMEM; + } + } + + nCompRes = compare_func((*pCurrentNode)->data, target_data); + if (nCompRes > 0) + { + success = avl_tree_replace_loop(compare_func, free_data_func, + &((*pCurrentNode)->left), target_data, taller); + if (*taller != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1: + avlLeftBalanceWhenInsert(pCurrentNode, taller); + break; + case 0: + (*pCurrentNode)->balance = -1; + break; + case 1: + (*pCurrentNode)->balance = 0; + *taller = 0; + break; + } + } + } + else if (nCompRes < 0) + { + success = avl_tree_replace_loop(compare_func, free_data_func, + &((*pCurrentNode)->right), target_data, taller); + if (*taller != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1 : + (*pCurrentNode)->balance = 0; + *taller = 0; + break; + case 0 : + (*pCurrentNode)->balance = 1; + break; + case 1 : + avlRightBalanceWhenInsert(pCurrentNode, taller); + break; + } + } + } + else + { + if (free_data_func != NULL) + { + free_data_func((*pCurrentNode)->data); + } + (*pCurrentNode)->data = target_data; + return 0; + } + + return success; +} + +int avl_tree_replace(AVLTreeInfo *tree, void *data) +{ + int taller; + + taller = 0; + return avl_tree_replace_loop(tree->compare_func, \ + tree->free_data_func, &(tree->root), data, &taller); +} + +static AVLTreeNode *avl_tree_find_loop(CompareFunc compare_func, \ + AVLTreeNode *pCurrentNode, void *target_data) +{ + int nCompRes; + nCompRes = compare_func(pCurrentNode->data, target_data); + if (nCompRes > 0) + { + if (pCurrentNode->left == NULL) + { + return NULL; + } + else + { + return avl_tree_find_loop(compare_func, \ + pCurrentNode->left, target_data); + } + } + else if (nCompRes < 0) + { + if (pCurrentNode->right == NULL) + { + return NULL; + } + else + { + return avl_tree_find_loop(compare_func, \ + pCurrentNode->right, target_data); + } + } + else + { + return pCurrentNode; + } +} + +static void *avl_tree_find_ge_loop(CompareFunc compare_func, \ + AVLTreeNode *pCurrentNode, void *target_data) +{ + int nCompRes; + void *found; + + nCompRes = compare_func(pCurrentNode->data, target_data); + if (nCompRes > 0) + { + if (pCurrentNode->left == NULL) + { + return pCurrentNode->data; + } + + found = avl_tree_find_ge_loop(compare_func, \ + pCurrentNode->left, target_data); + return found != NULL ? found : pCurrentNode->data; + } + else if (nCompRes < 0) + { + if (pCurrentNode->right == NULL) + { + return NULL; + } + else + { + return avl_tree_find_ge_loop(compare_func, \ + pCurrentNode->right, target_data); + } + } + else + { + return pCurrentNode->data; + } +} + +void *avl_tree_find(AVLTreeInfo *tree, void *target_data) +{ + AVLTreeNode *found; + + if (tree->root == NULL) + { + return NULL; + } + + found = avl_tree_find_loop(tree->compare_func, \ + tree->root, target_data); + + return found != NULL ? found->data : NULL; +} + +void *avl_tree_find_ge(AVLTreeInfo *tree, void *target_data) +{ + void *found; + + if (tree->root == NULL) + { + found = NULL; + } + else + { + found = avl_tree_find_ge_loop(tree->compare_func, \ + tree->root, target_data); + } + + return found; +} + +static void avlLeftBalanceWhenDelete(AVLTreeNode **pTreeNode, int *shorter) +{ + AVLTreeNode *leftsub; + AVLTreeNode *rightsub; + + leftsub = (*pTreeNode)->left; + switch (leftsub->balance) + { + case -1: + (*pTreeNode)->balance = leftsub->balance = 0; + avlRotateRight (*pTreeNode, pTreeNode); + break; + case 0: + leftsub->balance = 1; + avlRotateRight(*pTreeNode, pTreeNode); + *shorter = 0; + break; + case 1: + rightsub = leftsub->right; + switch ( rightsub->balance ) + { + case -1: + (*pTreeNode)->balance = 1; + leftsub->balance = 0; + break; + case 0 : + (*pTreeNode)->balance = leftsub->balance = 0; + break; + case 1 : + (*pTreeNode)->balance = 0; + leftsub->balance = -1; + break; + } + + rightsub->balance = 0; + avlRotateLeft( leftsub, &((*pTreeNode)->left)); + avlRotateRight(*pTreeNode, pTreeNode); + break; + } +} + +static void avlRightBalanceWhenDelete(AVLTreeNode **pTreeNode, int *shorter) +{ + AVLTreeNode *rightsub; + AVLTreeNode *leftsub; + + rightsub = (*pTreeNode)->right; + switch (rightsub->balance) + { + case 1: + (*pTreeNode)->balance = rightsub->balance = 0; + avlRotateLeft(*pTreeNode, pTreeNode); + break; + case 0: + rightsub->balance = -1; + avlRotateLeft(*pTreeNode, pTreeNode); + *shorter = 0; + break; + case -1: + leftsub = rightsub->left; + switch (leftsub->balance) + { + case 1: + (*pTreeNode)->balance = -1; + rightsub->balance = 0; + break; + case 0: + (*pTreeNode)->balance = rightsub->balance = 0; + break; + case -1: + (*pTreeNode)->balance = 0; + rightsub->balance = 1; + break; + } + + leftsub->balance = 0; + avlRotateRight(rightsub, &((*pTreeNode)->right)); + avlRotateLeft(*pTreeNode, pTreeNode); + break; + } +} + +static int avl_tree_delete_loop(AVLTreeInfo *tree, AVLTreeNode **pCurrentNode,\ + void *target_data, int *shorter, AVLTreeNode *pDeletedDataNode) +{ + int nCompRes; + bool result; + AVLTreeNode *leftsub; + AVLTreeNode *rightsub; + + if (pDeletedDataNode != NULL) + { + if ((*pCurrentNode)->right == NULL) + { + pDeletedDataNode->data = (*pCurrentNode)->data; + leftsub = (*pCurrentNode)->left; + + free(*pCurrentNode); + *pCurrentNode = leftsub; + *shorter = 1; + return 1; + } + + nCompRes = -1; + } + else + { + nCompRes = tree->compare_func((*pCurrentNode)->data, target_data); + } + + if (nCompRes > 0) + { + if ((*pCurrentNode)->left == NULL) + { + return 0; + } + + result = avl_tree_delete_loop(tree, &((*pCurrentNode)->left), \ + target_data, shorter, pDeletedDataNode); + if (*shorter != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1: + (*pCurrentNode)->balance = 0; + break; + case 0: + (*pCurrentNode)->balance = 1; + *shorter = 0; + break; + case 1: + avlRightBalanceWhenDelete(pCurrentNode, shorter); + break; + } + } + return result; + } + else if (nCompRes < 0) + { + if ((*pCurrentNode)->right == NULL) + { + return 0; + } + + result = avl_tree_delete_loop(tree, &((*pCurrentNode)->right), \ + target_data, shorter, pDeletedDataNode); + if (*shorter != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1: + avlLeftBalanceWhenDelete(pCurrentNode, shorter); + break; + case 0: + (*pCurrentNode)->balance = -1; + *shorter = 0; + break; + case 1: + (*pCurrentNode)->balance = 0; + break; + } + } + return result; + } + else + { + if (tree->free_data_func != NULL) + { + tree->free_data_func((*pCurrentNode)->data); + } + + leftsub = (*pCurrentNode)->left; + rightsub = (*pCurrentNode)->right; + if (leftsub == NULL) + { + free(*pCurrentNode); + *pCurrentNode = rightsub; + } + else if (rightsub == NULL) + { + free(*pCurrentNode); + *pCurrentNode = leftsub; + } + else + { + avl_tree_delete_loop(tree, &((*pCurrentNode)->left), \ + target_data, shorter, *pCurrentNode); + + if (*shorter != 0) + { + switch ((*pCurrentNode)->balance) + { + case -1: + (*pCurrentNode)->balance = 0; + break; + case 0: + (*pCurrentNode)->balance = 1; + *shorter = 0; + break; + case 1: + avlRightBalanceWhenDelete(pCurrentNode, shorter); + break; + } + } + return 1; + } + + *shorter = 1; + return 1; + } +} + +int avl_tree_delete(AVLTreeInfo *tree, void *data) +{ + int shorter; + + if (tree->root == NULL) + { + return 0; + } + + shorter = 0; + return avl_tree_delete_loop(tree, &(tree->root), data, &shorter, NULL); +} + +static int avl_tree_walk_loop(DataOpFunc data_op_func, \ + AVLTreeNode *pCurrentNode, void *args) +{ + int result; + + if (pCurrentNode->left != NULL) + { + result = avl_tree_walk_loop(data_op_func, \ + pCurrentNode->left, args); + if (result != 0) + { + return result; + } + } + + if ((result=data_op_func(pCurrentNode->data, args)) != 0) + { + return result; + } + + /* + if (pCurrentNode->balance >= -1 && pCurrentNode->balance <= 1) + { + //printf("==%d\n", pCurrentNode->balance); + } + else + { + printf("==bad %d!!!!!!!!!!!!\n", pCurrentNode->balance); + } + */ + + if (pCurrentNode->right != NULL) + { + result = avl_tree_walk_loop(data_op_func, \ + pCurrentNode->right, args); + } + + return result; +} + +int avl_tree_walk(AVLTreeInfo *tree, DataOpFunc data_op_func, void *args) +{ + if (tree->root == NULL) + { + return 0; + } + else + { + return avl_tree_walk_loop(data_op_func, tree->root, args); + } +} + +static void avl_tree_count_loop(AVLTreeNode *pCurrentNode, int *count) +{ + if (pCurrentNode->left != NULL) + { + avl_tree_count_loop(pCurrentNode->left, count); + } + + (*count)++; + + if (pCurrentNode->right != NULL) + { + avl_tree_count_loop(pCurrentNode->right, count); + } +} + +int avl_tree_count(AVLTreeInfo *tree) +{ + int count; + if (tree->root == NULL) + { + return 0; + } + + count = 0; + avl_tree_count_loop(tree->root, &count); + return count; +} + +int avl_tree_depth(AVLTreeInfo *tree) +{ + int depth; + AVLTreeNode *pNode; + + if (tree->root == NULL) + { + return 0; + } + + depth = 0; + pNode = tree->root; + while (pNode != NULL) + { + if (pNode->balance == -1) + { + pNode = pNode->left; + } + else + { + pNode = pNode->right; + } + depth++; + } + + return depth; +} + +/* +static void avl_tree_print_loop(AVLTreeNode *pCurrentNode) +{ + printf("%ld left: %ld, right: %ld, balance: %d\n", pCurrentNode->data, + pCurrentNode->left != NULL ? pCurrentNode->left->data : 0, + pCurrentNode->right != NULL ? pCurrentNode->right->data : 0, + pCurrentNode->balance); + + if (pCurrentNode->left != NULL) + { + avl_tree_print_loop(pCurrentNode->left); + } + + if (pCurrentNode->right != NULL) + { + avl_tree_print_loop(pCurrentNode->right); + } +} + +void avl_tree_print(AVLTreeInfo *tree) +{ + if (tree->root == NULL) + { + return; + } + + avl_tree_print_loop(tree->root); +} +*/ + diff --git a/src/avl_tree.h b/src/avl_tree.h new file mode 100644 index 0000000..1a9db45 --- /dev/null +++ b/src/avl_tree.h @@ -0,0 +1,47 @@ + +#ifndef AVL_TREE_H +#define AVL_TREE_H + +#include +#include +#include +#include "common_define.h" + +typedef struct tagAVLTreeNode { + void *data; + struct tagAVLTreeNode *left; + struct tagAVLTreeNode *right; + byte balance; +} AVLTreeNode; + +typedef int (*DataOpFunc) (void *data, void *args); + +typedef struct tagAVLTreeInfo { + AVLTreeNode *root; + FreeDataFunc free_data_func; + CompareFunc compare_func; +} AVLTreeInfo; + +#ifdef __cplusplus +extern "C" { +#endif + +int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \ + CompareFunc compare_func); +void avl_tree_destroy(AVLTreeInfo *tree); + +int avl_tree_insert(AVLTreeInfo *tree, void *data); +int avl_tree_replace(AVLTreeInfo *tree, void *data); +int avl_tree_delete(AVLTreeInfo *tree, void *data); +void *avl_tree_find(AVLTreeInfo *tree, void *target_data); +void *avl_tree_find_ge(AVLTreeInfo *tree, void *target_data); +int avl_tree_walk(AVLTreeInfo *tree, DataOpFunc data_op_func, void *args); +int avl_tree_count(AVLTreeInfo *tree); +int avl_tree_depth(AVLTreeInfo *tree); +//void avl_tree_print(AVLTreeInfo *tree); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..1ff77bf --- /dev/null +++ b/src/base64.c @@ -0,0 +1,393 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "base64.h" + +/** +* Marker value for chars we just ignore, e.g. \n \r high ascii +*/ +#define BASE64_IGNORE -1 + +/** +* Marker for = trailing pad +*/ +#define BASE64_PAD -2 + +/** +* determines how long the lines are that are generated by encode. +* Ignored by decode. +* @param length 0 means no newlines inserted. Must be a multiple of 4. +*/ +void base64_set_line_length(struct base64_context *context, const int length) +{ + context->line_length = (length / 4) * 4; +} + +/** + * How lines are separated. + * Ignored by decode. + * @param context->line_separator may be "" but not null. + * Usually contains only a combination of chars \n and \r. + * Could be any chars not in set A-Z a-z 0-9 + /. +*/ +void base64_set_line_separator(struct base64_context *context, \ + const char *pLineSeparator) +{ + context->line_sep_len = snprintf(context->line_separator, \ + sizeof(context->line_separator), "%s", pLineSeparator); +} + +void base64_init_ex(struct base64_context *context, const int nLineLength, \ + const unsigned char chPlus, const unsigned char chSplash, \ + const unsigned char chPad) +{ + int i; + + memset(context, 0, sizeof(struct base64_context)); + + context->line_length = nLineLength; + context->line_separator[0] = '\n'; + context->line_separator[1] = '\0'; + context->line_sep_len = 1; + + // build translate valueToChar table only once. + // 0..25 -> 'A'..'Z' + for (i=0; i<=25; i++) + { + context->valueToChar[i] = (char)('A'+i); + } + // 26..51 -> 'a'..'z' + for (i=0; i<=25; i++ ) + { + context->valueToChar[i+26] = (char)('a'+i); + } + // 52..61 -> '0'..'9' + for (i=0; i<=9; i++ ) + { + context->valueToChar[i+52] = (char)('0'+i); + } + context->valueToChar[62] = chPlus; + context->valueToChar[63] = chSplash; + + memset(context->charToValue, BASE64_IGNORE, sizeof(context->charToValue)); + for (i=0; i<64; i++ ) + { + context->charToValue[context->valueToChar[i]] = i; + } + + context->pad_ch = chPad; + context->charToValue[chPad] = BASE64_PAD; +} + +int base64_get_encode_length(struct base64_context *context, const int nSrcLen) +{ + // Each group or partial group of 3 bytes becomes four chars + // covered quotient + int outputLength; + + outputLength = ((nSrcLen + 2) / 3) * 4; + + // account for trailing newlines, on all but the very last line + if (context->line_length != 0) + { + int lines = (outputLength + context->line_length - 1) / + context->line_length - 1; + if ( lines > 0 ) + { + outputLength += lines * context->line_sep_len; + } + } + + return outputLength; +} + + /** + * Encode an arbitrary array of bytes as base64 printable ASCII. + * It will be broken into lines of 72 chars each. The last line is not + * terminated with a line separator. + * The output will always have an even multiple of data characters, + * exclusive of \n. It is padded out with =. + */ +char *base64_encode_ex(struct base64_context *context, const char *src, \ + const int nSrcLen, char *dest, int *dest_len, const bool bPad) +{ + int linePos; + int leftover; + int combined; + char *pDest; + int c0, c1, c2, c3; + unsigned char *pRaw; + unsigned char *pEnd; + const char *ppSrcs[2]; + int lens[2]; + char szPad[3]; + int k; + int loop; + + if (nSrcLen <= 0) + { + *dest = '\0'; + *dest_len = 0; + return dest; + } + + linePos = 0; + lens[0] = (nSrcLen / 3) * 3; + lens[1] = 3; + leftover = nSrcLen - lens[0]; + ppSrcs[0] = src; + ppSrcs[1] = szPad; + + szPad[0] = szPad[1] = szPad[2] = '\0'; + switch (leftover) + { + case 0: + default: + loop = 1; + break; + case 1: + loop = 2; + szPad[0] = src[nSrcLen-1]; + break; + case 2: + loop = 2; + szPad[0] = src[nSrcLen-2]; + szPad[1] = src[nSrcLen-1]; + break; + } + + pDest = dest; + for (k=0; k context->line_length) + { + if (context->line_length != 0) + { + memcpy(pDest, context->line_separator, context->line_sep_len); + pDest += context->line_sep_len; + } + linePos = 4; + } + + // get next three bytes in unsigned form lined up, + // in big-endian order + combined = ((*pRaw) << 16) | ((*(pRaw+1)) << 8) | (*(pRaw+2)); + + // break those 24 bits into a 4 groups of 6 bits, + // working LSB to MSB. + c3 = combined & 0x3f; + combined >>= 6; + c2 = combined & 0x3f; + combined >>= 6; + c1 = combined & 0x3f; + combined >>= 6; + c0 = combined & 0x3f; + + // Translate into the equivalent alpha character + // emitting them in big-endian order. + *pDest++ = context->valueToChar[c0]; + *pDest++ = context->valueToChar[c1]; + *pDest++ = context->valueToChar[c2]; + *pDest++ = context->valueToChar[c3]; + } + } + + *pDest = '\0'; + *dest_len = pDest - dest; + + // deal with leftover bytes + switch (leftover) + { + case 0: + default: + // nothing to do + break; + case 1: + // One leftover byte generates xx== + if (bPad) + { + *(pDest-1) = context->pad_ch; + *(pDest-2) = context->pad_ch; + } + else + { + *(pDest-2) = '\0'; + *dest_len -= 2; + } + break; + case 2: + // Two leftover bytes generates xxx= + if (bPad) + { + *(pDest-1) = context->pad_ch; + } + else + { + *(pDest-1) = '\0'; + *dest_len -= 1; + } + break; + } // end switch; + + return dest; +} + +char *base64_decode_auto(struct base64_context *context, const char *src, \ + const int nSrcLen, char *dest, int *dest_len) +{ + int nRemain; + int nPadLen; + int nNewLen; + char tmpBuff[256]; + char *pBuff; + + nRemain = nSrcLen % 4; + if (nRemain == 0) + { + return base64_decode(context, src, nSrcLen, dest, dest_len); + } + + nPadLen = 4 - nRemain; + nNewLen = nSrcLen + nPadLen; + if (nNewLen <= sizeof(tmpBuff)) + { + pBuff = tmpBuff; + } + else + { + pBuff = (char *)malloc(nNewLen); + if (pBuff == NULL) + { + fprintf(stderr, "Can't malloc %d bytes\n", \ + nSrcLen + nPadLen + 1); + *dest_len = 0; + *dest = '\0'; + return dest; + } + } + + memcpy(pBuff, src, nSrcLen); + memset(pBuff + nSrcLen, context->pad_ch, nPadLen); + + base64_decode(context, pBuff, nNewLen, dest, dest_len); + + if (pBuff != tmpBuff) + { + free(pBuff); + } + + return dest; +} + +/** +* decode a well-formed complete base64 string back into an array of bytes. +* It must have an even multiple of 4 data characters (not counting \n), +* padded out with = as needed. +*/ +char *base64_decode(struct base64_context *context, const char *src, \ + const int nSrcLen, char *dest, int *dest_len) +{ + // tracks where we are in a cycle of 4 input chars. + int cycle; + + // where we combine 4 groups of 6 bits and take apart as 3 groups of 8. + int combined; + + // will be an even multiple of 4 chars, plus some embedded \n + int dummies; + int value; + unsigned char *pSrc; + unsigned char *pSrcEnd; + char *pDest; + + cycle = 0; + combined = 0; + dummies = 0; + pDest = dest; + pSrcEnd = (unsigned char *)src + nSrcLen; + for (pSrc=(unsigned char *)src; pSrccharToValue[*pSrc]; + switch (value) + { + case BASE64_IGNORE: + // e.g. \n, just ignore it. + break; + case BASE64_PAD: + value = 0; + dummies++; + // fallthrough + default: + /* regular value character */ + switch (cycle) + { + case 0: + combined = value; + cycle = 1; + break; + case 1: + combined <<= 6; + combined |= value; + cycle = 2; + break; + case 2: + combined <<= 6; + combined |= value; + cycle = 3; + break; + case 3: + combined <<= 6; + combined |= value; + // we have just completed a cycle of 4 chars. + // the four 6-bit values are in combined in big-endian order + // peel them off 8 bits at a time working lsb to msb + // to get our original 3 8-bit bytes back + + *pDest++ = (char)(combined >> 16); + *pDest++ = (char)((combined & 0x0000FF00) >> 8); + *pDest++ = (char)(combined & 0x000000FF); + + cycle = 0; + break; + } + break; + } + } // end for + + if (cycle != 0) + { + *dest = '\0'; + *dest_len = 0; + fprintf(stderr, "Input to decode not an even multiple of " \ + "4 characters; pad with %c\n", context->pad_ch); + return dest; + } + + *dest_len = (pDest - dest) - dummies; + *(dest + (*dest_len)) = '\0'; + + return dest; +} + diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..6c85007 --- /dev/null +++ b/src/base64.h @@ -0,0 +1,134 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//base64.h + +#ifndef _BASE64_H +#define _BASE64_H + +#include "common_define.h" + +struct base64_context +{ + char line_separator[16]; + int line_sep_len; + + /** + * max chars per line, excluding line_separator. A multiple of 4. + */ + int line_length; + + /** + * letter of the alphabet used to encode binary values 0..63 + */ + unsigned char valueToChar[64]; + + /** + * binary value encoded by a given letter of the alphabet 0..63 + */ + int charToValue[256]; + int pad_ch; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** use stardand base64 charset + */ +#define base64_init(context, nLineLength) \ + base64_init_ex(context, nLineLength, '+', '/', '=') + + +/** stardand base64 encode + */ +#define base64_encode(context, src, nSrcLen, dest, dest_len) \ + base64_encode_ex(context, src, nSrcLen, dest, dest_len, true) + +/** base64 init function, before use base64 function, you should call + * this function + * parameters: + * context: the base64 context + * nLineLength: length of a line, 0 for never add line seperator + * chPlus: specify a char for base64 char plus (+) + * chSplash: specify a char for base64 char splash (/) + * chPad: specify a char for base64 padding char = + * return: none + */ +void base64_init_ex(struct base64_context *context, const int nLineLength, \ + const unsigned char chPlus, const unsigned char chSplash, \ + const unsigned char chPad); + +/** calculate the encoded length of the source buffer + * parameters: + * context: the base64 context + * nSrcLen: the source buffer length + * return: the encoded length of the source buffer + */ +int base64_get_encode_length(struct base64_context *context, const int nSrcLen); + +/** base64 encode buffer + * parameters: + * context: the base64 context + * src: the source buffer + * nSrcLen: the source buffer length + * dest: the dest buffer + * dest_len: return dest buffer length + * bPad: if padding + * return: the encoded buffer + */ +char *base64_encode_ex(struct base64_context *context, const char *src, \ + const int nSrcLen, char *dest, int *dest_len, const bool bPad); + +/** base64 decode buffer, work only with padding source string + * parameters: + * context: the base64 context + * src: the source buffer with padding + * nSrcLen: the source buffer length + * dest: the dest buffer + * dest_len: return dest buffer length + * return: the decoded buffer + */ +char *base64_decode(struct base64_context *context, const char *src, \ + const int nSrcLen, char *dest, int *dest_len); + +/** base64 decode buffer, can work with no padding source string + * parameters: + * context: the base64 context + * src: the source buffer with padding or no padding + * nSrcLen: the source buffer length + * dest: the dest buffer + * dest_len: return dest buffer length + * return: the decoded buffer + */ +char *base64_decode_auto(struct base64_context *context, const char *src, \ + const int nSrcLen, char *dest, int *dest_len); + +/** set line separator string, such as \n or \r\n + * parameters: + * context: the base64 context + * pLineSeparator: the line separator string + * return: none + */ +void base64_set_line_separator(struct base64_context *context, \ + const char *pLineSeparator); + +/** set line length, 0 for never add line separators + * parameters: + * context: the base64 context + * length: the line length + * return: none + */ +void base64_set_line_length(struct base64_context *context, const int length); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/chain.c b/src/chain.c new file mode 100644 index 0000000..a23e5ab --- /dev/null +++ b/src/chain.c @@ -0,0 +1,325 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include "chain.h" +//#include "use_mmalloc.h" + +void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \ + CompareFunc compareFunc) +{ + if (pList == NULL) + { + return; + } + + pList->head = NULL; + pList->tail = NULL; + pList->type = type; + pList->freeDataFunc = freeDataFunc; + pList->compareFunc = compareFunc; + + return; +} + +void chain_destroy(ChainList *pList) +{ + ChainNode *pNode; + ChainNode *pDeleted; + if (pList == NULL || pList->head == NULL) + { + return; + } + + pNode = pList->head; + while (pNode != NULL) + { + pDeleted = pNode; + pNode = pNode->next; + + freeChainNode(pList, pDeleted); + } + + pList->head = pList->tail = NULL; +} + +void freeChainNode(ChainList *pList, ChainNode *pChainNode) +{ + if (pList->freeDataFunc != NULL) + { + pList->freeDataFunc(pChainNode->data); + } + + free(pChainNode); +} + +int insertNodePrior(ChainList *pList, void *data) +{ + ChainNode *pNode; + if (pList == NULL) + { + return EINVAL; + } + + pNode = (ChainNode *)malloc(sizeof(ChainNode)); + if (pNode == NULL) + { + return ENOMEM; + } + + pNode->data = data; + pNode->next = pList->head; + + pList->head = pNode; + if (pList->tail == NULL) + { + pList->tail = pNode; + } + + return 0; +} + +int appendNode(ChainList *pList, void *data) +{ + ChainNode *pNode; + if (pList == NULL) + { + return EINVAL; + } + + pNode = (ChainNode *)malloc(sizeof(ChainNode)); + if (pNode == NULL) + { + return ENOMEM; + } + + pNode->data = data; + pNode->next = NULL; + + if (pList->tail != NULL) + { + pList->tail->next = pNode; + } + + pList->tail = pNode; + if (pList->head == NULL) + { + pList->head = pNode; + } + + return 0; +} + +int insertNodeAsc(ChainList *pList, void *data) +{ + ChainNode *pNew; + ChainNode *pNode; + ChainNode *pPrevious; + if (pList == NULL || pList->compareFunc == NULL) + { + return EINVAL; + } + + pNew = (ChainNode *)malloc(sizeof(ChainNode)); + if (pNew == NULL) + { + return ENOMEM; + } + + pNew->data = data; + + pPrevious = NULL; + pNode = pList->head; + while (pNode != NULL && pList->compareFunc(pNode->data, data) < 0) + { + pPrevious = pNode; + pNode = pNode->next; + } + + pNew->next = pNode; + if (pPrevious == NULL) + { + pList->head = pNew; + } + else + { + pPrevious->next = pNew; + } + + if (pNode == NULL) + { + pList->tail = pNew; + } + + return 0; +} + +int addNode(ChainList *pList, void *data) +{ + if (pList->type == CHAIN_TYPE_INSERT) + { + return insertNodePrior(pList, data); + } + else if (pList->type == CHAIN_TYPE_APPEND) + { + return appendNode(pList, data); + } + else + { + return insertNodeAsc(pList, data); + } +} + +void deleteNodeEx(ChainList *pList, ChainNode *pPreviousNode, \ + ChainNode *pDeletedNode) +{ + if (pDeletedNode == pList->head) + { + pList->head = pDeletedNode->next; + } + else + { + pPreviousNode->next = pDeletedNode->next; + } + + if (pDeletedNode == pList->tail) + { + pList->tail = pPreviousNode; + } + + freeChainNode(pList, pDeletedNode); +} + +void deleteToNodePrevious(ChainList *pList, ChainNode *pPreviousNode, \ + ChainNode *pDeletedNext) +{ + ChainNode *pNode; + ChainNode *pDeletedNode; + + if (pPreviousNode == NULL) + { + pNode = pList->head; + pList->head = pDeletedNext; + } + else + { + pNode = pPreviousNode->next; + pPreviousNode->next = pDeletedNext; + } + + while (pNode != NULL && pNode != pDeletedNext) + { + pDeletedNode = pNode; + pNode = pNode->next; + freeChainNode(pList, pDeletedNode); + } + + if (pDeletedNext == NULL) + { + pList->tail = pPreviousNode; + } +} + +void *chain_pop_head(ChainList *pList) +{ + ChainNode *pDeletedNode; + void *data; + + pDeletedNode = pList->head; + if (pDeletedNode == NULL) + { + return NULL; + } + + pList->head = pDeletedNode->next; + if (pList->head == NULL) + { + pList->tail = NULL; + } + + data = pDeletedNode->data; + free(pDeletedNode); + + return data; +} + +int chain_count(ChainList *pList) +{ + ChainNode *pNode; + int count; + + count = 0; + pNode = pList->head; + while (pNode != NULL) + { + count++; + pNode = pNode->next; + } + + return count; +} + +int deleteNode(ChainList *pList, void *data, bool bDeleteAll) +{ + ChainNode *pNode; + ChainNode *pPrevious; + ChainNode *pDeleted; + int nCount; + int nCompareRes; + + if (pList == NULL || pList->compareFunc == NULL) + { + return -EINVAL; + } + + nCount = 0; + pPrevious = NULL; + pNode = pList->head; + while (pNode != NULL) + { + nCompareRes = pList->compareFunc(pNode->data, data); + if (nCompareRes == 0) + { + pDeleted = pNode; + pNode = pNode->next; + + deleteNodeEx(pList, pPrevious, pDeleted); + nCount++; + + if (!bDeleteAll) + { + break; + } + + continue; + } + else if(nCompareRes > 0 && pList->type == CHAIN_TYPE_SORTED) + { + break; + } + + pPrevious = pNode; + pNode = pNode->next; + } + + return nCount; +} + +int deleteOne(ChainList *pList, void *data) +{ + return deleteNode(pList, data, false); +} + +int deleteAll(ChainList *pList, void *data) +{ + return deleteNode(pList, data, true); +} + diff --git a/src/chain.h b/src/chain.h new file mode 100644 index 0000000..9d18a3d --- /dev/null +++ b/src/chain.h @@ -0,0 +1,148 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#ifndef CHAIN_H +#define CHAIN_H + +#include "common_define.h" + +#define CHAIN_TYPE_INSERT 0 //insert new node before head +#define CHAIN_TYPE_APPEND 1 //insert new node after tail +#define CHAIN_TYPE_SORTED 2 //sorted chain + +typedef struct tagChainNode +{ + void *data; + struct tagChainNode *next; +} ChainNode; + +typedef struct +{ + int type; + ChainNode *head; + ChainNode *tail; + FreeDataFunc freeDataFunc; + CompareFunc compareFunc; +} ChainList; + +#ifdef __cplusplus +extern "C" { +#endif + +/** chain init function + * parameters: + * pList: the chain list + * type: chain type, value is one of following: + * CHAIN_TYPE_INSERT: insert new node before head + * CHAIN_TYPE_APPEND: insert new node after tail + * CHAIN_TYPE_SORTED: sorted chain + * freeDataFunc: free data function pointer, can be NULL + * compareFunc: compare data function pointer, can be NULL, + * must set when type is CHAIN_TYPE_SORTED + * return: none + */ +void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \ + CompareFunc compareFunc); + +/** destroy chain + * parameters: + * pList: the chain list + * return: none + */ +void chain_destroy(ChainList *pList); + + +/** get the chain node count + * parameters: + * pList: the chain list + * return: chain node count + */ +int chain_count(ChainList *pList); + +/** add a new node to the chain + * parameters: + * pList: the chain list + * data: the data to add + * return: error no, 0 for success, != 0 fail + */ +int addNode(ChainList *pList, void *data); + +/** free the chain node + * parameters: + * pList: the chain list + * pChainNode: the chain node to free + * return: none + */ +void freeChainNode(ChainList *pList, ChainNode *pChainNode); + +/** delete the chain node + * parameters: + * pList: the chain list + * pPreviousNode: the previous chain node + * pDeletedNode: the chain node to delete + * return: none + */ +void deleteNodeEx(ChainList *pList, ChainNode *pPreviousNode, \ + ChainNode *pDeletedNode); + +/** delete the chain nodes from pPreviousNode->next to pDeletedNext + * (not including pDeletedNext) + * parameters: + * pList: the chain list + * pPreviousNode: the previous chain node, delete from pPreviousNode->next + * pDeletedNext: the chain node after the deleted node + * return: none + */ +void deleteToNodePrevious(ChainList *pList, ChainNode *pPreviousNode, \ + ChainNode *pDeletedNext); + +/** delete the chain node using data compare function + * parameters: + * pList: the chain list + * data: the first node whose data equals this will be deleted + * return: delete chain node count, < 0 fail + */ +int deleteOne(ChainList *pList, void *data); + +/** delete all chain nodes using data compare function + * parameters: + * pList: the chain list + * data: the node whose data equals this will be deleted + * return: delete chain node count, < 0 fail + */ +int deleteAll(ChainList *pList, void *data); + +/** pop up a chain nodes from chain head + * parameters: + * pList: the chain list + * return: the head node, return NULL when the chain is empty + */ +void *chain_pop_head(ChainList *pList); + +/** add a chain nodes before the chain head + * parameters: + * pList: the chain list + * data: the node to insert + * return: error no, 0 for success, != 0 fail + */ +int insertNodePrior(ChainList *pList, void *data); + +/** add a chain nodes after the chain tail + * parameters: + * pList: the chain list + * data: the node to insert + * return: error no, 0 for success, != 0 fail + */ +int appendNode(ChainList *pList, void *data); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/common_define.h b/src/common_define.h new file mode 100644 index 0000000..5cb8128 --- /dev/null +++ b/src/common_define.h @@ -0,0 +1,169 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//common_define.h + +#ifndef _COMMON_DEFINE_H_ +#define _COMMON_DEFINE_H_ + +#include +#include + +#ifdef WIN32 + +#include +#include +typedef UINT in_addr_t; +#define FILE_SEPERATOR "\\" +#define THREAD_ENTRANCE_FUNC_DECLARE DWORD WINAPI +#define THREAD_RETURN_VALUE 0 +typedef DWORD (WINAPI *ThreadEntranceFunc)(LPVOID lpThreadParameter); +#else + +#include +#include +#include +#include +#include +#include +#define FILE_SEPERATOR "/" +typedef int SOCKET; +#define closesocket close +#define INVALID_SOCKET -1 +#define THREAD_ENTRANCE_FUNC_DECLARE void * +typedef void *LPVOID; +#define THREAD_RETURN_VALUE NULL +typedef void * (*ThreadEntranceFunc)(LPVOID lpThreadParameter); + +#endif + +#ifndef WIN32 +extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind); +#endif + +#ifdef OS_LINUX +#define PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP +#endif + +#include "_os_bits.h" + +#ifdef OS_BITS + #if OS_BITS == 64 + #define INT64_PRINTF_FORMAT "%ld" + #else + #define INT64_PRINTF_FORMAT "%lld" + #endif +#else + #define INT64_PRINTF_FORMAT "%lld" +#endif + +#ifdef OFF_BITS + #if OFF_BITS == 64 + #define OFF_PRINTF_FORMAT INT64_PRINTF_FORMAT + #else + #define OFF_PRINTF_FORMAT "%d" + #endif +#else + #define OFF_PRINTF_FORMAT INT64_PRINTF_FORMAT +#endif + +#ifndef WIN32 +#define USE_SENDFILE +#endif + +#define MAX_PATH_SIZE 256 +#define LOG_FILE_DIR "logs" +#define CONF_FILE_DIR "conf" +#define DEFAULT_CONNECT_TIMEOUT 30 +#define DEFAULT_NETWORK_TIMEOUT 30 +#define DEFAULT_MAX_CONNECTONS 256 +#define DEFAULT_WORK_THREADS 4 +#define SYNC_LOG_BUFF_DEF_INTERVAL 10 +#define TIME_NONE -1 + +#define IP_ADDRESS_SIZE 16 +#define INFINITE_FILE_SIZE (256 * 1024LL * 1024 * 1024 * 1024 * 1024LL) + +#ifndef __cplusplus +#ifndef true +typedef char bool; +#define true 1 +#define false 0 +#endif +#endif + +#ifndef byte +#define byte signed char +#endif + +#ifndef ubyte +#define ubyte unsigned char +#endif + +#ifndef WIN32 +#ifndef INADDR_NONE +#define INADDR_NONE ((in_addr_t) 0xffffffff) +#endif +#endif + +#ifndef ECANCELED +#define ECANCELED 125 +#endif + +#ifndef ENONET +#define ENONET 64 /* Machine is not on the network */ +#endif + +#define IS_UPPER_HEX(ch) ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) +#define STRERROR(no) (strerror(no) != NULL ? strerror(no) : "Unkown error") + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + byte hour; + byte minute; +} TimeInfo; + +typedef struct +{ + char major; + char minor; +} Version; + +typedef struct +{ + char *key; + char *value; +} KeyValuePair; + +typedef struct +{ + char *buff; + int alloc_size; + int length; +} BufferInfo; + +typedef void (*FreeDataFunc)(void *ptr); +typedef int (*CompareFunc)(void *p1, void *p2); +typedef void* (*MallocFunc)(size_t size); + +#define TO_UPPERCASE(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 32 : c) +#define MEM_ALIGN(x) (((x) + 7) & (~7)) + +#ifdef WIN32 +#define strcasecmp _stricmp +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/fast_link_library.sh b/src/fast_link_library.sh new file mode 100755 index 0000000..d0c9882 --- /dev/null +++ b/src/fast_link_library.sh @@ -0,0 +1,26 @@ +tmp_src_filename=_fast_check_bits_.c +cat < $tmp_src_filename +#include +#include +#include +int main() +{ + printf("%d\n", (int)sizeof(long)); + return 0; +} +EOF + +gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename +OS_BITS=`./a.out` + +rm $tmp_src_filename a.out + +TARGET_LIB="/usr/local/lib" +if [ "`id -u`" = "0" ]; then + ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib/libfastcommon.so + + if [ "$OS_BITS" = "8" ]; then + ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib64/libfastcommon.so + fi +fi + diff --git a/src/fast_task_queue.c b/src/fast_task_queue.c new file mode 100644 index 0000000..b55bc46 --- /dev/null +++ b/src/fast_task_queue.c @@ -0,0 +1,503 @@ +//fast_task_queue.c + +#include +#include +#include +#include "fast_task_queue.h" +#include "logger.h" +#include "shared_func.h" +#include "pthread_func.h" + +static struct fast_task_queue g_free_queue; + +struct mpool_chain { + struct fast_task_info *blocks; + struct fast_task_info *last_block; //last block + struct mpool_chain *next; +} *g_mpool = NULL; + +#define ALIGNED_TASK_INFO_SIZE MEM_ALIGN(sizeof(struct fast_task_info)) + +int task_queue_init(struct fast_task_queue *pQueue) +{ + int result; + + if ((result=init_pthread_lock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "init_pthread_lock fail, errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + pQueue->head = NULL; + pQueue->tail = NULL; + + return 0; +} + +static struct mpool_chain *malloc_mpool(const int block_size, \ + const int total_alloc_size) +{ + struct fast_task_info *pTask; + char *p; + char *pCharEnd; + struct mpool_chain *mpool; + + mpool = (struct mpool_chain *)malloc(sizeof(struct mpool_chain)); + if (mpool == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, (int)sizeof(struct mpool_chain), \ + errno, STRERROR(errno)); + return NULL; + } + + mpool->next = NULL; + mpool->blocks = (struct fast_task_info *)malloc(total_alloc_size); + if (mpool->blocks == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, total_alloc_size, \ + errno, STRERROR(errno)); + free(mpool); + return NULL; + } + memset(mpool->blocks, 0, total_alloc_size); + + pCharEnd = ((char *)mpool->blocks) + total_alloc_size; + for (p=(char *)mpool->blocks; psize = g_free_queue.min_buff_size; + + pTask->arg = p + ALIGNED_TASK_INFO_SIZE; + if (g_free_queue.malloc_whole_block) + { + pTask->data = (char *)pTask->arg + \ + g_free_queue.arg_size; + } + else + { + pTask->data = (char *)malloc(pTask->size); + if (pTask->data == NULL) + { + char *pt; + + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, pTask->size, \ + errno, STRERROR(errno)); + + for (pt=(char *)mpool->blocks; pt < p; \ + pt += block_size) + { + free(((struct fast_task_info *)pt)->data); + } + + free(mpool->blocks); + free(mpool); + return NULL; + } + } + } + + mpool->last_block = (struct fast_task_info *)(pCharEnd - block_size); + for (p=(char *)mpool->blocks; p<(char *)mpool->last_block; p += block_size) + { + pTask = (struct fast_task_info *)p; + pTask->next = (struct fast_task_info *)(p + block_size); + } + mpool->last_block->next = NULL; + + return mpool; +} + +int free_queue_init(const int max_connections, const int min_buff_size, \ + const int max_buff_size, const int arg_size) +{ + int64_t total_size; + struct mpool_chain *mpool; + int block_size; + int alloc_size; + int result; + int loop_count; + int aligned_min_size; + int aligned_max_size; + int aligned_arg_size; + rlim_t max_data_size; + + if ((result=init_pthread_lock(&(g_free_queue.lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "init_pthread_lock fail, errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + aligned_min_size = MEM_ALIGN(min_buff_size); + aligned_max_size = MEM_ALIGN(max_buff_size); + aligned_arg_size = MEM_ALIGN(arg_size); + block_size = ALIGNED_TASK_INFO_SIZE + aligned_arg_size; + alloc_size = block_size * max_connections; + if (aligned_max_size > aligned_min_size) + { + total_size = alloc_size; + g_free_queue.malloc_whole_block = false; + max_data_size = 0; + } + else + { + struct rlimit rlimit_data; + + if (getrlimit(RLIMIT_DATA, &rlimit_data) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "call getrlimit fail, " \ + "errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EPERM; + } + if (rlimit_data.rlim_cur == RLIM_INFINITY) + { + max_data_size = 256 * 1024 * 1024; + } + else + { + max_data_size = rlimit_data.rlim_cur; + if (max_data_size > 256 * 1024 * 1024) + { + max_data_size = 256 * 1024 * 1024; + } + } + + if (max_data_size >= (int64_t)(block_size + aligned_min_size) * + (int64_t)max_connections) + { + total_size = alloc_size + (int64_t)aligned_min_size * + max_connections; + g_free_queue.malloc_whole_block = true; + block_size += aligned_min_size; + } + else + { + total_size = alloc_size; + g_free_queue.malloc_whole_block = false; + max_data_size = 0; + } + } + + logDebug("file: "__FILE__", line: %d, " \ + "max_connections: %d, min_buff_size: %d, max_buff_size: %d, " \ + "block_size: %d, arg_size: %d, max_data_size: %d, " \ + "total_size: "INT64_PRINTF_FORMAT, __LINE__, \ + max_connections, aligned_min_size, aligned_max_size, \ + block_size, aligned_arg_size, (int)max_data_size, total_size); + + g_free_queue.max_connections = max_connections; + g_free_queue.min_buff_size = aligned_min_size; + g_free_queue.max_buff_size = aligned_max_size; + g_free_queue.arg_size = aligned_arg_size; + + if ((!g_free_queue.malloc_whole_block) || \ + (total_size <= max_data_size)) + { + loop_count = 1; + mpool = malloc_mpool(block_size, total_size); + if (mpool == NULL) + { + return errno != 0 ? errno : ENOMEM; + } + g_mpool = mpool; + } + else + { + struct mpool_chain *previous_mpool; + int remain_count; + int alloc_once; + int current_count; + int current_alloc_size; + + mpool = NULL; + previous_mpool = NULL; + loop_count = 0; + remain_count = max_connections; + alloc_once = max_data_size / block_size; + while (remain_count > 0) + { + current_count = (remain_count > alloc_once) ? \ + alloc_once : remain_count; + current_alloc_size = block_size * current_count; + mpool = malloc_mpool(block_size, current_alloc_size); + if (mpool == NULL) + { + free_queue_destroy(); + return errno != 0 ? errno : ENOMEM; + } + + if (previous_mpool == NULL) + { + g_mpool = mpool; + } + else + { + previous_mpool->next = mpool; + previous_mpool->last_block->next = mpool->blocks; + } + previous_mpool = mpool; + + remain_count -= current_count; + loop_count++; + } + + logDebug("file: "__FILE__", line: %d, " \ + "alloc_once: %d", __LINE__, alloc_once); + } + + logDebug("file: "__FILE__", line: %d, " \ + "malloc task info as whole: %d, malloc loop count: %d", \ + __LINE__, g_free_queue.malloc_whole_block, loop_count); + + if (g_mpool != NULL) + { + g_free_queue.head = g_mpool->blocks; + g_free_queue.tail = mpool->last_block; + + /* + struct fast_task_info *pTask; + int task_count = 0; + + pTask = g_free_queue.head; + while (pTask != NULL) + { + task_count++; + pTask = pTask->next; + } + logDebug("file: "__FILE__", line: %d, " \ + "task count: %d", __LINE__, task_count); + */ + } + + return 0; +} + +void free_queue_destroy() +{ + struct mpool_chain *mpool; + struct mpool_chain *mp; + + if (g_mpool == NULL) + { + return; + } + + if (!g_free_queue.malloc_whole_block) + { + char *p; + char *pCharEnd; + int block_size; + struct fast_task_info *pTask; + + block_size = ALIGNED_TASK_INFO_SIZE + g_free_queue.arg_size; + pCharEnd = ((char *)g_mpool->blocks) + block_size * \ + g_free_queue.max_connections; + for (p=(char *)g_mpool->blocks; pdata != NULL) + { + free(pTask->data); + pTask->data = NULL; + } + } + } + + mpool = g_mpool; + while (mpool != NULL) + { + mp = mpool; + mpool = mpool->next; + + free(mp->blocks); + free(mp); + } + g_mpool = NULL; + + pthread_mutex_destroy(&(g_free_queue.lock)); +} + +struct fast_task_info *free_queue_pop() +{ + return task_queue_pop(&g_free_queue);; +} + +int free_queue_push(struct fast_task_info *pTask) +{ + char *new_buff; + int result; + + *(pTask->client_ip) = '\0'; + pTask->length = 0; + pTask->offset = 0; + pTask->req_count = 0; + + if (pTask->size > g_free_queue.min_buff_size) //need thrink + { + new_buff = (char *)malloc(g_free_queue.min_buff_size); + if (new_buff == NULL) + { + logWarning("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, g_free_queue.min_buff_size, \ + errno, STRERROR(errno)); + } + else + { + free(pTask->data); + pTask->size = g_free_queue.min_buff_size; + pTask->data = new_buff; + } + } + + if ((result=pthread_mutex_lock(&g_free_queue.lock)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_lock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + pTask->next = g_free_queue.head; + g_free_queue.head = pTask; + if (g_free_queue.tail == NULL) + { + g_free_queue.tail = pTask; + } + + if ((result=pthread_mutex_unlock(&g_free_queue.lock)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_unlock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + return result; +} + +int free_queue_count() +{ + return task_queue_count(&g_free_queue); +} + +int task_queue_push(struct fast_task_queue *pQueue, \ + struct fast_task_info *pTask) +{ + int result; + + if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_lock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + pTask->next = NULL; + if (pQueue->tail == NULL) + { + pQueue->head = pTask; + } + else + { + pQueue->tail->next = pTask; + } + pQueue->tail = pTask; + + if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_unlock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + return 0; +} + +struct fast_task_info *task_queue_pop(struct fast_task_queue *pQueue) +{ + struct fast_task_info *pTask; + int result; + + if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_lock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return NULL; + } + + pTask = pQueue->head; + if (pTask != NULL) + { + pQueue->head = pTask->next; + if (pQueue->head == NULL) + { + pQueue->tail = NULL; + } + } + + if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_unlock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + return pTask; +} + +int task_queue_count(struct fast_task_queue *pQueue) +{ + struct fast_task_info *pTask; + int count; + int result; + + if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_lock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return 0; + } + + count = 0; + pTask = pQueue->head; + while (pTask != NULL) + { + pTask = pTask->next; + count++; + } + + if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_unlock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + return count; +} + diff --git a/src/fast_task_queue.h b/src/fast_task_queue.h new file mode 100644 index 0000000..695ee25 --- /dev/null +++ b/src/fast_task_queue.h @@ -0,0 +1,96 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//fast_task_queue.h + +#ifndef _FAST_TASK_QUEUE_H +#define _FAST_TASK_QUEUE_H + +#include +#include +#include +#include +#include "common_define.h" +#include "ioevent.h" +#include "fast_timer.h" + +struct fast_task_info; + +typedef int (*TaskFinishCallBack) (struct fast_task_info *pTask); +typedef void (*TaskCleanUpCallBack) (struct fast_task_info *pTask); + +typedef void (*IOEventCallback) (int sock, short event, void *arg); + +typedef struct ioevent_entry +{ + int fd; + FastTimerEntry timer; + IOEventCallback callback; +} IOEventEntry; + +struct nio_thread_data +{ + struct ioevent_puller ev_puller; + struct fast_timer timer; + int pipe_fds[2]; + struct fast_task_info *deleted_list; +}; + +struct fast_task_info +{ + IOEventEntry event; + char client_ip[IP_ADDRESS_SIZE]; + void *arg; //extra argument pointer + char *data; //buffer for write or recv + int size; //alloc size + int length; //data length + int offset; //current offset + int req_count; //request count + TaskFinishCallBack finish_callback; + struct nio_thread_data *thread_data; + struct fast_task_info *next; +}; + +struct fast_task_queue +{ + struct fast_task_info *head; + struct fast_task_info *tail; + pthread_mutex_t lock; + int max_connections; + int min_buff_size; + int max_buff_size; + int arg_size; + bool malloc_whole_block; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int free_queue_init(const int max_connections, const int min_buff_size, \ + const int max_buff_size, const int arg_size); + +void free_queue_destroy(); + +int free_queue_push(struct fast_task_info *pTask); +struct fast_task_info *free_queue_pop(); +int free_queue_count(); + + +int task_queue_init(struct fast_task_queue *pQueue); +int task_queue_push(struct fast_task_queue *pQueue, \ + struct fast_task_info *pTask); +struct fast_task_info *task_queue_pop(struct fast_task_queue *pQueue); +int task_queue_count(struct fast_task_queue *pQueue); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fast_task_queue.o b/src/fast_task_queue.o new file mode 100644 index 0000000..09b8b77 Binary files /dev/null and b/src/fast_task_queue.o differ diff --git a/src/fast_timer.c b/src/fast_timer.c new file mode 100644 index 0000000..8462786 --- /dev/null +++ b/src/fast_timer.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include "logger.h" +#include "fast_timer.h" + +int fast_timer_init(FastTimer *timer, const int slot_count, + const int64_t current_time) +{ + int bytes; + if (slot_count <= 0 || current_time <= 0) { + return EINVAL; + } + + timer->slot_count = slot_count; + timer->base_time = current_time; //base time for slot 0 + timer->current_time = current_time; + bytes = sizeof(FastTimerSlot) * slot_count; + timer->slots = (FastTimerSlot *)malloc(bytes); + if (timer->slots == NULL) { + return errno != 0 ? errno : ENOMEM; + } + memset(timer->slots, 0, bytes); + return 0; +} + +void fast_timer_destroy(FastTimer *timer) +{ + if (timer->slots != NULL) { + free(timer->slots); + timer->slots = NULL; + } +} + +#define TIMER_GET_SLOT_INDEX(timer, expires) \ + (((expires) - timer->base_time) % timer->slot_count) + +#define TIMER_GET_SLOT_POINTER(timer, expires) \ + (timer->slots + TIMER_GET_SLOT_INDEX(timer, expires)) + +int fast_timer_add(FastTimer *timer, FastTimerEntry *entry) +{ + FastTimerSlot *slot; + + slot = TIMER_GET_SLOT_POINTER(timer, entry->expires > + timer->current_time ? entry->expires : timer->current_time); + entry->next = slot->head.next; + if (slot->head.next != NULL) { + slot->head.next->prev = entry; + } + entry->prev = &slot->head; + slot->head.next = entry; + return 0; +} + +int fast_timer_modify(FastTimer *timer, FastTimerEntry *entry, + const int64_t new_expires) +{ + if (new_expires == entry->expires) { + return 0; + } + + if (new_expires < entry->expires) { + fast_timer_remove(timer, entry); + entry->expires = new_expires; + return fast_timer_add(timer, entry); + } + + entry->rehash = TIMER_GET_SLOT_INDEX(timer, new_expires) != + TIMER_GET_SLOT_INDEX(timer, entry->expires); + entry->expires = new_expires; //lazy move + return 0; +} + +int fast_timer_remove(FastTimer *timer, FastTimerEntry *entry) +{ + if (entry->prev == NULL) { + return ENOENT; //already removed + } + + if (entry->next != NULL) { + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + entry->next = NULL; + } + else { + entry->prev->next = NULL; + } + + entry->prev = NULL; + return 0; +} + +FastTimerSlot *fast_timer_slot_get(FastTimer *timer, const int64_t current_time) +{ + if (timer->current_time >= current_time) { + return NULL; + } + + return TIMER_GET_SLOT_POINTER(timer, timer->current_time++); +} + +int fast_timer_timeouts_get(FastTimer *timer, const int64_t current_time, + FastTimerEntry *head) +{ + FastTimerSlot *slot; + FastTimerEntry *entry; + FastTimerEntry *first; + FastTimerEntry *last; + FastTimerEntry *tail; + int count; + + head->prev = NULL; + head->next = NULL; + if (timer->current_time >= current_time) { + return 0; + } + + first = NULL; + last = NULL; + tail = head; + count = 0; + while (timer->current_time < current_time) { + slot = TIMER_GET_SLOT_POINTER(timer, timer->current_time++); + entry = slot->head.next; + while (entry != NULL) { + if (entry->expires >= current_time) { //not expired + if (first != NULL) { + first->prev->next = entry; + entry->prev = first->prev; + + tail->next = first; + first->prev = tail; + tail = last; + first = NULL; + } + if (entry->rehash) { + last = entry; + entry = entry->next; + + last->rehash = false; + fast_timer_remove(timer, last); + fast_timer_add(timer, last); + continue; + } + } + else { + count++; + if (first == NULL) { + first = entry; + } + } + last = entry; + entry = entry->next; + } + + if (first != NULL) { + first->prev->next = NULL; + + tail->next = first; + first->prev = tail; + tail = last; + first = NULL; + } + } + + if (count > 0) { + tail->next = NULL; + } + + return count; +} + diff --git a/src/fast_timer.h b/src/fast_timer.h new file mode 100644 index 0000000..a3e3e39 --- /dev/null +++ b/src/fast_timer.h @@ -0,0 +1,48 @@ +#ifndef __FAST_TIMER_H__ +#define __FAST_TIMER_H__ + +#include +#include "common_define.h" + +typedef struct fast_timer_entry { + int64_t expires; + void *data; + struct fast_timer_entry *prev; + struct fast_timer_entry *next; + bool rehash; +} FastTimerEntry; + +typedef struct fast_timer_slot { + struct fast_timer_entry head; +} FastTimerSlot; + +typedef struct fast_timer { + int slot_count; //time wheel slot count + int64_t base_time; //base time for slot 0 + int64_t current_time; + FastTimerSlot *slots; +} FastTimer; + +#ifdef __cplusplus +extern "C" { +#endif + +int fast_timer_init(FastTimer *timer, const int slot_count, + const int64_t current_time); +void fast_timer_destroy(FastTimer *timer); + +int fast_timer_add(FastTimer *timer, FastTimerEntry *entry); +int fast_timer_remove(FastTimer *timer, FastTimerEntry *entry); +int fast_timer_modify(FastTimer *timer, FastTimerEntry *entry, + const int64_t new_expires); + +FastTimerSlot *fast_timer_slot_get(FastTimer *timer, const int64_t current_time); +int fast_timer_timeouts_get(FastTimer *timer, const int64_t current_time, + FastTimerEntry *head); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fast_timer.o b/src/fast_timer.o new file mode 100644 index 0000000..a0f7646 Binary files /dev/null and b/src/fast_timer.o differ diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..b8a49d5 --- /dev/null +++ b/src/hash.c @@ -0,0 +1,1402 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include "pthread_func.h" +#include "hash.h" + +static unsigned int prime_array[] = { + 1, /* 0 */ + 3, /* 1 */ + 17, /* 2 */ + 37, /* 3 */ + 79, /* 4 */ + 163, /* 5 */ + 331, /* 6 */ + 673, /* 7 */ + 1361, /* 8 */ + 2729, /* 9 */ + 5471, /* 10 */ + 10949, /* 11 */ + 21911, /* 12 */ + 43853, /* 13 */ + 87719, /* 14 */ + 175447, /* 15 */ + 350899, /* 16 */ + 701819, /* 17 */ + 1403641, /* 18 */ + 2807303, /* 19 */ + 5614657, /* 20 */ + 11229331, /* 21 */ + 22458671, /* 22 */ + 44917381, /* 23 */ + 89834777, /* 24 */ + 179669557, /* 25 */ + 359339171, /* 26 */ + 718678369, /* 27 */ + 1437356741, /* 28 */ + 2147483647 /* 29 (largest signed int prime) */ +}; + +#define PRIME_ARRAY_SIZE 30 + +static int _hash_alloc_buckets(HashArray *pHash, const unsigned int old_capacity) +{ + size_t bytes; + + bytes = sizeof(HashData *) * (*pHash->capacity); + if (pHash->max_bytes > 0 && pHash->bytes_used+bytes > pHash->max_bytes) + { + return ENOSPC; + } + + pHash->buckets = (HashData **)malloc(bytes); + if (pHash->buckets == NULL) + { + return ENOMEM; + } + + memset(pHash->buckets, 0, bytes); + pHash->bytes_used += bytes - sizeof(HashData *) * old_capacity; + + return 0; +} + +int hash_init_ex(HashArray *pHash, HashFunc hash_func, \ + const unsigned int capacity, const double load_factor, \ + const int64_t max_bytes, const bool bMallocValue) +{ + unsigned int *pprime; + unsigned int *prime_end; + int result; + + memset(pHash, 0, sizeof(HashArray)); + prime_end = prime_array + PRIME_ARRAY_SIZE; + for (pprime = prime_array; pprime!=prime_end; pprime++) + { + if (*pprime > capacity) + { + pHash->capacity = pprime; + break; + } + } + + if (pHash->capacity == NULL) + { + return EINVAL; + } + + if ((result=_hash_alloc_buckets(pHash, 0)) != 0) + { + return result; + } + + pHash->hash_func = hash_func; + pHash->max_bytes = max_bytes; + pHash->is_malloc_value = bMallocValue; + + if (load_factor >= 0.00 && load_factor <= 1.00) + { + pHash->load_factor = load_factor; + } + else + { + pHash->load_factor = 0.50; + } + + return 0; +} + +int hash_set_locks(HashArray *pHash, const int lock_count) +{ + size_t bytes; + pthread_mutex_t *lock; + pthread_mutex_t *lock_end; + + if (pHash->locks != NULL) + { + return EEXIST; + } + + if (lock_count <= 0) + { + return EINVAL; + } + + if (pHash->load_factor >= 0.10) + { + return EINVAL; + } + + bytes = sizeof(pthread_mutex_t) * lock_count; + pHash->locks = (pthread_mutex_t *)malloc(bytes); + if (pHash->locks == NULL) + { + return ENOMEM; + } + + pHash->lock_count = lock_count; + lock_end = pHash->locks + lock_count; + for (lock=pHash->locks; lockbuckets == NULL) + { + return; + } + + bucket_end = pHash->buckets + (*pHash->capacity); + for (ppBucket=pHash->buckets; ppBucketnext; + free(pDelete); + } + } + + free(pHash->buckets); + pHash->buckets = NULL; + if (pHash->is_malloc_capacity) + { + free(pHash->capacity); + pHash->capacity = NULL; + pHash->is_malloc_capacity = false; + } + + pHash->item_count = 0; + pHash->bytes_used = 0; +} + +#define ADD_TO_BUCKET(pHash, ppBucket, hash_data) \ + hash_data->next = *ppBucket; \ + *ppBucket = hash_data; \ + pHash->item_count++; + + +#define DELETE_FROM_BUCKET(pHash, ppBucket, previous, hash_data) \ + if (previous == NULL) \ + { \ + *ppBucket = hash_data->next; \ + } \ + else \ + { \ + previous->next = hash_data->next; \ + } \ + pHash->item_count--; \ + pHash->bytes_used -= CALC_NODE_MALLOC_BYTES(hash_data->key_len, \ + hash_data->malloc_value_size); \ + free(hash_data); + +#define HASH_LOCK(pHash, index) \ + if (pHash->lock_count > 0) \ + { \ + pthread_mutex_lock(pHash->locks + (index) % pHash->lock_count); \ + } + +#define HASH_UNLOCK(pHash, index) \ + if (pHash->lock_count > 0) \ + { \ + pthread_mutex_unlock(pHash->locks + (index) % pHash->lock_count); \ + } + + +int hash_stat(HashArray *pHash, HashStat *pStat, \ + int *stat_by_lens, const int stat_size) +{ + HashData **ppBucket; + HashData **bucket_end; + HashData *hash_data; + int totalLength; + int last; + int count; + int i; + + memset(stat_by_lens, 0, sizeof(int) * stat_size); + pStat->bucket_max_length = 0; + pStat->bucket_used = 0; + last = stat_size - 1; + bucket_end = pHash->buckets + (*pHash->capacity); + for (ppBucket=pHash->buckets; ppBucketnext; + } + + pStat->bucket_used++; + if (count > last) + { + return ENOSPC; + } + stat_by_lens[count]++; + + if (count > pStat->bucket_max_length) + { + pStat->bucket_max_length = count; + } + } + + totalLength = 0; + for (i=0; i<=pStat->bucket_max_length; i++) + { + if (stat_by_lens[i] > 0) + { + totalLength += i * stat_by_lens[i]; + } + } + + pStat->capacity = *(pHash->capacity); + pStat->item_count = pHash->item_count; + pStat->bucket_avg_length = pStat->bucket_used > 0 ? \ + (double)totalLength / (double)pStat->bucket_used : 0.00; + + return 0; +} + +void hash_stat_print(HashArray *pHash) +{ +#define STAT_MAX_NUM 64 + HashStat hs; + int stats[STAT_MAX_NUM]; + + if (hash_stat(pHash, &hs, stats, STAT_MAX_NUM) != 0) + { + printf("hash max length exceeds %d!\n", STAT_MAX_NUM); + return; + } + + /* + printf("collision stat:\n"); + for (i=0; i 0) printf("%d: %d\n", i+1, stats[i]); + } + if (stats[i] > 0) printf(">=%d: %d\n", i+1, stats[i]); + */ + + printf("capacity: %d, item_count=%d, bucket_used: %d, " \ + "avg length: %.4f, max length: %d, bucket / item = %.2f%%\n", + hs.capacity, hs.item_count, hs.bucket_used, + hs.bucket_avg_length, hs.bucket_max_length, + (double)hs.bucket_used*100.00/(double)hs.capacity); +} + +static int _rehash1(HashArray *pHash, const int old_capacity, \ + unsigned int *new_capacity) +{ + HashData **old_buckets; + HashData **ppBucket; + HashData **bucket_end; + HashData *hash_data; + HashData *pNext; + int result; + + old_buckets = pHash->buckets; + pHash->capacity = new_capacity; + if ((result=_hash_alloc_buckets(pHash, old_capacity)) != 0) + { + pHash->buckets = old_buckets; + return result; + } + + //printf("old: %d, new: %d\n", old_capacity, *pHash->capacity); + + pHash->item_count = 0; + bucket_end = old_buckets + old_capacity; + for (ppBucket=old_buckets; ppBucketnext; + + ADD_TO_BUCKET(pHash, (pHash->buckets + \ + (HASH_CODE(pHash, hash_data) % \ + (*pHash->capacity))), hash_data) + + hash_data = pNext; + } + } + + free(old_buckets); + return 0; +} + +static int _rehash(HashArray *pHash) +{ + int result; + unsigned int *pOldCapacity; + + pOldCapacity = pHash->capacity; + if (pHash->is_malloc_capacity) + { + unsigned int *pprime; + unsigned int *prime_end; + + pHash->capacity = NULL; + + prime_end = prime_array + PRIME_ARRAY_SIZE; + for (pprime = prime_array; pprime!=prime_end; pprime++) + { + if (*pprime > *pOldCapacity) + { + pHash->capacity = pprime; + break; + } + } + } + else + { + pHash->capacity++; + } + + if ((result=_rehash1(pHash, *pOldCapacity, pHash->capacity)) != 0) + { + pHash->capacity = pOldCapacity; //rollback + } + else + { + if (pHash->is_malloc_capacity) + { + free(pOldCapacity); + pHash->is_malloc_capacity = false; + } + } + + /*printf("rehash, old_capacity=%d, new_capacity=%d\n", \ + old_capacity, *pHash->capacity); + */ + return result; +} + +static int _hash_conflict_count(HashArray *pHash) +{ + HashData **ppBucket; + HashData **bucket_end; + HashData *hash_data; + HashData *pNext; + int conflicted; + int conflict_count; + + bucket_end = pHash->buckets + (*pHash->capacity); + conflict_count = 0; + for (ppBucket=pHash->buckets; ppBucketnext == NULL) + { + continue; + } + + conflicted = 0; + hash_data = *ppBucket; + while (hash_data != NULL) + { + pNext = hash_data->next; + while (pNext != NULL) + { + if (HASH_CODE(pHash, hash_data) != \ + HASH_CODE(pHash, pNext)) + { + conflicted = 1; + break; + } + + pNext = pNext->next; + } + + if (conflicted) + { + break; + } + + hash_data = hash_data->next; + } + + conflict_count += conflicted; + } + + return conflict_count; +} + +int hash_best_op(HashArray *pHash, const int suggest_capacity) +{ + int old_capacity; + int conflict_count; + unsigned int *new_capacity; + int result; + + if ((conflict_count=_hash_conflict_count(pHash)) == 0) + { + return 0; + } + + old_capacity = *pHash->capacity; + new_capacity = (unsigned int *)malloc(sizeof(unsigned int)); + if (new_capacity == NULL) + { + return -ENOMEM; + } + + if ((suggest_capacity > 2) && (suggest_capacity >= pHash->item_count)) + { + *new_capacity = suggest_capacity - 2; + if (*new_capacity % 2 == 0) + { + ++(*new_capacity); + } + } + else + { + *new_capacity = 2 * (pHash->item_count - 1) + 1; + } + + do + { + do + { + *new_capacity += 2; + } while ((*new_capacity % 3 == 0) || (*new_capacity % 5 == 0) \ + || (*new_capacity % 7 == 0)); + + if ((result=_rehash1(pHash, old_capacity, new_capacity)) != 0) + { + pHash->is_malloc_capacity = \ + (pHash->capacity == new_capacity); + *pHash->capacity = old_capacity; + return -1 * result; + } + + old_capacity = *new_capacity; + /*printf("rehash, conflict_count=%d, old_capacity=%d, " \ + "new_capacity=%d\n", conflict_count, \ + old_capacity, *new_capacity); + */ + } while ((conflict_count=_hash_conflict_count(pHash)) > 0); + + pHash->is_malloc_capacity = true; + + //hash_stat_print(pHash); + return 1; +} + +static HashData *_chain_find_entry(HashData **ppBucket, const void *key, \ + const int key_len, const unsigned int hash_code) +{ + HashData *hash_data; + + hash_data = *ppBucket; + while (hash_data != NULL) + { + if (key_len == hash_data->key_len && \ + memcmp(key, hash_data->key, key_len) == 0) + { + return hash_data; + } + + hash_data = hash_data->next; + } + + return NULL; +} + +HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len) +{ + unsigned int hash_code; + HashData **ppBucket; + HashData *hash_data; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + HASH_LOCK(pHash, ppBucket - pHash->buckets) + hash_data = _chain_find_entry(ppBucket, key, key_len, hash_code); + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + + return hash_data; +} + +void *hash_find(HashArray *pHash, const void *key, const int key_len) +{ + unsigned int hash_code; + HashData **ppBucket; + HashData *hash_data; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + HASH_LOCK(pHash, ppBucket - pHash->buckets) + hash_data = _chain_find_entry(ppBucket, key, key_len, hash_code); + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + + if (hash_data != NULL) + { + return hash_data->value; + } + else + { + return NULL; + } +} + +int hash_get(HashArray *pHash, const void *key, const int key_len, + void *value, int *value_len) +{ + unsigned int hash_code; + int result; + HashData **ppBucket; + HashData *hash_data; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + HASH_LOCK(pHash, ppBucket - pHash->buckets) + hash_data = _chain_find_entry(ppBucket, key, key_len, hash_code); + if (hash_data != NULL) + { + if (hash_data->value_len <= *value_len) + { + *value_len = hash_data->value_len; + memcpy(value, hash_data->value, hash_data->value_len); + result = 0; + } + else + { + result = ENOSPC; + } + } + else + { + result = ENOENT; + } + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + return result; +} + +int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \ + void *value, const int value_len, const bool needLock) +{ + unsigned int hash_code; + HashData **ppBucket; + HashData *hash_data; + HashData *previous; + char *pBuff; + int bytes; + int malloc_value_size; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + previous = NULL; + + if (needLock) + { + HASH_LOCK(pHash, ppBucket - pHash->buckets) + } + + hash_data = *ppBucket; + while (hash_data != NULL) + { + if (key_len == hash_data->key_len && \ + memcmp(key, hash_data->key, key_len) == 0) + { + break; + } + + previous = hash_data; + hash_data = hash_data->next; + } + + if (hash_data != NULL) //exists + { + if (!pHash->is_malloc_value) + { + hash_data->value_len = value_len; + hash_data->value = (char *)value; + if (needLock) + { + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + } + return 0; + } + else + { + if (hash_data->malloc_value_size >= value_len && \ + (hash_data->malloc_value_size <= 128 || + hash_data->malloc_value_size / 2 < value_len)) + { + hash_data->value_len = value_len; + memcpy(hash_data->value, value, value_len); + if (needLock) + { + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + } + return 0; + } + + DELETE_FROM_BUCKET(pHash, ppBucket, previous, hash_data) + } + } + if (needLock) + { + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + } + + if (!pHash->is_malloc_value) + { + malloc_value_size = 0; + } + else + { + malloc_value_size = MEM_ALIGN(value_len); + } + + bytes = CALC_NODE_MALLOC_BYTES(key_len, malloc_value_size); + if (pHash->max_bytes > 0 && pHash->bytes_used+bytes > pHash->max_bytes) + { + return -ENOSPC; + } + + pBuff = (char *)malloc(bytes); + if (pBuff == NULL) + { + return -ENOMEM; + } + + pHash->bytes_used += bytes; + + hash_data = (HashData *)pBuff; + hash_data->malloc_value_size = malloc_value_size; + + hash_data->key_len = key_len; + memcpy(hash_data->key, key, key_len); +#ifdef HASH_STORE_HASH_CODE + hash_data->hash_code = hash_code; +#endif + hash_data->value_len = value_len; + + if (!pHash->is_malloc_value) + { + hash_data->value = (char *)value; + } + else + { + hash_data->value = hash_data->key + hash_data->key_len; + memcpy(hash_data->value, value, value_len); + } + + if (needLock) + { + HASH_LOCK(pHash, ppBucket - pHash->buckets) + ADD_TO_BUCKET(pHash, ppBucket, hash_data) + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + } + else + { + ADD_TO_BUCKET(pHash, ppBucket, hash_data) + } + + if (pHash->load_factor >= 0.10 && (double)pHash->item_count / + (double)*pHash->capacity >= pHash->load_factor) + { + _rehash(pHash); + } + + return 1; +} + +int64_t hash_inc_value(const HashData *old_data, const int inc, + char *new_value, int *new_value_len, void *arg) +{ + int64_t n; + if (old_data != NULL) + { + if (old_data->value_len < *new_value_len) + { + memcpy(new_value, old_data->value, old_data->value_len); + new_value[old_data->value_len] = '\0'; + n = strtoll(new_value, NULL, 10); + n += inc; + } + else + { + n = inc; + } + *new_value_len = sprintf(new_value, INT64_PRINTF_FORMAT, n); + } + else + { + n = inc; + *new_value_len = sprintf(new_value, INT64_PRINTF_FORMAT, n); + } + + return n; +} + +int hash_inc_ex(HashArray *pHash, const void *key, const int key_len, + const int inc, char *value, int *value_len, + ConvertValueFunc convert_func, void *arg) +{ + unsigned int hash_code; + int result; + HashData **ppBucket; + HashData *hash_data; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + HASH_LOCK(pHash, ppBucket - pHash->buckets) + hash_data = _chain_find_entry(ppBucket, key, key_len, hash_code); + convert_func(hash_data, inc, value, value_len, arg); + if (hash_data != NULL) + { + if (!pHash->is_malloc_value) + { + hash_data->value_len = *value_len; + hash_data->value = (char *)value; + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + return 0; + } + else + { + if (hash_data->malloc_value_size >= *value_len) + { + hash_data->value_len = *value_len; + memcpy(hash_data->value, value, *value_len); + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + return 0; + } + } + } + result = hash_insert_ex(pHash, key, key_len, value, *value_len, false); + if (result < 0) + { + *value = '\0'; + *value_len = 0; + result *= -1; + } + else + { + result = 0; + } + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + + return result; +} + +int hash_partial_set(HashArray *pHash, const void *key, const int key_len, + const char *value, const int offset, const int value_len) +{ + unsigned int hash_code; + int result; + HashData **ppBucket; + HashData *hash_data; + char *pNewBuff; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + HASH_LOCK(pHash, ppBucket - pHash->buckets) + hash_data = _chain_find_entry(ppBucket, key, key_len, hash_code); + do + { + if (hash_data != NULL) + { + if (offset < 0 || offset >= hash_data->value_len) + { + result = EINVAL; + break; + } + if (offset + value_len <= hash_data->value_len) + { + memcpy(hash_data->value+offset, value, value_len); + result = 0; + break; + } + + pNewBuff = (char *)malloc(offset + value_len); + if (pNewBuff == NULL) + { + result = errno != 0 ? errno : ENOMEM; + break; + } + + if (offset > 0) + { + memcpy(pNewBuff, hash_data->value, offset); + } + memcpy(pNewBuff + offset, value, value_len); + result = hash_insert_ex(pHash, key, key_len, pNewBuff, + offset + value_len, false); + free(pNewBuff); + } + else + { + if (offset != 0) + { + result = ENOENT; + break; + } + result = hash_insert_ex(pHash, key, key_len, (void *)value, + value_len, false); + } + + if (result < 0) + { + result *= -1; + } + else + { + result = 0; + } + } while (0); + + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + return result; +} + +int hash_delete(HashArray *pHash, const void *key, const int key_len) +{ + HashData **ppBucket; + HashData *hash_data; + HashData *previous; + unsigned int hash_code; + int result; + + hash_code = pHash->hash_func(key, key_len); + ppBucket = pHash->buckets + (hash_code % (*pHash->capacity)); + + result = ENOENT; + previous = NULL; + HASH_LOCK(pHash, ppBucket - pHash->buckets) + hash_data = *ppBucket; + while (hash_data != NULL) + { + if (key_len == hash_data->key_len && \ + memcmp(key, hash_data->key, key_len) == 0) + { + DELETE_FROM_BUCKET(pHash, ppBucket, previous, hash_data) + result = 0; + break; + } + + previous = hash_data; + hash_data = hash_data->next; + } + HASH_UNLOCK(pHash, ppBucket - pHash->buckets) + + return result; +} + +int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args) +{ + HashData **ppBucket; + HashData **bucket_end; + HashData *hash_data; + int index; + int result; + + index = 0; + bucket_end = pHash->buckets + (*pHash->capacity); + for (ppBucket=pHash->buckets; ppBucketnext; + } + } + + return 0; +} + +int hash_count(HashArray *pHash) +{ + return pHash->item_count; +} + +int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index) +{ + if (pHash->lock_count <= 0) + { + return 0; + } + + return pthread_mutex_lock(pHash->locks + bucket_index % + pHash->lock_count); +} + +int hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index) +{ + if (pHash->lock_count <= 0) + { + return 0; + } + + return pthread_mutex_unlock(pHash->locks + bucket_index % + pHash->lock_count); +} + +// RS Hash Function +int RSHash(const void *key, const int key_len) +{ + unsigned char *pKey; + unsigned char *pEnd; + int a = 63689; + int hash = 0; + + pEnd = (unsigned char *)key + key_len; + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) + { + hash = hash * a + (*pKey); + a *= 378551; + } + + return hash; +} + +#define JS_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int hash; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash ^= ((hash << 5) + (*pKey) + (hash >> 2)); \ + } \ + \ + return hash; \ + + +// JS Hash Function +int JSHash(const void *key, const int key_len) +{ + JS_HASH_FUNC(1315423911) +} + +int JSHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + JS_HASH_FUNC(init_value) +} + +#define BITS_IN_UNIGNED_INT (int)(sizeof(int) * 8) +#define THREE_QUARTERS (int)((BITS_IN_UNIGNED_INT * 3) / 4) +#define HASH_ONE_EIGHTH (int)(BITS_IN_UNIGNED_INT / 8) +#define HASH_HIGH_BITS (int)((unsigned int)(0xFFFFFFFF) << \ + (BITS_IN_UNIGNED_INT - HASH_ONE_EIGHTH)) + +#define PJW_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int hash; \ + int test; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash = (hash << HASH_ONE_EIGHTH) + (*(pKey)); \ + if ((test = hash & HASH_HIGH_BITS) != 0) \ + { \ + hash = ((hash ^ (test >> THREE_QUARTERS)) & (~HASH_HIGH_BITS)); \ + } \ + } \ + \ + return hash; \ + + +// P.J.Weinberger Hash Function, same as ELF Hash +int PJWHash(const void *key, const int key_len) +{ + PJW_HASH_FUNC(0) +} + +int PJWHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + PJW_HASH_FUNC(init_value) +} + +#define ELF_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int hash; \ + int x; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash = (hash << 4) + (*pKey); \ + if ((x = hash & 0xF0000000) != 0) \ + { \ + hash ^= (x >> 24); \ + hash &= ~x; \ + } \ + } \ + \ + return hash; \ + + +// ELF Hash Function, same as PJW Hash +int ELFHash(const void *key, const int key_len) +{ + ELF_HASH_FUNC(0) +} + +int ELFHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + ELF_HASH_FUNC(init_value) +} + +#define BKDR_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int seed = 131; /* 31 131 1313 13131 131313 etc..*/ \ + int hash; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash = hash * seed + (*pKey); \ + } \ + \ + return hash; \ + + +// BKDR Hash Function +int BKDRHash(const void *key, const int key_len) +{ + BKDR_HASH_FUNC(0) +} + +int BKDRHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + BKDR_HASH_FUNC(init_value) +} + +#define SDBM_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int hash; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash = (*pKey) + (hash << 6) + (hash << 16) - hash; \ + } \ + \ + return hash; \ + + +// SDBM Hash Function +int SDBMHash(const void *key, const int key_len) +{ + SDBM_HASH_FUNC(0) +} + +int SDBMHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + SDBM_HASH_FUNC(init_value) +} + +#define TIME33_HASH_FUNC(init_value) \ + int nHash; \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + \ + nHash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + nHash += (nHash << 5) + (*pKey); \ + } \ + \ + return nHash; \ + + +int Time33Hash(const void *key, const int key_len) +{ + TIME33_HASH_FUNC(0) +} + +int Time33Hash_ex(const void *key, const int key_len, \ + const int init_value) +{ + TIME33_HASH_FUNC(init_value) +} + +#define DJB_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int hash; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash += (hash << 5) + (*pKey); \ + } \ + \ + return hash; \ + + +// DJB Hash Function +int DJBHash(const void *key, const int key_len) +{ + DJB_HASH_FUNC(5381) +} + +int DJBHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + DJB_HASH_FUNC(init_value) +} + +#define AP_HASH_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int i; \ + int hash; \ + \ + hash = init_value; \ + \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key, i=0; pKey != pEnd; pKey++, i++) \ + { \ + if ((i & 1) == 0) \ + { \ + hash ^= ((hash << 7) ^ (*pKey) ^ (hash >> 3)); \ + } \ + else \ + { \ + hash ^= (~((hash << 11) ^ (*pKey) ^ (hash >> 5))); \ + } \ + } \ + \ + return hash; \ + + +// AP Hash Function +int APHash(const void *key, const int key_len) +{ + AP_HASH_FUNC(0) +} + +int APHash_ex(const void *key, const int key_len, \ + const int init_value) +{ + AP_HASH_FUNC(init_value) +} + +int calc_hashnr (const void* key, const int key_len) +{ + unsigned char *pKey; + unsigned char *pEnd; + int nr = 1, nr2 = 4; + + pEnd = (unsigned char *)key + key_len; + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) + { + nr ^= (((nr & 63) + nr2) * (*pKey)) + (nr << 8); + nr2 += 3; + } + + return nr; + +} + +#define CALC_HASHNR1_FUNC(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int hash; \ + \ + hash = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + hash *= 16777619; \ + hash ^= *pKey; \ + } \ + return hash; \ + +int calc_hashnr1(const void* key, const int key_len) +{ + CALC_HASHNR1_FUNC(0) +} + +int calc_hashnr1_ex(const void* key, const int key_len, \ + const int init_value) +{ + CALC_HASHNR1_FUNC(init_value) +} + +#define SIMPLE_HASH_FUNC(init_value) \ + int h; \ + unsigned char *p; \ + unsigned char *pEnd; \ + \ + h = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (p = (unsigned char *)key; p!= pEnd; p++) \ + { \ + h = 31 * h + *p; \ + } \ + \ + return h; \ + +int simple_hash(const void* key, const int key_len) +{ + SIMPLE_HASH_FUNC(0) +} + +int simple_hash_ex(const void* key, const int key_len, \ + const int init_value) +{ + SIMPLE_HASH_FUNC(init_value) +} + +static unsigned int crc_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +#define CRC32_BODY(init_value) \ + unsigned char *pKey; \ + unsigned char *pEnd; \ + int crc; \ + \ + crc = init_value; \ + pEnd = (unsigned char *)key + key_len; \ + for (pKey = (unsigned char *)key; pKey != pEnd; pKey++) \ + { \ + crc = crc_table[(crc ^ *pKey) & 0xFF] ^ (crc >> 8); \ + } \ + +int CRC32(void *key, const int key_len) +{ + CRC32_BODY(CRC32_XINIT) + + return crc ^ CRC32_XOROT; +} + +int CRC32_ex(void *key, const int key_len, \ + const int init_value) +{ + CRC32_BODY(init_value) + + return crc; +} + diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..9be6001 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,381 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#ifndef _HASH_H_ +#define _HASH_H_ + +#include +#include +#include "common_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CRC32_XINIT 0xFFFFFFFF /* initial value */ +#define CRC32_XOROT 0xFFFFFFFF /* final xor value */ + +typedef int (*HashFunc) (const void *key, const int key_len); + +#ifdef HASH_STORE_HASH_CODE +#define HASH_CODE(pHash, hash_data) hash_data->hash_code +#else +#define HASH_CODE(pHash, hash_data) ((unsigned int)pHash->hash_func( \ + hash_data->key, hash_data->key_len)) +#endif + +#define CALC_NODE_MALLOC_BYTES(key_len, value_size) \ + sizeof(HashData) + key_len + value_size + +#define FREE_HASH_DATA(pHash, hash_data) \ + pHash->item_count--; \ + pHash->bytes_used -= CALC_NODE_MALLOC_BYTES(hash_data->key_len, \ + hash_data->malloc_value_size); \ + free(hash_data); + + +typedef struct tagHashData +{ + int key_len; + int value_len; + int malloc_value_size; + +#ifdef HASH_STORE_HASH_CODE + unsigned int hash_code; +#endif + + char *value; + struct tagHashData *next; + char key[0]; +} HashData; + +typedef int64_t (*ConvertValueFunc)(const HashData *old_data, const int inc, + char *new_value, int *new_value_len, void *arg); + +typedef struct tagHashArray +{ + HashData **buckets; + HashFunc hash_func; + int item_count; + unsigned int *capacity; + double load_factor; + int64_t max_bytes; + int64_t bytes_used; + bool is_malloc_capacity; + bool is_malloc_value; + unsigned int lock_count; + pthread_mutex_t *locks; +} HashArray; + +typedef struct tagHashStat +{ + unsigned int capacity; + int item_count; + int bucket_used; + double bucket_avg_length; + int bucket_max_length; +} HashStat; + +/** + * hash walk function + * parameters: + * index: item index based 0 + * data: hash data, including key and value + * args: passed by hash_walk function + * return 0 for success, != 0 for error +*/ +typedef int (*HashWalkFunc)(const int index, const HashData *data, void *args); + +#define hash_init(pHash, hash_func, capacity, load_factor) \ + hash_init_ex(pHash, hash_func, capacity, load_factor, 0, false) + +#define hash_insert(pHash, key, key_len, value) \ + hash_insert_ex(pHash, key, key_len, value, 0, true) + +/** + * hash init function + * parameters: + * pHash: the hash table + * hash_func: hash function + * capacity: init capacity + * load_factor: hash load factor, such as 0.75 + * max_bytes: max memory can be used (bytes) + * bMallocValue: if need malloc value buffer + * return 0 for success, != 0 for error +*/ +int hash_init_ex(HashArray *pHash, HashFunc hash_func, \ + const unsigned int capacity, const double load_factor, \ + const int64_t max_bytes, const bool bMallocValue); + +/** + * set hash locks function + * parameters: + * lock_count: the lock count + * return 0 for success, != 0 for error +*/ +int hash_set_locks(HashArray *pHash, const int lock_count); + +/** + * convert the value + * parameters: + * HashData: the old hash data + * inc: the increasement value + * new_value: return the new value + * new_value_len: return the length of the new value + * arg: the user data + * return the number after increasement +*/ +int64_t hash_inc_value(const HashData *old_data, const int inc, + char *new_value, int *new_value_len, void *arg); + +#define hash_inc(pHash, key, key_len, inc, value, value_len) \ + hash_inc_ex(pHash, key, key_len, inc, value, value_len, \ + hash_inc_value, NULL) + +/** + * atomic increase value + * parameters: + * pHash: the hash table + * key: the key to insert + * key_len: length of th key + * inc: the increasement value + * value: return the new value + * value_len: return the length of the new value + * convert_func: the convert function + * arg: the arg to convert function + * return 0 for success, != 0 for error (errno) + * +*/ +int hash_inc_ex(HashArray *pHash, const void *key, const int key_len, + const int inc, char *value, int *value_len, + ConvertValueFunc convert_func, void *arg); + +/** + * hash destroy function + * parameters: + * pHash: the hash table + * return none +*/ +void hash_destroy(HashArray *pHash); + +/** + * hash insert key + * parameters: + * pHash: the hash table + * key: the key to insert + * key_len: length of th key + * value: the value + * value_len: length of the value + * needLock: if need lock + * return >= 0 for success, 0 for key already exist (update), + * 1 for new key (insert), < 0 for error +*/ +int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \ + void *value, const int value_len, const bool needLock); + +/** + * hash find key + * parameters: + * pHash: the hash table + * key: the key to find + * key_len: length of th key + * return user data, return NULL when the key not exist +*/ +void *hash_find(HashArray *pHash, const void *key, const int key_len); + +/** + * hash find key + * parameters: + * pHash: the hash table + * key: the key to find + * key_len: length of th key + * return hash data, return NULL when the key not exist +*/ +HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len); + + +/** + * hash get the value of the key + * parameters: + * pHash: the hash table + * key: the key to find + * key_len: length of th key + * value: store the value + * value_len: input for the max size of the value + * output for the length fo the value + * return 0 for success, != 0 fail (errno) +*/ +int hash_get(HashArray *pHash, const void *key, const int key_len, + void *value, int *value_len); + + +/** + * hash partial set + * parameters: + * pHash: the hash table + * key: the key to insert + * key_len: length of th key + * value: the value + * offset: the offset of existed value + * value_len: length of the value + * return 0 for success, != 0 fail (errno) +*/ +int hash_partial_set(HashArray *pHash, const void *key, const int key_len, + const char *value, const int offset, const int value_len); + +/** + * hash delete key + * parameters: + * pHash: the hash table + * key: the key to delete + * key_len: length of th key + * return 0 for success, != 0 fail (errno) +*/ +int hash_delete(HashArray *pHash, const void *key, const int key_len); + +/** + * hash walk (iterator) + * parameters: + * pHash: the hash table + * walkFunc: walk (interator) function + * args: extra args which will be passed to walkFunc + * return 0 for success, != 0 fail (errno) +*/ +int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args); + +/** + * get hash item count + * parameters: + * pHash: the hash table + * return item count +*/ +int hash_count(HashArray *pHash); + +/** + * hash best optimize + * parameters: + * pHash: the hash table + * suggest_capacity: suggest init capacity for speed + * return >0 for success, < 0 fail (errno) +*/ +int hash_best_op(HashArray *pHash, const int suggest_capacity); + +/** + * hash stat + * parameters: + * pHash: the hash table + * pStat: return stat info + * stat_by_lens: return stats array by bucket length + * stat_by_lens[0] empty buckets count + * stat_by_lens[1] contain 1 key buckets count + * stat_by_lens[2] contain 2 key buckets count, etc + * stat_size: stats array size (contain max elments) + * return 0 for success, != 0 fail (errno) +*/ +int hash_stat(HashArray *pHash, HashStat *pStat, \ + int *stat_by_lens, const int stat_size); + +/** + * print hash stat info + * parameters: + * pHash: the hash table + * return none +*/ +void hash_stat_print(HashArray *pHash); + +/** + * lock the bucket of hash table + * parameters: + * pHash: the hash table + * bucket_index: the index of bucket + * return 0 for success, != 0 fail (errno) +*/ +int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index); + +/** + * unlock the bucket of hash table + * parameters: + * pHash: the hash table + * bucket_index: the index of bucket + * return 0 for success, != 0 fail (errno) +*/ +int hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index); + +int RSHash(const void *key, const int key_len); + +int JSHash(const void *key, const int key_len); +int JSHash_ex(const void *key, const int key_len, \ + const int init_value); + +int PJWHash(const void *key, const int key_len); +int PJWHash_ex(const void *key, const int key_len, \ + const int init_value); + +int ELFHash(const void *key, const int key_len); +int ELFHash_ex(const void *key, const int key_len, \ + const int init_value); + +int BKDRHash(const void *key, const int key_len); +int BKDRHash_ex(const void *key, const int key_len, \ + const int init_value); + +int SDBMHash(const void *key, const int key_len); +int SDBMHash_ex(const void *key, const int key_len, \ + const int init_value); + +int Time33Hash(const void *key, const int key_len); +int Time33Hash_ex(const void *key, const int key_len, \ + const int init_value); + +int DJBHash(const void *key, const int key_len); +int DJBHash_ex(const void *key, const int key_len, \ + const int init_value); + +int APHash(const void *key, const int key_len); +int APHash_ex(const void *key, const int key_len, \ + const int init_value); + +int calc_hashnr (const void* key, const int key_len); + +int calc_hashnr1(const void* key, const int key_len); +int calc_hashnr1_ex(const void* key, const int key_len, \ + const int init_value); + +int simple_hash(const void* key, const int key_len); +int simple_hash_ex(const void* key, const int key_len, \ + const int init_value); + +int CRC32(void *key, const int key_len); +int CRC32_ex(void *key, const int key_len, \ + const int init_value); + +#define CRC32_FINAL(crc) (crc ^ CRC32_XOROT) + +#define INIT_HASH_CODES4(hash_codes) \ + hash_codes[0] = CRC32_XINIT; \ + hash_codes[1] = 0; \ + hash_codes[2] = 0; \ + hash_codes[3] = 0; \ + +#define CALC_HASH_CODES4(buff, buff_len, hash_codes) \ + hash_codes[0] = CRC32_ex(buff, buff_len, hash_codes[0]); \ + hash_codes[1] = ELFHash_ex(buff, buff_len, hash_codes[1]); \ + hash_codes[2] = simple_hash_ex(buff, buff_len, hash_codes[2]); \ + hash_codes[3] = Time33Hash_ex(buff, buff_len, hash_codes[3]); \ + + +#define FINISH_HASH_CODES4(hash_codes) \ + hash_codes[0] = CRC32_FINAL(hash_codes[0]); \ + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/http_func.c b/src/http_func.c new file mode 100644 index 0000000..a543fc8 --- /dev/null +++ b/src/http_func.c @@ -0,0 +1,302 @@ + +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sockopt.h" +#include "logger.h" +#include "shared_func.h" + +int get_url_content(const char *url, const int connect_timeout, \ + const int network_timeout, int *http_status, \ + char **content, int *content_len, char *error_info) +{ + char domain_name[256]; + char ip_addr[IP_ADDRESS_SIZE]; + char out_buff[4096]; + int domain_len; + int url_len; + int out_len; + int alloc_size; + int recv_bytes; + int result; + int sock; + int port; + const char *pDomain; + const char *pContent; + const char *pURI; + char *pPort; + char *pSpace; + + *http_status = 0; + *content_len = 0; + *content = NULL; + + url_len = strlen(url); + if (url_len <= 7 || strncasecmp(url, "http://", 7) != 0) + { + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "invalid url.", __LINE__); + return EINVAL; + } + + pDomain = url + 7; + pURI = strchr(pDomain, '/'); + if (pURI == NULL) + { + domain_len = url_len - 7; + pURI = "/"; + } + else + { + domain_len = pURI - pDomain; + } + + if (domain_len >= sizeof(domain_name)) + { + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "domain is too large, exceed %d.", \ + __LINE__, (int)sizeof(domain_name)); + return EINVAL; + } + + memcpy(domain_name, pDomain, domain_len); + *(domain_name + domain_len) = '\0'; + pPort = strchr(domain_name, ':'); + if (pPort == NULL) + { + port = 80; + } + else + { + *pPort = '\0'; + port = atoi(pPort + 1); + } + + if (getIpaddrByName(domain_name, ip_addr, \ + sizeof(ip_addr)) == INADDR_NONE) + { + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "resolve domain \"%s\" fail.", \ + __LINE__, domain_name); + return EINVAL; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) + { + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "socket create failed, errno: %d, " \ + "error info: %s", __LINE__, \ + errno, STRERROR(errno)); + return errno != 0 ? errno : EPERM; + } + + if ((result=connectserverbyip_nb_auto(sock, ip_addr, port, \ + connect_timeout)) != 0) + { + close(sock); + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "connect to %s:%d fail, errno: %d, " \ + "error info: %s", __LINE__, domain_name, \ + port, result, STRERROR(result)); + + return result; + } + + out_len = snprintf(out_buff, sizeof(out_buff), \ + "GET %s HTTP/1.0\r\n" \ + "Host: %s:%d\r\n" \ + "Connection: close\r\n" \ + "\r\n", pURI, domain_name, port); + if ((result=tcpsenddata(sock, out_buff, out_len, network_timeout)) != 0) + { + close(sock); + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "send data to %s:%d fail, errno: %d, " \ + "error info: %s", __LINE__, domain_name, \ + port, result, STRERROR(result)); + + return result; + } + + alloc_size = 64 * 1024; + *content = (char *)malloc(alloc_size + 1); + if (*content == NULL) + { + close(sock); + result = errno != 0 ? errno : ENOMEM; + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, errno: %d, " \ + "error info: %s", __LINE__, alloc_size + 1, \ + result, STRERROR(result)); + + return result; + } + + do + { + recv_bytes = alloc_size - *content_len; + if (recv_bytes <= 0) + { + alloc_size *= 2; + *content = (char *)realloc(*content, alloc_size + 1); + if (*content == NULL) + { + close(sock); + result = errno != 0 ? errno : ENOMEM; + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "realloc %d bytes fail, errno: %d, " \ + "error info: %s", __LINE__, \ + alloc_size + 1, \ + result, STRERROR(result)); + + return result; + } + + recv_bytes = alloc_size - *content_len; + } + + result = tcprecvdata_ex(sock, *content + *content_len, \ + recv_bytes, network_timeout, &recv_bytes); + + *content_len += recv_bytes; + } while (result == 0); + + if (result != ENOTCONN) + { + close(sock); + free(*content); + *content = NULL; + *content_len = 0; + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "recv data from %s:%d fail, errno: %d, " \ + "error info: %s", __LINE__, domain_name, \ + port, result, STRERROR(result)); + + return result; + } + + *(*content + *content_len) = '\0'; + pContent = strstr(*content, "\r\n\r\n"); + if (pContent == NULL) + { + close(sock); + free(*content); + *content = NULL; + *content_len = 0; + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "response data from %s:%d is invalid", \ + __LINE__, domain_name, port); + + return EINVAL; + } + + pContent += 4; + pSpace = strchr(*content, ' '); + if (pSpace == NULL || pSpace >= pContent) + { + close(sock); + free(*content); + *content = NULL; + *content_len = 0; + + sprintf(error_info, "file: "__FILE__", line: %d, " \ + "response data from %s:%d is invalid", \ + __LINE__, domain_name, port); + + return EINVAL; + } + + *http_status = atoi(pSpace + 1); + *content_len -= pContent - *content; + memcpy(*content, pContent, *content_len); + *(*content + *content_len) = '\0'; + + close(sock); + + *error_info = '\0'; + return 0; +} + +int http_parse_query(char *url, KeyValuePair *params, const int max_count) +{ + KeyValuePair *pCurrent; + KeyValuePair *pEnd; + char *pParamStart; + char *p; + char *pStrEnd; + int value_len; + + pParamStart = strchr(url, '?'); + if (pParamStart == NULL) + { + return 0; + } + + *pParamStart = '\0'; + + pEnd = params + max_count; + pCurrent = params; + p = pParamStart + 1; + while (p != NULL && *p != '\0') + { + if (pCurrent >= pEnd) + { + return pCurrent - params; + } + + pCurrent->key = p; + pStrEnd = strchr(p, '&'); + if (pStrEnd == NULL) + { + p = NULL; + } + else + { + *pStrEnd = '\0'; + p = pStrEnd + 1; + } + + pStrEnd = strchr(pCurrent->key, '='); + if (pStrEnd == NULL) + { + continue; + } + + *pStrEnd = '\0'; + pCurrent->value = pStrEnd + 1; + if (*pCurrent->key == '\0') + { + continue; + } + + urldecode(pCurrent->value, strlen(pCurrent->value), \ + pCurrent->value, &value_len); + pCurrent++; + } + + return pCurrent - params; +} + diff --git a/src/http_func.h b/src/http_func.h new file mode 100644 index 0000000..b61edbb --- /dev/null +++ b/src/http_func.h @@ -0,0 +1,54 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#ifndef _HTTP_FUNC_H +#define _HTTP_FUNC_H + +#include +#include +#include +#include +#include +#include "common_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +get content from url +params: + url: the url to fetch, must start as: "http://" + connect_timeout: connect timeout (seconds) + network_timeout: network timeout (seconds) + http_status: return http status code, 200 for Ok + content: return the content (HTTP body only, not including HTTP header), + *content should be freed by caller + content_len: return content length (bytes) +return: 0 for success, != 0 for error +**/ +int get_url_content(const char *url, const int connect_timeout, \ + const int network_timeout, int *http_status, \ + char **content, int *content_len, char *error_info); + +/** +parse url +params: + url: the url to parse + params: params array to store param and it's value + max_count: max param count +return: param count +**/ +int http_parse_query(char *url, KeyValuePair *params, const int max_count); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/ini_file_reader.c b/src/ini_file_reader.c new file mode 100644 index 0000000..f163c74 --- /dev/null +++ b/src/ini_file_reader.c @@ -0,0 +1,784 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//ini_file_reader.c + +#include +#include +#include +#include +#include +#include +#include "shared_func.h" +#include "logger.h" +#include "http_func.h" +#include "ini_file_reader.h" + +#define _LINE_BUFFER_SIZE 512 +#define _ALLOC_ITEMS_ONCE 8 + +static int iniDoLoadFromFile(const char *szFilename, \ + IniContext *pContext); +static int iniDoLoadItemsFromBuffer(char *content, \ + IniContext *pContext); + +static int iniCompareByItemName(const void *p1, const void *p2) +{ + return strcmp(((IniItem *)p1)->name, ((IniItem *)p2)->name); +} + +static int iniInitContext(IniContext *pContext) +{ + int result; + + memset(pContext, 0, sizeof(IniContext)); + pContext->current_section = &pContext->global; + if ((result=hash_init(&pContext->sections, PJWHash, 10, 0.75)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "hash_init fail, errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + return result; +} + +static int iniSortHashData(const int index, const HashData *data, void *args) +{ + IniSection *pSection; + + pSection = (IniSection *)data->value; + if (pSection->count > 1) + { + qsort(pSection->items, pSection->count, \ + sizeof(IniItem), iniCompareByItemName); + } + + return 0; +} + +static void iniSortItems(IniContext *pContext) +{ + if (pContext->global.count > 1) + { + qsort(pContext->global.items, pContext->global.count, \ + sizeof(IniItem), iniCompareByItemName); + } + + hash_walk(&pContext->sections, iniSortHashData, NULL); +} + +int iniLoadFromFile(const char *szFilename, IniContext *pContext) +{ + int result; + int len; + char *pLast; + char full_filename[MAX_PATH_SIZE]; + + if ((result=iniInitContext(pContext)) != 0) + { + return result; + } + + if (strncasecmp(szFilename, "http://", 7) == 0) + { + *pContext->config_path = '\0'; + snprintf(full_filename, sizeof(full_filename),"%s",szFilename); + } + else + { + if (*szFilename == '/') + { + pLast = strrchr(szFilename, '/'); + len = pLast - szFilename; + if (len >= sizeof(pContext->config_path)) + { + logError("file: "__FILE__", line: %d, "\ + "the path of the config file: %s is " \ + "too long!", __LINE__, szFilename); + return ENOSPC; + } + + memcpy(pContext->config_path, szFilename, len); + *(pContext->config_path + len) = '\0'; + snprintf(full_filename, sizeof(full_filename), \ + "%s", szFilename); + } + else + { + memset(pContext->config_path, 0, \ + sizeof(pContext->config_path)); + if (getcwd(pContext->config_path, sizeof( \ + pContext->config_path)) == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "getcwd fail, errno: %d, " \ + "error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EPERM; + } + + len = strlen(pContext->config_path); + if (len > 0 && pContext->config_path[len - 1] == '/') + { + len--; + *(pContext->config_path + len) = '\0'; + } + + snprintf(full_filename, sizeof(full_filename), \ + "%s/%s", pContext->config_path, szFilename); + + pLast = strrchr(szFilename, '/'); + if (pLast != NULL) + { + int tail_len; + + tail_len = pLast - szFilename; + if (len + tail_len >= sizeof( \ + pContext->config_path)) + { + logError("file: "__FILE__", line: %d, "\ + "the path of the config " \ + "file: %s is too long!", \ + __LINE__, szFilename); + return ENOSPC; + } + + memcpy(pContext->config_path + len, \ + szFilename, tail_len); + len += tail_len; + *(pContext->config_path + len) = '\0'; + } + } + } + + result = iniDoLoadFromFile(full_filename, pContext); + if (result == 0) + { + iniSortItems(pContext); + } + else + { + iniFreeContext(pContext); + } + + return result; +} + +static int iniDoLoadFromFile(const char *szFilename, \ + IniContext *pContext) +{ + char *content; + int result; + int http_status; + int content_len; + int64_t file_size; + char error_info[512]; + + if (strncasecmp(szFilename, "http://", 7) == 0) + { + if ((result=get_url_content(szFilename, 10, 60, &http_status, \ + &content, &content_len, error_info)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "get_url_content fail, " \ + "url: %s, error info: %s", \ + __LINE__, szFilename, error_info); + return result; + } + + if (http_status != 200) + { + free(content); + logError("file: "__FILE__", line: %d, " \ + "HTTP status code: %d != 200, url: %s", \ + __LINE__, http_status, szFilename); + return EINVAL; + } + } + else + { + if ((result=getFileContent(szFilename, &content, \ + &file_size)) != 0) + { + return result; + } + } + + result = iniDoLoadItemsFromBuffer(content, pContext); + free(content); + + return result; +} + +int iniLoadFromBuffer(char *content, IniContext *pContext) +{ + int result; + + if ((result=iniInitContext(pContext)) != 0) + { + return result; + } + + result = iniDoLoadItemsFromBuffer(content, pContext); + if (result == 0) + { + iniSortItems(pContext); + } + else + { + iniFreeContext(pContext); + } + + return result; +} + +static int iniDoLoadItemsFromBuffer(char *content, IniContext *pContext) +{ + IniSection *pSection; + IniItem *pItem; + char *pLine; + char *pLastEnd; + char *pEqualChar; + char *pIncludeFilename; + char full_filename[MAX_PATH_SIZE]; + int nLineLen; + int nNameLen; + int nValueLen; + int result; + + result = 0; + pLastEnd = content - 1; + pSection = pContext->current_section; + if (pSection->count > 0) + { + pItem = pSection->items + pSection->count; + } + else + { + pItem = pSection->items; + } + + while (pLastEnd != NULL) + { + pLine = pLastEnd + 1; + pLastEnd = strchr(pLine, '\n'); + if (pLastEnd != NULL) + { + *pLastEnd = '\0'; + } + + if (*pLine == '#' && \ + strncasecmp(pLine+1, "include", 7) == 0 && \ + (*(pLine+8) == ' ' || *(pLine+8) == '\t')) + { + pIncludeFilename = strdup(pLine + 9); + if (pIncludeFilename == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "strdup %d bytes fail", __LINE__, \ + (int)strlen(pLine + 9) + 1); + result = errno != 0 ? errno : ENOMEM; + break; + } + + trim(pIncludeFilename); + if (strncasecmp(pIncludeFilename, "http://", 7) == 0) + { + snprintf(full_filename, sizeof(full_filename),\ + "%s", pIncludeFilename); + } + else + { + if (*pIncludeFilename == '/') + { + snprintf(full_filename, sizeof(full_filename), \ + "%s", pIncludeFilename); + } + else + { + snprintf(full_filename, sizeof(full_filename), \ + "%s/%s", pContext->config_path, \ + pIncludeFilename); + } + + if (!fileExists(full_filename)) + { + logError("file: "__FILE__", line: %d, " \ + "include file \"%s\" not exists, " \ + "line: \"%s\"", __LINE__, \ + pIncludeFilename, pLine); + free(pIncludeFilename); + result = ENOENT; + break; + } + } + + result = iniDoLoadFromFile(full_filename, pContext); + if (result != 0) + { + free(pIncludeFilename); + break; + } + + pSection = pContext->current_section; + if (pSection->count > 0) + { + pItem = pSection->items + pSection->count; //must re-asign + } + else + { + pItem = pSection->items; + } + + free(pIncludeFilename); + continue; + } + + trim(pLine); + if (*pLine == '#' || *pLine == '\0') + { + continue; + } + + nLineLen = strlen(pLine); + if (*pLine == '[' && *(pLine + (nLineLen - 1)) == ']') //section + { + char *section_name; + int section_len; + + *(pLine + (nLineLen - 1)) = '\0'; + section_name = pLine + 1; //skip [ + + trim(section_name); + if (*section_name == '\0') //global section + { + pContext->current_section = &pContext->global; + pSection = pContext->current_section; + if (pSection->count > 0) + { + pItem = pSection->items + pSection->count; + } + else + { + pItem = pSection->items; + } + continue; + } + + section_len = strlen(section_name); + pSection = (IniSection *)hash_find(&pContext->sections,\ + section_name, section_len); + if (pSection == NULL) + { + pSection = (IniSection *)malloc(sizeof(IniSection)); + if (pSection == NULL) + { + result = errno != 0 ? errno : ENOMEM; + logError("file: "__FILE__", line: %d, "\ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, \ + (int)sizeof(IniSection), \ + result, STRERROR(result)); + + break; + } + + memset(pSection, 0, sizeof(IniSection)); + result = hash_insert(&pContext->sections, \ + section_name, section_len, pSection); + if (result < 0) + { + result *= -1; + logError("file: "__FILE__", line: %d, "\ + "insert into hash table fail, "\ + "errno: %d, error info: %s", \ + __LINE__, result, \ + STRERROR(result)); + break; + } + else + { + result = 0; + } + } + + pContext->current_section = pSection; + if (pSection->count > 0) + { + pItem = pSection->items + pSection->count; + } + else + { + pItem = pSection->items; + } + continue; + } + + pEqualChar = strchr(pLine, '='); + if (pEqualChar == NULL) + { + continue; + } + + nNameLen = pEqualChar - pLine; + nValueLen = strlen(pLine) - (nNameLen + 1); + if (nNameLen > FAST_INI_ITEM_NAME_LEN) + { + nNameLen = FAST_INI_ITEM_NAME_LEN; + } + + if (nValueLen > FAST_INI_ITEM_VALUE_LEN) + { + nValueLen = FAST_INI_ITEM_VALUE_LEN; + } + + if (pSection->count >= pSection->alloc_count) + { + pSection->alloc_count += _ALLOC_ITEMS_ONCE; + pSection->items=(IniItem *)realloc(pSection->items, + sizeof(IniItem) * pSection->alloc_count); + if (pSection->items == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "realloc %d bytes fail", __LINE__, \ + (int)sizeof(IniItem) * \ + pSection->alloc_count); + result = errno != 0 ? errno : ENOMEM; + break; + } + + pItem = pSection->items + pSection->count; + memset(pItem, 0, sizeof(IniItem) * \ + (pSection->alloc_count - pSection->count)); + } + + memcpy(pItem->name, pLine, nNameLen); + memcpy(pItem->value, pEqualChar + 1, nValueLen); + + trim(pItem->name); + trim(pItem->value); + + pSection->count++; + pItem++; + } + + return result; +} + +static int iniFreeHashData(const int index, const HashData *data, void *args) +{ + IniSection *pSection; + + pSection = (IniSection *)data->value; + if (pSection == NULL) + { + return 0; + } + + if (pSection->items != NULL) + { + free(pSection->items); + memset(pSection, 0, sizeof(IniSection)); + } + + free(pSection); + ((HashData *)data)->value = NULL; + return 0; +} + +void iniFreeContext(IniContext *pContext) +{ + if (pContext == NULL) + { + return; + } + + if (pContext->global.items != NULL) + { + free(pContext->global.items); + memset(&pContext->global, 0, sizeof(IniSection)); + } + + hash_walk(&pContext->sections, iniFreeHashData, NULL); + hash_destroy(&pContext->sections); +} + + +#define INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ + targetItem, pItem, return_val) \ + if (szSectionName == NULL || *szSectionName == '\0') \ + { \ + pSection = &pContext->global; \ + } \ + else \ + { \ + pSection = (IniSection *)hash_find(&pContext->sections, \ + szSectionName, strlen(szSectionName)); \ + if (pSection == NULL) \ + { \ + return return_val; \ + } \ + } \ + \ + if (pSection->count <= 0) \ + { \ + return return_val; \ + } \ + \ + snprintf(targetItem.name, sizeof(targetItem.name), "%s", szItemName); \ + pItem = (IniItem *)bsearch(&targetItem, pSection->items, \ + pSection->count, sizeof(IniItem), iniCompareByItemName); + + +char *iniGetStrValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext) +{ + IniItem targetItem; + IniSection *pSection; + IniItem *pItem; + + INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ + targetItem, pItem, NULL) + + if (pItem == NULL) + { + return NULL; + } + else + { + return pItem->value; + } +} + +int64_t iniGetInt64Value(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const int64_t nDefaultValue) +{ + char *pValue; + + pValue = iniGetStrValue(szSectionName, szItemName, pContext); + if (pValue == NULL) + { + return nDefaultValue; + } + else + { + return strtoll(pValue, NULL, 10); + } +} + +int iniGetIntValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const int nDefaultValue) +{ + char *pValue; + + pValue = iniGetStrValue(szSectionName, szItemName, pContext); + if (pValue == NULL) + { + return nDefaultValue; + } + else + { + return atoi(pValue); + } +} + +double iniGetDoubleValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const double dbDefaultValue) +{ + char *pValue; + + pValue = iniGetStrValue(szSectionName, szItemName, pContext); + if (pValue == NULL) + { + return dbDefaultValue; + } + else + { + return strtod(pValue, NULL); + } +} + +bool iniGetBoolValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const bool bDefaultValue) +{ + char *pValue; + + pValue = iniGetStrValue(szSectionName, szItemName, pContext); + if (pValue == NULL) + { + return bDefaultValue; + } + else + { + return strcasecmp(pValue, "true") == 0 || + strcasecmp(pValue, "yes") == 0 || + strcasecmp(pValue, "on") == 0 || + strcmp(pValue, "1") == 0; + } +} + +int iniGetValues(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, char **szValues, const int max_values) +{ + IniItem targetItem; + IniSection *pSection; + IniItem *pFound; + IniItem *pItem; + IniItem *pItemEnd; + char **ppValues; + + if (max_values <= 0) + { + return 0; + } + + INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ + targetItem, pFound, 0) + if (pFound == NULL) + { + return 0; + } + + ppValues = szValues; + *ppValues++ = pFound->value; + for (pItem=pFound-1; pItem>=pSection->items; pItem--) + { + if (strcmp(pItem->name, szItemName) != 0) + { + break; + } + + if (ppValues - szValues < max_values) + { + *ppValues++ = pItem->value; + } + } + + pItemEnd = pSection->items + pSection->count; + for (pItem=pFound+1; pItemname, szItemName) != 0) + { + break; + } + + if (ppValues - szValues < max_values) + { + *ppValues++ = pItem->value; + } + } + + return ppValues - szValues; +} + +IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, int *nTargetCount) +{ + IniItem targetItem; + IniSection *pSection; + IniItem *pFound; + IniItem *pItem; + IniItem *pItemEnd; + IniItem *pItemStart; + + *nTargetCount = 0; + INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ + targetItem, pFound, NULL) + if (pFound == NULL) + { + return NULL; + } + + *nTargetCount = 1; + for (pItem=pFound-1; pItem>=pSection->items; pItem--) + { + if (strcmp(pItem->name, szItemName) != 0) + { + break; + } + + (*nTargetCount)++; + } + pItemStart = pFound - (*nTargetCount) + 1; + + pItemEnd = pSection->items + pSection->count; + for (pItem=pFound+1; pItemname, szItemName) != 0) + { + break; + } + + (*nTargetCount)++; + } + + return pItemStart; +} + +static int iniPrintHashData(const int index, const HashData *data, void *args) +{ + IniSection *pSection; + IniItem *pItem; + IniItem *pItemEnd; + char section_name[256]; + int section_len; + int i; + + pSection = (IniSection *)data->value; + if (pSection == NULL) + { + return 0; + } + + section_len = data->key_len; + if (section_len >= sizeof(section_name)) + { + section_len = sizeof(section_name) - 1; + } + + memcpy(section_name, data->key, section_len); + *(section_name + section_len) = '\0'; + + printf("section: %s, item count: %d\n", section_name, pSection->count); + if (pSection->count > 0) + { + i = 0; + pItemEnd = pSection->items + pSection->count; + for (pItem=pSection->items; pItemname, pItem->value); + } + } + printf("\n"); + + return 0; +} + +void iniPrintItems(IniContext *pContext) +{ + IniItem *pItem; + IniItem *pItemEnd; + int i; + + printf("global section, item count: %d\n", pContext->global.count); + if (pContext->global.count > 0) + { + i = 0; + pItemEnd = pContext->global.items + pContext->global.count; + for (pItem=pContext->global.items; pItemname, pItem->value); + } + } + printf("\n"); + + hash_walk(&pContext->sections, iniPrintHashData, NULL); +} + diff --git a/src/ini_file_reader.h b/src/ini_file_reader.h new file mode 100644 index 0000000..b3ba6b7 --- /dev/null +++ b/src/ini_file_reader.h @@ -0,0 +1,166 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//ini_file_reader.h +#ifndef INI_FILE_READER_H +#define INI_FILE_READER_H + +#include +#include +#include +#include "common_define.h" +#include "hash.h" + +#define FAST_INI_ITEM_NAME_LEN 64 +#define FAST_INI_ITEM_VALUE_LEN 256 + +typedef struct +{ + char name[FAST_INI_ITEM_NAME_LEN + 1]; + char value[FAST_INI_ITEM_VALUE_LEN + 1]; +} IniItem; + +typedef struct +{ + IniItem *items; + int count; //item count + int alloc_count; +} IniSection; + +typedef struct +{ + IniSection global; + HashArray sections; //key is session name, and value is IniSection + IniSection *current_section; //for load from ini file + char config_path[MAX_PATH_SIZE]; //save the config filepath +} IniContext; + +#ifdef __cplusplus +extern "C" { +#endif + +/** load ini items from file + * parameters: + * szFilename: the filename, can be an URL + * pContext: the ini context + * return: error no, 0 for success, != 0 fail +*/ +int iniLoadFromFile(const char *szFilename, IniContext *pContext); + +/** load ini items from string buffer + * parameters: + * content: the string buffer to parse + * pContext: the ini context + * return: error no, 0 for success, != 0 fail +*/ +int iniLoadFromBuffer(char *content, IniContext *pContext); + +/** free ini context + * parameters: + * pContext: the ini context + * return: none +*/ +void iniFreeContext(IniContext *pContext); + +/** get item string value + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * return: item value, return NULL when the item not exist +*/ +char *iniGetStrValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext); + +/** get item string value + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * szValues: string array to store the values + * max_values: max string array elements + * return: item value count +*/ +int iniGetValues(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, char **szValues, const int max_values); + +/** get item int value (32 bits) + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * nDefaultValue: the default value + * return: item value, return nDefaultValue when the item not exist +*/ +int iniGetIntValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const int nDefaultValue); + +/** get item string value array + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * nTargetCount: store the item value count + * return: item value array, return NULL when the item not exist +*/ +IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, int *nTargetCount); + +/** get item int64 value (64 bits) + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * nDefaultValue: the default value + * return: int64 value, return nDefaultValue when the item not exist +*/ +int64_t iniGetInt64Value(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const int64_t nDefaultValue); + +/** get item boolean value + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * bDefaultValue: the default value + * return: item boolean value, return bDefaultValue when the item not exist +*/ +bool iniGetBoolValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const bool bDefaultValue); + +/** get item double value + * parameters: + * szSectionName: the section name, NULL or empty string for + * global section + * szItemName: the item name + * pContext: the ini context + * dbDefaultValue: the default value + * return: item value, return dbDefaultValue when the item not exist +*/ +double iniGetDoubleValue(const char *szSectionName, const char *szItemName, \ + IniContext *pContext, const double dbDefaultValue); + +/** print all items + * parameters: + * pContext: the ini context + * return: none +*/ +void iniPrintItems(IniContext *pContext); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/io_opt.c b/src/io_opt.c new file mode 100644 index 0000000..46a7ad7 --- /dev/null +++ b/src/io_opt.c @@ -0,0 +1,102 @@ +/** +* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com +* +* FastLib may be copied only under the terms of the GNU General +* Public License V3, which may be found in the FastLib source kit. +* Please visit the FastLib Home Page http://www.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include + +#include "io_opt.h" + +int mkdir_by_cascading(const char *path,mode_t mode) +{ + int length,pointer_postion = 0; + char *postion; + char *path_temp = path; + char cwd[MAX_PATH_SIZE]; + char *subfolder; + int is_error = 0; + int result = 0; + if(NULL == getcwd(cwd,sizeof(cwd))) + { + return -1; + } + + if(*path_temp == '/') + { + if(-1 == chdir("/")) + { + return -2; + } + pointer_postion ++; + } + + while(pointer_postion != strlen(path_temp)) + { + postion = strchr(path_temp+pointer_postion,'/'); + if(0 == postion) + { + length = strlen(path_temp) - pointer_postion; + } + else + { + length = postion - path_temp - pointer_postion; + } + + do + { + subfolder = (char *)calloc(length,sizeof(char)); + if(NULL == subfolder) + { + result = -3; + break; + } + memcpy(subfolder,path_temp+pointer_postion,length); + if(is_dir(subfolder)) + { + if(-1 == chdir(subfolder)) + { + result = -2; + break; + } + } + if(-1 == mkdir(subfolder,mode)) + { + result = -4; + break; + } + if(-1 == chdir(subfolder)) + { + result = -2; + break; + } + }while(0); + + if(NULL != subfolder) + { + free(subfolder); + subfolder = NULL; + } + + pointer_postion += 0 == postion ? length : length + 1; + } + + return result; +} + +int is_dir(const char *dir_path) { + struct stat buf; + if (0 != stat(dir_path, &buf)) { + return 0; + } + + return S_ISDIR(buf.st_mode); +} + diff --git a/src/io_opt.h b/src/io_opt.h new file mode 100644 index 0000000..cf7b4d7 --- /dev/null +++ b/src/io_opt.h @@ -0,0 +1,41 @@ +/** +* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com +* +* FastLib may be copied only under the terms of the GNU General +* Public License V3, which may be found in the FastLib source kit. +* Please visit the FastLib Home Page http://www.csource.org/ for more detail. +**/ + +#ifndef IO_OPT_H_ +#define IO_OPT_H_ + +#ifndef MAX_PATH_SIZE +#define MAX_PATH_SIZE 1024 +#endif + +/* + * create the dir by full dir_path + * parameters: + * path : the dir full path + * mode : the mode for mkdir + * return: + * 0:create dir is success + * -1 : get current path is error; + * -2 : change dir is error; + * -3 : malloc memory to subfolder is error + * -4 : create dir is error; + */ +int mkdir_by_cascading(const char *path,mode_t mode); + +/* + * check the first parameter is the dir + * parameters: + * path : the dir full path + * return: + * 0:the path is dir + * not 0: the path is not dir + */ +int is_dir(const char *path); + + +#endif /* IO_OPT_H_ */ diff --git a/src/ioevent.c b/src/ioevent.c new file mode 100644 index 0000000..9ff0658 --- /dev/null +++ b/src/ioevent.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include "ioevent.h" + +#if IOEVENT_USE_KQUEUE +/* we define these here as numbers, because for kqueue mapping them to a combination of + * filters / flags is hard to do. */ +int kqueue_ev_convert(int16_t event, uint16_t flags) +{ + int r; + + if (event == EVFILT_READ) { + r = KPOLLIN; + } + else if (event == EVFILT_WRITE) { + r = KPOLLOUT; + } + else { + r = 0; + } + + if (flags & EV_EOF) { + r |= KPOLLHUP; + } + return r; +} +#endif + +int ioevent_init(IOEventPoller *ioevent, const int size, + const int timeout, const int extra_events) +{ + int bytes; + + ioevent->size = size; + ioevent->extra_events = extra_events; + +#if IOEVENT_USE_EPOLL + ioevent->timeout = timeout; + ioevent->poll_fd = epoll_create(ioevent->size); + bytes = sizeof(struct epoll_event) * size; + ioevent->events = (struct epoll_event *)malloc(bytes); +#elif IOEVENT_USE_KQUEUE + ioevent->timeout.tv_sec = timeout / 1000; + ioevent->timeout.tv_nsec = 1000000 * (timeout % 1000); + ioevent->poll_fd = kqueue(); + bytes = sizeof(struct kevent) * size; + ioevent->events = (struct kevent *)malloc(bytes); +#elif IOEVENT_USE_PORT + ioevent->timeout.tv_sec = timeout / 1000; + ioevent->timeout.tv_nsec = 1000000 * (timeout % 1000); + ioevent->poll_fd = port_create(); + bytes = sizeof(port_event_t) * size; + ioevent->events = (port_event_t *)malloc(bytes); +#endif + if (ioevent->events == NULL) { + return errno != 0 ? errno : ENOMEM; + } + return 0; +} + +void ioevent_destroy(IOEventPoller *ioevent) +{ + if (ioevent->events != NULL) { + free(ioevent->events); + ioevent->events = NULL; + } + + if (ioevent->poll_fd >=0) { + close(ioevent->poll_fd); + ioevent->poll_fd = -1; + } +} + +int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e, + void *data) +{ +#if IOEVENT_USE_EPOLL + struct epoll_event ev; + memset(&ev, 0, sizeof(ev)); + ev.events = e | ioevent->extra_events; + ev.data.ptr = data; + return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_ADD, fd, &ev); +#elif IOEVENT_USE_KQUEUE + struct kevent ev[2]; + int n = 0; + if (e & IOEVENT_READ) { + EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | ioevent->extra_events, 0, 0, data); + } + if (e & IOEVENT_WRITE) { + EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | ioevent->extra_events, 0, 0, data); + } + return kevent(ioevent->poll_fd, ev, n, NULL, 0, NULL); +#elif IOEVENT_USE_PORT + return port_associate(ioevent->poll_fd, PORT_SOURCE_FD, fd, e, data); +#endif +} + +int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e, + void *data) +{ +#if IOEVENT_USE_EPOLL + struct epoll_event ev; + memset(&ev, 0, sizeof(ev)); + ev.events = e | ioevent->extra_events; + ev.data.ptr = data; + return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_MOD, fd, &ev); +#elif IOEVENT_USE_KQUEUE + struct kevent ev[2]; + int n = 0; + if (e & IOEVENT_READ) { + EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | ioevent->extra_events, 0, 0, data); + } + else { + EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, data); + } + + if (e & IOEVENT_WRITE) { + EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | ioevent->extra_events, 0, 0, data); + } + else { + EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, data); + } + return kevent(ioevent->poll_fd, ev, n, NULL, 0, NULL); +#elif IOEVENT_USE_PORT + return port_associate(ioevent->poll_fd, PORT_SOURCE_FD, fd, e, data); +#endif +} + +int ioevent_detach(IOEventPoller *ioevent, const int fd) +{ +#if IOEVENT_USE_EPOLL + return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_DEL, fd, NULL); +#elif IOEVENT_USE_PORT + return port_dissociate(ioevent->poll_fd, PORT_SOURCE_FD, fd); +#else + return 0; +#endif +} + +int ioevent_poll(IOEventPoller *ioevent) +{ +#if IOEVENT_USE_EPOLL + return epoll_wait(ioevent->poll_fd, ioevent->events, ioevent->size, ioevent->timeout); +#elif IOEVENT_USE_KQUEUE + return kevent(ioevent->poll_fd, NULL, 0, ioevent->events, ioevent->size, &ioevent->timeout); +#elif IOEVENT_USE_PORT + int result; + int retval; + unsigned int nget = 1; + if((retval = port_getn(ioevent->poll_fd, ioevent->events, + ioevent->size, &nget, &ioevent->timeout)) == 0) + { + result = (int)nget; + } else { + switch(errno) { + case EINTR: + case EAGAIN: + case ETIME: + if (nget > 0) { + result = (int)nget; + } + else { + result = 0; + } + break; + default: + result = -1; + break; + } + } + return result; +#else +#error port me +#endif +} + diff --git a/src/ioevent.h b/src/ioevent.h new file mode 100644 index 0000000..79e2c5b --- /dev/null +++ b/src/ioevent.h @@ -0,0 +1,113 @@ +#ifndef __IOEVENT_H__ +#define __IOEVENT_H__ + +#include +#include +#include + +#define IOEVENT_TIMEOUT 0x8000 + +#if IOEVENT_USE_EPOLL +#include +#define IOEVENT_EDGE_TRIGGER EPOLLET + +#define IOEVENT_READ EPOLLIN +#define IOEVENT_WRITE EPOLLOUT +#define IOEVENT_ERROR (EPOLLERR | EPOLLPRI | EPOLLHUP) + +#elif IOEVENT_USE_KQUEUE +#include +#define IOEVENT_EDGE_TRIGGER EV_CLEAR + +#define KPOLLIN 0x001 +#define KPOLLPRI 0x002 +#define KPOLLOUT 0x004 +#define KPOLLERR 0x010 +#define KPOLLHUP 0x020 +#define IOEVENT_READ KPOLLIN +#define IOEVENT_WRITE KPOLLOUT +#define IOEVENT_ERROR (KPOLLHUP | KPOLLPRI | KPOLLHUP) + +#ifdef __cplusplus +extern "C" { +#endif + +int kqueue_ev_convert(int16_t event, uint16_t flags); + +#ifdef __cplusplus +} +#endif + +#elif IOEVENT_USE_PORT +#include +#define IOEVENT_EDGE_TRIGGER 0 + +#define IOEVENT_READ POLLIN +#define IOEVENT_WRITE POLLOUT +#define IOEVENT_ERROR (POLLERR | POLLPRI | POLLHUP) +#endif + +typedef struct ioevent_puller { + int size; //max events (fd) + int extra_events; + int poll_fd; + +#if IOEVENT_USE_EPOLL + struct epoll_event *events; + int timeout; +#elif IOEVENT_USE_KQUEUE + struct kevent *events; + struct timespec timeout; +#elif IOEVENT_USE_PORT + port_event_t *events; + timespec_t timeout; +#endif +} IOEventPoller; + +#if IOEVENT_USE_EPOLL + #define IOEVENT_GET_EVENTS(ioevent, index) \ + ioevent->events[index].events +#elif IOEVENT_USE_KQUEUE + #define IOEVENT_GET_EVENTS(ioevent, index) kqueue_ev_convert( \ + ioevent->events[index].filter, ioevent->events[index].flags) +#elif IOEVENT_USE_PORT + #define IOEVENT_GET_EVENTS(ioevent, index) \ + ioevent->events[index].portev_events +#else +#error port me +#endif + +#if IOEVENT_USE_EPOLL + #define IOEVENT_GET_DATA(ioevent, index) \ + ioevent->events[index].data.ptr +#elif IOEVENT_USE_KQUEUE + #define IOEVENT_GET_DATA(ioevent, index) \ + ioevent->events[index].udata +#elif IOEVENT_USE_PORT + #define IOEVENT_GET_DATA(ioevent, index) \ + ioevent->events[index].portev_user +#else +#error port me +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +int ioevent_init(IOEventPoller *ioevent, const int size, + const int timeout, const int extra_events); +void ioevent_destroy(IOEventPoller *ioevent); + +int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e, + void *data); +int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e, + void *data); +int ioevent_detach(IOEventPoller *ioevent, const int fd); +int ioevent_poll(IOEventPoller *ioevent); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/ioevent.o b/src/ioevent.o new file mode 100644 index 0000000..e4eef1e Binary files /dev/null and b/src/ioevent.o differ diff --git a/src/ioevent_loop.c b/src/ioevent_loop.c new file mode 100644 index 0000000..f2e2938 --- /dev/null +++ b/src/ioevent_loop.c @@ -0,0 +1,150 @@ +#include "sched_thread.h" +#include "logger.h" +#include "ioevent_loop.h" + +static void deal_ioevents(IOEventPoller *ioevent, const int count) +{ + int i; + int event; + IOEventEntry *pEntry; + for (i=0; icallback(pEntry->fd, event, pEntry->timer.data); + } +} + +static void deal_timeouts(FastTimerEntry *head) +{ + FastTimerEntry *entry; + FastTimerEntry *curent; + IOEventEntry *pEventEntry; + + entry = head->next; + while (entry != NULL) + { + curent = entry; + entry = entry->next; + + pEventEntry = (IOEventEntry *)curent->data; + if (pEventEntry != NULL) + { + pEventEntry->callback(pEventEntry->fd, IOEVENT_TIMEOUT, + curent->data); + } + } +} + +int ioevent_loop(struct nio_thread_data *pThreadData, + IOEventCallback recv_notify_callback, TaskCleanUpCallBack + clean_up_callback, volatile bool *continue_flag) +{ + int result; + IOEventEntry ev_notify; + FastTimerEntry head; + struct fast_task_info *pTask; + time_t last_check_time; + int count; + + memset(&ev_notify, 0, sizeof(ev_notify)); + ev_notify.fd = pThreadData->pipe_fds[0]; + ev_notify.callback = recv_notify_callback; + if (ioevent_attach(&pThreadData->ev_puller, + pThreadData->pipe_fds[0], IOEVENT_READ, + &ev_notify) != 0) + { + result = errno != 0 ? errno : ENOMEM; + logCrit("file: "__FILE__", line: %d, " \ + "ioevent_attach fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + last_check_time = g_current_time; + while (*continue_flag) + { + pThreadData->deleted_list = NULL; + count = ioevent_poll(&pThreadData->ev_puller); + if (count > 0) + { + deal_ioevents(&pThreadData->ev_puller, count); + } + else if (count < 0) + { + result = errno != 0 ? errno : EINVAL; + if (result != EINTR) + { + logError("file: "__FILE__", line: %d, " \ + "ioevent_poll fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + } + + if (pThreadData->deleted_list != NULL) + { + count = 0; + while (pThreadData->deleted_list != NULL) + { + pTask = pThreadData->deleted_list; + pThreadData->deleted_list = pTask->next; + + clean_up_callback(pTask); + count++; + } + logInfo("cleanup task count: %d", count); + } + + if (g_current_time - last_check_time > 0) + { + last_check_time = g_current_time; + count = fast_timer_timeouts_get( + &pThreadData->timer, g_current_time, &head); + if (count > 0) + { + deal_timeouts(&head); + } + } + } + + return 0; +} + +int ioevent_set(struct fast_task_info *pTask, struct nio_thread_data *pThread, + int sock, short event, IOEventCallback callback, const int timeout) +{ + int result; + + pTask->thread_data = pThread; + pTask->event.fd = sock; + pTask->event.callback = callback; + if (ioevent_attach(&pThread->ev_puller, + sock, event, pTask) < 0) + { + result = errno != 0 ? errno : ENOENT; + logError("file: "__FILE__", line: %d, " \ + "ioevent_attach fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + pTask->event.timer.data = pTask; + pTask->event.timer.expires = g_current_time + timeout; + result = fast_timer_add(&pThread->timer, &pTask->event.timer); + if (result != 0) + { + logError("file: "__FILE__", line: %d, " \ + "fast_timer_add fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + return 0; +} + diff --git a/src/ioevent_loop.h b/src/ioevent_loop.h new file mode 100644 index 0000000..dbed3a1 --- /dev/null +++ b/src/ioevent_loop.h @@ -0,0 +1,22 @@ +#ifndef _IOEVENT_LOOP_H +#define _IOEVENT_LOOP_H + +#include "fast_task_queue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ioevent_loop(struct nio_thread_data *pThreadData, + IOEventCallback recv_notify_callback, TaskCleanUpCallBack + clean_up_callback, volatile bool *continue_flag); + +int ioevent_set(struct fast_task_info *pTask, struct nio_thread_data *pThread, + int sock, short event, IOEventCallback callback, const int timeout); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/ioevent_loop.o b/src/ioevent_loop.o new file mode 100644 index 0000000..d661b54 Binary files /dev/null and b/src/ioevent_loop.o differ diff --git a/src/local_ip_func.c b/src/local_ip_func.c new file mode 100644 index 0000000..d2829fb --- /dev/null +++ b/src/local_ip_func.c @@ -0,0 +1,134 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include "logger.h" +#include "sockopt.h" +#include "shared_func.h" +#include "local_ip_func.h" + +int g_local_host_ip_count = 0; +char g_local_host_ip_addrs[FAST_MAX_LOCAL_IP_ADDRS * \ + IP_ADDRESS_SIZE]; +char g_if_alias_prefix[FAST_IF_ALIAS_PREFIX_MAX_SIZE] = {0}; + +bool is_local_host_ip(const char *client_ip) +{ + char *p; + char *pEnd; + + pEnd = g_local_host_ip_addrs + \ + IP_ADDRESS_SIZE * g_local_host_ip_count; + for (p=g_local_host_ip_addrs; p= FAST_MAX_LOCAL_IP_ADDRS) + { + return -1; + } + + strcpy(g_local_host_ip_addrs + \ + IP_ADDRESS_SIZE * g_local_host_ip_count, \ + client_ip); + g_local_host_ip_count++; + return 1; +} + +static void log_local_host_ip_addrs() +{ + char *p; + char *pEnd; + char buff[512]; + int len; + + len = sprintf(buff, "local_host_ip_count: %d,", g_local_host_ip_count); + pEnd = g_local_host_ip_addrs + \ + IP_ADDRESS_SIZE * g_local_host_ip_count; + for (p=g_local_host_ip_addrs; p +#include +#include +#include +#include "common_define.h" + +#define FAST_IF_ALIAS_PREFIX_MAX_SIZE 32 +#define FAST_MAX_LOCAL_IP_ADDRS 16 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int g_local_host_ip_count; +extern char g_local_host_ip_addrs[FAST_MAX_LOCAL_IP_ADDRS * \ + IP_ADDRESS_SIZE]; +extern char g_if_alias_prefix[FAST_IF_ALIAS_PREFIX_MAX_SIZE]; + +void load_local_host_ip_addrs(); +bool is_local_host_ip(const char *client_ip); +int insert_into_local_host_ip(const char *client_ip); +void print_local_host_ip_addrs(); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 0000000..f66a763 --- /dev/null +++ b/src/logger.c @@ -0,0 +1,668 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shared_func.h" +#include "pthread_func.h" +#include "sched_thread.h" +#include "logger.h" + +#ifndef LINE_MAX +#define LINE_MAX 2048 +#endif + +#define LOG_BUFF_SIZE 64 * 1024 + +LogContext g_log_context = {LOG_INFO, STDERR_FILENO, NULL}; + +static int log_fsync(LogContext *pContext, const bool bNeedLock); + +static int check_and_mk_log_dir(const char *base_path) +{ + char data_path[MAX_PATH_SIZE]; + + snprintf(data_path, sizeof(data_path), "%s/logs", base_path); + if (!fileExists(data_path)) + { + if (mkdir(data_path, 0755) != 0) + { + fprintf(stderr, "mkdir \"%s\" fail, " \ + "errno: %d, error info: %s", \ + data_path, errno, STRERROR(errno)); + return errno != 0 ? errno : EPERM; + } + } + + return 0; +} + +int log_init() +{ + if (g_log_context.log_buff != NULL) + { + return 0; + } + + return log_init_ex(&g_log_context); +} + +int log_init_ex(LogContext *pContext) +{ + int result; + + memset(pContext, 0, sizeof(LogContext)); + pContext->log_level = LOG_INFO; + pContext->log_fd = STDERR_FILENO; + pContext->log_to_cache = false; + pContext->rotate_immediately = false; + pContext->time_precision = LOG_TIME_PRECISION_SECOND; + + pContext->log_buff = (char *)malloc(LOG_BUFF_SIZE); + if (pContext->log_buff == NULL) + { + fprintf(stderr, "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + LOG_BUFF_SIZE, errno, STRERROR(errno)); + return errno != 0 ? errno : ENOMEM; + } + pContext->pcurrent_buff = pContext->log_buff; + + if ((result=init_pthread_lock(&(pContext->log_thread_lock))) != 0) + { + return result; + } + + return 0; +} + +static int log_open(LogContext *pContext) +{ + if ((pContext->log_fd = open(pContext->log_filename, O_WRONLY | \ + O_CREAT | O_APPEND, 0644)) < 0) + { + fprintf(stderr, "open log file \"%s\" to write fail, " \ + "errno: %d, error info: %s", \ + pContext->log_filename, errno, STRERROR(errno)); + pContext->log_fd = STDERR_FILENO; + return errno != 0 ? errno : EACCES; + } + + pContext->current_size = lseek(pContext->log_fd, 0, SEEK_END); + if (pContext->current_size < 0) + { + fprintf(stderr, "lseek file \"%s\" fail, " \ + "errno: %d, error info: %s", \ + pContext->log_filename, errno, STRERROR(errno)); + return errno != 0 ? errno : EACCES; + } + + return 0; +} + +int log_set_prefix_ex(LogContext *pContext, const char *base_path, \ + const char *filename_prefix) +{ + int result; + + if ((result=check_and_mk_log_dir(base_path)) != 0) + { + return result; + } + + snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s/logs/%s.log", \ + base_path, filename_prefix); + + return log_open(pContext); +} + +int log_set_filename_ex(LogContext *pContext, const char *log_filename) +{ + snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s", log_filename); + return log_open(pContext); +} + +void log_set_cache_ex(LogContext *pContext, const bool bLogCache) +{ + pContext->log_to_cache = bLogCache; +} + +void log_set_time_precision(LogContext *pContext, const int time_precision) +{ + pContext->time_precision = time_precision; +} + +void log_destroy_ex(LogContext *pContext) +{ + if (pContext->log_fd >= 0 && pContext->log_fd != STDERR_FILENO) + { + log_fsync(pContext, true); + + close(pContext->log_fd); + pContext->log_fd = STDERR_FILENO; + + pthread_mutex_destroy(&pContext->log_thread_lock); + } + + if (pContext->log_buff != NULL) + { + free(pContext->log_buff); + pContext->log_buff = NULL; + pContext->pcurrent_buff = NULL; + } +} + +int log_sync_func(void *args) +{ + if (args == NULL) + { + return EINVAL; + } + + return log_fsync((LogContext *)args, true); +} + +int log_notify_rotate(void *args) +{ + if (args == NULL) + { + return EINVAL; + } + + ((LogContext *)args)->rotate_immediately = true; + return 0; +} + +static int log_rotate(LogContext *pContext) +{ + struct tm tm; + time_t current_time; + char new_filename[MAX_PATH_SIZE + 32]; + + if (*(pContext->log_filename) == '\0') + { + return ENOENT; + } + + close(pContext->log_fd); + + current_time = get_current_time(); + localtime_r(¤t_time, &tm); + sprintf(new_filename, "%s.%04d%02d%02d_%02d%02d%02d", \ + pContext->log_filename, \ + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \ + tm.tm_hour, tm.tm_min, tm.tm_sec); + if (rename(pContext->log_filename, new_filename) != 0) + { + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "rename %s to %s fail, errno: %d, error info: %s", \ + __LINE__, pContext->log_filename, new_filename, \ + errno, STRERROR(errno)); + } + + return log_open(pContext); +} + +static int log_check_rotate(LogContext *pContext, const bool bNeedLock) +{ + int result; + + if (pContext->log_fd == STDERR_FILENO) + { + if (pContext->current_size > 0) + { + pContext->current_size = 0; + } + return ENOENT; + } + + if (bNeedLock) + { + pthread_mutex_lock(&(pContext->log_thread_lock)); + } + + if (pContext->rotate_immediately) + { + result = log_rotate(pContext); + pContext->rotate_immediately = false; + } + else + { + result = 0; + } + + if (bNeedLock) + { + pthread_mutex_unlock(&(pContext->log_thread_lock)); + } + + return result; +} + +static int log_fsync(LogContext *pContext, const bool bNeedLock) +{ + int result; + int lock_res; + int write_bytes; + + write_bytes = pContext->pcurrent_buff - pContext->log_buff; + if (write_bytes == 0) + { + if (!pContext->rotate_immediately) + { + return 0; + } + else + { + return log_check_rotate(pContext, bNeedLock); + } + } + + if (bNeedLock && ((lock_res=pthread_mutex_lock( \ + &(pContext->log_thread_lock))) != 0)) + { + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "call pthread_mutex_lock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, lock_res, STRERROR(lock_res)); + } + + if (pContext->rotate_size > 0) + { + pContext->current_size += write_bytes; + if (pContext->current_size > pContext->rotate_size) + { + pContext->rotate_immediately = true; + log_check_rotate(pContext, false); + } + } + + result = 0; + do + { + write_bytes = pContext->pcurrent_buff - pContext->log_buff; + if (write(pContext->log_fd, pContext->log_buff, write_bytes) != \ + write_bytes) + { + result = errno != 0 ? errno : EIO; + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "call write fail, errno: %d, error info: %s\n",\ + __LINE__, result, STRERROR(result)); + break; + } + + if (pContext->log_fd != STDERR_FILENO) + { + if (fsync(pContext->log_fd) != 0) + { + result = errno != 0 ? errno : EIO; + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "call fsync fail, errno: %d, error info: %s\n",\ + __LINE__, result, STRERROR(result)); + break; + } + } + + if (pContext->rotate_immediately) + { + result = log_check_rotate(pContext, false); + } + } while (0); + + pContext->pcurrent_buff = pContext->log_buff; + if (bNeedLock && ((lock_res=pthread_mutex_unlock( \ + &(pContext->log_thread_lock))) != 0)) + { + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "call pthread_mutex_unlock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, lock_res, STRERROR(lock_res)); + } + + return result; +} + +static void doLogEx(LogContext *pContext, struct timeval *tv, \ + const char *caption, const char *text, const int text_len, \ + const bool bNeedSync) +{ + struct tm tm; + int time_fragment; + int buff_len; + int result; + + if (pContext->time_precision == LOG_TIME_PRECISION_SECOND) + { + time_fragment = 0; + } + else + { + if (pContext->time_precision == LOG_TIME_PRECISION_MSECOND) + { + time_fragment = tv->tv_usec / 1000; + } + else + { + time_fragment = tv->tv_usec; + } + } + + localtime_r(&tv->tv_sec, &tm); + if ((result=pthread_mutex_lock(&pContext->log_thread_lock)) != 0) + { + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "call pthread_mutex_lock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + if (text_len + 64 > LOG_BUFF_SIZE) + { + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "log buff size: %d < log text length: %d ", \ + __LINE__, LOG_BUFF_SIZE, text_len + 64); + pthread_mutex_unlock(&(pContext->log_thread_lock)); + return; + } + + if ((pContext->pcurrent_buff - pContext->log_buff) + text_len + 64 \ + > LOG_BUFF_SIZE) + { + log_fsync(pContext, false); + } + + if (pContext->time_precision == LOG_TIME_PRECISION_SECOND) + { + buff_len = sprintf(pContext->pcurrent_buff, \ + "[%04d-%02d-%02d %02d:%02d:%02d] ", \ + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \ + tm.tm_hour, tm.tm_min, tm.tm_sec); + } + else + { + buff_len = sprintf(pContext->pcurrent_buff, \ + "[%04d-%02d-%02d %02d:%02d:%02d.%03d] ", \ + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \ + tm.tm_hour, tm.tm_min, tm.tm_sec, time_fragment); + } + pContext->pcurrent_buff += buff_len; + + if (caption != NULL) + { + buff_len = sprintf(pContext->pcurrent_buff, "%s - ", caption); + pContext->pcurrent_buff += buff_len; + } + memcpy(pContext->pcurrent_buff, text, text_len); + pContext->pcurrent_buff += text_len; + *pContext->pcurrent_buff++ = '\n'; + + if (!pContext->log_to_cache || bNeedSync) + { + log_fsync(pContext, false); + } + + if ((result=pthread_mutex_unlock(&(pContext->log_thread_lock))) != 0) + { + fprintf(stderr, "file: "__FILE__", line: %d, " \ + "call pthread_mutex_unlock fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } +} + +static void doLog(LogContext *pContext, const char *caption, \ + const char *text, const int text_len, const bool bNeedSync) +{ + struct timeval tv; + + if (pContext->time_precision == LOG_TIME_PRECISION_SECOND) + { + tv.tv_sec = get_current_time(); + tv.tv_usec = 0; + } + else + { + gettimeofday(&tv, NULL); + } + + doLogEx(pContext, &tv, caption, text, text_len, bNeedSync); +} + +void log_it_ex1(LogContext *pContext, const int priority, \ + const char *text, const int text_len) +{ + bool bNeedSync; + char *caption; + + switch(priority) + { + case LOG_DEBUG: + bNeedSync = true; + caption = "DEBUG"; + break; + case LOG_INFO: + bNeedSync = true; + caption = "INFO"; + break; + case LOG_NOTICE: + bNeedSync = false; + caption = "NOTICE"; + break; + case LOG_WARNING: + bNeedSync = false; + caption = "WARNING"; + break; + case LOG_ERR: + bNeedSync = false; + caption = "ERROR"; + break; + case LOG_CRIT: + bNeedSync = true; + caption = "CRIT"; + break; + case LOG_ALERT: + bNeedSync = true; + caption = "ALERT"; + break; + case LOG_EMERG: + bNeedSync = true; + caption = "EMERG"; + break; + default: + bNeedSync = false; + caption = "UNKOWN"; + break; + } + + doLog(pContext, caption, text, text_len, bNeedSync); +} + +void log_it_ex(LogContext *pContext, const int priority, const char *format, ...) +{ + bool bNeedSync; + char text[LINE_MAX]; + char *caption; + int len; + + va_list ap; + va_start(ap, format); + len = vsnprintf(text, sizeof(text), format, ap); + va_end(ap); + + switch(priority) + { + case LOG_DEBUG: + bNeedSync = true; + caption = "DEBUG"; + break; + case LOG_INFO: + bNeedSync = true; + caption = "INFO"; + break; + case LOG_NOTICE: + bNeedSync = false; + caption = "NOTICE"; + break; + case LOG_WARNING: + bNeedSync = false; + caption = "WARNING"; + break; + case LOG_ERR: + bNeedSync = false; + caption = "ERROR"; + break; + case LOG_CRIT: + bNeedSync = true; + caption = "CRIT"; + break; + case LOG_ALERT: + bNeedSync = true; + caption = "ALERT"; + break; + case LOG_EMERG: + bNeedSync = true; + caption = "EMERG"; + break; + default: + bNeedSync = false; + caption = "UNKOWN"; + break; + } + + doLog(pContext, caption, text, len, bNeedSync); +} + + +#define _DO_LOG(pContext, priority, caption, bNeedSync) \ + char text[LINE_MAX]; \ + int len; \ +\ + if (pContext->log_level < priority) \ + { \ + return; \ + } \ +\ + { \ + va_list ap; \ + va_start(ap, format); \ + len = vsnprintf(text, sizeof(text), format, ap); \ + va_end(ap); \ + } \ +\ + doLog(pContext, caption, text, len, bNeedSync); \ + + +void logEmergEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_EMERG, "EMERG", true) +} + +void logAlertEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_ALERT, "ALERT", true) +} + +void logCritEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_CRIT, "CRIT", true) +} + +void logErrorEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_ERR, "ERROR", false) +} + +void logWarningEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_WARNING, "WARNING", false) +} + +void logNoticeEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_NOTICE, "NOTICE", false) +} + +void logInfoEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_INFO, "INFO", false) +} + +void logDebugEx(LogContext *pContext, const char *format, ...) +{ + _DO_LOG(pContext, LOG_DEBUG, "DEBUG", false) +} + +void logAccess(LogContext *pContext, struct timeval *tvStart, \ + const char *format, ...) +{ + char text[LINE_MAX]; + int len; + va_list ap; + + va_start(ap, format); + len = vsnprintf(text, sizeof(text), format, ap); + va_end(ap); + + doLogEx(pContext, tvStart, NULL, text, len, false); +} + +#ifndef LOG_FORMAT_CHECK + +void logEmerg(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_EMERG, "EMERG", true) +} + +void logAlert(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_ALERT, "ALERT", true) +} + +void logCrit(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_CRIT, "CRIT", true) +} + +void logError(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_ERR, "ERROR", false) +} + +void logWarning(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_WARNING, "WARNING", false) +} + +void logNotice(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_NOTICE, "NOTICE", false) +} + +void logInfo(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_INFO, "INFO", true) +} + +void logDebug(const char *format, ...) +{ + _DO_LOG((&g_log_context), LOG_DEBUG, "DEBUG", true) +} + +#endif + diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..79f5025 --- /dev/null +++ b/src/logger.h @@ -0,0 +1,212 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//logger.h +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include "common_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_TIME_PRECISION_SECOND 's' //second +#define LOG_TIME_PRECISION_MSECOND 'm' //millisecond +#define LOG_TIME_PRECISION_USSECOND 'u' //microsecond + +typedef struct log_context +{ + /* log level value please see: sys/syslog.h + default value is LOG_INFO */ + int log_level; + + /* default value is STDERR_FILENO */ + int log_fd; + + /* cache buffer */ + char *log_buff; + + /* string end in the cache buffer for next sprintf */ + char *pcurrent_buff; + + /* mutext lock */ + pthread_mutex_t log_thread_lock; + + /* + rotate the log when the log file exceeds this parameter + rotate_size > 0 means need rotate log by log file size + */ + int64_t rotate_size; + + /* log file current size */ + int64_t current_size; + + /* if write to buffer firstly, then sync to disk. + default value is false (no cache) */ + bool log_to_cache; + + /* if rotate the access log */ + bool rotate_immediately; + + /* time precision */ + char time_precision; + + /* save the log filename */ + char log_filename[MAX_PATH_SIZE]; +} LogContext; + +extern LogContext g_log_context; + +/** init function using global log context + * return: 0 for success, != 0 fail +*/ +int log_init(); + +#define log_set_prefix(base_path, filename_prefix) \ + log_set_prefix_ex(&g_log_context, base_path, filename_prefix) + +#define log_set_filename(log_filename) \ + log_set_filename_ex(&g_log_context, log_filename) + +#define log_set_cache(bLogCache) log_set_cache_ex(&g_log_context, bLogCache) + +#define log_destroy() log_destroy_ex(&g_log_context) + +/** init function, use stderr for output by default + * parameters: + * pContext: the log context + * return: 0 for success, != 0 fail +*/ +int log_init_ex(LogContext *pContext); + +/** set log filename prefix, such as "tracker", the log filename will be + * ${base_path}/logs/tracker.log + * parameters: + * pContext: the log context + * base_path: base path + * filename_prefix: log filename prefix + * return: 0 for success, != 0 fail +*/ +int log_set_prefix_ex(LogContext *pContext, const char *base_path, \ + const char *filename_prefix); + +/** set log filename + * parameters: + * pContext: the log context + * log_filename: log filename + * return: 0 for success, != 0 fail +*/ +int log_set_filename_ex(LogContext *pContext, const char *log_filename); + +/** set if use log cache + * parameters: + * pContext: the log context + * bLogCache: true for cache in buffer, false directly write to disk + * return: none +*/ +void log_set_cache_ex(LogContext *pContext, const bool bLogCache); + +/** set time precision + * parameters: + * pContext: the log context + * time_precision: the time precision + * return: none +*/ +void log_set_time_precision(LogContext *pContext, const int time_precision); + +/** destroy function + * parameters: + * pContext: the log context + * bLogCache: true for cache in buffer, false directly write to disk + * return: none +*/ +void log_destroy_ex(LogContext *pContext); + +/** log to file + * parameters: + * pContext: the log context + * priority: unix priority + * format: printf format + * ...: arguments for printf format + * return: none +*/ +void log_it_ex(LogContext *pContext, const int priority, \ + const char *format, ...); + +/** log to file + * parameters: + * pContext: the log context + * priority: unix priority + * text: text string to log + * text_len: text string length (bytes) + * return: none +*/ +void log_it_ex1(LogContext *pContext, const int priority, \ + const char *text, const int text_len); + +/** sync log buffer to log file + * parameters: + * args: should be (LogContext *) + * return: error no, 0 for success, != 0 fail +*/ +int log_sync_func(void *args); + +/** set rotate flag to true + * parameters: + * args: should be (LogContext *) + * return: error no, 0 for success, != 0 fail +*/ +int log_notify_rotate(void *args); + +void logEmergEx(LogContext *pContext, const char *format, ...); +void logCritEx(LogContext *pContext, const char *format, ...); +void logAlertEx(LogContext *pContext, const char *format, ...); +void logErrorEx(LogContext *pContext, const char *format, ...); +void logWarningEx(LogContext *pContext, const char *format, ...); +void logNoticeEx(LogContext *pContext, const char *format, ...); +void logInfoEx(LogContext *pContext, const char *format, ...); +void logDebugEx(LogContext *pContext, const char *format, ...); +void logAccess(LogContext *pContext, struct timeval *tvStart, \ + const char *format, ...); + +//#define LOG_FORMAT_CHECK + +#ifdef LOG_FORMAT_CHECK /*only for format check*/ + +#define logEmerg printf +#define logCrit printf +#define logAlert printf +#define logError printf +#define logWarning printf +#define logNotice printf +#define logInfo printf +#define logDebug printf + +#else + +/* following functions use global log context: g_log_context */ +void logEmerg(const char *format, ...); +void logCrit(const char *format, ...); +void logAlert(const char *format, ...); +void logError(const char *format, ...); +void logWarning(const char *format, ...); +void logNotice(const char *format, ...); +void logInfo(const char *format, ...); +void logDebug(const char *format, ...); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..4ca8b77 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,356 @@ +#include "md5.h" +#include +#include +#include + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform (UINT4[4], unsigned char[64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, unsigned char *, unsigned int); +static void MD5_memcpy (POINTER, POINTER, unsigned int); +static void MD5_memset (POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* + * ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* + * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is + * separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ +(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ +(a) = ROTATE_LEFT ((a), (s)); \ +(a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ +(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ +(a) = ROTATE_LEFT ((a), (s)); \ +(a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ +(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ +(a) = ROTATE_LEFT ((a), (s)); \ +(a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ +(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ +(a) = ROTATE_LEFT ((a), (s)); \ +(a) += (b); \ + } + +/* + * MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +my_md5_init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* + * Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* + * MD5 block update operation. Continues an MD5 message-digest operation, + * processing another message block, and updating the context. + */ +void +my_md5_update(MD5_CTX *context, unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4) inputLen << 3)) < ((UINT4) inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4) inputLen >> 29); + + partLen = 64 - index; + + /* + * Transform as many times as possible. + */ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER) & context->buffer[index], (POINTER) input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER) & context->buffer[index], (POINTER) & input[i], + inputLen - i); +} + +/* + * MD5 finalization. Ends an MD5 message-digest operation, writing the the + * message digest and zeroizing the context. + */ +void +my_md5_final(unsigned char digest[16], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* + * Pad out to 56 mod 64. + */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + my_md5_update(context, PADDING, padLen); + + /* Append length (before padding) */ + my_md5_update(context, bits, 8); + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* + * Zeroize sensitive information. + */ + MD5_memset((POINTER) context, 0, sizeof(*context)); +} + +/* + * MD5 basic transformation. Transforms state based on block. + */ +static void +MD5Transform(UINT4 state[4], unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], + x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* + * Zeroize sensitive information. + * + */ + MD5_memset((POINTER) x, 0, sizeof(x)); +} + +/* + * Encodes input (UINT4) into output (unsigned char). Assumes len is a + * multiple of 4. + */ +static void +Encode(unsigned char *output, UINT4 *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char) (input[i] & 0xff); + output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff); + output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff); + output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff); + } +} + +/* + * Decodes input (unsigned char) into output (UINT4). Assumes len is a + * multiple of 4. + */ +static void +Decode(UINT4 *output, unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) | + (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24); +} + +/* + * Note: Replace "for loop" with standard memcpy if possible. + */ + +static void +MD5_memcpy(POINTER output, POINTER input, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* + * Note: Replace "for loop" with standard memset if possible. + */ +static void +MD5_memset(POINTER output, int value, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *) output)[i] = (char) value; +} + +/* + * Digests a string + */ +int my_md5_string(char *string,unsigned char digest[16]) +{ + MD5_CTX context; + unsigned int len = strlen(string); + + my_md5_init(&context); + my_md5_update(&context, (unsigned char *)string, len); + my_md5_final(digest, &context); + return 0; +} + +int my_md5_buffer(char *buffer, unsigned int len, unsigned char digest[16]) +{ + MD5_CTX context; + + my_md5_init(&context); + my_md5_update(&context, (unsigned char *)buffer, len); + my_md5_final(digest, &context); + return 0; +} + +int my_md5_file(char *filename,unsigned char digest[16]) +{ + FILE *file; + MD5_CTX context; + int len; + unsigned char buffer[1024]; + + if ((file = fopen(filename, "rb")) == NULL) + return -1; + else { + my_md5_init(&context); + while ((len = fread(buffer, 1, 1024, file)) > 0) + { + my_md5_update(&context, buffer, len); + } + my_md5_final(digest, &context); + + fclose(file); + } + return 0; +} + diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..8998286 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,55 @@ +#ifndef MCL_MD5_H +#define MCL_MD5_H +#include + +typedef unsigned char *POINTER; +typedef unsigned short int UINT2; +typedef unsigned int UINT4; + +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +#ifdef __cplusplus +extern "C" { +#endif + +/** md5 for string + * parameters: + * string: the string to md5 + * digest: store the md5 digest + * return: 0 for success, != 0 fail +*/ +int my_md5_string(char *string, unsigned char digest[16]); + +/** md5 for file + * parameters: + * filename: the filename whose content to md5 + * digest: store the md5 digest + * return: 0 for success, != 0 fail +*/ +int my_md5_file(char *filename, unsigned char digest[16]); + +/** md5 for buffer + * parameters: + * buffer: the buffer to md5 + * len: the buffer length + * digest: store the md5 digest + * return: 0 for success, != 0 fail +*/ +int my_md5_buffer(char *buffer, unsigned int len, unsigned char digest[16]); + +void my_md5_init (MD5_CTX *); + +void my_md5_update (MD5_CTX *, unsigned char *, unsigned int); + +void my_md5_final (unsigned char [16], MD5_CTX *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/process_ctrl.c b/src/process_ctrl.c new file mode 100644 index 0000000..e52b06e --- /dev/null +++ b/src/process_ctrl.c @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include "shared_func.h" +#include "logger.h" +#include "process_ctrl.h" + +int get_pid_from_file(const char *pidFilename, pid_t *pid) +{ + char buff[32]; + int64_t file_size; + int result; + + if (access(pidFilename, F_OK) != 0) { + return errno != 0 ? errno : EPERM; + } + + file_size = sizeof(buff) - 1; + if ((result=getFileContentEx(pidFilename, buff, 0, &file_size)) != 0) { + return result; + } + + *(buff + file_size) = '\0'; + *pid = strtol(buff, NULL, 10); + if (*pid == 0) { + return EINVAL; + } + + return 0; +} + +int write_to_pid_file(const char *pidFilename) +{ + char buff[32]; + int len; + + len = sprintf(buff, "%d", (int)getpid()); + return writeToFile(pidFilename, buff, len); +} + +int delete_pid_file(const char *pidFilename) +{ + if (unlink(pidFilename) == 0) { + return 0; + } + else { + return errno != 0 ? errno : ENOENT; + } +} + +static int do_stop(const char *pidFilename, const bool bShowError, pid_t *pid) +{ + int result; + + if ((result=get_pid_from_file(pidFilename, pid)) != 0) { + if (bShowError) { + if (result == ENOENT) { + fprintf(stderr, "pid file: %s not exist!\n", pidFilename); + } + else { + fprintf(stderr, "get pid from file: %s fail, " \ + "errno: %d, error info: %s\n", + pidFilename, result, strerror(result)); + } + } + + return result; + } + + if (kill(*pid, SIGTERM) == 0) { + return 0; + } + else { + result = errno != 0 ? errno : EPERM; + if (bShowError || result != ESRCH) { + fprintf(stderr, "kill pid: %d fail, errno: %d, error info: %s\n", + (int)*pid, result, strerror(result)); + } + return result; + } +} + +int process_stop(const char *pidFilename) +{ + pid_t pid; + int result; + + result = do_stop(pidFilename, true, &pid); + if (result != 0) { + return result; + } + + fprintf(stderr, "waiting for pid [%d] exit ...\n", (int)pid); + do { + sleep(1); + } while (kill(pid, SIGTERM) == 0); + fprintf(stderr, "pid [%d] exit.\n", (int)pid); + + return 0; +} + +int process_restart(const char *pidFilename) +{ + int result; + pid_t pid; + + result = do_stop(pidFilename, false, &pid); + if (result == 0) { + fprintf(stderr, "waiting for pid [%d] exit ...\n", (int)pid); + do { + sleep(1); + } while (kill(pid, SIGTERM) == 0); + fprintf(stderr, "starting ...\n"); + } + + if (result == ENOENT || result == ESRCH) { + return 0; + } + + return result; +} + +int process_exist(const char *pidFilename) +{ + pid_t pid; + int result; + + if ((result=get_pid_from_file(pidFilename, &pid)) != 0) { + if (result == ENOENT) { + return false; + } + else { + fprintf(stderr, "get pid from file: %s fail, " \ + "errno: %d, error info: %s\n", + pidFilename, result, strerror(result)); + return true; + } + } + + if (kill(pid, 0) == 0) { + return true; + } + else if (errno == ENOENT || errno == ESRCH) { + return false; + } + else { + fprintf(stderr, "kill pid: %d fail, errno: %d, error info: %s\n", + (int)pid, errno, strerror(errno)); + return true; + } +} + +int get_base_path_from_conf_file(const char *filename, char *base_path, + const int path_size) +{ + char *pBasePath; + IniContext iniContext; + int result; + + memset(&iniContext, 0, sizeof(IniContext)); + if ((result=iniLoadFromFile(filename, &iniContext)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "load conf file \"%s\" fail, ret code: %d", \ + __LINE__, filename, result); + return result; + } + + do + { + pBasePath = iniGetStrValue(NULL, "base_path", &iniContext); + if (pBasePath == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "conf file \"%s\" must have item " \ + "\"base_path\"!", __LINE__, filename); + result = ENOENT; + break; + } + + snprintf(base_path, path_size, "%s", pBasePath); + chopPath(base_path); + if (!fileExists(base_path)) + { + logError("file: "__FILE__", line: %d, " \ + "\"%s\" can't be accessed, error info: %s", \ + __LINE__, base_path, STRERROR(errno)); + result = errno != 0 ? errno : ENOENT; + break; + } + if (!isDir(base_path)) + { + logError("file: "__FILE__", line: %d, " \ + "\"%s\" is not a directory!", \ + __LINE__, base_path); + result = ENOTDIR; + break; + } + } while (0); + + iniFreeContext(&iniContext); + return result; +} + +int process_action(const char *pidFilename, const char *action, bool *stop) +{ + *stop = false; + if (action == NULL) + { + return 0; + } + + if (strcmp(action, "stop") == 0) + { + *stop = true; + return process_stop(pidFilename); + } + else if (strcmp(action, "restart") == 0) + { + return process_restart(pidFilename); + } + else if (strcmp(action, "start") == 0) + { + return 0; + } + else + { + fprintf(stderr, "invalid action: %s\n", action); + return EINVAL; + } +} + diff --git a/src/process_ctrl.h b/src/process_ctrl.h new file mode 100644 index 0000000..e9f9fa1 --- /dev/null +++ b/src/process_ctrl.h @@ -0,0 +1,37 @@ + +#ifndef PROCESS_CTRL_H +#define PROCESS_CTRL_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int get_base_path_from_conf_file(const char *filename, char *base_path, + const int path_size); + +int get_pid_from_file(const char *pidFilename, pid_t *pid); + +int write_to_pid_file(const char *pidFilename); + +int delete_pid_file(const char *pidFilename); + +int process_stop(const char *pidFilename); + +int process_restart(const char *pidFilename); + +int process_exist(const char *pidFilename); + +int process_action(const char *pidFilename, const char *action, bool *stop); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/process_ctrl.o b/src/process_ctrl.o new file mode 100644 index 0000000..d1ef506 Binary files /dev/null and b/src/process_ctrl.o differ diff --git a/src/pthread_func.c b/src/pthread_func.c new file mode 100644 index 0000000..2ec04f4 --- /dev/null +++ b/src/pthread_func.c @@ -0,0 +1,193 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pthread_func.h" +#include "logger.h" + +int init_pthread_lock(pthread_mutex_t *pthread_lock) +{ + pthread_mutexattr_t mat; + int result; + + if ((result=pthread_mutexattr_init(&mat)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutexattr_init fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + if ((result=pthread_mutexattr_settype(&mat, \ + PTHREAD_MUTEX_ERRORCHECK)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutexattr_settype fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + if ((result=pthread_mutex_init(pthread_lock, &mat)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_mutex_init fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + if ((result=pthread_mutexattr_destroy(&mat)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call thread_mutexattr_destroy fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + return 0; +} + +int init_pthread_attr(pthread_attr_t *pattr, const int stack_size) +{ + size_t old_stack_size; + size_t new_stack_size; + int result; + + if ((result=pthread_attr_init(pattr)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_attr_init fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + if ((result=pthread_attr_getstacksize(pattr, &old_stack_size)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_attr_getstacksize fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + if (stack_size > 0) + { + if (old_stack_size != stack_size) + { + new_stack_size = stack_size; + } + else + { + new_stack_size = 0; + } + } + else if (old_stack_size < 1 * 1024 * 1024) + { + new_stack_size = 1 * 1024 * 1024; + } + else + { + new_stack_size = 0; + } + + if (new_stack_size > 0) + { + if ((result=pthread_attr_setstacksize(pattr, \ + new_stack_size)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_attr_setstacksize fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + } + + if ((result=pthread_attr_setdetachstate(pattr, \ + PTHREAD_CREATE_DETACHED)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call pthread_attr_setdetachstate fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + return result; + } + + return 0; +} + +int create_work_threads(int *count, void *(*start_func)(void *), \ + void *arg, pthread_t *tids, const int stack_size) +{ + int result; + pthread_attr_t thread_attr; + pthread_t *ptid; + pthread_t *ptid_end; + + if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0) + { + return result; + } + + result = 0; + ptid_end = tids + (*count); + for (ptid=tids; ptid +#include +#include +#include +#include +#include +#include "common_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int init_pthread_lock(pthread_mutex_t *pthread_lock); +int init_pthread_attr(pthread_attr_t *pattr, const int stack_size); + +int create_work_threads(int *count, void *(*start_func)(void *), \ + void *arg, pthread_t *tids, const int stack_size); +int kill_work_threads(pthread_t *tids, const int count); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/pthread_pool.c b/src/pthread_pool.c new file mode 100644 index 0000000..2fa3966 --- /dev/null +++ b/src/pthread_pool.c @@ -0,0 +1,256 @@ +/** +* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com +* +* FastLib may be copied only under the terms of the GNU General +* Public License V3, which may be found in the FastLib source kit. +* Please visit the FastLib Home Page http://www.csource.org/ for more detail. +**/ + +#include +#include +#include +#include + +#include "pthread_pool.h" + +/* + *the thread pool + */ +static threadpool_info_t *pool; + +/* + * the thread callback function proxy + * parameters: + * arg:the thread callback function parameter + */ +static void *callback_proxy(void *arg); + +/* + * push the thread into the pool + * parameters: + * thread:the thread will push into the poolbool + * return: + * 0:success + * >0 : fail + */ +static int push2pool(thread_info_t *thread); + +static void *callback_proxy(void *arg) +{ + thread_info_t* thread = (thread_info_t *) arg; + while(initialized == pool->state) + { + thread->func(thread->arg); + + if(pool == NULL || initialized != pool->state) break; + + pthread_mutex_lock(&thread->mutex_locker); + + if(0 == push2pool(thread)) + { + pthread_cond_wait(&thread->run_locker,&thread->mutex_locker); + pthread_mutex_unlock(&thread->mutex_locker); + } + else + { + pthread_mutex_unlock( &thread->mutex_locker ); + pthread_cond_destroy( &thread->run_locker ); + pthread_mutex_destroy( &thread->mutex_locker ); + + free( thread ); + break; + } + } + + pthread_mutex_lock(&pool->mutex_locker); + pool->current_size --; + if(0 >= pool->current_size) pthread_cond_signal(&pool->empty_locker); + pthread_mutex_unlock(&pool->mutex_locker); + return NULL; +} + +static int push2pool(thread_info_t *thread) +{ + int result = -1; + do + { + pthread_mutex_lock(&pool->mutex_locker); + if( pool->current_index < pool->total_size ) + { + pool->list[ pool->current_index ] = thread; + pool->current_index++; + result = 0; + + pthread_cond_signal( &pool->run_locker); + + if( pool->current_index >= pool->current_size ) + { + pthread_cond_signal( &pool->full_locker ); + } + } + }while(0); + pthread_mutex_unlock(&pool->mutex_locker); + + return result; +} + +int threadpool_init(int size) +{ + if(0 >= size) + { + return -1; + } + + pool = (threadpool_info_t *) malloc(sizeof(threadpool_info_t)); + if(NULL == pool) + { + return -2; + } + memset(pool,0,sizeof(threadpool_info_t)); + pool->state = initializing; + pool->total_size = size; + pool->current_size = 0; + pool->current_index = 0; + pthread_mutex_init(&pool->mutex_locker,NULL); + pthread_cond_init(&pool->run_locker,NULL); + pthread_cond_init(&pool->empty_locker,NULL); + pthread_cond_init(&pool->full_locker,NULL); + + pool->list = (thread_info_t **) malloc(sizeof(thread_info_t*) * size); + if(NULL == pool->list) + { + pthread_cond_destroy(&pool->run_locker); + pthread_cond_destroy(&pool->empty_locker); + pthread_cond_destroy(&pool->full_locker); + pthread_mutex_destroy(&pool->mutex_locker); + free(pool); + return -2; + } + + pool->state = initialized; + return 0; +} + +int threadpool_run(callback func,void *arg) +{ + if(NULL == pool) + { + return -1; + } + + int result = 0; + do + { + pthread_mutex_lock(&pool->mutex_locker); + if(NULL == pool || initialized != pool->state) //the pool cannot use + { + result = -1; + break; + } + + //current size is >= the max pool size and all thread are busy now + while(pool->current_index <= 0 && pool->current_size >= pool->total_size) + { + pthread_cond_wait(&pool->run_locker,&pool->mutex_locker); + } + + if(0 >= pool->current_index) + { + thread_info_t * thread = (thread_info_t *) malloc(sizeof(thread_info_t)); + if(NULL == thread) + { + result = -2; + break; + } + memset(thread,0,sizeof(thread_info_t)); + + pthread_mutex_init(&thread->mutex_locker,NULL); + pthread_cond_init(&thread->run_locker,NULL); + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + + thread->arg = arg; + thread->func = func; + + if(0 == pthread_create(&thread->id,&attr,callback_proxy,thread)) + { + pool->current_size ++; + } + else + { + result = -3; + pthread_mutex_destroy(&thread->mutex_locker); + pthread_cond_destroy(&thread->run_locker); + free(thread); + } + break; + } + else + { + pool->current_index --;//because the array begin with 0 + thread_info_t *thread = pool->list[ pool->current_index ]; + pool->list[ pool->current_index ] = NULL; + + thread->func = func; + thread->arg = arg; + + pthread_mutex_lock( &thread->mutex_locker ); + pthread_cond_signal( &thread->run_locker ) ; + pthread_mutex_unlock ( &thread->mutex_locker ); + } + }while(0); + pthread_mutex_unlock(&pool->mutex_locker); + return result; + + return 0; +} + +int threadpool_free() +{ + if(NULL == pool) return 0; + + pthread_mutex_lock( &pool->mutex_locker); + + if( pool->current_index < pool->current_size ) + { + pthread_cond_wait( &pool->full_locker, &pool->mutex_locker ); + } + + pool->state = uninstalling; + int i = 0; + for( i = 0; i < pool->current_index; i++ ) + { + thread_info_t *thread = pool->list[i]; + + pthread_mutex_lock( &thread->mutex_locker ); + pthread_cond_signal( &thread->run_locker ) ; + pthread_mutex_unlock ( &thread->mutex_locker ); + } + + if(0 < pool->current_size) + { + pthread_cond_wait( &pool->empty_locker, &pool->mutex_locker); + } + + for( i = 0; i < pool->current_index; i++ ) + { + free( pool->list[ i ] ); + pool->list[ i ] = NULL; + } + + pthread_mutex_unlock( &pool->mutex_locker ); + + pool->current_index = 0; + + pthread_mutex_destroy( &pool->mutex_locker ); + pthread_cond_destroy( &pool->run_locker ); + pthread_cond_destroy( &pool->full_locker ); + pthread_cond_destroy( &pool->empty_locker ); + + free( pool->list ); + pool->list = NULL; + free( pool); + pool = NULL; + return 0; +} diff --git a/src/pthread_pool.h b/src/pthread_pool.h new file mode 100644 index 0000000..e6da86f --- /dev/null +++ b/src/pthread_pool.h @@ -0,0 +1,116 @@ +/** +* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com +* +* FastLib may be copied only under the terms of the GNU General +* Public License V3, which may be found in the FastLib source kit. +* Please visit the FastLib Home Page http://www.csource.org/ for more detail. +**/ + +#ifndef PTHREAD_POOL_H_ +#define PTHREAD_POOL_H_ + +#include + +/* + * define the callback function type of thread + */ +typedef void (*callback)(void *); + + +/* + * the thread pool state + * member: + * uninit : not initialize the thread pool. + * initing : initializing the thread pool. + * using : the pool can use. + * uninstalling : uninstalling the thread pool. + * uninstalled : uninstall the thread pool is over. + */ +typedef enum threadpool_state +{ + uninitialized, + initializing, + initialized, + uninstalling, + uninstalled, +}thread_state_t; + + +/* + * define the thread type which in the pool + * members: + * id : the thread id + * mutex_locker : the mutext locker + * run_locker : the locker for noticing the thread do running or waitting + * func : the callback function for thread + * arg : the callback parameter + */ +typedef struct thread_info +{ + pthread_t id; + pthread_mutex_t mutex_locker; + pthread_cond_t run_locker; + callback func; + void *arg; +}thread_info_t; + +/* + * the structure for the thread pool + * member: + * list : the initialazed thread list + * mutex_locker : the mutex locker for the thread operation. + * run_locker : the locker for noticing the thread do running or waitting. + * full_locker : the locker notice the thread is stoping when free the thread pool and the pool is not full . + * empry_Locker : the locker notice the thread waitting for the busy thread work over,then do with the thread. + * state : the pool's current state. + * total_size : the pool max size; + * current_size : the thread count for the current pool ; + * current_index : the busy thread in the pool index. + */ +typedef struct threadpool_info +{ + thread_info_t **list; + pthread_mutex_t mutex_locker; + pthread_cond_t run_locker; + pthread_cond_t full_locker; + pthread_cond_t empty_locker; + thread_state_t state; + int total_size; + int current_size; + int current_index; +}threadpool_info_t; + +/* + * initialize the thread pool + * parameters: + * size : thread pool max size + * return: + * 0:initialize pool success; + * -1:the size parameter is less 0; + * -2:initialize pool is fail,malloc memory for pool or pool->list is error; + */ +int threadpool_init(int size); + +/* + * run the function with the thread from pool + * parameter: + * func:the thread callback function + * arg:the parameter of callback function + * return: + * 0 : success + * -1: the pool is NULL; + * -2 : malloc memory for thread is error; + * -3 : create thread is error; + */ +int threadpool_run(callback func,void *arg); + +/* + * free and destroy the thread pool memory + * return: + * 0 : success + * less 0 : fail + */ +int threadpool_destroy(); + + +#endif /* PTHREAD_POOL_H_ */ diff --git a/src/sched_thread.c b/src/sched_thread.c new file mode 100644 index 0000000..58f67f5 --- /dev/null +++ b/src/sched_thread.c @@ -0,0 +1,508 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include "sched_thread.h" +#include "shared_func.h" +#include "pthread_func.h" +#include "logger.h" + +volatile bool g_schedule_flag = false; +volatile time_t g_current_time = 0; + +static ScheduleArray waiting_schedule_array = {NULL, 0}; +static int waiting_del_id = -1; + +static int sched_cmp_by_next_call_time(const void *p1, const void *p2) +{ + return ((ScheduleEntry *)p1)->next_call_time - \ + ((ScheduleEntry *)p2)->next_call_time; +} + +static int sched_init_entries(ScheduleArray *pScheduleArray) +{ + ScheduleEntry *pEntry; + ScheduleEntry *pEnd; + time_t time_base; + struct tm tm_current; + struct tm tm_base; + + if (pScheduleArray->count < 0) + { + logError("file: "__FILE__", line: %d, " \ + "schedule count %d < 0", \ + __LINE__, pScheduleArray->count); + return EINVAL; + } + if (pScheduleArray->count == 0) + { + return 0; + } + + g_current_time = time(NULL); + localtime_r((time_t *)&g_current_time, &tm_current); + pEnd = pScheduleArray->entries + pScheduleArray->count; + for (pEntry=pScheduleArray->entries; pEntryinterval <= 0) + { + logError("file: "__FILE__", line: %d, " \ + "shedule interval %d <= 0", \ + __LINE__, pEntry->interval); + return EINVAL; + } + + if (pEntry->time_base.hour == TIME_NONE) + { + pEntry->next_call_time = g_current_time + \ + pEntry->interval; + } + else + { + if (tm_current.tm_hour > pEntry->time_base.hour || \ + (tm_current.tm_hour == pEntry->time_base.hour \ + && tm_current.tm_min >= pEntry->time_base.minute)) + { + memcpy(&tm_base, &tm_current, sizeof(struct tm)); + } + else + { + time_base = g_current_time - 24 * 3600; + localtime_r(&time_base, &tm_base); + } + + tm_base.tm_hour = pEntry->time_base.hour; + tm_base.tm_min = pEntry->time_base.minute; + tm_base.tm_sec = 0; + time_base = mktime(&tm_base); + + pEntry->next_call_time = g_current_time + \ + pEntry->interval - (g_current_time - \ + time_base) % pEntry->interval; + } + + /* + { + char buff1[32]; + char buff2[32]; + logInfo("id=%d, current time=%s, first call time=%s\n", \ + pEntry->id, formatDatetime(g_current_time, \ + "%Y-%m-%d %H:%M:%S", buff1, sizeof(buff1)), \ + formatDatetime(pEntry->next_call_time, \ + "%Y-%m-%d %H:%M:%S", buff2, sizeof(buff2))); + } + */ + } + + return 0; +} + +static void sched_make_chain(ScheduleContext *pContext) +{ + ScheduleArray *pScheduleArray; + ScheduleEntry *pEntry; + + pScheduleArray = &(pContext->scheduleArray); + if (pScheduleArray->count == 0) + { + pContext->head = NULL; + pContext->tail = NULL; + return; + } + + qsort(pScheduleArray->entries, pScheduleArray->count, \ + sizeof(ScheduleEntry), sched_cmp_by_next_call_time); + + pContext->head = pScheduleArray->entries; + pContext->tail = pScheduleArray->entries + (pScheduleArray->count - 1); + for (pEntry=pScheduleArray->entries; pEntrytail; pEntry++) + { + pEntry->next = pEntry + 1; + } + pContext->tail->next = NULL; +} + +static int sched_check_waiting(ScheduleContext *pContext) +{ + ScheduleArray *pScheduleArray; + ScheduleEntry *newEntries; + ScheduleEntry *pWaitingEntry; + ScheduleEntry *pWaitingEnd; + ScheduleEntry *pSchedEntry; + ScheduleEntry *pSchedEnd; + int allocCount; + int newCount; + int result; + int deleteCount; + + pScheduleArray = &(pContext->scheduleArray); + deleteCount = 0; + if (waiting_del_id >= 0) + { + pSchedEnd = pScheduleArray->entries + pScheduleArray->count; + for (pSchedEntry=pScheduleArray->entries; \ + pSchedEntryid == waiting_del_id) + { + break; + } + } + + if (pSchedEntry < pSchedEnd) + { + pSchedEntry++; + while (pSchedEntry < pSchedEnd) + { + memcpy(pSchedEntry - 1, pSchedEntry, \ + sizeof(ScheduleEntry)); + pSchedEntry++; + } + + deleteCount++; + pScheduleArray->count--; + + logDebug("file: "__FILE__", line: %d, " \ + "delete task id: %d, " \ + "current schedule count: %d", __LINE__, \ + waiting_del_id, pScheduleArray->count); + } + + waiting_del_id = -1; + } + + if (waiting_schedule_array.count == 0) + { + if (deleteCount > 0) + { + sched_make_chain(pContext); + return 0; + } + + return ENOENT; + } + + allocCount = pScheduleArray->count + waiting_schedule_array.count; + newEntries = (ScheduleEntry *)malloc(sizeof(ScheduleEntry) * allocCount); + if (newEntries == NULL) + { + result = errno != 0 ? errno : ENOMEM; + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes failed, " \ + "errno: %d, error info: %s", \ + __LINE__, (int)sizeof(ScheduleEntry) * allocCount, \ + result, STRERROR(result)); + + if (deleteCount > 0) + { + sched_make_chain(pContext); + } + return result; + } + + if (pScheduleArray->count > 0) + { + memcpy(newEntries, pScheduleArray->entries, \ + sizeof(ScheduleEntry) * pScheduleArray->count); + } + newCount = pScheduleArray->count; + pWaitingEnd = waiting_schedule_array.entries + waiting_schedule_array.count; + for (pWaitingEntry=waiting_schedule_array.entries; \ + pWaitingEntryid == pSchedEntry->id) + { + memcpy(pSchedEntry, pWaitingEntry, \ + sizeof(ScheduleEntry)); + break; + } + } + + if (pSchedEntry == pSchedEnd) + { + memcpy(pSchedEntry, pWaitingEntry, \ + sizeof(ScheduleEntry)); + newCount++; + } + } + + logDebug("file: "__FILE__", line: %d, " \ + "schedule add entries: %d, replace entries: %d", + __LINE__, newCount - pScheduleArray->count, \ + waiting_schedule_array.count - (newCount - pScheduleArray->count)); + + if (pScheduleArray->entries != NULL) + { + free(pScheduleArray->entries); + } + pScheduleArray->entries = newEntries; + pScheduleArray->count = newCount; + + free(waiting_schedule_array.entries); + waiting_schedule_array.count = 0; + waiting_schedule_array.entries = NULL; + + sched_make_chain(pContext); + + return 0; +} + +static void *sched_thread_entrance(void *args) +{ + ScheduleContext *pContext; + ScheduleEntry *pPrevious; + ScheduleEntry *pCurrent; + ScheduleEntry *pSaveNext; + ScheduleEntry *pNode; + ScheduleEntry *pUntil; + int exec_count; + int i; + int sleep_time; + + pContext = (ScheduleContext *)args; + if (sched_init_entries(&(pContext->scheduleArray)) != 0) + { + free(pContext); + return NULL; + } + sched_make_chain(pContext); + + g_schedule_flag = true; + while (*(pContext->pcontinue_flag)) + { + sched_check_waiting(pContext); + if (pContext->scheduleArray.count == 0) //no schedule entry + { + sleep(1); + g_current_time = time(NULL); + continue; + } + + g_current_time = time(NULL); + sleep_time = pContext->head->next_call_time - g_current_time; + + /* + //fprintf(stderr, "count=%d, sleep_time=%d\n", \ + pContext->scheduleArray.count, sleep_time); + */ + while (sleep_time > 0 && *(pContext->pcontinue_flag)) + { + sleep(1); + g_current_time = time(NULL); + if (sched_check_waiting(pContext) == 0) + { + break; + } + sleep_time--; + } + + if (!(*(pContext->pcontinue_flag))) + { + break; + } + + exec_count = 0; + pCurrent = pContext->head; + while (*(pContext->pcontinue_flag) && (pCurrent != NULL \ + && pCurrent->next_call_time <= g_current_time)) + { + //fprintf(stderr, "exec task id=%d\n", pCurrent->id); + pCurrent->task_func(pCurrent->func_args); + pCurrent->next_call_time = g_current_time + \ + pCurrent->interval; + pCurrent = pCurrent->next; + exec_count++; + } + + if (exec_count == 0 || pContext->scheduleArray.count == 1) + { + continue; + } + + if (exec_count > pContext->scheduleArray.count / 2) + { + sched_make_chain(pContext); + continue; + } + + pNode = pContext->head; + pContext->head = pCurrent; //new chain head + for (i=0; inext_call_time >= pContext->tail->next_call_time) + { + pContext->tail->next = pNode; + pContext->tail = pNode; + pNode = pNode->next; + pContext->tail->next = NULL; + continue; + } + + pPrevious = NULL; + pUntil = pContext->head; + while (pUntil != NULL && \ + pNode->next_call_time > pUntil->next_call_time) + { + pPrevious = pUntil; + pUntil = pUntil->next; + } + + pSaveNext = pNode->next; + if (pPrevious == NULL) + { + pContext->head = pNode; + } + else + { + pPrevious->next = pNode; + } + pNode->next = pUntil; + + pNode = pSaveNext; + } + } + + g_schedule_flag = false; + + logDebug("file: "__FILE__", line: %d, " \ + "schedule thread exit", __LINE__); + + free(pContext); + return NULL; +} + +static int sched_dup_array(const ScheduleArray *pSrcArray, \ + ScheduleArray *pDestArray) +{ + int result; + int bytes; + + if (pSrcArray->count == 0) + { + pDestArray->entries = NULL; + pDestArray->count = 0; + return 0; + } + + bytes = sizeof(ScheduleEntry) * pSrcArray->count; + pDestArray->entries = (ScheduleEntry *)malloc(bytes); + if (pDestArray->entries == NULL) + { + result = errno != 0 ? errno : ENOMEM; + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes failed, " \ + "errno: %d, error info: %s", \ + __LINE__, bytes, result, STRERROR(result)); + return result; + } + + memcpy(pDestArray->entries, pSrcArray->entries, bytes); + pDestArray->count = pSrcArray->count; + return 0; +} + +int sched_add_entries(const ScheduleArray *pScheduleArray) +{ + int result; + + if (pScheduleArray->count == 0) + { + logDebug("file: "__FILE__", line: %d, " \ + "no schedule entry", __LINE__); + return ENOENT; + } + + while (waiting_schedule_array.entries != NULL) + { + logDebug("file: "__FILE__", line: %d, " \ + "waiting for schedule array ready ...", __LINE__); + sleep(1); + } + + if ((result=sched_dup_array(pScheduleArray, &waiting_schedule_array))!=0) + { + return result; + } + + return sched_init_entries(&waiting_schedule_array); +} + +int sched_del_entry(const int id) +{ + if (id < 0) + { + logError("file: "__FILE__", line: %d, " \ + "id: %d is invalid!", __LINE__, id); + return EINVAL; + } + + while (waiting_del_id >= 0) + { + logDebug("file: "__FILE__", line: %d, " \ + "waiting for delete ready ...", __LINE__); + sleep(1); + } + + waiting_del_id = id; + return 0; +} + +int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid, \ + const int stack_size, bool * volatile pcontinue_flag) +{ + int result; + pthread_attr_t thread_attr; + ScheduleContext *pContext; + + pContext = (ScheduleContext *)malloc(sizeof(ScheduleContext)); + if (pContext == NULL) + { + result = errno != 0 ? errno : ENOMEM; + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes failed, " \ + "errno: %d, error info: %s", \ + __LINE__, (int)sizeof(ScheduleContext), \ + result, STRERROR(result)); + return result; + } + + if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0) + { + free(pContext); + return result; + } + + if ((result=sched_dup_array(pScheduleArray, \ + &(pContext->scheduleArray))) != 0) + { + free(pContext); + return result; + } + + pContext->pcontinue_flag = pcontinue_flag; + if ((result=pthread_create(ptid, &thread_attr, \ + sched_thread_entrance, pContext)) != 0) + { + free(pContext); + logError("file: "__FILE__", line: %d, " \ + "create thread failed, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + } + + pthread_attr_destroy(&thread_attr); + return result; +} + diff --git a/src/sched_thread.h b/src/sched_thread.h new file mode 100644 index 0000000..9224a6f --- /dev/null +++ b/src/sched_thread.h @@ -0,0 +1,78 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#ifndef _SCHED_THREAD_H_ +#define _SCHED_THREAD_H_ + +#include +#include "common_define.h" + +typedef int (*TaskFunc) (void *args); + +typedef struct tagScheduleEntry +{ + int id; //the task id + + /* the time base to execute task, such as 00:00, interval is 3600, + means execute the task every hour as 1:00, 2:00, 3:00 etc. */ + TimeInfo time_base; + + int interval; //the interval for execute task, unit is second + + TaskFunc task_func; //callback function + void *func_args; //arguments pass to callback function + + /* following are internal fields, do not set manually! */ + time_t next_call_time; + struct tagScheduleEntry *next; +} ScheduleEntry; + +typedef struct +{ + ScheduleEntry *entries; + int count; +} ScheduleArray; + +typedef struct +{ + ScheduleArray scheduleArray; + ScheduleEntry *head; //schedule chain head + ScheduleEntry *tail; //schedule chain tail + bool *pcontinue_flag; +} ScheduleContext; + +#ifdef __cplusplus +extern "C" { +#endif + +extern volatile bool g_schedule_flag; //schedule continue running flag +extern volatile time_t g_current_time; //the current time + + +#define get_current_time() (g_schedule_flag ? g_current_time: time(NULL)) + +int sched_add_entries(const ScheduleArray *pScheduleArray); +int sched_del_entry(const int id); + +/** execute the schedule thread + * parameters: + * pScheduleArray: schedule task + * ptid: store the schedule thread id + * stack_size: set thread stack size (byes) + * pcontinue_flag: main process continue running flag + * return: error no, 0 for success, != 0 fail +*/ +int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid, \ + const int stack_size, bool * volatile pcontinue_flag); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/shared_func.c b/src/shared_func.c new file mode 100644 index 0000000..0096bf3 --- /dev/null +++ b/src/shared_func.c @@ -0,0 +1,2069 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shared_func.h" +#include "logger.h" +#include "sockopt.h" + +char *formatDatetime(const time_t nTime, \ + const char *szDateFormat, \ + char *buff, const int buff_size) +{ + static char szDateBuff[128]; + struct tm tmTime; + int size; + + localtime_r(&nTime, &tmTime); + if (buff == NULL) + { + buff = szDateBuff; + size = sizeof(szDateBuff); + } + else + { + size = buff_size; + } + + *buff = '\0'; + strftime(buff, size, szDateFormat, &tmTime); + + return buff; +} + +int getCharLen(const char *s) +{ + unsigned char *p; + int count = 0; + + p = (unsigned char *)s; + while (*p != '\0') + { + if (*p > 127) + { + if (*(++p) != '\0') + { + p++; + } + } + else + { + p++; + } + + count++; + } + + return count; +} + +char *replaceCRLF2Space(char *s) +{ + char *p = s; + + while (*p != '\0') + { + if (*p == '\r' || *p == '\n') + { + *p = ' '; + } + + p++; + } + + return s; +} + +char *getAbsolutePath(const char *filename, char *szAbsPath, \ + const int pathSize) +{ + char *p; + int nPathLen; + char szPath[1024]; + char cwd[256]; + + p = strrchr(filename, '/'); + if (p == NULL) + { + szPath[0] = '\0'; + } + else + { + nPathLen = p - filename; + if (nPathLen >= sizeof(szPath)) + { + logError("file: "__FILE__", line: %d, " \ + "filename length: %d is too long, exceeds %d",\ + __LINE__, nPathLen, (int)sizeof(szPath)); + return NULL; + } + + memcpy(szPath, filename, nPathLen); + szPath[nPathLen] = '\0'; + } + + if (szPath[0] == '/') + { + snprintf(szAbsPath, pathSize, "%s", szPath); + } + else + { + if (getcwd(cwd, sizeof(cwd)) == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "call getcwd fail, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return NULL; + } + + nPathLen = strlen(cwd); + if (cwd[nPathLen - 1] == '/') + { + cwd[nPathLen - 1] = '\0'; + } + + if (szPath[0] != '\0') + { + snprintf(szAbsPath, pathSize, "%s/%s", cwd, szPath); + } + else + { + snprintf(szAbsPath, pathSize, "%s", cwd); + } + } + + return szAbsPath; +} + +char *getExeAbsoluteFilename(const char *exeFilename, char *szAbsFilename, \ + const int maxSize) +{ + const char *filename; + const char *p; + int nFileLen; + int nPathLen; + char cwd[256]; + char szPath[1024]; + + nFileLen = strlen(exeFilename); + if (nFileLen >= sizeof(szPath)) + { + logError("file: "__FILE__", line: %d, " \ + "filename length: %d is too long, exceeds %d!", \ + __LINE__, nFileLen, (int)sizeof(szPath)); + return NULL; + } + + p = strrchr(exeFilename, '/'); + if (p == NULL) + { + int i; + char *search_paths[] = {"/bin", "/usr/bin", "/usr/local/bin"}; + + *szPath = '\0'; + filename = exeFilename; + for (i=0; i<3; i++) + { + snprintf(cwd, sizeof(cwd), "%s/%s", \ + search_paths[i], filename); + if (fileExists(cwd)) + { + strcpy(szPath, search_paths[i]); + break; + } + } + + if (*szPath == '\0') + { + if (!fileExists(filename)) + { + logError("file: "__FILE__", line: %d, " \ + "can't find exe file %s!", __LINE__, \ + filename); + return NULL; + } + } + else + { + snprintf(szAbsFilename, maxSize, "%s/%s", \ + szPath, filename); + return szAbsFilename; + } + } + else + { + filename = p + 1; + nPathLen = p - exeFilename; + memcpy(szPath, exeFilename, nPathLen); + szPath[nPathLen] = '\0'; + } + + if (*szPath == '/') + { + snprintf(szAbsFilename, maxSize, "%s/%s", szPath, filename); + } + else + { + if (getcwd(cwd, sizeof(cwd)) == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "call getcwd fail, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return NULL; + } + + nPathLen = strlen(cwd); + if (cwd[nPathLen - 1] == '/') + { + cwd[nPathLen - 1] = '\0'; + } + + if (*szPath != '\0') + { + snprintf(szAbsFilename, maxSize, "%s/%s/%s", \ + cwd, szPath, filename); + } + else + { + snprintf(szAbsFilename, maxSize, "%s/%s", \ + cwd, filename); + } + } + + return szAbsFilename; +} + +#ifndef WIN32 +int getProccessCount(const char *progName, const bool bAllOwners) +{ + int *pids = NULL; + return getUserProcIds(progName, bAllOwners, pids, 0); +} + +int getUserProcIds(const char *progName, const bool bAllOwners, \ + int pids[], const int arrSize) +{ + char path[128]="/proc"; + char fullpath[128]; + struct stat statbuf; + struct dirent *dirp; + DIR *dp; + int myuid=getuid(); + int fd; + char filepath[128]; + char buf[256]; + char *ptr; + int nbytes; + char procname[64]; + int cnt=0; + char *pTargetProg; + + if ((dp = opendir(path)) == NULL) + { + return -1; + } + + pTargetProg = (char *)malloc(strlen(progName) + 1); + if (pTargetProg == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail", __LINE__, \ + (int)strlen(progName) + 1); + return -1; + } + + ptr = strrchr(progName, '/'); + if (ptr == NULL) + { + strcpy(pTargetProg, progName); + } + else + { + strcpy(pTargetProg, ptr + 1); + } + + while( (dirp = readdir(dp)) != NULL ) + { + if (strcmp(dirp->d_name, ".")==0 || strcmp(dirp->d_name, "..")==0 ) + { + continue; + } + + sprintf(fullpath, "%s/%s", path, dirp->d_name); + memset(&statbuf, 0, sizeof(statbuf)); + if (lstat(fullpath, &statbuf)<0) + { + continue; + } + + if ((bAllOwners || (statbuf.st_uid == myuid)) && S_ISDIR(statbuf.st_mode)) + { + sprintf(filepath, "%s/cmdline", fullpath); + if ((fd = open(filepath, O_RDONLY))<0) + { + continue; + } + + memset(buf, 0, 256); + if ((nbytes = read(fd, buf, 255)) < 0){ + close(fd); + continue; + } + close(fd); + + if (*buf == '\0') + { + continue; + } + + ptr = strrchr(buf, '/'); + if (ptr == NULL) + { + snprintf(procname, 64, "%s", buf); + } + else + { + snprintf(procname, 64, "%s", ptr + 1); + } + + if (strcmp(procname, pTargetProg) == 0) + { + if (pids != NULL) + { + if (cnt >= arrSize) + { + break; + } + pids[cnt] = atoi(dirp->d_name); + } + + cnt++; + } + } + } + free(pTargetProg); + + closedir(dp); + return cnt; +} + +int getExecResult(const char *command, char *output, const int buff_size) +{ + FILE *fp; + char *pCurrent; + int bytes_read; + int remain_bytes; + + if((fp=popen(command, "r")) == NULL) + { + return errno != 0 ? errno : EMFILE; + } + + pCurrent = output; + remain_bytes = buff_size; + while (remain_bytes > 0 && \ + (bytes_read=fread(pCurrent, 1, remain_bytes, fp)) > 0) + { + pCurrent += bytes_read; + remain_bytes -= bytes_read; + } + + pclose(fp); + + if (remain_bytes <= 0) + { + return ENOSPC; + } + + *pCurrent = '\0'; + return 0; +} + +#endif + +char *toLowercase(char *src) +{ + char *p; + + p = src; + while (*p != '\0') + { + if (*p >= 'A' && *p <= 'Z') + { + *p += 32; + } + p++; + } + + return src; +} + +char *toUppercase(char *src) +{ + char *p; + + p = src; + while (*p != '\0') + { + if (*p >= 'a' && *p <= 'z') + { + *p -= 32; + } + p++; + } + + return src; +} + +void daemon_init(bool bCloseFiles) +{ +#ifndef WIN32 + pid_t pid; + int i; + + if((pid=fork()) != 0) + { + exit(0); + } + + setsid(); + + if((pid=fork()) != 0) + { + exit(0); + } + +#ifdef DEBUG_FLAG + #define MAX_CORE_FILE_SIZE (256 * 1024 * 1024) + if (set_rlimit(RLIMIT_CORE, MAX_CORE_FILE_SIZE) != 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "set max core dump file size to %d MB fail, " \ + "errno: %d, error info: %s", \ + __LINE__, MAX_CORE_FILE_SIZE / (1024 * 1024), \ + errno, STRERROR(errno)); + } +#else + if (chdir("/") != 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "change directory to / fail, " \ + "errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + } +#endif + + if (bCloseFiles) + { + for(i=0; i<=2; i++) + { + close(i); + } + } +#endif + + return; +} + +char *bin2hex(const char *s, const int len, char *szHexBuff) +{ + unsigned char *p; + unsigned char *pEnd; + int nLen; + + nLen = 0; + pEnd = (unsigned char *)s + len; + for (p=(unsigned char *)s; p=pStr; p--) + { + if (!(' ' == *p || '\n' == *p || '\r' == *p || '\t' == *p)) + { + break; + } + } + + if (p != pEnd) + { + *(p+1) = '\0'; + } + + return pStr; +} + +char *trim(char *pStr) +{ + trim_right(pStr); + trim_left(pStr); + return pStr; +} + +char *formatDateYYYYMMDDHHMISS(const time_t t, char *szDateBuff, const int nSize) +{ + time_t timer = t; + struct tm tm; + + localtime_r(&timer, &tm); + + snprintf(szDateBuff, nSize, "%04d%02d%02d%02d%02d%02d", \ + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \ + tm.tm_hour, tm.tm_min, tm.tm_sec); + + return szDateBuff; +} + +int getOccurCount(const char *src, const char seperator) +{ + int count; + char *p; + + count = 0; + p = strchr(src, seperator); + while (p != NULL) + { + count++; + p = strchr(p + 1, seperator); + } + + return count; +} + +char **split(char *src, const char seperator, const int nMaxCols, int *nColCount) +{ + char **pCols; + char **pCurrent; + char *p; + int i; + int nLastIndex; + + if (src == NULL) + { + *nColCount = 0; + return NULL; + } + + *nColCount = 1; + p = strchr(src, seperator); + + while (p != NULL) + { + (*nColCount)++; + p = strchr(p + 1, seperator); + } + + if (nMaxCols > 0 && (*nColCount) > nMaxCols) + { + *nColCount = nMaxCols; + } + + pCurrent = pCols = (char **)malloc(sizeof(char *) * (*nColCount)); + if (pCols == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail", __LINE__, \ + (int)sizeof(char *) * (*nColCount)); + return NULL; + } + + p = src; + nLastIndex = *nColCount - 1; + for (i=0; i<*nColCount; i++) + { + *pCurrent = p; + pCurrent++; + + p = strchr(p, seperator); + if (i != nLastIndex) + { + *p = '\0'; + p++; + } + } + + return pCols; +} + +void freeSplit(char **p) +{ + if (p != NULL) + { + free(p); + } +} + +int splitEx(char *src, const char seperator, char **pCols, const int nMaxCols) +{ + char *p; + char **pCurrent; + int count = 0; + + if (nMaxCols <= 0) + { + return 0; + } + + p = src; + pCurrent = pCols; + + while (true) + { + *pCurrent = p; + pCurrent++; + + count++; + if (count >= nMaxCols) + { + break; + } + + p = strchr(p, seperator); + if (p == NULL) + { + break; + } + + *p = '\0'; + p++; + } + + return count; +} + +int my_strtok(char *src, const char *delim, char **pCols, const int nMaxCols) +{ + char *p; + char **pCurrent; + int count; + bool bWordEnd; + + if (src == NULL || pCols == NULL) + { + return -1; + } + + if (nMaxCols <= 0) + { + return 0; + } + + p = src; + pCurrent = pCols; + + while (*p != '\0') + { + if (strchr(delim, *p) == NULL) + { + break; + } + p++; + } + + if (*p == '\0') + { + return 0; + } + + *pCurrent = p; + bWordEnd = false; + count = 1; + if (count >= nMaxCols) + { + return count; + } + + while (*p != '\0') + { + if (strchr(delim, *p) != NULL) + { + *p = '\0'; + bWordEnd = true; + } + else + { + if (bWordEnd) + { + pCurrent++; + *pCurrent = p; + + count++; + if (count >= nMaxCols) + { + break; + } + + bWordEnd = false; + } + } + + p++; + } + + return count; +} + +int str_replace(const char *s, const int src_len, const char *replaced, + const char *new_str, char *dest, const int dest_size) +{ + const char *pStart; + const char *pEnd; + char *pDest; + const char *p; + int old_len; + int new_len; + int len; + int max_dest_len; + int remain_len; + + if (dest_size <= 0) + { + return 0; + } + + max_dest_len = dest_size - 1; + old_len = strlen(replaced); + new_len = strlen(new_str); + if (old_len == 0) + { + len = src_len < max_dest_len ? src_len : max_dest_len; + memcpy(dest, s, len); + dest[len] = '\0'; + return len; + } + + remain_len = max_dest_len; + pDest = dest; + pStart = s; + pEnd = s + src_len; + while (1) + { + p = strstr(pStart, replaced); + if (p == NULL) + { + break; + } + + len = p - pStart; + if (len > 0) + { + if (len < remain_len) + { + memcpy(pDest, pStart, len); + pDest += len; + remain_len -= len; + } + else + { + memcpy(pDest, pStart, remain_len); + pDest += remain_len; + *pDest = '\0'; + return pDest - dest; + } + } + + if (new_len < remain_len) + { + memcpy(pDest, new_str, new_len); + pDest += new_len; + remain_len -= new_len; + } + else + { + memcpy(pDest, new_str, remain_len); + pDest += remain_len; + *pDest = '\0'; + return pDest - dest; + } + + pStart = p + old_len; + } + + len = pEnd - pStart; + if (len > 0) + { + if (len > remain_len) + { + len = remain_len; + } + memcpy(pDest, pStart, len); + pDest += len; + } + *pDest = '\0'; + return pDest - dest; +} + +bool fileExists(const char *filename) +{ + return access(filename, 0) == 0; +} + +bool isDir(const char *filename) +{ + struct stat buf; + if (stat(filename, &buf) != 0) + { + return false; + } + + return S_ISDIR(buf.st_mode); +} + +bool isFile(const char *filename) +{ + struct stat buf; + if (stat(filename, &buf) != 0) + { + return false; + } + + return S_ISREG(buf.st_mode); +} + +void chopPath(char *filePath) +{ + int lastIndex; + if (*filePath == '\0') + { + return; + } + + lastIndex = strlen(filePath) - 1; + if (filePath[lastIndex] == '/') + { + filePath[lastIndex] = '\0'; + } +} + +int getFileContent(const char *filename, char **buff, int64_t *file_size) +{ + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + { + *buff = NULL; + *file_size = 0; + logError("file: "__FILE__", line: %d, " \ + "open file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : ENOENT; + } + + if ((*file_size=lseek(fd, 0, SEEK_END)) < 0) + { + *buff = NULL; + *file_size = 0; + close(fd); + logError("file: "__FILE__", line: %d, " \ + "lseek file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : EIO; + } + + *buff = (char *)malloc(*file_size + 1); + if (*buff == NULL) + { + *file_size = 0; + close(fd); + + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail", __LINE__, \ + (int)(*file_size + 1)); + return errno != 0 ? errno : ENOMEM; + } + + if (lseek(fd, 0, SEEK_SET) < 0) + { + *buff = NULL; + *file_size = 0; + close(fd); + logError("file: "__FILE__", line: %d, " \ + "lseek file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : EIO; + } + if (read(fd, *buff, *file_size) != *file_size) + { + free(*buff); + *buff = NULL; + *file_size = 0; + close(fd); + logError("file: "__FILE__", line: %d, " \ + "read from file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : EIO; + } + + (*buff)[*file_size] = '\0'; + close(fd); + + return 0; +} + +int getFileContentEx(const char *filename, char *buff, \ + int64_t offset, int64_t *size) +{ + int fd; + int read_bytes; + + if (*size <= 0) + { + logError("file: "__FILE__", line: %d, " \ + "invalid size: "INT64_PRINTF_FORMAT, \ + __LINE__, *size); + return EINVAL; + } + + fd = open(filename, O_RDONLY); + if (fd < 0) + { + *size = 0; + logError("file: "__FILE__", line: %d, " \ + "open file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : ENOENT; + } + + if (offset > 0 && lseek(fd, offset, SEEK_SET) < 0) + { + *size = 0; + close(fd); + logError("file: "__FILE__", line: %d, " \ + "lseek file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : EIO; + } + + if ((read_bytes=read(fd, buff, *size)) < 0) + { + *size = 0; + close(fd); + logError("file: "__FILE__", line: %d, " \ + "read from file %s fail, " \ + "errno: %d, error info: %s", __LINE__, \ + filename, errno, STRERROR(errno)); + return errno != 0 ? errno : EIO; + } + + *size = read_bytes; + *(buff + (*size)) = '\0'; + close(fd); + + return 0; +} + +int writeToFile(const char *filename, const char *buff, const int file_size) +{ + int fd; + int result; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + result = errno != 0 ? errno : EIO; + logError("file: "__FILE__", line: %d, " \ + "open file %s fail, " \ + "errno: %d, error info: %s", \ + __LINE__, filename, \ + result, STRERROR(result)); + return result; + } + + if (write(fd, buff, file_size) != file_size) + { + result = errno != 0 ? errno : EIO; + logError("file: "__FILE__", line: %d, " \ + "write file %s fail, " \ + "errno: %d, error info: %s", \ + __LINE__, filename, \ + result, STRERROR(result)); + close(fd); + return result; + } + + if (fsync(fd) != 0) + { + result = errno != 0 ? errno : EIO; + logError("file: "__FILE__", line: %d, " \ + "fsync file \"%s\" fail, " \ + "errno: %d, error info: %s", \ + __LINE__, filename, \ + result, STRERROR(result)); + close(fd); + return result; + } + + close(fd); + return 0; +} + +int safeWriteToFile(const char *filename, const char *buff, \ + const int file_size) +{ + char tmpFilename[MAX_PATH_SIZE]; + int result; + + snprintf(tmpFilename, sizeof(tmpFilename), "%s.tmp", filename); + if ((result=writeToFile(tmpFilename, buff, file_size)) != 0) + { + return result; + } + + if (rename(tmpFilename, filename) != 0) + { + result = errno != 0 ? errno : EIO; + logError("file: "__FILE__", line: %d, " \ + "rename file \"%s\" to \"%s\" fail, " \ + "errno: %d, error info: %s", \ + __LINE__, tmpFilename, filename, \ + result, STRERROR(result)); + return result; + } + + return 0; +} + +void int2buff(const int n, char *buff) +{ + unsigned char *p; + p = (unsigned char *)buff; + *p++ = (n >> 24) & 0xFF; + *p++ = (n >> 16) & 0xFF; + *p++ = (n >> 8) & 0xFF; + *p++ = n & 0xFF; +} + +int buff2int(const char *buff) +{ + return (((unsigned char)(*buff)) << 24) | \ + (((unsigned char)(*(buff+1))) << 16) | \ + (((unsigned char)(*(buff+2))) << 8) | \ + ((unsigned char)(*(buff+3))); +} + +void long2buff(int64_t n, char *buff) +{ + unsigned char *p; + p = (unsigned char *)buff; + *p++ = (n >> 56) & 0xFF; + *p++ = (n >> 48) & 0xFF; + *p++ = (n >> 40) & 0xFF; + *p++ = (n >> 32) & 0xFF; + *p++ = (n >> 24) & 0xFF; + *p++ = (n >> 16) & 0xFF; + *p++ = (n >> 8) & 0xFF; + *p++ = n & 0xFF; +} + +int64_t buff2long(const char *buff) +{ + unsigned char *p; + p = (unsigned char *)buff; + return (((int64_t)(*p)) << 56) | \ + (((int64_t)(*(p+1))) << 48) | \ + (((int64_t)(*(p+2))) << 40) | \ + (((int64_t)(*(p+3))) << 32) | \ + (((int64_t)(*(p+4))) << 24) | \ + (((int64_t)(*(p+5))) << 16) | \ + (((int64_t)(*(p+6))) << 8) | \ + ((int64_t)(*(p+7))); +} + +int fd_gets(int fd, char *buff, const int size, int once_bytes) +{ + char *pDest; + char *p; + char *pEnd; + int read_bytes; + int remain_bytes; + int rewind_bytes; + + if (once_bytes <= 0) + { + once_bytes = 1; + } + + pDest = buff; + remain_bytes = size - 1; + while (remain_bytes > 0) + { + if (once_bytes > remain_bytes) + { + once_bytes = remain_bytes; + } + + read_bytes = read(fd, pDest, once_bytes); + if (read_bytes < 0) + { + return -1; + } + if (read_bytes == 0) + { + break; + } + + pEnd = pDest + read_bytes; + for (p=pDest; p= value)) + { + return 0; + } + + limit.rlim_cur = value; + if (setrlimit(resource, &limit) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call setrlimit fail, resource=%d, value=%d, " \ + "errno: %d, error info: %s", \ + __LINE__, resource, (int)value, \ + errno, STRERROR(errno)); + return errno != 0 ? errno : EPERM; + } + + return 0; +} + +bool is_filename_secure(const char *filename, const int len) +{ + if (len < 3) + { + return true; + } + + if (memcmp(filename, "../", 3) == 0) + { + return false; + } + + return (strstr(filename, "/../") == NULL); +} + +void load_log_level(IniContext *pIniContext) +{ + set_log_level(iniGetStrValue(NULL, "log_level", pIniContext)); +} + +int load_log_level_ex(const char *conf_filename) +{ + int result; + IniContext iniContext; + + if ((result=iniLoadFromFile(conf_filename, &iniContext)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "load conf file \"%s\" fail, ret code: %d", \ + __LINE__, conf_filename, result); + return result; + } + + load_log_level(&iniContext); + iniFreeContext(&iniContext); + return 0; +} + +void set_log_level(char *pLogLevel) +{ + if (pLogLevel != NULL) + { + toUppercase(pLogLevel); + if ( strncmp(pLogLevel, "DEBUG", 5) == 0 || \ + strcmp(pLogLevel, "LOG_DEBUG") == 0) + { + g_log_context.log_level = LOG_DEBUG; + } + else if ( strncmp(pLogLevel, "INFO", 4) == 0 || \ + strcmp(pLogLevel, "LOG_INFO") == 0) + { + g_log_context.log_level = LOG_INFO; + } + else if ( strncmp(pLogLevel, "NOTICE", 6) == 0 || \ + strcmp(pLogLevel, "LOG_NOTICE") == 0) + { + g_log_context.log_level = LOG_NOTICE; + } + else if ( strncmp(pLogLevel, "WARN", 4) == 0 || \ + strcmp(pLogLevel, "LOG_WARNING") == 0) + { + g_log_context.log_level = LOG_WARNING; + } + else if ( strncmp(pLogLevel, "ERR", 3) == 0 || \ + strcmp(pLogLevel, "LOG_ERR") == 0) + { + g_log_context.log_level = LOG_ERR; + } + else if ( strncmp(pLogLevel, "CRIT", 4) == 0 || \ + strcmp(pLogLevel, "LOG_CRIT") == 0) + { + g_log_context.log_level = LOG_CRIT; + } + else if ( strncmp(pLogLevel, "ALERT", 5) == 0 || \ + strcmp(pLogLevel, "LOG_ALERT") == 0) + { + g_log_context.log_level = LOG_ALERT; + } + else if ( strncmp(pLogLevel, "EMERG", 5) == 0 || \ + strcmp(pLogLevel, "LOG_EMERG") == 0) + { + g_log_context.log_level = LOG_EMERG; + } + } +} + +int fd_add_flags(int fd, int adding_flags) +{ + int flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + { + logError("file: "__FILE__", line: %d, " \ + "fcntl fail, errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EACCES; + } + + if (fcntl(fd, F_SETFL, flags | adding_flags) == -1) + { + logError("file: "__FILE__", line: %d, " \ + "fcntl fail, errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EACCES; + } + + return 0; +} + +int set_run_by(const char *group_name, const char *username) +{ +#ifndef WIN32 + struct group *pGroup; + struct passwd *pUser; + int nErrNo; + if (group_name != NULL && *group_name != '\0') + { + pGroup = getgrnam(group_name); + if (pGroup == NULL) + { + nErrNo = errno != 0 ? errno : ENOENT; + logError("file: "__FILE__", line: %d, " \ + "getgrnam fail, errno: %d, error info: %s.", \ + __LINE__, nErrNo, STRERROR(nErrNo)); + return nErrNo; + } + + if (setegid(pGroup->gr_gid) != 0) + { + nErrNo = errno != 0 ? errno : EPERM; + logError("file: "__FILE__", line: %d, " \ + "setegid fail, errno: %d, error info: %s.", \ + __LINE__, nErrNo, STRERROR(nErrNo)); + return nErrNo; + } + } + + if (username != NULL && *username != '\0') + { + pUser = getpwnam(username); + if (pUser == NULL) + { + nErrNo = errno != 0 ? errno : ENOENT; + logError("file: "__FILE__", line: %d, " \ + "getpwnam fail, errno: %d, error info: %s.", \ + __LINE__, nErrNo, STRERROR(nErrNo)); + return nErrNo; + } + + if (seteuid(pUser->pw_uid) != 0) + { + nErrNo = errno != 0 ? errno : EPERM; + logError("file: "__FILE__", line: %d, " \ + "seteuid fail, errno: %d, error info: %s.", \ + __LINE__, nErrNo, STRERROR(nErrNo)); + return nErrNo; + } + } +#endif + + return 0; +} + +int load_allow_hosts(IniContext *pIniContext, \ + in_addr_t **allow_ip_addrs, int *allow_ip_count) +{ + int count; + IniItem *pItem; + IniItem *pItemStart; + IniItem *pItemEnd; + char *pItemValue; + char *pStart; + char *pEnd; + char *p; + char *pTail; + int alloc_count; + int nHeadLen; + int i; + in_addr_t addr; + char hostname[256]; + + if ((pItemStart=iniGetValuesEx(NULL, "allow_hosts", \ + pIniContext, &count)) == NULL) + { + *allow_ip_count = -1; /* -1 means match any ip address */ + *allow_ip_addrs = NULL; + return 0; + } + + pItemEnd = pItemStart + count; + for (pItem=pItemStart; pItemvalue, "*") == 0) + { + *allow_ip_count = -1; /* -1 means match any ip address*/ + *allow_ip_addrs = NULL; + return 0; + } + } + + alloc_count = count; + *allow_ip_count = 0; + *allow_ip_addrs = (in_addr_t *)malloc(sizeof(in_addr_t) * alloc_count); + if (*allow_ip_addrs == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, errno: %d, error info: %s.", \ + __LINE__, (int)sizeof(in_addr_t) * alloc_count, \ + errno, STRERROR(errno)); + return errno != 0 ? errno : ENOMEM; + } + + for (pItem=pItemStart; pItemvalue) == '\0') + { + continue; + } + + pStart = strchr(pItem->value, '['); + if (pStart == NULL) + { + addr = getIpaddrByName(pItem->value, NULL, 0); + if (addr == INADDR_NONE) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid host name: %s", \ + __LINE__, pItem->value); + } + else + { + if (alloc_count < (*allow_ip_count) + 1) + { + alloc_count = (*allow_ip_count) + \ + (pItemEnd - pItem); + *allow_ip_addrs = (in_addr_t *)realloc( + *allow_ip_addrs, + sizeof(in_addr_t)*alloc_count); + if (*allow_ip_addrs == NULL) + { + logError("file: "__FILE__", line: %d, "\ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, (int)sizeof(in_addr_t) + * alloc_count, \ + errno, STRERROR(errno)); + + return errno != 0 ? errno : ENOMEM; + } + } + + (*allow_ip_addrs)[*allow_ip_count] = addr; + (*allow_ip_count)++; + } + + continue; + } + + + pEnd = strchr(pStart, ']'); + if (pEnd == NULL) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid host name: %s, expect \"]\"", \ + __LINE__, pItem->value); + continue; + } + + pItemValue = strdup(pItem->value); + if (pItemValue == NULL) + { + logWarning("file: "__FILE__", line: %d, " \ + "strdup fail, " \ + "errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + continue; + } + + nHeadLen = pStart - pItem->value; + pStart = pItemValue + nHeadLen; + pEnd = pItemValue + (pEnd - pItem->value); + pTail = pEnd + 1; + + memcpy(hostname, pItem->value, nHeadLen); + p = pStart + 1; //skip [ + + while (p <= pEnd) + { + char *pNumStart1; + char *pNumStart2; + int nStart; + int nEnd; + int nNumLen1; + int nNumLen2; + char end_ch1; + char end_ch2; + char szFormat[16]; + + while (*p == ' ' || *p == '\t') //trim prior spaces + { + p++; + } + + pNumStart1 = p; + while (*p >='0' && *p <= '9') + { + p++; + } + + nNumLen1 = p - pNumStart1; + while (*p == ' ' || *p == '\t') //trim tail spaces + { + p++; + } + + if (!(*p == ',' || *p == '-' || *p == ']')) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid char \"%c\" in host name: %s",\ + __LINE__, *p, pItem->value); + break; + } + + end_ch1 = *p; + *(pNumStart1 + nNumLen1) = '\0'; + + if (nNumLen1 == 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid host name: %s, " \ + "empty entry before \"%c\"", \ + __LINE__, pItem->value, end_ch1); + break; + } + + nStart = atoi(pNumStart1); + if (end_ch1 == '-') + { + p++; //skip - + + /* trim prior spaces */ + while (*p == ' ' || *p == '\t') + { + p++; + } + + pNumStart2 = p; + while (*p >='0' && *p <= '9') + { + p++; + } + + nNumLen2 = p - pNumStart2; + /* trim tail spaces */ + while (*p == ' ' || *p == '\t') + { + p++; + } + + if (!(*p == ',' || *p == ']')) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid char \"%c\" in host name: %s",\ + __LINE__, *p, pItem->value); + break; + } + + end_ch2 = *p; + *(pNumStart2 + nNumLen2) = '\0'; + + if (nNumLen2 == 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid host name: %s, " \ + "empty entry before \"%c\"", \ + __LINE__, pItem->value, end_ch2); + break; + } + + nEnd = atoi(pNumStart2); + } + else + { + nEnd = nStart; + } + + if (alloc_count < *allow_ip_count+(nEnd - nStart + 1)) + { + alloc_count = *allow_ip_count + (nEnd - nStart) + + (pItemEnd - pItem); + *allow_ip_addrs = (in_addr_t *)realloc( + *allow_ip_addrs, + sizeof(in_addr_t)*alloc_count); + if (*allow_ip_addrs == NULL) + { + logError("file: "__FILE__", line: %d, "\ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s.", \ + __LINE__, \ + (int)sizeof(in_addr_t) * \ + alloc_count,\ + errno, STRERROR(errno)); + + free(pItemValue); + return errno != 0 ? errno : ENOMEM; + } + } + + sprintf(szFormat, "%%0%dd%%s", nNumLen1); + for (i=nStart; i<=nEnd; i++) + { + sprintf(hostname + nHeadLen, szFormat, \ + i, pTail); + + addr = getIpaddrByName(hostname, NULL, 0); + if (addr == INADDR_NONE) + { + logWarning("file: "__FILE__", line: %d, " \ + "invalid host name: %s", \ + __LINE__, hostname); + } + else + { + (*allow_ip_addrs)[*allow_ip_count]=addr; + (*allow_ip_count)++; + } + + } + + p++; + } + + free(pItemValue); + } + + if (*allow_ip_count == 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "allow ip count: 0", __LINE__); + } + + if (*allow_ip_count > 0) + { + qsort(*allow_ip_addrs, *allow_ip_count, sizeof(in_addr_t), \ + cmp_by_ip_addr_t); + } + + /* + printf("*allow_ip_count=%d\n", *allow_ip_count); + for (i=0; i<*allow_ip_count; i++) + { + struct in_addr address; + address.s_addr = (*allow_ip_addrs)[i]; + printf("%s\n", inet_ntoa(address)); + } + */ + + return 0; +} + +int cmp_by_ip_addr_t(const void *p1, const void *p2) +{ + return memcmp((in_addr_t *)p1, (in_addr_t *)p2, sizeof(in_addr_t)); +} + +int parse_bytes(char *pStr, const int default_unit_bytes, int64_t *bytes) +{ + char *pReservedEnd; + + pReservedEnd = NULL; + *bytes = strtol(pStr, &pReservedEnd, 10); + if (*bytes < 0) + { + logError("file: "__FILE__", line: %d, " \ + "bytes: "INT64_PRINTF_FORMAT" < 0", \ + __LINE__, *bytes); + return EINVAL; + } + + if (pReservedEnd == NULL || *pReservedEnd == '\0') + { + *bytes *= default_unit_bytes; + } + else if (*pReservedEnd == 'G' || *pReservedEnd == 'g') + { + *bytes *= 1024 * 1024 * 1024; + } + else if (*pReservedEnd == 'M' || *pReservedEnd == 'm') + { + *bytes *= 1024 * 1024; + } + else if (*pReservedEnd == 'K' || *pReservedEnd == 'k') + { + *bytes *= 1024; + } + + return 0; +} + +int set_rand_seed() +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call gettimeofday fail, " \ + "errno=%d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EPERM; + } + + srand(tv.tv_sec ^ tv.tv_usec); + return 0; +} + +int get_time_item_from_conf(IniContext *pIniContext, \ + const char *item_name, TimeInfo *pTimeInfo, \ + const byte default_hour, const byte default_minute) +{ + char *pValue; + int hour; + int minute; + + pValue = iniGetStrValue(NULL, item_name, pIniContext); + if (pValue == NULL) + { + pTimeInfo->hour = default_hour; + pTimeInfo->minute = default_minute; + return 0; + } + + if (sscanf(pValue, "%d:%d", &hour, &minute) != 2) + { + logError("file: "__FILE__", line: %d, " \ + "item \"%s\" 's value \"%s\" is not an valid time", \ + __LINE__, item_name, pValue); + return EINVAL; + } + + if ((hour < 0 || hour > 23) || (minute < 0 || minute > 59)) + { + logError("file: "__FILE__", line: %d, " \ + "item \"%s\" 's value \"%s\" is not an valid time", \ + __LINE__, item_name, pValue); + return EINVAL; + } + + pTimeInfo->hour = (byte)hour; + pTimeInfo->minute = (byte)minute; + + return 0; +} + +char *urlencode(const char *src, const int src_len, char *dest, int *dest_len) +{ + static unsigned char hex_chars[] = "0123456789ABCDEF"; + const unsigned char *pSrc; + const unsigned char *pEnd; + char *pDest; + + pDest = dest; + pEnd = (unsigned char *)src + src_len; + for (pSrc=(unsigned char *)src; pSrc= '0' && *pSrc <= '9') || + (*pSrc >= 'a' && *pSrc <= 'z') || + (*pSrc >= 'A' && *pSrc <= 'Z') || + (*pSrc == '_' || *pSrc == '-' || *pSrc == '.')) + { + *pDest++ = *pSrc; + } + else if (*pSrc == ' ') + { + *pDest++ = '+'; + } + else + { + *pDest++ = '%'; + *pDest++ = hex_chars[(*pSrc) >> 4]; + *pDest++ = hex_chars[(*pSrc) & 0x0F]; + } + } + + *pDest = '\0'; + *dest_len = pDest - dest; + + return dest; +} + +char *urldecode(const char *src, const int src_len, char *dest, int *dest_len) +{ +#define IS_HEX_CHAR(ch) \ + ((ch >= '0' && ch <= '9') || \ + (ch >= 'a' && ch <= 'f') || \ + (ch >= 'A' && ch <= 'F')) + +#define HEX_VALUE(ch, value) \ + if (ch >= '0' && ch <= '9') \ + { \ + value = ch - '0'; \ + } \ + else if (ch >= 'a' && ch <= 'f') \ + { \ + value = ch - 'a' + 10; \ + } \ + else \ + { \ + value = ch - 'A' + 10; \ + } + + const unsigned char *pSrc; + const unsigned char *pEnd; + char *pDest; + unsigned char cHigh; + unsigned char cLow; + int valHigh; + int valLow; + + pDest = dest; + pSrc = (unsigned char *)src; + pEnd = (unsigned char *)src + src_len; + while (pSrc < pEnd) + { + if (*pSrc == '%' && pSrc + 2 < pEnd) + { + cHigh = *(pSrc + 1); + cLow = *(pSrc + 2); + + if (IS_HEX_CHAR(cHigh) && IS_HEX_CHAR(cLow)) + { + HEX_VALUE(cHigh, valHigh) + HEX_VALUE(cLow, valLow) + *pDest++ = (valHigh << 4) | valLow; + pSrc += 3; + } + else + { + *pDest++ = *pSrc; + pSrc++; + } + } + else if (*pSrc == '+') + { + *pDest++ = ' '; + pSrc++; + } + else + { + *pDest++ = *pSrc; + pSrc++; + } + } + + *pDest = '\0'; + *dest_len = pDest - dest; + + return dest; +} + +int buffer_strcpy(BufferInfo *pBuff, const char *str) +{ + pBuff->length = strlen(str); + if (pBuff->alloc_size <= pBuff->length) + { + if (pBuff->buff != NULL) + { + free(pBuff->buff); + } + + pBuff->alloc_size = pBuff->length + 1; + pBuff->buff = (char *)malloc(pBuff->alloc_size); + if (pBuff->buff == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, pBuff->alloc_size, \ + errno, STRERROR(errno)); + pBuff->alloc_size = 0; + return errno != 0 ? errno : ENOMEM; + } + } + + memcpy(pBuff->buff, str, pBuff->length + 1); + return 0; +} + +int buffer_memcpy(BufferInfo *pBuff, const char *buff, const int len) +{ + pBuff->length = len; + if (pBuff->alloc_size <= pBuff->length) + { + if (pBuff->buff != NULL) + { + free(pBuff->buff); + } + + pBuff->alloc_size = pBuff->length; + pBuff->buff = (char *)malloc(pBuff->alloc_size); + if (pBuff->buff == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "malloc %d bytes fail, " \ + "errno: %d, error info: %s", \ + __LINE__, pBuff->alloc_size, \ + errno, STRERROR(errno)); + pBuff->alloc_size = 0; + return errno != 0 ? errno : ENOMEM; + } + } + + memcpy(pBuff->buff, buff, pBuff->length); + return 0; +} + +int set_timer(const int first_remain_seconds, const int interval, \ + void (*sighandler)(int)) +{ + struct itimerval value; + struct sigaction act; + + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = sighandler; + if(sigaction(SIGALRM, &act, NULL) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "call sigaction fail, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + memset(&value, 0, sizeof(value)); + value.it_interval.tv_sec = interval; + value.it_value.tv_sec = first_remain_seconds; + if (setitimer(ITIMER_REAL, &value, NULL) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "call setitimer fail, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + return 0; +} + +int set_file_utimes(const char *filename, const time_t new_time) +{ + struct timeval tvs[2]; + + tvs[0].tv_sec = new_time; + tvs[0].tv_usec = 0; + tvs[1].tv_sec = new_time; + tvs[1].tv_usec = 0; + if (utimes(filename, tvs) != 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "call utimes file: %s fail" \ + ", errno: %d, error info: %s", \ + __LINE__, filename, errno, STRERROR(errno)); + return errno != 0 ? errno : ENOENT; + } + + return 0; +} + +int ignore_signal_pipe() +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + if(sigaction(SIGPIPE, &act, NULL) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "call sigaction fail, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno; + } + + return 0; +} + diff --git a/src/shared_func.h b/src/shared_func.h new file mode 100644 index 0000000..971c0ad --- /dev/null +++ b/src/shared_func.h @@ -0,0 +1,495 @@ +/** +* 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.csource.org/ for more detail. +**/ + +#ifndef SHARED_FUNC_H +#define SHARED_FUNC_H + +#include +#include +#include +#include +#include +#include "common_define.h" +#include "ini_file_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** lowercase the string + * parameters: + * src: input string, will be changed + * return: lowercased string +*/ +char *toLowercase(char *src); + +/** uppercase the string + * parameters: + * src: input string, will be changed + * return: uppercased string +*/ +char *toUppercase(char *src); + + +/** date format to string + * parameters: + * nTime: unix timestamp + * szDateFormat: date format, more detail man strftime + * buff: store the formated result, can be NULL + * buff_size: buffer size, max bytes can contain + * return: formated date string +*/ +char *formatDatetime(const time_t nTime, \ + const char *szDateFormat, \ + char *buff, const int buff_size); + +/** get character count, only support GB charset + * parameters: + * s: the string + * return: character count +*/ +int getCharLen(const char *s); + +/** replace \r and \n to space + * parameters: + * s: the string + * return: replaced string +*/ +char *replaceCRLF2Space(char *s); + +/** get the filename absolute path + * parameters: + * fileame: the filename + * szAbsPath: store the absolute path + * pathSize: max bytes to contain + * return: absolute path, NULL for fail +*/ +char *getAbsolutePath(const char *fileame, char *szAbsPath, \ + const int pathSize); + +/** get the executable file absolute filename + * parameters: + * exeFilename: the executable filename + * szAbsFilename: store the absolute filename + * maxSize: max bytes to contain + * return: absolute filename, NULL for fail +*/ +char *getExeAbsoluteFilename(const char *exeFilename, char *szAbsFilename, \ + const int maxSize); + +#ifndef WIN32 + +/** get running process count by program name such as fdfs_trackerd + * parameters: + * progName: the program name + * bAllOwners: false for only get my proccess count + * return: proccess count, >= 0 success, < 0 fail +*/ +int getProccessCount(const char *progName, const bool bAllOwners); + +/** get running process ids by program name such as fdfs_trackerd + * parameters: + * progName: the program name + * bAllOwners: false for only get my proccess count + * pids: store the pids + * arrSize: max pids + * return: proccess count, >= 0 success, < 0 fail +*/ +int getUserProcIds(const char *progName, const bool bAllOwners, \ + int pids[], const int arrSize); + +/** execute program, get it's output + * parameters: + * command: the program + * output: store ouput result + * buff_size: output max size (bytes) + * return: error no, 0 success, != 0 fail +*/ +int getExecResult(const char *command, char *output, const int buff_size); + +#endif + +/** daemon init + * parameters: + * bCloseFiles: if close the stdin, stdout and stderr + * return: none +*/ +void daemon_init(bool bCloseFiles); + +/** convert buffer content to hex string such as 0B82A1 + * parameters: + * s: the buffer + * len: the buffer length + * szHexBuff: store the hex string (must have enough space) + * return: hex string (szHexBuff) +*/ +char *bin2hex(const char *s, const int len, char *szHexBuff); + +/** parse hex string to binary content + * parameters: + * s: the hex string such as 8B04CD + * szBinBuff: store the converted binary content(must have enough space) + * nDestLen: store the converted content length + * return: converted binary content (szBinBuff) +*/ +char *hex2bin(const char *s, char *szBinBuff, int *nDestLen); + +/** print binary buffer as hex string + * parameters: + * s: the buffer + * len: the buffer length + * return: none +*/ +void printBuffHex(const char *s, const int len); + +/** 32 bits int convert to buffer (big-endian) + * parameters: + * n: 32 bits int value + * buff: the buffer, at least 4 bytes space, no tail \0 + * return: none +*/ +void int2buff(const int n, char *buff); + +/** buffer convert to 32 bits int + * parameters: + * buff: big-endian 4 bytes buffer + * return: 32 bits int value +*/ +int buff2int(const char *buff); + +/** long (64 bits) convert to buffer (big-endian) + * parameters: + * n: 64 bits int value + * buff: the buffer, at least 8 bytes space, no tail \0 + * return: none +*/ +void long2buff(int64_t n, char *buff); + +/** buffer convert to 64 bits int + * parameters: + * buff: big-endian 8 bytes buffer + * return: 64 bits int value +*/ +int64_t buff2long(const char *buff); + +/** trim leading spaces ( \t\r\n) + * parameters: + * pStr: the string to trim + * return: trimed string porinter as pStr +*/ +char *trim_left(char *pStr); + +/** trim tail spaces ( \t\r\n) + * parameters: + * pStr: the string to trim + * return: trimed string porinter as pStr +*/ +char *trim_right(char *pStr); + +/** trim leading and tail spaces ( \t\r\n) + * parameters: + * pStr: the string to trim + * return: trimed string porinter as pStr +*/ +char *trim(char *pStr); + +/** copy string to BufferInfo + * parameters: + * pBuff: the dest buffer + * str: source string + * return: error no, 0 success, != 0 fail +*/ +int buffer_strcpy(BufferInfo *pBuff, const char *str); + +/** copy binary buffer to BufferInfo + * parameters: + * pBuff: the dest buffer + * buff: source buffer + * len: source buffer length + * return: error no, 0 success, != 0 fail +*/ +int buffer_memcpy(BufferInfo *pBuff, const char *buff, const int len); + +/** url encode + * parameters: + * src: the source string to encode + * src_len: source string length + * dest: store dest string + * dest_len: store the dest string length + * return: error no, 0 success, != 0 fail +*/ +char *urlencode(const char *src, const int src_len, char *dest, int *dest_len); + +/** url decode + * parameters: + * src: the source string to decode + * src_len: source string length + * dest: store dest string + * dest_len: store the dest string length + * return: error no, 0 success, != 0 fail +*/ +char *urldecode(const char *src, const int src_len, char *dest, int *dest_len); + +/** get char occurs count + * parameters: + * src: the source string + * seperator: find this char occurs times + * return: char occurs count +*/ +int getOccurCount(const char *src, const char seperator); + +/** split string + * parameters: + * src: the source string, will be modified by this function + * seperator: seperator char + * nMaxCols: max columns (max split count) + * nColCount: store the columns (array elements) count + * return: string array, should call freeSplit to free, return NULL when fail +*/ +char **split(char *src, const char seperator, const int nMaxCols, \ + int *nColCount); + +/** free split results + * parameters: + * p: return by function split + * return: none +*/ +void freeSplit(char **p); + + +/** split string + * parameters: + * src: the source string, will be modified by this function + * seperator: seperator char + * pCols: store split strings + * nMaxCols: max columns (max split count) + * return: string array / column count +*/ +int splitEx(char *src, const char seperator, char **pCols, const int nMaxCols); + +/** split string + * parameters: + * src: the source string, will be modified by this function + * seperator: seperator char + * pCols: store split strings + * nMaxCols: max columns (max split count) + * return: string array / column count +*/ +int my_strtok(char *src, const char *delim, char **pCols, const int nMaxCols); + +/** check file exist + * parameters: + * filename: the filename + * return: true if file exists, otherwise false +*/ +bool fileExists(const char *filename); + +/** check if a directory + * parameters: + * filename: the filename + * return: true for directory +*/ +bool isDir(const char *filename); + +/** check if a regular file + * parameters: + * filename: the filename + * return: true for regular file +*/ +bool isFile(const char *filename); + +/** check if filename securty, /../ ocur in filename not allowed + * parameters: + * filename: the filename + * len: filename length + * return: true for regular file +*/ +bool is_filename_secure(const char *filename, const int len); + +/** load log_level from config context + * parameters: + * pIniContext: the config context + * return: none +*/ +void load_log_level(IniContext *pIniContext); + +/** load log_level from config file + * parameters: + * conf_filename: the config filename + * return: none +*/ +int load_log_level_ex(const char *conf_filename); + +/** set global log level + * parameters: + * pLogLevel: log level string value + * return: none +*/ +void set_log_level(char *pLogLevel); + +/** load allow hosts from config context + * parameters: + * pIniContext: the config context + * allow_ip_addrs: store allow ip addresses + * allow_ip_count: store allow ip address count + * return: error no , 0 success, != 0 fail +*/ +int load_allow_hosts(IniContext *pIniContext, \ + in_addr_t **allow_ip_addrs, int *allow_ip_count); + +/** get time item from config context + * parameters: + * pIniContext: the config context + * item_name: item name in config file, time format: hour:minute, such as 15:25 + * pTimeInfo: store time info + * default_hour: default hour value + * default_minute: default minute value + * return: error no , 0 success, != 0 fail +*/ +int get_time_item_from_conf(IniContext *pIniContext, \ + const char *item_name, TimeInfo *pTimeInfo, \ + const byte default_hour, const byte default_minute); + +/** trim path tail char / + * parameters: + * filePath: the file path to chop + * return: none +*/ +void chopPath(char *filePath); + +/** get file content + * parameters: + * filename: the filename + * buff: return the buff, must be freed + * file_size: store the file size + * return: error no , 0 success, != 0 fail +*/ +int getFileContent(const char *filename, char **buff, int64_t *file_size); + +/** get file content + * parameters: + * filename: the filename + * buff: the buff to store file content + * offset: the start offset + * size: specify the size to fetch and return the fetched size + * return: error no , 0 success, != 0 fail +*/ +int getFileContentEx(const char *filename, char *buff, \ + int64_t offset, int64_t *size); + +/** write to file + * parameters: + * filename: the filename to write + * buff: the buffer to write + * file_size: the file size + * return: error no , 0 success, != 0 fail +*/ +int writeToFile(const char *filename, const char *buff, const int file_size); + +/** safe write to file, first write to tmp file, then rename to true filename + * parameters: + * filename: the filename to write + * buff: the buffer to write + * file_size: the file size + * return: error no , 0 success, != 0 fail +*/ +int safeWriteToFile(const char *filename, const char *buff, \ + const int file_size); + +/** get a line from file + * parameters: + * fd: the fd to read + * buff: the buffer to store the line + * size: the buffer max size + * once_bytes: the bytes per read + * return: error no , 0 success, != 0 fail +*/ +int fd_gets(int fd, char *buff, const int size, int once_bytes); + +/** set unix rlimit + * parameters: + * resource: resource id, please see sys/resource.h + * value: the value to set + * return: error no , 0 success, != 0 fail +*/ +int set_rlimit(int resource, const rlim_t value); + +/** set non block mode + * parameters: + * fd: the fd to set + * adding_flags: the flags to add + * return: error no , 0 success, != 0 fail +*/ +int fd_add_flags(int fd, int adding_flags); + +/** set non block mode + * parameters: + * fd: the fd to set + * return: error no , 0 success, != 0 fail +*/ +#define set_nonblock(fd) fd_add_flags(fd, O_NONBLOCK) + +/** set run by group and user + * parameters: + * group_name: the group name, can be NULL or empty + * username: the username, can be NULL or empty + * return: error no , 0 success, != 0 fail +*/ +int set_run_by(const char *group_name, const char *username); + +/** compare ip address, type is (in_addr_t *) + * parameters: + * p1: the first ip address + * p2: the second ip address + * return: > 0 when p1 > p2, 0 when p1 == p2, < 0 when p1 < p2 +*/ +int cmp_by_ip_addr_t(const void *p1, const void *p2); + +/** parse bytes + * parameters: + * pStr: the string to parse + * default_unit_bytes: default unit if not specified the unit like MB etc. + * bytes: store the parsed bytes + * return: error no , 0 success, != 0 fail +*/ +int parse_bytes(char *pStr, const int default_unit_bytes, int64_t *bytes); + +/** set rand seed + * return: error no , 0 success, != 0 fail +*/ +int set_rand_seed(); + +/** set timer wrapper + * parameters: + * first_remain_seconds: remain time for first time, in seconds + * interval: the interval + * sighandler: handler function + * return: error no , 0 success, != 0 fail +*/ +int set_timer(const int first_remain_seconds, const int interval, \ + void (*sighandler)(int)); + +/** set file access and modified times + * parameters: + * filename: the file to modify times + * new_time: the time to set + * return: error no , 0 success, != 0 fail +*/ +int set_file_utimes(const char *filename, const time_t new_time); + +/** ignore singal pipe (SIGPIPE) + * return: error no , 0 success, != 0 fail +*/ +int ignore_signal_pipe(); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/sockopt.c b/src/sockopt.c new file mode 100644 index 0000000..2a8dc42 --- /dev/null +++ b/src/sockopt.c @@ -0,0 +1,1744 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//socketopt.c +#include "common_define.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(OS_LINUX) || defined(OS_FREEBSD) +#include +#endif + +#include +#include +#include "shared_func.h" + +#ifdef OS_SUNOS +#include +#endif + +#ifdef USE_SENDFILE + +#ifdef OS_LINUX +#include +#else +#ifdef OS_FREEBSD +#include +#endif +#endif + +#endif + +#include "logger.h" +#include "hash.h" +#include "sockopt.h" + +#ifdef WIN32 +#define USE_SELECT +#else +#define USE_POLL +#endif + +#ifdef OS_LINUX +#ifndef TCP_KEEPIDLE +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#endif + +#ifndef TCP_KEEPINTVL +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#endif + +#ifndef TCP_KEEPCNT +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#endif +#endif + +int tcpgets(int sock, char* s, const int size, const int timeout) +{ + int result; + char t; + int i=1; + + if (s == NULL || size <= 0) + { + return EINVAL; + } + + while (i < size) + { + result = tcprecvdata(sock, &t, 1, timeout); + if (result != 0) + { + *s = 0; + return result; + } + + if (t == '\r') + { + continue; + } + + if (t == '\n') + { + *s = t; + s++; + *s = 0; + return 0; + } + + *s = t; + s++,i++; + } + + *s = 0; + return 0; +} + +int tcprecvdata_ex(int sock, void *data, const int size, \ + const int timeout, int *count) +{ + int left_bytes; + int read_bytes; + int res; + int ret_code; + unsigned char* p; +#ifdef USE_SELECT + fd_set read_set; + struct timeval t; +#else + struct pollfd pollfds; +#endif + +#ifdef USE_SELECT + FD_ZERO(&read_set); + FD_SET(sock, &read_set); +#else + pollfds.fd = sock; + pollfds.events = POLLIN; +#endif + + read_bytes = 0; + ret_code = 0; + p = (unsigned char*)data; + left_bytes = size; + while (left_bytes > 0) + { + +#ifdef USE_SELECT + if (timeout <= 0) + { + res = select(sock+1, &read_set, NULL, NULL, NULL); + } + else + { + t.tv_usec = 0; + t.tv_sec = timeout; + res = select(sock+1, &read_set, NULL, NULL, &t); + } +#else + res = poll(&pollfds, 1, 1000 * timeout); + if (pollfds.revents & POLLHUP) + { + ret_code = ENOTCONN; + break; + } +#endif + + if (res < 0) + { + ret_code = errno != 0 ? errno : EINTR; + break; + } + else if (res == 0) + { + ret_code = ETIMEDOUT; + break; + } + + read_bytes = recv(sock, p, left_bytes, 0); + if (read_bytes < 0) + { + ret_code = errno != 0 ? errno : EINTR; + break; + } + if (read_bytes == 0) + { + ret_code = ENOTCONN; + break; + } + + left_bytes -= read_bytes; + p += read_bytes; + } + + if (count != NULL) + { + *count = size - left_bytes; + } + + return ret_code; +} + +int tcpsenddata(int sock, void* data, const int size, const int timeout) +{ + int left_bytes; + int write_bytes; + int result; + unsigned char* p; +#ifdef USE_SELECT + fd_set write_set; + struct timeval t; +#else + struct pollfd pollfds; +#endif + +#ifdef USE_SELECT + FD_ZERO(&write_set); + FD_SET(sock, &write_set); +#else + pollfds.fd = sock; + pollfds.events = POLLOUT; +#endif + + p = (unsigned char*)data; + left_bytes = size; + while (left_bytes > 0) + { +#ifdef USE_SELECT + if (timeout <= 0) + { + result = select(sock+1, NULL, &write_set, NULL, NULL); + } + else + { + t.tv_usec = 0; + t.tv_sec = timeout; + result = select(sock+1, NULL, &write_set, NULL, &t); + } +#else + result = poll(&pollfds, 1, 1000 * timeout); + if (pollfds.revents & POLLHUP) + { + return ENOTCONN; + break; + } +#endif + + if (result < 0) + { + return errno != 0 ? errno : EINTR; + } + else if (result == 0) + { + return ETIMEDOUT; + } + + write_bytes = send(sock, p, left_bytes, 0); + if (write_bytes < 0) + { + return errno != 0 ? errno : EINTR; + } + + left_bytes -= write_bytes; + p += write_bytes; + } + + return 0; +} + +int tcprecvdata_nb_ex(int sock, void *data, const int size, \ + const int timeout, int *count) +{ + int left_bytes; + int read_bytes; + int res; + int ret_code; + unsigned char* p; +#ifdef USE_SELECT + fd_set read_set; + struct timeval t; +#else + struct pollfd pollfds; +#endif + +#ifdef USE_SELECT + FD_ZERO(&read_set); + FD_SET(sock, &read_set); +#else + pollfds.fd = sock; + pollfds.events = POLLIN; +#endif + + read_bytes = 0; + ret_code = 0; + p = (unsigned char*)data; + left_bytes = size; + while (left_bytes > 0) + { + read_bytes = recv(sock, p, left_bytes, 0); + if (read_bytes > 0) + { + left_bytes -= read_bytes; + p += read_bytes; + continue; + } + + if (read_bytes < 0) + { + + if (!(errno == EAGAIN || errno == EWOULDBLOCK)) + { + ret_code = errno != 0 ? errno : EINTR; + break; + } + } + else + { + ret_code = ENOTCONN; + break; + } + +#ifdef USE_SELECT + if (timeout <= 0) + { + res = select(sock+1, &read_set, NULL, NULL, NULL); + } + else + { + t.tv_usec = 0; + t.tv_sec = timeout; + res = select(sock+1, &read_set, NULL, NULL, &t); + } +#else + res = poll(&pollfds, 1, 1000 * timeout); + if (pollfds.revents & POLLHUP) + { + ret_code = ENOTCONN; + break; + } +#endif + + if (res < 0) + { + ret_code = errno != 0 ? errno : EINTR; + break; + } + else if (res == 0) + { + ret_code = ETIMEDOUT; + break; + } + } + + if (count != NULL) + { + *count = size - left_bytes; + } + + return ret_code; +} + +int tcpsenddata_nb(int sock, void* data, const int size, const int timeout) +{ + int left_bytes; + int write_bytes; + int result; + unsigned char* p; +#ifdef USE_SELECT + fd_set write_set; + struct timeval t; +#else + struct pollfd pollfds; +#endif + +#ifdef USE_SELECT + FD_ZERO(&write_set); + FD_SET(sock, &write_set); +#else + pollfds.fd = sock; + pollfds.events = POLLOUT; +#endif + + p = (unsigned char*)data; + left_bytes = size; + while (left_bytes > 0) + { + write_bytes = send(sock, p, left_bytes, 0); + if (write_bytes < 0) + { + if (!(errno == EAGAIN || errno == EWOULDBLOCK)) + { + return errno != 0 ? errno : EINTR; + } + } + else + { + left_bytes -= write_bytes; + p += write_bytes; + continue; + } + +#ifdef USE_SELECT + if (timeout <= 0) + { + result = select(sock+1, NULL, &write_set, NULL, NULL); + } + else + { + t.tv_usec = 0; + t.tv_sec = timeout; + result = select(sock+1, NULL, &write_set, NULL, &t); + } +#else + result = poll(&pollfds, 1, 1000 * timeout); + if (pollfds.revents & POLLHUP) + { + return ENOTCONN; + } +#endif + + if (result < 0) + { + return errno != 0 ? errno : EINTR; + } + else if (result == 0) + { + return ETIMEDOUT; + } + } + + return 0; +} + +int connectserverbyip(int sock, const char *server_ip, const short server_port) +{ + int result; + struct sockaddr_in addr; + + addr.sin_family = PF_INET; + addr.sin_port = htons(server_port); + result = inet_aton(server_ip, &addr.sin_addr); + if (result == 0 ) + { + return EINVAL; + } + + result = connect(sock, (const struct sockaddr*)&addr, sizeof(addr)); + if (result < 0) + { + return errno != 0 ? errno : EINTR; + } + + return 0; +} + +int connectserverbyip_nb_ex(int sock, const char *server_ip, \ + const short server_port, const int timeout, \ + const bool auto_detect) +{ + int result; + int flags; + bool needRestore; + socklen_t len; + +#ifdef USE_SELECT + fd_set rset; + fd_set wset; + struct timeval tval; +#else + struct pollfd pollfds; +#endif + + struct sockaddr_in addr; + + addr.sin_family = PF_INET; + addr.sin_port = htons(server_port); + result = inet_aton(server_ip, &addr.sin_addr); + if (result == 0 ) + { + return EINVAL; + } + + if (auto_detect) + { + flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) + { + return errno != 0 ? errno : EACCES; + } + + if ((flags & O_NONBLOCK) == 0) + { + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) + { + return errno != 0 ? errno : EACCES; + } + + needRestore = true; + } + else + { + needRestore = false; + } + } + else + { + needRestore = false; + flags = 0; + } + + do + { + if (connect(sock, (const struct sockaddr*)&addr, \ + sizeof(addr)) < 0) + { + result = errno != 0 ? errno : EINPROGRESS; + if (result != EINPROGRESS) + { + break; + } + } + else + { + result = 0; + break; + } + + +#ifdef USE_SELECT + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(sock, &rset); + FD_SET(sock, &wset); + tval.tv_sec = timeout; + tval.tv_usec = 0; + + result = select(sock+1, &rset, &wset, NULL, \ + timeout > 0 ? &tval : NULL); +#else + pollfds.fd = sock; + pollfds.events = POLLIN | POLLOUT; + result = poll(&pollfds, 1, 1000 * timeout); +#endif + + if (result == 0) + { + result = ETIMEDOUT; + break; + } + else if (result < 0) + { + result = errno != 0 ? errno : EINTR; + break; + } + + len = sizeof(result); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &result, &len) < 0) + { + result = errno != 0 ? errno : EACCES; + break; + } + } while (0); + + if (needRestore) + { + fcntl(sock, F_SETFL, flags); + } + + return result; +} + +in_addr_t getIpaddr(getnamefunc getname, int sock, \ + char *buff, const int bufferSize) +{ + struct sockaddr_in addr; + socklen_t addrlen; + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + + if (getname(sock, (struct sockaddr *)&addr, &addrlen) != 0) + { + *buff = '\0'; + return INADDR_NONE; + } + + if (addrlen > 0) + { + if (inet_ntop(AF_INET, &addr.sin_addr, buff, bufferSize) == NULL) + { + *buff = '\0'; + } + } + else + { + *buff = '\0'; + } + + return addr.sin_addr.s_addr; +} + +char *getHostnameByIp(const char *szIpAddr, char *buff, const int bufferSize) +{ + struct in_addr ip_addr; + struct hostent *ent; + + if (inet_pton(AF_INET, szIpAddr, &ip_addr) != 1) + { + *buff = '\0'; + return buff; + } + + ent = gethostbyaddr((char *)&ip_addr, sizeof(ip_addr), AF_INET); + if (ent == NULL || ent->h_name == NULL) + { + *buff = '\0'; + } + else + { + snprintf(buff, bufferSize, "%s", ent->h_name); + } + + return buff; +} + +in_addr_t getIpaddrByName(const char *name, char *buff, const int bufferSize) +{ + struct in_addr ip_addr; + struct hostent *ent; + in_addr_t **addr_list; + + if ((*name >= '0' && *name <= '9') && + inet_pton(AF_INET, name, &ip_addr) == 1) + { + if (buff != NULL) + { + snprintf(buff, bufferSize, "%s", name); + } + return ip_addr.s_addr; + } + + ent = gethostbyname(name); + if (ent == NULL) + { + return INADDR_NONE; + } + + addr_list = (in_addr_t **)ent->h_addr_list; + if (addr_list[0] == NULL) + { + return INADDR_NONE; + } + + memset(&ip_addr, 0, sizeof(ip_addr)); + ip_addr.s_addr = *(addr_list[0]); + if (buff != NULL) + { + if (inet_ntop(AF_INET, &ip_addr, buff, bufferSize) == NULL) + { + *buff = '\0'; + } + } + + return ip_addr.s_addr; +} + +int nbaccept(int sock, const int timeout, int *err_no) +{ + struct sockaddr_in inaddr; + socklen_t sockaddr_len; + fd_set read_set; + struct timeval t; + int result; + + if (timeout > 0) + { + t.tv_usec = 0; + t.tv_sec = timeout; + + FD_ZERO(&read_set); + FD_SET(sock, &read_set); + + result = select(sock+1, &read_set, NULL, NULL, &t); + if (result == 0) //timeout + { + *err_no = ETIMEDOUT; + return -1; + } + else if (result < 0) //error + { + *err_no = errno != 0 ? errno : EINTR; + return -1; + } + + /* + if (!FD_ISSET(sock, &read_set)) + { + *err_no = EAGAIN; + return -1; + } + */ + } + + sockaddr_len = sizeof(inaddr); + result = accept(sock, (struct sockaddr*)&inaddr, &sockaddr_len); + if (result < 0) + { + *err_no = errno != 0 ? errno : EINTR; + } + else + { + *err_no = 0; + } + + return result; +} + +int socketBind(int sock, const char *bind_ipaddr, const int port) +{ + struct sockaddr_in bindaddr; + + bindaddr.sin_family = AF_INET; + bindaddr.sin_port = htons(port); + if (bind_ipaddr == NULL || *bind_ipaddr == '\0') + { + bindaddr.sin_addr.s_addr = INADDR_ANY; + } + else + { + if (inet_aton(bind_ipaddr, &bindaddr.sin_addr) == 0) + { + logError("file: "__FILE__", line: %d, " \ + "invalid ip addr %s", \ + __LINE__, bind_ipaddr); + return EINVAL; + } + } + + if (bind(sock, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "bind port %d failed, " \ + "errno: %d, error info: %s.", \ + __LINE__, port, errno, STRERROR(errno)); + return errno != 0 ? errno : ENOMEM; + } + + return 0; +} + +int socketServer(const char *bind_ipaddr, const int port, int *err_no) +{ + int sock; + int result; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + *err_no = errno != 0 ? errno : EMFILE; + logError("file: "__FILE__", line: %d, " \ + "socket create failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return -1; + } + + result = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &result, sizeof(int))<0) + { + *err_no = errno != 0 ? errno : ENOMEM; + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + close(sock); + return -2; + } + + if ((*err_no=socketBind(sock, bind_ipaddr, port)) != 0) + { + close(sock); + return -3; + } + + if (listen(sock, 1024) < 0) + { + *err_no = errno != 0 ? errno : EINVAL; + logError("file: "__FILE__", line: %d, " \ + "listen port %d failed, " \ + "errno: %d, error info: %s", \ + __LINE__, port, errno, STRERROR(errno)); + close(sock); + return -4; + } + + *err_no = 0; + return sock; +} + +int tcprecvfile(int sock, const char *filename, const int64_t file_bytes, \ + const int fsync_after_written_bytes, const int timeout, \ + int64_t *true_file_bytes) +{ + int write_fd; + char buff[FDFS_WRITE_BUFF_SIZE]; + int64_t remain_bytes; + int recv_bytes; + int written_bytes; + int result; + int flags; + int count; + tcprecvdata_exfunc recv_func; + + *true_file_bytes = 0; + flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) + { + return errno != 0 ? errno : EACCES; + } + + if (flags & O_NONBLOCK) + { + recv_func = tcprecvdata_nb_ex; + } + else + { + recv_func = tcprecvdata_ex; + } + + write_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (write_fd < 0) + { + return errno != 0 ? errno : EACCES; + } + + written_bytes = 0; + remain_bytes = file_bytes; + while (remain_bytes > 0) + { + if (remain_bytes > sizeof(buff)) + { + recv_bytes = sizeof(buff); + } + else + { + recv_bytes = remain_bytes; + } + + result = recv_func(sock, buff, recv_bytes, \ + timeout, &count); + if (result != 0) + { + if (file_bytes != INFINITE_FILE_SIZE) + { + close(write_fd); + unlink(filename); + return result; + } + } + + if (count > 0 && write(write_fd, buff, count) != count) + { + result = errno != 0 ? errno: EIO; + close(write_fd); + unlink(filename); + return result; + } + + *true_file_bytes += count; + if (fsync_after_written_bytes > 0) + { + written_bytes += count; + if (written_bytes >= fsync_after_written_bytes) + { + written_bytes = 0; + if (fsync(write_fd) != 0) + { + result = errno != 0 ? errno: EIO; + close(write_fd); + unlink(filename); + return result; + } + } + } + + if (result != 0) //recv infinite file, does not delete the file + { + int read_fd; + read_fd = -1; + + do + { + if (*true_file_bytes < 8) + { + break; + } + + read_fd = open(filename, O_RDONLY); + if (read_fd < 0) + { + return errno != 0 ? errno : EACCES; + } + + if (lseek(read_fd, -8, SEEK_END) < 0) + { + result = errno != 0 ? errno : EIO; + break; + } + + if (read(read_fd, buff, 8) != 8) + { + result = errno != 0 ? errno : EIO; + break; + } + + *true_file_bytes -= 8; + if (buff2long(buff) != *true_file_bytes) + { + result = EINVAL; + break; + } + + if (ftruncate(write_fd, *true_file_bytes) != 0) + { + result = errno != 0 ? errno : EIO; + break; + } + + result = 0; + } while (0); + + close(write_fd); + if (read_fd >= 0) + { + close(read_fd); + } + + if (result != 0) + { + unlink(filename); + } + + return result; + } + + remain_bytes -= count; + } + + close(write_fd); + return 0; +} + +int tcprecvfile_ex(int sock, const char *filename, const int64_t file_bytes, \ + const int fsync_after_written_bytes, \ + unsigned int *hash_codes, const int timeout) +{ + int fd; + char buff[FDFS_WRITE_BUFF_SIZE]; + int64_t remain_bytes; + int recv_bytes; + int written_bytes; + int result; + int flags; + tcprecvdata_exfunc recv_func; + + flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) + { + return errno != 0 ? errno : EACCES; + } + + if (flags & O_NONBLOCK) + { + recv_func = tcprecvdata_nb_ex; + } + else + { + recv_func = tcprecvdata_ex; + } + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + return errno != 0 ? errno : EACCES; + } + + INIT_HASH_CODES4(hash_codes) + + written_bytes = 0; + remain_bytes = file_bytes; + while (remain_bytes > 0) + { + if (remain_bytes > sizeof(buff)) + { + recv_bytes = sizeof(buff); + } + else + { + recv_bytes = remain_bytes; + } + + if ((result=recv_func(sock, buff, recv_bytes, \ + timeout, NULL)) != 0) + { + close(fd); + unlink(filename); + return result; + } + + if (write(fd, buff, recv_bytes) != recv_bytes) + { + result = errno != 0 ? errno: EIO; + close(fd); + unlink(filename); + return result; + } + + if (fsync_after_written_bytes > 0) + { + written_bytes += recv_bytes; + if (written_bytes >= fsync_after_written_bytes) + { + written_bytes = 0; + if (fsync(fd) != 0) + { + result = errno != 0 ? errno: EIO; + close(fd); + unlink(filename); + return result; + } + } + } + + CALC_HASH_CODES4(buff, recv_bytes, hash_codes) + + remain_bytes -= recv_bytes; + } + + close(fd); + + FINISH_HASH_CODES4(hash_codes) + + return 0; +} + +int tcpdiscard(int sock, const int bytes, const int timeout, \ + int64_t *total_recv_bytes) +{ + char buff[FDFS_WRITE_BUFF_SIZE]; + int remain_bytes; + int recv_bytes; + int result; + int flags; + int count; + tcprecvdata_exfunc recv_func; + + *total_recv_bytes = 0; + flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) + { + return errno != 0 ? errno : EACCES; + } + + if (flags & O_NONBLOCK) + { + recv_func = tcprecvdata_nb_ex; + } + else + { + recv_func = tcprecvdata_ex; + } + + remain_bytes = bytes; + while (remain_bytes > 0) + { + if (remain_bytes > sizeof(buff)) + { + recv_bytes = sizeof(buff); + } + else + { + recv_bytes = remain_bytes; + } + + result = recv_func(sock, buff, recv_bytes, \ + timeout, &count); + *total_recv_bytes += count; + if (result != 0) + { + return result; + } + + remain_bytes -= recv_bytes; + } + + return 0; +} + +int tcpsendfile_ex(int sock, const char *filename, const int64_t file_offset, \ + const int64_t file_bytes, const int timeout, int64_t *total_send_bytes) +{ + int fd; + int64_t send_bytes; + int result; + int flags; +#ifdef USE_SENDFILE + #if defined(OS_FREEBSD) || defined(OS_LINUX) + off_t offset; + #ifdef OS_LINUX + int64_t remain_bytes; + #endif + #endif +#else + int64_t remain_bytes; +#endif + + fd = open(filename, O_RDONLY); + if (fd < 0) + { + *total_send_bytes = 0; + return errno != 0 ? errno : EACCES; + } + + flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) + { + *total_send_bytes = 0; + return errno != 0 ? errno : EACCES; + } + +#ifdef USE_SENDFILE + + if (flags & O_NONBLOCK) + { + if (fcntl(sock, F_SETFL, flags & ~O_NONBLOCK) == -1) + { + *total_send_bytes = 0; + return errno != 0 ? errno : EACCES; + } + } + +#ifdef OS_LINUX + /* + result = 1; + if (setsockopt(sock, SOL_TCP, TCP_CORK, &result, sizeof(int)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + close(fd); + *total_send_bytes = 0; + return errno != 0 ? errno : EIO; + } + */ + +#define FILE_1G_SIZE (1 * 1024 * 1024 * 1024) + + result = 0; + offset = file_offset; + remain_bytes = file_bytes; + while (remain_bytes > 0) + { + if (remain_bytes > FILE_1G_SIZE) + { + send_bytes = sendfile(sock, fd, &offset, FILE_1G_SIZE); + } + else + { + send_bytes = sendfile(sock, fd, &offset, remain_bytes); + } + + if (send_bytes <= 0) + { + result = errno != 0 ? errno : EIO; + break; + } + + remain_bytes -= send_bytes; + } + + *total_send_bytes = file_bytes - remain_bytes; +#else +#ifdef OS_FREEBSD + offset = file_offset; + if (sendfile(fd, sock, offset, file_bytes, NULL, NULL, 0) != 0) + { + *total_send_bytes = 0; + result = errno != 0 ? errno : EIO; + } + else + { + *total_send_bytes = file_bytes; + result = 0; + } +#endif +#endif + + if (flags & O_NONBLOCK) //restore + { + if (fcntl(sock, F_SETFL, flags) == -1) + { + result = errno != 0 ? errno : EACCES; + } + } + +#ifdef OS_LINUX + close(fd); + return result; +#endif + +#ifdef OS_FREEBSD + close(fd); + return result; +#endif + +#endif + + { + char buff[FDFS_WRITE_BUFF_SIZE]; + int64_t remain_bytes; + tcpsenddatafunc send_func; + + if (file_offset > 0 && lseek(fd, file_offset, SEEK_SET) < 0) + { + result = errno != 0 ? errno : EIO; + close(fd); + *total_send_bytes = 0; + return result; + } + + if (flags & O_NONBLOCK) + { + send_func = tcpsenddata_nb; + } + else + { + send_func = tcpsenddata; + } + + result = 0; + remain_bytes = file_bytes; + while (remain_bytes > 0) + { + if (remain_bytes > sizeof(buff)) + { + send_bytes = sizeof(buff); + } + else + { + send_bytes = remain_bytes; + } + + if (read(fd, buff, send_bytes) != send_bytes) + { + result = errno != 0 ? errno : EIO; + break; + } + + if ((result=send_func(sock, buff, send_bytes, \ + timeout)) != 0) + { + break; + } + + remain_bytes -= send_bytes; + } + + *total_send_bytes = file_bytes - remain_bytes; + } + + close(fd); + return result; +} + +int tcpsetserveropt(int fd, const int timeout) +{ + int flags; + int result; + + struct linger linger; + struct timeval waittime; + +/* + linger.l_onoff = 1; +#ifdef OS_FREEBSD + linger.l_linger = timeout * 100; +#else + linger.l_linger = timeout; +#endif +*/ + linger.l_onoff = 0; + linger.l_linger = 0; + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, \ + &linger, (socklen_t)sizeof(struct linger)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : ENOMEM; + } + + waittime.tv_sec = timeout; + waittime.tv_usec = 0; + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, + &waittime, (socklen_t)sizeof(struct timeval)) < 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + &waittime, (socklen_t)sizeof(struct timeval)) < 0) + { + logWarning("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + } + + flags = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, \ + (char *)&flags, sizeof(flags)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + if ((result=tcpsetkeepalive(fd, 2 * timeout + 1)) != 0) + { + return result; + } + + return 0; +} + +int tcpsetkeepalive(int fd, const int idleSeconds) +{ + int keepAlive; + +#ifdef OS_LINUX + int keepIdle; + int keepInterval; + int keepCount; +#endif + + keepAlive = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, \ + (char *)&keepAlive, sizeof(keepAlive)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + +#ifdef OS_LINUX + keepIdle = idleSeconds; + if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *)&keepIdle, \ + sizeof(keepIdle)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + keepInterval = 10; + if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *)&keepInterval, \ + sizeof(keepInterval)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + keepCount = 3; + if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *)&keepCount, \ + sizeof(keepCount)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } +#endif + + return 0; +} + +int tcpprintkeepalive(int fd) +{ + int keepAlive; + socklen_t len; + +#ifdef OS_LINUX + int keepIdle; + int keepInterval; + int keepCount; +#endif + + len = sizeof(keepAlive); + if (getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, \ + (char *)&keepAlive, &len) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + +#ifdef OS_LINUX + len = sizeof(keepIdle); + if (getsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *)&keepIdle, \ + &len) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + len = sizeof(keepInterval); + if (getsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *)&keepInterval, \ + &len) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + len = sizeof(keepCount); + if (getsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *)&keepCount, \ + &len) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + logInfo("keepAlive=%d, keepIdle=%d, keepInterval=%d, keepCount=%d", + keepAlive, keepIdle, keepInterval, keepCount); +#else + logInfo("keepAlive=%d", keepAlive); +#endif + + return 0; +} + +int tcpsetnonblockopt(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + { + logError("file: "__FILE__", line: %d, " \ + "fcntl failed, errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EACCES; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + { + logError("file: "__FILE__", line: %d, " \ + "fcntl failed, errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EACCES; + } + + return 0; +} + +int tcpsetnodelay(int fd, const int timeout) +{ + int flags; + int result; + + if ((result=tcpsetkeepalive(fd, 2 * timeout + 1)) != 0) + { + return result; + } + + flags = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, \ + (char *)&flags, sizeof(flags)) < 0) + { + logError("file: "__FILE__", line: %d, " \ + "setsockopt failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EINVAL; + } + + return 0; +} + +#if defined(OS_LINUX) || defined(OS_FREEBSD) +int getlocaladdrs(char ip_addrs[][IP_ADDRESS_SIZE], \ + const int max_count, int *count) +{ + struct ifaddrs *ifc; + struct ifaddrs *ifc1; + + *count = 0; + if (0 != getifaddrs(&ifc)) + { + logError("file: "__FILE__", line: %d, " \ + "call getifaddrs fail, " \ + "errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EMFILE; + } + + ifc1 = ifc; + while (NULL != ifc) + { + struct sockaddr *s; + s = ifc->ifa_addr; + if (NULL != s && AF_INET == s->sa_family) + { + if (max_count <= *count) + { + logError("file: "__FILE__", line: %d, "\ + "max_count: %d < iterface count: %d", \ + __LINE__, max_count, *count); + freeifaddrs(ifc1); + return ENOSPC; + } + + if (inet_ntop(AF_INET, &((struct sockaddr_in *)s)-> \ + sin_addr, ip_addrs[*count], IP_ADDRESS_SIZE) != NULL) + { + (*count)++; + } + else + { + logWarning("file: "__FILE__", line: %d, " \ + "call inet_ntop fail, " \ + "errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + } + } + + ifc = ifc->ifa_next; + } + + freeifaddrs(ifc1); + return *count > 0 ? 0 : ENOENT; +} + +#else + +int getlocaladdrs(char ip_addrs[][IP_ADDRESS_SIZE], \ + const int max_count, int *count) +{ + int s; + struct ifconf ifconf; + struct ifreq ifr[32]; + int if_count; + int i; + int result; + + *count = 0; + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + { + logError("file: "__FILE__", line: %d, " \ + "socket create fail, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EMFILE; + } + + ifconf.ifc_buf = (char *) ifr; + ifconf.ifc_len = sizeof(ifr); + if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) + { + result = errno != 0 ? errno : EMFILE; + logError("file: "__FILE__", line: %d, " \ + "call ioctl fail, errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + close(s); + return result; + } + + if_count = ifconf.ifc_len / sizeof(ifr[0]); + if (max_count < if_count) + { + logError("file: "__FILE__", line: %d, " \ + "max_count: %d < iterface count: %d", \ + __LINE__, max_count, if_count); + close(s); + return ENOSPC; + } + + for (i = 0; i < if_count; i++) + { + struct sockaddr_in *s_in; + s_in = (struct sockaddr_in *) &ifr[i].ifr_addr; + if (!inet_ntop(AF_INET, &s_in->sin_addr, \ + ip_addrs[*count], IP_ADDRESS_SIZE)) + { + result = errno != 0 ? errno : EMFILE; + logError("file: "__FILE__", line: %d, " \ + "call inet_ntop fail, " \ + "errno: %d, error info: %s", \ + __LINE__, result, STRERROR(result)); + close(s); + return result; + } + (*count)++; + } + + close(s); + return *count > 0 ? 0 : ENOENT; +} + +#endif + +int gethostaddrs(char **if_alias_prefixes, const int prefix_count, \ + char ip_addrs[][IP_ADDRESS_SIZE], const int max_count, int *count) +{ + struct hostent *ent; + char hostname[128]; + char *alias_prefixes1[1]; + char **true_alias_prefixes; + int true_count; + int i; + int k; + int sock; + struct ifreq req; + struct sockaddr_in *addr; + int ret; + + *count = 0; + if (prefix_count <= 0) + { + if (getlocaladdrs(ip_addrs, max_count, count) == 0) + { + return 0; + } + +#ifdef OS_FREEBSD + #define IF_NAME_PREFIX "bge" +#else + #ifdef OS_SUNOS + #define IF_NAME_PREFIX "e1000g" + #else + #ifdef OS_AIX + #define IF_NAME_PREFIX "en" + #else + #define IF_NAME_PREFIX "eth" + #endif + #endif +#endif + + alias_prefixes1[0] = IF_NAME_PREFIX; + true_count = 1; + true_alias_prefixes = alias_prefixes1; + } + else + { + true_count = prefix_count; + true_alias_prefixes = if_alias_prefixes; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + logError("file: "__FILE__", line: %d, " \ + "socket create failed, errno: %d, error info: %s.", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EMFILE; + } + + for (i=0; isin_addr, ip_addrs[*count], \ + IP_ADDRESS_SIZE) != NULL) + { + (*count)++; + if (*count >= max_count) + { + break; + } + } + } + } + + close(sock); + if (*count > 0) + { + return 0; + } + + if (gethostname(hostname, sizeof(hostname)) != 0) + { + logError("file: "__FILE__", line: %d, " \ + "call gethostname fail, " \ + "error no: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return errno != 0 ? errno : EFAULT; + } + + ent = gethostbyname(hostname); + if (ent == NULL) + { + logError("file: "__FILE__", line: %d, " \ + "call gethostbyname fail, " \ + "error no: %d, error info: %s", \ + __LINE__, h_errno, STRERROR(h_errno)); + return h_errno != 0 ? h_errno : EFAULT; + } + + k = 0; + while (ent->h_addr_list[k] != NULL) + { + if (*count >= max_count) + { + break; + } + + if (inet_ntop(ent->h_addrtype, ent->h_addr_list[k], \ + ip_addrs[*count], IP_ADDRESS_SIZE) != NULL) + { + (*count)++; + } + + k++; + } + + return 0; +} + diff --git a/src/sockopt.h b/src/sockopt.h new file mode 100644 index 0000000..1c8773c --- /dev/null +++ b/src/sockopt.h @@ -0,0 +1,335 @@ +/** +* 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.csource.org/ for more detail. +**/ + +//socketopt.h + +#ifndef _SOCKETOPT_H_ +#define _SOCKETOPT_H_ + +#include "common_define.h" + +#define FDFS_WRITE_BUFF_SIZE 256 * 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*getnamefunc)(int socket, struct sockaddr *address, \ + socklen_t *address_len); + +typedef int (*tcpsenddatafunc)(int sock, void* data, const int size, \ + const int timeout); + +typedef int (*tcprecvdata_exfunc)(int sock, void *data, const int size, \ + const int timeout, int *count); + +#define getSockIpaddr(sock, buff, bufferSize) \ + getIpaddr(getsockname, sock, buff, bufferSize) + +#define getPeerIpaddr(sock, buff, bufferSize) \ + getIpaddr(getpeername, sock, buff, bufferSize) + +/** get a line from socket + * parameters: + * sock: the socket + * s: the buffer + * size: buffer size (max bytes can receive) + * timeout: read timeout + * return: error no, 0 success, != 0 fail +*/ +int tcpgets(int sock, char *s, const int size, const int timeout); + +/** recv data (block mode) + * parameters: + * sock: the socket + * data: the buffer + * size: buffer size (max bytes can receive) + * timeout: read timeout + * count: store the bytes recveived + * return: error no, 0 success, != 0 fail +*/ +int tcprecvdata_ex(int sock, void *data, const int size, \ + const int timeout, int *count); + +/** recv data (non-block mode) + * parameters: + * sock: the socket + * data: the buffer + * size: buffer size (max bytes can receive) + * timeout: read timeout + * count: store the bytes recveived + * return: error no, 0 success, != 0 fail +*/ +int tcprecvdata_nb_ex(int sock, void *data, const int size, \ + const int timeout, int *count); + +/** send data (block mode) + * parameters: + * sock: the socket + * data: the buffer to send + * size: buffer size + * timeout: write timeout + * return: error no, 0 success, != 0 fail +*/ +int tcpsenddata(int sock, void* data, const int size, const int timeout); + +/** send data (non-block mode) + * parameters: + * sock: the socket + * data: the buffer to send + * size: buffer size + * timeout: write timeout + * return: error no, 0 success, != 0 fail +*/ +int tcpsenddata_nb(int sock, void* data, const int size, const int timeout); + +/** connect to server by block mode + * parameters: + * sock: the socket + * server_ip: ip address of the server + * server_port: port of the server + * return: error no, 0 success, != 0 fail +*/ +int connectserverbyip(int sock, const char *server_ip, const short server_port); + +/** connect to server by non-block mode + * parameters: + * sock: the socket + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * auto_detect: if detect and adjust the block mode of the socket + * return: error no, 0 success, != 0 fail +*/ +int connectserverbyip_nb_ex(int sock, const char *server_ip, \ + const short server_port, const int timeout, \ + const bool auto_detect); + +/** connect to server by non-block mode, the socket must be set to non-block + * parameters: + * sock: the socket, must be set to non-block + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * return: error no, 0 success, != 0 fail +*/ +#define connectserverbyip_nb(sock, server_ip, server_port, timeout) \ + connectserverbyip_nb_ex(sock, server_ip, server_port, timeout, false) + +/** connect to server by non-block mode, auto detect socket block mode + * parameters: + * sock: the socket, can be block mode + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * return: error no, 0 success, != 0 fail +*/ +#define connectserverbyip_nb_auto(sock, server_ip, server_port, timeout) \ + connectserverbyip_nb_ex(sock, server_ip, server_port, timeout, true) + +/** accept client connect request + * parameters: + * sock: the server socket + * timeout: read timeout + * err_no: store the error no, 0 for success + * return: client socket, < 0 for error +*/ +int nbaccept(int sock, const int timeout, int *err_no); + +/** set socket options + * parameters: + * sock: the socket + * timeout: read & write timeout + * return: error no, 0 success, != 0 fail +*/ +int tcpsetserveropt(int fd, const int timeout); + +/** set socket non-block options + * parameters: + * sock: the socket + * return: error no, 0 success, != 0 fail +*/ +int tcpsetnonblockopt(int fd); + +/** set socket no delay on send data + * parameters: + * sock: the socket + * timeout: read & write timeout + * return: error no, 0 success, != 0 fail +*/ +int tcpsetnodelay(int fd, const int timeout); + +/** set socket keep-alive + * parameters: + * sock: the socket + * idleSeconds: max idle time (seconds) + * return: error no, 0 success, != 0 fail +*/ +int tcpsetkeepalive(int fd, const int idleSeconds); + +/** print keep-alive related parameters + * parameters: + * sock: the socket + * return: error no, 0 success, != 0 fail +*/ +int tcpprintkeepalive(int fd); + +/** get ip address + * parameters: + * getname: the function name, should be getpeername or getsockname + * sock: the socket + * buff: buffer to store the ip address + * bufferSize: the buffer size (max bytes) + * return: in_addr_t, INADDR_NONE for fail +*/ +in_addr_t getIpaddr(getnamefunc getname, int sock, \ + char *buff, const int bufferSize); + +/** get hostname by it's ip address + * parameters: + * szIpAddr: the ip address + * buff: buffer to store the hostname + * bufferSize: the buffer size (max bytes) + * return: hostname, empty buffer for error +*/ +char *getHostnameByIp(const char *szIpAddr, char *buff, const int bufferSize); + +/** get by ip address by it's hostname + * parameters: + * name: the hostname + * buff: buffer to store the ip address + * bufferSize: the buffer size (max bytes) + * return: in_addr_t, INADDR_NONE for fail +*/ +in_addr_t getIpaddrByName(const char *name, char *buff, const int bufferSize); + +/** bind wrapper + * parameters: + * sock: the socket + * bind_ipaddr: the ip address to bind + * port: the port to bind + * return: error no, 0 success, != 0 fail +*/ +int socketBind(int sock, const char *bind_ipaddr, const int port); + +/** start a socket server (socket, bind and listen) + * parameters: + * sock: the socket + * bind_ipaddr: the ip address to bind + * port: the port to bind + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +int socketServer(const char *bind_ipaddr, const int port, int *err_no); + +#define tcprecvdata(sock, data, size, timeout) \ + tcprecvdata_ex(sock, data, size, timeout, NULL) + +#define tcpsendfile(sock, filename, file_bytes, timeout, total_send_bytes) \ + tcpsendfile_ex(sock, filename, 0, file_bytes, timeout, total_send_bytes) + +#define tcprecvdata_nb(sock, data, size, timeout) \ + tcprecvdata_nb_ex(sock, data, size, timeout, NULL) + +/** send a file + * parameters: + * sock: the socket + * filename: the file to send + * file_offset: file offset, start position + * file_bytes: send file length + * timeout: write timeout + * total_send_bytes: store the send bytes + * return: error no, 0 success, != 0 fail +*/ +int tcpsendfile_ex(int sock, const char *filename, const int64_t file_offset, \ + const int64_t file_bytes, const int timeout, int64_t *total_send_bytes); + +/** receive data to a file + * parameters: + * sock: the socket + * filename: the file to write + * file_bytes: file size (bytes) + * fsync_after_written_bytes: call fsync every x bytes + * timeout: read/recv timeout + * true_file_bytes: store the true file bytes + * return: error no, 0 success, != 0 fail +*/ +int tcprecvfile(int sock, const char *filename, const int64_t file_bytes, \ + const int fsync_after_written_bytes, const int timeout, \ + int64_t *true_file_bytes); + + +#define tcprecvinfinitefile(sock, filename, fsync_after_written_bytes, \ + timeout, file_bytes) \ + tcprecvfile(sock, filename, INFINITE_FILE_SIZE, \ + fsync_after_written_bytes, timeout, file_bytes) + + +/** receive data to a file + * parameters: + * sock: the socket + * filename: the file to write + * file_bytes: file size (bytes) + * fsync_after_written_bytes: call fsync every x bytes + * hash_codes: return hash code of file content + * timeout: read/recv timeout + * return: error no, 0 success, != 0 fail +*/ +int tcprecvfile_ex(int sock, const char *filename, const int64_t file_bytes, \ + const int fsync_after_written_bytes, \ + unsigned int *hash_codes, const int timeout); + +/** receive specified data and discard + * parameters: + * sock: the socket + * bytes: data bytes to discard + * timeout: read timeout + * total_recv_bytes: store the total recv bytes + * return: error no, 0 success, != 0 fail +*/ +int tcpdiscard(int sock, const int bytes, const int timeout, \ + int64_t *total_recv_bytes); + +/** get local host ip addresses + * parameters: + * ip_addrs: store the ip addresses + * max_count: max ip address (max ip_addrs elements) + * count: store the ip address count + * return: error no, 0 success, != 0 fail +*/ +int getlocaladdrs(char ip_addrs[][IP_ADDRESS_SIZE], \ + const int max_count, int *count); + +/** get local host ip addresses + * parameters: + * ip_addrs: store the ip addresses + * max_count: max ip address (max ip_addrs elements) + * count: store the ip address count + * return: error no, 0 success, != 0 fail +*/ +int getlocaladdrs1(char ip_addrs[][IP_ADDRESS_SIZE], \ + const int max_count, int *count); + +/** get local host ip addresses by if alias prefix + * parameters: + * if_alias_prefixes: if alias prefixes, such as eth, bond etc. + * prefix_count: if alias prefix count + * ip_addrs: store the ip addresses + * max_count: max ip address (max ip_addrs elements) + * count: store the ip address count + * return: error no, 0 success, != 0 fail +*/ +int gethostaddrs(char **if_alias_prefixes, const int prefix_count, \ + char ip_addrs[][IP_ADDRESS_SIZE], const int max_count, int *count); + +#ifdef __cplusplus +} +#endif + +#endif +