示例#1
0
文件: wbt_config.c 项目: fcten/BitMQ
wbt_status wbt_conf_reload() {
    /* TODO 清理已保存的配置信息 */

    /* 尝试读取配置文件 */
    wbt_file_t wbt_config_file;
	wbt_config_file.fd = wbt_open_file(wbt_config_file_name.str);
    
    if( wbt_config_file.fd <= 0 ) {
        /* 找不到配置文件 */
        wbt_log_print("Can't find config file: %.*s\n", wbt_config_file_name.len, wbt_config_file_name.str);
        return WBT_ERROR;
    }

#ifndef WIN32
    int len = wbt_get_file_path_by_fd(wbt_config_file.fd, wbt_config_file_path.str, wbt_config_file_path.len);
    if( len <= 0 ) {
        return WBT_ERROR;
    }
    wbt_config_file_path.str[len] = '\0';
#endif
    
    struct stat statbuff;  
    if(stat(wbt_config_file_name.str, &statbuff) < 0){
        return WBT_ERROR;
    }
    wbt_config_file.size = statbuff.st_size;

    wbt_config_file_content.len = wbt_config_file.size;
    wbt_config_file_content.str = wbt_malloc( wbt_config_file_content.len );
    if( wbt_config_file_content.str == NULL ) {
        return WBT_ERROR;
    }
    if( wbt_read_file( wbt_config_file.fd, wbt_config_file_content.str, wbt_config_file_content.len, 0) != wbt_config_file_content.len ) {
        wbt_log_print("Read config file failed\n");
        return WBT_ERROR;
    }
    
    /* 关闭配置文件 */
	wbt_close_file(wbt_config_file.fd);

    /* 解析配置文件 */
    if( wbt_conf_parse(&wbt_config_file_content) == WBT_OK ) {
        //wbt_rbtree_print(wbt_config_rbtree.root);
        wbt_free(wbt_config_file_content.str);

#ifndef WIN32
        char tmp[1024];
        snprintf(tmp, sizeof(tmp), "bmq: master process (%.*s)", len, wbt_config_file_path.str );
        wbt_set_proc_title(tmp);
#endif

		return WBT_OK;
    } else {
        wbt_free(wbt_config_file_content.str);
        wbt_log_print("Syntax error on config file: line %d, charactor %d\n", wbt_conf_line, wbt_conf_charactor);
        return WBT_ERROR;
    }
}
示例#2
0
文件: wbt_rbtree.c 项目: wljcom/webit
void wbt_rbtree_destroy_recursive_ignore_value(wbt_rbtree_node_t *node) {
    if(node != wbt_rbtree_node_nil) {
        wbt_rbtree_destroy_recursive(node->left);
        wbt_rbtree_destroy_recursive(node->right);
        
        wbt_mem_t tmp;
        tmp.ptr = node;
        tmp.len = sizeof(wbt_rbtree_node_t);
        wbt_free(&node->key);
        //wbt_free(&node->value);
        wbt_free(&tmp);
    }
}
示例#3
0
文件: wbt_file.c 项目: fcten/BitMQ
wbt_status wbt_file_compress( wbt_file_t *file ) {
    if( !file->ptr && wbt_file_read( file ) < 0 ) {
        return WBT_ERROR;
    }
    
    if( file->gzip_ptr ) {
        return WBT_OK;
    }
    
    int ret;
    size_t size = file->size > 1024 ? file->size : 1024;
    do {
        // 最大分配 16M
        if( size > 1024 * 1024 * 16 ) {
            wbt_free( file->gzip_ptr );
            file->gzip_ptr = NULL;
            return WBT_ERROR;
        }

        void *p = wbt_realloc( file->gzip_ptr, size );
        if( p == NULL ) {
            wbt_free( file->gzip_ptr );
            file->gzip_ptr = NULL;
            return WBT_ERROR;
        }
        file->gzip_ptr = p;
        file->gzip_size = size;

        ret = wbt_gzip_compress((Bytef *)file->ptr,
                (uLong)file->size,
                (Bytef *)file->gzip_ptr,
                (uLong *)&file->gzip_size );

        if( ret == Z_OK ) {
            file->gzip_ptr = wbt_realloc( file->gzip_ptr, file->gzip_size );
            return WBT_OK;
        } else if( ret == Z_BUF_ERROR ) {
            // 缓冲区不够大
            size *= 2;
        } else {
            // 内存不足
            wbt_free( file->gzip_ptr );
            file->gzip_ptr = NULL;
            return WBT_ERROR;
        }
    } while( ret == Z_BUF_ERROR );

    return WBT_ERROR;
}
示例#4
0
文件: wbt_file.c 项目: fcten/BitMQ
void wbt_file_cleanup_recursive(wbt_rb_node_t *node) {
    /* 从叶节点递归处理至根节点 */
    if(node) {
        wbt_file_cleanup_recursive(node->left);
        wbt_file_cleanup_recursive(node->right);
        
        wbt_file_t * tmp_file = (wbt_file_t *)node->value.str;
        if( tmp_file->refer == 0 && wbt_cur_mtime - tmp_file->last_use_mtime > 10000 ) {
            wbt_log_debug("closed fd:%d %.*s\n", tmp_file->fd, node->key.len, node->key.str.s);
            wbt_close_file(tmp_file->fd);
            wbt_free(tmp_file->ptr);
            wbt_free(tmp_file->gzip_ptr);
            wbt_rb_delete(&wbt_file_rbtree, node);
        }
    }
}
示例#5
0
static wbt_status wbt_mq_persist_timer(wbt_timer_t *timer) {
    if( wbt_persist_mid_fd ) {
        ssize_t nwrite = wbt_write_file(wbt_persist_mid_fd, &wbt_mq_persist_count, sizeof(wbt_mq_persist_count), 0);
        if( nwrite < 0 ) {
            wbt_log_add("Error writing to the MID file: %d\n", wbt_errno);
        } else {
            if (wbt_sync_file_data(wbt_persist_mid_fd) != 0) {
                // 如果该操作失败,可能是磁盘故障
            //} else {
            //    wbt_log_add("msg_id %lld synced\n", wbt_mq_persist_count);
            }
        }
    }
    
    if( wbt_persist_aof_fd ) {
        // TODO 尝试使用 fdatasync
        if( wbt_sync_file(wbt_persist_aof_fd) != 0 ) {
            // 如果该操作失败,可能是磁盘故障
        }
    }

    if(timer && !wbt_wating_to_exit) {
        /* 重新注册定时事件 */
        timer->timeout += 1 * 1000;

        if(wbt_timer_mod(timer) != WBT_OK) {
            return WBT_ERROR;
        }
    } else {
        wbt_free(timer);
    }
    
    return WBT_OK;
}
示例#6
0
文件: wbt_file.c 项目: fcten/BitMQ
wbt_status wbt_file_cleanup(wbt_timer_t *timer) {
    //wbt_log_debug("opened fd before cleanup: %d\n", wbt_file_rbtree.size);
    
    // TODO 遍历所有文件可能会花费过多的时间
    wbt_file_cleanup_recursive(wbt_file_rbtree.root);
    wbt_mem_print();
    
    //wbt_log_debug("opened fd after cleanup: %d\n", wbt_file_rbtree.size);

    if(!wbt_wating_to_exit) {
        /* 重新注册定时事件 */
        timer->timeout += 10000;

        if(wbt_timer_mod(timer) != WBT_OK) {
            return WBT_ERROR;
        }
    } else {
        wbt_free(timer);
    }

    return WBT_OK;
}
示例#7
0
wbt_status wbt_bmtp2_on_pub(wbt_event_t *ev) {
    wbt_bmtp2_t *bmtp = ev->data;
    
    stream_id = 0;
    wbt_memset(&wbt_mq_parsed_msg, 0, sizeof(wbt_mq_parsed_msg));
    
    if( wbt_bmtp2_param_parser(ev, wbt_bmtp2_on_pub_parser) != WBT_OK ) {
        if( stream_id ) {
            return wbt_bmtp2_send_puback(ev, stream_id, RET_INVALID_MESSAGE);
        } else {
            return WBT_OK;
        }
    }
    
    if( !wbt_mq_parsed_msg.consumer_id || !wbt_mq_parsed_msg.type || ( wbt_mq_parsed_msg.type != MSG_ACK  && !bmtp->payload_length ) ) {
        if( stream_id ) {
            return wbt_bmtp2_send_puback(ev, stream_id, RET_INVALID_MESSAGE);
        } else {
            return WBT_OK;
        }
    }
    
    if( bmtp->role == BMTP_SERVER || bmtp->role == BMTP_SERVER_REPL ) {
        if( wbt_conf.master_host.len > 0 ) {
            // slave 模式下不可写消息
            if( stream_id ) {
                return wbt_bmtp2_send_puback(ev, stream_id, RET_READ_ONLY_SLAVE);
            } else {
                return WBT_OK;
            }
        }
        wbt_mq_parsed_msg.msg_id = 0;
    } else if( bmtp->role == BMTP_CLIENT ) {
        if( wbt_mq_parsed_msg.msg_id == 0 ) {
            if( stream_id ) {
                return wbt_bmtp2_send_puback(ev, stream_id, RET_INVALID_MESSAGE);
            } else {
                return WBT_OK;
            }
        }
        wbt_mq_parsed_msg.create = 0;
    }
    
    wbt_mq_parsed_msg.data_len = bmtp->payload_length;
    wbt_mq_parsed_msg.data = wbt_strdup(bmtp->payload, bmtp->payload_length);
    
    wbt_status ret;
    if( wbt_mq_parsed_msg.is_lastwill ) {
        ret = wbt_mq_set_last_will(ev, &wbt_mq_parsed_msg);
    } else {
        ret = wbt_mq_push(ev, &wbt_mq_parsed_msg);
    }
    
    // wbt_mq_push 以及 wbt_mq_set_last_will 操作可能会失败,所以在这里释放一次
    // 以免内存泄漏。
    // 如果 data 被使用了,上述方法会将 data 置为 NULL 以防止数据被释放
    wbt_free(wbt_mq_parsed_msg.data);
    wbt_mq_parsed_msg.data = NULL;
    
    if( stream_id != 0 ) {
        if( ret != WBT_OK ) {
            // TODO 需要返回更详细的错误原因
            return wbt_bmtp2_send_puback(ev, stream_id, RET_PERMISSION_DENIED);
        } else {
            return wbt_bmtp2_send_puback(ev, stream_id, RET_OK);
        }
    }
    
    return WBT_OK;
}
示例#8
0
文件: wbt_rbtree.c 项目: wljcom/webit
// 在红黑树 rbt 中删除结点 node
void wbt_rbtree_delete(wbt_rbtree_t * rbt, wbt_rbtree_node_t * node) {
    wbt_rbtree_node_t * y; // 将要被删除的结点 
    wbt_rbtree_node_t * x; // 将要被删除的结点的唯一儿子 
    
    if( node->left == wbt_rbtree_node_nil || node->right == wbt_rbtree_node_nil ) {
        // 如果 node 有一个子树为空的话,那么将直接删除 node,即 y 指向 node
        y = node;
    } else {
        // 如果 node 的左右子树皆不为空的话,则寻找 node 的中序后继 y,
        // 用其值代替 node 的值,然后将 y 删除 ( 注意: y肯定是没有左子树的 ) 
        y = wbt_rbtree_successor(rbt, node);  
    }

    if( y->left != wbt_rbtree_node_nil ) {
        // 如果y的左子树不为空,则 x 指向 y 的左子树 
        x = y->left; 
    } else { 
        x = y->right; 
    }

    // 将原来 y 的父母设为 x 的父母,y 即将被删除
    x->parent = y->parent;

    if( y->parent == wbt_rbtree_node_nil ) { 
        rbt->root=x; 
    } else { 
        if( y == y->parent->left ) { 
            y->parent->left = x; 
        } else { 
            y->parent->right = x; 
        } 
    }

    if( y != node ) {
        // 如果被删除的结点 y 不是原来将要删除的结点 node,
        // 即只是用 y 的值来代替 node 的值,然后变相删除 y 以达到删除 node 的效果
        wbt_free( &node->value );
        node->value = y->value;
        wbt_free( &node->key );
        node->key = y->key;
        
        y->key.ptr = NULL;
        y->key.len = 0;
    }

    if( y->color == WBT_RBT_COLOR_BLACK ) {
        // 如果被删除的结点 y 的颜色为黑色,那么可能会导致树违背性质4,导致某条路径上少了一个黑色 
        wbt_rbtree_delete_fixup(rbt, x); 
    }

    /* 删除 y */
    wbt_mem_t tmp;
    tmp.ptr = y;
    tmp.len = sizeof(wbt_rbtree_node_t);
    wbt_free( &y->key );
    wbt_free( &tmp );

    rbt->size --;
    
    wbt_rbtree_print(rbt->root);
    wbt_log_debug("rbtree delete");
} 
示例#9
0
wbt_status wbt_mq_persist_recovery(wbt_timer_t *timer) {
    wbt_mq_persist_aof_lock();
    
    wbt_msg_block_t *block;
    wbt_msg_t *msg;
    uint32_t crc32;
    ssize_t nread;
    char *data;
    
    block = wbt_malloc(sizeof(wbt_msg_block_t));
    if(!block) {
        goto error;
    }

    int wait_time = 50;
    
    int n = 1000;
    while( !timer || n ) {
        if(wbt_is_oom()) {
            // 内存不足,等待一会儿再尝试恢复
            wait_time = 1000;
            n = 0;
            break;
        }

        nread = wbt_read_file(wbt_persist_aof_fd, block, sizeof(wbt_msg_block_t), wbt_mq_persist_recovery_offset);
        if( nread == 0 ) {
            break;
        } else if( nread != sizeof(wbt_msg_block_t) ) {
            goto error;
        } else {
            wbt_mq_persist_recovery_offset += sizeof(wbt_msg_block_t);
        }

        if( wbt_conf.aof_crc ) {
            if (wbt_read_file(wbt_persist_aof_fd, &crc32, sizeof(uint32_t), wbt_mq_persist_recovery_offset) != sizeof(uint32_t)) {
                goto error;
            } else {
                wbt_mq_persist_recovery_offset += sizeof(uint32_t);
            }
            
            if( crc32 != wbt_crc32( (unsigned char *)block, sizeof(wbt_msg_block_t) ) ) {
                // 校验失败
                goto error;
            }
        }
        
        /* 注意,这里可能出现 malloc(0) 的情况
         * 目前看来,已知的 malloc 实现都会在 mallc(0) 时返回一个可用指针而不是 NULL
         * 但是,C 标准规定 malloc(0) 可以返回 NULL。如果发生这种情况,可以考虑故意多
         * 申请一个字节的空间。
         */
        // data = wbt_malloc(block->data_len+1);
        data = wbt_malloc(block->data_len);
        if(!data) {
            goto error;
        }
        if (wbt_read_file(wbt_persist_aof_fd, data, block->data_len, wbt_mq_persist_recovery_offset) != block->data_len) {
            wbt_free( data );
            goto error;
        } else {
            wbt_mq_persist_recovery_offset += block->data_len;
        }

        if( wbt_conf.aof_crc ) {
            if (wbt_read_file(wbt_persist_aof_fd, &crc32, sizeof(uint32_t), wbt_mq_persist_recovery_offset) != sizeof(uint32_t)) {
                wbt_free( data );
                goto error;
            } else {
                wbt_mq_persist_recovery_offset += sizeof(uint32_t);
            }
            
            if( crc32 != wbt_crc32( (unsigned char *)data, block->data_len ) ) {
                // 校验失败
                wbt_free( data );
                goto error;
            }
        }

        wbt_log_debug( "msg %lld recovered\n", block->msg_id );

        // 由于内存和文件中保存的结构并不一致,这里我们不得不进行内存拷贝
        // TODO 修改消息相关的内存操作,尝试直接使用读入的内存
        // TODO 使用 mdb 文件加速载入
        // 已过期的消息应当被忽略
        if( block->type == MSG_ACK ) {
            // 未过期但是已经收到 ACK 的负载均衡消息可能会在 fast_boot 期间被重复处理
            wbt_msg_t *msg = wbt_mq_msg_get(block->consumer_id);
            if(msg) {
                wbt_mq_msg_event_expire( &msg->timer );
            }
            wbt_free(data);
        } else if( block->expire > wbt_cur_mtime ) {
            msg = wbt_mq_msg_create(block->msg_id);
            if( msg == NULL ) {
                // * ID 冲突,忽略该条消息,并将该消息从 aof 文件中移除
                // * 注意,只能通过重写 aof 文件来完成移除操作,如果在重写操作完
                // 成之前程序再次重启,aof 文件中将存在一部分 msg_id 冲突的消息
                // * 不使用 fast_boot 可以避免该问题
                wbt_free(data);
            } else {
                wbt_memcpy(msg, block, sizeof(wbt_msg_block_t));
                msg->data = data;

                wbt_mq_msg_delivery(msg);
            }
        } else {
            wbt_free(data);
        }
        
        n --;
    }
    
    wbt_log_add( "%d msg recovered\n", 1000-n );

//success:
    if(timer && ( n == 0 ) && !wbt_wating_to_exit) {
        /* 重新注册定时事件 */
        timer->timeout += wait_time;

        if(wbt_timer_mod(timer) != WBT_OK) {
            goto error;
        }
    } else {
        wbt_free(timer);
        wbt_mq_persist_aof_unlock();
    }
    
    wbt_free(block);
    
    return WBT_OK;
    
error:

    wbt_free( block );
    wbt_free( timer );

    wbt_mq_persist_aof_unlock();

    return WBT_ERROR;
}
示例#10
0
// * 遍历并导出内存中的所有活跃消息
// * 目前不允许对已投递消息做任何修改,所以我们可以将这个操作通过定时事件分步完成。
// * 和 fork 新进程的方式相比,这样做可以节省大量的内存。这允许我们更加频繁地执行该
// 操作。而我们需要付出的唯一代价是导出的文件中可能会存在少量刚刚过期的消息
// TODO 
static wbt_status wbt_mq_persist_dump(wbt_timer_t *timer) {
    char aof_file[256];
    snprintf( aof_file, sizeof(aof_file), "%.*s/bmq.aof", wbt_conf.data.len, wbt_conf.data.str );
    
    char mdp_file[256];
    snprintf( mdp_file, sizeof(mdp_file), "%.*s/bmq.mdp", wbt_conf.data.len, wbt_conf.data.str );

    // 临时文件 fd
    static wbt_fd_t rdp_fd = 0;
    // 记录当前的最大消息 ID max
    static wbt_mq_id processed_msg_id = 0;

    if( wbt_mq_persist_aof_is_lock() == 1 ) {
        // 如果目前正在从 aof 文件中恢复数据,则 dump 操作必须被推迟
        // 这是为了保证 dump 完成的时刻,所有未过期消息都在内存中
        if(timer && !wbt_wating_to_exit) {
            /* 重新注册定时事件 */
            timer->timeout += 60 * 1000;

            if(wbt_timer_mod(timer) != WBT_OK) {
                return WBT_ERROR;
            }
        }
        
        // 发生数据恢复往往是因为旧的消息被删除了,所以推迟之后,dump 操作应当重新开始
        rdp_fd = 0;
        
        return WBT_OK;
    } else if( 0 /* 如果当前负载过高 */) {
        // 如果当前负载过高,则 dump 操作必须被推迟
        if(timer && !wbt_wating_to_exit) {
            /* 重新注册定时事件 */
            timer->timeout += 3600 * 1000;

            if(wbt_timer_mod(timer) != WBT_OK) {
                return WBT_ERROR;
            }
        }
        
        // 负载过高时,会推迟很长一段时间再尝试,所以推迟之后,dump 操作应当重新开始
        rdp_fd = 0;
        
        return WBT_OK;
    }

    if( !rdp_fd ) {
        // 创建临时文件
        rdp_fd = wbt_open_tmpfile(mdp_file);
        if( (int)rdp_fd <= 0 ) {
            wbt_log_add("Could not open mdp file: %d\n", wbt_errno);
            // TODO data目录存在权限问题时会导致错误
            return WBT_ERROR;
        }
        processed_msg_id = 0;
    }

    wbt_str_t key;
    wbt_variable_to_str(processed_msg_id, key);
    
    // 从小到大遍历消息,直至 max
    int max = 1000;
    wbt_rb_node_t *node;
    wbt_msg_t *msg;
      
    for (node = wbt_rb_get_greater(wbt_mq_msg_get_all(), &key);
         node;
         node = wbt_rb_next(node)) {
        msg = (wbt_msg_t *)node->value.str;

        if( wbt_mq_persist_append_to_file(rdp_fd, msg) == WBT_OK ) {
            processed_msg_id = msg->msg_id;
        } else {
            return WBT_ERROR;
        }

        if( --max <= 0 && timer ) {
            break;
        }
    }
    
    if( node == NULL ) {
        // 重写完成
        // 如果刷盘策略为 AOF_FSYNC_ALWAYS,我们立刻将数据同步到磁盘上。
        // 如果刷盘策略为 AOF_FSYNC_EVERYSEC,我们什么都不做,只需要等待定时任务执行 fsync(2)。
        // 如果刷盘策略为 AOF_FSYNC_NO,我们什么都不做。
        if( wbt_conf.aof_fsync == AOF_FSYNC_ALWAYS ) {
            wbt_sync_file(rdp_fd);
        }

        // 旧的 aof 文件已经无用了,不需要保证数据落盘,所以直接 close 即可
        if( wbt_close_file(wbt_persist_aof_fd) < 0 ) {
            return WBT_ERROR;
        }
        wbt_persist_aof_fd = 0;

        // 用 mdp 文件覆盖 aof 文件
#ifdef WIN32
        if(wbt_close_file(rdp_fd) < 0) {
            return WBT_ERROR;
        }
        rdp_fd = 0;

        if(MoveFileEx(mdp_file, aof_file, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0) {
#else
        if( rename(mdp_file, aof_file) < 0 ) {
#endif
            wbt_log_add("Could not rename mdp file. "
                "rename: %d\n", wbt_errno);
            
            // 尝试重新打开 aof 文件
            wbt_persist_aof_fd = wbt_open_logfile(aof_file);
            if( (int)wbt_persist_aof_fd < 0 ) {
                wbt_persist_aof_fd = 0;
                wbt_log_add("Could not open AOF file: %d\n", wbt_errno);
            }
            
            return WBT_ERROR;
        }

#ifdef WIN32
        wbt_persist_aof_fd = wbt_open_logfile(aof_file);
        if( (int)wbt_persist_aof_fd <= 0 ) {
            wbt_persist_aof_fd = 0;
            wbt_log_add("Could not open AOF file: %d\n", wbt_errno);
            return WBT_ERROR;
        }
#else
        wbt_persist_aof_fd = rdp_fd;
        rdp_fd = 0;
#endif
        
        // 重写完成时,必然所有消息都在内存中生效,所以直接将 offset 设定为文件末尾
        wbt_mq_persist_recovery_offset = wbt_get_file_size(wbt_persist_aof_fd);

        // 重写完成后,下一次重写将在设定的时间间隔之后运行
        if(!wbt_wating_to_exit) {
            if(!timer) {
                timer = wbt_malloc(sizeof(wbt_timer_t));
                timer->on_timeout = wbt_mq_persist_dump;
                timer->heap_idx = 0;
                timer->timeout = wbt_cur_mtime;
            }

            /* 重新注册定时事件 */
            // TODO 从配置文件中读取
            timer->timeout += 6 * 3600 * 1000;

            if(wbt_timer_mod(timer) != WBT_OK) {
                return WBT_ERROR;
            }
        } else {
            wbt_free(timer);
        }
    } else {
        if(!wbt_wating_to_exit) {
            /* 重新注册定时事件 */
            timer->timeout += 50;

            if(wbt_timer_mod(timer) != WBT_OK) {
                return WBT_ERROR;
            }
        } else {
            wbt_free(timer);
        }
    }

    return WBT_OK;
}
示例#11
0
文件: wbt_config.c 项目: fcten/BitMQ
static wbt_status wbt_conf_parse(wbt_str_t * conf) {
    int status = 0;
    char *p, ch;
    
    wbt_conf_line = 1;
    wbt_conf_charactor = 0;
    
    wbt_str_t key, value;
    
    //wbt_log_debug("Parse config file: size %d", conf->len);
    
    int i = 0;
    for( i = 0 ; i < conf->len ; i ++ ) {
        p = conf->str;
        ch = *(p+i);

        wbt_conf_charactor ++;

        switch( status ) {
            case 0:
                if( ch == ' ' || ch == '\t' ) {
                    /* continue; */
                } else if( ch == '\n' ) {
                    wbt_conf_line ++;
                    wbt_conf_charactor = 0;
                } else if( ch == '#' ) {
                    status = 1;
                } else if( ( ch >= 'a' && ch <= 'z' ) || ch == '_' ) {
                    key.str = p + i;
                    status = 2;
                } else {
                    return WBT_ERROR;
                }
                break;
            case 1:
                if( ch == '\n' ) {
                    wbt_conf_line ++;
                    wbt_conf_charactor = 0;
                    status = 0;
                }
                break;
            case 2:
                if( ( ch >= 'a' && ch <= 'z' ) || ch == '_' ) {
                    /* continue; */
                } else if( ch == ' ' || ch == '\t' ) {
                    key.len = p + i - key.str;
                    status = 3;
                } else {
                    return WBT_ERROR;
                }
                break;
            case 3:
                if( ch == ' ' || ch == '\t' ) {
                    /* continue; */
                } else {
                    value.str = p + i;
                    status = 4;
                }
                break;
            case 4:
                if( ch == '\n' ) {
                    wbt_conf_line ++;
                    wbt_conf_charactor = 0;
                    value.len = p + i - value.str;
                    status = 0;
                    
                    /* 储存配置信息 */
                    wbt_rb_node_t *option =  wbt_rb_get(&wbt_config_rbtree, &key);
                    if( option == NULL ) {
                        /* 新的值 */
                        option = wbt_rb_insert(&wbt_config_rbtree, &key);
                    } else {
                        /* 已有的值 */
                        wbt_free(option->value.str);
                        option->value.len = 0;
                    }
                    option->value.str = wbt_strdup(value.str, value.len);
                    if( option->value.str == NULL ) {
                        /* 这里返回 WBT_ERROR 会导致提示配置文件语法错误,
                         * 但事实上是由于内存不足导致的。在启动阶段出现这种情况的概率不大,所以就这样吧。 */
                        return WBT_ERROR;
                    }
                    option->value.len = value.len;
                }
                break;
            default:
                /* 不应当出现未知的状态 */
                return WBT_ERROR;
        }
    }
    
    return WBT_OK;
}